summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 11:40:17 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 12:42:11 +0000
commit5d87695f37678f96492b258bbab36486c59866b4 (patch)
treebe9783bbaf04fb930c4d74ca9c00b5e7954c8bc6 /chromium/net/third_party/quiche
parent6c11fb357ec39bf087b8b632e2b1e375aef1b38b (diff)
downloadqtwebengine-chromium-5d87695f37678f96492b258bbab36486c59866b4.tar.gz
BASELINE: Update Chromium to 75.0.3770.56
Change-Id: I86d2007fd27a45d5797eee06f4c9369b8b50ac4f Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/net/third_party/quiche')
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.cc56
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h116
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_address_test_utils.h16
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_bug.h12
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_expect_bug.h12
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_export.h10
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_logging.h19
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_ptr_util.h21
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_test.h11
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_thread.h27
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_time.h16
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.cc812
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.h1051
-rw-r--r--chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server_test.cc2517
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/decode_buffer.h2
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/decode_buffer_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.cc18
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.h18
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.cc6
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc20
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h2
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.cc79
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h4
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc6
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc27
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.h35
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc6
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc21
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc24
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder.cc12
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc14
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc16
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc19
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc18
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc17
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc21
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc26
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc4
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h23
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder.cc13
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder_test.cc8
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder.cc12
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder_test.cc6
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc15
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc26
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc16
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc6
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder.cc23
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder_test.cc12
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder.cc11
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc14
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder.cc16
-rw-r--r--chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc10
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc4
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc10
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h4
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.cc26
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc49
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer.cc31
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc34
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc29
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc4
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc20
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.cc22
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h14
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_listener.cc8
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc10
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.cc44
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc8
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc6
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants_test.cc47
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc14
-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.cc2
-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/tools/hpack_example.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc5
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc23
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc39
-rw-r--r--chromium/net/third_party/quiche/src/http2/http2_constants.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/http2_structures.h2
-rw-r--r--chromium/net/third_party/quiche/src/http2/http2_structures_test.cc20
-rw-r--r--chromium/net/third_party/quiche/src/http2/platform/api/http2_logging.h22
-rw-r--r--chromium/net/third_party/quiche/src/http2/platform/api/http2_mock_log.h18
-rw-r--r--chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.cc98
-rw-r--r--chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h2
-rw-r--r--chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector.cc2
-rw-r--r--chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.cc83
-rw-r--r--chromium/net/third_party/quiche/src/http2/test_tools/http2_random.cc4
-rw-r--r--chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.cc21
-rw-r--r--chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h2
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc303
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/chlo_extractor.h45
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/chlo_extractor_test.cc169
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc214
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h337
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc464
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc976
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h416
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender_test.cc1342
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.cc191
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.h102
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes_test.cc388
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc236
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h84
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc578
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.cc104
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.h83
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start_test.cc76
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h49
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc161
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h107
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender_test.cc471
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender.cc62
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h43
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender_test.cc123
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.cc103
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h109
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc230
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.cc56
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h146
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc371
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc431
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.h172
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc834
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc86
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h53
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc159
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h160
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter_test.cc387
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.cc202
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h73
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.cc187
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h80
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h38
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc286
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.cc30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h34
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc241
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.cc37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter_test.cc291
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.cc30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h32
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter_test.cc274
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.cc37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.h36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter_test.cc295
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.cc30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.h32
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter_test.cc257
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.cc38
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h32
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.cc37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h31
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor.cc642
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor.h56
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor_test.cc130
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h40
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter_test.cc176
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.cc30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter_test.cc157
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h38
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc186
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.h34
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc170
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.cc42
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h31
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.cc39
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/channel_id.cc89
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/channel_id.h97
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/channel_id_test.cc321
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set.cc164
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set.h47
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2.c122
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2a.inc5622
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2b.inc5739
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3.c118
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3a.inc5033
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3b.inc5717
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_test.cc249
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer.cc353
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer.h138
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer_test.cc464
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake.cc41
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h189
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc379
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h155
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message_test.cc131
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.h35
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc56
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h313
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.cc143
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h67
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer_test.cc81
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc1175
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc470
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h228
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc154
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.cc83
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h53
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange_test.cc102
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc42
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.h105
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.cc124
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.h62
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter_test.cc136
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.cc97
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.h55
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter_test.cc100
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.cc119
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h68
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange_test.cc107
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.cc16
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h145
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/proof_verifier.h119
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.cc129
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h108
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache_test.cc99
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypter.h83
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc1022
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h430
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc656
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.cc11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h29
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc1925
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h984
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config_test.cc508
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.cc69
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h90
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.cc50
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h73
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf.cc93
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h70
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf_test.cc91
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.cc44
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.h33
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/quic_random_test.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc302
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h93
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc440
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.cc326
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h144
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.cc31
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h51
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc82
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h77
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.cc42
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h51
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc340
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h145
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc644
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.cc25
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.h40
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.cc39
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.h51
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h23
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.cc33
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.cc24
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.h37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_padding_frame.cc15
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h35
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.cc37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.cc36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_ping_frame.cc20
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h33
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.cc25
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h33
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.cc42
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h54
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.cc30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h34
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.cc20
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h30
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_frame.cc46
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h51
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.cc27
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.h40
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.cc29
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h48
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc3915
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc469
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h196
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc510
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc258
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h85
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc186
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/http_frames.h156
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc144
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h114
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info_test.cc354
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.cc48
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h99
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index_test.cc118
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.cc73
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.h89
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_header_list_test.cc83
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.cc157
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.h96
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc969
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc163
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h53
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc172
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h47
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc124
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc278
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h139
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc740
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc183
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h103
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc210
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h141
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc803
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.cc160
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h100
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc232
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.cc48
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h31
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base_test.cc94
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc702
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h289
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc1808
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc661
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h283
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.cc128
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h75
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer_test.cc241
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc1559
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc355
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h79
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc557
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc139
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h104
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager_test.cc179
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h225
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue_test.cc179
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/proto/cached_network_parameters.proto43
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/proto/crypto_server_config.proto34
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/proto/source_address_token.proto32
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc273
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h71
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc44
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc200
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h147
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc72
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h66
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc119
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc141
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h102
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc52
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h63
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc91
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc61
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h55
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc91
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc703
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc82
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h114
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc55
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h72
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc60
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h68
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc169
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc81
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h58
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc113
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc168
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h64
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc204
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h144
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc356
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc310
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h146
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc171
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc217
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h118
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc152
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc369
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h140
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder_test.cc124
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.cc139
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h57
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc137
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.cc140
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h31
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc53
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc23
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h29
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_ack_listener_interface.cc11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc73
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_alarm.h86
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_alarm_factory.h36
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc166
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h208
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr_test.cc102
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_bandwidth.cc40
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_bandwidth.h151
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_bandwidth_test.cc122
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h29
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.cc11
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.h37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc237
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h178
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc441
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_config.cc826
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_config.h491
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc288
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection.cc4141
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection.h1525
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_close_delegate_interface.h29
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc106
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h103
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_id_test.cc97
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc97
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h107
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc8864
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_constants.cc25
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_constants.h237
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc312
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h153
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc306
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc702
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h237
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc212
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc99
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h174
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream_test.cc474
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc49
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h50
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.cc485
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h239
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc174
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h224
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc573
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc435
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h173
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc518
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_data_reader.cc317
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_data_reader.h177
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_data_writer.cc356
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_data_writer.h144
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_data_writer_test.cc1119
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.cc68
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.h55
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc1405
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h509
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc2885
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.cc89
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h35
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory_test.cc140
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.cc41
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h55
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper_test.cc41
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc171
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h363
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_error_codes_test.cc26
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.cc308
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.h207
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_flow_controller_test.cc408
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_framer.cc5767
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_framer.h939
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc13346
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_ietf_framer_test.cc1484
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_interval.h378
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_interval_set.h914
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_interval_set_test.cc1010
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_interval_test.cc457
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_lru_cache.h74
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_lru_cache_test.cc76
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena.h82
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena_test.cc60
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc1041
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h421
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc1804
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.cc546
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.h316
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_generator_test.cc1560
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_number.cc119
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_number.h150
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_number_test.cc67
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.cc226
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.h90
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_writer.h132
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.cc79
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h61
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packets.cc422
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_packets.h368
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_pending_retransmission.h54
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_process_packet_interface.h24
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc359
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.h197
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc807
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc1266
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h634
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc2695
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_server_id.cc41
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_server_id.h46
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_server_id_test.cc129
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_session.cc1699
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_session.h702
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc2352
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.cc21
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h22
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator_test.cc28
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder.cc88
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder.h41
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder_test.cc128
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream.cc1100
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream.h541
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h39
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc351
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.h238
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc844
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc308
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h170
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc325
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc274
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h211
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc528
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h248
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc1086
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc763
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc1549
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.cc62
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h95
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder_test.cc133
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_tag.cc83
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_tag.h53
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_tag_test.cc42
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time.cc85
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time.h283
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time_test.cc182
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc386
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h275
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc583
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc332
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.h70
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor_test.cc168
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_transmission_info.cc43
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_transmission_info.h67
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_types.cc121
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_types.h605
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc568
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h318
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map_test.cc758
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_utils.cc581
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_utils.h197
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc233
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc71
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h66
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc71
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_versions.cc439
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_versions.h378
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc635
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h261
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc233
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/session_notifier_interface.h43
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/stateless_rejector.cc162
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/stateless_rejector.h123
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/stateless_rejector_test.cc292
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc357
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h128
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc230
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h145
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc434
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc407
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h183
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc210
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h100
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc323
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc210
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.h107
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc799
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_aligned.h15
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_arraysize.h12
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h15
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_cert_utils.h23
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_client_stats.h87
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_clock.cc70
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_clock.h67
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h56
-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_default_proof_providers.h31
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_endian.h54
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_endian_test.cc51
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_epoll.h19
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_epoll_test_tools.h12
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h14
-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_expect_bug.h14
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_export.h10
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h96
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h12
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.cc22
-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_flag_utils.h19
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_flags.h48
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h16
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h34
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc89
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_iovec.h27
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.cc91
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.h84
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address_family.h20
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_logging.h39
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_macros.h13
-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.h62
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h54
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span_test.cc47
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h39
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc60
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc50
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mock_log.h18
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.cc45
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.h88
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h28
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_port_utils.h26
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_prefetch.h39
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h27
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h162
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_reference_counted_test.cc173
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_server_stats.h82
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_sleep.h19
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.cc59
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.h50
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h20
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_str_cat.h28
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_str_cat_test.cc168
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_stream_buffer_allocator.h16
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_string_piece.h16
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_string_utils.h23
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_string_utils_test.cc178
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h16
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_test.h27
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_test_loopback.cc29
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h32
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h35
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_test_output.h25
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils.h120
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc207
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_thread.h27
-rw-r--r--chromium/net/third_party/quiche/src/quic/platform/api/quic_uint128.h19
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h43
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc24
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h33
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc162
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h122
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc89
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h73
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc117
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h181
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc214
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc242
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h67
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h139
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h122
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc110
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc76
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h113
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc397
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h284
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc559
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc171
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h142
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc640
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc95
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h84
-rw-r--r--chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc165
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/bad_packet_writer.cc36
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h36
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc1026
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h276
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils_test.cc171
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h38
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc128
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h117
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.cc30
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.h37
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.cc29
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.h34
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h32
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.cc32
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.h42
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h56
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.cc19
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.h34
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc32
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h60
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_random.cc29
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/mock_random.h39
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc252
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h176
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.cc53
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h45
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.cc25
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h32
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_client_peer.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_client_peer.h28
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.cc17
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.h22
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc67
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h50
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc393
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h148
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.cc162
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h98
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.cc111
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h71
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.cc68
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h45
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.cc355
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h174
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.cc141
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h66
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.cc20
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h28
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.cc239
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h114
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_server_peer.cc32
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_server_peer.h28
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_server_session_base_peer.h37
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc197
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h80
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc71
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h53
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.cc26
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h31
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.cc36
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h32
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_peer.cc62
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h44
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.cc39
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h29
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.cc155
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h63
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.cc40
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h33
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h35
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc909
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h413
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc232
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.h113
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc1148
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h1191
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils_test.cc61
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.cc32
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h29
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.cc24
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h25
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.cc21
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.h26
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/server_thread.cc121
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/server_thread.h88
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.cc71
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.h91
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc400
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h69
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc646
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h153
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc271
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/actor.cc31
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/actor.h66
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.cc81
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h39
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/link.cc121
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/link.h91
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.cc40
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h75
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/port.cc21
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/port.h66
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc123
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.h120
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc421
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h234
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_test.cc209
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.cc167
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.h166
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc830
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/switch.cc85
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/switch.h89
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.cc60
-rw-r--r--chromium/net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h54
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.cc29
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h84
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client.cc99
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client.h81
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc353
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h364
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_bin.cc403
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.cc206
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h135
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc120
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc419
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h198
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc235
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc246
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc43
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_server.cc212
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_server.h159
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc70
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc215
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc18
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h38
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.cc33
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h34
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h38
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc25
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc68
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h52
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h60
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc228
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h154
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc846
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc341
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h102
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc679
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc273
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h216
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc111
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h73
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc103
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_url.cc101
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_url.h61
-rw-r--r--chromium/net/third_party/quiche/src/quic/tools/quic_url_test.cc157
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_constants.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc49
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc10
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.cc23
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc3
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.cc42
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc6
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.cc167
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h8
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc99
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h3
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor_test.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc6
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h9
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder_test.cc27
-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_test.cc101
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.cc29
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_header_block.h1
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.cc2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_protocol.h3
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.cc20
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h12
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena.cc103
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena.h77
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena_test.cc136
-rw-r--r--chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc4
-rw-r--r--chromium/net/third_party/quiche/src/spdy/platform/api/spdy_logging.h23
-rw-r--r--chromium/net/third_party/quiche/src/spdy/platform/api/spdy_macros.h2
-rw-r--r--chromium/net/third_party/quiche/src/spdy/platform/api/spdy_test_utils_prod.h12
903 files changed, 216760 insertions, 1002 deletions
diff --git a/chromium/net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.cc b/chromium/net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.cc
new file mode 100644
index 00000000000..7e559459e7b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h"
+
+namespace epoll_server {
+namespace test {
+
+FakeTimeSimpleEpollServer::FakeTimeSimpleEpollServer() : now_in_usec_(0) {}
+
+FakeTimeSimpleEpollServer::~FakeTimeSimpleEpollServer() = default;
+
+int64_t FakeTimeSimpleEpollServer::NowInUsec() const { return now_in_usec_; }
+
+FakeSimpleEpollServer::FakeSimpleEpollServer() : until_in_usec_(-1) {}
+
+FakeSimpleEpollServer::~FakeSimpleEpollServer() = default;
+
+int FakeSimpleEpollServer::epoll_wait_impl(int epfd, struct epoll_event* events,
+ int max_events, int timeout_in_ms) {
+ int num_events = 0;
+ while (!event_queue_.empty() && num_events < max_events &&
+ event_queue_.begin()->first <= NowInUsec() &&
+ ((until_in_usec_ == -1) ||
+ (event_queue_.begin()->first < until_in_usec_))) {
+ int64_t event_time_in_usec = event_queue_.begin()->first;
+ events[num_events] = event_queue_.begin()->second;
+ if (event_time_in_usec > NowInUsec()) {
+ set_now_in_usec(event_time_in_usec);
+ }
+ event_queue_.erase(event_queue_.begin());
+ ++num_events;
+ }
+ if (num_events == 0) { // then we'd have waited 'till the timeout.
+ if (until_in_usec_ < 0) { // then we don't care what the final time is.
+ if (timeout_in_ms > 0) {
+ AdvanceBy(timeout_in_ms * 1000);
+ }
+ } else { // except we assume that we don't wait for the timeout
+ // period if until_in_usec_ is a positive number.
+ set_now_in_usec(until_in_usec_);
+ // And reset until_in_usec_ to signal no waiting (as
+ // the AdvanceByExactly* stuff is meant to be one-shot,
+ // as are all similar net::EpollServer functions)
+ until_in_usec_ = -1;
+ }
+ }
+ if (until_in_usec_ >= 0) {
+ CHECK(until_in_usec_ >= NowInUsec());
+ }
+ return num_events;
+}
+
+} // namespace test
+} // namespace epoll_server
diff --git a/chromium/net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h b/chromium/net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h
new file mode 100644
index 00000000000..470311a4088
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_
+#define QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_export.h"
+#include "net/third_party/quiche/src/epoll_server/simple_epoll_server.h"
+
+namespace epoll_server {
+namespace test {
+
+// Unlike the full FakeEpollServer, this only lies about the time but lets
+// fd events operate normally. Usefully when interacting with real backends
+// but wanting to skip forward in time to trigger timeouts.
+class EPOLL_EXPORT_PRIVATE FakeTimeSimpleEpollServer
+ : public SimpleEpollServer {
+ public:
+ FakeTimeSimpleEpollServer();
+ FakeTimeSimpleEpollServer(const FakeTimeSimpleEpollServer&) = delete;
+ FakeTimeSimpleEpollServer operator=(const FakeTimeSimpleEpollServer&) =
+ delete;
+
+ ~FakeTimeSimpleEpollServer() override;
+
+ // Replaces the net::EpollServer NowInUsec.
+ int64_t NowInUsec() const override;
+
+ void set_now_in_usec(int64_t nius) { now_in_usec_ = nius; }
+
+ // Advances the virtual 'now' by advancement_usec.
+ void AdvanceBy(int64_t advancement_usec) {
+ set_now_in_usec(NowInUsec() + advancement_usec);
+ }
+
+ // Advances the virtual 'now' by advancement_usec, and
+ // calls WaitForEventAndExecteCallbacks.
+ // Note that the WaitForEventsAndExecuteCallbacks invocation
+ // may cause NowInUs to advance beyond what was specified here.
+ // If that is not desired, use the AdvanceByExactly calls.
+ void AdvanceByAndWaitForEventsAndExecuteCallbacks(int64_t advancement_usec) {
+ AdvanceBy(advancement_usec);
+ WaitForEventsAndExecuteCallbacks();
+ }
+
+ private:
+ int64_t now_in_usec_;
+};
+
+class EPOLL_EXPORT_PRIVATE FakeSimpleEpollServer
+ : public FakeTimeSimpleEpollServer {
+ public: // type definitions
+ using EventQueue = std::unordered_multimap<int64_t, struct epoll_event>;
+
+ FakeSimpleEpollServer();
+ FakeSimpleEpollServer(const FakeSimpleEpollServer&) = delete;
+ FakeSimpleEpollServer operator=(const FakeSimpleEpollServer&) = delete;
+
+ ~FakeSimpleEpollServer() override;
+
+ // time_in_usec is the time at which the event specified
+ // by 'ee' will be delivered. Note that it -is- possible
+ // to add an event for a time which has already been passed..
+ // .. upon the next time that the callbacks are invoked,
+ // all events which are in the 'past' will be delivered.
+ void AddEvent(int64_t time_in_usec, const struct epoll_event& ee) {
+ event_queue_.insert(std::make_pair(time_in_usec, ee));
+ }
+
+ // Advances the virtual 'now' by advancement_usec,
+ // and ensure that the next invocation of
+ // WaitForEventsAndExecuteCallbacks goes no farther than
+ // advancement_usec from the current time.
+ void AdvanceByExactly(int64_t advancement_usec) {
+ until_in_usec_ = NowInUsec() + advancement_usec;
+ set_now_in_usec(NowInUsec() + advancement_usec);
+ }
+
+ // As above, except calls WaitForEventsAndExecuteCallbacks.
+ void AdvanceByExactlyAndCallCallbacks(int64_t advancement_usec) {
+ AdvanceByExactly(advancement_usec);
+ WaitForEventsAndExecuteCallbacks();
+ }
+
+ std::unordered_set<AlarmCB*>::size_type NumberOfAlarms() const {
+ return all_alarms_.size();
+ }
+
+ protected: // functions
+ // These functions do nothing here, as we're not actually
+ // using the epoll_* syscalls.
+ void DelFD(int fd) const override {}
+ void AddFD(int fd, int event_mask) const override {}
+ void ModFD(int fd, int event_mask) const override {}
+
+ // Replaces the epoll_server's epoll_wait_impl.
+ int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events,
+ int timeout_in_ms) override;
+ void SetNonblocking(int fd) override {}
+
+ private: // members
+ EventQueue event_queue_;
+ int64_t until_in_usec_;
+};
+
+} // namespace test
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_FAKE_SIMPLE_EPOLL_SERVER_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_address_test_utils.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_address_test_utils.h
new file mode 100644
index 00000000000..e224afba138
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_address_test_utils.h
@@ -0,0 +1,16 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
+
+#include "net/tools/epoll_server/platform/impl/epoll_address_test_utils_impl.h"
+
+namespace epoll_server {
+
+int AddressFamilyUnderTest() { return AddressFamilyUnderTestImpl(); }
+
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_ADDRESS_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_bug.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_bug.h
new file mode 100644
index 00000000000..33a3478bd29
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_bug.h
@@ -0,0 +1,12 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
+
+#include "net/tools/epoll_server/platform/impl/epoll_bug_impl.h"
+
+#define EPOLL_BUG EPOLL_BUG_IMPL
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_BUG_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_expect_bug.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_expect_bug.h
new file mode 100644
index 00000000000..313823c31aa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_expect_bug.h
@@ -0,0 +1,12 @@
+// 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_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
+
+#include "net/tools/epoll_server/platform/impl/epoll_expect_bug_impl.h"
+
+#define EXPECT_EPOLL_BUG EXPECT_EPOLL_BUG_IMPL
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPECT_BUG_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_export.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_export.h
new file mode 100644
index 00000000000..728d4b7f245
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_export.h
@@ -0,0 +1,10 @@
+// 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_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPORT_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPORT_H_
+
+#include "net/tools/epoll_server/platform/impl/epoll_export_impl.h"
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_EXPORT_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_logging.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_logging.h
new file mode 100644
index 00000000000..3cd2fac172d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_logging.h
@@ -0,0 +1,19 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
+
+#include "net/tools/epoll_server/platform/impl/epoll_logging_impl.h"
+
+namespace epoll_server {
+
+#define EPOLL_LOG(severity) EPOLL_LOG_IMPL(severity)
+#define EPOLL_VLOG(verbosity) EPOLL_VLOG_IMPL(verbosity)
+#define EPOLL_DVLOG(verbosity) EPOLL_DVLOG_IMPL(verbosity)
+#define EPOLL_PLOG(severity) EPOLL_PLOG_IMPL(severity)
+
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_LOGGING_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_ptr_util.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_ptr_util.h
new file mode 100644
index 00000000000..2de39ca3872
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_ptr_util.h
@@ -0,0 +1,21 @@
+// 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_EPOLL_SERVER_PLATFORM_API_EPOLL_PTR_UTIL_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_PTR_UTIL_H_
+
+#include <memory>
+
+#include "net/tools/epoll_server/platform/impl/epoll_ptr_util_impl.h"
+
+namespace epoll_server {
+
+template <typename T, typename... Args>
+std::unique_ptr<T> EpollMakeUnique(Args&&... args) {
+ return EpollMakeUniqueImpl<T>(std::forward<Args>(args)...);
+}
+
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_PTR_UTIL_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_test.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_test.h
new file mode 100644
index 00000000000..89214b43eaa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_test.h
@@ -0,0 +1,11 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
+
+#include "net/tools/epoll_server/platform/impl/epoll_test_impl.h"
+#define EpollTest EpollTestImpl
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TEST_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_thread.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_thread.h
new file mode 100644
index 00000000000..97c0b08d394
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_thread.h
@@ -0,0 +1,27 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_
+
+#include <string>
+
+#include "net/tools/epoll_server/platform/impl/epoll_thread_impl.h"
+
+namespace epoll_server {
+
+// A class representing a thread of execution in QUIC.
+class EpollThread : public EpollThreadImpl {
+ public:
+ EpollThread(const std::string& string) : EpollThreadImpl(string) {}
+ EpollThread(const EpollThread&) = delete;
+ EpollThread& operator=(const EpollThread&) = delete;
+
+ // Impl defines a virtual void Run() method which subclasses
+ // must implement.
+};
+
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_THREAD_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_time.h b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_time.h
new file mode 100644
index 00000000000..95f04079a97
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/platform/api/epoll_time.h
@@ -0,0 +1,16 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_
+#define QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_
+
+#include "net/tools/epoll_server/platform/impl/epoll_time_impl.h"
+
+namespace epoll_server {
+
+int64_t WallTimeNowInUsec() { return WallTimeNowInUsecImpl(); }
+
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_PLATFORM_API_EPOLL_TIME_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.cc b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.cc
new file mode 100644
index 00000000000..01175421107
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.cc
@@ -0,0 +1,812 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/epoll_server/simple_epoll_server.h"
+
+#include <errno.h> // for errno and strerror_r
+#include <stdlib.h> // for abort
+#include <unistd.h> // For read, pipe, close and write.
+
+#include <algorithm>
+#include <utility>
+
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_bug.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_time.h"
+
+// Design notes: An efficient implementation of ready list has the following
+// desirable properties:
+//
+// A. O(1) insertion into/removal from the list in any location.
+// B. Once the callback is found by hash lookup using the fd, the lookup of
+// corresponding entry in the list is O(1).
+// C. Safe insertion into/removal from the list during list iteration. (The
+// ready list's purpose is to enable completely event driven I/O model.
+// Thus, all the interesting bits happen in the callback. It is critical
+// to not place any restriction on the API during list iteration.
+//
+// The current implementation achieves these goals with the following design:
+//
+// - The ready list is constructed as a doubly linked list to enable O(1)
+// insertion/removal (see man 3 queue).
+// - The forward and backward links are directly embedded inside the
+// CBAndEventMask struct. This enables O(1) lookup in the list for a given
+// callback. (Techincally, we could've used std::list of hash_set::iterator,
+// and keep a list::iterator in CBAndEventMask to achieve the same effect.
+// However, iterators have two problems: no way to portably invalidate them,
+// and no way to tell whether an iterator is singular or not. The only way to
+// overcome these issues is to keep bools in both places, but that throws off
+// memory alignment (up to 7 wasted bytes for each bool). The extra level of
+// indirection will also likely be less cache friendly. Direct manipulation
+// of link pointers makes it easier to retrieve the CBAndEventMask from the
+// list, easier to check whether an CBAndEventMask is in the list, uses less
+// memory (save 32 bytes/fd), and does not affect cache usage (we need to
+// read in the struct to use the callback anyway).)
+// - Embed the fd directly into CBAndEventMask and switch to using hash_set.
+// This removes the need to store hash_map::iterator in the list just so that
+// we can get both the fd and the callback.
+// - The ready list is "one shot": each entry is removed before OnEvent is
+// called. This removes the mutation-while-iterating problem.
+// - Use two lists to keep track of callbacks. The ready_list_ is the one used
+// for registration. Before iteration, the ready_list_ is swapped into the
+// tmp_list_. Once iteration is done, tmp_list_ will be empty, and
+// ready_list_ will have all the new ready fds.
+
+// The size we use for buffers passed to strerror_r
+static const int kErrorBufferSize = 256;
+
+namespace epoll_server {
+
+template <typename T>
+class AutoReset {
+ public:
+ AutoReset(T* scoped_variable, T new_value)
+ : scoped_variable_(scoped_variable),
+ original_value_(std::move(*scoped_variable)) {
+ *scoped_variable_ = std::move(new_value);
+ }
+ AutoReset(const AutoReset&) = delete;
+ AutoReset& operator=(const AutoReset&) = delete;
+
+ ~AutoReset() { *scoped_variable_ = std::move(original_value_); }
+
+ private:
+ T* scoped_variable_;
+ T original_value_;
+};
+
+// Clears the pipe and returns. Used for waking the epoll server up.
+class ReadPipeCallback : public EpollCallbackInterface {
+ public:
+ void OnEvent(int fd, EpollEvent* event) override {
+ DCHECK(event->in_events == EPOLLIN);
+ int data;
+ int data_read = 1;
+ // Read until the pipe is empty.
+ while (data_read > 0) {
+ data_read = read(fd, &data, sizeof(data));
+ }
+ }
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {}
+ void OnRegistration(SimpleEpollServer*, int, int) override {}
+ void OnModification(int, int) override {} // COV_NF_LINE
+ void OnUnregistration(int, bool) override {} // COV_NF_LINE
+ std::string Name() const override { return "ReadPipeCallback"; }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+SimpleEpollServer::SimpleEpollServer()
+ : epoll_fd_(epoll_create(1024)),
+ timeout_in_us_(0),
+ recorded_now_in_us_(0),
+ ready_list_size_(0),
+ wake_cb_(new ReadPipeCallback),
+ read_fd_(-1),
+ write_fd_(-1),
+ in_wait_for_events_and_execute_callbacks_(false),
+ in_shutdown_(false),
+ last_delay_in_usec_(0) {
+ // ensure that the epoll_fd_ is valid.
+ CHECK_NE(epoll_fd_, -1);
+ LIST_INIT(&ready_list_);
+ LIST_INIT(&tmp_list_);
+
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ // Unfortunately, it is impossible to test any such initialization in
+ // a constructor (as virtual methods do not yet work).
+ // This -could- be solved by moving initialization to an outside
+ // call...
+ int saved_errno = errno;
+ char buf[kErrorBufferSize];
+ EPOLL_LOG(FATAL) << "Error " << saved_errno << " in pipe(): "
+ << strerror_r(saved_errno, buf, sizeof(buf));
+ }
+ read_fd_ = pipe_fds[0];
+ write_fd_ = pipe_fds[1];
+ RegisterFD(read_fd_, wake_cb_.get(), EPOLLIN);
+}
+
+void SimpleEpollServer::CleanupFDToCBMap() {
+ auto cb_iter = cb_map_.begin();
+ while (cb_iter != cb_map_.end()) {
+ int fd = cb_iter->fd;
+ CB* cb = cb_iter->cb;
+
+ cb_iter->in_use = true;
+ if (cb) {
+ cb->OnShutdown(this, fd);
+ }
+
+ cb_map_.erase(cb_iter);
+ cb_iter = cb_map_.begin();
+ }
+}
+
+void SimpleEpollServer::CleanupTimeToAlarmCBMap() {
+ TimeToAlarmCBMap::iterator erase_it;
+
+ // Call OnShutdown() on alarms. Note that the structure of the loop
+ // is similar to the structure of loop in the function HandleAlarms()
+ for (auto i = alarm_map_.begin(); i != alarm_map_.end();) {
+ // Note that OnShutdown() can call UnregisterAlarm() on
+ // other iterators. OnShutdown() should not call UnregisterAlarm()
+ // on self because by definition the iterator is not valid any more.
+ i->second->OnShutdown(this);
+ erase_it = i;
+ ++i;
+ alarm_map_.erase(erase_it);
+ }
+}
+
+SimpleEpollServer::~SimpleEpollServer() {
+ DCHECK_EQ(in_shutdown_, false);
+ in_shutdown_ = true;
+#ifdef EPOLL_SERVER_EVENT_TRACING
+ EPOLL_LOG(INFO) << "\n" << event_recorder_;
+#endif
+ EPOLL_VLOG(2) << "Shutting down epoll server ";
+ CleanupFDToCBMap();
+
+ LIST_INIT(&ready_list_);
+ LIST_INIT(&tmp_list_);
+
+ CleanupTimeToAlarmCBMap();
+
+ close(read_fd_);
+ close(write_fd_);
+ close(epoll_fd_);
+}
+
+// Whether a CBAandEventMask is on the ready list is determined by a non-NULL
+// le_prev pointer (le_next being NULL indicates end of list).
+inline void SimpleEpollServer::AddToReadyList(CBAndEventMask* cb_and_mask) {
+ if (cb_and_mask->entry.le_prev == NULL) {
+ LIST_INSERT_HEAD(&ready_list_, cb_and_mask, entry);
+ ++ready_list_size_;
+ }
+}
+
+inline void SimpleEpollServer::RemoveFromReadyList(
+ const CBAndEventMask& cb_and_mask) {
+ if (cb_and_mask.entry.le_prev != NULL) {
+ LIST_REMOVE(&cb_and_mask, entry);
+ // Clean up all the ready list states. Don't bother with the other fields
+ // as they are initialized when the CBAandEventMask is added to the ready
+ // list. This saves a few cycles in the inner loop.
+ cb_and_mask.entry.le_prev = NULL;
+ --ready_list_size_;
+ if (ready_list_size_ == 0) {
+ DCHECK(ready_list_.lh_first == NULL);
+ DCHECK(tmp_list_.lh_first == NULL);
+ }
+ }
+}
+
+void SimpleEpollServer::RegisterFD(int fd, CB* cb, int event_mask) {
+ CHECK(cb);
+ EPOLL_VLOG(3) << "RegisterFD fd=" << fd << " event_mask=" << event_mask;
+ auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
+ if (cb_map_.end() != fd_i) {
+ // do we just abort, or do we just unregister the other callback?
+ // for now, lets just unregister the other callback.
+
+ // unregister any callback that may already be registered for this FD.
+ CB* other_cb = fd_i->cb;
+ if (other_cb) {
+ // Must remove from the ready list before erasing.
+ RemoveFromReadyList(*fd_i);
+ other_cb->OnUnregistration(fd, true);
+ ModFD(fd, event_mask);
+ } else {
+ // already unregistered, so just recycle the node.
+ AddFD(fd, event_mask);
+ }
+ fd_i->cb = cb;
+ fd_i->event_mask = event_mask;
+ fd_i->events_to_fake = 0;
+ } else {
+ AddFD(fd, event_mask);
+ cb_map_.insert(CBAndEventMask(cb, event_mask, fd));
+ }
+
+ // set the FD to be non-blocking.
+ SetNonblocking(fd);
+
+ cb->OnRegistration(this, fd, event_mask);
+}
+
+void SimpleEpollServer::SetNonblocking(int fd) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ int saved_errno = errno;
+ char buf[kErrorBufferSize];
+ EPOLL_LOG(FATAL) << "Error " << saved_errno << " doing fcntl(" << fd
+ << ", F_GETFL, 0): "
+ << strerror_r(saved_errno, buf, sizeof(buf));
+ }
+ if (!(flags & O_NONBLOCK)) {
+ int saved_flags = flags;
+ flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (flags == -1) {
+ // bad.
+ int saved_errno = errno;
+ char buf[kErrorBufferSize];
+ EPOLL_LOG(FATAL) << "Error " << saved_errno << " doing fcntl(" << fd
+ << ", F_SETFL, " << saved_flags
+ << "): " << strerror_r(saved_errno, buf, sizeof(buf));
+ }
+ }
+}
+
+int SimpleEpollServer::epoll_wait_impl(int epfd, struct epoll_event* events,
+ int max_events, int timeout_in_ms) {
+ return epoll_wait(epfd, events, max_events, timeout_in_ms);
+}
+
+void SimpleEpollServer::RegisterFDForWrite(int fd, CB* cb) {
+ RegisterFD(fd, cb, EPOLLOUT);
+}
+
+void SimpleEpollServer::RegisterFDForReadWrite(int fd, CB* cb) {
+ RegisterFD(fd, cb, EPOLLIN | EPOLLOUT);
+}
+
+void SimpleEpollServer::RegisterFDForRead(int fd, CB* cb) {
+ RegisterFD(fd, cb, EPOLLIN);
+}
+
+void SimpleEpollServer::UnregisterFD(int fd) {
+ auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
+ if (cb_map_.end() == fd_i || fd_i->cb == NULL) {
+ // Doesn't exist in server, or has gone through UnregisterFD once and still
+ // inside the callchain of OnEvent.
+ return;
+ }
+#ifdef EPOLL_SERVER_EVENT_TRACING
+ event_recorder_.RecordUnregistration(fd);
+#endif
+ CB* cb = fd_i->cb;
+ // Since the links are embedded within the struct, we must remove it from the
+ // list before erasing it from the hash_set.
+ RemoveFromReadyList(*fd_i);
+ DelFD(fd);
+ cb->OnUnregistration(fd, false);
+ // fd_i->cb is NULL if that fd is unregistered inside the callchain of
+ // OnEvent. Since the SimpleEpollServer needs a valid CBAndEventMask after
+ // OnEvent returns in order to add it to the ready list, we cannot have
+ // UnregisterFD erase the entry if it is in use. Thus, a NULL fd_i->cb is used
+ // as a condition that tells the SimpleEpollServer that this entry is unused
+ // at a later point.
+ if (!fd_i->in_use) {
+ cb_map_.erase(fd_i);
+ } else {
+ // Remove all trace of the registration, and just keep the node alive long
+ // enough so the code that calls OnEvent doesn't have to worry about
+ // figuring out whether the CBAndEventMask is valid or not.
+ fd_i->cb = NULL;
+ fd_i->event_mask = 0;
+ fd_i->events_to_fake = 0;
+ }
+}
+
+void SimpleEpollServer::ModifyCallback(int fd, int event_mask) {
+ ModifyFD(fd, ~0, event_mask);
+}
+
+void SimpleEpollServer::StopRead(int fd) { ModifyFD(fd, EPOLLIN, 0); }
+
+void SimpleEpollServer::StartRead(int fd) { ModifyFD(fd, 0, EPOLLIN); }
+
+void SimpleEpollServer::StopWrite(int fd) { ModifyFD(fd, EPOLLOUT, 0); }
+
+void SimpleEpollServer::StartWrite(int fd) { ModifyFD(fd, 0, EPOLLOUT); }
+
+void SimpleEpollServer::HandleEvent(int fd, int event_mask) {
+#ifdef EPOLL_SERVER_EVENT_TRACING
+ event_recorder_.RecordEpollEvent(fd, event_mask);
+#endif
+ auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
+ if (fd_i == cb_map_.end() || fd_i->cb == NULL) {
+ // Ignore the event.
+ // This could occur if epoll() returns a set of events, and
+ // while processing event A (earlier) we removed the callback
+ // for event B (and are now processing event B).
+ return;
+ }
+ fd_i->events_asserted = event_mask;
+ CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i);
+ AddToReadyList(cb_and_mask);
+}
+
+void SimpleEpollServer::WaitForEventsAndExecuteCallbacks() {
+ if (in_wait_for_events_and_execute_callbacks_) {
+ EPOLL_LOG(DFATAL) << "Attempting to call WaitForEventsAndExecuteCallbacks"
+ " when an ancestor to the current function is already"
+ " WaitForEventsAndExecuteCallbacks!";
+ // The line below is actually tested, but in coverage mode,
+ // we never see it.
+ return; // COV_NF_LINE
+ }
+ AutoReset<bool> recursion_guard(&in_wait_for_events_and_execute_callbacks_,
+ true);
+ if (alarm_map_.empty()) {
+ // no alarms, this is business as usual.
+ WaitForEventsAndCallHandleEvents(timeout_in_us_, events_, events_size_);
+ recorded_now_in_us_ = 0;
+ return;
+ }
+
+ // store the 'now'. If we recomputed 'now' every iteration
+ // down below, then we might never exit that loop-- any
+ // long-running alarms might install other long-running
+ // alarms, etc. By storing it here now, we ensure that
+ // a more reasonable amount of work is done here.
+ int64_t now_in_us = NowInUsec();
+
+ // Get the first timeout from the alarm_map where it is
+ // stored in absolute time.
+ int64_t next_alarm_time_in_us = alarm_map_.begin()->first;
+ EPOLL_VLOG(4) << "next_alarm_time = " << next_alarm_time_in_us
+ << " now = " << now_in_us
+ << " timeout_in_us = " << timeout_in_us_;
+
+ int64_t wait_time_in_us;
+ int64_t alarm_timeout_in_us = next_alarm_time_in_us - now_in_us;
+
+ // If the next alarm is sooner than the default timeout, or if there is no
+ // timeout (timeout_in_us_ == -1), wake up when the alarm should fire.
+ // Otherwise use the default timeout.
+ if (alarm_timeout_in_us < timeout_in_us_ || timeout_in_us_ < 0) {
+ wait_time_in_us = std::max(alarm_timeout_in_us, static_cast<int64_t>(0));
+ } else {
+ wait_time_in_us = timeout_in_us_;
+ }
+
+ EPOLL_VLOG(4) << "wait_time_in_us = " << wait_time_in_us;
+
+ // wait for events.
+
+ WaitForEventsAndCallHandleEvents(wait_time_in_us, events_, events_size_);
+ CallAndReregisterAlarmEvents();
+ recorded_now_in_us_ = 0;
+}
+
+void SimpleEpollServer::SetFDReady(int fd, int events_to_fake) {
+ auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
+ if (cb_map_.end() != fd_i && fd_i->cb != NULL) {
+ // This const_cast is necessary for LIST_HEAD_INSERT to work. Declaring
+ // entry mutable is insufficient because LIST_HEAD_INSERT assigns the
+ // forward pointer of the list head to the current cb_and_mask, and the
+ // compiler complains that it can't assign a const T* to a T*.
+ CBAndEventMask* cb_and_mask = const_cast<CBAndEventMask*>(&*fd_i);
+ // Note that there is no clearly correct behavior here when
+ // cb_and_mask->events_to_fake != 0 and this function is called.
+ // Of the two operations:
+ // cb_and_mask->events_to_fake = events_to_fake
+ // cb_and_mask->events_to_fake |= events_to_fake
+ // the first was picked because it discourages users from calling
+ // SetFDReady repeatedly to build up the correct event set as it is more
+ // efficient to call SetFDReady once with the correct, final mask.
+ cb_and_mask->events_to_fake = events_to_fake;
+ AddToReadyList(cb_and_mask);
+ }
+}
+
+void SimpleEpollServer::SetFDNotReady(int fd) {
+ auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
+ if (cb_map_.end() != fd_i) {
+ RemoveFromReadyList(*fd_i);
+ }
+}
+
+bool SimpleEpollServer::IsFDReady(int fd) const {
+ auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
+ return (cb_map_.end() != fd_i && fd_i->cb != NULL &&
+ fd_i->entry.le_prev != NULL);
+}
+
+void SimpleEpollServer::VerifyReadyList() const {
+ int count = 0;
+ CBAndEventMask* cur = ready_list_.lh_first;
+ for (; cur; cur = cur->entry.le_next) {
+ ++count;
+ }
+ for (cur = tmp_list_.lh_first; cur; cur = cur->entry.le_next) {
+ ++count;
+ }
+ CHECK_EQ(ready_list_size_, count) << "Ready list size does not match count";
+}
+
+void SimpleEpollServer::RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac) {
+ EPOLL_VLOG(4) << "RegisteringAlarm " << ac << " at : " << timeout_time_in_us;
+ CHECK(ac);
+ if (all_alarms_.find(ac) != all_alarms_.end()) {
+ EPOLL_BUG << "Alarm already exists";
+ }
+
+ auto alarm_iter = alarm_map_.insert(std::make_pair(timeout_time_in_us, ac));
+
+ all_alarms_.insert(ac);
+ // Pass the iterator to the EpollAlarmCallbackInterface.
+ ac->OnRegistration(alarm_iter, this);
+}
+
+// Unregister a specific alarm callback: iterator_token must be a
+// valid iterator. The caller must ensure the validity of the iterator.
+void SimpleEpollServer::UnregisterAlarm(const AlarmRegToken& iterator_token) {
+ AlarmCB* cb = iterator_token->second;
+ EPOLL_VLOG(4) << "UnregisteringAlarm " << cb;
+ alarm_map_.erase(iterator_token);
+ all_alarms_.erase(cb);
+ cb->OnUnregistration();
+}
+
+SimpleEpollServer::AlarmRegToken SimpleEpollServer::ReregisterAlarm(
+ SimpleEpollServer::AlarmRegToken iterator_token,
+ int64_t timeout_time_in_us) {
+ AlarmCB* cb = iterator_token->second;
+ alarm_map_.erase(iterator_token);
+ return alarm_map_.emplace(timeout_time_in_us, cb);
+}
+
+int SimpleEpollServer::NumFDsRegistered() const {
+ DCHECK_GE(cb_map_.size(), 1u);
+ // Omit the internal FD (read_fd_)
+ return cb_map_.size() - 1;
+}
+
+void SimpleEpollServer::Wake() {
+ char data = 'd'; // 'd' is for data. It's good enough for me.
+ int rv = write(write_fd_, &data, 1);
+ DCHECK_EQ(rv, 1);
+}
+
+int64_t SimpleEpollServer::NowInUsec() const { return WallTimeNowInUsec(); }
+
+int64_t SimpleEpollServer::ApproximateNowInUsec() const {
+ if (recorded_now_in_us_ != 0) {
+ return recorded_now_in_us_;
+ }
+ return this->NowInUsec();
+}
+
+std::string SimpleEpollServer::EventMaskToString(int event_mask) {
+ std::string s;
+ if (event_mask & EPOLLIN) s += "EPOLLIN ";
+ if (event_mask & EPOLLPRI) s += "EPOLLPRI ";
+ if (event_mask & EPOLLOUT) s += "EPOLLOUT ";
+ if (event_mask & EPOLLRDNORM) s += "EPOLLRDNORM ";
+ if (event_mask & EPOLLRDBAND) s += "EPOLLRDBAND ";
+ if (event_mask & EPOLLWRNORM) s += "EPOLLWRNORM ";
+ if (event_mask & EPOLLWRBAND) s += "EPOLLWRBAND ";
+ if (event_mask & EPOLLMSG) s += "EPOLLMSG ";
+ if (event_mask & EPOLLERR) s += "EPOLLERR ";
+ if (event_mask & EPOLLHUP) s += "EPOLLHUP ";
+ if (event_mask & EPOLLONESHOT) s += "EPOLLONESHOT ";
+ if (event_mask & EPOLLET) s += "EPOLLET ";
+ return s;
+}
+
+void SimpleEpollServer::LogStateOnCrash() {
+ EPOLL_LOG(ERROR)
+ << "-------------------Epoll Server-------------------------";
+ EPOLL_LOG(ERROR) << "Epoll server " << this << " polling on fd " << epoll_fd_;
+ EPOLL_LOG(ERROR) << "timeout_in_us_: " << timeout_in_us_;
+
+ // Log sessions with alarms.
+ EPOLL_LOG(ERROR) << alarm_map_.size() << " alarms registered.";
+ for (auto it = alarm_map_.begin(); it != alarm_map_.end(); ++it) {
+ const bool skipped =
+ alarms_reregistered_and_should_be_skipped_.find(it->second) !=
+ alarms_reregistered_and_should_be_skipped_.end();
+ EPOLL_LOG(ERROR) << "Alarm " << it->second << " registered at time "
+ << it->first << " and should be skipped = " << skipped;
+ }
+
+ EPOLL_LOG(ERROR) << cb_map_.size() << " fd callbacks registered.";
+ for (auto it = cb_map_.begin(); it != cb_map_.end(); ++it) {
+ EPOLL_LOG(ERROR) << "fd: " << it->fd << " with mask " << it->event_mask
+ << " registered with cb: " << it->cb;
+ }
+ EPOLL_LOG(ERROR)
+ << "-------------------/Epoll Server------------------------";
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+void SimpleEpollServer::DelFD(int fd) const {
+ struct epoll_event ee;
+ memset(&ee, 0, sizeof(ee));
+#ifdef EPOLL_SERVER_EVENT_TRACING
+ event_recorder_.RecordFDMaskEvent(fd, 0, "DelFD");
+#endif
+ if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee)) {
+ int saved_errno = errno;
+ char buf[kErrorBufferSize];
+ EPOLL_LOG(FATAL) << "Epoll set removal error for fd " << fd << ": "
+ << strerror_r(saved_errno, buf, sizeof(buf));
+ }
+}
+
+////////////////////////////////////////
+
+void SimpleEpollServer::AddFD(int fd, int event_mask) const {
+ struct epoll_event ee;
+ memset(&ee, 0, sizeof(ee));
+ ee.events = event_mask | EPOLLERR | EPOLLHUP;
+ ee.data.fd = fd;
+#ifdef EPOLL_SERVER_EVENT_TRACING
+ event_recorder_.RecordFDMaskEvent(fd, ee.events, "AddFD");
+#endif
+ if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ee)) {
+ int saved_errno = errno;
+ char buf[kErrorBufferSize];
+ EPOLL_LOG(FATAL) << "Epoll set insertion error for fd " << fd << ": "
+ << strerror_r(saved_errno, buf, sizeof(buf));
+ }
+}
+
+////////////////////////////////////////
+
+void SimpleEpollServer::ModFD(int fd, int event_mask) const {
+ struct epoll_event ee;
+ memset(&ee, 0, sizeof(ee));
+ ee.events = event_mask | EPOLLERR | EPOLLHUP;
+ ee.data.fd = fd;
+#ifdef EPOLL_SERVER_EVENT_TRACING
+ event_recorder_.RecordFDMaskEvent(fd, ee.events, "ModFD");
+#endif
+ EPOLL_VLOG(3) << "modifying fd= " << fd << " "
+ << EventMaskToString(ee.events);
+ if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &ee)) {
+ int saved_errno = errno;
+ char buf[kErrorBufferSize];
+ EPOLL_LOG(FATAL) << "Epoll set modification error for fd " << fd << ": "
+ << strerror_r(saved_errno, buf, sizeof(buf));
+ }
+}
+
+////////////////////////////////////////
+
+void SimpleEpollServer::ModifyFD(int fd, int remove_event, int add_event) {
+ auto fd_i = cb_map_.find(CBAndEventMask(NULL, 0, fd));
+ if (cb_map_.end() == fd_i) {
+ EPOLL_VLOG(2) << "Didn't find the fd " << fd << "in internal structures";
+ return;
+ }
+
+ if (fd_i->cb != NULL) {
+ int& event_mask = fd_i->event_mask;
+ EPOLL_VLOG(3) << "fd= " << fd
+ << " event_mask before: " << EventMaskToString(event_mask);
+ event_mask &= ~remove_event;
+ event_mask |= add_event;
+
+ EPOLL_VLOG(3) << " event_mask after: " << EventMaskToString(event_mask);
+
+ ModFD(fd, event_mask);
+
+ fd_i->cb->OnModification(fd, event_mask);
+ }
+}
+
+void SimpleEpollServer::WaitForEventsAndCallHandleEvents(
+ int64_t timeout_in_us, struct epoll_event events[], int events_size) {
+ if (timeout_in_us == 0 || ready_list_.lh_first != NULL) {
+ // If ready list is not empty, then don't sleep at all.
+ timeout_in_us = 0;
+ } else if (timeout_in_us < 0) {
+ EPOLL_LOG(INFO) << "Negative epoll timeout: " << timeout_in_us
+ << "us; epoll will wait forever for events.";
+ // If timeout_in_us is < 0 we are supposed to Wait forever. This means we
+ // should set timeout_in_us to -1000 so we will
+ // Wait(-1000/1000) == Wait(-1) == Wait forever.
+ timeout_in_us = -1000;
+ } else {
+ // If timeout is specified, and the ready list is empty.
+ if (timeout_in_us < 1000) {
+ timeout_in_us = 1000;
+ }
+ }
+ const int timeout_in_ms = timeout_in_us / 1000;
+ int64_t expected_wakeup_us = NowInUsec() + timeout_in_us;
+
+ int nfds = epoll_wait_impl(epoll_fd_, events, events_size, timeout_in_ms);
+ EPOLL_VLOG(3) << "nfds=" << nfds;
+
+#ifdef EPOLL_SERVER_EVENT_TRACING
+ event_recorder_.RecordEpollWaitEvent(timeout_in_ms, nfds);
+#endif
+
+ // If you're wondering why the NowInUsec() is recorded here, the answer is
+ // simple: If we did it before the epoll_wait_impl, then the max error for
+ // the ApproximateNowInUs() call would be as large as the maximum length of
+ // epoll_wait, which can be arbitrarily long. Since this would make
+ // ApproximateNowInUs() worthless, we instead record the time -after- we've
+ // done epoll_wait, which guarantees that the maximum error is the amount of
+ // time it takes to process all the events generated by epoll_wait.
+ recorded_now_in_us_ = NowInUsec();
+
+ if (timeout_in_us > 0) {
+ int64_t delta = NowInUsec() - expected_wakeup_us;
+ last_delay_in_usec_ = delta > 0 ? delta : 0;
+ } else {
+ // timeout_in_us < 0 means we waited forever until an event;
+ // timeout_in_us == 0 means there was no kernel delay to track.
+ last_delay_in_usec_ = 0;
+ }
+
+ if (nfds > 0) {
+ for (int i = 0; i < nfds; ++i) {
+ int event_mask = events[i].events;
+ int fd = events[i].data.fd;
+ HandleEvent(fd, event_mask);
+ }
+ } else if (nfds < 0) {
+ // Catch interrupted syscall and just ignore it and move on.
+ if (errno != EINTR && errno != 0) {
+ int saved_errno = errno;
+ char buf[kErrorBufferSize];
+ EPOLL_LOG(FATAL) << "Error " << saved_errno << " in epoll_wait: "
+ << strerror_r(saved_errno, buf, sizeof(buf));
+ }
+ }
+
+ // Now run through the ready list.
+ if (ready_list_.lh_first) {
+ CallReadyListCallbacks();
+ }
+}
+
+void SimpleEpollServer::CallReadyListCallbacks() {
+ // Check pre-conditions.
+ DCHECK(tmp_list_.lh_first == NULL);
+ // Swap out the ready_list_ into the tmp_list_ before traversing the list to
+ // enable SetFDReady() to just push new items into the ready_list_.
+ std::swap(ready_list_.lh_first, tmp_list_.lh_first);
+ if (tmp_list_.lh_first) {
+ tmp_list_.lh_first->entry.le_prev = &tmp_list_.lh_first;
+ EpollEvent event(0);
+ while (tmp_list_.lh_first != NULL) {
+ DCHECK_GT(ready_list_size_, 0);
+ CBAndEventMask* cb_and_mask = tmp_list_.lh_first;
+ RemoveFromReadyList(*cb_and_mask);
+
+ event.out_ready_mask = 0;
+ event.in_events =
+ cb_and_mask->events_asserted | cb_and_mask->events_to_fake;
+ // TODO(fenix): get rid of the two separate fields in cb_and_mask.
+ cb_and_mask->events_asserted = 0;
+ cb_and_mask->events_to_fake = 0;
+ {
+ // OnEvent() may call UnRegister, so we set in_use, here. Any
+ // UnRegister call will now simply set the cb to NULL instead of
+ // invalidating the cb_and_mask object (by deleting the object in the
+ // map to which cb_and_mask refers)
+ AutoReset<bool> in_use_guard(&(cb_and_mask->in_use), true);
+ cb_and_mask->cb->OnEvent(cb_and_mask->fd, &event);
+ }
+
+ // Since OnEvent may have called UnregisterFD, we must check here that
+ // the callback is still valid. If it isn't, then UnregisterFD *was*
+ // called, and we should now get rid of the object.
+ if (cb_and_mask->cb == NULL) {
+ cb_map_.erase(*cb_and_mask);
+ } else if (event.out_ready_mask != 0) {
+ cb_and_mask->events_to_fake = event.out_ready_mask;
+ AddToReadyList(cb_and_mask);
+ }
+ }
+ }
+ DCHECK(tmp_list_.lh_first == NULL);
+}
+
+void SimpleEpollServer::CallAndReregisterAlarmEvents() {
+ int64_t now_in_us = recorded_now_in_us_;
+ DCHECK_NE(0, recorded_now_in_us_);
+
+ TimeToAlarmCBMap::iterator erase_it;
+
+ // execute alarms.
+ for (auto i = alarm_map_.begin(); i != alarm_map_.end();) {
+ if (i->first > now_in_us) {
+ break;
+ }
+ AlarmCB* cb = i->second;
+ // Execute the OnAlarm() only if we did not register
+ // it in this loop itself.
+ const bool added_in_this_round =
+ alarms_reregistered_and_should_be_skipped_.find(cb) !=
+ alarms_reregistered_and_should_be_skipped_.end();
+ if (added_in_this_round) {
+ ++i;
+ continue;
+ }
+ all_alarms_.erase(cb);
+ const int64_t new_timeout_time_in_us = cb->OnAlarm();
+
+ erase_it = i;
+ ++i;
+ alarm_map_.erase(erase_it);
+
+ if (new_timeout_time_in_us > 0) {
+ // We add to hash_set only if the new timeout is <= now_in_us.
+ // if timeout is > now_in_us then we have no fear that this alarm
+ // can be reexecuted in this loop, and hence we do not need to
+ // worry about a recursive loop.
+ EPOLL_DVLOG(3) << "Reregistering alarm "
+ << " " << cb << " " << new_timeout_time_in_us << " "
+ << now_in_us;
+ if (new_timeout_time_in_us <= now_in_us) {
+ alarms_reregistered_and_should_be_skipped_.insert(cb);
+ }
+ RegisterAlarm(new_timeout_time_in_us, cb);
+ }
+ }
+ alarms_reregistered_and_should_be_skipped_.clear();
+}
+
+EpollAlarm::EpollAlarm() : eps_(NULL), registered_(false) {}
+
+EpollAlarm::~EpollAlarm() { UnregisterIfRegistered(); }
+
+int64_t EpollAlarm::OnAlarm() {
+ registered_ = false;
+ return 0;
+}
+
+void EpollAlarm::OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
+ SimpleEpollServer* eps) {
+ DCHECK_EQ(false, registered_);
+
+ token_ = token;
+ eps_ = eps;
+ registered_ = true;
+}
+
+void EpollAlarm::OnUnregistration() { registered_ = false; }
+
+void EpollAlarm::OnShutdown(SimpleEpollServer* eps) {
+ registered_ = false;
+ eps_ = NULL;
+}
+
+// If the alarm was registered, unregister it.
+void EpollAlarm::UnregisterIfRegistered() {
+ if (!registered_) {
+ return;
+ }
+
+ eps_->UnregisterAlarm(token_);
+}
+
+void EpollAlarm::ReregisterAlarm(int64_t timeout_time_in_us) {
+ DCHECK(registered_);
+ token_ = eps_->ReregisterAlarm(token_, timeout_time_in_us);
+}
+
+} // namespace epoll_server
diff --git a/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.h b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.h
new file mode 100644
index 00000000000..0f3dcdc914c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server.h
@@ -0,0 +1,1051 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_EPOLL_SERVER_H_
+#define QUICHE_EPOLL_SERVER_H_
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/queue.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+// #define EPOLL_SERVER_EVENT_TRACING 1
+//
+// Defining EPOLL_SERVER_EVENT_TRACING
+// causes code to exist which didn't before.
+// This code tracks each event generated by the epollserver,
+// as well as providing a per-fd-registered summary of
+// events. Note that enabling this code vastly slows
+// down operations, and uses substantially more
+// memory. For these reasons, it should only be enabled by developers doing
+// development at their workstations.
+//
+// A structure called 'EventRecorder' will exist when
+// the macro is defined. See the EventRecorder class interface
+// within the SimpleEpollServer class for more details.
+#ifdef EPOLL_SERVER_EVENT_TRACING
+#include <ostream>
+#endif
+
+#include <sys/epoll.h>
+
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_export.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_logging.h"
+
+namespace epoll_server {
+
+class SimpleEpollServer;
+class EpollAlarmCallbackInterface;
+class ReadPipeCallback;
+
+struct EpollEvent {
+ EpollEvent(int events) : in_events(events), out_ready_mask(0) {}
+
+ int in_events; // incoming events
+ int out_ready_mask; // the new event mask for ready list (0 means don't
+ // get on the ready list). This field is always
+ // initialized to 0 when the event is passed to
+ // OnEvent.
+};
+
+// Callbacks which go into SimpleEpollServers are expected to derive from this
+// class.
+class EpollCallbackInterface {
+ public:
+ // Summary:
+ // Called when the callback is registered into a SimpleEpollServer.
+ // Args:
+ // eps - the poll server into which this callback was registered
+ // fd - the file descriptor which was registered
+ // event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc)
+ // which was registered (and will initially be used
+ // in the epoll() calls)
+ virtual void OnRegistration(SimpleEpollServer* eps, int fd,
+ int event_mask) = 0;
+
+ // Summary:
+ // Called when the event_mask is modified (for a file-descriptor)
+ // Args:
+ // fd - the file descriptor which was registered
+ // event_mask - the event mask (composed of EPOLLIN, EPOLLOUT, etc)
+ // which was is now curren (and will be used
+ // in subsequent epoll() calls)
+ virtual void OnModification(int fd, int event_mask) = 0;
+
+ // Summary:
+ // Called whenever an event occurs on the file-descriptor.
+ // This is where the bulk of processing is expected to occur.
+ // Args:
+ // fd - the file descriptor which was registered
+ // event - a struct that contains the event mask (composed of EPOLLIN,
+ // EPOLLOUT, etc), a flag that indicates whether this is a true
+ // epoll_wait event vs one from the ready list, and an output
+ // parameter for OnEvent to inform the SimpleEpollServer whether to
+ // put this fd on the ready list.
+ virtual void OnEvent(int fd, EpollEvent* event) = 0;
+
+ // Summary:
+ // Called when the file-descriptor is unregistered from the poll-server.
+ // Args:
+ // fd - the file descriptor which was registered, and of this call, is now
+ // unregistered.
+ // replaced - If true, this callback is being replaced by another, otherwise
+ // it is simply being removed.
+ virtual void OnUnregistration(int fd, bool replaced) = 0;
+
+ // Summary:
+ // Called when the epoll server is shutting down. This is different from
+ // OnUnregistration because the subclass may want to clean up memory.
+ // This is called in leiu of OnUnregistration.
+ // Args:
+ // fd - the file descriptor which was registered.
+ virtual void OnShutdown(SimpleEpollServer* eps, int fd) = 0;
+
+ // Summary:
+ // Returns a name describing the class for use in debug/error reporting.
+ virtual std::string Name() const = 0;
+
+ virtual ~EpollCallbackInterface() {}
+
+ protected:
+ EpollCallbackInterface() {}
+};
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+class EPOLL_EXPORT_PRIVATE SimpleEpollServer {
+ public:
+ typedef EpollAlarmCallbackInterface AlarmCB;
+ typedef EpollCallbackInterface CB;
+
+ typedef std::multimap<int64_t, AlarmCB*> TimeToAlarmCBMap;
+ typedef TimeToAlarmCBMap::iterator AlarmRegToken;
+
+ // Summary:
+ // Constructor:
+ // By default, we don't wait any amount of time for events, and
+ // we suggest to the epoll-system that we're going to use on-the-order
+ // of 1024 FDs.
+ SimpleEpollServer();
+
+ SimpleEpollServer(const SimpleEpollServer&) = delete;
+ SimpleEpollServer operator=(const SimpleEpollServer&) = delete;
+
+ ////////////////////////////////////////
+
+ // Destructor
+ virtual ~SimpleEpollServer();
+
+ ////////////////////////////////////////
+
+ // Summary
+ // Register a callback to be called whenever an event contained
+ // in the set of events included in event_mask occurs on the
+ // file-descriptor 'fd'
+ //
+ // Note that only one callback is allowed to be registered for
+ // any specific file-decriptor.
+ //
+ // If a callback is registered for a file-descriptor which has already
+ // been registered, then the previous callback is unregistered with
+ // the 'replaced' flag set to true. I.e. the previous callback's
+ // OnUnregistration() function is called like so:
+ // OnUnregistration(fd, true);
+ //
+ // The epoll server does NOT take on ownership of the callback: the callback
+ // creator is responsible for managing that memory.
+ //
+ // Args:
+ // fd - a valid file-descriptor
+ // cb - an instance of a subclass of EpollCallbackInterface
+ // event_mask - a combination of (EPOLLOUT, EPOLLIN.. etc) indicating
+ // the events for which the callback would like to be
+ // called.
+ virtual void RegisterFD(int fd, CB* cb, int event_mask);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // A shortcut for RegisterFD which sets things up such that the
+ // callback is called when 'fd' is available for writing.
+ // Args:
+ // fd - a valid file-descriptor
+ // cb - an instance of a subclass of EpollCallbackInterface
+ virtual void RegisterFDForWrite(int fd, CB* cb);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // A shortcut for RegisterFD which sets things up such that the
+ // callback is called when 'fd' is available for reading or writing.
+ // Args:
+ // fd - a valid file-descriptor
+ // cb - an instance of a subclass of EpollCallbackInterface
+ virtual void RegisterFDForReadWrite(int fd, CB* cb);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // A shortcut for RegisterFD which sets things up such that the
+ // callback is called when 'fd' is available for reading.
+ // Args:
+ // fd - a valid file-descriptor
+ // cb - an instance of a subclass of EpollCallbackInterface
+ virtual void RegisterFDForRead(int fd, CB* cb);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Removes the FD and the associated callback from the pollserver.
+ // If the callback is registered with other FDs, they will continue
+ // to be processed using the callback without modification.
+ // If the file-descriptor specified is not registered in the
+ // epoll_server, then nothing happens as a result of this call.
+ // Args:
+ // fd - the file-descriptor which should no-longer be monitored.
+ virtual void UnregisterFD(int fd);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Modifies the event mask for the file-descriptor, replacing
+ // the old event_mask with the new one specified here.
+ // If the file-descriptor specified is not registered in the
+ // epoll_server, then nothing happens as a result of this call.
+ // Args:
+ // fd - the fd whose event mask should be modified.
+ // event_mask - the new event mask.
+ virtual void ModifyCallback(int fd, int event_mask);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Modifies the event mask for the file-descriptor such that we
+ // no longer request events when 'fd' is readable.
+ // If the file-descriptor specified is not registered in the
+ // epoll_server, then nothing happens as a result of this call.
+ // Args:
+ // fd - the fd whose event mask should be modified.
+ virtual void StopRead(int fd);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Modifies the event mask for the file-descriptor such that we
+ // request events when 'fd' is readable.
+ // If the file-descriptor specified is not registered in the
+ // epoll_server, then nothing happens as a result of this call.
+ // Args:
+ // fd - the fd whose event mask should be modified.
+ virtual void StartRead(int fd);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Modifies the event mask for the file-descriptor such that we
+ // no longer request events when 'fd' is writable.
+ // If the file-descriptor specified is not registered in the
+ // epoll_server, then nothing happens as a result of this call.
+ // Args:
+ // fd - the fd whose event mask should be modified.
+ virtual void StopWrite(int fd);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Modifies the event mask for the file-descriptor such that we
+ // request events when 'fd' is writable.
+ // If the file-descriptor specified is not registered in the
+ // epoll_server, then nothing happens as a result of this call.
+ // Args:
+ // fd - the fd whose event mask should be modified.
+ virtual void StartWrite(int fd);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Looks up the callback associated with the file-descriptor 'fd'.
+ // If a callback is associated with this file-descriptor, then
+ // it's OnEvent() method is called with the file-descriptor 'fd',
+ // and event_mask 'event_mask'
+ //
+ // If no callback is registered for this file-descriptor, nothing
+ // will happen as a result of this call.
+ //
+ // This function is used internally by the SimpleEpollServer, but is
+ // available publicly so that events might be 'faked'. Calling
+ // this function with an fd and event_mask is equivalent (as far
+ // as the callback is concerned) to having a real event generated
+ // by epoll (except, of course, that read(), etc won't necessarily
+ // be able to read anything)
+ // Args:
+ // fd - the file-descriptor on which an event has occurred.
+ // event_mask - a bitmask representing the events which have occurred
+ // on/for this fd. This bitmask is composed of
+ // POLLIN, POLLOUT, etc.
+ //
+ void HandleEvent(int fd, int event_mask);
+
+ // Summary:
+ // Call this when you want the pollserver to
+ // wait for events and execute the callbacks associated with
+ // the file-descriptors on which those events have occurred.
+ // Depending on the value of timeout_in_us_, this may or may
+ // not return immediately. Please reference the set_timeout()
+ // function for the specific behaviour.
+ virtual void WaitForEventsAndExecuteCallbacks();
+
+ // Summary:
+ // When an fd is registered to use edge trigger notification, the ready
+ // list can be used to simulate level trigger semantics. Edge trigger
+ // registration doesn't send an initial event, and only rising edge (going
+ // from blocked to unblocked) events are sent. A callback can put itself on
+ // the ready list by calling SetFDReady() after calling RegisterFD(). The
+ // OnEvent method of all callbacks associated with the fds on the ready
+ // list will be called immediately after processing the events returned by
+ // epoll_wait(). The fd is removed from the ready list before the
+ // callback's OnEvent() method is invoked. To stay on the ready list, the
+ // OnEvent() (or some function in that call chain) must call SetFDReady
+ // again. When a fd is unregistered using UnregisterFD(), the fd is
+ // automatically removed from the ready list.
+ //
+ // When the callback for a edge triggered fd hits the falling edge (about
+ // to block, either because of it got an EAGAIN, or had a short read/write
+ // operation), it should remove itself from the ready list using
+ // SetFDNotReady() (since OnEvent cannot distinguish between invocation
+ // from the ready list vs from a normal epoll event). All four ready list
+ // methods are safe to be called within the context of the callbacks.
+ //
+ // Since the ready list invokes EpollCallbackInterface::OnEvent, only fds
+ // that are registered with the SimpleEpollServer will be put on the ready
+ // list. SetFDReady() and SetFDNotReady() will do nothing if the
+ // SimpleEpollServer doesn't know about the fd passed in.
+ //
+ // Since the ready list cannot reliably determine proper set of events
+ // which should be sent to the callback, SetFDReady() requests the caller
+ // to provide the ready list with the event mask, which will be used later
+ // when OnEvent() is invoked by the ready list. Hence, the event_mask
+ // passedto SetFDReady() does not affect the actual epoll registration of
+ // the fd with the kernel. If a fd is already put on the ready list, and
+ // SetFDReady() is called again for that fd with a different event_mask,
+ // the event_mask will be updated.
+ virtual void SetFDReady(int fd, int events_to_fake);
+
+ virtual void SetFDNotReady(int fd);
+
+ // Summary:
+ // IsFDReady(), ReadyListSize(), and VerifyReadyList are intended as
+ // debugging tools and for writing unit tests.
+ // ISFDReady() returns whether a fd is in the ready list.
+ // ReadyListSize() returns the number of fds on the ready list.
+ // VerifyReadyList() checks the consistency of internal data structure. It
+ // will CHECK if it finds an error.
+ virtual bool IsFDReady(int fd) const;
+
+ size_t ReadyListSize() const { return ready_list_size_; }
+
+ void VerifyReadyList() const;
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Registers an alarm 'ac' to go off at time 'timeout_time_in_us'.
+ // If the callback returns a positive number from its OnAlarm() function,
+ // then the callback will be re-registered at that time, else the alarm
+ // owner is responsible for freeing up memory.
+ //
+ // Important: A give AlarmCB* can not be registered again if it is already
+ // registered. If a user wants to register a callback again it should first
+ // unregister the previous callback before calling RegisterAlarm again.
+ // Args:
+ // timeout_time_in_us - the absolute time at which the alarm should go off
+ // ac - the alarm which will be called.
+ virtual void RegisterAlarm(int64_t timeout_time_in_us, AlarmCB* ac);
+
+ // Summary:
+ // Registers an alarm 'ac' to go off at time: (ApproximateNowInUs() +
+ // delta_in_us). While this is somewhat less accurate (see the description
+ // for ApproximateNowInUs() to see how 'approximate'), the error is never
+ // worse than the amount of time it takes to process all events in one
+ // WaitForEvents. As with 'RegisterAlarm()', if the callback returns a
+ // positive number from its OnAlarm() function, then the callback will be
+ // re-registered at that time, else the alarm owner is responsible for
+ // freeing up memory.
+ // Note that this function is purely a convienence. The
+ // same thing may be accomplished by using RegisterAlarm with
+ // ApproximateNowInUs() directly.
+ //
+ // Important: A give AlarmCB* can not be registered again if it is already
+ // registered. If a user wants to register a callback again it should first
+ // unregister the previous callback before calling RegisterAlarm again.
+ // Args:
+ // delta_in_us - the delta in microseconds from the ApproximateTimeInUs() at
+ // which point the alarm should go off.
+ // ac - the alarm which will be called.
+ void RegisterAlarmApproximateDelta(int64_t delta_in_us, AlarmCB* ac) {
+ RegisterAlarm(ApproximateNowInUsec() + delta_in_us, ac);
+ }
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Unregister the alarm referred to by iterator_token; Callers should
+ // be warned that a token may have become already invalid when OnAlarm()
+ // is called, was unregistered, or OnShutdown was called on that alarm.
+ // Args:
+ // iterator_token - iterator to the alarm callback to unregister.
+ virtual void UnregisterAlarm(
+ const SimpleEpollServer::AlarmRegToken& iterator_token);
+
+ virtual SimpleEpollServer::AlarmRegToken ReregisterAlarm(
+ SimpleEpollServer::AlarmRegToken iterator_token,
+ int64_t timeout_time_in_us);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // returns the number of file-descriptors registered in this
+ // SimpleEpollServer.
+ // Returns:
+ // number of FDs registered (discounting the internal pipe used for Wake)
+ virtual int NumFDsRegistered() const;
+
+ // Summary:
+ // Force the epoll server to wake up (by writing to an internal pipe).
+ virtual void Wake();
+
+ // Summary:
+ // Wrapper around WallTimer's NowInUsec. We do this so that we can test
+ // SimpleEpollServer without using the system clock (and can avoid the
+ // flakiness that would ensue)
+ // Returns:
+ // the current time as number of microseconds since the Unix epoch.
+ virtual int64_t NowInUsec() const;
+
+ // Summary:
+ // Since calling NowInUsec() many thousands of times per
+ // WaitForEventsAndExecuteCallbacks function call is, to say the least,
+ // inefficient, we allow users to use an approximate time instead. The
+ // time returned from this function is as accurate as NowInUsec() when
+ // WaitForEventsAndExecuteCallbacks is not an ancestor of the caller's
+ // callstack.
+ // However, when WaitForEventsAndExecuteCallbacks -is- an ancestor, then
+ // this function returns the time at which the
+ // WaitForEventsAndExecuteCallbacks function started to process events or
+ // alarms.
+ //
+ // Essentially, this function makes available a fast and mostly accurate
+ // mechanism for getting the time for any function handling an event or
+ // alarm. When functions which are not handling callbacks or alarms call
+ // this function, they get the slow and "absolutely" accurate time.
+ //
+ // Users should be encouraged to use this function.
+ // Returns:
+ // the "approximate" current time as number of microseconds since the Unix
+ // epoch.
+ virtual int64_t ApproximateNowInUsec() const;
+
+ static std::string EventMaskToString(int event_mask);
+
+ // Summary:
+ // Logs the state of the epoll server with EPOLL_LOG(ERROR).
+ void LogStateOnCrash();
+
+ // Summary:
+ // Set the timeout to the value specified.
+ // If the timeout is set to a negative number,
+ // WaitForEventsAndExecuteCallbacks() will only return when an event has
+ // occurred
+ // If the timeout is set to zero,
+ // WaitForEventsAndExecuteCallbacks() will return immediately
+ // If the timeout is set to a positive number,
+ // WaitForEventsAndExecuteCallbacks() will return when an event has
+ // occurred, or when timeout_in_us microseconds has elapsed, whichever
+ // is first.
+ // Args:
+ // timeout_in_us - value specified depending on behaviour desired.
+ // See above.
+ void set_timeout_in_us(int64_t timeout_in_us) {
+ timeout_in_us_ = timeout_in_us;
+ }
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Accessor for the current value of timeout_in_us.
+ int timeout_in_us_for_test() const { return timeout_in_us_; }
+
+ // Summary:
+ // Returns true when the SimpleEpollServer() is being destroyed.
+ bool in_shutdown() const { return in_shutdown_; }
+
+ // Compatibility stub.
+ void Shutdown() {}
+
+ // Summary:
+ // A function for implementing the ready list. It invokes OnEvent for each
+ // of the fd in the ready list, and takes care of adding them back to the
+ // ready list if the callback requests it (by checking that out_ready_mask
+ // is non-zero).
+ void CallReadyListCallbacks();
+
+ int64_t LastDelayInUsec() const { return last_delay_in_usec_; }
+
+ protected:
+ virtual void SetNonblocking(int fd);
+
+ // This exists here so that we can override this function in unittests
+ // in order to make effective mock SimpleEpollServer objects.
+ virtual int epoll_wait_impl(int epfd, struct epoll_event* events,
+ int max_events, int timeout_in_ms);
+
+ // this struct is used internally, and is never used by anything external
+ // to this class. Some of its members are declared mutable to get around the
+ // restriction imposed by hash_set. Since hash_set knows nothing about the
+ // objects it stores, it has to assume that every bit of the object is used
+ // in the hash function and equal_to comparison. Thus hash_set::iterator is a
+ // const iterator. In this case, the only thing that must stay constant is
+ // fd. Everything else are just along for the ride and changing them doesn't
+ // compromise the hash_set integrity.
+ struct CBAndEventMask {
+ CBAndEventMask()
+ : cb(NULL),
+ fd(-1),
+ event_mask(0),
+ events_asserted(0),
+ events_to_fake(0),
+ in_use(false) {
+ entry.le_next = NULL;
+ entry.le_prev = NULL;
+ }
+
+ CBAndEventMask(EpollCallbackInterface* cb, int event_mask, int fd)
+ : cb(cb),
+ fd(fd),
+ event_mask(event_mask),
+ events_asserted(0),
+ events_to_fake(0),
+ in_use(false) {
+ entry.le_next = NULL;
+ entry.le_prev = NULL;
+ }
+
+ // Required operator for hash_set. Normally operator== should be a free
+ // standing function. However, since CBAndEventMask is a protected type and
+ // it will never be a base class, it makes no difference.
+ bool operator==(const CBAndEventMask& cb_and_mask) const {
+ return fd == cb_and_mask.fd;
+ }
+ // A callback. If the fd is unregistered inside the callchain of OnEvent,
+ // the cb will be set to NULL.
+ mutable EpollCallbackInterface* cb;
+
+ mutable LIST_ENTRY(CBAndEventMask) entry;
+ // file descriptor registered with the epoll server.
+ int fd;
+ // the current event_mask registered for this callback.
+ mutable int event_mask;
+ // the event_mask that was returned by epoll
+ mutable int events_asserted;
+ // the event_mask for the ready list to use to call OnEvent.
+ mutable int events_to_fake;
+ // toggle around calls to OnEvent to tell UnregisterFD to not erase the
+ // iterator because HandleEvent is using it.
+ mutable bool in_use;
+ };
+
+ // Custom hash function to be used by hash_set.
+ struct CBAndEventMaskHash {
+ size_t operator()(const CBAndEventMask& cb_and_eventmask) const {
+ return static_cast<size_t>(cb_and_eventmask.fd);
+ }
+ };
+
+ using FDToCBMap = std::unordered_set<CBAndEventMask, CBAndEventMaskHash>;
+
+ // the following four functions are OS-specific, and are likely
+ // to be changed in a subclass if the poll/select method is changed
+ // from epoll.
+
+ // Summary:
+ // Deletes a file-descriptor from the set of FDs that should be
+ // monitored with epoll.
+ // Note that this only deals with modifying data relating -directly-
+ // with the epoll call-- it does not modify any data within the
+ // epoll_server.
+ // Args:
+ // fd - the file descriptor to-be-removed from the monitoring set
+ virtual void DelFD(int fd) const;
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Adds a file-descriptor to the set of FDs that should be
+ // monitored with epoll.
+ // Note that this only deals with modifying data relating -directly-
+ // with the epoll call.
+ // Args:
+ // fd - the file descriptor to-be-added to the monitoring set
+ // event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc
+ // OR'd together) which will be associated with this
+ // FD initially.
+ virtual void AddFD(int fd, int event_mask) const;
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Modifies a file-descriptor in the set of FDs that should be
+ // monitored with epoll.
+ // Note that this only deals with modifying data relating -directly-
+ // with the epoll call.
+ // Args:
+ // fd - the file descriptor to-be-added to the monitoring set
+ // event_mask - the event mask (consisting of EPOLLIN, EPOLLOUT, etc
+ // OR'd together) which will be associated with this
+ // FD after this call.
+ virtual void ModFD(int fd, int event_mask) const;
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Modified the event mask associated with an FD in the set of
+ // data needed by epoll.
+ // Events are removed before they are added, thus, if ~0 is put
+ // in 'remove_event', whatever is put in 'add_event' will be
+ // the new event mask.
+ // If the file-descriptor specified is not registered in the
+ // epoll_server, then nothing happens as a result of this call.
+ // Args:
+ // fd - the file descriptor whose event mask is to be modified
+ // remove_event - the events which are to be removed from the current
+ // event_mask
+ // add_event - the events which are to be added to the current event_mask
+ //
+ //
+ virtual void ModifyFD(int fd, int remove_event, int add_event);
+
+ ////////////////////////////////////////
+
+ // Summary:
+ // Waits for events, and calls HandleEvents() for each
+ // fd, event pair discovered to possibly have an event.
+ // Note that a callback (B) may get a spurious event if
+ // another callback (A) has closed a file-descriptor N, and
+ // the callback (B) has a newly opened file-descriptor, which
+ // also happens to be N.
+ virtual void WaitForEventsAndCallHandleEvents(int64_t timeout_in_us,
+ struct epoll_event events[],
+ int events_size);
+
+ // Summary:
+ // An internal function for implementing the ready list. It adds a fd's
+ // CBAndEventMask to the ready list. If the fd is already on the ready
+ // list, it is a no-op.
+ void AddToReadyList(CBAndEventMask* cb_and_mask);
+
+ // Summary:
+ // An internal function for implementing the ready list. It remove a fd's
+ // CBAndEventMask from the ready list. If the fd is not on the ready list,
+ // it is a no-op.
+ void RemoveFromReadyList(const CBAndEventMask& cb_and_mask);
+
+ // Summary:
+ // Calls any pending alarms that should go off and reregisters them if they
+ // were recurring.
+ virtual void CallAndReregisterAlarmEvents();
+
+ // The file-descriptor created for epolling
+ int epoll_fd_;
+
+ // The mapping of file-descriptor to CBAndEventMasks
+ FDToCBMap cb_map_;
+
+ // Custom hash function to be used by hash_set.
+ struct AlarmCBHash {
+ size_t operator()(AlarmCB* const& p) const {
+ return reinterpret_cast<size_t>(p);
+ }
+ };
+
+ // TODO(sushantj): Having this hash_set is avoidable. We currently have it
+ // only so that we can enforce stringent checks that a caller can not register
+ // the same alarm twice. One option is to have an implementation in which
+ // this hash_set is used only in the debug mode.
+ using AlarmCBMap = std::unordered_set<AlarmCB*, AlarmCBHash>;
+ AlarmCBMap all_alarms_;
+
+ TimeToAlarmCBMap alarm_map_;
+
+ // The amount of time in microseconds that we'll wait before returning
+ // from the WaitForEventsAndExecuteCallbacks() function.
+ // If this is positive, wait that many microseconds.
+ // If this is negative, wait forever, or for the first event that occurs
+ // If this is zero, never wait for an event.
+ int64_t timeout_in_us_;
+
+ // This is nonzero only after the invocation of epoll_wait_impl within
+ // WaitForEventsAndCallHandleEvents and before the function
+ // WaitForEventsAndExecuteCallbacks returns. At all other times, this is
+ // zero. This enables us to have relatively accurate time returned from the
+ // ApproximateNowInUs() function. See that function for more details.
+ int64_t recorded_now_in_us_;
+
+ // This is used to implement CallAndReregisterAlarmEvents. This stores
+ // all alarms that were reregistered because OnAlarm() returned a
+ // value > 0 and the time at which they should be executed is less that
+ // the current time. By storing such alarms in this map we ensure
+ // that while calling CallAndReregisterAlarmEvents we do not call
+ // OnAlarm on any alarm in this set. This ensures that we do not
+ // go in an infinite loop.
+ AlarmCBMap alarms_reregistered_and_should_be_skipped_;
+
+ LIST_HEAD(ReadyList, CBAndEventMask) ready_list_;
+ LIST_HEAD(TmpList, CBAndEventMask) tmp_list_;
+ int ready_list_size_;
+ // TODO(alyssar): make this into something that scales up.
+ static const int events_size_ = 256;
+ struct epoll_event events_[256];
+
+#ifdef EPOLL_SERVER_EVENT_TRACING
+ struct EventRecorder {
+ public:
+ EventRecorder() : num_records_(0), record_threshold_(10000) {}
+
+ ~EventRecorder() { Clear(); }
+
+ // When a number of events equals the record threshold,
+ // the collected data summary for all FDs will be written
+ // to EPOLL_LOG(INFO). Note that this does not include the
+ // individual events (if you'reinterested in those, you'll
+ // have to get at them programmatically).
+ // After any such flushing to EPOLL_LOG(INFO) all events will
+ // be cleared.
+ // Note that the definition of an 'event' is a bit 'hazy',
+ // as it includes the 'Unregistration' event, and perhaps
+ // others.
+ void set_record_threshold(int64_t new_threshold) {
+ record_threshold_ = new_threshold;
+ }
+
+ void Clear() {
+ for (int i = 0; i < debug_events_.size(); ++i) {
+ delete debug_events_[i];
+ }
+ debug_events_.clear();
+ unregistered_fds_.clear();
+ event_counts_.clear();
+ }
+
+ void MaybeRecordAndClear() {
+ ++num_records_;
+ if ((num_records_ > record_threshold_) && (record_threshold_ > 0)) {
+ EPOLL_LOG(INFO) << "\n" << *this;
+ num_records_ = 0;
+ Clear();
+ }
+ }
+
+ void RecordFDMaskEvent(int fd, int mask, const char* function) {
+ FDMaskOutput* fdmo = new FDMaskOutput(fd, mask, function);
+ debug_events_.push_back(fdmo);
+ MaybeRecordAndClear();
+ }
+
+ void RecordEpollWaitEvent(int timeout_in_ms, int num_events_generated) {
+ EpollWaitOutput* ewo =
+ new EpollWaitOutput(timeout_in_ms, num_events_generated);
+ debug_events_.push_back(ewo);
+ MaybeRecordAndClear();
+ }
+
+ void RecordEpollEvent(int fd, int event_mask) {
+ Events& events_for_fd = event_counts_[fd];
+ events_for_fd.AssignFromMask(event_mask);
+ MaybeRecordAndClear();
+ }
+
+ friend ostream& operator<<(ostream& os, const EventRecorder& er) {
+ for (int i = 0; i < er.unregistered_fds_.size(); ++i) {
+ os << "fd: " << er.unregistered_fds_[i] << "\n";
+ os << er.unregistered_fds_[i];
+ }
+ for (EventCountsMap::const_iterator i = er.event_counts_.begin();
+ i != er.event_counts_.end(); ++i) {
+ os << "fd: " << i->first << "\n";
+ os << i->second;
+ }
+ for (int i = 0; i < er.debug_events_.size(); ++i) {
+ os << *(er.debug_events_[i]) << "\n";
+ }
+ return os;
+ }
+
+ void RecordUnregistration(int fd) {
+ EventCountsMap::iterator i = event_counts_.find(fd);
+ if (i != event_counts_.end()) {
+ unregistered_fds_.push_back(i->second);
+ event_counts_.erase(i);
+ }
+ MaybeRecordAndClear();
+ }
+
+ protected:
+ class DebugOutput {
+ public:
+ friend ostream& operator<<(ostream& os, const DebugOutput& debug_output) {
+ debug_output.OutputToStream(os);
+ return os;
+ }
+ virtual void OutputToStream(ostream* os) const = 0;
+ virtual ~DebugOutput() {}
+ };
+
+ class FDMaskOutput : public DebugOutput {
+ public:
+ FDMaskOutput(int fd, int mask, const char* function)
+ : fd_(fd), mask_(mask), function_(function) {}
+ virtual void OutputToStream(ostream* os) const {
+ (*os) << "func: " << function_ << "\tfd: " << fd_;
+ if (mask_ != 0) {
+ (*os) << "\tmask: " << EventMaskToString(mask_);
+ }
+ }
+ int fd_;
+ int mask_;
+ const char* function_;
+ };
+
+ class EpollWaitOutput : public DebugOutput {
+ public:
+ EpollWaitOutput(int timeout_in_ms, int num_events_generated)
+ : timeout_in_ms_(timeout_in_ms),
+ num_events_generated_(num_events_generated) {}
+ virtual void OutputToStream(ostream* os) const {
+ (*os) << "timeout_in_ms: " << timeout_in_ms_
+ << "\tnum_events_generated: " << num_events_generated_;
+ }
+
+ protected:
+ int timeout_in_ms_;
+ int num_events_generated_;
+ };
+
+ struct Events {
+ Events()
+ : epoll_in(0),
+ epoll_pri(0),
+ epoll_out(0),
+ epoll_rdnorm(0),
+ epoll_rdband(0),
+ epoll_wrnorm(0),
+ epoll_wrband(0),
+ epoll_msg(0),
+ epoll_err(0),
+ epoll_hup(0),
+ epoll_oneshot(0),
+ epoll_et(0) {}
+
+ void AssignFromMask(int event_mask) {
+ if (event_mask & EPOLLIN) ++epoll_in;
+ if (event_mask & EPOLLPRI) ++epoll_pri;
+ if (event_mask & EPOLLOUT) ++epoll_out;
+ if (event_mask & EPOLLRDNORM) ++epoll_rdnorm;
+ if (event_mask & EPOLLRDBAND) ++epoll_rdband;
+ if (event_mask & EPOLLWRNORM) ++epoll_wrnorm;
+ if (event_mask & EPOLLWRBAND) ++epoll_wrband;
+ if (event_mask & EPOLLMSG) ++epoll_msg;
+ if (event_mask & EPOLLERR) ++epoll_err;
+ if (event_mask & EPOLLHUP) ++epoll_hup;
+ if (event_mask & EPOLLONESHOT) ++epoll_oneshot;
+ if (event_mask & EPOLLET) ++epoll_et;
+ }
+
+ friend ostream& operator<<(ostream& os, const Events& ev) {
+ if (ev.epoll_in) {
+ os << "\t EPOLLIN: " << ev.epoll_in << "\n";
+ }
+ if (ev.epoll_pri) {
+ os << "\t EPOLLPRI: " << ev.epoll_pri << "\n";
+ }
+ if (ev.epoll_out) {
+ os << "\t EPOLLOUT: " << ev.epoll_out << "\n";
+ }
+ if (ev.epoll_rdnorm) {
+ os << "\t EPOLLRDNORM: " << ev.epoll_rdnorm << "\n";
+ }
+ if (ev.epoll_rdband) {
+ os << "\t EPOLLRDBAND: " << ev.epoll_rdband << "\n";
+ }
+ if (ev.epoll_wrnorm) {
+ os << "\t EPOLLWRNORM: " << ev.epoll_wrnorm << "\n";
+ }
+ if (ev.epoll_wrband) {
+ os << "\t EPOLLWRBAND: " << ev.epoll_wrband << "\n";
+ }
+ if (ev.epoll_msg) {
+ os << "\t EPOLLMSG: " << ev.epoll_msg << "\n";
+ }
+ if (ev.epoll_err) {
+ os << "\t EPOLLERR: " << ev.epoll_err << "\n";
+ }
+ if (ev.epoll_hup) {
+ os << "\t EPOLLHUP: " << ev.epoll_hup << "\n";
+ }
+ if (ev.epoll_oneshot) {
+ os << "\t EPOLLONESHOT: " << ev.epoll_oneshot << "\n";
+ }
+ if (ev.epoll_et) {
+ os << "\t EPOLLET: " << ev.epoll_et << "\n";
+ }
+ return os;
+ }
+
+ unsigned int epoll_in;
+ unsigned int epoll_pri;
+ unsigned int epoll_out;
+ unsigned int epoll_rdnorm;
+ unsigned int epoll_rdband;
+ unsigned int epoll_wrnorm;
+ unsigned int epoll_wrband;
+ unsigned int epoll_msg;
+ unsigned int epoll_err;
+ unsigned int epoll_hup;
+ unsigned int epoll_oneshot;
+ unsigned int epoll_et;
+ };
+
+ std::vector<DebugOutput*> debug_events_;
+ std::vector<Events> unregistered_fds_;
+ using EventCountsMap = std::unordered_map<int, Events>;
+ EventCountsMap event_counts_;
+ int64_t num_records_;
+ int64_t record_threshold_;
+ };
+
+ void ClearEventRecords() { event_recorder_.Clear(); }
+ void WriteEventRecords(ostream* os) const { (*os) << event_recorder_; }
+
+ mutable EventRecorder event_recorder_;
+
+#endif
+
+ private:
+ // Helper functions used in the destructor.
+ void CleanupFDToCBMap();
+ void CleanupTimeToAlarmCBMap();
+
+ // The callback registered to the fds below. As the purpose of their
+ // registration is to wake the epoll server it just clears the pipe and
+ // returns.
+ std::unique_ptr<ReadPipeCallback> wake_cb_;
+
+ // A pipe owned by the epoll server. The server will be registered to listen
+ // on read_fd_ and can be woken by Wake() which writes to write_fd_.
+ int read_fd_;
+ int write_fd_;
+
+ // This boolean is checked to see if it is false at the top of the
+ // WaitForEventsAndExecuteCallbacks function. If not, then it either returns
+ // without doing work, and logs to ERROR, or aborts the program (in
+ // DEBUG mode). If so, then it sets the bool to true, does work, and
+ // sets it back to false when done. This catches unwanted recursion.
+ bool in_wait_for_events_and_execute_callbacks_;
+
+ // Returns true when the SimpleEpollServer() is being destroyed.
+ bool in_shutdown_;
+ int64_t last_delay_in_usec_;
+};
+
+class EpollAlarmCallbackInterface {
+ public:
+ // Summary:
+ // Called when an alarm times out. Invalidates an AlarmRegToken.
+ // WARNING: If a token was saved to refer to an alarm callback, OnAlarm must
+ // delete it, as the reference is no longer valid.
+ // Returns:
+ // the unix time (in microseconds) at which this alarm should be signaled
+ // again, or 0 if the alarm should be removed.
+ virtual int64_t OnAlarm() = 0;
+
+ // Summary:
+ // Called when the an alarm is registered. Invalidates an AlarmRegToken.
+ // Args:
+ // token: the iterator to the alarm registered in the alarm map.
+ // WARNING: this token becomes invalid when the alarm fires, is
+ // unregistered, or OnShutdown is called on that alarm.
+ // eps: the epoll server the alarm is registered with.
+ virtual void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
+ SimpleEpollServer* eps) = 0;
+
+ // Summary:
+ // Called when the an alarm is unregistered.
+ // WARNING: It is not valid to unregister a callback and then use the token
+ // that was saved to refer to the callback.
+ virtual void OnUnregistration() = 0;
+
+ // Summary:
+ // Called when the epoll server is shutting down.
+ // Invalidates the AlarmRegToken that was given when this alarm was
+ // registered.
+ virtual void OnShutdown(SimpleEpollServer* eps) = 0;
+
+ virtual ~EpollAlarmCallbackInterface() {}
+
+ protected:
+ EpollAlarmCallbackInterface() {}
+};
+
+// A simple alarm which unregisters itself on destruction.
+//
+// PLEASE NOTE:
+// Any classes overriding these functions must either call the implementation
+// of the parent class, or is must otherwise make sure that the 'registered_'
+// boolean and the token, 'token_', are updated appropriately.
+class EPOLL_EXPORT_PRIVATE EpollAlarm : public EpollAlarmCallbackInterface {
+ public:
+ EpollAlarm();
+
+ ~EpollAlarm() override;
+
+ // Marks the alarm as unregistered and returns 0. The return value may be
+ // safely ignored by subclasses.
+ int64_t OnAlarm() override;
+
+ // Marks the alarm as registered, and stores the token.
+ void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
+ SimpleEpollServer* eps) override;
+
+ // Marks the alarm as unregistered.
+ void OnUnregistration() override;
+
+ // Marks the alarm as unregistered.
+ void OnShutdown(SimpleEpollServer* eps) override;
+
+ // If the alarm was registered, unregister it.
+ void UnregisterIfRegistered();
+
+ // Reregisters the alarm at specified time.
+ void ReregisterAlarm(int64_t timeout_time_in_us);
+
+ bool registered() const { return registered_; }
+
+ const SimpleEpollServer* eps() const { return eps_; }
+
+ private:
+ SimpleEpollServer::AlarmRegToken token_;
+ SimpleEpollServer* eps_;
+ bool registered_;
+};
+
+} // namespace epoll_server
+
+#endif // QUICHE_EPOLL_SERVER_H_
diff --git a/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server_test.cc b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server_test.cc
new file mode 100644
index 00000000000..7c024f1d562
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/epoll_server/simple_epoll_server_test.cc
@@ -0,0 +1,2517 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Epoll tests which determine that the right things happen in the right order.
+// Also lots of testing of individual functions.
+
+#include "net/third_party/quiche/src/epoll_server/simple_epoll_server.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/epoll_server/fake_simple_epoll_server.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_address_test_utils.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_expect_bug.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_ptr_util.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_test.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_thread.h"
+#include "net/third_party/quiche/src/epoll_server/platform/api/epoll_time.h"
+
+namespace epoll_server {
+
+namespace test {
+
+namespace {
+
+const int kPageSize = 4096;
+const int kMaxBufLen = 10000;
+
+// These are used to record what is happening.
+enum {
+ CREATION,
+ REGISTRATION,
+ MODIFICATION,
+ EVENT,
+ UNREGISTRATION,
+ SHUTDOWN,
+ DESTRUCTION
+};
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+struct RecordEntry {
+ RecordEntry() : time(0), instance(nullptr), event_type(0), fd(0), data(0) {}
+
+ RecordEntry(int64_t time, void* instance, int event_type, int fd, int data)
+ : time(time),
+ instance(instance),
+ event_type(event_type),
+ fd(fd),
+ data(data) {}
+
+ int64_t time;
+ void* instance;
+ int event_type;
+ int fd;
+ int data;
+
+ bool IsEqual(const RecordEntry *entry) const {
+ bool retval = true;
+
+ if (instance != entry->instance) {
+ retval = false;
+ EPOLL_LOG(INFO) << " instance (" << instance << ") != entry->instance("
+ << entry->instance << ")";
+ }
+ if (event_type != entry->event_type) {
+ retval = false;
+ EPOLL_LOG(INFO) << " event_type (" << event_type
+ << ") != entry->event_type(" << entry->event_type << ")";
+ }
+ if ( fd != entry->fd ) {
+ retval = false;
+ EPOLL_LOG(INFO) << " fd (" << fd << ") != entry->fd (" << entry->fd
+ << ")";
+ }
+ if (data != entry->data) {
+ retval = false;
+ EPOLL_LOG(INFO) << " data (" << data << ") != entry->data(" << entry->data
+ << ")";
+ }
+ return retval;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+class Recorder {
+ public:
+ void Record(void* instance, int event_type, int fd, int data) {
+ records_.push_back(
+ RecordEntry(WallTimeNowInUsec(), instance, event_type, fd, data));
+ }
+
+ const std::vector<RecordEntry> *records() const { return &records_; }
+
+ bool IsEqual(const Recorder *recorder) const {
+ const std::vector<RecordEntry> *records = recorder->records();
+
+ if (records_.size() != records->size()) {
+ EPOLL_LOG(INFO) << "records_.size() (" << records_.size()
+ << ") != records->size() (" << records->size() << ")";
+ return false;
+ }
+ for (size_t i = 0; i < std::min(records_.size(), records->size()); ++i) {
+ if (!records_[i].IsEqual(&(*records)[i])) {
+ EPOLL_LOG(INFO) << "entry in index: " << i
+ << " differs from recorder.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ std::vector<RecordEntry> records_;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+class RecordingCB : public EpollCallbackInterface {
+ public:
+ RecordingCB() : recorder_(new Recorder()) {
+ recorder_->Record(this, CREATION, 0, 0);
+ }
+
+ ~RecordingCB() override {
+ recorder_->Record(this, DESTRUCTION, 0, 0);
+ delete recorder_;
+ }
+
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ recorder_->Record(this, REGISTRATION, fd, event_mask);
+ }
+
+ void OnModification(int fd, int event_mask) override {
+ recorder_->Record(this, MODIFICATION, fd, event_mask);
+ }
+
+ void OnEvent(int fd, EpollEvent* event) override {
+ recorder_->Record(this, EVENT, fd, event->in_events);
+ if (event->in_events & EPOLLIN) {
+ const int kLength = 1024;
+ char buf[kLength];
+ read(fd, &buf, kLength);
+ }
+ }
+
+ void OnUnregistration(int fd, bool replaced) override {
+ recorder_->Record(this, UNREGISTRATION, fd, replaced);
+ }
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {
+ if (fd >= 0) {
+ eps->UnregisterFD(fd);
+ }
+ recorder_->Record(this, SHUTDOWN, fd, 0);
+ }
+
+ std::string Name() const override { return "RecordingCB"; }
+
+ const Recorder* recorder() const { return recorder_; }
+
+ protected:
+ Recorder* recorder_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// A simple test server that adds some test functions to SimpleEpollServer as
+// well as allowing access to protected functions.
+class EpollTestServer : public SimpleEpollServer {
+ public:
+ EpollTestServer() : SimpleEpollServer() {}
+
+ ~EpollTestServer() override {}
+
+ void CheckMapping(int fd, CB* cb) {
+ CBAndEventMask tmp;
+ tmp.fd = fd;
+ FDToCBMap::iterator fd_i = cb_map_.find(tmp);
+ CHECK(fd_i != cb_map_.end()); // Chokes CHECK_NE.
+ CHECK(fd_i->cb == cb);
+ }
+
+ void CheckNotMapped(int fd) {
+ CBAndEventMask tmp;
+ tmp.fd = fd;
+ FDToCBMap::iterator fd_i = cb_map_.find(tmp);
+ CHECK(fd_i == cb_map_.end()); // Chokes CHECK_EQ.
+ }
+
+ void CheckEventMask(int fd, int event_mask) {
+ CBAndEventMask tmp;
+ tmp.fd = fd;
+ FDToCBMap::iterator fd_i = cb_map_.find(tmp);
+ CHECK(cb_map_.end() != fd_i); // Chokes CHECK_NE.
+ CHECK_EQ(fd_i->event_mask, event_mask);
+ }
+
+ void CheckNotRegistered(int fd) {
+ struct epoll_event ee;
+ memset(&ee, 0, sizeof(ee));
+ // If the fd is registered, the epoll_ctl call would succeed (return 0) and
+ // the CHECK would fail.
+ CHECK(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &ee));
+ }
+
+ size_t GetNumPendingAlarmsForTest() const { return alarm_map_.size(); }
+
+ bool ContainsAlarm(AlarmCB* ac) {
+ return all_alarms_.find(ac) != all_alarms_.end();
+ }
+
+ using SimpleEpollServer::WaitForEventsAndCallHandleEvents;
+};
+
+class EpollFunctionTest : public EpollTest {
+ public:
+ EpollFunctionTest()
+ : fd_(-1), fd2_(-1), recorder_(nullptr), cb_(nullptr), ep_(nullptr) {
+ }
+
+ ~EpollFunctionTest() override {
+ delete ep_;
+ delete cb_;
+ }
+
+ void SetUp() override {
+ ep_ = new EpollTestServer();
+ cb_ = new RecordingCB();
+ // recorder_ is safe to use directly as we know it has the same scope as
+ // cb_
+ recorder_ = cb_->recorder();
+
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ PLOG(FATAL) << "pipe() failed";
+ }
+ fd_ = pipe_fds[0];
+ fd2_ = pipe_fds[1];
+ }
+
+ void TearDown() override {
+ close(fd_);
+ close(fd2_);
+ }
+
+ void DeleteSimpleEpollServer() {
+ delete ep_;
+ ep_ = nullptr;
+ }
+
+ int fd() { return fd_; }
+ int fd2() { return fd2_; }
+ EpollTestServer* ep() { return ep_; }
+ EpollCallbackInterface* cb() { return cb_; }
+ const Recorder* recorder() { return recorder_; }
+
+ private:
+ int fd_;
+ int fd2_;
+ const Recorder *recorder_;
+ RecordingCB* cb_;
+ EpollTestServer* ep_;
+};
+
+TEST_F(EpollFunctionTest, TestUnconnectedSocket) {
+ int fd = socket(AddressFamilyUnderTest(), SOCK_STREAM, IPPROTO_TCP);
+ ep()->RegisterFD(fd, cb(), EPOLLIN | EPOLLOUT);
+ ep()->WaitForEventsAndExecuteCallbacks();
+
+ Recorder tmp;
+ tmp.Record(cb(), CREATION, 0, 0);
+ tmp.Record(cb(), REGISTRATION, fd, EPOLLIN | EPOLLOUT);
+ tmp.Record(cb(), EVENT, fd, EPOLLOUT | EPOLLHUP);
+ EXPECT_TRUE(recorder()->IsEqual(&tmp));
+}
+
+TEST_F(EpollFunctionTest, TestRegisterFD) {
+ // Check that the basic register works.
+ ep()->RegisterFD(fd(), cb(), EPOLLIN);
+
+ // Make sure that the fd-CB mapping is there.
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN);
+
+ // Now make sure that if we register again, we stomp the old callback.
+ // Also make sure we handle O_NONBLOCK correctly
+ RecordingCB cb2;
+ ep()->RegisterFD(fd(), &cb2, EPOLLOUT | O_NONBLOCK);
+ ep()->CheckMapping(fd(), &cb2);
+ ep()->CheckEventMask(fd(), EPOLLOUT | O_NONBLOCK);
+
+ // Clean up.
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestRegisterFDForWrite) {
+ ep()->RegisterFDForWrite(fd(), cb());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLOUT);
+
+ // Clean up.
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestRegisterFDForReadWrite) {
+ ep()->RegisterFDForReadWrite(fd(), cb());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
+
+ // Clean up.
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestRegisterFDForRead) {
+ ep()->RegisterFDForRead(fd(), cb());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN);
+
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestUnregisterFD) {
+ ep()->RegisterFDForRead(fd(), cb());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN);
+
+ // Unregister and make sure that it's gone.
+ ep()->UnregisterFD(fd());
+ ep()->CheckNotMapped(fd());
+ ep()->CheckNotRegistered(fd());
+
+ // And make sure that unregistering something a second time doesn't cause
+ // crashes.
+ ep()->UnregisterFD(fd());
+ ep()->CheckNotMapped(fd());
+ ep()->CheckNotRegistered(fd());
+}
+
+TEST_F(EpollFunctionTest, TestModifyCallback) {
+ // Check that nothing terrible happens if we modify an unregistered fd.
+ ep()->ModifyCallback(fd(), EPOLLOUT);
+ ep()->CheckNotMapped(fd());
+ ep()->CheckNotRegistered(fd());
+
+ // Check that the basic register works.
+ ep()->RegisterFD(fd(), cb(), EPOLLIN);
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN);
+
+ // Check that adding a signal swaps it out for the first.
+ ep()->ModifyCallback(fd(), EPOLLOUT);
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLOUT);
+
+ // Check that modifying from X to X works correctly.
+ ep()->ModifyCallback(fd(), EPOLLOUT);
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLOUT);
+
+ // Check that modifying from something to nothing works.
+ ep()->ModifyCallback(fd(), 0);
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), 0);
+
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestStopRead) {
+ ep()->RegisterFDForReadWrite(fd(), cb());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
+
+ // Unregister and make sure you only lose the read event.
+ ep()->StopRead(fd());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLOUT);
+
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestStartRead) {
+ ep()->RegisterFDForWrite(fd(), cb());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLOUT);
+
+ // Make sure that StartRead adds EPOLLIN and doesn't remove other signals.
+ ep()->StartRead(fd());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
+
+ // Clean up.
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestStopWrite) {
+ ep()->RegisterFDForReadWrite(fd(), cb());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
+
+ // Unregister write and make sure you only lose the write event.
+ ep()->StopWrite(fd());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN);
+
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestStartWrite) {
+ ep()->RegisterFDForRead(fd(), cb());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN);
+
+ // Make sure that StartWrite adds EPOLLOUT and doesn't remove other
+ // signals.
+ ep()->StartWrite(fd());
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), EPOLLIN | EPOLLOUT);
+
+ // Clean up.
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestSet_timeout_in_us) {
+ // Check that set works with various values. There's a separate test below
+ // to make sure the values are used properly.
+ ep()->set_timeout_in_us(10);
+ EXPECT_EQ(10, ep()->timeout_in_us_for_test());
+
+ ep()->set_timeout_in_us(-1);
+ EXPECT_EQ(-1, ep()->timeout_in_us_for_test());
+}
+
+TEST_F(EpollFunctionTest, TestHandleEvent) {
+ const std::vector<RecordEntry> *records = recorder()->records();
+
+ // Test that nothing bad happens if the FD is not in the map.
+ ep()->HandleEvent(fd(), EPOLLOUT);
+ ep()->CallReadyListCallbacks();
+
+ ep()->RegisterFD(fd(), cb(), 0);
+ ep()->CheckMapping(fd(), cb());
+ ep()->CheckEventMask(fd(), 0);
+
+ // At this point we should have creation and registration recorded.
+ EXPECT_EQ(2u, records->size());
+
+ // Call handle event and make sure something was recorded.
+ ep()->HandleEvent(fd(), EPOLLOUT);
+ ep()->CallReadyListCallbacks();
+ EXPECT_EQ(3u, records->size());
+
+ // Call handle event and make sure something was recorded.
+ ep()->HandleEvent(fd(), EPOLLIN | O_NONBLOCK);
+ ep()->CallReadyListCallbacks();
+ EXPECT_EQ(4u, records->size());
+
+ Recorder tmp;
+ tmp.Record(cb(), CREATION, 0, 0);
+ tmp.Record(cb(), REGISTRATION, fd(), 0);
+ tmp.Record(cb(), EVENT, fd(), EPOLLOUT);
+ tmp.Record(cb(), EVENT, fd(), EPOLLIN | O_NONBLOCK);
+
+ EXPECT_TRUE(recorder()->IsEqual(&tmp));
+ ep()->UnregisterFD(fd());
+}
+
+TEST_F(EpollFunctionTest, TestNumFDsRegistered) {
+ EXPECT_EQ(0, ep()->NumFDsRegistered());
+
+ ep()->RegisterFD(fd(), cb(), 0);
+ EXPECT_EQ(1, ep()->NumFDsRegistered());
+
+ ep()->RegisterFD(fd2(), cb(), 0);
+ EXPECT_EQ(2, ep()->NumFDsRegistered());
+
+ ep()->RegisterFD(fd2(), cb(), 0);
+ EXPECT_EQ(2, ep()->NumFDsRegistered());
+
+ ep()->UnregisterFD(fd2());
+ EXPECT_EQ(1, ep()->NumFDsRegistered());
+
+ ep()->UnregisterFD(fd());
+ EXPECT_EQ(0, ep()->NumFDsRegistered());
+}
+
+// Check all of the individual signals and 1-2 combinations.
+TEST_F(EpollFunctionTest, TestEventMaskToString) {
+ std::string test;
+
+ test = SimpleEpollServer::EventMaskToString(EPOLLIN);
+ EXPECT_EQ(test, "EPOLLIN ");
+
+ test = SimpleEpollServer::EventMaskToString(EPOLLOUT);
+ EXPECT_EQ(test, "EPOLLOUT ");
+
+ test = SimpleEpollServer::EventMaskToString(EPOLLPRI);
+ EXPECT_EQ(test, "EPOLLPRI ");
+
+ test = SimpleEpollServer::EventMaskToString(EPOLLERR);
+ EXPECT_EQ(test, "EPOLLERR ");
+
+ test = SimpleEpollServer::EventMaskToString(EPOLLHUP);
+ EXPECT_EQ(test, "EPOLLHUP ");
+
+ test = SimpleEpollServer::EventMaskToString(EPOLLHUP | EPOLLIN);
+ EXPECT_EQ(test, "EPOLLIN EPOLLHUP ");
+
+ test = SimpleEpollServer::EventMaskToString(EPOLLIN | EPOLLOUT);
+ EXPECT_EQ(test, "EPOLLIN EPOLLOUT ");
+}
+
+class TestAlarm : public EpollAlarmCallbackInterface {
+ public:
+ TestAlarm()
+ : time_before_next_alarm_(-1),
+ was_called_(false),
+ num_called_(0),
+ absolute_time_(false),
+ onshutdown_called_(false),
+ has_token_(false),
+ eps_(nullptr) {
+ }
+ ~TestAlarm() override {
+ }
+ int64_t OnAlarm() override {
+ has_token_ = false;
+ was_called_ = true;
+ ++num_called_;
+ if (time_before_next_alarm_ < 0) {
+ return 0;
+ }
+ if (absolute_time_) {
+ return time_before_next_alarm_;
+ } else {
+ return WallTimeNowInUsec() + time_before_next_alarm_;
+ }
+ }
+
+ void OnShutdown(SimpleEpollServer* eps) override {
+ onshutdown_called_ = true;
+ has_token_ = false;
+ }
+ void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
+ SimpleEpollServer* eps) override {
+ has_token_ = true;
+ last_token_ = token;
+ eps_ = eps;
+ }
+ void OnUnregistration() override {
+ has_token_ = false;
+ }
+
+ void UnregisterIfRegistered(SimpleEpollServer* eps) {
+ if (has_token_) {
+ eps->UnregisterAlarm(last_token_);
+ }
+ }
+
+ void ReregisterAlarm(int64_t timeout_in_us) {
+ CHECK(has_token_);
+ eps_->ReregisterAlarm(last_token_, timeout_in_us);
+ }
+
+ void Reset() {
+ time_before_next_alarm_ = -1;
+ was_called_ = false;
+ absolute_time_ = false;
+ }
+
+ bool was_called() const { return was_called_; }
+ int num_called() const { return num_called_; }
+
+ void set_time_before_next_alarm(int64_t time) {
+ time_before_next_alarm_ = time;
+ }
+ void set_absolute_time(bool absolute) {
+ absolute_time_ = absolute;
+ }
+ bool onshutdown_called() { return onshutdown_called_; }
+
+ protected:
+ int64_t time_before_next_alarm_;
+ bool was_called_;
+ int num_called_;
+ // Is time_before_next_alarm relative to the current time or absolute?
+ bool absolute_time_;
+ bool onshutdown_called_;
+ bool has_token_;
+ SimpleEpollServer::AlarmRegToken last_token_;
+ SimpleEpollServer* eps_;
+};
+
+class TestChildAlarm;
+
+// This node unregister all other alarms when it receives
+// OnShutdown() from any one child.
+class TestParentAlarm {
+ public:
+ void OnShutdown(TestChildAlarm* child, SimpleEpollServer* eps) {
+ // Unregister
+ for (ChildTokenMap::const_iterator it = child_tokens_.begin();
+ it != child_tokens_.end(); ++it) {
+ if (it->first != child) {
+ eps->UnregisterAlarm(it->second);
+ }
+ }
+ child_tokens_.clear();
+ }
+
+ void OnRegistration(TestChildAlarm* child,
+ const SimpleEpollServer::AlarmRegToken& token) {
+ child_tokens_[child] = token;
+ }
+
+ protected:
+ typedef std::unordered_map<TestChildAlarm*, SimpleEpollServer::AlarmRegToken>
+ ChildTokenMap;
+
+ ChildTokenMap child_tokens_;
+};
+
+class TestChildAlarm : public TestAlarm {
+ public:
+ void set_parent(TestParentAlarm* tp) { parent_ = tp; }
+ void OnShutdown(SimpleEpollServer* eps) override {
+ onshutdown_called_ = true;
+ // Inform parent of shutdown
+ parent_->OnShutdown(this, eps);
+ }
+ void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
+ SimpleEpollServer* eps) override {
+ parent_->OnRegistration(this, token);
+ }
+
+ protected:
+ TestParentAlarm* parent_;
+};
+
+class TestAlarmThatRegistersAnotherAlarm : public TestAlarm {
+ public:
+ TestAlarmThatRegistersAnotherAlarm()
+ : alarm_(nullptr),
+ reg_time_delta_usec_(0),
+ eps_to_register_(nullptr),
+ has_reg_alarm_(false) {}
+ void SetRegisterAlarm(TestAlarm* alarm, int64_t time_delta_usec,
+ SimpleEpollServer* eps) {
+ alarm_ = alarm;
+ reg_time_delta_usec_ = time_delta_usec;
+ has_reg_alarm_ = true;
+ eps_to_register_ = eps;
+ }
+ int64_t OnAlarm() override {
+ if (has_reg_alarm_) {
+ eps_to_register_->RegisterAlarm(
+ eps_to_register_->ApproximateNowInUsec() + reg_time_delta_usec_,
+ alarm_);
+ has_reg_alarm_ = false;
+ }
+ return TestAlarm::OnAlarm();
+ }
+
+ protected:
+ TestAlarm* alarm_;
+ int64_t reg_time_delta_usec_;
+ SimpleEpollServer* eps_to_register_;
+ bool has_reg_alarm_;
+};
+
+class TestAlarmThatRegistersAndReregistersAnotherAlarm : public TestAlarm {
+ public:
+ TestAlarmThatRegistersAndReregistersAnotherAlarm()
+ : alarm_(nullptr),
+ reg_time_delta_usec_(0),
+ reregister_time_delta_usec_(0),
+ eps_to_register_(nullptr),
+ has_reg_alarm_(false) {}
+ void SetRegisterAndReregisterAlarm(TestAlarm* alarm, int64_t time_delta_usec,
+ int64_t reregister_delta_usec,
+ SimpleEpollServer* eps) {
+ alarm_ = alarm;
+ reg_time_delta_usec_ = time_delta_usec;
+ reregister_time_delta_usec_ = reregister_delta_usec;
+ has_reg_alarm_ = true;
+ eps_to_register_ = eps;
+ }
+ int64_t OnAlarm() override {
+ if (has_reg_alarm_) {
+ eps_to_register_->RegisterAlarm(
+ eps_to_register_->ApproximateNowInUsec() + reg_time_delta_usec_,
+ alarm_);
+ alarm_->ReregisterAlarm(eps_to_register_->ApproximateNowInUsec() +
+ reregister_time_delta_usec_);
+ has_reg_alarm_ = false;
+ }
+ return TestAlarm::OnAlarm();
+ }
+
+ protected:
+ TestAlarm* alarm_;
+ int64_t reg_time_delta_usec_;
+ int64_t reregister_time_delta_usec_;
+ SimpleEpollServer* eps_to_register_;
+ bool has_reg_alarm_;
+};
+
+class TestAlarmThatUnregistersAnotherAlarm : public TestAlarm {
+ public:
+ TestAlarmThatUnregistersAnotherAlarm()
+ : alarm_(nullptr), eps_to_register_(nullptr), has_unreg_alarm_(false) {}
+ void SetUnregisterAlarm(TestAlarm* alarm, SimpleEpollServer* eps) {
+ alarm_ = alarm;
+ has_unreg_alarm_ = true;
+ eps_to_register_ = eps;
+ }
+ int64_t OnAlarm() override {
+ if (has_unreg_alarm_) {
+ has_unreg_alarm_ = false;
+ alarm_->UnregisterIfRegistered(eps_to_register_);
+ }
+ return TestAlarm::OnAlarm();
+ }
+
+ protected:
+ TestAlarm* alarm_;
+ SimpleEpollServer* eps_to_register_;
+ bool has_unreg_alarm_;
+};
+
+class TestAlarmUnregister : public TestAlarm {
+ public:
+ TestAlarmUnregister()
+ : onunregistration_called_(false),
+ iterator_token_(nullptr) {
+ }
+ ~TestAlarmUnregister() override {
+ delete iterator_token_;
+ }
+
+ void OnShutdown(SimpleEpollServer* eps) override {
+ onshutdown_called_ = true;
+ }
+
+ int64_t OnAlarm() override {
+ delete iterator_token_;
+ iterator_token_ = nullptr;
+
+ return TestAlarm::OnAlarm();
+ }
+
+ void OnRegistration(const SimpleEpollServer::AlarmRegToken& token,
+ SimpleEpollServer* eps) override {
+ // Multiple iterator tokens are not maintained by this code,
+ // so we should have reset the iterator_token in OnAlarm or
+ // OnUnregistration.
+ CHECK(iterator_token_ == nullptr);
+ iterator_token_ = new SimpleEpollServer::AlarmRegToken(token);
+ }
+ void OnUnregistration() override {
+ delete iterator_token_;
+ iterator_token_ = nullptr;
+ // Make sure that this alarm was not already unregistered.
+ CHECK(onunregistration_called_ == false);
+ onunregistration_called_ = true;
+ }
+
+ bool onunregistration_called() { return onunregistration_called_; }
+ // Returns true if the token has been filled in with the saved iterator
+ // and false if it has not.
+ bool get_token(SimpleEpollServer::AlarmRegToken* token) {
+ if (iterator_token_ != nullptr) {
+ *token = *iterator_token_;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected:
+ bool onunregistration_called_;
+ SimpleEpollServer::AlarmRegToken* iterator_token_;
+};
+
+void WaitForAlarm(SimpleEpollServer* eps, const TestAlarm& alarm) {
+ for (int i = 0; i < 5; ++i) {
+ // Ideally we would only have to call this once but it could wake up a bit
+ // early and so not call the alarm. If it wakes up early several times
+ // there is something wrong.
+ eps->WaitForEventsAndExecuteCallbacks();
+ if (alarm.was_called()) {
+ break;
+ }
+ }
+}
+
+// Check a couple of alarm times to make sure they're falling within a
+// reasonable range.
+TEST(SimpleEpollServerTest, TestAlarms) {
+ EpollTestServer ep;
+ TestAlarm alarm;
+
+ int alarm_time = 10;
+
+ // Register an alarm and make sure we wait long enough to hit it.
+ ep.set_timeout_in_us(alarm_time * 1000 * 2);
+ ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ WaitForAlarm(&ep, alarm);
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ alarm.Reset();
+
+ // Test a different time just to be careful.
+ alarm_time = 20;
+ ep.set_timeout_in_us(alarm_time * 1000 * 2);
+ ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ WaitForAlarm(&ep, alarm);
+ EXPECT_TRUE(alarm.was_called());
+ alarm.Reset();
+
+ // The alarm was a one-time thing. Make sure that we don't hit it again.
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_FALSE(alarm.was_called());
+ alarm.Reset();
+}
+
+// Same as above, but using RegisterAlarmApproximateDelta.
+TEST(SimpleEpollServerTest, TestRegisterAlarmApproximateDelta) {
+ EpollTestServer ep;
+ TestAlarm alarm;
+
+ int alarm_time = 10;
+
+ // Register an alarm and make sure we wait long enough to hit it.
+ ep.set_timeout_in_us(alarm_time * 1000 * 2);
+ ep.RegisterAlarmApproximateDelta(alarm_time * 1000, &alarm);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ WaitForAlarm(&ep, alarm);
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ alarm.Reset();
+ int64_t first_now = ep.ApproximateNowInUsec();
+ EXPECT_LT(0u, first_now);
+
+ // Test a different time just to be careful.
+ alarm_time = 20;
+ ep.set_timeout_in_us(alarm_time * 1000 * 2);
+ ep.RegisterAlarmApproximateDelta(alarm_time * 1000, &alarm);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ WaitForAlarm(&ep, alarm);
+ EXPECT_TRUE(alarm.was_called());
+ alarm.Reset();
+ int64_t second_now = ep.ApproximateNowInUsec();
+
+ EXPECT_LT(first_now, second_now);
+
+
+ // The alarm was a one-time thing. Make sure that we don't hit it again.
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_FALSE(alarm.was_called());
+ alarm.Reset();
+}
+
+TEST(SimpleEpollServerTest, TestAlarmsWithInfiniteWait) {
+ EpollTestServer ep;
+ TestAlarm alarm;
+
+ int alarm_time = 10;
+
+ // Register an alarm and make sure we wait long enough to hit it.
+ ep.set_timeout_in_us(-1);
+ ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ WaitForAlarm(&ep, alarm);
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ alarm.Reset();
+}
+
+// In this test we have an alarm that when fires gets re-registered
+// at almost the same time at which it fires. Here, we want to make
+// sure that when the alarm gets re-registered we do not call OnAlarm()
+// on the same Alarm object again, until we have called
+// WaitForEventsAndExecuteCallbacks(). A poor implementation of epoll
+// server alarm handling can potentially cause OnAlarm() to be called
+// multiple times. We make sure that the epoll server is not going in
+// an infinite loop by checking that OnAlarm() is called exactly once
+// on the alarm object that got registered again.
+TEST(SimpleEpollServerTest, TestAlarmsThatGetReRegisteredAreNotCalledTwice) {
+ // This alarm would get registered again
+ TestAlarm alarm;
+ TestAlarm alarm2;
+ EpollTestServer ep;
+ ep.set_timeout_in_us(-1);
+
+ int64_t alarm_time = 10;
+ int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
+
+ // This will make the alarm re-register when OnAlarm is called.
+ alarm.set_absolute_time(true);
+ alarm.set_time_before_next_alarm(abs_time + 2);
+
+ // Register two alarms and make sure we wait long enough to hit it.
+ ep.RegisterAlarm(abs_time, &alarm);
+ ep.RegisterAlarm(abs_time, &alarm2);
+ EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
+
+ WaitForAlarm(&ep, alarm);
+
+ EXPECT_TRUE(alarm.was_called());
+ // Make sure that alarm is called only once.
+ EXPECT_EQ(1, alarm.num_called());
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ alarm.Reset();
+}
+
+// Here we make sure that when one alarm unregisters another alarm
+// (that is supposed to be registered again because its OnAlarm
+// returned > 0), the alarm thats supposed to be unregistered does
+// actually gets unregistered.
+TEST(SimpleEpollServerTest, TestAlarmsOneOnAlarmUnRegistersAnotherAlarm) {
+ TestAlarm alarm;
+ TestAlarmThatUnregistersAnotherAlarm alarm2;
+ EpollTestServer ep;
+ ep.set_timeout_in_us(-1);
+
+ int64_t alarm_time = 1;
+ int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
+
+ // This will make the alarm re-register when OnAlarm is called.
+ alarm.set_absolute_time(true);
+ alarm.set_time_before_next_alarm(abs_time + 2);
+
+
+ // Register two alarms and make sure we wait long enough to hit it.
+ ep.RegisterAlarm(abs_time, &alarm);
+ // This would cause us to unregister alarm when OnAlarm is called
+ // on alarm2.
+ alarm2.SetUnregisterAlarm(&alarm, &ep);
+ ep.RegisterAlarm(abs_time + 1, &alarm2);
+ EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
+
+ WaitForAlarm(&ep, alarm);
+
+ EXPECT_TRUE(alarm.was_called());
+ // Make sure that alarm is called only once.
+ EXPECT_EQ(1, alarm.num_called());
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ alarm.Reset();
+}
+
+// Check a couple of alarm times to make sure they're falling within a
+// reasonable range.
+TEST(SimpleEpollServerTest, TestRepeatAlarms) {
+ EpollTestServer ep;
+ TestAlarm alarm;
+
+ int alarm_time = 20;
+
+ // Register an alarm and make sure we wait long enough to hit it.
+ ep.set_timeout_in_us(alarm_time * 1000 * 2);
+ alarm.set_time_before_next_alarm(1000*alarm_time);
+ ep.RegisterAlarm(WallTimeNowInUsec() + alarm_time, &alarm);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+
+ WaitForAlarm(&ep, alarm);
+ // When we wake up it should be because the Alarm has been called, and has
+ // registered itself to be called again.
+
+ // Make sure the first alarm was called properly.
+ EXPECT_TRUE(alarm.was_called());
+
+ // Resetting means that the alarm is no longer a recurring alarm. It will be
+ // called once more and then stop.
+ alarm.Reset();
+
+ // Make sure the alarm is called one final time.
+ EXPECT_EQ(1, ep.GetNumPendingAlarmsForTest());
+ ep.set_timeout_in_us(alarm_time * 1000 * 2);
+ WaitForAlarm(&ep, alarm);
+
+ EXPECT_TRUE(alarm.was_called());
+ alarm.Reset();
+
+ // The alarm was a one-time thing. Make sure that we don't hit it again.
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_FALSE(alarm.was_called());
+}
+
+// Verify that an alarm that repeats itself in the past works properly.
+TEST(SimpleEpollServerTest, TestRepeatAlarmInPast) {
+ EpollTestServer ep;
+ TestAlarm alarm;
+
+ int64_t alarm_time = 20;
+ int64_t abs_time = WallTimeNowInUsec() + alarm_time * 1000;
+
+ // Make the alarm re-register in the past when OnAlarm is called.
+ alarm.set_absolute_time(true);
+ alarm.set_time_before_next_alarm(abs_time - 1000);
+
+ // Register the alarm and make sure we wait long enough to hit it.
+ ep.set_timeout_in_us(alarm_time * 1000 * 2);
+ ep.RegisterAlarm(abs_time, &alarm);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+
+ WaitForAlarm(&ep, alarm);
+ // When we wake up it should be because the Alarm has been called, and has
+ // registered itself to be called again.
+
+ // Make sure the first alarm was called properly.
+ EXPECT_TRUE(alarm.was_called());
+
+ // Resetting means that the alarm is no longer a recurring alarm. It will be
+ // called once more and then stop.
+ alarm.Reset();
+
+ // Make sure the alarm is called one final time.
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ ep.set_timeout_in_us(alarm_time * 1000 * 2);
+ WaitForAlarm(&ep, alarm);
+
+ EXPECT_TRUE(alarm.was_called());
+ alarm.Reset();
+
+ // The alarm was a one-time thing. Make sure that we don't hit it again.
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_FALSE(alarm.was_called());
+}
+
+class EpollTestAlarms : public SimpleEpollServer {
+ public:
+ EpollTestAlarms() : SimpleEpollServer() {}
+
+ inline int64_t NowInUsec() const override { return time_; }
+
+ void CallAndReregisterAlarmEvents() override {
+ recorded_now_in_us_ = NowInUsec();
+ SimpleEpollServer::CallAndReregisterAlarmEvents();
+ }
+
+ void set_time(int64_t time) { time_ = time; }
+
+ size_t GetNumPendingAlarmsForTest() const { return alarm_map_.size(); }
+
+ private:
+ int64_t time_;
+};
+
+// Test multiple interleaving alarms to make sure they work right.
+// Pattern is roughly:
+// time: 15 20 30 40
+// alarm: A B A' C
+TEST(SimpleEpollServerTest, TestMultipleAlarms) {
+ EpollTestAlarms ep;
+ TestAlarm alarmA;
+ TestAlarm alarmB;
+ TestAlarm alarmC;
+
+ ep.set_timeout_in_us(50 * 1000 * 2);
+ alarmA.set_time_before_next_alarm(1000 * 30);
+ alarmA.set_absolute_time(true);
+ ep.RegisterAlarm(15 * 1000, &alarmA);
+ ep.RegisterAlarm(20 * 1000, &alarmB);
+ ep.RegisterAlarm(40 * 1000, &alarmC);
+
+ ep.set_time(15 * 1000);
+ ep.CallAndReregisterAlarmEvents(); // A
+ EXPECT_TRUE(alarmA.was_called());
+ EXPECT_FALSE(alarmB.was_called());
+ EXPECT_FALSE(alarmC.was_called());
+ alarmA.Reset(); // Unregister A in the future.
+
+ ep.set_time(20 * 1000);
+ ep.CallAndReregisterAlarmEvents(); // B
+ EXPECT_FALSE(alarmA.was_called());
+ EXPECT_TRUE(alarmB.was_called());
+ EXPECT_FALSE(alarmC.was_called());
+ alarmB.Reset();
+
+ ep.set_time(30 * 1000);
+ ep.CallAndReregisterAlarmEvents(); // A
+ EXPECT_TRUE(alarmA.was_called());
+ EXPECT_FALSE(alarmB.was_called());
+ EXPECT_FALSE(alarmC.was_called());
+ alarmA.Reset();
+
+ ep.set_time(40 * 1000);
+ ep.CallAndReregisterAlarmEvents(); // C
+ EXPECT_FALSE(alarmA.was_called());
+ EXPECT_FALSE(alarmB.was_called());
+ EXPECT_TRUE(alarmC.was_called());
+ alarmC.Reset();
+
+ ep.CallAndReregisterAlarmEvents(); // None.
+ EXPECT_FALSE(alarmA.was_called());
+ EXPECT_FALSE(alarmB.was_called());
+ EXPECT_FALSE(alarmC.was_called());
+}
+
+TEST(SimpleEpollServerTest, TestAlarmOnShutdown) {
+ TestAlarm alarm1;
+ {
+ EpollTestServer ep;
+ const int64_t now = WallTimeNowInUsec();
+ ep.RegisterAlarm(now + 5000, &alarm1);
+ }
+
+ EXPECT_TRUE(alarm1.onshutdown_called());
+}
+
+// Tests that if we have multiple alarms
+// OnShutdown then we handle them properly.
+TEST(SimpleEpollServerTest, TestMultipleAlarmOnShutdown) {
+ TestAlarm alarm1;
+ TestAlarm alarm2;
+ TestAlarm alarm3;
+ {
+ EpollTestServer ep;
+ const int64_t now = WallTimeNowInUsec();
+ ep.RegisterAlarm(now + 5000, &alarm1);
+ ep.RegisterAlarm(now + 9000, &alarm2);
+ ep.RegisterAlarm(now + 9000, &alarm3);
+ }
+
+ EXPECT_TRUE(alarm1.onshutdown_called());
+ EXPECT_TRUE(alarm2.onshutdown_called());
+ EXPECT_TRUE(alarm3.onshutdown_called());
+}
+TEST(SimpleEpollServerTest, TestMultipleAlarmUnregistrationOnShutdown) {
+ TestParentAlarm tp;
+ TestChildAlarm alarm1;
+ TestChildAlarm alarm2;
+ alarm1.set_parent(&tp);
+ alarm2.set_parent(&tp);
+ {
+ EpollTestServer ep;
+ const int64_t now = WallTimeNowInUsec();
+ ep.RegisterAlarm(now + 5000, &alarm1);
+ ep.RegisterAlarm(now + 9000, &alarm2);
+ }
+
+ EXPECT_TRUE(alarm1.onshutdown_called());
+ EXPECT_FALSE(alarm2.onshutdown_called());
+}
+
+// Check an alarm set in the past runs right away.
+TEST(SimpleEpollServerTest, TestPastAlarm) {
+ EpollTestServer ep;
+ TestAlarm alarm;
+
+ // Register the alarm and make sure we wait long enough to hit it.
+ ep.set_timeout_in_us(1000 * 2);
+ ep.RegisterAlarm(WallTimeNowInUsec() - 1000, &alarm);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ alarm.Reset();
+}
+
+// Test Unregistering of Alarms
+TEST(SimpleEpollServerTest, TestUnregisterAlarm) {
+ EpollTestServer ep;
+ SimpleEpollServer::AlarmRegToken temptok;
+
+ TestAlarmUnregister alarm1;
+ TestAlarmUnregister alarm2;
+
+ ep.RegisterAlarm(WallTimeNowInUsec() + 5 * 1000, &alarm1);
+ ep.RegisterAlarm(WallTimeNowInUsec() + 13 * 1000, &alarm2);
+
+ // Unregister an alarm.
+ if (alarm2.get_token(&temptok)) {
+ ep.UnregisterAlarm(temptok);
+ }
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ EXPECT_TRUE(alarm2.onunregistration_called());
+
+ if (alarm1.get_token(&temptok)) {
+ ep.UnregisterAlarm(temptok);
+ }
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+ EXPECT_TRUE(alarm1.onunregistration_called());
+}
+
+// Test Reregistering of Alarms
+TEST(SimpleEpollServerTest, TestReregisterAlarm) {
+ EpollTestAlarms ep;
+ SimpleEpollServer::AlarmRegToken token;
+
+ TestAlarmUnregister alarm;
+ ep.set_time(1000);
+ ep.RegisterAlarm(5000, &alarm);
+
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ ASSERT_TRUE(alarm.get_token(&token));
+ ep.ReregisterAlarm(token, 6000);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+
+ ep.set_time(5000);
+ ep.set_timeout_in_us(0);
+ ep.CallAndReregisterAlarmEvents();
+ EXPECT_FALSE(alarm.was_called());
+
+ ep.set_time(6000);
+ ep.CallAndReregisterAlarmEvents();
+ EXPECT_TRUE(alarm.was_called());
+}
+
+TEST(SimpleEpollServerTest, TestReregisterDeferredAlarm) {
+ EpollTestAlarms ep;
+ ep.set_timeout_in_us(0);
+
+ TestAlarm alarm;
+ TestAlarmThatRegistersAndReregistersAnotherAlarm register_alarm;
+ // Register the alarm in the past so it is added as a deferred alarm.
+ register_alarm.SetRegisterAndReregisterAlarm(&alarm, -500, 500, &ep);
+ ep.set_time(1000);
+ ep.RegisterAlarm(1000, &register_alarm);
+ // Call reregister twice, first to run register_alarm and second to run any
+ // scheduled deferred alarms.
+ ep.CallAndReregisterAlarmEvents();
+ ep.CallAndReregisterAlarmEvents();
+
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ EXPECT_FALSE(alarm.was_called());
+
+ ep.set_time(1500);
+ ep.CallAndReregisterAlarmEvents();
+ EXPECT_TRUE(alarm.was_called());
+}
+
+// Check if an alarm fired and got reregistered, you are able to
+// unregister the second registration.
+TEST(SimpleEpollServerTest, TestFiredReregisteredAlarm) {
+ EpollTestAlarms ep;
+ TestAlarmUnregister alarmA;
+
+ SimpleEpollServer::AlarmRegToken first_token;
+ SimpleEpollServer::AlarmRegToken second_token;
+ bool found;
+
+ ep.set_timeout_in_us(50 * 1000 * 2);
+ alarmA.set_time_before_next_alarm(1000 * 30);
+ alarmA.set_absolute_time(true);
+
+// Alarm A first fires at 15, then 30
+ ep.RegisterAlarm(15 * 1000, &alarmA);
+
+ found = alarmA.get_token(&first_token);
+ EXPECT_TRUE(found);
+
+ ep.set_time(15 * 1000);
+ ep.CallAndReregisterAlarmEvents(); // A
+ EXPECT_TRUE(alarmA.was_called());
+
+ alarmA.Reset();
+
+ found = alarmA.get_token(&second_token);
+ EXPECT_TRUE(found);
+ if (found) {
+ ep.UnregisterAlarm(second_token);
+ }
+
+ ep.set_time(30 * 1000);
+ ep.CallAndReregisterAlarmEvents(); // A
+
+ alarmA.Reset();
+}
+
+// Here we make sure that one alarm can unregister another alarm
+// in OnShutdown().
+TEST(SimpleEpollServerTest, TestAlarmCanUnregistersAnotherAlarmOnShutdown) {
+ TestAlarmThatUnregistersAnotherAlarm alarm1;
+ TestAlarm alarm2;
+ {
+ EpollTestServer ep;
+ // Register two alarms and make alarm1 is placed in queue in front of alarm2
+ // so that when the queue is cleared, alarm1 is processed first.
+ const int64_t now = WallTimeNowInUsec();
+ ep.RegisterAlarm(now + 5000, &alarm1);
+ ep.RegisterAlarm(now + 9000, &alarm2);
+ alarm1.SetUnregisterAlarm(&alarm2, &ep);
+ EXPECT_EQ(2u, ep.GetNumPendingAlarmsForTest());
+ }
+}
+
+class TestAlarmRegisterAnotherAlarmShutdown : public TestAlarmUnregister {
+ public:
+ TestAlarmRegisterAnotherAlarmShutdown(EpollAlarmCallbackInterface* alarm2,
+ int64_t when)
+ : alarm2_(alarm2), when_(when) {}
+ void OnShutdown(SimpleEpollServer* eps) override {
+ TestAlarmUnregister::OnShutdown(eps);
+ eps->RegisterAlarm(when_, alarm2_);
+ }
+
+ private:
+ EpollAlarmCallbackInterface* alarm2_;
+ int64_t when_;
+};
+
+// This tests that alarm registers another alarm when shutting down.
+// The two cases are: new alarm comes before and after the alarm being
+// notified by OnShutdown()
+TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownBeforeSelf) {
+ TestAlarm alarm2;
+ int64_t alarm_time = WallTimeNowInUsec() + 5000;
+ TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time - 1000);
+ {
+ EpollTestAlarms ep;
+ ep.RegisterAlarm(alarm_time, &alarm1);
+ }
+ EXPECT_TRUE(alarm1.onshutdown_called());
+ EXPECT_FALSE(alarm2.onshutdown_called());
+}
+
+TEST(SimpleEpollServerTest, AlarmRegistersAnotherAlarmOnShutdownAfterSelf) {
+ TestAlarm alarm2;
+ int64_t alarm_time = WallTimeNowInUsec() + 5000;
+ TestAlarmRegisterAnotherAlarmShutdown alarm1(&alarm2, alarm_time + 1000);
+ {
+ EpollTestAlarms ep;
+ ep.RegisterAlarm(alarm_time, &alarm1);
+ }
+ EXPECT_TRUE(alarm1.onshutdown_called());
+ EXPECT_TRUE(alarm2.onshutdown_called());
+}
+
+TEST(SimpleEpollServerTest, TestWrite) {
+ SimpleEpollServer ep;
+ ep.set_timeout_in_us(1);
+ char data[kPageSize] = {0};
+
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+ int read_fd = pipe_fds[0];
+ int write_fd = pipe_fds[1];
+
+ RecordingCB recording_cb;
+ const Recorder* recorder = recording_cb.recorder();
+ const std::vector<RecordEntry> *records = recorder->records();
+
+ // Register to listen to write events.
+ ep.RegisterFD(write_fd, &recording_cb, EPOLLOUT | O_NONBLOCK);
+ // At this point the recorder should have the creation and registration
+ // events.
+ EXPECT_EQ(2u, records->size());
+
+ // Fill up the pipe.
+ int written = 1;
+ for (int i = 0; i < 17 && written > 0 ; ++i) {
+ written = write(write_fd, &data, kPageSize);
+ }
+ EXPECT_LT(written, 0);
+
+ // There should be no new events as the pipe is not available for writing.
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(2u, records->size());
+
+ // Now read data from the pipe to make it writable again. This time the
+ // we should get an EPOLLOUT event.
+ int size = read(read_fd, &data, kPageSize);
+ EXPECT_EQ(kPageSize, size);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(3u, records->size());
+
+ // Now unsubscribe from writable events (which adds a modification record)
+ // and wait to verify that no event records are added.
+ ep.StopWrite(write_fd);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(4u, records->size());
+
+ // We had the right number of events all along. Make sure they were actually
+ // the right events.
+ Recorder tmp;
+ tmp.Record(&recording_cb, CREATION, 0, 0);
+ tmp.Record(&recording_cb, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK);
+ tmp.Record(&recording_cb, EVENT, write_fd, EPOLLOUT);
+ tmp.Record(&recording_cb, MODIFICATION, write_fd, O_NONBLOCK);
+
+ EXPECT_TRUE(recorder->IsEqual(&tmp));
+ ep.UnregisterFD(write_fd);
+
+ close(read_fd);
+ close(write_fd);
+}
+
+TEST(SimpleEpollServerTest, TestReadWrite) {
+ SimpleEpollServer ep;
+ ep.set_timeout_in_us(1);
+ char data[kPageSize] = {0};
+
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+ int read_fd = pipe_fds[0];
+ int write_fd = pipe_fds[1];
+
+ RecordingCB recording_cb;
+ const Recorder* recorder = recording_cb.recorder();
+ const std::vector<RecordEntry> *records = recorder->records();
+
+ // Register to listen to read and write events.
+ ep.RegisterFDForReadWrite(read_fd, &recording_cb);
+ // At this point the recorder should have the creation and registration
+ // events.
+ EXPECT_EQ(2u, records->size());
+
+ int written = write(write_fd, &data, kPageSize);
+ EXPECT_EQ(kPageSize, written);
+
+ ep.WaitForEventsAndExecuteCallbacks();
+ ep.UnregisterFD(read_fd);
+
+ close(read_fd);
+ close(write_fd);
+}
+
+TEST(SimpleEpollServerTest, TestMultipleFDs) {
+ SimpleEpollServer ep;
+ ep.set_timeout_in_us(1);
+ char data = 'x';
+
+ int pipe_one[2];
+ if (pipe(pipe_one) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+ int pipe_two[2];
+ if (pipe(pipe_two) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+
+ RecordingCB recording_cb_one;
+ const Recorder* recorder_one = recording_cb_one.recorder();
+ const std::vector<RecordEntry> *records_one = recorder_one->records();
+
+ RecordingCB recording_cb_two;
+ const Recorder* recorder_two = recording_cb_two.recorder();
+ const std::vector<RecordEntry> *records_two = recorder_two->records();
+
+ // Register to listen to read events for both pipes
+ ep.RegisterFDForRead(pipe_one[0], &recording_cb_one);
+ ep.RegisterFDForRead(pipe_two[0], &recording_cb_two);
+
+ EXPECT_EQ(2u, records_one->size());
+ EXPECT_EQ(2u, records_two->size());
+
+ write(pipe_one[1], &data, 1);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(3u, records_one->size());
+ EXPECT_EQ(2u, records_two->size());
+
+ write(pipe_two[1], &data, 1);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(3u, records_one->size());
+ EXPECT_EQ(3u, records_two->size());
+
+ write(pipe_one[1], &data, 1);
+ write(pipe_two[1], &data, 1);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(4u, records_one->size());
+ EXPECT_EQ(4u, records_two->size());
+
+ ep.WaitForEventsAndExecuteCallbacks();
+ ep.UnregisterFD(pipe_one[0]);
+ ep.UnregisterFD(pipe_two[0]);
+ close(pipe_one[0]);
+ close(pipe_one[1]);
+ close(pipe_two[0]);
+ close(pipe_two[1]);
+}
+
+// Check that the SimpleEpollServer calls OnShutdown for any registered FDs.
+TEST(SimpleEpollServerTest, TestFDOnShutdown) {
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+ int read_fd = pipe_fds[0];
+ int write_fd = pipe_fds[1];
+
+ RecordingCB recording_cb1;
+ RecordingCB recording_cb2;
+ const Recorder* recorder1 = recording_cb1.recorder();
+ const Recorder* recorder2 = recording_cb2.recorder();
+
+ {
+ SimpleEpollServer ep;
+ ep.set_timeout_in_us(1);
+
+ // Register to listen to write events.
+ ep.RegisterFD(write_fd, &recording_cb1, EPOLLOUT | O_NONBLOCK);
+ ep.RegisterFD(read_fd, &recording_cb2, EPOLLIN | O_NONBLOCK);
+ }
+
+ // Make sure OnShutdown was called for both callbacks.
+ Recorder write_recorder;
+ write_recorder.Record(&recording_cb1, CREATION, 0, 0);
+ write_recorder.Record(
+ &recording_cb1, REGISTRATION, write_fd, EPOLLOUT | O_NONBLOCK);
+ write_recorder.Record(&recording_cb1, UNREGISTRATION, write_fd, false);
+ write_recorder.Record(&recording_cb1, SHUTDOWN, write_fd, 0);
+ EXPECT_TRUE(recorder1->IsEqual(&write_recorder));
+
+ Recorder read_recorder;
+ read_recorder.Record(&recording_cb2, CREATION, 0, 0);
+ read_recorder.Record(
+ &recording_cb2, REGISTRATION, read_fd, EPOLLIN | O_NONBLOCK);
+ read_recorder.Record(&recording_cb2, UNREGISTRATION, read_fd, false);
+ read_recorder.Record(&recording_cb2, SHUTDOWN, read_fd, 0);
+ EXPECT_TRUE(recorder2->IsEqual(&read_recorder));
+
+ close(read_fd);
+ close(write_fd);
+}
+
+class UnregisterCB : public EpollCallbackInterface {
+ public:
+ explicit UnregisterCB(int fd)
+ : eps_(nullptr), fd_(fd), onshutdown_called_(false) {
+ }
+
+ ~UnregisterCB() override {
+ }
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {
+ eps_->UnregisterFD(fd_);
+ eps_->UnregisterFD(fd);
+ onshutdown_called_ = true;
+ eps_ = nullptr;
+ }
+
+ void set_epollserver(SimpleEpollServer* eps) { eps_ = eps; }
+ bool onshutdown_called() { return onshutdown_called_; }
+
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ }
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, EpollEvent* event) override {}
+ void OnUnregistration(int fd, bool replaced) override {}
+
+ std::string Name() const override { return "UnregisterCB"; }
+
+ protected:
+ SimpleEpollServer* eps_;
+ int fd_;
+ bool onshutdown_called_;
+};
+
+// Check that unregistering fds in OnShutdown works cleanly.
+TEST(SimpleEpollServerTest, TestUnregisteringFDsOnShutdown) {
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+ int read_fd = pipe_fds[0];
+ int write_fd = pipe_fds[1];
+
+ UnregisterCB unreg_cb1(read_fd);
+ UnregisterCB unreg_cb2(write_fd);
+
+ {
+ SimpleEpollServer ep;
+ ep.set_timeout_in_us(1);
+
+ unreg_cb1.set_epollserver(&ep);
+ unreg_cb2.set_epollserver(&ep);
+
+ // Register to listen to write events.
+ ep.RegisterFD(write_fd, &unreg_cb1, EPOLLOUT | O_NONBLOCK);
+ ep.RegisterFD(read_fd, &unreg_cb2, EPOLLIN | O_NONBLOCK);
+ }
+
+ // Make sure at least one onshutdown was called.
+ EXPECT_TRUE(unreg_cb1.onshutdown_called() ||
+ unreg_cb2.onshutdown_called());
+ // Make sure that both onshutdowns were not called.
+ EXPECT_TRUE(!(unreg_cb1.onshutdown_called() &&
+ unreg_cb2.onshutdown_called()));
+
+ close(read_fd);
+ close(write_fd);
+}
+
+TEST(SimpleEpollServerTest, TestFDsAndAlarms) {
+ SimpleEpollServer ep;
+ ep.set_timeout_in_us(5);
+ char data = 'x';
+
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+
+ RecordingCB recording_cb;
+ const Recorder* recorder = recording_cb.recorder();
+ const std::vector<RecordEntry> *records = recorder->records();
+
+ TestAlarm alarm;
+
+ ep.RegisterFDForRead(pipe_fds[0], &recording_cb);
+
+ EXPECT_EQ(2u, records->size());
+ EXPECT_FALSE(alarm.was_called());
+
+ // Write to the pipe and set a longish alarm so we get a read event followed
+ // by an alarm event.
+ int written = write(pipe_fds[1], &data, 1);
+ EXPECT_EQ(1, written);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(3u, records->size());
+ EXPECT_FALSE(alarm.was_called());
+ ep.RegisterAlarm(WallTimeNowInUsec() + 1000, &alarm);
+ WaitForAlarm(&ep, alarm);
+ EXPECT_EQ(3u, records->size());
+ EXPECT_TRUE(alarm.was_called());
+ alarm.Reset();
+
+ // Now set a short alarm so the alarm and the read event are called together.
+ ep.RegisterAlarm(WallTimeNowInUsec(), &alarm);
+ written = write(pipe_fds[1], &data, 1);
+ EXPECT_EQ(1, written);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_TRUE(alarm.was_called());
+ EXPECT_EQ(4u, records->size());
+
+ ep.UnregisterFD(pipe_fds[0]);
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+}
+
+class EpollReader: public EpollCallbackInterface {
+ public:
+ explicit EpollReader(int len)
+ : len_(0),
+ expected_len_(len),
+ done_reading_(false) {
+ memset(&buf_, 0, kMaxBufLen);
+ }
+
+ ~EpollReader() override {}
+
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ }
+
+ void OnModification(int fd, int event_mask) override {}
+
+ void OnEvent(int fd, EpollEvent* event) override {
+ if (event->in_events & EPOLLIN) {
+ len_ += read(fd, &buf_ + len_, kMaxBufLen - len_);
+ }
+
+ // If we have finished reading...
+ if (event->in_events & EPOLLHUP) {
+ CHECK_EQ(len_, expected_len_);
+ done_reading_ = true;
+ }
+ }
+
+ void OnUnregistration(int fd, bool replaced) override {}
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {
+ // None of the current tests involve having active callbacks when the
+ // server shuts down.
+ EPOLL_LOG(FATAL);
+ }
+
+ std::string Name() const override { return "EpollReader"; }
+
+ // Returns true if the data in buf is the same as buf_, false otherwise.
+ bool CheckOutput(char* buf, int len) {
+ if (len != len_) {
+ return false;
+ }
+ return !memcmp(buf, buf_, len);
+ }
+
+ bool done_reading() { return done_reading_; }
+
+ protected:
+ int len_;
+ int expected_len_;
+ char buf_[kMaxBufLen];
+ bool done_reading_;
+};
+
+void TestPipe(char *test_message, int len) {
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ PLOG(FATAL) << "pipe failed()";
+ }
+ int reader_pipe = pipe_fds[0];
+ int writer_pipe = pipe_fds[1];
+ int child_pid;
+ memset(test_message, 'x', len);
+
+ switch (child_pid = fork()) {
+ case 0: { // Child will send message.
+ const char *message = test_message;
+ int size;
+ close(reader_pipe);
+ while ((size = write(writer_pipe, message, len)) > 0) {
+ message += size;
+ len -= size;
+ if (len == 0) {
+ break;
+ }
+ }
+ if (len > 0) {
+ PLOG(FATAL) << "write() failed";
+ }
+ close(writer_pipe);
+
+ _exit(0);
+ }
+ case -1:
+ PLOG(FATAL) << "fork() failed";
+ break;
+ default: { // Parent will receive message.
+ close(writer_pipe);
+ auto ep = EpollMakeUnique<SimpleEpollServer>();
+ ep->set_timeout_in_us(1);
+ EpollReader reader(len);
+ ep->RegisterFD(reader_pipe, &reader, EPOLLIN);
+
+ int64_t start_ms = WallTimeNowInUsec() / 1000;
+ // Loop until we're either done reading, or have waited ~10 us.
+ while (!reader.done_reading() &&
+ (WallTimeNowInUsec() / 1000 - start_ms) < 10000) {
+ ep->WaitForEventsAndExecuteCallbacks();
+ }
+ ep->UnregisterFD(reader_pipe);
+ CHECK(reader.CheckOutput(test_message, len));
+ break;
+ }
+ }
+
+ close(reader_pipe);
+ close(writer_pipe);
+}
+
+TEST(SimpleEpollServerTest, TestSmallPipe) {
+ char buf[kMaxBufLen];
+ TestPipe(buf, 10);
+}
+
+TEST(SimpleEpollServerTest, TestLargePipe) {
+ char buf[kMaxBufLen];
+ TestPipe(buf, kMaxBufLen);
+}
+
+// Tests RegisterFDForRead as well as StopRead.
+TEST(SimpleEpollServerTest, TestRead) {
+ SimpleEpollServer ep;
+ ep.set_timeout_in_us(1);
+ int len = 1;
+
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+ int read_fd = pipe_fds[0];
+ int write_fd = pipe_fds[1];
+
+ auto reader = EpollMakeUnique<EpollReader>(len);
+
+ // Check that registering a FD for read alerts us when there is data to be
+ // read.
+ ep.RegisterFDForRead(read_fd, reader.get());
+ char data = 'a';
+ int size = write(write_fd, &data, 1);
+ EXPECT_EQ(1, size);
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_TRUE(reader->CheckOutput(&data, len));
+
+ // Remove the callback for read events, write to the pipe and make sure that
+ // we did not read more data.
+ ep.StopRead(read_fd);
+ size = write(write_fd, &data, len);
+ EXPECT_EQ(1, size);
+ // The wait will return after timeout.
+ ep.WaitForEventsAndExecuteCallbacks();
+ EXPECT_TRUE(reader->CheckOutput(&data, len));
+ ep.UnregisterFD(read_fd);
+
+ close(read_fd);
+ close(write_fd);
+}
+
+class EdgeTriggerCB : public EpollCallbackInterface {
+ public:
+ EdgeTriggerCB(int read_size, int write_size, char write_char, char peer_char)
+ : eps_(nullptr),
+ read_buf_(read_size),
+ write_buf_(write_size, write_char),
+ peer_char_(peer_char) {
+ Reset();
+ }
+
+ ~EdgeTriggerCB() override {}
+
+ void Reset() {
+ CHECK(eps_ == nullptr);
+ bytes_read_ = 0;
+ bytes_written_ = 0;
+ can_read_ = false;
+ will_read_ = false;
+ can_write_ = false;
+ will_write_ = false;
+ read_closed_ = false;
+ write_closed_ = false;
+ }
+
+ void ResetByteCounts() {
+ bytes_read_ = bytes_written_ = 0;
+ }
+
+ void set_will_read(bool will_read) { will_read_ = will_read; }
+
+ void set_will_write(bool will_write) { will_write_ = will_write; }
+
+ bool can_write() const { return can_write_; }
+
+ int bytes_read() const { return bytes_read_; }
+
+ int bytes_written() const { return bytes_written_; }
+
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ EXPECT_TRUE(eps_ == nullptr);
+ eps_ = eps;
+ Initialize(fd, event_mask);
+ }
+
+ void OnModification(int fd, int event_mask) override {
+ EXPECT_TRUE(eps_ != nullptr);
+ if (event_mask & EPOLLET) {
+ Initialize(fd, event_mask);
+ } else {
+ eps_->SetFDNotReady(fd);
+ }
+ }
+
+ void OnEvent(int fd, EpollEvent* event) override {
+ const int event_mask = event->in_events;
+ if (event_mask & (EPOLLHUP | EPOLLERR)) {
+ write_closed_ = true;
+ return;
+ }
+ if (will_read_ && event_mask & EPOLLIN) {
+ EXPECT_FALSE(read_closed_);
+ int read_size = read_buf_.size();
+ memset(&read_buf_[0], 0, read_size);
+ int len = recv(fd, &read_buf_[0], read_size, MSG_DONTWAIT);
+ // Update the readiness states
+ can_read_ = (len == read_size);
+
+ if (len > 0) {
+ bytes_read_ += len;
+ EPOLL_VLOG(1) << "fd: " << fd << ", read " << len
+ << ", total: " << bytes_read_;
+ // Now check the bytes read
+ EXPECT_TRUE(CheckReadBuffer(len));
+ } else if (len < 0) {
+ EPOLL_VLOG(1) << "fd: " << fd << " read hit EAGAIN";
+ EXPECT_EQ(EAGAIN, errno) << strerror(errno);
+ can_read_ = false;
+ } else {
+ read_closed_ = true;
+ }
+ }
+ if (will_write_ && event_mask & EPOLLOUT) {
+ // Write side close/full close can only detected by EPOLLHUP, which is
+ // caused by EPIPE.
+ EXPECT_FALSE(write_closed_);
+ int write_size = write_buf_.size();
+ int len = send(fd, &write_buf_[0], write_size, MSG_DONTWAIT);
+ can_write_ = (len == write_size);
+ if (len > 0) {
+ bytes_written_ += len;
+ EPOLL_VLOG(1) << "fd: " << fd << ", write " << len
+ << ", total: " << bytes_written_;
+ } else {
+ EPOLL_VLOG(1) << "fd: " << fd << " write hit EAGAIN";
+ EXPECT_EQ(EAGAIN, errno) << strerror(errno);
+ can_write_ = false;
+ }
+ }
+ // Since we can only get on the ready list once, wait till we confirm both
+ // read and write side continuation state and set the correct event mask
+ // for the ready list.
+ event->out_ready_mask = can_read_ ? EPOLLIN : 0;
+ if (can_write_) {
+ event->out_ready_mask |= EPOLLOUT;
+ }
+ }
+
+ void OnUnregistration(int fd, bool replaced) override {
+ EXPECT_TRUE(eps_ != nullptr);
+ eps_ = nullptr;
+ }
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {
+ // None of the current tests involve having active callbacks when the
+ // server shuts down.
+ EPOLL_LOG(FATAL);
+ }
+
+ std::string Name() const override { return "EdgeTriggerCB"; }
+
+ private:
+ SimpleEpollServer* eps_;
+ std::vector<char> read_buf_;
+ int bytes_read_;
+ std::vector<char> write_buf_;
+ int bytes_written_;
+ char peer_char_; // The char we expected to read.
+ bool can_read_;
+ bool will_read_;
+ bool can_write_;
+ bool will_write_;
+ bool read_closed_;
+ bool write_closed_;
+
+ void Initialize(int fd, int event_mask) {
+ CHECK(eps_);
+ can_read_ = can_write_ = false;
+ if (event_mask & EPOLLET) {
+ int events = 0;
+ if (event_mask & EPOLLIN) {
+ events |= EPOLLIN;
+ can_read_ = true;
+ }
+ if (event_mask & EPOLLOUT) {
+ events |= EPOLLOUT;
+ can_write_ = true;
+ }
+ eps_->SetFDReady(fd, events);
+ }
+ }
+
+ bool CheckReadBuffer(int len) const {
+ for (int i = 0; i < len; ++i) {
+ if (peer_char_ != read_buf_[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+// Test adding and removing from the ready list.
+TEST(SimpleEpollServerTest, TestReadyList) {
+ SimpleEpollServer ep;
+ int pipe_fds[2];
+ if (pipe(pipe_fds) < 0) {
+ EPOLL_PLOG(FATAL) << "pipe() failed";
+ }
+
+ // Just use any CB will do, since we never wait on epoll events.
+ EdgeTriggerCB reader1(0, 0, 0, 0);
+ EdgeTriggerCB reader2(0, 0, 0, 0);
+
+ ep.RegisterFD(pipe_fds[0], &reader1, EPOLLIN);
+ ep.RegisterFD(pipe_fds[1], &reader2, EPOLLOUT);
+
+ // Adding fds that are registered with eps
+ EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
+ EXPECT_FALSE(ep.IsFDReady(pipe_fds[1]));
+
+ ep.SetFDReady(pipe_fds[0], EPOLLIN);
+ EXPECT_TRUE(ep.IsFDReady(pipe_fds[0]));
+ EXPECT_FALSE(ep.IsFDReady(pipe_fds[1]));
+ EXPECT_EQ(1u, ep.ReadyListSize());
+ ep.SetFDReady(pipe_fds[1], EPOLLOUT);
+ EXPECT_TRUE(ep.IsFDReady(pipe_fds[0]));
+ EXPECT_TRUE(ep.IsFDReady(pipe_fds[1]));
+ EXPECT_EQ(2u, ep.ReadyListSize());
+
+ // Now check that SetFDNotReady doesn't affect other fds
+ ep.SetFDNotReady(pipe_fds[0]);
+ EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
+ EXPECT_TRUE(ep.IsFDReady(pipe_fds[1]));
+ EXPECT_EQ(1u, ep.ReadyListSize());
+
+ ep.UnregisterFD(pipe_fds[0]);
+ ep.UnregisterFD(pipe_fds[1]);
+ EXPECT_EQ(0u, ep.ReadyListSize());
+
+ // Now try adding them when they are not registered, and it shouldn't work.
+ ep.SetFDReady(pipe_fds[0], EPOLLIN);
+ EXPECT_FALSE(ep.IsFDReady(pipe_fds[0]));
+ EXPECT_EQ(0u, ep.ReadyListSize());
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+}
+
+class EPSWaitThread : public EpollThread {
+ public:
+ explicit EPSWaitThread(SimpleEpollServer* eps)
+ : EpollThread("EPSWait"), eps_(eps), done_(false) {}
+
+ void Run() override {
+ eps_->WaitForEventsAndExecuteCallbacks();
+ }
+
+ bool done() { return done_; }
+ private:
+ SimpleEpollServer* eps_;
+ bool done_;
+};
+
+TEST(EpollServerTest, TestWake) {
+ SimpleEpollServer eps;
+ eps.set_timeout_in_us(-1);
+ EPSWaitThread eps_thread(&eps);
+ eps_thread.Start();
+
+ EXPECT_FALSE(eps_thread.done());
+ eps.Wake();
+ eps_thread.Join();
+}
+
+class UnRegisterWhileProcessingCB: public EpollCallbackInterface {
+ public:
+ explicit UnRegisterWhileProcessingCB(int fd) : eps_(nullptr), fd_(fd) {}
+
+ ~UnRegisterWhileProcessingCB() override {
+ }
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {}
+
+ void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ }
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, EpollEvent* event) override {
+ // This should cause no problems.
+ eps_->UnregisterFD(fd_);
+ }
+ void OnUnregistration(int fd, bool replaced) override {}
+ std::string Name() const override { return "UnRegisterWhileProcessingCB"; }
+
+ protected:
+ SimpleEpollServer* eps_;
+ int fd_;
+};
+
+class RegisterWhileProcessingCB: public EpollCallbackInterface {
+ public:
+ RegisterWhileProcessingCB(int fd, EpollCallbackInterface* cb)
+ : eps_(nullptr), fd_(fd), cb_(cb) {}
+
+ ~RegisterWhileProcessingCB() override {
+ }
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {}
+
+ void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ }
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, EpollEvent* event) override {
+ // This should cause no problems.
+ eps_->RegisterFDForReadWrite(fd_, cb_);
+ }
+ void OnUnregistration(int fd, bool replaced) override {}
+ std::string Name() const override { return "RegisterWhileProcessingCB"; }
+
+ protected:
+ SimpleEpollServer* eps_;
+ int fd_;
+ EpollCallbackInterface* cb_;
+};
+
+// Nothing bad should happen when we do this. We're -only-
+// testing that nothing bad occurs in this test.
+TEST(SimpleEpollServerTest, NothingBadWhenUnRegisteringFDWhileProcessingIt) {
+ UnRegisterWhileProcessingCB cb(0);
+ {
+ FakeSimpleEpollServer epoll_server;
+ cb.set_epoll_server(&epoll_server);
+ epoll_server.RegisterFDForReadWrite(0, &cb);
+ epoll_event ee;
+ ee.data.fd = 0;
+ epoll_server.AddEvent(0, ee);
+ epoll_server.AdvanceBy(1);
+ epoll_server.WaitForEventsAndExecuteCallbacks();
+ }
+}
+
+//
+// testing that nothing bad occurs in this test.
+TEST(SimpleEpollServerTest,
+ NoEventsDeliveredForFdsOfUnregisteredCBsWithReRegdFD) {
+ // events: fd0, fd1, fd2
+ // fd0 -> unreg fd2
+ // fd1 -> reg fd2
+ // fd2 -> no event should be seen
+ RecordingCB recorder_cb;
+ UnRegisterWhileProcessingCB unreg_cb(-3);
+ RegisterWhileProcessingCB reg_other_cb(-3, &recorder_cb);
+ {
+ FakeSimpleEpollServer epoll_server;
+ unreg_cb.set_epoll_server(&epoll_server);
+ reg_other_cb.set_epoll_server(&epoll_server);
+ epoll_server.RegisterFDForReadWrite(-1, &unreg_cb);
+ epoll_server.RegisterFDForReadWrite(-2, &reg_other_cb);
+ epoll_server.RegisterFDForReadWrite(-3, &recorder_cb);
+
+ epoll_event ee;
+ ee.events = EPOLLIN; // asserted for all events for this test.
+
+ // Note that these events are in 'backwards' order in terms of time.
+ // Currently, the SimpleEpollServer code invokes the CBs from last delivered
+ // to first delivered, so this is to be sure that we invoke the CB for -1
+ // before -2, before -3.
+ ee.data.fd = -1;
+ epoll_server.AddEvent(2, ee);
+ ee.data.fd = -2;
+ epoll_server.AddEvent(1, ee);
+ ee.data.fd = -3;
+ epoll_server.AddEvent(0, ee);
+
+ epoll_server.AdvanceBy(5);
+ epoll_server.WaitForEventsAndExecuteCallbacks();
+ }
+
+ Recorder correct_recorder;
+ correct_recorder.Record(&recorder_cb, CREATION, 0, 0);
+ correct_recorder.Record(&recorder_cb, REGISTRATION, -3,
+ EPOLLIN | EPOLLOUT);
+ correct_recorder.Record(&recorder_cb, UNREGISTRATION, -3, 0);
+ correct_recorder.Record(&recorder_cb, REGISTRATION, -3,
+ EPOLLIN | EPOLLOUT);
+ correct_recorder.Record(&recorder_cb, SHUTDOWN, -3, 0);
+
+ EXPECT_TRUE(correct_recorder.IsEqual(recorder_cb.recorder()));
+}
+
+class ReRegWhileReadyListOnEvent: public EpollCallbackInterface {
+ public:
+ explicit ReRegWhileReadyListOnEvent(int fd) : eps_(nullptr) {}
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {}
+
+ void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ }
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, EpollEvent* event) override {
+ // This should cause no problems.
+ eps_->UnregisterFD(fd);
+ eps_->RegisterFDForReadWrite(fd, this);
+ eps_->UnregisterFD(fd);
+ }
+ void OnUnregistration(int fd, bool replaced) override {}
+ std::string Name() const override { return "ReRegWhileReadyListOnEvent"; }
+
+ protected:
+ SimpleEpollServer* eps_;
+};
+
+// Nothing bad should happen when we do this. We're -only-
+// testing that nothing bad occurs in this test.
+TEST(SimpleEpollServerTest,
+ NothingBadWhenReRegisteringFDWhileProcessingFromReadyList) {
+ ReRegWhileReadyListOnEvent cb(0);
+ {
+ FakeSimpleEpollServer epoll_server;
+ cb.set_epoll_server(&epoll_server);
+ epoll_server.RegisterFDForReadWrite(0, &cb);
+ epoll_event ee;
+ ee.data.fd = 0;
+ epoll_server.AddEvent(0, ee);
+ epoll_server.AdvanceBy(1);
+ epoll_server.WaitForEventsAndExecuteCallbacks();
+ }
+}
+
+class UnRegEverythingReadyListOnEvent: public EpollCallbackInterface {
+ public:
+ UnRegEverythingReadyListOnEvent() : eps_(nullptr), fd_(0), fd_range_(0) {}
+
+ void set_fd(int fd) { fd_ = fd; }
+ void set_fd_range(int fd_range) { fd_range_ = fd_range; }
+ void set_num_called(int* num_called) { num_called_ = num_called; }
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {}
+
+ void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ eps->SetFDReady(fd, EPOLLIN);
+ }
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, EpollEvent* event) override {
+ // This should cause no problems.
+ CHECK(num_called_ != nullptr);
+ ++(*num_called_);
+ // Note that we're iterating from -fd_range + 1 -> 0.
+ // We do this because there is an FD installed into the
+ // epollserver somewhere in the low numbers.
+ // Using negative FD numbers (which are guaranteed to not
+ // exist in the epoll-server) ensures that we will not
+ // come in conflict with the preexisting FD.
+ for (int i = -fd_range_ + 1; i <= 0; ++i) {
+ eps_->UnregisterFD(i);
+ }
+ }
+ void OnUnregistration(int fd, bool replaced) override {}
+ std::string Name() const override {
+ return "UnRegEverythingReadyListOnEvent";
+ }
+
+ protected:
+ SimpleEpollServer* eps_;
+ int fd_;
+ int fd_range_;
+ int* num_called_;
+};
+
+TEST(SimpleEpollServerTest,
+ NothingBadWhenUnRegisteredWhileProcessingFromReadyList) {
+ const size_t kNumCallbacks = 32u;
+ UnRegEverythingReadyListOnEvent callbacks[kNumCallbacks];
+ int num_called = 0;
+ {
+ FakeSimpleEpollServer epoll_server;
+ for (size_t i = 0; i < kNumCallbacks; ++i) {
+ callbacks[i].set_fd(-i);
+ callbacks[i].set_fd_range(kNumCallbacks);
+ callbacks[i].set_num_called(&num_called);
+ callbacks[i].set_epoll_server(&epoll_server);
+ epoll_server.RegisterFDForReadWrite(0, &callbacks[i]);
+ epoll_event ee;
+ ee.data.fd = -i;
+ epoll_server.AddEvent(0, ee);
+ }
+ epoll_server.AdvanceBy(1);
+ epoll_server.WaitForEventsAndExecuteCallbacks();
+ epoll_server.WaitForEventsAndExecuteCallbacks();
+ }
+ EXPECT_EQ(1, num_called);
+}
+
+TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithNothingInList) {
+ FakeSimpleEpollServer epoll_server;
+ epoll_server.VerifyReadyList();
+}
+
+TEST(SimpleEpollServerTest, TestThatVerifyReadyListWorksWithStuffInLists) {
+ FakeSimpleEpollServer epoll_server;
+ epoll_server.VerifyReadyList();
+}
+
+TEST(SimpleEpollServerTest,
+ ApproximateNowInUsAccurateOutideOfWaitForEventsAndExecuteCallbacks) {
+ FakeSimpleEpollServer epoll_server;
+ epoll_server.AdvanceBy(1232);
+ EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec());
+ epoll_server.AdvanceBy(1111);
+ EXPECT_EQ(epoll_server.ApproximateNowInUsec(), epoll_server.NowInUsec());
+}
+
+class ApproximateNowInUsecTestCB: public EpollCallbackInterface {
+ public:
+ ApproximateNowInUsecTestCB() : feps_(nullptr), called_(false) {}
+
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ }
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, EpollEvent* event) override {
+ EXPECT_EQ(feps_->ApproximateNowInUsec(), feps_->NowInUsec());
+ feps_->AdvanceBy(1111);
+ EXPECT_EQ(1 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec());
+ feps_->AdvanceBy(1111);
+ EXPECT_EQ(2 * 1111 + feps_->ApproximateNowInUsec(), feps_->NowInUsec());
+ called_ = true;
+ }
+ void OnUnregistration(int fd, bool replaced) override {}
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {}
+ std::string Name() const override { return "ApproximateNowInUsecTestCB"; }
+
+ void set_fakeepollserver(FakeSimpleEpollServer* feps) { feps_ = feps; }
+ bool called() const { return called_; }
+
+ protected:
+ FakeSimpleEpollServer* feps_;
+ bool called_;
+};
+
+TEST(SimpleEpollServerTest,
+ ApproximateNowInUsApproximateInsideOfWaitForEventsAndExecuteCallbacks) {
+ int dummy_fd = 11111;
+ ApproximateNowInUsecTestCB aniutcb;
+ {
+ FakeSimpleEpollServer epoll_server;
+ aniutcb.set_fakeepollserver(&epoll_server);
+
+ epoll_server.RegisterFD(dummy_fd, &aniutcb, EPOLLIN);
+ epoll_event ee;
+ ee.data.fd = dummy_fd;
+ ee.events = EPOLLIN;
+ epoll_server.AddEvent(10242, ee);
+ epoll_server.set_timeout_in_us(-1);
+ epoll_server.AdvanceByAndWaitForEventsAndExecuteCallbacks(20000);
+ EXPECT_TRUE(aniutcb.called());
+ }
+}
+
+// A mock epoll server that also simulates kernel delay in scheduling epoll
+// events.
+class FakeEpollServerWithDelay : public FakeSimpleEpollServer {
+ public:
+ FakeEpollServerWithDelay() : FakeSimpleEpollServer(), delay(0) {}
+
+ int delay;
+
+ protected:
+ int epoll_wait_impl(int epfd, struct epoll_event* events, int max_events,
+ int timeout_in_ms) override {
+ int out = FakeSimpleEpollServer::epoll_wait_impl(epfd, events, max_events,
+ timeout_in_ms);
+ AdvanceBy(delay);
+ return out;
+ }
+};
+
+// A callback that records the epoll event's delay.
+class RecordDelayOnEvent: public EpollCallbackInterface {
+ public:
+ RecordDelayOnEvent() : last_delay(-1), eps_(nullptr) {}
+
+ ~RecordDelayOnEvent() override {
+ }
+
+ void OnShutdown(SimpleEpollServer* eps, int fd) override {}
+
+ std::string Name() const override { return "RecordDelayOnEvent"; }
+
+ void set_epoll_server(SimpleEpollServer* eps) { eps_ = eps; }
+ void OnRegistration(SimpleEpollServer* eps, int fd, int event_mask) override {
+ }
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, EpollEvent* event) override {
+ last_delay = eps_->LastDelayInUsec();
+ }
+ void OnUnregistration(int fd, bool replaced) override {}
+
+ int64_t last_delay;
+
+ protected:
+ SimpleEpollServer* eps_;
+};
+
+// Tests that an epoll callback sees the correct delay for its event when it
+// calls LastDelayInUsec().
+TEST(EpollServerTest, TestLastDelay) {
+ RecordDelayOnEvent cb;
+ FakeEpollServerWithDelay epoll_server;
+
+ cb.set_epoll_server(&epoll_server);
+
+ epoll_server.RegisterFDForReadWrite(0, &cb);
+ epoll_event ee;
+ ee.data.fd = 0;
+
+ // Inject delay, and confirm that it's reported.
+ epoll_server.set_timeout_in_us(5000);
+ epoll_server.delay = 6000;
+ epoll_server.AddEvent(0, ee);
+ epoll_server.AdvanceBy(1);
+ epoll_server.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(cb.last_delay, 1000);
+
+ // Fire an event before the timeout ends, and confirm that reported delay
+ // isn't negative.
+ epoll_server.set_timeout_in_us(5000);
+ epoll_server.delay = 0;
+ epoll_server.AddEvent(0, ee);
+ epoll_server.AdvanceBy(1);
+ epoll_server.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(cb.last_delay, 0);
+
+ // Wait forever until an event fires, and confirm there's no reported delay.
+ epoll_server.set_timeout_in_us(-1);
+ epoll_server.delay = 6000;
+ epoll_server.AddEvent(0, ee);
+ epoll_server.AdvanceBy(1);
+ epoll_server.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(cb.last_delay, 0);
+}
+
+TEST(SimpleEpollServerAlarmTest, TestShutdown) {
+ std::unique_ptr<SimpleEpollServer> eps(new SimpleEpollServer);
+ EpollAlarm alarm1;
+ EpollAlarm alarm2;
+
+ eps->RegisterAlarmApproximateDelta(10000000, &alarm1);
+ eps->RegisterAlarmApproximateDelta(10000000, &alarm2);
+
+ alarm2.UnregisterIfRegistered();
+ EXPECT_FALSE(alarm2.registered());
+ eps = nullptr;
+
+ EXPECT_FALSE(alarm1.registered());
+}
+
+TEST(SimpleEpollServerAlarmTest, TestUnregister) {
+ SimpleEpollServer eps;
+ EpollAlarm alarm;
+
+ eps.RegisterAlarmApproximateDelta(10000000, &alarm);
+ EXPECT_TRUE(alarm.registered());
+
+ alarm.UnregisterIfRegistered();
+ EXPECT_FALSE(alarm.registered());
+
+ alarm.UnregisterIfRegistered();
+ EXPECT_FALSE(alarm.registered());
+}
+
+TEST(SimpleEpollServerAlarmTest, TestUnregisterOnDestruction) {
+ EpollTestServer eps;
+ std::unique_ptr<EpollAlarm> alarm(new EpollAlarm());
+ EpollAlarm* alarm_ptr = alarm.get();
+
+ eps.RegisterAlarmApproximateDelta(10000000, alarm.get());
+ EXPECT_TRUE(eps.ContainsAlarm(alarm_ptr));
+ alarm = nullptr;
+ EXPECT_EQ(0u, eps.GetNumPendingAlarmsForTest());
+}
+
+TEST(SimpleEpollServerAlarmTest, TestUnregisterOnAlarm) {
+ EpollTestServer eps;
+ EpollAlarm alarm;
+
+ eps.RegisterAlarmApproximateDelta(1, &alarm);
+ EXPECT_TRUE(eps.ContainsAlarm(&alarm));
+
+ while (alarm.registered()) {
+ eps.WaitForEventsAndExecuteCallbacks();
+ }
+ EXPECT_FALSE(eps.ContainsAlarm(&alarm));
+}
+
+TEST(SimpleEpollServerAlarmTest, TestReregisterAlarm) {
+ EpollTestAlarms ep;
+
+ EpollAlarm alarm;
+ ep.set_time(1000);
+ ep.RegisterAlarm(5000, &alarm);
+
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+ alarm.ReregisterAlarm(6000);
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+
+ ep.set_time(5000);
+ ep.set_timeout_in_us(0);
+ ep.CallAndReregisterAlarmEvents();
+ EXPECT_EQ(1u, ep.GetNumPendingAlarmsForTest());
+
+ ep.set_time(6000);
+ ep.CallAndReregisterAlarmEvents();
+ EXPECT_EQ(0u, ep.GetNumPendingAlarmsForTest());
+}
+
+TEST(SimpleEpollServerAlarmTest, TestThatSameAlarmCanNotBeRegisteredTwice) {
+ TestAlarm alarm;
+ SimpleEpollServer epoll_server;
+ epoll_server.RegisterAlarm(1, &alarm);
+ EXPECT_EPOLL_BUG(epoll_server.RegisterAlarm(1, &alarm),
+ "Alarm already exists");
+}
+
+} // namespace
+
+} // namespace test
+
+} // namespace epoll_server
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/decode_buffer.h b/chromium/net/third_party/quiche/src/http2/decoder/decode_buffer.h
index 3b213c2a861..24f252ed169 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/decode_buffer.h
+++ b/chromium/net/third_party/quiche/src/http2/decoder/decode_buffer.h
@@ -17,8 +17,8 @@
#include <algorithm>
#include <cstdint>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/decode_buffer_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/decode_buffer_test.cc
index e8e3d3bf176..73c9ec689e3 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/decode_buffer_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/decode_buffer_test.cc
@@ -6,8 +6,8 @@
#include <functional>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures.cc b/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures.cc
index 71f6cce57bf..f106ab0a91e 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures.cc
@@ -6,9 +6,9 @@
#include <cstdint>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc
index 43bd7e8d040..91b3ef78bb9 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc
@@ -9,12 +9,12 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc b/chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc
index 6a913f05e50..3d319078e9d 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/decode_status.cc
@@ -4,8 +4,8 @@
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.cc b/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.cc
index 946a6ee8d31..5487997e978 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.cc
@@ -8,8 +8,8 @@ namespace http2 {
DecodeStatus FrameDecoderState::ReadPadLength(DecodeBuffer* db,
bool report_pad_length) {
- DVLOG(2) << "ReadPadLength db->Remaining=" << db->Remaining()
- << "; payload_length=" << frame_header().payload_length;
+ HTTP2_DVLOG(2) << "ReadPadLength db->Remaining=" << db->Remaining()
+ << "; payload_length=" << frame_header().payload_length;
DCHECK(IsPaddable());
DCHECK(frame_header().IsPadded());
@@ -51,9 +51,9 @@ DecodeStatus FrameDecoderState::ReadPadLength(DecodeBuffer* db,
}
bool FrameDecoderState::SkipPadding(DecodeBuffer* db) {
- DVLOG(2) << "SkipPadding remaining_padding_=" << remaining_padding_
- << ", db->Remaining=" << db->Remaining()
- << ", header: " << frame_header();
+ HTTP2_DVLOG(2) << "SkipPadding remaining_padding_=" << remaining_padding_
+ << ", db->Remaining=" << db->Remaining()
+ << ", header: " << frame_header();
DCHECK_EQ(remaining_payload_, 0u);
DCHECK(IsPaddable()) << "header: " << frame_header();
DCHECK_GE(remaining_padding_, 0u);
@@ -70,10 +70,10 @@ bool FrameDecoderState::SkipPadding(DecodeBuffer* db) {
}
DecodeStatus FrameDecoderState::ReportFrameSizeError() {
- DVLOG(2) << "FrameDecoderState::ReportFrameSizeError: "
- << " remaining_payload_=" << remaining_payload_
- << "; remaining_padding_=" << remaining_padding_
- << ", header: " << frame_header();
+ HTTP2_DVLOG(2) << "FrameDecoderState::ReportFrameSizeError: "
+ << " remaining_payload_=" << remaining_payload_
+ << "; remaining_padding_=" << remaining_padding_
+ << ", header: " << frame_header();
listener()->OnFrameSizeError(frame_header());
return DecodeStatus::kDecodeError;
}
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.h b/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.h
index 1581752f086..b44d29b12af 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.h
+++ b/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state.h
@@ -16,7 +16,6 @@
#include <cstdint>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
@@ -24,6 +23,7 @@
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
namespace test {
@@ -60,15 +60,16 @@ class HTTP2_EXPORT_PRIVATE FrameDecoderState {
// remaining payload.
template <class S>
DecodeStatus StartDecodingStructureInPayload(S* out, DecodeBuffer* db) {
- DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
- << "\n\tremaining_payload_=" << remaining_payload_
- << "\n\tneed=" << S::EncodedSize();
+ HTTP2_DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
+ << "\n\tremaining_payload_=" << remaining_payload_
+ << "\n\tneed=" << S::EncodedSize();
DecodeStatus status =
structure_decoder_.Start(out, db, &remaining_payload_);
if (status != DecodeStatus::kDecodeError) {
return status;
}
- DVLOG(2) << "StartDecodingStructureInPayload: detected frame size error";
+ HTTP2_DVLOG(2)
+ << "StartDecodingStructureInPayload: detected frame size error";
return ReportFrameSizeError();
}
@@ -77,14 +78,15 @@ class HTTP2_EXPORT_PRIVATE FrameDecoderState {
// the payload. Returns values are as for StartDecodingStructureInPayload.
template <class S>
DecodeStatus ResumeDecodingStructureInPayload(S* out, DecodeBuffer* db) {
- DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
- << "\n\tremaining_payload_=" << remaining_payload_;
+ HTTP2_DVLOG(2) << __func__ << "\n\tdb->Remaining=" << db->Remaining()
+ << "\n\tremaining_payload_=" << remaining_payload_;
if (structure_decoder_.Resume(out, db, &remaining_payload_)) {
return DecodeStatus::kDecodeDone;
} else if (remaining_payload_ > 0) {
return DecodeStatus::kDecodeInProgress;
} else {
- DVLOG(2) << "ResumeDecodingStructureInPayload: detected frame size error";
+ HTTP2_DVLOG(2)
+ << "ResumeDecodingStructureInPayload: detected frame size error";
return ReportFrameSizeError();
}
}
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.cc b/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.cc
index 7c13ef00ada..0bfe1f75b2e 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.cc
@@ -4,10 +4,10 @@
#include "net/third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test_util.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
#include "net/third_party/quiche/src/http2/tools/random_decoder_test.h"
@@ -16,7 +16,7 @@ namespace test {
// static
void FrameDecoderStatePeer::Randomize(FrameDecoderState* p, Http2Random* rng) {
- VLOG(1) << "FrameDecoderStatePeer::Randomize";
+ HTTP2_VLOG(1) << "FrameDecoderStatePeer::Randomize";
::http2::test::Randomize(&p->frame_header_, rng);
p->remaining_payload_ = rng->Rand32();
p->remaining_padding_ = rng->Rand32();
@@ -26,7 +26,7 @@ void FrameDecoderStatePeer::Randomize(FrameDecoderState* p, Http2Random* rng) {
// static
void FrameDecoderStatePeer::set_frame_header(const Http2FrameHeader& header,
FrameDecoderState* p) {
- VLOG(1) << "FrameDecoderStatePeer::set_frame_header " << header;
+ HTTP2_VLOG(1) << "FrameDecoderStatePeer::set_frame_header " << header;
p->frame_header_ = header;
}
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc
index 314dfbfa19d..9fb290dd997 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.cc
@@ -48,7 +48,7 @@ Http2FrameDecoderListener* Http2FrameDecoder::listener() const {
}
DecodeStatus Http2FrameDecoder::DecodeFrame(DecodeBuffer* db) {
- DVLOG(2) << "Http2FrameDecoder::DecodeFrame state=" << state_;
+ HTTP2_DVLOG(2) << "Http2FrameDecoder::DecodeFrame state=" << state_;
switch (state_) {
case State::kStartDecodingHeader:
if (frame_decoder_state_.StartDecodingFrameHeader(db)) {
@@ -88,17 +88,17 @@ DecodeStatus Http2FrameDecoder::StartDecodingPayload(DecodeBuffer* db) {
// TODO(jamessynge): Remove OnFrameHeader once done with supporting
// SpdyFramer's exact states.
if (!listener()->OnFrameHeader(header)) {
- DVLOG(2) << "OnFrameHeader rejected the frame, will discard; header: "
- << header;
+ HTTP2_DVLOG(2) << "OnFrameHeader rejected the frame, will discard; header: "
+ << header;
state_ = State::kDiscardPayload;
frame_decoder_state_.InitializeRemainders();
return DecodeStatus::kDecodeError;
}
if (header.payload_length > maximum_payload_size_) {
- DVLOG(2) << "Payload length is greater than allowed: "
- << header.payload_length << " > " << maximum_payload_size_
- << "\n header: " << header;
+ HTTP2_DVLOG(2) << "Payload length is greater than allowed: "
+ << header.payload_length << " > " << maximum_payload_size_
+ << "\n header: " << header;
state_ = State::kDiscardPayload;
frame_decoder_state_.InitializeRemainders();
listener()->OnFrameSizeError(header);
@@ -412,13 +412,15 @@ DecodeStatus Http2FrameDecoder::ResumeDecodingWindowUpdatePayload(
}
DecodeStatus Http2FrameDecoder::DiscardPayload(DecodeBuffer* db) {
- DVLOG(2) << "remaining_payload=" << frame_decoder_state_.remaining_payload_
- << "; remaining_padding=" << frame_decoder_state_.remaining_padding_;
+ HTTP2_DVLOG(2) << "remaining_payload="
+ << frame_decoder_state_.remaining_payload_
+ << "; remaining_padding="
+ << frame_decoder_state_.remaining_padding_;
frame_decoder_state_.remaining_payload_ +=
frame_decoder_state_.remaining_padding_;
frame_decoder_state_.remaining_padding_ = 0;
const size_t avail = frame_decoder_state_.AvailablePayload(db);
- DVLOG(2) << "avail=" << avail;
+ HTTP2_DVLOG(2) << "avail=" << avail;
if (avail > 0) {
frame_decoder_state_.ConsumePayload(avail);
db->AdvanceCursor(avail);
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h
index 6257bbeb825..dea40b7dd75 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h
+++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h
@@ -21,7 +21,6 @@
#include <cstdint>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/decoder/frame_decoder_state.h"
@@ -40,6 +39,7 @@
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
namespace test {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.cc
index bc8cf1d5f72..c1cee8bae5a 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.cc
@@ -4,11 +4,11 @@
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h"
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
@@ -203,7 +203,7 @@ LoggingHttp2FrameDecoderListener::~LoggingHttp2FrameDecoderListener() = default;
bool LoggingHttp2FrameDecoderListener::OnFrameHeader(
const Http2FrameHeader& header) {
- VLOG(1) << "OnFrameHeader: " << header;
+ HTTP2_VLOG(1) << "OnFrameHeader: " << header;
if (wrapped_ != nullptr) {
return wrapped_->OnFrameHeader(header);
}
@@ -212,7 +212,7 @@ bool LoggingHttp2FrameDecoderListener::OnFrameHeader(
void LoggingHttp2FrameDecoderListener::OnDataStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnDataStart: " << header;
+ HTTP2_VLOG(1) << "OnDataStart: " << header;
if (wrapped_ != nullptr) {
wrapped_->OnDataStart(header);
}
@@ -220,14 +220,14 @@ void LoggingHttp2FrameDecoderListener::OnDataStart(
void LoggingHttp2FrameDecoderListener::OnDataPayload(const char* data,
size_t len) {
- VLOG(1) << "OnDataPayload: len=" << len;
+ HTTP2_VLOG(1) << "OnDataPayload: len=" << len;
if (wrapped_ != nullptr) {
wrapped_->OnDataPayload(data, len);
}
}
void LoggingHttp2FrameDecoderListener::OnDataEnd() {
- VLOG(1) << "OnDataEnd";
+ HTTP2_VLOG(1) << "OnDataEnd";
if (wrapped_ != nullptr) {
wrapped_->OnDataEnd();
}
@@ -235,7 +235,7 @@ void LoggingHttp2FrameDecoderListener::OnDataEnd() {
void LoggingHttp2FrameDecoderListener::OnHeadersStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnHeadersStart: " << header;
+ HTTP2_VLOG(1) << "OnHeadersStart: " << header;
if (wrapped_ != nullptr) {
wrapped_->OnHeadersStart(header);
}
@@ -243,7 +243,7 @@ void LoggingHttp2FrameDecoderListener::OnHeadersStart(
void LoggingHttp2FrameDecoderListener::OnHeadersPriority(
const Http2PriorityFields& priority) {
- VLOG(1) << "OnHeadersPriority: " << priority;
+ HTTP2_VLOG(1) << "OnHeadersPriority: " << priority;
if (wrapped_ != nullptr) {
wrapped_->OnHeadersPriority(priority);
}
@@ -251,14 +251,14 @@ void LoggingHttp2FrameDecoderListener::OnHeadersPriority(
void LoggingHttp2FrameDecoderListener::OnHpackFragment(const char* data,
size_t len) {
- VLOG(1) << "OnHpackFragment: len=" << len;
+ HTTP2_VLOG(1) << "OnHpackFragment: len=" << len;
if (wrapped_ != nullptr) {
wrapped_->OnHpackFragment(data, len);
}
}
void LoggingHttp2FrameDecoderListener::OnHeadersEnd() {
- VLOG(1) << "OnHeadersEnd";
+ HTTP2_VLOG(1) << "OnHeadersEnd";
if (wrapped_ != nullptr) {
wrapped_->OnHeadersEnd();
}
@@ -267,7 +267,7 @@ void LoggingHttp2FrameDecoderListener::OnHeadersEnd() {
void LoggingHttp2FrameDecoderListener::OnPriorityFrame(
const Http2FrameHeader& header,
const Http2PriorityFields& priority) {
- VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
+ HTTP2_VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
if (wrapped_ != nullptr) {
wrapped_->OnPriorityFrame(header, priority);
}
@@ -275,21 +275,21 @@ void LoggingHttp2FrameDecoderListener::OnPriorityFrame(
void LoggingHttp2FrameDecoderListener::OnContinuationStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnContinuationStart: " << header;
+ HTTP2_VLOG(1) << "OnContinuationStart: " << header;
if (wrapped_ != nullptr) {
wrapped_->OnContinuationStart(header);
}
}
void LoggingHttp2FrameDecoderListener::OnContinuationEnd() {
- VLOG(1) << "OnContinuationEnd";
+ HTTP2_VLOG(1) << "OnContinuationEnd";
if (wrapped_ != nullptr) {
wrapped_->OnContinuationEnd();
}
}
void LoggingHttp2FrameDecoderListener::OnPadLength(size_t trailing_length) {
- VLOG(1) << "OnPadLength: trailing_length=" << trailing_length;
+ HTTP2_VLOG(1) << "OnPadLength: trailing_length=" << trailing_length;
if (wrapped_ != nullptr) {
wrapped_->OnPadLength(trailing_length);
}
@@ -297,7 +297,7 @@ void LoggingHttp2FrameDecoderListener::OnPadLength(size_t trailing_length) {
void LoggingHttp2FrameDecoderListener::OnPadding(const char* padding,
size_t skipped_length) {
- VLOG(1) << "OnPadding: skipped_length=" << skipped_length;
+ HTTP2_VLOG(1) << "OnPadding: skipped_length=" << skipped_length;
if (wrapped_ != nullptr) {
wrapped_->OnPadding(padding, skipped_length);
}
@@ -306,7 +306,7 @@ void LoggingHttp2FrameDecoderListener::OnPadding(const char* padding,
void LoggingHttp2FrameDecoderListener::OnRstStream(
const Http2FrameHeader& header,
Http2ErrorCode error_code) {
- VLOG(1) << "OnRstStream: " << header << "; code=" << error_code;
+ HTTP2_VLOG(1) << "OnRstStream: " << header << "; code=" << error_code;
if (wrapped_ != nullptr) {
wrapped_->OnRstStream(header, error_code);
}
@@ -314,7 +314,7 @@ void LoggingHttp2FrameDecoderListener::OnRstStream(
void LoggingHttp2FrameDecoderListener::OnSettingsStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnSettingsStart: " << header;
+ HTTP2_VLOG(1) << "OnSettingsStart: " << header;
if (wrapped_ != nullptr) {
wrapped_->OnSettingsStart(header);
}
@@ -322,14 +322,14 @@ void LoggingHttp2FrameDecoderListener::OnSettingsStart(
void LoggingHttp2FrameDecoderListener::OnSetting(
const Http2SettingFields& setting_fields) {
- VLOG(1) << "OnSetting: " << setting_fields;
+ HTTP2_VLOG(1) << "OnSetting: " << setting_fields;
if (wrapped_ != nullptr) {
wrapped_->OnSetting(setting_fields);
}
}
void LoggingHttp2FrameDecoderListener::OnSettingsEnd() {
- VLOG(1) << "OnSettingsEnd";
+ HTTP2_VLOG(1) << "OnSettingsEnd";
if (wrapped_ != nullptr) {
wrapped_->OnSettingsEnd();
}
@@ -337,7 +337,7 @@ void LoggingHttp2FrameDecoderListener::OnSettingsEnd() {
void LoggingHttp2FrameDecoderListener::OnSettingsAck(
const Http2FrameHeader& header) {
- VLOG(1) << "OnSettingsAck: " << header;
+ HTTP2_VLOG(1) << "OnSettingsAck: " << header;
if (wrapped_ != nullptr) {
wrapped_->OnSettingsAck(header);
}
@@ -347,15 +347,15 @@ void LoggingHttp2FrameDecoderListener::OnPushPromiseStart(
const Http2FrameHeader& header,
const Http2PushPromiseFields& promise,
size_t total_padding_length) {
- VLOG(1) << "OnPushPromiseStart: " << header << "; promise: " << promise
- << "; total_padding_length: " << total_padding_length;
+ HTTP2_VLOG(1) << "OnPushPromiseStart: " << header << "; promise: " << promise
+ << "; total_padding_length: " << total_padding_length;
if (wrapped_ != nullptr) {
wrapped_->OnPushPromiseStart(header, promise, total_padding_length);
}
}
void LoggingHttp2FrameDecoderListener::OnPushPromiseEnd() {
- VLOG(1) << "OnPushPromiseEnd";
+ HTTP2_VLOG(1) << "OnPushPromiseEnd";
if (wrapped_ != nullptr) {
wrapped_->OnPushPromiseEnd();
}
@@ -363,7 +363,7 @@ void LoggingHttp2FrameDecoderListener::OnPushPromiseEnd() {
void LoggingHttp2FrameDecoderListener::OnPing(const Http2FrameHeader& header,
const Http2PingFields& ping) {
- VLOG(1) << "OnPing: " << header << "; ping: " << ping;
+ HTTP2_VLOG(1) << "OnPing: " << header << "; ping: " << ping;
if (wrapped_ != nullptr) {
wrapped_->OnPing(header, ping);
}
@@ -371,7 +371,7 @@ void LoggingHttp2FrameDecoderListener::OnPing(const Http2FrameHeader& header,
void LoggingHttp2FrameDecoderListener::OnPingAck(const Http2FrameHeader& header,
const Http2PingFields& ping) {
- VLOG(1) << "OnPingAck: " << header << "; ping: " << ping;
+ HTTP2_VLOG(1) << "OnPingAck: " << header << "; ping: " << ping;
if (wrapped_ != nullptr) {
wrapped_->OnPingAck(header, ping);
}
@@ -380,7 +380,7 @@ void LoggingHttp2FrameDecoderListener::OnPingAck(const Http2FrameHeader& header,
void LoggingHttp2FrameDecoderListener::OnGoAwayStart(
const Http2FrameHeader& header,
const Http2GoAwayFields& goaway) {
- VLOG(1) << "OnGoAwayStart: " << header << "; goaway: " << goaway;
+ HTTP2_VLOG(1) << "OnGoAwayStart: " << header << "; goaway: " << goaway;
if (wrapped_ != nullptr) {
wrapped_->OnGoAwayStart(header, goaway);
}
@@ -388,14 +388,14 @@ void LoggingHttp2FrameDecoderListener::OnGoAwayStart(
void LoggingHttp2FrameDecoderListener::OnGoAwayOpaqueData(const char* data,
size_t len) {
- VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+ HTTP2_VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
if (wrapped_ != nullptr) {
wrapped_->OnGoAwayOpaqueData(data, len);
}
}
void LoggingHttp2FrameDecoderListener::OnGoAwayEnd() {
- VLOG(1) << "OnGoAwayEnd";
+ HTTP2_VLOG(1) << "OnGoAwayEnd";
if (wrapped_ != nullptr) {
wrapped_->OnGoAwayEnd();
}
@@ -404,7 +404,7 @@ void LoggingHttp2FrameDecoderListener::OnGoAwayEnd() {
void LoggingHttp2FrameDecoderListener::OnWindowUpdate(
const Http2FrameHeader& header,
uint32_t increment) {
- VLOG(1) << "OnWindowUpdate: " << header << "; increment=" << increment;
+ HTTP2_VLOG(1) << "OnWindowUpdate: " << header << "; increment=" << increment;
if (wrapped_ != nullptr) {
wrapped_->OnWindowUpdate(header, increment);
}
@@ -414,8 +414,9 @@ void LoggingHttp2FrameDecoderListener::OnAltSvcStart(
const Http2FrameHeader& header,
size_t origin_length,
size_t value_length) {
- VLOG(1) << "OnAltSvcStart: " << header << "; origin_length: " << origin_length
- << "; value_length: " << value_length;
+ HTTP2_VLOG(1) << "OnAltSvcStart: " << header
+ << "; origin_length: " << origin_length
+ << "; value_length: " << value_length;
if (wrapped_ != nullptr) {
wrapped_->OnAltSvcStart(header, origin_length, value_length);
}
@@ -423,7 +424,7 @@ void LoggingHttp2FrameDecoderListener::OnAltSvcStart(
void LoggingHttp2FrameDecoderListener::OnAltSvcOriginData(const char* data,
size_t len) {
- VLOG(1) << "OnAltSvcOriginData: len=" << len;
+ HTTP2_VLOG(1) << "OnAltSvcOriginData: len=" << len;
if (wrapped_ != nullptr) {
wrapped_->OnAltSvcOriginData(data, len);
}
@@ -431,14 +432,14 @@ void LoggingHttp2FrameDecoderListener::OnAltSvcOriginData(const char* data,
void LoggingHttp2FrameDecoderListener::OnAltSvcValueData(const char* data,
size_t len) {
- VLOG(1) << "OnAltSvcValueData: len=" << len;
+ HTTP2_VLOG(1) << "OnAltSvcValueData: len=" << len;
if (wrapped_ != nullptr) {
wrapped_->OnAltSvcValueData(data, len);
}
}
void LoggingHttp2FrameDecoderListener::OnAltSvcEnd() {
- VLOG(1) << "OnAltSvcEnd";
+ HTTP2_VLOG(1) << "OnAltSvcEnd";
if (wrapped_ != nullptr) {
wrapped_->OnAltSvcEnd();
}
@@ -446,7 +447,7 @@ void LoggingHttp2FrameDecoderListener::OnAltSvcEnd() {
void LoggingHttp2FrameDecoderListener::OnUnknownStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnUnknownStart: " << header;
+ HTTP2_VLOG(1) << "OnUnknownStart: " << header;
if (wrapped_ != nullptr) {
wrapped_->OnUnknownStart(header);
}
@@ -454,14 +455,14 @@ void LoggingHttp2FrameDecoderListener::OnUnknownStart(
void LoggingHttp2FrameDecoderListener::OnUnknownPayload(const char* data,
size_t len) {
- VLOG(1) << "OnUnknownPayload: len=" << len;
+ HTTP2_VLOG(1) << "OnUnknownPayload: len=" << len;
if (wrapped_ != nullptr) {
wrapped_->OnUnknownPayload(data, len);
}
}
void LoggingHttp2FrameDecoderListener::OnUnknownEnd() {
- VLOG(1) << "OnUnknownEnd";
+ HTTP2_VLOG(1) << "OnUnknownEnd";
if (wrapped_ != nullptr) {
wrapped_->OnUnknownEnd();
}
@@ -470,8 +471,8 @@ void LoggingHttp2FrameDecoderListener::OnUnknownEnd() {
void LoggingHttp2FrameDecoderListener::OnPaddingTooLong(
const Http2FrameHeader& header,
size_t missing_length) {
- VLOG(1) << "OnPaddingTooLong: " << header
- << "; missing_length: " << missing_length;
+ HTTP2_VLOG(1) << "OnPaddingTooLong: " << header
+ << "; missing_length: " << missing_length;
if (wrapped_ != nullptr) {
wrapped_->OnPaddingTooLong(header, missing_length);
}
@@ -479,7 +480,7 @@ void LoggingHttp2FrameDecoderListener::OnPaddingTooLong(
void LoggingHttp2FrameDecoderListener::OnFrameSizeError(
const Http2FrameHeader& header) {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
if (wrapped_ != nullptr) {
wrapped_->OnFrameSizeError(header);
}
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h
index d6e84efe414..36a25ba5b79 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h
+++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h
@@ -75,8 +75,8 @@ class FailingHttp2FrameDecoderListener : public Http2FrameDecoderListener {
void EnsureNotAbstract() { FailingHttp2FrameDecoderListener instance; }
};
-// VLOG's all the calls it receives, and forwards those calls to an optional
-// listener.
+// HTTP2_VLOG's all the calls it receives, and forwards those calls to an
+// optional listener.
class LoggingHttp2FrameDecoderListener : public Http2FrameDecoderListener {
public:
LoggingHttp2FrameDecoderListener();
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc
index 172bd62ef01..50e75edf454 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc
@@ -8,9 +8,9 @@
#include <vector>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_reconstruct_object.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
@@ -44,7 +44,7 @@ class Http2FrameDecoderTest : public RandomDecoderTest {
}
DecodeStatus StartDecoding(DecodeBuffer* db) override {
- DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining();
collector_.Reset();
PrepareDecoder();
@@ -61,7 +61,7 @@ class Http2FrameDecoderTest : public RandomDecoderTest {
}
DecodeStatus ResumeDecoding(DecodeBuffer* db) override {
- DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining();
DecodeStatus status = decoder_.DecodeFrame(db);
if (status != DecodeStatus::kDecodeInProgress) {
// Keep track of this so that a concrete test can verify that both fast
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc
index e4f2c1af079..f30a91be163 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.cc
@@ -33,23 +33,24 @@ uint32_t Http2StructureDecoder::IncompleteStart(DecodeBuffer* db,
DecodeStatus Http2StructureDecoder::IncompleteStart(DecodeBuffer* db,
uint32_t* remaining_payload,
uint32_t target_size) {
- DVLOG(1) << "IncompleteStart@" << this
- << ": *remaining_payload=" << *remaining_payload
- << "; target_size=" << target_size
- << "; db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(1) << "IncompleteStart@" << this
+ << ": *remaining_payload=" << *remaining_payload
+ << "; target_size=" << target_size
+ << "; db->Remaining=" << db->Remaining();
*remaining_payload -=
IncompleteStart(db, std::min(target_size, *remaining_payload));
if (*remaining_payload > 0 && db->Empty()) {
return DecodeStatus::kDecodeInProgress;
}
- DVLOG(1) << "IncompleteStart: kDecodeError";
+ HTTP2_DVLOG(1) << "IncompleteStart: kDecodeError";
return DecodeStatus::kDecodeError;
}
bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
uint32_t target_size) {
- DVLOG(2) << "ResumeFillingBuffer@" << this << ": target_size=" << target_size
- << "; offset_=" << offset_ << "; db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "ResumeFillingBuffer@" << this
+ << ": target_size=" << target_size << "; offset_=" << offset_
+ << "; db->Remaining=" << db->Remaining();
if (target_size < offset_) {
HTTP2_BUG << "Already filled buffer_! target_size=" << target_size
<< " offset_=" << offset_;
@@ -57,7 +58,7 @@ bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
}
const uint32_t needed = target_size - offset_;
const uint32_t num_to_copy = db->MinLengthRemaining(needed);
- DVLOG(2) << "ResumeFillingBuffer num_to_copy=" << num_to_copy;
+ HTTP2_DVLOG(2) << "ResumeFillingBuffer num_to_copy=" << num_to_copy;
memcpy(&buffer_[offset_], db->cursor(), num_to_copy);
db->AdvanceCursor(num_to_copy);
offset_ += num_to_copy;
@@ -67,10 +68,10 @@ bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
uint32_t* remaining_payload,
uint32_t target_size) {
- DVLOG(2) << "ResumeFillingBuffer@" << this << ": target_size=" << target_size
- << "; offset_=" << offset_
- << "; *remaining_payload=" << *remaining_payload
- << "; db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "ResumeFillingBuffer@" << this
+ << ": target_size=" << target_size << "; offset_=" << offset_
+ << "; *remaining_payload=" << *remaining_payload
+ << "; db->Remaining=" << db->Remaining();
if (target_size < offset_) {
HTTP2_BUG << "Already filled buffer_! target_size=" << target_size
<< " offset_=" << offset_;
@@ -79,7 +80,7 @@ bool Http2StructureDecoder::ResumeFillingBuffer(DecodeBuffer* db,
const uint32_t needed = target_size - offset_;
const uint32_t num_to_copy =
db->MinLengthRemaining(std::min(needed, *remaining_payload));
- DVLOG(2) << "ResumeFillingBuffer num_to_copy=" << num_to_copy;
+ HTTP2_DVLOG(2) << "ResumeFillingBuffer num_to_copy=" << num_to_copy;
memcpy(&buffer_[offset_], db->cursor(), num_to_copy);
db->AdvanceCursor(num_to_copy);
offset_ += num_to_copy;
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.h b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.h
index 46c88b85f9d..8ef5f785b77 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.h
+++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder.h
@@ -15,12 +15,12 @@
#include <cstdint>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_http2_structures.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
namespace test {
@@ -44,8 +44,9 @@ class HTTP2_EXPORT_PRIVATE Http2StructureDecoder {
template <class S>
bool Start(S* out, DecodeBuffer* db) {
static_assert(S::EncodedSize() <= sizeof buffer_, "buffer_ is too small");
- DVLOG(2) << __func__ << "@" << this << ": db->Remaining=" << db->Remaining()
- << "; EncodedSize=" << S::EncodedSize();
+ HTTP2_DVLOG(2) << __func__ << "@" << this
+ << ": db->Remaining=" << db->Remaining()
+ << "; EncodedSize=" << S::EncodedSize();
if (db->Remaining() >= S::EncodedSize()) {
DoDecode(out, db);
return true;
@@ -56,12 +57,12 @@ class HTTP2_EXPORT_PRIVATE Http2StructureDecoder {
template <class S>
bool Resume(S* out, DecodeBuffer* db) {
- DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_
- << "; db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_
+ << "; db->Remaining=" << db->Remaining();
if (ResumeFillingBuffer(db, S::EncodedSize())) {
// We have the whole thing now.
- DVLOG(2) << __func__ << "@" << this << " offset_=" << offset_
- << " Ready to decode from buffer_.";
+ HTTP2_DVLOG(2) << __func__ << "@" << this << " offset_=" << offset_
+ << " Ready to decode from buffer_.";
DecodeBuffer buffer_db(buffer_, S::EncodedSize());
DoDecode(out, &buffer_db);
return true;
@@ -77,10 +78,10 @@ class HTTP2_EXPORT_PRIVATE Http2StructureDecoder {
template <class S>
DecodeStatus Start(S* out, DecodeBuffer* db, uint32_t* remaining_payload) {
static_assert(S::EncodedSize() <= sizeof buffer_, "buffer_ is too small");
- DVLOG(2) << __func__ << "@" << this
- << ": *remaining_payload=" << *remaining_payload
- << "; db->Remaining=" << db->Remaining()
- << "; EncodedSize=" << S::EncodedSize();
+ HTTP2_DVLOG(2) << __func__ << "@" << this
+ << ": *remaining_payload=" << *remaining_payload
+ << "; db->Remaining=" << db->Remaining()
+ << "; EncodedSize=" << S::EncodedSize();
if (db->MinLengthRemaining(*remaining_payload) >= S::EncodedSize()) {
DoDecode(out, db);
*remaining_payload -= S::EncodedSize();
@@ -91,14 +92,14 @@ class HTTP2_EXPORT_PRIVATE Http2StructureDecoder {
template <class S>
bool Resume(S* out, DecodeBuffer* db, uint32_t* remaining_payload) {
- DVLOG(3) << __func__ << "@" << this << ": offset_=" << offset_
- << "; *remaining_payload=" << *remaining_payload
- << "; db->Remaining=" << db->Remaining()
- << "; EncodedSize=" << S::EncodedSize();
+ HTTP2_DVLOG(3) << __func__ << "@" << this << ": offset_=" << offset_
+ << "; *remaining_payload=" << *remaining_payload
+ << "; db->Remaining=" << db->Remaining()
+ << "; EncodedSize=" << S::EncodedSize();
if (ResumeFillingBuffer(db, remaining_payload, S::EncodedSize())) {
// We have the whole thing now.
- DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_
- << "; Ready to decode from buffer_.";
+ HTTP2_DVLOG(2) << __func__ << "@" << this << ": offset_=" << offset_
+ << "; Ready to decode from buffer_.";
DecodeBuffer buffer_db(buffer_, S::EncodedSize());
DoDecode(out, &buffer_db);
return true;
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc
index 8d8da7bb577..7b9afc59234 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc
@@ -22,12 +22,12 @@
#include <cstdint>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_reconstruct_object.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
@@ -137,8 +137,8 @@ class Http2StructureDecoderTest : public RandomDecoderTest {
VERIFY_EQ(0u, incomplete_resume_count_);
}
if (expected != nullptr) {
- DVLOG(1) << "DecodeLeadingStructure expected: " << *expected;
- DVLOG(1) << "DecodeLeadingStructure actual: " << structure_;
+ HTTP2_DVLOG(1) << "DecodeLeadingStructure expected: " << *expected;
+ HTTP2_DVLOG(1) << "DecodeLeadingStructure actual: " << structure_;
VERIFY_EQ(*expected, structure_);
}
return AssertionSuccess();
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc
index 4e4d860afd5..b6dde85978c 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder.cc
@@ -6,13 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
namespace http2 {
@@ -39,8 +38,8 @@ std::ostream& operator<<(std::ostream& out,
DecodeStatus AltSvcPayloadDecoder::StartDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "AltSvcPayloadDecoder::StartDecodingPayload: "
- << state->frame_header();
+ HTTP2_DVLOG(2) << "AltSvcPayloadDecoder::StartDecodingPayload: "
+ << state->frame_header();
DCHECK_EQ(Http2FrameType::ALTSVC, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
DCHECK_EQ(0, state->frame_header().flags);
@@ -55,7 +54,8 @@ DecodeStatus AltSvcPayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
const Http2FrameHeader& frame_header = state->frame_header();
- DVLOG(2) << "AltSvcPayloadDecoder::ResumeDecodingPayload: " << frame_header;
+ HTTP2_DVLOG(2) << "AltSvcPayloadDecoder::ResumeDecodingPayload: "
+ << frame_header;
DCHECK_EQ(Http2FrameType::ALTSVC, frame_header.type);
DCHECK_LE(state->remaining_payload(), frame_header.payload_length);
DCHECK_LE(db->Remaining(), state->remaining_payload());
@@ -65,8 +65,9 @@ DecodeStatus AltSvcPayloadDecoder::ResumeDecodingPayload(
// see DCHECK_NE above.
DecodeStatus status = DecodeStatus::kDecodeError;
while (true) {
- DVLOG(2) << "AltSvcPayloadDecoder::ResumeDecodingPayload payload_state_="
- << payload_state_;
+ HTTP2_DVLOG(2)
+ << "AltSvcPayloadDecoder::ResumeDecodingPayload payload_state_="
+ << payload_state_;
switch (payload_state_) {
case PayloadState::kStartDecodingStruct:
status = state->StartDecodingStructureInPayload(&altsvc_fields_, db);
@@ -108,9 +109,9 @@ DecodeStatus AltSvcPayloadDecoder::ResumeDecodingPayload(
DecodeStatus AltSvcPayloadDecoder::DecodeStrings(FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "AltSvcPayloadDecoder::DecodeStrings remaining_payload="
- << state->remaining_payload()
- << ", db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "AltSvcPayloadDecoder::DecodeStrings remaining_payload="
+ << state->remaining_payload()
+ << ", db->Remaining=" << db->Remaining();
// Note that we don't explicitly keep track of exactly how far through the
// origin; instead we compute it from how much is left of the original
// payload length and the decoded total length of the origin.
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
index 0221328754c..6a4bee53b14 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc
@@ -6,12 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
@@ -39,29 +39,29 @@ struct Listener : public FramePartsCollector {
void OnAltSvcStart(const Http2FrameHeader& header,
size_t origin_length,
size_t value_length) override {
- VLOG(1) << "OnAltSvcStart header: " << header
- << "; origin_length=" << origin_length
- << "; value_length=" << value_length;
+ HTTP2_VLOG(1) << "OnAltSvcStart header: " << header
+ << "; origin_length=" << origin_length
+ << "; value_length=" << value_length;
StartFrame(header)->OnAltSvcStart(header, origin_length, value_length);
}
void OnAltSvcOriginData(const char* data, size_t len) override {
- VLOG(1) << "OnAltSvcOriginData: len=" << len;
+ HTTP2_VLOG(1) << "OnAltSvcOriginData: len=" << len;
CurrentFrame()->OnAltSvcOriginData(data, len);
}
void OnAltSvcValueData(const char* data, size_t len) override {
- VLOG(1) << "OnAltSvcValueData: len=" << len;
+ HTTP2_VLOG(1) << "OnAltSvcValueData: len=" << len;
CurrentFrame()->OnAltSvcValueData(data, len);
}
void OnAltSvcEnd() override {
- VLOG(1) << "OnAltSvcEnd";
+ HTTP2_VLOG(1) << "OnAltSvcEnd";
EndFrame()->OnAltSvcEnd();
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
@@ -88,15 +88,17 @@ class AltSvcPayloadLengthTests : public AltSvcPayloadDecoderTest,
AltSvcPayloadLengthTests()
: origin_length_(::testing::get<0>(GetParam())),
value_length_(::testing::get<1>(GetParam())) {
- VLOG(1) << "################ origin_length_=" << origin_length_
- << " value_length_=" << value_length_ << " ################";
+ HTTP2_VLOG(1) << "################ origin_length_=" << origin_length_
+ << " value_length_=" << value_length_
+ << " ################";
}
const uint16_t origin_length_;
const uint32_t value_length_;
};
-INSTANTIATE_TEST_SUITE_P(VariousOriginAndValueLengths, AltSvcPayloadLengthTests,
+INSTANTIATE_TEST_SUITE_P(VariousOriginAndValueLengths,
+ AltSvcPayloadLengthTests,
::testing::Combine(::testing::Values(0, 1, 3, 65535),
::testing::Values(0, 1, 3, 65537)));
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder.cc
index aa9c8172f32..37109f2d375 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder.cc
@@ -6,11 +6,11 @@
#include <stddef.h>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
@@ -20,8 +20,8 @@ DecodeStatus ContinuationPayloadDecoder::StartDecodingPayload(
const Http2FrameHeader& frame_header = state->frame_header();
const uint32_t total_length = frame_header.payload_length;
- DVLOG(2) << "ContinuationPayloadDecoder::StartDecodingPayload: "
- << frame_header;
+ HTTP2_DVLOG(2) << "ContinuationPayloadDecoder::StartDecodingPayload: "
+ << frame_header;
DCHECK_EQ(Http2FrameType::CONTINUATION, frame_header.type);
DCHECK_LE(db->Remaining(), total_length);
DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::END_HEADERS));
@@ -34,9 +34,9 @@ DecodeStatus ContinuationPayloadDecoder::StartDecodingPayload(
DecodeStatus ContinuationPayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "ContinuationPayloadDecoder::ResumeDecodingPayload"
- << " remaining_payload=" << state->remaining_payload()
- << " db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "ContinuationPayloadDecoder::ResumeDecodingPayload"
+ << " remaining_payload=" << state->remaining_payload()
+ << " db->Remaining=" << db->Remaining();
DCHECK_EQ(Http2FrameType::CONTINUATION, state->frame_header().type);
DCHECK_LE(state->remaining_payload(), state->frame_header().payload_length);
DCHECK_LE(db->Remaining(), state->remaining_payload());
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
index 6e818286910..35479a15082 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc
@@ -8,12 +8,12 @@
#include <type_traits>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
@@ -39,17 +39,17 @@ namespace {
struct Listener : public FramePartsCollector {
void OnContinuationStart(const Http2FrameHeader& header) override {
- VLOG(1) << "OnContinuationStart: " << header;
+ HTTP2_VLOG(1) << "OnContinuationStart: " << header;
StartFrame(header)->OnContinuationStart(header);
}
void OnHpackFragment(const char* data, size_t len) override {
- VLOG(1) << "OnHpackFragment: len=" << len;
+ HTTP2_VLOG(1) << "OnHpackFragment: len=" << len;
CurrentFrame()->OnHpackFragment(data, len);
}
void OnContinuationEnd() override {
- VLOG(1) << "OnContinuationEnd";
+ HTTP2_VLOG(1) << "OnContinuationEnd";
EndFrame()->OnContinuationEnd();
}
};
@@ -61,13 +61,15 @@ class ContinuationPayloadDecoderTest
public ::testing::WithParamInterface<uint32_t> {
protected:
ContinuationPayloadDecoderTest() : length_(GetParam()) {
- VLOG(1) << "################ length_=" << length_ << " ################";
+ HTTP2_VLOG(1) << "################ length_=" << length_
+ << " ################";
}
const uint32_t length_;
};
-INSTANTIATE_TEST_SUITE_P(VariousLengths, ContinuationPayloadDecoderTest,
+INSTANTIATE_TEST_SUITE_P(VariousLengths,
+ ContinuationPayloadDecoderTest,
::testing::Values(0, 1, 2, 3, 4, 5, 6));
TEST_P(ContinuationPayloadDecoderTest, ValidLength) {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc
index b6b80414738..fb483a8a40e 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder.cc
@@ -6,13 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
namespace http2 {
@@ -39,7 +38,8 @@ DecodeStatus DataPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
const Http2FrameHeader& frame_header = state->frame_header();
const uint32_t total_length = frame_header.payload_length;
- DVLOG(2) << "DataPayloadDecoder::StartDecodingPayload: " << frame_header;
+ HTTP2_DVLOG(2) << "DataPayloadDecoder::StartDecodingPayload: "
+ << frame_header;
DCHECK_EQ(Http2FrameType::DATA, frame_header.type);
DCHECK_LE(db->Remaining(), total_length);
DCHECK_EQ(0, frame_header.flags &
@@ -49,11 +49,11 @@ DecodeStatus DataPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
// the decode buffer. TO BE SEEN if that is true. It certainly requires that
// the transport buffers be large (e.g. >> 16KB typically).
// TODO(jamessynge) Add counters.
- DVLOG(2) << "StartDecodingPayload total_length=" << total_length;
+ HTTP2_DVLOG(2) << "StartDecodingPayload total_length=" << total_length;
if (!frame_header.IsPadded()) {
- DVLOG(2) << "StartDecodingPayload !IsPadded";
+ HTTP2_DVLOG(2) << "StartDecodingPayload !IsPadded";
if (db->Remaining() == total_length) {
- DVLOG(2) << "StartDecodingPayload all present";
+ HTTP2_DVLOG(2) << "StartDecodingPayload all present";
// Note that we don't cache the listener field so that the callee can
// replace it if the frame is bad.
// If this case is common enough, consider combining the 3 callbacks
@@ -77,8 +77,8 @@ DecodeStatus DataPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
DecodeStatus DataPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "DataPayloadDecoder::ResumeDecodingPayload payload_state_="
- << payload_state_;
+ HTTP2_DVLOG(2) << "DataPayloadDecoder::ResumeDecodingPayload payload_state_="
+ << payload_state_;
const Http2FrameHeader& frame_header = state->frame_header();
DCHECK_EQ(Http2FrameType::DATA, frame_header.type);
DCHECK_LE(state->remaining_payload_and_padding(),
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc
index 0622f17ca7d..1cae7141ef5 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc
@@ -6,13 +6,13 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
@@ -43,34 +43,34 @@ namespace {
struct Listener : public FramePartsCollector {
void OnDataStart(const Http2FrameHeader& header) override {
- VLOG(1) << "OnDataStart: " << header;
+ HTTP2_VLOG(1) << "OnDataStart: " << header;
StartFrame(header)->OnDataStart(header);
}
void OnDataPayload(const char* data, size_t len) override {
- VLOG(1) << "OnDataPayload: len=" << len;
+ HTTP2_VLOG(1) << "OnDataPayload: len=" << len;
CurrentFrame()->OnDataPayload(data, len);
}
void OnDataEnd() override {
- VLOG(1) << "OnDataEnd";
+ HTTP2_VLOG(1) << "OnDataEnd";
EndFrame()->OnDataEnd();
}
void OnPadLength(size_t pad_length) override {
- VLOG(1) << "OnPadLength: " << pad_length;
+ HTTP2_VLOG(1) << "OnPadLength: " << pad_length;
CurrentFrame()->OnPadLength(pad_length);
}
void OnPadding(const char* padding, size_t skipped_length) override {
- VLOG(1) << "OnPadding: " << skipped_length;
+ HTTP2_VLOG(1) << "OnPadding: " << skipped_length;
CurrentFrame()->OnPadding(padding, skipped_length);
}
void OnPaddingTooLong(const Http2FrameHeader& header,
size_t missing_length) override {
- VLOG(1) << "OnPaddingTooLong: " << header
- << " missing_length: " << missing_length;
+ HTTP2_VLOG(1) << "OnPaddingTooLong: " << header
+ << " missing_length: " << missing_length;
EndFrame()->OnPaddingTooLong(header, missing_length);
}
};
@@ -98,7 +98,8 @@ class DataPayloadDecoderTest
}
};
-INSTANTIATE_TEST_SUITE_P(VariousPadLengths, DataPayloadDecoderTest,
+INSTANTIATE_TEST_SUITE_P(VariousPadLengths,
+ DataPayloadDecoderTest,
::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
TEST_P(DataPayloadDecoderTest, VariousDataPayloadSizes) {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc
index cf7b673c5ee..d1402aee08b 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder.cc
@@ -6,13 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
namespace http2 {
@@ -39,8 +38,8 @@ std::ostream& operator<<(std::ostream& out,
DecodeStatus GoAwayPayloadDecoder::StartDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "GoAwayPayloadDecoder::StartDecodingPayload: "
- << state->frame_header();
+ HTTP2_DVLOG(2) << "GoAwayPayloadDecoder::StartDecodingPayload: "
+ << state->frame_header();
DCHECK_EQ(Http2FrameType::GOAWAY, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
DCHECK_EQ(0, state->frame_header().flags);
@@ -53,9 +52,9 @@ DecodeStatus GoAwayPayloadDecoder::StartDecodingPayload(
DecodeStatus GoAwayPayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "GoAwayPayloadDecoder::ResumeDecodingPayload: remaining_payload="
- << state->remaining_payload()
- << ", db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2)
+ << "GoAwayPayloadDecoder::ResumeDecodingPayload: remaining_payload="
+ << state->remaining_payload() << ", db->Remaining=" << db->Remaining();
const Http2FrameHeader& frame_header = state->frame_header();
DCHECK_EQ(Http2FrameType::GOAWAY, frame_header.type);
@@ -68,8 +67,9 @@ DecodeStatus GoAwayPayloadDecoder::ResumeDecodingPayload(
DecodeStatus status = DecodeStatus::kDecodeError;
size_t avail;
while (true) {
- DVLOG(2) << "GoAwayPayloadDecoder::ResumeDecodingPayload payload_state_="
- << payload_state_;
+ HTTP2_DVLOG(2)
+ << "GoAwayPayloadDecoder::ResumeDecodingPayload payload_state_="
+ << payload_state_;
switch (payload_state_) {
case PayloadState::kStartDecodingFixedFields:
status = state->StartDecodingStructureInPayload(&goaway_fields_, db);
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
index c90cdcc9fc8..5ea533a4703 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc
@@ -6,12 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
@@ -36,22 +36,23 @@ namespace {
struct Listener : public FramePartsCollector {
void OnGoAwayStart(const Http2FrameHeader& header,
const Http2GoAwayFields& goaway) override {
- VLOG(1) << "OnGoAwayStart header: " << header << "; goaway: " << goaway;
+ HTTP2_VLOG(1) << "OnGoAwayStart header: " << header
+ << "; goaway: " << goaway;
StartFrame(header)->OnGoAwayStart(header, goaway);
}
void OnGoAwayOpaqueData(const char* data, size_t len) override {
- VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+ HTTP2_VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
CurrentFrame()->OnGoAwayOpaqueData(data, len);
}
void OnGoAwayEnd() override {
- VLOG(1) << "OnGoAwayEnd";
+ HTTP2_VLOG(1) << "OnGoAwayEnd";
EndFrame()->OnGoAwayEnd();
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
@@ -77,13 +78,15 @@ class GoAwayOpaqueDataLengthTests
public ::testing::WithParamInterface<uint32_t> {
protected:
GoAwayOpaqueDataLengthTests() : length_(GetParam()) {
- VLOG(1) << "################ length_=" << length_ << " ################";
+ HTTP2_VLOG(1) << "################ length_=" << length_
+ << " ################";
}
const uint32_t length_;
};
-INSTANTIATE_TEST_SUITE_P(VariousLengths, GoAwayOpaqueDataLengthTests,
+INSTANTIATE_TEST_SUITE_P(VariousLengths,
+ GoAwayOpaqueDataLengthTests,
::testing::Values(0, 1, 2, 3, 4, 5, 6));
TEST_P(GoAwayOpaqueDataLengthTests, ValidLength) {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc
index ecaf0fade11..d177eb67ab3 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder.cc
@@ -6,13 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
namespace http2 {
@@ -44,7 +43,8 @@ DecodeStatus HeadersPayloadDecoder::StartDecodingPayload(
const Http2FrameHeader& frame_header = state->frame_header();
const uint32_t total_length = frame_header.payload_length;
- DVLOG(2) << "HeadersPayloadDecoder::StartDecodingPayload: " << frame_header;
+ HTTP2_DVLOG(2) << "HeadersPayloadDecoder::StartDecodingPayload: "
+ << frame_header;
DCHECK_EQ(Http2FrameType::HEADERS, frame_header.type);
DCHECK_LE(db->Remaining(), total_length);
@@ -67,9 +67,9 @@ DecodeStatus HeadersPayloadDecoder::StartDecodingPayload(
// set then we can decode faster.
const auto payload_flags = Http2FrameFlag::PADDED | Http2FrameFlag::PRIORITY;
if (!frame_header.HasAnyFlags(payload_flags)) {
- DVLOG(2) << "StartDecodingPayload !IsPadded && !HasPriority";
+ HTTP2_DVLOG(2) << "StartDecodingPayload !IsPadded && !HasPriority";
if (db->Remaining() == total_length) {
- DVLOG(2) << "StartDecodingPayload all present";
+ HTTP2_DVLOG(2) << "StartDecodingPayload all present";
// Note that we don't cache the listener field so that the callee can
// replace it if the frame is bad.
// If this case is common enough, consider combining the 3 callbacks
@@ -97,9 +97,9 @@ DecodeStatus HeadersPayloadDecoder::StartDecodingPayload(
DecodeStatus HeadersPayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "HeadersPayloadDecoder::ResumeDecodingPayload "
- << "remaining_payload=" << state->remaining_payload()
- << "; db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "HeadersPayloadDecoder::ResumeDecodingPayload "
+ << "remaining_payload=" << state->remaining_payload()
+ << "; db->Remaining=" << db->Remaining();
const Http2FrameHeader& frame_header = state->frame_header();
@@ -110,8 +110,9 @@ DecodeStatus HeadersPayloadDecoder::ResumeDecodingPayload(
DecodeStatus status;
size_t avail;
while (true) {
- DVLOG(2) << "HeadersPayloadDecoder::ResumeDecodingPayload payload_state_="
- << payload_state_;
+ HTTP2_DVLOG(2)
+ << "HeadersPayloadDecoder::ResumeDecodingPayload payload_state_="
+ << payload_state_;
switch (payload_state_) {
case PayloadState::kReadPadLength:
// ReadPadLength handles the OnPadLength callback, and updating the
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
index cb58b8eff7d..a7a90bfd21f 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc
@@ -6,12 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
@@ -47,44 +47,44 @@ namespace {
// via the public methods of FramePartsCollector.
struct Listener : public FramePartsCollector {
void OnHeadersStart(const Http2FrameHeader& header) override {
- VLOG(1) << "OnHeadersStart: " << header;
+ HTTP2_VLOG(1) << "OnHeadersStart: " << header;
StartFrame(header)->OnHeadersStart(header);
}
void OnHeadersPriority(const Http2PriorityFields& priority) override {
- VLOG(1) << "OnHeadersPriority: " << priority;
+ HTTP2_VLOG(1) << "OnHeadersPriority: " << priority;
CurrentFrame()->OnHeadersPriority(priority);
}
void OnHpackFragment(const char* data, size_t len) override {
- VLOG(1) << "OnHpackFragment: len=" << len;
+ HTTP2_VLOG(1) << "OnHpackFragment: len=" << len;
CurrentFrame()->OnHpackFragment(data, len);
}
void OnHeadersEnd() override {
- VLOG(1) << "OnHeadersEnd";
+ HTTP2_VLOG(1) << "OnHeadersEnd";
EndFrame()->OnHeadersEnd();
}
void OnPadLength(size_t pad_length) override {
- VLOG(1) << "OnPadLength: " << pad_length;
+ HTTP2_VLOG(1) << "OnPadLength: " << pad_length;
CurrentFrame()->OnPadLength(pad_length);
}
void OnPadding(const char* padding, size_t skipped_length) override {
- VLOG(1) << "OnPadding: " << skipped_length;
+ HTTP2_VLOG(1) << "OnPadding: " << skipped_length;
CurrentFrame()->OnPadding(padding, skipped_length);
}
void OnPaddingTooLong(const Http2FrameHeader& header,
size_t missing_length) override {
- VLOG(1) << "OnPaddingTooLong: " << header
- << "; missing_length: " << missing_length;
+ HTTP2_VLOG(1) << "OnPaddingTooLong: " << header
+ << "; missing_length: " << missing_length;
FrameError(header)->OnPaddingTooLong(header, missing_length);
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
@@ -94,14 +94,16 @@ class HeadersPayloadDecoderTest
HeadersPayloadDecoderPeer,
Listener> {};
-INSTANTIATE_TEST_SUITE_P(VariousPadLengths, HeadersPayloadDecoderTest,
+INSTANTIATE_TEST_SUITE_P(VariousPadLengths,
+ HeadersPayloadDecoderTest,
::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
// Decode various sizes of (fake) HPACK payload, both with and without the
// PRIORITY flag set.
TEST_P(HeadersPayloadDecoderTest, VariousHpackPayloadSizes) {
for (size_t hpack_size : {0, 1, 2, 3, 255, 256, 1024}) {
- LOG(INFO) << "########### hpack_size = " << hpack_size << " ###########";
+ HTTP2_LOG(INFO) << "########### hpack_size = " << hpack_size
+ << " ###########";
Http2PriorityFields priority(RandStreamId(), 1 + Random().Rand8(),
Random().OneIn(2));
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc
index 88e1b98afe2..ba6d04a7e44 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc
@@ -19,7 +19,7 @@ PayloadDecoderBaseTest::PayloadDecoderBaseTest() {
}
DecodeStatus PayloadDecoderBaseTest::StartDecoding(DecodeBuffer* db) {
- DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "StartDecoding, db->Remaining=" << db->Remaining();
// Make sure sub-class has set frame_header_ so that we can inject it
// into the payload decoder below.
if (!frame_header_is_set_) {
@@ -63,7 +63,7 @@ DecodeStatus PayloadDecoderBaseTest::StartDecoding(DecodeBuffer* db) {
}
DecodeStatus PayloadDecoderBaseTest::ResumeDecoding(DecodeBuffer* db) {
- DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "ResumeDecoding, db->Remaining=" << db->Remaining();
DecodeStatus status = ResumeDecodingPayload(db);
if (status != DecodeStatus::kDecodeInProgress) {
// Keep track of this so that a concrete test can verify that both fast
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
index 8297e70dab4..66dd88a48c0 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h
@@ -9,7 +9,6 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
@@ -18,6 +17,7 @@
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_constants_test_util.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_reconstruct_object.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
@@ -55,7 +55,7 @@ class PayloadDecoderBaseTest : public RandomDecoderTest {
void set_frame_header(const Http2FrameHeader& header) {
EXPECT_EQ(0, InvalidFlagMaskForFrameType(header.type) & header.flags);
if (!frame_header_is_set_ || frame_header_ != header) {
- VLOG(2) << "set_frame_header: " << frame_header_;
+ HTTP2_VLOG(2) << "set_frame_header: " << frame_header_;
}
frame_header_ = header;
frame_header_is_set_ = true;
@@ -174,13 +174,14 @@ class AbstractPayloadDecoderTest : public PayloadDecoderBaseTest {
// Start decoding the payload.
DecodeStatus StartDecodingPayload(DecodeBuffer* db) override {
- DVLOG(2) << "StartDecodingPayload, db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "StartDecodingPayload, db->Remaining=" << db->Remaining();
return payload_decoder_.StartDecodingPayload(mutable_state(), db);
}
// Resume decoding the payload.
DecodeStatus ResumeDecodingPayload(DecodeBuffer* db) override {
- DVLOG(2) << "ResumeDecodingPayload, db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "ResumeDecodingPayload, db->Remaining="
+ << db->Remaining();
return payload_decoder_.ResumeDecodingPayload(mutable_state(), db);
}
@@ -220,8 +221,8 @@ class AbstractPayloadDecoderTest : public PayloadDecoderBaseTest {
validator = [header, validator, this](
const DecodeBuffer& input,
DecodeStatus status) -> ::testing::AssertionResult {
- DVLOG(2) << "VerifyDetectsFrameSizeError validator; status=" << status
- << "; input.Remaining=" << input.Remaining();
+ HTTP2_DVLOG(2) << "VerifyDetectsFrameSizeError validator; status="
+ << status << "; input.Remaining=" << input.Remaining();
VERIFY_EQ(DecodeStatus::kDecodeError, status);
VERIFY_FALSE(listener_.IsInProgress());
VERIFY_EQ(1u, listener_.size());
@@ -277,7 +278,7 @@ class AbstractPayloadDecoderTest : public PayloadDecoderBaseTest {
if (approve_size != nullptr && !approve_size(real_payload_size)) {
continue;
}
- VLOG(1) << "real_payload_size=" << real_payload_size;
+ HTTP2_VLOG(1) << "real_payload_size=" << real_payload_size;
uint8_t flags = required_flags | RandFlags();
Http2FrameBuilder fb;
if (total_pad_length > 0) {
@@ -343,7 +344,7 @@ class AbstractPaddablePayloadDecoderTest
typedef typename Base::Validator Validator;
AbstractPaddablePayloadDecoderTest() : total_pad_length_(GetParam()) {
- LOG(INFO) << "total_pad_length_ = " << total_pad_length_;
+ HTTP2_LOG(INFO) << "total_pad_length_ = " << total_pad_length_;
}
// Note that total_pad_length_ includes the size of the Pad Length field,
@@ -422,12 +423,12 @@ class AbstractPaddablePayloadDecoderTest
if (IsPadded()) {
fb.AppendUInt8(pad_length());
fb.AppendZeroes(pad_length());
- VLOG(1) << "fb.size=" << fb.size();
+ HTTP2_VLOG(1) << "fb.size=" << fb.size();
// Pick a random length for the payload that is shorter than neccesary.
payload_length = Random().Uniform(fb.size());
}
- VLOG(1) << "payload_length=" << payload_length;
+ HTTP2_VLOG(1) << "payload_length=" << payload_length;
Http2String payload = fb.buffer().substr(0, payload_length);
// The missing length is the amount we cut off the end, unless
@@ -435,7 +436,7 @@ class AbstractPaddablePayloadDecoderTest
// byte, the Pad Length field, is missing.
size_t missing_length =
payload_length == 0 ? 1 : fb.size() - payload_length;
- VLOG(1) << "missing_length=" << missing_length;
+ HTTP2_VLOG(1) << "missing_length=" << missing_length;
const Http2FrameHeader header(payload_length, DecoderPeer::FrameType(),
flags, RandStreamId());
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder.cc
index 8d98046d70a..1363d4f9674 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder.cc
@@ -4,9 +4,9 @@
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
namespace {
@@ -18,7 +18,8 @@ DecodeStatus PingPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
const Http2FrameHeader& frame_header = state->frame_header();
const uint32_t total_length = frame_header.payload_length;
- DVLOG(2) << "PingPayloadDecoder::StartDecodingPayload: " << frame_header;
+ HTTP2_DVLOG(2) << "PingPayloadDecoder::StartDecodingPayload: "
+ << frame_header;
DCHECK_EQ(Http2FrameType::PING, frame_header.type);
DCHECK_LE(db->Remaining(), total_length);
DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::ACK));
@@ -50,8 +51,8 @@ DecodeStatus PingPayloadDecoder::StartDecodingPayload(FrameDecoderState* state,
DecodeStatus PingPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
- << state->remaining_payload();
+ HTTP2_DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
+ << state->remaining_payload();
DCHECK_EQ(Http2FrameType::PING, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
return HandleStatus(
@@ -60,8 +61,8 @@ DecodeStatus PingPayloadDecoder::ResumeDecodingPayload(FrameDecoderState* state,
DecodeStatus PingPayloadDecoder::HandleStatus(FrameDecoderState* state,
DecodeStatus status) {
- DVLOG(2) << "HandleStatus: status=" << status
- << "; remaining_payload=" << state->remaining_payload();
+ HTTP2_DVLOG(2) << "HandleStatus: status=" << status
+ << "; remaining_payload=" << state->remaining_payload();
if (status == DecodeStatus::kDecodeDone) {
if (state->remaining_payload() == 0) {
const Http2FrameHeader& frame_header = state->frame_header();
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder_test.cc
index 34833b2f50a..932149f0da0 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder_test.cc
@@ -6,12 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
@@ -35,18 +35,18 @@ namespace {
struct Listener : public FramePartsCollector {
void OnPing(const Http2FrameHeader& header,
const Http2PingFields& ping) override {
- VLOG(1) << "OnPing: " << header << "; " << ping;
+ HTTP2_VLOG(1) << "OnPing: " << header << "; " << ping;
StartAndEndFrame(header)->OnPing(header, ping);
}
void OnPingAck(const Http2FrameHeader& header,
const Http2PingFields& ping) override {
- VLOG(1) << "OnPingAck: " << header << "; " << ping;
+ HTTP2_VLOG(1) << "OnPingAck: " << header << "; " << ping;
StartAndEndFrame(header)->OnPingAck(header, ping);
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder.cc
index 7be1c95073b..0c5faceb82c 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder.cc
@@ -4,19 +4,19 @@
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
DecodeStatus PriorityPayloadDecoder::StartDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "PriorityPayloadDecoder::StartDecodingPayload: "
- << state->frame_header();
+ HTTP2_DVLOG(2) << "PriorityPayloadDecoder::StartDecodingPayload: "
+ << state->frame_header();
DCHECK_EQ(Http2FrameType::PRIORITY, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
// PRIORITY frames have no flags.
@@ -29,9 +29,9 @@ DecodeStatus PriorityPayloadDecoder::StartDecodingPayload(
DecodeStatus PriorityPayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "PriorityPayloadDecoder::ResumeDecodingPayload"
- << " remaining_payload=" << state->remaining_payload()
- << " db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "PriorityPayloadDecoder::ResumeDecodingPayload"
+ << " remaining_payload=" << state->remaining_payload()
+ << " db->Remaining=" << db->Remaining();
DCHECK_EQ(Http2FrameType::PRIORITY, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
return HandleStatus(
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder_test.cc
index 4e44ebaebae..3b0ac156637 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder_test.cc
@@ -6,12 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
@@ -37,12 +37,12 @@ namespace {
struct Listener : public FramePartsCollector {
void OnPriorityFrame(const Http2FrameHeader& header,
const Http2PriorityFields& priority_fields) override {
- VLOG(1) << "OnPriority: " << header << "; " << priority_fields;
+ HTTP2_VLOG(1) << "OnPriority: " << header << "; " << priority_fields;
StartAndEndFrame(header)->OnPriorityFrame(header, priority_fields);
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc
index cec1c072f90..8b043b6f0c8 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder.cc
@@ -6,13 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
namespace http2 {
@@ -42,8 +41,8 @@ DecodeStatus PushPromisePayloadDecoder::StartDecodingPayload(
const Http2FrameHeader& frame_header = state->frame_header();
const uint32_t total_length = frame_header.payload_length;
- DVLOG(2) << "PushPromisePayloadDecoder::StartDecodingPayload: "
- << frame_header;
+ HTTP2_DVLOG(2) << "PushPromisePayloadDecoder::StartDecodingPayload: "
+ << frame_header;
DCHECK_EQ(Http2FrameType::PUSH_PROMISE, frame_header.type);
DCHECK_LE(db->Remaining(), total_length);
@@ -66,9 +65,9 @@ DecodeStatus PushPromisePayloadDecoder::StartDecodingPayload(
DecodeStatus PushPromisePayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "UnknownPayloadDecoder::ResumeDecodingPayload"
- << " remaining_payload=" << state->remaining_payload()
- << " db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "UnknownPayloadDecoder::ResumeDecodingPayload"
+ << " remaining_payload=" << state->remaining_payload()
+ << " db->Remaining=" << db->Remaining();
const Http2FrameHeader& frame_header = state->frame_header();
DCHECK_EQ(Http2FrameType::PUSH_PROMISE, frame_header.type);
@@ -77,7 +76,7 @@ DecodeStatus PushPromisePayloadDecoder::ResumeDecodingPayload(
DecodeStatus status;
while (true) {
- DVLOG(2)
+ HTTP2_DVLOG(2)
<< "PushPromisePayloadDecoder::ResumeDecodingPayload payload_state_="
<< payload_state_;
switch (payload_state_) {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
index 94220f7be0a..64dd935b44e 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc
@@ -6,12 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
@@ -46,38 +46,38 @@ struct Listener : public FramePartsCollector {
void OnPushPromiseStart(const Http2FrameHeader& header,
const Http2PushPromiseFields& promise,
size_t total_padding_length) override {
- VLOG(1) << "OnPushPromiseStart header: " << header
- << " promise: " << promise
- << " total_padding_length: " << total_padding_length;
+ HTTP2_VLOG(1) << "OnPushPromiseStart header: " << header
+ << " promise: " << promise
+ << " total_padding_length: " << total_padding_length;
EXPECT_EQ(Http2FrameType::PUSH_PROMISE, header.type);
StartFrame(header)->OnPushPromiseStart(header, promise,
total_padding_length);
}
void OnHpackFragment(const char* data, size_t len) override {
- VLOG(1) << "OnHpackFragment: len=" << len;
+ HTTP2_VLOG(1) << "OnHpackFragment: len=" << len;
CurrentFrame()->OnHpackFragment(data, len);
}
void OnPushPromiseEnd() override {
- VLOG(1) << "OnPushPromiseEnd";
+ HTTP2_VLOG(1) << "OnPushPromiseEnd";
EndFrame()->OnPushPromiseEnd();
}
void OnPadding(const char* padding, size_t skipped_length) override {
- VLOG(1) << "OnPadding: " << skipped_length;
+ HTTP2_VLOG(1) << "OnPadding: " << skipped_length;
CurrentFrame()->OnPadding(padding, skipped_length);
}
void OnPaddingTooLong(const Http2FrameHeader& header,
size_t missing_length) override {
- VLOG(1) << "OnPaddingTooLong: " << header
- << "; missing_length: " << missing_length;
+ HTTP2_VLOG(1) << "OnPaddingTooLong: " << header
+ << "; missing_length: " << missing_length;
FrameError(header)->OnPaddingTooLong(header, missing_length);
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
@@ -87,14 +87,16 @@ class PushPromisePayloadDecoderTest
PushPromisePayloadDecoderPeer,
Listener> {};
-INSTANTIATE_TEST_SUITE_P(VariousPadLengths, PushPromisePayloadDecoderTest,
+INSTANTIATE_TEST_SUITE_P(VariousPadLengths,
+ PushPromisePayloadDecoderTest,
::testing::Values(0, 1, 2, 3, 4, 254, 255, 256));
// Payload contains the required Http2PushPromiseFields, followed by some
// (fake) HPACK payload.
TEST_P(PushPromisePayloadDecoderTest, VariousHpackPayloadSizes) {
for (size_t hpack_size : {0, 1, 2, 3, 255, 256, 1024}) {
- LOG(INFO) << "########### hpack_size = " << hpack_size << " ###########";
+ HTTP2_LOG(INFO) << "########### hpack_size = " << hpack_size
+ << " ###########";
Reset();
Http2String hpack_payload = Random().RandString(hpack_size);
Http2PushPromiseFields push_promise{RandStreamId()};
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc
index c39a16a8aa4..720209dc466 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder.cc
@@ -4,19 +4,19 @@
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
DecodeStatus RstStreamPayloadDecoder::StartDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "RstStreamPayloadDecoder::StartDecodingPayload: "
- << state->frame_header();
+ HTTP2_DVLOG(2) << "RstStreamPayloadDecoder::StartDecodingPayload: "
+ << state->frame_header();
DCHECK_EQ(Http2FrameType::RST_STREAM, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
// RST_STREAM has no flags.
@@ -29,9 +29,9 @@ DecodeStatus RstStreamPayloadDecoder::StartDecodingPayload(
DecodeStatus RstStreamPayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "RstStreamPayloadDecoder::ResumeDecodingPayload"
- << " remaining_payload=" << state->remaining_payload()
- << " db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "RstStreamPayloadDecoder::ResumeDecodingPayload"
+ << " remaining_payload=" << state->remaining_payload()
+ << " db->Remaining=" << db->Remaining();
DCHECK_EQ(Http2FrameType::RST_STREAM, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
return HandleStatus(
@@ -40,8 +40,8 @@ DecodeStatus RstStreamPayloadDecoder::ResumeDecodingPayload(
DecodeStatus RstStreamPayloadDecoder::HandleStatus(FrameDecoderState* state,
DecodeStatus status) {
- DVLOG(2) << "HandleStatus: status=" << status
- << "; remaining_payload=" << state->remaining_payload();
+ HTTP2_DVLOG(2) << "HandleStatus: status=" << status
+ << "; remaining_payload=" << state->remaining_payload();
if (status == DecodeStatus::kDecodeDone) {
if (state->remaining_payload() == 0) {
state->listener()->OnRstStream(state->frame_header(),
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc
index 08f66210518..6e00e804bcb 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc
@@ -6,13 +6,13 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_constants_test_util.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
@@ -38,12 +38,12 @@ namespace {
struct Listener : public FramePartsCollector {
void OnRstStream(const Http2FrameHeader& header,
Http2ErrorCode error_code) override {
- VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code;
+ HTTP2_VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code;
StartAndEndFrame(header)->OnRstStream(header, error_code);
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder.cc
index bf29c4d84fb..fb7d26a94ec 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder.cc
@@ -4,11 +4,11 @@
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
@@ -18,7 +18,8 @@ DecodeStatus SettingsPayloadDecoder::StartDecodingPayload(
const Http2FrameHeader& frame_header = state->frame_header();
const uint32_t total_length = frame_header.payload_length;
- DVLOG(2) << "SettingsPayloadDecoder::StartDecodingPayload: " << frame_header;
+ HTTP2_DVLOG(2) << "SettingsPayloadDecoder::StartDecodingPayload: "
+ << frame_header;
DCHECK_EQ(Http2FrameType::SETTINGS, frame_header.type);
DCHECK_LE(db->Remaining(), total_length);
DCHECK_EQ(0, frame_header.flags & ~(Http2FrameFlag::ACK));
@@ -41,9 +42,9 @@ DecodeStatus SettingsPayloadDecoder::StartDecodingPayload(
DecodeStatus SettingsPayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "SettingsPayloadDecoder::ResumeDecodingPayload"
- << " remaining_payload=" << state->remaining_payload()
- << " db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "SettingsPayloadDecoder::ResumeDecodingPayload"
+ << " remaining_payload=" << state->remaining_payload()
+ << " db->Remaining=" << db->Remaining();
DCHECK_EQ(Http2FrameType::SETTINGS, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
@@ -59,9 +60,9 @@ DecodeStatus SettingsPayloadDecoder::ResumeDecodingPayload(
DecodeStatus SettingsPayloadDecoder::StartDecodingSettings(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "SettingsPayloadDecoder::StartDecodingSettings"
- << " remaining_payload=" << state->remaining_payload()
- << " db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "SettingsPayloadDecoder::StartDecodingSettings"
+ << " remaining_payload=" << state->remaining_payload()
+ << " db->Remaining=" << db->Remaining();
while (state->remaining_payload() > 0) {
DecodeStatus status =
state->StartDecodingStructureInPayload(&setting_fields_, db);
@@ -71,9 +72,9 @@ DecodeStatus SettingsPayloadDecoder::StartDecodingSettings(
}
return HandleNotDone(state, db, status);
}
- DVLOG(2) << "LEAVING SettingsPayloadDecoder::StartDecodingSettings"
- << "\n\tdb->Remaining=" << db->Remaining()
- << "\n\t remaining_payload=" << state->remaining_payload();
+ HTTP2_DVLOG(2) << "LEAVING SettingsPayloadDecoder::StartDecodingSettings"
+ << "\n\tdb->Remaining=" << db->Remaining()
+ << "\n\t remaining_payload=" << state->remaining_payload();
state->listener()->OnSettingsEnd();
return DecodeStatus::kDecodeDone;
}
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder_test.cc
index 3429779faf7..d3f116b8006 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder_test.cc
@@ -8,13 +8,13 @@
#include <vector>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_constants_test_util.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
@@ -41,29 +41,29 @@ namespace {
struct Listener : public FramePartsCollector {
void OnSettingsStart(const Http2FrameHeader& header) override {
- VLOG(1) << "OnSettingsStart: " << header;
+ HTTP2_VLOG(1) << "OnSettingsStart: " << header;
EXPECT_EQ(Http2FrameType::SETTINGS, header.type) << header;
EXPECT_EQ(Http2FrameFlag(), header.flags) << header;
StartFrame(header)->OnSettingsStart(header);
}
void OnSetting(const Http2SettingFields& setting_fields) override {
- VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields;
+ HTTP2_VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields;
CurrentFrame()->OnSetting(setting_fields);
}
void OnSettingsEnd() override {
- VLOG(1) << "OnSettingsEnd";
+ HTTP2_VLOG(1) << "OnSettingsEnd";
EndFrame()->OnSettingsEnd();
}
void OnSettingsAck(const Http2FrameHeader& header) override {
- VLOG(1) << "OnSettingsAck: " << header;
+ HTTP2_VLOG(1) << "OnSettingsAck: " << header;
StartAndEndFrame(header)->OnSettingsAck(header);
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder.cc
index 10eaf2438a5..574fd613990 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder.cc
@@ -6,11 +6,11 @@
#include <stddef.h>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
@@ -19,7 +19,8 @@ DecodeStatus UnknownPayloadDecoder::StartDecodingPayload(
DecodeBuffer* db) {
const Http2FrameHeader& frame_header = state->frame_header();
- DVLOG(2) << "UnknownPayloadDecoder::StartDecodingPayload: " << frame_header;
+ HTTP2_DVLOG(2) << "UnknownPayloadDecoder::StartDecodingPayload: "
+ << frame_header;
DCHECK(!IsSupportedHttp2FrameType(frame_header.type)) << frame_header;
DCHECK_LE(db->Remaining(), frame_header.payload_length);
@@ -31,9 +32,9 @@ DecodeStatus UnknownPayloadDecoder::StartDecodingPayload(
DecodeStatus UnknownPayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "UnknownPayloadDecoder::ResumeDecodingPayload "
- << "remaining_payload=" << state->remaining_payload()
- << "; db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "UnknownPayloadDecoder::ResumeDecodingPayload "
+ << "remaining_payload=" << state->remaining_payload()
+ << "; db->Remaining=" << db->Remaining();
DCHECK(!IsSupportedHttp2FrameType(state->frame_header().type))
<< state->frame_header();
DCHECK_LE(state->remaining_payload(), state->frame_header().payload_length);
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
index f9d203a35fc..018bd82e046 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc
@@ -8,12 +8,12 @@
#include <type_traits>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
@@ -41,17 +41,17 @@ namespace {
struct Listener : public FramePartsCollector {
void OnUnknownStart(const Http2FrameHeader& header) override {
- VLOG(1) << "OnUnknownStart: " << header;
+ HTTP2_VLOG(1) << "OnUnknownStart: " << header;
StartFrame(header)->OnUnknownStart(header);
}
void OnUnknownPayload(const char* data, size_t len) override {
- VLOG(1) << "OnUnknownPayload: len=" << len;
+ HTTP2_VLOG(1) << "OnUnknownPayload: len=" << len;
CurrentFrame()->OnUnknownPayload(data, len);
}
void OnUnknownEnd() override {
- VLOG(1) << "OnUnknownEnd";
+ HTTP2_VLOG(1) << "OnUnknownEnd";
EndFrame()->OnUnknownEnd();
}
};
@@ -66,7 +66,8 @@ class UnknownPayloadDecoderTest
public ::testing::WithParamInterface<uint32_t> {
protected:
UnknownPayloadDecoderTest() : length_(GetParam()) {
- VLOG(1) << "################ length_=" << length_ << " ################";
+ HTTP2_VLOG(1) << "################ length_=" << length_
+ << " ################";
// Each test case will choose a random frame type that isn't supported.
do {
@@ -77,7 +78,8 @@ class UnknownPayloadDecoderTest
const uint32_t length_;
};
-INSTANTIATE_TEST_SUITE_P(VariousLengths, UnknownPayloadDecoderTest,
+INSTANTIATE_TEST_SUITE_P(VariousLengths,
+ UnknownPayloadDecoderTest,
::testing::Values(0, 1, 2, 3, 255, 256));
TEST_P(UnknownPayloadDecoderTest, ValidLength) {
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder.cc
index c0bb028b005..3f83339e93b 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder.cc
@@ -4,12 +4,12 @@
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_http2_structures.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
@@ -19,8 +19,8 @@ DecodeStatus WindowUpdatePayloadDecoder::StartDecodingPayload(
const Http2FrameHeader& frame_header = state->frame_header();
const uint32_t total_length = frame_header.payload_length;
- DVLOG(2) << "WindowUpdatePayloadDecoder::StartDecodingPayload: "
- << frame_header;
+ HTTP2_DVLOG(2) << "WindowUpdatePayloadDecoder::StartDecodingPayload: "
+ << frame_header;
DCHECK_EQ(Http2FrameType::WINDOW_UPDATE, frame_header.type);
DCHECK_LE(db->Remaining(), total_length);
@@ -45,9 +45,9 @@ DecodeStatus WindowUpdatePayloadDecoder::StartDecodingPayload(
DecodeStatus WindowUpdatePayloadDecoder::ResumeDecodingPayload(
FrameDecoderState* state,
DecodeBuffer* db) {
- DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
- << state->remaining_payload()
- << "; db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "ResumeDecodingPayload: remaining_payload="
+ << state->remaining_payload()
+ << "; db->Remaining=" << db->Remaining();
DCHECK_EQ(Http2FrameType::WINDOW_UPDATE, state->frame_header().type);
DCHECK_LE(db->Remaining(), state->frame_header().payload_length);
return HandleStatus(state, state->ResumeDecodingStructureInPayload(
@@ -56,8 +56,8 @@ DecodeStatus WindowUpdatePayloadDecoder::ResumeDecodingPayload(
DecodeStatus WindowUpdatePayloadDecoder::HandleStatus(FrameDecoderState* state,
DecodeStatus status) {
- DVLOG(2) << "HandleStatus: status=" << status
- << "; remaining_payload=" << state->remaining_payload();
+ HTTP2_DVLOG(2) << "HandleStatus: status=" << status
+ << "; remaining_payload=" << state->remaining_payload();
if (status == DecodeStatus::kDecodeDone) {
if (state->remaining_payload() == 0) {
state->listener()->OnWindowUpdate(
diff --git a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc
index 77a2b90a45c..66d7d7eb05d 100644
--- a/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc
@@ -6,12 +6,12 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts.h"
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
@@ -37,14 +37,14 @@ namespace {
struct Listener : public FramePartsCollector {
void OnWindowUpdate(const Http2FrameHeader& header,
uint32_t window_size_increment) override {
- VLOG(1) << "OnWindowUpdate: " << header
- << "; window_size_increment=" << window_size_increment;
+ HTTP2_VLOG(1) << "OnWindowUpdate: " << header
+ << "; window_size_increment=" << window_size_increment;
EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, header.type);
StartAndEndFrame(header)->OnWindowUpdate(header, window_size_increment);
}
void OnFrameSizeError(const Http2FrameHeader& header) override {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
};
@@ -57,7 +57,7 @@ class WindowUpdatePayloadDecoderTest
Http2WindowUpdateFields RandWindowUpdateFields() {
Http2WindowUpdateFields fields;
test::Randomize(&fields, RandomPtr());
- VLOG(3) << "RandWindowUpdateFields: " << fields;
+ HTTP2_VLOG(3) << "RandWindowUpdateFields: " << fields;
return fields;
}
};
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc
index 5b2c6b7293a..1137be53d85 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc
@@ -7,7 +7,7 @@
#include <algorithm>
#include <memory>
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
using ::testing::AssertionResult;
@@ -55,7 +55,7 @@ void HpackBlockCollector::OnValueEnd() {
void HpackBlockCollector::PushPendingEntry() {
EXPECT_TRUE(pending_entry_.IsComplete());
- DVLOG(2) << "PushPendingEntry: " << pending_entry_;
+ HTTP2_DVLOG(2) << "PushPendingEntry: " << pending_entry_;
entries_.push_back(pending_entry_);
EXPECT_TRUE(entries_.back().IsComplete());
pending_entry_.Clear();
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc
index c6882d292c4..656f8e9866e 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.cc
@@ -6,15 +6,15 @@
#include <cstdint>
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
namespace http2 {
DecodeStatus HpackBlockDecoder::Decode(DecodeBuffer* db) {
if (!before_entry_) {
- DVLOG(2) << "HpackBlockDecoder::Decode resume entry, db->Remaining="
- << db->Remaining();
+ HTTP2_DVLOG(2) << "HpackBlockDecoder::Decode resume entry, db->Remaining="
+ << db->Remaining();
DecodeStatus status = entry_decoder_.Resume(db, listener_);
switch (status) {
case DecodeStatus::kDecodeDone:
@@ -29,8 +29,8 @@ DecodeStatus HpackBlockDecoder::Decode(DecodeBuffer* db) {
}
DCHECK(before_entry_);
while (db->HasData()) {
- DVLOG(2) << "HpackBlockDecoder::Decode start entry, db->Remaining="
- << db->Remaining();
+ HTTP2_DVLOG(2) << "HpackBlockDecoder::Decode start entry, db->Remaining="
+ << db->Remaining();
DecodeStatus status = entry_decoder_.Start(db, listener_);
switch (status) {
case DecodeStatus::kDecodeDone:
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h
index e8b23e16dce..a17664f8ded 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder.h
@@ -10,12 +10,12 @@
// or dynamic table support, so table indices remain indices at this level.
// Reports the entries to an HpackEntryDecoderListener.
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h"
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
namespace http2 {
@@ -35,7 +35,7 @@ class HTTP2_EXPORT_PRIVATE HpackBlockDecoder {
// to be called from an implementation of Http2FrameDecoderListener's
// OnHeadersStart or OnPushPromiseStart methods.
void Reset() {
- DVLOG(2) << "HpackBlockDecoder::Reset";
+ HTTP2_DVLOG(2) << "HpackBlockDecoder::Reset";
before_entry_ = true;
}
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 d6897cbbfa1..f2f8584e97a 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
@@ -4,9 +4,9 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
@@ -33,8 +33,8 @@ void HpackDecoder::ApplyHeaderTableSizeSetting(uint32_t max_header_table_size) {
}
bool HpackDecoder::StartDecodingBlock() {
- DVLOG(3) << "HpackDecoder::StartDecodingBlock, error_detected="
- << (error_detected() ? "true" : "false");
+ HTTP2_DVLOG(3) << "HpackDecoder::StartDecodingBlock, error_detected="
+ << (error_detected() ? "true" : "false");
if (error_detected()) {
return false;
}
@@ -47,9 +47,9 @@ bool HpackDecoder::StartDecodingBlock() {
}
bool HpackDecoder::DecodeFragment(DecodeBuffer* db) {
- DVLOG(3) << "HpackDecoder::DecodeFragment, error_detected="
- << (error_detected() ? "true" : "false")
- << ", size=" << db->Remaining();
+ HTTP2_DVLOG(3) << "HpackDecoder::DecodeFragment, error_detected="
+ << (error_detected() ? "true" : "false")
+ << ", size=" << db->Remaining();
if (error_detected()) {
return false;
}
@@ -74,8 +74,8 @@ bool HpackDecoder::DecodeFragment(DecodeBuffer* db) {
}
bool HpackDecoder::EndDecodingBlock() {
- DVLOG(3) << "HpackDecoder::EndDecodingBlock, error_detected="
- << (error_detected() ? "true" : "false");
+ HTTP2_DVLOG(3) << "HpackDecoder::EndDecodingBlock, error_detected="
+ << (error_detected() ? "true" : "false");
if (error_detected()) {
return false;
}
@@ -95,10 +95,10 @@ bool HpackDecoder::EndDecodingBlock() {
bool HpackDecoder::error_detected() {
if (!error_detected_) {
if (entry_buffer_.error_detected()) {
- DVLOG(2) << "HpackDecoder::error_detected in entry_buffer_";
+ HTTP2_DVLOG(2) << "HpackDecoder::error_detected in entry_buffer_";
error_detected_ = true;
} else if (decoder_state_.error_detected()) {
- DVLOG(2) << "HpackDecoder::error_detected in decoder_state_";
+ HTTP2_DVLOG(2) << "HpackDecoder::error_detected in decoder_state_";
error_detected_ = true;
}
}
@@ -110,9 +110,9 @@ size_t HpackDecoder::EstimateMemoryUsage() const {
}
void HpackDecoder::ReportError(Http2StringPiece error_message) {
- DVLOG(3) << "HpackDecoder::ReportError is new="
- << (!error_detected_ ? "true" : "false")
- << ", error_message: " << error_message;
+ HTTP2_DVLOG(3) << "HpackDecoder::ReportError is new="
+ << (!error_detected_ ? "true" : "false")
+ << ", error_message: " << error_message;
if (!error_detected_) {
error_detected_ = true;
decoder_state_.listener()->OnHeaderErrorDetected(error_message);
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc
index 2ddf421775d..921d48ca392 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.cc
@@ -4,9 +4,9 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/hpack/hpack_string.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
namespace http2 {
@@ -41,21 +41,21 @@ void HpackDecoderState::set_tables_debug_listener(
void HpackDecoderState::ApplyHeaderTableSizeSetting(
uint32_t header_table_size) {
- DVLOG(2) << "HpackDecoderState::ApplyHeaderTableSizeSetting("
- << header_table_size << ")";
+ HTTP2_DVLOG(2) << "HpackDecoderState::ApplyHeaderTableSizeSetting("
+ << header_table_size << ")";
DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
if (header_table_size < lowest_header_table_size_) {
lowest_header_table_size_ = header_table_size;
}
final_header_table_size_ = header_table_size;
- DVLOG(2) << "low water mark: " << lowest_header_table_size_;
- DVLOG(2) << "final limit: " << final_header_table_size_;
+ HTTP2_DVLOG(2) << "low water mark: " << lowest_header_table_size_;
+ HTTP2_DVLOG(2) << "final limit: " << final_header_table_size_;
}
// Called to notify this object that we're starting to decode an HPACK block
// (e.g. a HEADERS or PUSH_PROMISE frame's header has been decoded).
void HpackDecoderState::OnHeaderBlockStart() {
- DVLOG(2) << "HpackDecoderState::OnHeaderBlockStart";
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnHeaderBlockStart";
// This instance can't be reused after an error has been detected, as we must
// assume that the encoder and decoder compression states are no longer
// synchronized.
@@ -72,14 +72,14 @@ void HpackDecoderState::OnHeaderBlockStart() {
(lowest_header_table_size_ <
decoder_tables_.current_header_table_size() ||
final_header_table_size_ < decoder_tables_.header_table_size_limit());
- DVLOG(2) << "HpackDecoderState::OnHeaderListStart "
- << "require_dynamic_table_size_update_="
- << require_dynamic_table_size_update_;
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnHeaderListStart "
+ << "require_dynamic_table_size_update_="
+ << require_dynamic_table_size_update_;
listener_->OnHeaderListStart();
}
void HpackDecoderState::OnIndexedHeader(size_t index) {
- DVLOG(2) << "HpackDecoderState::OnIndexedHeader: " << index;
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnIndexedHeader: " << index;
if (error_detected_) {
return;
}
@@ -101,8 +101,9 @@ void HpackDecoderState::OnNameIndexAndLiteralValue(
HpackEntryType entry_type,
size_t name_index,
HpackDecoderStringBuffer* value_buffer) {
- DVLOG(2) << "HpackDecoderState::OnNameIndexAndLiteralValue " << entry_type
- << ", " << name_index << ", " << value_buffer->str();
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnNameIndexAndLiteralValue "
+ << entry_type << ", " << name_index << ", "
+ << value_buffer->str();
if (error_detected_) {
return;
}
@@ -127,8 +128,8 @@ void HpackDecoderState::OnLiteralNameAndValue(
HpackEntryType entry_type,
HpackDecoderStringBuffer* name_buffer,
HpackDecoderStringBuffer* value_buffer) {
- DVLOG(2) << "HpackDecoderState::OnLiteralNameAndValue " << entry_type << ", "
- << name_buffer->str() << ", " << value_buffer->str();
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnLiteralNameAndValue " << entry_type
+ << ", " << name_buffer->str() << ", " << value_buffer->str();
if (error_detected_) {
return;
}
@@ -146,11 +147,11 @@ void HpackDecoderState::OnLiteralNameAndValue(
}
void HpackDecoderState::OnDynamicTableSizeUpdate(size_t size_limit) {
- DVLOG(2) << "HpackDecoderState::OnDynamicTableSizeUpdate " << size_limit
- << ", required="
- << (require_dynamic_table_size_update_ ? "true" : "false")
- << ", allowed="
- << (allow_dynamic_table_size_update_ ? "true" : "false");
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnDynamicTableSizeUpdate " << size_limit
+ << ", required="
+ << (require_dynamic_table_size_update_ ? "true" : "false")
+ << ", allowed="
+ << (allow_dynamic_table_size_update_ ? "true" : "false");
if (error_detected_) {
return;
}
@@ -185,14 +186,14 @@ void HpackDecoderState::OnDynamicTableSizeUpdate(size_t size_limit) {
}
void HpackDecoderState::OnHpackDecodeError(Http2StringPiece error_message) {
- DVLOG(2) << "HpackDecoderState::OnHpackDecodeError " << error_message;
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnHpackDecodeError " << error_message;
if (!error_detected_) {
ReportError(error_message);
}
}
void HpackDecoderState::OnHeaderBlockEnd() {
- DVLOG(2) << "HpackDecoderState::OnHeaderBlockEnd";
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnHeaderBlockEnd";
if (error_detected_) {
return;
}
@@ -206,9 +207,9 @@ void HpackDecoderState::OnHeaderBlockEnd() {
}
void HpackDecoderState::ReportError(Http2StringPiece error_message) {
- DVLOG(2) << "HpackDecoderState::ReportError is new="
- << (!error_detected_ ? "true" : "false")
- << ", error_message: " << error_message;
+ HTTP2_DVLOG(2) << "HpackDecoderState::ReportError is new="
+ << (!error_detected_ ? "true" : "false")
+ << ", error_message: " << error_message;
if (!error_detected_) {
listener_->OnHeaderErrorDetected(error_message);
error_detected_ = true;
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc
index 115827e1912..cbc4224fd81 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc
@@ -9,12 +9,12 @@
#include <utility>
#include <vector>
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/hpack/hpack_string.h"
#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
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 b20c37a8204..76431a6ffd0 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
@@ -6,9 +6,9 @@
#include <utility>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
namespace http2 {
@@ -57,12 +57,12 @@ HpackDecoderStringBuffer::HpackDecoderStringBuffer()
HpackDecoderStringBuffer::~HpackDecoderStringBuffer() = default;
void HpackDecoderStringBuffer::Reset() {
- DVLOG(3) << "HpackDecoderStringBuffer::Reset";
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::Reset";
state_ = State::RESET;
}
void HpackDecoderStringBuffer::Set(Http2StringPiece value, bool is_static) {
- DVLOG(2) << "HpackDecoderStringBuffer::Set";
+ HTTP2_DVLOG(2) << "HpackDecoderStringBuffer::Set";
DCHECK_EQ(state_, State::RESET);
value_ = value;
state_ = State::COMPLETE;
@@ -73,7 +73,7 @@ void HpackDecoderStringBuffer::Set(Http2StringPiece value, bool is_static) {
}
void HpackDecoderStringBuffer::OnStart(bool huffman_encoded, size_t len) {
- DVLOG(2) << "HpackDecoderStringBuffer::OnStart";
+ HTTP2_DVLOG(2) << "HpackDecoderStringBuffer::OnStart";
DCHECK_EQ(state_, State::RESET);
remaining_len_ = len;
@@ -106,8 +106,8 @@ void HpackDecoderStringBuffer::OnStart(bool huffman_encoded, size_t len) {
}
bool HpackDecoderStringBuffer::OnData(const char* data, size_t len) {
- DVLOG(2) << "HpackDecoderStringBuffer::OnData state=" << state_
- << ", backing=" << backing_;
+ HTTP2_DVLOG(2) << "HpackDecoderStringBuffer::OnData state=" << state_
+ << ", backing=" << backing_;
DCHECK_EQ(state_, State::COLLECTING);
DCHECK_LE(len, remaining_len_);
remaining_len_ -= len;
@@ -145,7 +145,7 @@ bool HpackDecoderStringBuffer::OnData(const char* data, size_t len) {
}
bool HpackDecoderStringBuffer::OnEnd() {
- DVLOG(2) << "HpackDecoderStringBuffer::OnEnd";
+ HTTP2_DVLOG(2) << "HpackDecoderStringBuffer::OnEnd";
DCHECK_EQ(state_, State::COLLECTING);
DCHECK_EQ(0u, remaining_len_);
@@ -164,11 +164,12 @@ bool HpackDecoderStringBuffer::OnEnd() {
}
void HpackDecoderStringBuffer::BufferStringIfUnbuffered() {
- DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
- << state_ << ", backing=" << backing_;
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
+ << state_ << ", backing=" << backing_;
if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) {
- DVLOG(2) << "HpackDecoderStringBuffer buffering Http2String of length "
- << value_.size();
+ HTTP2_DVLOG(2)
+ << "HpackDecoderStringBuffer buffering Http2String of length "
+ << value_.size();
buffer_.assign(value_.data(), value_.size());
if (state_ == State::COMPLETE) {
value_ = buffer_;
@@ -178,23 +179,23 @@ void HpackDecoderStringBuffer::BufferStringIfUnbuffered() {
}
bool HpackDecoderStringBuffer::IsBuffered() const {
- DVLOG(3) << "HpackDecoderStringBuffer::IsBuffered";
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::IsBuffered";
return state_ != State::RESET && backing_ == Backing::BUFFERED;
}
size_t HpackDecoderStringBuffer::BufferedLength() const {
- DVLOG(3) << "HpackDecoderStringBuffer::BufferedLength";
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::BufferedLength";
return IsBuffered() ? buffer_.size() : 0;
}
Http2StringPiece HpackDecoderStringBuffer::str() const {
- DVLOG(3) << "HpackDecoderStringBuffer::str";
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::str";
DCHECK_EQ(state_, State::COMPLETE);
return value_;
}
Http2String HpackDecoderStringBuffer::ReleaseString() {
- DVLOG(3) << "HpackDecoderStringBuffer::ReleaseString";
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::ReleaseString";
DCHECK_EQ(state_, State::COMPLETE);
DCHECK_EQ(backing_, Backing::BUFFERED);
if (state_ == State::COMPLETE) {
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 009b894fbe6..11b0d9773bb 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
@@ -8,9 +8,9 @@
#include <initializer_list>
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
@@ -31,10 +31,10 @@ class HpackDecoderStringBufferTest : public ::testing::Test {
State state() const { return buf_.state_for_testing(); }
Backing backing() const { return buf_.backing_for_testing(); }
- // We want to know that LOG(x) << buf_ will work in production should that
- // be needed, so we test that it outputs the expected values.
+ // We want to know that HTTP2_LOG(x) << buf_ will work in production should
+ // that be needed, so we test that it outputs the expected values.
AssertionResult VerifyLogHasSubstrs(std::initializer_list<Http2String> strs) {
- VLOG(1) << buf_;
+ HTTP2_VLOG(1) << buf_;
std::ostringstream ss;
buf_.OutputDebugStringTo(ss);
Http2String dbg_str(ss.str());
@@ -54,7 +54,7 @@ TEST_F(HpackDecoderStringBufferTest, SetStatic) {
EXPECT_TRUE(VerifyLogHasSubstrs({"state=RESET"}));
buf_.Set(data, /*is_static*/ true);
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::STATIC);
EXPECT_EQ(data, buf_.str());
@@ -75,13 +75,13 @@ TEST_F(HpackDecoderStringBufferTest, SetStatic) {
TEST_F(HpackDecoderStringBufferTest, PlainWhole) {
Http2StringPiece data("some text.");
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
EXPECT_EQ(state(), State::RESET);
buf_.OnStart(/*huffman_encoded*/ false, data.size());
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::RESET);
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnData(data.data(), data.size()));
EXPECT_EQ(state(), State::COLLECTING);
@@ -101,7 +101,7 @@ TEST_F(HpackDecoderStringBufferTest, PlainWhole) {
// Now force it to buffer the string, after which it will still have the same
// string value, but the backing store will be different.
buf_.BufferStringIfUnbuffered();
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), data.size());
EXPECT_EQ(data, buf_.str());
@@ -126,7 +126,7 @@ TEST_F(HpackDecoderStringBufferTest, PlainSplit) {
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), part1.size());
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
EXPECT_EQ(state(), State::COLLECTING);
@@ -137,7 +137,7 @@ TEST_F(HpackDecoderStringBufferTest, PlainSplit) {
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), data.size());
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
Http2StringPiece buffered = buf_.str();
EXPECT_EQ(data, buffered);
@@ -188,31 +188,31 @@ TEST_F(HpackDecoderStringBufferTest, HuffmanSplit) {
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(0u, buf_.BufferedLength());
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_GT(buf_.BufferedLength(), 0u);
EXPECT_LT(buf_.BufferedLength(), decoded.size());
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), decoded.size());
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnEnd());
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), decoded.size());
EXPECT_EQ(decoded, buf_.str());
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
buf_.Reset();
EXPECT_EQ(state(), State::RESET);
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
}
TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnData) {
@@ -226,7 +226,7 @@ TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnData) {
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
}
TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnEnd) {
@@ -241,7 +241,7 @@ TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnEnd) {
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_FALSE(buf_.OnEnd());
- LOG(INFO) << buf_;
+ HTTP2_LOG(INFO) << buf_;
}
// TODO(jamessynge): Add tests for ReleaseString().
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc
index c88648ff597..09a18a8a03f 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.cc
@@ -4,8 +4,8 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
namespace {
@@ -60,7 +60,8 @@ HpackDecoderDynamicTable::HpackDecoderDynamicTable()
HpackDecoderDynamicTable::~HpackDecoderDynamicTable() = default;
void HpackDecoderDynamicTable::DynamicTableSizeUpdate(size_t size_limit) {
- DVLOG(3) << "HpackDecoderDynamicTable::DynamicTableSizeUpdate " << size_limit;
+ HTTP2_DVLOG(3) << "HpackDecoderDynamicTable::DynamicTableSizeUpdate "
+ << size_limit;
EnsureSizeNoMoreThan(size_limit);
DCHECK_LE(current_size_, size_limit);
size_limit_ = size_limit;
@@ -72,12 +73,12 @@ bool HpackDecoderDynamicTable::Insert(const HpackString& name,
const HpackString& value) {
HpackDecoderTableEntry entry(name, value);
size_t entry_size = entry.size();
- DVLOG(2) << "InsertEntry of size=" << entry_size << "\n name: " << name
- << "\n value: " << value;
+ HTTP2_DVLOG(2) << "InsertEntry of size=" << entry_size
+ << "\n name: " << name << "\n value: " << value;
if (entry_size > size_limit_) {
- DVLOG(2) << "InsertEntry: entry larger than table, removing "
- << table_.size() << " entries, of total size " << current_size_
- << " bytes.";
+ HTTP2_DVLOG(2) << "InsertEntry: entry larger than table, removing "
+ << table_.size() << " entries, of total size "
+ << current_size_ << " bytes.";
table_.clear();
current_size_ = 0;
return false; // Not inserted because too large.
@@ -85,14 +86,14 @@ bool HpackDecoderDynamicTable::Insert(const HpackString& name,
++insert_count_;
if (debug_listener_ != nullptr) {
entry.time_added = debug_listener_->OnEntryInserted(entry, insert_count_);
- DVLOG(2) << "OnEntryInserted returned time_added=" << entry.time_added
- << " for insert_count_=" << insert_count_;
+ HTTP2_DVLOG(2) << "OnEntryInserted returned time_added=" << entry.time_added
+ << " for insert_count_=" << insert_count_;
}
size_t insert_limit = size_limit_ - entry_size;
EnsureSizeNoMoreThan(insert_limit);
table_.push_front(entry);
current_size_ += entry_size;
- DVLOG(2) << "InsertEntry: current_size_=" << current_size_;
+ HTTP2_DVLOG(2) << "InsertEntry: current_size_=" << current_size_;
DCHECK_GE(current_size_, entry_size);
DCHECK_LE(current_size_, size_limit_);
return true;
@@ -112,8 +113,8 @@ const HpackStringPair* HpackDecoderDynamicTable::Lookup(size_t index) const {
}
void HpackDecoderDynamicTable::EnsureSizeNoMoreThan(size_t limit) {
- DVLOG(2) << "EnsureSizeNoMoreThan limit=" << limit
- << ", current_size_=" << current_size_;
+ HTTP2_DVLOG(2) << "EnsureSizeNoMoreThan limit=" << limit
+ << ", current_size_=" << current_size_;
// Not the most efficient choice, but any easy way to start.
while (current_size_ > limit) {
RemoveLastEntry();
@@ -124,8 +125,8 @@ void HpackDecoderDynamicTable::EnsureSizeNoMoreThan(size_t limit) {
void HpackDecoderDynamicTable::RemoveLastEntry() {
DCHECK(!table_.empty());
if (!table_.empty()) {
- DVLOG(2) << "RemoveLastEntry current_size_=" << current_size_
- << ", last entry size=" << table_.back().size();
+ HTTP2_DVLOG(2) << "RemoveLastEntry current_size_=" << current_size_
+ << ", last entry size=" << table_.back().size();
DCHECK_GE(current_size_, table_.back().size());
current_size_ -= table_.back().size();
table_.pop_back();
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc
index a54fa5f053b..592ce563544 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc
@@ -8,10 +8,10 @@
#include <tuple>
#include <vector>
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc
index 83a0ede3a5f..bf2cee52c8d 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc
@@ -10,7 +10,6 @@
#include <utility>
#include <vector>
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
@@ -22,6 +21,7 @@
#include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h"
#include "net/third_party/quiche/src/http2/hpack/tools/hpack_example.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
@@ -130,7 +130,7 @@ class HpackDecoderTest : public ::testing::TestWithParam<bool>,
}
AssertionResult DecodeBlock(Http2StringPiece block) {
- VLOG(1) << "HpackDecoderTest::DecodeBlock";
+ HTTP2_VLOG(1) << "HpackDecoderTest::DecodeBlock";
VERIFY_FALSE(decoder_.error_detected());
VERIFY_TRUE(error_messages_.empty());
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc
index 9d17465577f..327075df7ee 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc
@@ -4,10 +4,10 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.h"
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h"
#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc
index 96b9f066030..5e1bd8c7176 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc
@@ -8,9 +8,8 @@
#include <cstdint>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
namespace http2 {
@@ -98,7 +97,8 @@ DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
switch (state_) {
case EntryDecoderState::kResumeDecodingType:
// entry_type_decoder_ returned kDecodeInProgress when last called.
- DVLOG(1) << "kResumeDecodingType: db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(1) << "kResumeDecodingType: db->Remaining="
+ << db->Remaining();
status = entry_type_decoder_.Resume(db);
if (status != DecodeStatus::kDecodeDone) {
return status;
@@ -109,7 +109,7 @@ DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
case EntryDecoderState::kDecodedType:
// entry_type_decoder_ returned kDecodeDone, now need to decide how
// to proceed.
- DVLOG(1) << "kDecodedType: db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(1) << "kDecodedType: db->Remaining=" << db->Remaining();
if (DispatchOnType(listener)) {
// All done.
return DecodeStatus::kDecodeDone;
@@ -117,7 +117,8 @@ DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
continue;
case EntryDecoderState::kStartDecodingName:
- DVLOG(1) << "kStartDecodingName: db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(1) << "kStartDecodingName: db->Remaining="
+ << db->Remaining();
{
NameDecoderListener ncb(listener);
status = string_decoder_.Start(db, &ncb);
@@ -134,7 +135,8 @@ DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
HTTP2_FALLTHROUGH;
case EntryDecoderState::kStartDecodingValue:
- DVLOG(1) << "kStartDecodingValue: db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(1) << "kStartDecodingValue: db->Remaining="
+ << db->Remaining();
{
ValueDecoderListener vcb(listener);
status = string_decoder_.Start(db, &vcb);
@@ -153,7 +155,8 @@ DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
case EntryDecoderState::kResumeDecodingName:
// The literal name was split across decode buffers.
- DVLOG(1) << "kResumeDecodingName: db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(1) << "kResumeDecodingName: db->Remaining="
+ << db->Remaining();
{
NameDecoderListener ncb(listener);
status = string_decoder_.Resume(db, &ncb);
@@ -171,7 +174,8 @@ DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
case EntryDecoderState::kResumeDecodingValue:
// The literal value was split across decode buffers.
- DVLOG(1) << "kResumeDecodingValue: db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(1) << "kResumeDecodingValue: db->Remaining="
+ << db->Remaining();
{
ValueDecoderListener vcb(listener);
status = string_decoder_.Resume(db, &vcb);
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h
index fe59d964841..5858c1e82e6 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h
@@ -10,7 +10,6 @@
// must provide a non-empty decode buffer. Continue with calls to Resume() if
// Start, and any subsequent calls to Resume, returns kDecodeInProgress.
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.h"
@@ -18,6 +17,7 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h"
#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.cc
index b783b150da8..a0571c47769 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.cc
@@ -4,12 +4,12 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_listener.h"
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
void HpackEntryDecoderVLoggingListener::OnIndexedHeader(size_t index) {
- VLOG(1) << "OnIndexedHeader, index=" << index;
+ HTTP2_VLOG(1) << "OnIndexedHeader, index=" << index;
if (wrapped_) {
wrapped_->OnIndexedHeader(index);
}
@@ -18,8 +18,8 @@ void HpackEntryDecoderVLoggingListener::OnIndexedHeader(size_t index) {
void HpackEntryDecoderVLoggingListener::OnStartLiteralHeader(
HpackEntryType entry_type,
size_t maybe_name_index) {
- VLOG(1) << "OnStartLiteralHeader: entry_type=" << entry_type
- << ", maybe_name_index=" << maybe_name_index;
+ HTTP2_VLOG(1) << "OnStartLiteralHeader: entry_type=" << entry_type
+ << ", maybe_name_index=" << maybe_name_index;
if (wrapped_) {
wrapped_->OnStartLiteralHeader(entry_type, maybe_name_index);
}
@@ -27,7 +27,7 @@ void HpackEntryDecoderVLoggingListener::OnStartLiteralHeader(
void HpackEntryDecoderVLoggingListener::OnNameStart(bool huffman_encoded,
size_t len) {
- VLOG(1) << "OnNameStart: H=" << huffman_encoded << ", len=" << len;
+ HTTP2_VLOG(1) << "OnNameStart: H=" << huffman_encoded << ", len=" << len;
if (wrapped_) {
wrapped_->OnNameStart(huffman_encoded, len);
}
@@ -35,14 +35,14 @@ void HpackEntryDecoderVLoggingListener::OnNameStart(bool huffman_encoded,
void HpackEntryDecoderVLoggingListener::OnNameData(const char* data,
size_t len) {
- VLOG(1) << "OnNameData: len=" << len;
+ HTTP2_VLOG(1) << "OnNameData: len=" << len;
if (wrapped_) {
wrapped_->OnNameData(data, len);
}
}
void HpackEntryDecoderVLoggingListener::OnNameEnd() {
- VLOG(1) << "OnNameEnd";
+ HTTP2_VLOG(1) << "OnNameEnd";
if (wrapped_) {
wrapped_->OnNameEnd();
}
@@ -50,7 +50,7 @@ void HpackEntryDecoderVLoggingListener::OnNameEnd() {
void HpackEntryDecoderVLoggingListener::OnValueStart(bool huffman_encoded,
size_t len) {
- VLOG(1) << "OnValueStart: H=" << huffman_encoded << ", len=" << len;
+ HTTP2_VLOG(1) << "OnValueStart: H=" << huffman_encoded << ", len=" << len;
if (wrapped_) {
wrapped_->OnValueStart(huffman_encoded, len);
}
@@ -58,21 +58,21 @@ void HpackEntryDecoderVLoggingListener::OnValueStart(bool huffman_encoded,
void HpackEntryDecoderVLoggingListener::OnValueData(const char* data,
size_t len) {
- VLOG(1) << "OnValueData: len=" << len;
+ HTTP2_VLOG(1) << "OnValueData: len=" << len;
if (wrapped_) {
wrapped_->OnValueData(data, len);
}
}
void HpackEntryDecoderVLoggingListener::OnValueEnd() {
- VLOG(1) << "OnValueEnd";
+ HTTP2_VLOG(1) << "OnValueEnd";
if (wrapped_) {
wrapped_->OnValueEnd();
}
}
void HpackEntryDecoderVLoggingListener::OnDynamicTableSizeUpdate(size_t size) {
- VLOG(1) << "OnDynamicTableSizeUpdate: size=" << size;
+ HTTP2_VLOG(1) << "OnDynamicTableSizeUpdate: size=" << size;
if (wrapped_) {
wrapped_->OnDynamicTableSizeUpdate(size);
}
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc
index 121e3d0b02c..7519f5a07b8 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.cc
@@ -4,8 +4,8 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h
index 79898becbef..092a8440f90 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder.h
@@ -12,12 +12,12 @@
#include <cstdint>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
index afff7cc629d..60248f3326f 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
@@ -6,9 +6,9 @@
#include <vector>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
#include "net/third_party/quiche/src/http2/tools/random_decoder_test.h"
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h
index 2128728e9d1..e30b36dc0bd 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder.h
@@ -14,12 +14,11 @@
#include <algorithm>
#include <cstdint>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
@@ -82,7 +81,8 @@ class HTTP2_EXPORT_PRIVATE HpackStringDecoder {
while (true) {
switch (state_) {
case kStartDecodingLength:
- DVLOG(2) << "kStartDecodingLength: db->Remaining=" << db->Remaining();
+ HTTP2_DVLOG(2) << "kStartDecodingLength: db->Remaining="
+ << db->Remaining();
if (!StartDecodingLength(db, cb, &status)) {
// The length is split across decode buffers.
return status;
@@ -99,13 +99,13 @@ class HTTP2_EXPORT_PRIVATE HpackStringDecoder {
HTTP2_FALLTHROUGH;
case kDecodingString:
- DVLOG(2) << "kDecodingString: db->Remaining=" << db->Remaining()
- << " remaining_=" << remaining_;
+ HTTP2_DVLOG(2) << "kDecodingString: db->Remaining=" << db->Remaining()
+ << " remaining_=" << remaining_;
return DecodeString(db, cb);
case kResumeDecodingLength:
- DVLOG(2) << "kResumeDecodingLength: db->Remaining="
- << db->Remaining();
+ HTTP2_DVLOG(2) << "kResumeDecodingLength: db->Remaining="
+ << db->Remaining();
if (!ResumeDecodingLength(db, cb, &status)) {
return status;
}
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_listener.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_listener.cc
index e0fbc657ef9..7a132e400a6 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_listener.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_listener.cc
@@ -4,14 +4,14 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_listener.h"
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
namespace test {
void HpackStringDecoderVLoggingListener::OnStringStart(bool huffman_encoded,
size_t len) {
- VLOG(1) << "OnStringStart: H=" << huffman_encoded << ", len=" << len;
+ HTTP2_VLOG(1) << "OnStringStart: H=" << huffman_encoded << ", len=" << len;
if (wrapped_) {
wrapped_->OnStringStart(huffman_encoded, len);
}
@@ -19,14 +19,14 @@ void HpackStringDecoderVLoggingListener::OnStringStart(bool huffman_encoded,
void HpackStringDecoderVLoggingListener::OnStringData(const char* data,
size_t len) {
- VLOG(1) << "OnStringData: len=" << len;
+ HTTP2_VLOG(1) << "OnStringData: len=" << len;
if (wrapped_) {
return wrapped_->OnStringData(data, len);
}
}
void HpackStringDecoderVLoggingListener::OnStringEnd() {
- VLOG(1) << "OnStringEnd";
+ HTTP2_VLOG(1) << "OnStringEnd";
if (wrapped_) {
return wrapped_->OnStringEnd();
}
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc
index 349b649d211..53469998d14 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc
@@ -38,13 +38,13 @@ class HpackStringDecoderTest : public RandomDecoderTest {
DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
// Provides coverage of DebugString and StateToString.
// Not validating output.
- VLOG(1) << decoder_.DebugString();
- VLOG(2) << collector_;
+ HTTP2_VLOG(1) << decoder_.DebugString();
+ HTTP2_VLOG(2) << collector_;
return decoder_.Resume(b, &listener_);
}
AssertionResult Collected(Http2StringPiece s, bool huffman_encoded) {
- VLOG(1) << collector_;
+ HTTP2_VLOG(1) << collector_;
return collector_.Collected(s, huffman_encoded);
}
@@ -64,9 +64,9 @@ class HpackStringDecoderTest : public RandomDecoderTest {
VERIFY_NE(collector_,
HpackStringCollector(expected_str, expected_huffman));
}
- VLOG(2) << collector_.ToString();
+ HTTP2_VLOG(2) << collector_.ToString();
collector_.Clear();
- VLOG(2) << collector_;
+ HTTP2_VLOG(2) << collector_;
return result;
};
}
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 b3e29b176cc..dea6698faa6 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
@@ -4,8 +4,8 @@
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
@@ -37,26 +37,26 @@ size_t HpackWholeEntryBuffer::EstimateMemoryUsage() const {
}
void HpackWholeEntryBuffer::OnIndexedHeader(size_t index) {
- DVLOG(2) << "HpackWholeEntryBuffer::OnIndexedHeader: index=" << index;
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnIndexedHeader: index=" << index;
listener_->OnIndexedHeader(index);
}
void HpackWholeEntryBuffer::OnStartLiteralHeader(HpackEntryType entry_type,
size_t maybe_name_index) {
- DVLOG(2) << "HpackWholeEntryBuffer::OnStartLiteralHeader: entry_type="
- << entry_type << ", maybe_name_index=" << maybe_name_index;
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnStartLiteralHeader: entry_type="
+ << entry_type << ", maybe_name_index=" << maybe_name_index;
entry_type_ = entry_type;
maybe_name_index_ = maybe_name_index;
}
void HpackWholeEntryBuffer::OnNameStart(bool huffman_encoded, size_t len) {
- DVLOG(2) << "HpackWholeEntryBuffer::OnNameStart: huffman_encoded="
- << (huffman_encoded ? "true" : "false") << ", len=" << len;
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnNameStart: huffman_encoded="
+ << (huffman_encoded ? "true" : "false") << ", len=" << len;
DCHECK_EQ(maybe_name_index_, 0u);
if (!error_detected_) {
if (len > max_string_size_bytes_) {
- DVLOG(1) << "Name length (" << len << ") is longer than permitted ("
- << max_string_size_bytes_ << ")";
+ HTTP2_DVLOG(1) << "Name length (" << len << ") is longer than permitted ("
+ << max_string_size_bytes_ << ")";
ReportError("HPACK entry name size is too long.");
return;
}
@@ -65,8 +65,9 @@ void HpackWholeEntryBuffer::OnNameStart(bool huffman_encoded, size_t len) {
}
void HpackWholeEntryBuffer::OnNameData(const char* data, size_t len) {
- DVLOG(2) << "HpackWholeEntryBuffer::OnNameData: len=" << len << " data:\n"
- << Http2HexDump(Http2StringPiece(data, len));
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnNameData: len=" << len
+ << " data:\n"
+ << Http2HexDump(Http2StringPiece(data, len));
DCHECK_EQ(maybe_name_index_, 0u);
if (!error_detected_ && !name_.OnData(data, len)) {
ReportError("Error decoding HPACK entry name.");
@@ -74,7 +75,7 @@ void HpackWholeEntryBuffer::OnNameData(const char* data, size_t len) {
}
void HpackWholeEntryBuffer::OnNameEnd() {
- DVLOG(2) << "HpackWholeEntryBuffer::OnNameEnd";
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnNameEnd";
DCHECK_EQ(maybe_name_index_, 0u);
if (!error_detected_ && !name_.OnEnd()) {
ReportError("Error decoding HPACK entry name.");
@@ -82,12 +83,13 @@ void HpackWholeEntryBuffer::OnNameEnd() {
}
void HpackWholeEntryBuffer::OnValueStart(bool huffman_encoded, size_t len) {
- DVLOG(2) << "HpackWholeEntryBuffer::OnValueStart: huffman_encoded="
- << (huffman_encoded ? "true" : "false") << ", len=" << len;
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnValueStart: huffman_encoded="
+ << (huffman_encoded ? "true" : "false") << ", len=" << len;
if (!error_detected_) {
if (len > max_string_size_bytes_) {
- DVLOG(1) << "Value length (" << len << ") is longer than permitted ("
- << max_string_size_bytes_ << ")";
+ HTTP2_DVLOG(1) << "Value length (" << len
+ << ") is longer than permitted (" << max_string_size_bytes_
+ << ")";
ReportError("HPACK entry value size is too long.");
return;
}
@@ -96,15 +98,16 @@ void HpackWholeEntryBuffer::OnValueStart(bool huffman_encoded, size_t len) {
}
void HpackWholeEntryBuffer::OnValueData(const char* data, size_t len) {
- DVLOG(2) << "HpackWholeEntryBuffer::OnValueData: len=" << len << " data:\n"
- << Http2HexDump(Http2StringPiece(data, len));
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnValueData: len=" << len
+ << " data:\n"
+ << Http2HexDump(Http2StringPiece(data, len));
if (!error_detected_ && !value_.OnData(data, len)) {
ReportError("Error decoding HPACK entry value.");
}
}
void HpackWholeEntryBuffer::OnValueEnd() {
- DVLOG(2) << "HpackWholeEntryBuffer::OnValueEnd";
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnValueEnd";
if (error_detected_) {
return;
}
@@ -123,13 +126,14 @@ void HpackWholeEntryBuffer::OnValueEnd() {
}
void HpackWholeEntryBuffer::OnDynamicTableSizeUpdate(size_t size) {
- DVLOG(2) << "HpackWholeEntryBuffer::OnDynamicTableSizeUpdate: size=" << size;
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnDynamicTableSizeUpdate: size="
+ << size;
listener_->OnDynamicTableSizeUpdate(size);
}
void HpackWholeEntryBuffer::ReportError(Http2StringPiece error_message) {
if (!error_detected_) {
- DVLOG(1) << "HpackWholeEntryBuffer::ReportError: " << error_message;
+ HTTP2_DVLOG(1) << "HpackWholeEntryBuffer::ReportError: " << error_message;
error_detected_ = true;
listener_->OnHpackDecodeError(error_message);
listener_ = HpackWholeEntryNoOpListener::NoOpListener();
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc
index 58bb821810e..957cffa62d7 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string.cc
@@ -6,7 +6,7 @@
#include <utility>
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
namespace http2 {
@@ -47,16 +47,16 @@ std::ostream& operator<<(std::ostream& out, const HpackString& v) {
HpackStringPair::HpackStringPair(const HpackString& name,
const HpackString& value)
: name(name), value(value) {
- DVLOG(3) << DebugString() << " ctor";
+ HTTP2_DVLOG(3) << DebugString() << " ctor";
}
HpackStringPair::HpackStringPair(Http2StringPiece name, Http2StringPiece value)
: name(name), value(value) {
- DVLOG(3) << DebugString() << " ctor";
+ HTTP2_DVLOG(3) << DebugString() << " ctor";
}
HpackStringPair::~HpackStringPair() {
- DVLOG(3) << DebugString() << " dtor";
+ HTTP2_DVLOG(3) << DebugString() << " dtor";
}
Http2String HpackStringPair::DebugString() const {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc
index 87f5975886d..898ef8d1fd1 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/hpack_string_test.cc
@@ -8,8 +8,8 @@
#include <utility>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
using ::testing::AssertionFailure;
@@ -140,8 +140,8 @@ TEST_F(HpackStringTest, MoveConstructor) {
EXPECT_TRUE(VerifyEqual(&hs0, ""));
EXPECT_TRUE(VerifyNotEqual(&hs1, ""));
- LOG(INFO) << hs0;
- LOG(INFO) << hs1;
+ HTTP2_LOG(INFO) << hs0;
+ HTTP2_LOG(INFO) << hs1;
}
} // namespace
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants_test.cc b/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants_test.cc
index 5b20b186efd..b266554961c 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/http2_hpack_constants_test.cc
@@ -4,10 +4,11 @@
#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
-#include "base/logging.h"
+#include <sstream>
+
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "net/third_party/quiche/src/http2/platform/api/http2_mock_log.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
namespace test {
@@ -30,40 +31,34 @@ TEST(HpackEntryTypeTest, HpackEntryTypeToString) {
TEST(HpackEntryTypeTest, OutputHpackEntryType) {
{
- CREATE_HTTP2_MOCK_LOG(log);
- log.StartCapturingLogs();
- EXPECT_HTTP2_LOG_CALL_CONTAINS(log, INFO, "kIndexedHeader");
- LOG(INFO) << HpackEntryType::kIndexedHeader;
+ std::stringstream log;
+ log << HpackEntryType::kIndexedHeader;
+ EXPECT_EQ("kIndexedHeader", log.str());
}
{
- CREATE_HTTP2_MOCK_LOG(log);
- log.StartCapturingLogs();
- EXPECT_HTTP2_LOG_CALL_CONTAINS(log, INFO, "kDynamicTableSizeUpdate");
- LOG(INFO) << HpackEntryType::kDynamicTableSizeUpdate;
+ std::stringstream log;
+ log << HpackEntryType::kDynamicTableSizeUpdate;
+ EXPECT_EQ("kDynamicTableSizeUpdate", log.str());
}
{
- CREATE_HTTP2_MOCK_LOG(log);
- log.StartCapturingLogs();
- EXPECT_HTTP2_LOG_CALL_CONTAINS(log, INFO, "kIndexedLiteralHeader");
- LOG(INFO) << HpackEntryType::kIndexedLiteralHeader;
+ std::stringstream log;
+ log << HpackEntryType::kIndexedLiteralHeader;
+ EXPECT_EQ("kIndexedLiteralHeader", log.str());
}
{
- CREATE_HTTP2_MOCK_LOG(log);
- log.StartCapturingLogs();
- EXPECT_HTTP2_LOG_CALL_CONTAINS(log, INFO, "kUnindexedLiteralHeader");
- LOG(INFO) << HpackEntryType::kUnindexedLiteralHeader;
+ std::stringstream log;
+ log << HpackEntryType::kUnindexedLiteralHeader;
+ EXPECT_EQ("kUnindexedLiteralHeader", log.str());
}
{
- CREATE_HTTP2_MOCK_LOG(log);
- log.StartCapturingLogs();
- EXPECT_HTTP2_LOG_CALL_CONTAINS(log, INFO, "kNeverIndexedLiteralHeader");
- LOG(INFO) << HpackEntryType::kNeverIndexedLiteralHeader;
+ std::stringstream log;
+ log << HpackEntryType::kNeverIndexedLiteralHeader;
+ EXPECT_EQ("kNeverIndexedLiteralHeader", log.str());
}
{
- CREATE_HTTP2_MOCK_LOG(log);
- log.StartCapturingLogs();
- EXPECT_HTTP2_LOG_CALL_CONTAINS(log, INFO, "UnknownHpackEntryType(1234321)");
- LOG(INFO) << static_cast<HpackEntryType>(1234321);
+ std::stringstream log;
+ log << static_cast<HpackEntryType>(1234321);
+ EXPECT_EQ("UnknownHpackEntryType(1234321)", log.str());
}
}
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc
index d1076ed3c07..e74ca143403 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.cc
@@ -7,7 +7,7 @@
#include <bitset>
#include <limits>
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
// Terminology:
//
@@ -415,13 +415,13 @@ HpackHuffmanDecoder::HpackHuffmanDecoder() = default;
HpackHuffmanDecoder::~HpackHuffmanDecoder() = default;
bool HpackHuffmanDecoder::Decode(Http2StringPiece input, Http2String* output) {
- DVLOG(1) << "HpackHuffmanDecoder::Decode";
+ HTTP2_DVLOG(1) << "HpackHuffmanDecoder::Decode";
// Fill bit_buffer_ from input.
input.remove_prefix(bit_buffer_.AppendBytes(input));
while (true) {
- DVLOG(3) << "Enter Decode Loop, bit_buffer_: " << bit_buffer_;
+ HTTP2_DVLOG(3) << "Enter Decode Loop, bit_buffer_: " << bit_buffer_;
if (bit_buffer_.count() >= 7) {
// Get high 7 bits of the bit buffer, see if that contains a complete
// code of 5, 6 or 7 bits.
@@ -447,10 +447,10 @@ bool HpackHuffmanDecoder::Decode(Http2StringPiece input, Http2String* output) {
}
HuffmanCode code_prefix = bit_buffer_.value() >> kExtraAccumulatorBitCount;
- DVLOG(3) << "code_prefix: " << HuffmanCodeBitSet(code_prefix);
+ HTTP2_DVLOG(3) << "code_prefix: " << HuffmanCodeBitSet(code_prefix);
PrefixInfo prefix_info = PrefixToInfo(code_prefix);
- DVLOG(3) << "prefix_info: " << prefix_info;
+ HTTP2_DVLOG(3) << "prefix_info: " << prefix_info;
DCHECK_LE(kMinCodeBitCount, prefix_info.code_length);
DCHECK_LE(prefix_info.code_length, kMaxCodeBitCount);
@@ -465,8 +465,8 @@ bool HpackHuffmanDecoder::Decode(Http2StringPiece input, Http2String* output) {
continue;
}
// Encoder is not supposed to explicity encode the EOS symbol.
- DLOG(ERROR) << "EOS explicitly encoded!\n " << bit_buffer_ << "\n "
- << prefix_info;
+ HTTP2_DLOG(ERROR) << "EOS explicitly encoded!\n " << bit_buffer_ << "\n "
+ << prefix_info;
return false;
}
// bit_buffer_ doesn't have enough bits in it to decode the next symbol.
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 6c07ca5b55b..30b586fd2a6 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
@@ -8,7 +8,6 @@
#include <iostream>
-#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.cc
index 9f561eedf46..a92a4c0ed7c 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.cc
@@ -4,8 +4,8 @@
#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/hpack/huffman/huffman_spec_tables.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
// TODO(jamessynge): Remove use of binary literals, that is a C++ 14 feature.
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 ccb6983bc66..2a8999fd3a8 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
@@ -4,7 +4,6 @@
#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h"
-#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_arraysize.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
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 d20eb354260..d7b8c7c6739 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
@@ -6,8 +6,8 @@
#include <ctype.h>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc
index ac85247f370..9741a23cbdd 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc
@@ -103,8 +103,9 @@ DecodeStatus HpackVarintDecoder::Resume(DecodeBuffer* db) {
}
// Signal error if value is too large or there are too many extension bytes.
- DLOG(WARNING) << "Variable length int encoding is too large or too long. "
- << DebugString();
+ HTTP2_DLOG(WARNING)
+ << "Variable length int encoding is too large or too long. "
+ << DebugString();
MarkDone();
return DecodeStatus::kDecodeError;
}
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h
index 5c99b5c194b..855ced449ab 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h
+++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h
@@ -30,10 +30,10 @@
#include <cstdint>
#include <limits>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
namespace http2 {
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 2d589fd7e60..16101262df8 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
@@ -8,9 +8,9 @@
#include <stddef.h>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_arraysize.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
#include "net/third_party/quiche/src/http2/tools/random_decoder_test.h"
@@ -108,7 +108,8 @@ class HpackVarintDecoderTest : public RandomDecoderTest,
};
INSTANTIATE_TEST_SUITE_P(
- HpackVarintDecoderTest, HpackVarintDecoderTest,
+ HpackVarintDecoderTest,
+ HpackVarintDecoderTest,
::testing::Combine(
// Bits of the first byte not part of the prefix should be ignored.
::testing::Values(0b00000000, 0b11111111, 0b10101010),
@@ -259,11 +260,11 @@ struct {
};
TEST_P(HpackVarintDecoderTest, Success) {
- for (size_t i = 0; i < HTTP2_ARRAYSIZE(kSuccessTestData); ++i) {
- DecodeExpectSuccess(Http2HexDecode(kSuccessTestData[i].data),
- kSuccessTestData[i].prefix_length,
- kSuccessTestData[i].expected_value);
- }
+ for (size_t i = 0; i < HTTP2_ARRAYSIZE(kSuccessTestData); ++i) {
+ DecodeExpectSuccess(Http2HexDecode(kSuccessTestData[i].data),
+ kSuccessTestData[i].prefix_length,
+ kSuccessTestData[i].expected_value);
+ }
}
struct {
@@ -300,10 +301,10 @@ struct {
{"ff80feffffffffffffff8100", 8}};
TEST_P(HpackVarintDecoderTest, Error) {
- for (size_t i = 0; i < HTTP2_ARRAYSIZE(kErrorTestData); ++i) {
- DecodeExpectError(Http2HexDecode(kErrorTestData[i].data),
- kErrorTestData[i].prefix_length);
- }
+ for (size_t i = 0; i < HTTP2_ARRAYSIZE(kErrorTestData); ++i) {
+ DecodeExpectError(Http2HexDecode(kErrorTestData[i].data),
+ kErrorTestData[i].prefix_length);
+ }
}
} // namespace
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc
index d3685d988cd..2e7ad3e2597 100644
--- a/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc
+++ b/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.cc
@@ -4,7 +4,7 @@
#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h"
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.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 cd26553284f..594317b3866 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
@@ -13,9 +13,9 @@
#include <set>
#include <vector>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
#include "net/third_party/quiche/src/http2/tools/random_decoder_test.h"
@@ -167,9 +167,9 @@ class HpackVarintRoundTripTest : public RandomDecoderTest {
Http2HexDump(buffer_));
if (value == minimum) {
- LOG(INFO) << "Checking minimum; " << msg;
+ HTTP2_LOG(INFO) << "Checking minimum; " << msg;
} else if (value == maximum) {
- LOG(INFO) << "Checking maximum; " << msg;
+ HTTP2_LOG(INFO) << "Checking maximum; " << msg;
}
SCOPED_TRACE(msg);
@@ -206,13 +206,15 @@ class HpackVarintRoundTripTest : public RandomDecoderTest {
const uint8_t prefix_mask = (1 << prefix_length) - 1;
const uint64_t beyond = start + range;
- LOG(INFO) << "############################################################";
- LOG(INFO) << "prefix_length=" << static_cast<int>(prefix_length);
- LOG(INFO) << "prefix_mask=" << std::hex << static_cast<int>(prefix_mask);
- LOG(INFO) << "start=" << start << " (" << std::hex << start << ")";
- LOG(INFO) << "range=" << range << " (" << std::hex << range << ")";
- LOG(INFO) << "beyond=" << beyond << " (" << std::hex << beyond << ")";
- LOG(INFO) << "expected_bytes=" << expected_bytes;
+ HTTP2_LOG(INFO)
+ << "############################################################";
+ HTTP2_LOG(INFO) << "prefix_length=" << static_cast<int>(prefix_length);
+ HTTP2_LOG(INFO) << "prefix_mask=" << std::hex
+ << static_cast<int>(prefix_mask);
+ HTTP2_LOG(INFO) << "start=" << start << " (" << std::hex << start << ")";
+ HTTP2_LOG(INFO) << "range=" << range << " (" << std::hex << range << ")";
+ HTTP2_LOG(INFO) << "beyond=" << beyond << " (" << std::hex << beyond << ")";
+ HTTP2_LOG(INFO) << "expected_bytes=" << expected_bytes;
if (expected_bytes < 11) {
// Confirm the claim that beyond requires more bytes.
@@ -243,7 +245,7 @@ class HpackVarintRoundTripTest : public RandomDecoderTest {
uint8_t prefix_length_;
};
-// To help me and future debuggers of varint encodings, this LOGs out the
+// To help me and future debuggers of varint encodings, this HTTP2_LOGs out the
// transition points where a new extension byte is added.
TEST_F(HpackVarintRoundTripTest, Encode) {
for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
@@ -258,11 +260,12 @@ TEST_F(HpackVarintRoundTripTest, Encode) {
const uint64_t i = HiValueOfExtensionBytes(8, prefix_length);
const uint64_t j = HiValueOfExtensionBytes(9, prefix_length);
- LOG(INFO) << "############################################################";
- LOG(INFO) << "prefix_length=" << prefix_length << " a=" << a
- << " b=" << b << " c=" << c << " d=" << d << " e=" << e
- << " f=" << f << " g=" << g << " h=" << h << " i=" << i
- << " j=" << j;
+ HTTP2_LOG(INFO)
+ << "############################################################";
+ HTTP2_LOG(INFO) << "prefix_length=" << prefix_length << " a=" << a
+ << " b=" << b << " c=" << c << " d=" << d
+ << " e=" << e << " f=" << f << " g=" << g
+ << " h=" << h << " i=" << i << " j=" << j;
std::vector<uint64_t> values = {
0, 1, // Force line break.
@@ -281,8 +284,8 @@ TEST_F(HpackVarintRoundTripTest, Encode) {
for (uint64_t value : values) {
EncodeNoRandom(value, prefix_length);
Http2String dump = Http2HexDump(buffer_);
- LOG(INFO) << Http2StringPrintf("%10llu %0#18x ", value, value)
- << Http2HexDump(buffer_).substr(7);
+ HTTP2_LOG(INFO) << Http2StringPrintf("%10llu %0#18x ", value, value)
+ << Http2HexDump(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 d4d59a555c7..ad15b69da90 100644
--- a/chromium/net/third_party/quiche/src/http2/http2_constants.cc
+++ b/chromium/net/third_party/quiche/src/http2/http2_constants.cc
@@ -4,7 +4,7 @@
#include "net/third_party/quiche/src/http2/http2_constants.h"
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
diff --git a/chromium/net/third_party/quiche/src/http2/http2_structures.h b/chromium/net/third_party/quiche/src/http2/http2_structures.h
index 01c011e1f9c..f236cfbacec 100644
--- a/chromium/net/third_party/quiche/src/http2/http2_structures.h
+++ b/chromium/net/third_party/quiche/src/http2/http2_structures.h
@@ -30,9 +30,9 @@
#include <cstdint>
#include <ostream>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_export.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.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 78107f7dd2f..9e38248b036 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
@@ -154,8 +154,9 @@ class Http2FrameHeaderTypeAndFlagTest
protected:
Http2FrameHeaderTypeAndFlagTest()
: type_(std::get<0>(GetParam())), flags_(std::get<1>(GetParam())) {
- LOG(INFO) << "Frame type: " << type_;
- LOG(INFO) << "Frame flags: " << Http2FrameFlagsToString(type_, flags_);
+ HTTP2_LOG(INFO) << "Frame type: " << type_;
+ HTTP2_LOG(INFO) << "Frame flags: "
+ << Http2FrameFlagsToString(type_, flags_);
}
const Http2FrameType type_;
@@ -163,7 +164,8 @@ class Http2FrameHeaderTypeAndFlagTest
};
class IsEndStreamTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_SUITE_P(IsEndStream, IsEndStreamTest,
+INSTANTIATE_TEST_SUITE_P(IsEndStream,
+ IsEndStreamTest,
Combine(ValuesIn(ValidFrameTypes()),
Values(~Http2FrameFlag::END_STREAM, 0xff)));
TEST_P(IsEndStreamTest, IsEndStream) {
@@ -200,7 +202,8 @@ TEST_P(IsEndStreamTest, IsEndStream) {
}
class IsACKTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_SUITE_P(IsAck, IsACKTest,
+INSTANTIATE_TEST_SUITE_P(IsAck,
+ IsACKTest,
Combine(ValuesIn(ValidFrameTypes()),
Values(~Http2FrameFlag::ACK, 0xff)));
TEST_P(IsACKTest, IsAck) {
@@ -236,7 +239,8 @@ TEST_P(IsACKTest, IsAck) {
}
class IsEndHeadersTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_SUITE_P(IsEndHeaders, IsEndHeadersTest,
+INSTANTIATE_TEST_SUITE_P(IsEndHeaders,
+ IsEndHeadersTest,
Combine(ValuesIn(ValidFrameTypes()),
Values(~Http2FrameFlag::END_HEADERS, 0xff)));
TEST_P(IsEndHeadersTest, IsEndHeaders) {
@@ -276,7 +280,8 @@ TEST_P(IsEndHeadersTest, IsEndHeaders) {
}
class IsPaddedTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_SUITE_P(IsPadded, IsPaddedTest,
+INSTANTIATE_TEST_SUITE_P(IsPadded,
+ IsPaddedTest,
Combine(ValuesIn(ValidFrameTypes()),
Values(~Http2FrameFlag::PADDED, 0xff)));
TEST_P(IsPaddedTest, IsPadded) {
@@ -314,7 +319,8 @@ TEST_P(IsPaddedTest, IsPadded) {
}
class HasPriorityTest : public Http2FrameHeaderTypeAndFlagTest {};
-INSTANTIATE_TEST_SUITE_P(HasPriority, HasPriorityTest,
+INSTANTIATE_TEST_SUITE_P(HasPriority,
+ HasPriorityTest,
Combine(ValuesIn(ValidFrameTypes()),
Values(~Http2FrameFlag::PRIORITY, 0xff)));
TEST_P(HasPriorityTest, HasPriority) {
diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_logging.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_logging.h
new file mode 100644
index 00000000000..cd93ac36dbc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/http2/platform/api/http2_logging.h
@@ -0,0 +1,22 @@
+#ifndef QUICHE_HTTP2_PLATFORM_API_HTTP2_LOGGING_H_
+#define QUICHE_HTTP2_PLATFORM_API_HTTP2_LOGGING_H_
+
+#include "net/http2/platform/impl/http2_logging_impl.h"
+
+#define HTTP2_LOG(severity) HTTP2_LOG_IMPL(severity)
+
+#define HTTP2_VLOG(verbose_level) HTTP2_VLOG_IMPL(verbose_level)
+
+#define HTTP2_DLOG(severity) HTTP2_DLOG_IMPL(severity)
+
+#define HTTP2_DLOG_IF(severity, condition) \
+ HTTP2_DLOG_IF_IMPL(severity, condition)
+
+#define HTTP2_DVLOG(verbose_level) HTTP2_DVLOG_IMPL(verbose_level)
+
+#define HTTP2_DVLOG_IF(verbose_level, condition) \
+ HTTP2_DVLOG_IF_IMPL(verbose_level, condition)
+
+#define HTTP2_DLOG_EVERY_N(severity, n) HTTP2_DLOG_EVERY_N_IMPL(severity, n)
+
+#endif // QUICHE_HTTP2_PLATFORM_API_HTTP2_LOGGING_H_
diff --git a/chromium/net/third_party/quiche/src/http2/platform/api/http2_mock_log.h b/chromium/net/third_party/quiche/src/http2/platform/api/http2_mock_log.h
deleted file mode 100644
index 3d16837d518..00000000000
--- a/chromium/net/third_party/quiche/src/http2/platform/api/http2_mock_log.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 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_HTTP2_PLATFORM_API_HTTP2_MOCK_LOG_H_
-#define QUICHE_HTTP2_PLATFORM_API_HTTP2_MOCK_LOG_H_
-
-#include "net/http2/platform/impl/http2_mock_log_impl.h"
-
-using Http2MockLog = Http2MockLogImpl;
-#define CREATE_HTTP2_MOCK_LOG(log) CREATE_HTTP2_MOCK_LOG_IMPL(log)
-
-#define EXPECT_HTTP2_LOG_CALL(log) EXPECT_HTTP2_LOG_CALL_IMPL(log)
-
-#define EXPECT_HTTP2_LOG_CALL_CONTAINS(log, level, content) \
- EXPECT_HTTP2_LOG_CALL_CONTAINS_IMPL(log, level, content)
-
-#endif // QUICHE_HTTP2_PLATFORM_API_HTTP2_MOCK_LOG_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 3d0453e9294..d5507882da9 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
@@ -6,10 +6,10 @@
#include <type_traits>
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
@@ -48,12 +48,12 @@ AssertionResult VerifyOptionalEq(const T& opt_a, const T& opt_b) {
} // namespace
FrameParts::FrameParts(const Http2FrameHeader& header) : frame_header_(header) {
- VLOG(1) << "FrameParts, header: " << frame_header_;
+ HTTP2_VLOG(1) << "FrameParts, header: " << frame_header_;
}
FrameParts::FrameParts(const Http2FrameHeader& header, Http2StringPiece payload)
: FrameParts(header) {
- VLOG(1) << "FrameParts with payload.size() = " << payload.size();
+ HTTP2_VLOG(1) << "FrameParts with payload.size() = " << payload.size();
this->payload_.append(payload.data(), payload.size());
opt_payload_length_ = payload.size();
}
@@ -61,7 +61,7 @@ FrameParts::FrameParts(const Http2FrameHeader& header,
Http2StringPiece payload,
size_t total_pad_length)
: FrameParts(header, payload) {
- VLOG(1) << "FrameParts with total_pad_length=" << total_pad_length;
+ HTTP2_VLOG(1) << "FrameParts with total_pad_length=" << total_pad_length;
SetTotalPadLength(total_pad_length);
}
@@ -110,9 +110,10 @@ void FrameParts::SetTotalPadLength(size_t total_pad_length) {
}
if (opt_pad_length_) {
- VLOG(1) << "SetTotalPadLength: pad_length=" << opt_pad_length_.value();
+ HTTP2_VLOG(1) << "SetTotalPadLength: pad_length="
+ << opt_pad_length_.value();
} else {
- VLOG(1) << "SetTotalPadLength: has no pad length";
+ HTTP2_VLOG(1) << "SetTotalPadLength: has no pad length";
}
}
@@ -130,33 +131,33 @@ bool FrameParts::OnFrameHeader(const Http2FrameHeader& header) {
}
void FrameParts::OnDataStart(const Http2FrameHeader& header) {
- VLOG(1) << "OnDataStart: " << header;
+ HTTP2_VLOG(1) << "OnDataStart: " << header;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::DATA)) << *this;
opt_payload_length_ = header.payload_length;
}
void FrameParts::OnDataPayload(const char* data, size_t len) {
- VLOG(1) << "OnDataPayload: len=" << len
- << "; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnDataPayload: len=" << len
+ << "; frame_header_: " << frame_header_;
ASSERT_TRUE(InFrameOfType(Http2FrameType::DATA)) << *this;
ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &payload_,
&opt_payload_length_));
}
void FrameParts::OnDataEnd() {
- VLOG(1) << "OnDataEnd; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnDataEnd; frame_header_: " << frame_header_;
ASSERT_TRUE(EndFrameOfType(Http2FrameType::DATA)) << *this;
}
void FrameParts::OnHeadersStart(const Http2FrameHeader& header) {
- VLOG(1) << "OnHeadersStart: " << header;
+ HTTP2_VLOG(1) << "OnHeadersStart: " << header;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::HEADERS)) << *this;
opt_payload_length_ = header.payload_length;
}
void FrameParts::OnHeadersPriority(const Http2PriorityFields& priority) {
- VLOG(1) << "OnHeadersPriority: priority: " << priority
- << "; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnHeadersPriority: priority: " << priority
+ << "; frame_header_: " << frame_header_;
ASSERT_TRUE(InFrameOfType(Http2FrameType::HEADERS)) << *this;
ASSERT_FALSE(opt_priority_);
opt_priority_ = priority;
@@ -166,8 +167,8 @@ void FrameParts::OnHeadersPriority(const Http2PriorityFields& priority) {
}
void FrameParts::OnHpackFragment(const char* data, size_t len) {
- VLOG(1) << "OnHpackFragment: len=" << len
- << "; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnHpackFragment: len=" << len
+ << "; frame_header_: " << frame_header_;
ASSERT_TRUE(got_start_callback_);
ASSERT_FALSE(got_end_callback_);
ASSERT_TRUE(FrameCanHaveHpackPayload(frame_header_)) << *this;
@@ -176,13 +177,13 @@ void FrameParts::OnHpackFragment(const char* data, size_t len) {
}
void FrameParts::OnHeadersEnd() {
- VLOG(1) << "OnHeadersEnd; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnHeadersEnd; frame_header_: " << frame_header_;
ASSERT_TRUE(EndFrameOfType(Http2FrameType::HEADERS)) << *this;
}
void FrameParts::OnPriorityFrame(const Http2FrameHeader& header,
const Http2PriorityFields& priority) {
- VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
+ HTTP2_VLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PRIORITY)) << *this;
ASSERT_FALSE(opt_priority_);
opt_priority_ = priority;
@@ -190,18 +191,18 @@ void FrameParts::OnPriorityFrame(const Http2FrameHeader& header,
}
void FrameParts::OnContinuationStart(const Http2FrameHeader& header) {
- VLOG(1) << "OnContinuationStart: " << header;
+ HTTP2_VLOG(1) << "OnContinuationStart: " << header;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::CONTINUATION)) << *this;
opt_payload_length_ = header.payload_length;
}
void FrameParts::OnContinuationEnd() {
- VLOG(1) << "OnContinuationEnd; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnContinuationEnd; frame_header_: " << frame_header_;
ASSERT_TRUE(EndFrameOfType(Http2FrameType::CONTINUATION)) << *this;
}
void FrameParts::OnPadLength(size_t trailing_length) {
- VLOG(1) << "OnPadLength: trailing_length=" << trailing_length;
+ HTTP2_VLOG(1) << "OnPadLength: trailing_length=" << trailing_length;
ASSERT_TRUE(InPaddedFrame()) << *this;
ASSERT_FALSE(opt_pad_length_);
ASSERT_TRUE(opt_payload_length_);
@@ -212,7 +213,7 @@ void FrameParts::OnPadLength(size_t trailing_length) {
}
void FrameParts::OnPadding(const char* pad, size_t skipped_length) {
- VLOG(1) << "OnPadding: skipped_length=" << skipped_length;
+ HTTP2_VLOG(1) << "OnPadding: skipped_length=" << skipped_length;
ASSERT_TRUE(InPaddedFrame()) << *this;
ASSERT_TRUE(opt_pad_length_);
ASSERT_TRUE(AppendString(Http2StringPiece(pad, skipped_length), &padding_,
@@ -221,7 +222,7 @@ void FrameParts::OnPadding(const char* pad, size_t skipped_length) {
void FrameParts::OnRstStream(const Http2FrameHeader& header,
Http2ErrorCode error_code) {
- VLOG(1) << "OnRstStream: " << header << "; code=" << error_code;
+ HTTP2_VLOG(1) << "OnRstStream: " << header << "; code=" << error_code;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::RST_STREAM)) << *this;
ASSERT_FALSE(opt_rst_stream_error_code_);
opt_rst_stream_error_code_ = error_code;
@@ -229,25 +230,25 @@ void FrameParts::OnRstStream(const Http2FrameHeader& header,
}
void FrameParts::OnSettingsStart(const Http2FrameHeader& header) {
- VLOG(1) << "OnSettingsStart: " << header;
+ HTTP2_VLOG(1) << "OnSettingsStart: " << header;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::SETTINGS)) << *this;
ASSERT_EQ(0u, settings_.size());
ASSERT_FALSE(header.IsAck()) << header;
}
void FrameParts::OnSetting(const Http2SettingFields& setting_fields) {
- VLOG(1) << "OnSetting: " << setting_fields;
+ HTTP2_VLOG(1) << "OnSetting: " << setting_fields;
ASSERT_TRUE(InFrameOfType(Http2FrameType::SETTINGS)) << *this;
settings_.push_back(setting_fields);
}
void FrameParts::OnSettingsEnd() {
- VLOG(1) << "OnSettingsEnd; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnSettingsEnd; frame_header_: " << frame_header_;
ASSERT_TRUE(EndFrameOfType(Http2FrameType::SETTINGS)) << *this;
}
void FrameParts::OnSettingsAck(const Http2FrameHeader& header) {
- VLOG(1) << "OnSettingsAck: " << header;
+ HTTP2_VLOG(1) << "OnSettingsAck: " << header;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::SETTINGS)) << *this;
ASSERT_EQ(0u, settings_.size());
ASSERT_TRUE(header.IsAck());
@@ -257,8 +258,9 @@ void FrameParts::OnSettingsAck(const Http2FrameHeader& header) {
void FrameParts::OnPushPromiseStart(const Http2FrameHeader& header,
const Http2PushPromiseFields& promise,
size_t total_padding_length) {
- VLOG(1) << "OnPushPromiseStart header: " << header << "; promise: " << promise
- << "; total_padding_length: " << total_padding_length;
+ HTTP2_VLOG(1) << "OnPushPromiseStart header: " << header
+ << "; promise: " << promise
+ << "; total_padding_length: " << total_padding_length;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PUSH_PROMISE)) << *this;
ASSERT_GE(header.payload_length, Http2PushPromiseFields::EncodedSize());
opt_payload_length_ =
@@ -274,13 +276,13 @@ void FrameParts::OnPushPromiseStart(const Http2FrameHeader& header,
}
void FrameParts::OnPushPromiseEnd() {
- VLOG(1) << "OnPushPromiseEnd; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnPushPromiseEnd; frame_header_: " << frame_header_;
ASSERT_TRUE(EndFrameOfType(Http2FrameType::PUSH_PROMISE)) << *this;
}
void FrameParts::OnPing(const Http2FrameHeader& header,
const Http2PingFields& ping) {
- VLOG(1) << "OnPing header: " << header << " ping: " << ping;
+ HTTP2_VLOG(1) << "OnPing header: " << header << " ping: " << ping;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PING)) << *this;
ASSERT_FALSE(header.IsAck());
ASSERT_FALSE(opt_ping_);
@@ -290,7 +292,7 @@ void FrameParts::OnPing(const Http2FrameHeader& header,
void FrameParts::OnPingAck(const Http2FrameHeader& header,
const Http2PingFields& ping) {
- VLOG(1) << "OnPingAck header: " << header << " ping: " << ping;
+ HTTP2_VLOG(1) << "OnPingAck header: " << header << " ping: " << ping;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::PING)) << *this;
ASSERT_TRUE(header.IsAck());
ASSERT_FALSE(opt_ping_);
@@ -300,7 +302,7 @@ void FrameParts::OnPingAck(const Http2FrameHeader& header,
void FrameParts::OnGoAwayStart(const Http2FrameHeader& header,
const Http2GoAwayFields& goaway) {
- VLOG(1) << "OnGoAwayStart: " << goaway;
+ HTTP2_VLOG(1) << "OnGoAwayStart: " << goaway;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::GOAWAY)) << *this;
ASSERT_FALSE(opt_goaway_);
opt_goaway_ = goaway;
@@ -309,21 +311,21 @@ void FrameParts::OnGoAwayStart(const Http2FrameHeader& header,
}
void FrameParts::OnGoAwayOpaqueData(const char* data, size_t len) {
- VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+ HTTP2_VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
ASSERT_TRUE(InFrameOfType(Http2FrameType::GOAWAY)) << *this;
ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &payload_,
&opt_payload_length_));
}
void FrameParts::OnGoAwayEnd() {
- VLOG(1) << "OnGoAwayEnd; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnGoAwayEnd; frame_header_: " << frame_header_;
ASSERT_TRUE(EndFrameOfType(Http2FrameType::GOAWAY)) << *this;
}
void FrameParts::OnWindowUpdate(const Http2FrameHeader& header,
uint32_t increment) {
- VLOG(1) << "OnWindowUpdate header: " << header
- << " increment=" << increment;
+ HTTP2_VLOG(1) << "OnWindowUpdate header: " << header
+ << " increment=" << increment;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::WINDOW_UPDATE)) << *this;
ASSERT_FALSE(opt_window_update_increment_);
opt_window_update_increment_ = increment;
@@ -333,9 +335,9 @@ void FrameParts::OnWindowUpdate(const Http2FrameHeader& header,
void FrameParts::OnAltSvcStart(const Http2FrameHeader& header,
size_t origin_length,
size_t value_length) {
- VLOG(1) << "OnAltSvcStart: " << header
- << " origin_length: " << origin_length
- << " value_length: " << value_length;
+ HTTP2_VLOG(1) << "OnAltSvcStart: " << header
+ << " origin_length: " << origin_length
+ << " value_length: " << value_length;
ASSERT_TRUE(StartFrameOfType(header, Http2FrameType::ALTSVC)) << *this;
ASSERT_FALSE(opt_altsvc_origin_length_);
opt_altsvc_origin_length_ = origin_length;
@@ -344,26 +346,26 @@ void FrameParts::OnAltSvcStart(const Http2FrameHeader& header,
}
void FrameParts::OnAltSvcOriginData(const char* data, size_t len) {
- VLOG(1) << "OnAltSvcOriginData: len=" << len;
+ HTTP2_VLOG(1) << "OnAltSvcOriginData: len=" << len;
ASSERT_TRUE(InFrameOfType(Http2FrameType::ALTSVC)) << *this;
ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &altsvc_origin_,
&opt_altsvc_origin_length_));
}
void FrameParts::OnAltSvcValueData(const char* data, size_t len) {
- VLOG(1) << "OnAltSvcValueData: len=" << len;
+ HTTP2_VLOG(1) << "OnAltSvcValueData: len=" << len;
ASSERT_TRUE(InFrameOfType(Http2FrameType::ALTSVC)) << *this;
ASSERT_TRUE(AppendString(Http2StringPiece(data, len), &altsvc_value_,
&opt_altsvc_value_length_));
}
void FrameParts::OnAltSvcEnd() {
- VLOG(1) << "OnAltSvcEnd; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnAltSvcEnd; frame_header_: " << frame_header_;
ASSERT_TRUE(EndFrameOfType(Http2FrameType::ALTSVC)) << *this;
}
void FrameParts::OnUnknownStart(const Http2FrameHeader& header) {
- VLOG(1) << "OnUnknownStart: " << header;
+ HTTP2_VLOG(1) << "OnUnknownStart: " << header;
ASSERT_FALSE(IsSupportedHttp2FrameType(header.type)) << header;
ASSERT_FALSE(got_start_callback_);
ASSERT_EQ(frame_header_, header);
@@ -372,7 +374,7 @@ void FrameParts::OnUnknownStart(const Http2FrameHeader& header) {
}
void FrameParts::OnUnknownPayload(const char* data, size_t len) {
- VLOG(1) << "OnUnknownPayload: len=" << len;
+ HTTP2_VLOG(1) << "OnUnknownPayload: len=" << len;
ASSERT_FALSE(IsSupportedHttp2FrameType(frame_header_.type)) << *this;
ASSERT_TRUE(got_start_callback_);
ASSERT_FALSE(got_end_callback_);
@@ -381,7 +383,7 @@ void FrameParts::OnUnknownPayload(const char* data, size_t len) {
}
void FrameParts::OnUnknownEnd() {
- VLOG(1) << "OnUnknownEnd; frame_header_: " << frame_header_;
+ HTTP2_VLOG(1) << "OnUnknownEnd; frame_header_: " << frame_header_;
ASSERT_FALSE(IsSupportedHttp2FrameType(frame_header_.type)) << *this;
ASSERT_TRUE(got_start_callback_);
ASSERT_FALSE(got_end_callback_);
@@ -390,8 +392,8 @@ void FrameParts::OnUnknownEnd() {
void FrameParts::OnPaddingTooLong(const Http2FrameHeader& header,
size_t missing_length) {
- VLOG(1) << "OnPaddingTooLong: " << header
- << "; missing_length: " << missing_length;
+ HTTP2_VLOG(1) << "OnPaddingTooLong: " << header
+ << "; missing_length: " << missing_length;
ASSERT_EQ(frame_header_, header);
ASSERT_FALSE(got_end_callback_);
ASSERT_TRUE(FrameIsPadded(header));
@@ -403,7 +405,7 @@ void FrameParts::OnPaddingTooLong(const Http2FrameHeader& header,
}
void FrameParts::OnFrameSizeError(const Http2FrameHeader& header) {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
ASSERT_EQ(frame_header_, header);
ASSERT_FALSE(got_end_callback_);
ASSERT_FALSE(has_frame_size_error_);
diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h
index b531174fa1d..5a7d872e893 100644
--- a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h
+++ b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts.h
@@ -15,11 +15,11 @@
#include <cstdint>
#include <vector>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder_listener.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
#include "net/third_party/quiche/src/http2/http2_structures.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_optional.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector.cc b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector.cc
index be4d986259f..cb0e32b2287 100644
--- a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector.cc
+++ b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector.cc
@@ -6,9 +6,9 @@
#include <utility>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/http2_structures_test_util.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_ptr_util.h"
namespace http2 {
diff --git a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.cc b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.cc
index 8d9da78707b..a7c3eccb171 100644
--- a/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.cc
+++ b/chromium/net/third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.cc
@@ -4,95 +4,95 @@
#include "net/third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.h"
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
namespace http2 {
namespace test {
bool FramePartsCollectorListener::OnFrameHeader(
const Http2FrameHeader& header) {
- VLOG(1) << "OnFrameHeader: " << header;
+ HTTP2_VLOG(1) << "OnFrameHeader: " << header;
ExpectFrameHeader(header);
return true;
}
void FramePartsCollectorListener::OnDataStart(const Http2FrameHeader& header) {
- VLOG(1) << "OnDataStart: " << header;
+ HTTP2_VLOG(1) << "OnDataStart: " << header;
StartFrame(header)->OnDataStart(header);
}
void FramePartsCollectorListener::OnDataPayload(const char* data, size_t len) {
- VLOG(1) << "OnDataPayload: len=" << len;
+ HTTP2_VLOG(1) << "OnDataPayload: len=" << len;
CurrentFrame()->OnDataPayload(data, len);
}
void FramePartsCollectorListener::OnDataEnd() {
- VLOG(1) << "OnDataEnd";
+ HTTP2_VLOG(1) << "OnDataEnd";
EndFrame()->OnDataEnd();
}
void FramePartsCollectorListener::OnHeadersStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnHeadersStart: " << header;
+ HTTP2_VLOG(1) << "OnHeadersStart: " << header;
StartFrame(header)->OnHeadersStart(header);
}
void FramePartsCollectorListener::OnHeadersPriority(
const Http2PriorityFields& priority) {
- VLOG(1) << "OnHeadersPriority: " << priority;
+ HTTP2_VLOG(1) << "OnHeadersPriority: " << priority;
CurrentFrame()->OnHeadersPriority(priority);
}
void FramePartsCollectorListener::OnHpackFragment(const char* data,
size_t len) {
- VLOG(1) << "OnHpackFragment: len=" << len;
+ HTTP2_VLOG(1) << "OnHpackFragment: len=" << len;
CurrentFrame()->OnHpackFragment(data, len);
}
void FramePartsCollectorListener::OnHeadersEnd() {
- VLOG(1) << "OnHeadersEnd";
+ HTTP2_VLOG(1) << "OnHeadersEnd";
EndFrame()->OnHeadersEnd();
}
void FramePartsCollectorListener::OnPriorityFrame(
const Http2FrameHeader& header,
const Http2PriorityFields& priority_fields) {
- VLOG(1) << "OnPriority: " << header << "; " << priority_fields;
+ HTTP2_VLOG(1) << "OnPriority: " << header << "; " << priority_fields;
StartAndEndFrame(header)->OnPriorityFrame(header, priority_fields);
}
void FramePartsCollectorListener::OnContinuationStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnContinuationStart: " << header;
+ HTTP2_VLOG(1) << "OnContinuationStart: " << header;
StartFrame(header)->OnContinuationStart(header);
}
void FramePartsCollectorListener::OnContinuationEnd() {
- VLOG(1) << "OnContinuationEnd";
+ HTTP2_VLOG(1) << "OnContinuationEnd";
EndFrame()->OnContinuationEnd();
}
void FramePartsCollectorListener::OnPadLength(size_t pad_length) {
- VLOG(1) << "OnPadLength: " << pad_length;
+ HTTP2_VLOG(1) << "OnPadLength: " << pad_length;
CurrentFrame()->OnPadLength(pad_length);
}
void FramePartsCollectorListener::OnPadding(const char* padding,
size_t skipped_length) {
- VLOG(1) << "OnPadding: " << skipped_length;
+ HTTP2_VLOG(1) << "OnPadding: " << skipped_length;
CurrentFrame()->OnPadding(padding, skipped_length);
}
void FramePartsCollectorListener::OnRstStream(const Http2FrameHeader& header,
Http2ErrorCode error_code) {
- VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code;
+ HTTP2_VLOG(1) << "OnRstStream: " << header << "; error_code=" << error_code;
StartAndEndFrame(header)->OnRstStream(header, error_code);
}
void FramePartsCollectorListener::OnSettingsStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnSettingsStart: " << header;
+ HTTP2_VLOG(1) << "OnSettingsStart: " << header;
EXPECT_EQ(Http2FrameType::SETTINGS, header.type) << header;
EXPECT_EQ(Http2FrameFlag(), header.flags) << header;
StartFrame(header)->OnSettingsStart(header);
@@ -100,18 +100,18 @@ void FramePartsCollectorListener::OnSettingsStart(
void FramePartsCollectorListener::OnSetting(
const Http2SettingFields& setting_fields) {
- VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields;
+ HTTP2_VLOG(1) << "Http2SettingFields: setting_fields=" << setting_fields;
CurrentFrame()->OnSetting(setting_fields);
}
void FramePartsCollectorListener::OnSettingsEnd() {
- VLOG(1) << "OnSettingsEnd";
+ HTTP2_VLOG(1) << "OnSettingsEnd";
EndFrame()->OnSettingsEnd();
}
void FramePartsCollectorListener::OnSettingsAck(
const Http2FrameHeader& header) {
- VLOG(1) << "OnSettingsAck: " << header;
+ HTTP2_VLOG(1) << "OnSettingsAck: " << header;
StartAndEndFrame(header)->OnSettingsAck(header);
}
@@ -119,52 +119,53 @@ void FramePartsCollectorListener::OnPushPromiseStart(
const Http2FrameHeader& header,
const Http2PushPromiseFields& promise,
size_t total_padding_length) {
- VLOG(1) << "OnPushPromiseStart header: " << header << " promise: " << promise
- << " total_padding_length: " << total_padding_length;
+ HTTP2_VLOG(1) << "OnPushPromiseStart header: " << header
+ << " promise: " << promise
+ << " total_padding_length: " << total_padding_length;
EXPECT_EQ(Http2FrameType::PUSH_PROMISE, header.type);
StartFrame(header)->OnPushPromiseStart(header, promise, total_padding_length);
}
void FramePartsCollectorListener::OnPushPromiseEnd() {
- VLOG(1) << "OnPushPromiseEnd";
+ HTTP2_VLOG(1) << "OnPushPromiseEnd";
EndFrame()->OnPushPromiseEnd();
}
void FramePartsCollectorListener::OnPing(const Http2FrameHeader& header,
const Http2PingFields& ping) {
- VLOG(1) << "OnPing: " << header << "; " << ping;
+ HTTP2_VLOG(1) << "OnPing: " << header << "; " << ping;
StartAndEndFrame(header)->OnPing(header, ping);
}
void FramePartsCollectorListener::OnPingAck(const Http2FrameHeader& header,
const Http2PingFields& ping) {
- VLOG(1) << "OnPingAck: " << header << "; " << ping;
+ HTTP2_VLOG(1) << "OnPingAck: " << header << "; " << ping;
StartAndEndFrame(header)->OnPingAck(header, ping);
}
void FramePartsCollectorListener::OnGoAwayStart(
const Http2FrameHeader& header,
const Http2GoAwayFields& goaway) {
- VLOG(1) << "OnGoAwayStart header: " << header << "; goaway: " << goaway;
+ HTTP2_VLOG(1) << "OnGoAwayStart header: " << header << "; goaway: " << goaway;
StartFrame(header)->OnGoAwayStart(header, goaway);
}
void FramePartsCollectorListener::OnGoAwayOpaqueData(const char* data,
size_t len) {
- VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+ HTTP2_VLOG(1) << "OnGoAwayOpaqueData: len=" << len;
CurrentFrame()->OnGoAwayOpaqueData(data, len);
}
void FramePartsCollectorListener::OnGoAwayEnd() {
- VLOG(1) << "OnGoAwayEnd";
+ HTTP2_VLOG(1) << "OnGoAwayEnd";
EndFrame()->OnGoAwayEnd();
}
void FramePartsCollectorListener::OnWindowUpdate(
const Http2FrameHeader& header,
uint32_t window_size_increment) {
- VLOG(1) << "OnWindowUpdate: " << header
- << "; window_size_increment=" << window_size_increment;
+ HTTP2_VLOG(1) << "OnWindowUpdate: " << header
+ << "; window_size_increment=" << window_size_increment;
EXPECT_EQ(Http2FrameType::WINDOW_UPDATE, header.type);
StartAndEndFrame(header)->OnWindowUpdate(header, window_size_increment);
}
@@ -172,57 +173,57 @@ void FramePartsCollectorListener::OnWindowUpdate(
void FramePartsCollectorListener::OnAltSvcStart(const Http2FrameHeader& header,
size_t origin_length,
size_t value_length) {
- VLOG(1) << "OnAltSvcStart header: " << header
- << "; origin_length=" << origin_length
- << "; value_length=" << value_length;
+ HTTP2_VLOG(1) << "OnAltSvcStart header: " << header
+ << "; origin_length=" << origin_length
+ << "; value_length=" << value_length;
StartFrame(header)->OnAltSvcStart(header, origin_length, value_length);
}
void FramePartsCollectorListener::OnAltSvcOriginData(const char* data,
size_t len) {
- VLOG(1) << "OnAltSvcOriginData: len=" << len;
+ HTTP2_VLOG(1) << "OnAltSvcOriginData: len=" << len;
CurrentFrame()->OnAltSvcOriginData(data, len);
}
void FramePartsCollectorListener::OnAltSvcValueData(const char* data,
size_t len) {
- VLOG(1) << "OnAltSvcValueData: len=" << len;
+ HTTP2_VLOG(1) << "OnAltSvcValueData: len=" << len;
CurrentFrame()->OnAltSvcValueData(data, len);
}
void FramePartsCollectorListener::OnAltSvcEnd() {
- VLOG(1) << "OnAltSvcEnd";
+ HTTP2_VLOG(1) << "OnAltSvcEnd";
EndFrame()->OnAltSvcEnd();
}
void FramePartsCollectorListener::OnUnknownStart(
const Http2FrameHeader& header) {
- VLOG(1) << "OnUnknownStart: " << header;
+ HTTP2_VLOG(1) << "OnUnknownStart: " << header;
StartFrame(header)->OnUnknownStart(header);
}
void FramePartsCollectorListener::OnUnknownPayload(const char* data,
size_t len) {
- VLOG(1) << "OnUnknownPayload: len=" << len;
+ HTTP2_VLOG(1) << "OnUnknownPayload: len=" << len;
CurrentFrame()->OnUnknownPayload(data, len);
}
void FramePartsCollectorListener::OnUnknownEnd() {
- VLOG(1) << "OnUnknownEnd";
+ HTTP2_VLOG(1) << "OnUnknownEnd";
EndFrame()->OnUnknownEnd();
}
void FramePartsCollectorListener::OnPaddingTooLong(
const Http2FrameHeader& header,
size_t missing_length) {
- VLOG(1) << "OnPaddingTooLong: " << header
- << " missing_length: " << missing_length;
+ HTTP2_VLOG(1) << "OnPaddingTooLong: " << header
+ << " missing_length: " << missing_length;
EndFrame()->OnPaddingTooLong(header, missing_length);
}
void FramePartsCollectorListener::OnFrameSizeError(
const Http2FrameHeader& header) {
- VLOG(1) << "OnFrameSizeError: " << header;
+ HTTP2_VLOG(1) << "OnFrameSizeError: " << header;
FrameError(header)->OnFrameSizeError(header);
}
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 fc577f467fe..81c036679dd 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
@@ -1,6 +1,6 @@
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
-#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/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"
@@ -13,7 +13,7 @@ namespace test {
Http2Random::Http2Random() {
RAND_bytes(key_, sizeof(key_));
- LOG(INFO) << "Initialized test RNG with the following key: " << Key();
+ HTTP2_LOG(INFO) << "Initialized test RNG with the following key: " << Key();
}
Http2Random::Http2Random(Http2StringPiece key) {
diff --git a/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.cc b/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.cc
index 623487c6e0c..640e7e9ee0e 100644
--- a/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.cc
+++ b/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.cc
@@ -9,11 +9,11 @@
#include <algorithm>
#include <memory>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/http2_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
using ::testing::AssertionFailure;
@@ -33,13 +33,14 @@ DecodeStatus RandomDecoderTest::DecodeSegments(DecodeBuffer* original,
const SelectSize& select_size) {
DecodeStatus status = DecodeStatus::kDecodeInProgress;
bool first = true;
- VLOG(2) << "DecodeSegments: input size=" << original->Remaining();
+ HTTP2_VLOG(2) << "DecodeSegments: input size=" << original->Remaining();
while (first || original->HasData()) {
size_t remaining = original->Remaining();
size_t size =
std::min(remaining, select_size(first, original->Offset(), remaining));
DecodeBuffer db(original->cursor(), size);
- VLOG(2) << "Decoding " << size << " bytes of " << remaining << " remaining";
+ HTTP2_VLOG(2) << "Decoding " << size << " bytes of " << remaining
+ << " remaining";
if (first) {
first = false;
status = StartDecoding(&db);
@@ -78,13 +79,13 @@ AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
bool return_non_zero_on_first,
const Validator& validator) {
const uint32_t original_remaining = original->Remaining();
- VLOG(1) << "DecodeAndValidateSeveralWays - Start, remaining = "
- << original_remaining;
+ HTTP2_VLOG(1) << "DecodeAndValidateSeveralWays - Start, remaining = "
+ << original_remaining;
uint32_t first_consumed;
{
// Fast decode (no stopping unless decoder does so).
DecodeBuffer input(original->cursor(), original_remaining);
- VLOG(2) << "DecodeSegmentsAndValidate with SelectRemaining";
+ HTTP2_VLOG(2) << "DecodeSegmentsAndValidate with SelectRemaining";
VERIFY_SUCCESS(
DecodeSegmentsAndValidate(&input, SelectRemaining(), validator))
<< "\nFailed with SelectRemaining; input.Offset=" << input.Offset()
@@ -94,7 +95,7 @@ AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
if (original_remaining <= 30) {
// Decode again, one byte at a time.
DecodeBuffer input(original->cursor(), original_remaining);
- VLOG(2) << "DecodeSegmentsAndValidate with SelectOne";
+ HTTP2_VLOG(2) << "DecodeSegmentsAndValidate with SelectOne";
VERIFY_SUCCESS(DecodeSegmentsAndValidate(&input, SelectOne(), validator))
<< "\nFailed with SelectOne; input.Offset=" << input.Offset()
<< "; input.Remaining=" << input.Remaining();
@@ -103,7 +104,7 @@ AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
if (original_remaining <= 20) {
// Decode again, one or zero bytes at a time.
DecodeBuffer input(original->cursor(), original_remaining);
- VLOG(2) << "DecodeSegmentsAndValidate with SelectZeroAndOne";
+ HTTP2_VLOG(2) << "DecodeSegmentsAndValidate with SelectZeroAndOne";
VERIFY_SUCCESS(DecodeSegmentsAndValidate(
&input, SelectZeroAndOne(return_non_zero_on_first), validator))
<< "\nFailed with SelectZeroAndOne";
@@ -114,7 +115,7 @@ AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
{
// Decode again, with randomly selected segment sizes.
DecodeBuffer input(original->cursor(), original_remaining);
- VLOG(2) << "DecodeSegmentsAndValidate with SelectRandom";
+ HTTP2_VLOG(2) << "DecodeSegmentsAndValidate with SelectRandom";
VERIFY_SUCCESS(DecodeSegmentsAndValidate(
&input, SelectRandom(return_non_zero_on_first), validator))
<< "\nFailed with SelectRandom; input.Offset=" << input.Offset()
@@ -123,7 +124,7 @@ AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
}
VERIFY_EQ(original_remaining, original->Remaining());
original->AdvanceCursor(first_consumed);
- VLOG(1) << "DecodeAndValidateSeveralWays - SUCCESS";
+ HTTP2_VLOG(1) << "DecodeAndValidateSeveralWays - SUCCESS";
return ::testing::AssertionSuccess();
}
diff --git a/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h b/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h
index 36f319605f5..f162ad79ad9 100644
--- a/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h
+++ b/chromium/net/third_party/quiche/src/http2/tools/random_decoder_test.h
@@ -17,10 +17,10 @@
#include <memory>
#include <type_traits>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
diff --git a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc
new file mode 100644
index 00000000000..4ce5d8caccb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.cc
@@ -0,0 +1,303 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/chlo_extractor.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+namespace {
+
+class ChloFramerVisitor : public QuicFramerVisitorInterface,
+ public CryptoFramerVisitorInterface {
+ public:
+ ChloFramerVisitor(QuicFramer* framer,
+ const QuicTagVector& create_session_tag_indicators,
+ ChloExtractor::Delegate* delegate);
+
+ ~ChloFramerVisitor() override = default;
+
+ // QuicFramerVisitorInterface implementation
+ void OnError(QuicFramer* framer) override {}
+ bool OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) override;
+ void OnPacket() override {}
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {}
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {}
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
+ void OnDecryptedPacket(EncryptionLevel level) override {}
+ bool OnPacketHeader(const QuicPacketHeader& header) override;
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
+ bool OnStreamFrame(const QuicStreamFrame& frame) override;
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override;
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override;
+ bool OnAckFrameEnd(QuicPacketNumber start) override;
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
+ bool OnPingFrame(const QuicPingFrame& frame) override;
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override;
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override;
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override;
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override;
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override;
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override;
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override;
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override;
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override;
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
+ bool OnMessageFrame(const QuicMessageFrame& frame) override;
+ void OnPacketComplete() override {}
+ bool IsValidStatelessResetToken(QuicUint128 token) const override;
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {}
+
+ // CryptoFramerVisitorInterface implementation.
+ void OnError(CryptoFramer* framer) override;
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
+
+ // Shared implementation between OnStreamFrame and OnCryptoFrame.
+ bool OnHandshakeData(QuicStringPiece data);
+
+ bool found_chlo() { return found_chlo_; }
+ bool chlo_contains_tags() { return chlo_contains_tags_; }
+
+ private:
+ QuicFramer* framer_;
+ const QuicTagVector& create_session_tag_indicators_;
+ ChloExtractor::Delegate* delegate_;
+ bool found_chlo_;
+ bool chlo_contains_tags_;
+ QuicConnectionId connection_id_;
+};
+
+ChloFramerVisitor::ChloFramerVisitor(
+ QuicFramer* framer,
+ const QuicTagVector& create_session_tag_indicators,
+ ChloExtractor::Delegate* delegate)
+ : framer_(framer),
+ create_session_tag_indicators_(create_session_tag_indicators),
+ delegate_(delegate),
+ found_chlo_(false),
+ chlo_contains_tags_(false),
+ connection_id_(EmptyQuicConnectionId()) {}
+
+bool ChloFramerVisitor::OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat /*form*/) {
+ if (!framer_->IsSupportedVersion(version)) {
+ return false;
+ }
+ framer_->set_version(version);
+ return true;
+}
+
+bool ChloFramerVisitor::OnUnauthenticatedPublicHeader(
+ const QuicPacketHeader& header) {
+ connection_id_ = header.destination_connection_id;
+ return true;
+}
+bool ChloFramerVisitor::OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) {
+ return true;
+}
+bool ChloFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) {
+ return true;
+}
+void ChloFramerVisitor::OnCoalescedPacket(const QuicEncryptedPacket& packet) {}
+bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
+ if (QuicVersionUsesCryptoFrames(framer_->transport_version())) {
+ // CHLO will be sent in CRYPTO frames in v47 and above.
+ return false;
+ }
+ QuicStringPiece data(frame.data_buffer, frame.data_length);
+ if (frame.stream_id ==
+ QuicUtils::GetCryptoStreamId(framer_->transport_version()) &&
+ frame.offset == 0 && QuicTextUtils::StartsWith(data, "CHLO")) {
+ return OnHandshakeData(data);
+ }
+ return true;
+}
+
+bool ChloFramerVisitor::OnCryptoFrame(const QuicCryptoFrame& frame) {
+ if (!QuicVersionUsesCryptoFrames(framer_->transport_version())) {
+ // CHLO will be in stream frames before v47.
+ return false;
+ }
+ QuicStringPiece data(frame.data_buffer, frame.data_length);
+ if (frame.offset == 0 && QuicTextUtils::StartsWith(data, "CHLO")) {
+ return OnHandshakeData(data);
+ }
+ return true;
+}
+
+bool ChloFramerVisitor::OnHandshakeData(QuicStringPiece data) {
+ CryptoFramer crypto_framer;
+ crypto_framer.set_visitor(this);
+ if (!crypto_framer.ProcessInput(data)) {
+ return false;
+ }
+ // Interrogate the crypto framer and see if there are any
+ // intersecting tags between what we saw in the maybe-CHLO and the
+ // indicator set.
+ for (const QuicTag tag : create_session_tag_indicators_) {
+ if (crypto_framer.HasTag(tag)) {
+ chlo_contains_tags_ = true;
+ }
+ }
+ if (chlo_contains_tags_ && delegate_) {
+ // Unfortunately, because this is a partial CHLO,
+ // OnHandshakeMessage was never called, so the ALPN was never
+ // extracted. Fake it up a bit and send it to the delegate so that
+ // the correct dispatch can happen.
+ crypto_framer.ForceHandshake();
+ }
+
+ return true;
+}
+
+bool ChloFramerVisitor::OnAckFrameStart(QuicPacketNumber /*largest_acked*/,
+ QuicTime::Delta /*ack_delay_time*/) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnAckRange(QuicPacketNumber /*start*/,
+ QuicPacketNumber /*end*/) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnAckTimestamp(QuicPacketNumber /*packet_number*/,
+ QuicTime /*timestamp*/) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnAckFrameEnd(QuicPacketNumber /*start*/) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnPingFrame(const QuicPingFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnPathChallengeFrame(
+ const QuicPathChallengeFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnPathResponseFrame(
+ const QuicPathResponseFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnNewConnectionIdFrame(
+ const QuicNewConnectionIdFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnPaddingFrame(const QuicPaddingFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnMessageFrame(const QuicMessageFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::IsValidStatelessResetToken(QuicUint128 token) const {
+ return false;
+}
+
+bool ChloFramerVisitor::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) {
+ return true;
+}
+
+bool ChloFramerVisitor::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ return true;
+}
+
+void ChloFramerVisitor::OnError(CryptoFramer* framer) {}
+
+void ChloFramerVisitor::OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ if (delegate_ != nullptr) {
+ delegate_->OnChlo(framer_->transport_version(), connection_id_, message);
+ }
+ found_chlo_ = true;
+}
+
+} // namespace
+
+// static
+bool ChloExtractor::Extract(const QuicEncryptedPacket& packet,
+ const ParsedQuicVersionVector& versions,
+ const QuicTagVector& create_session_tag_indicators,
+ Delegate* delegate,
+ uint8_t connection_id_length) {
+ QuicFramer framer(versions, QuicTime::Zero(), Perspective::IS_SERVER,
+ connection_id_length);
+ ChloFramerVisitor visitor(&framer, create_session_tag_indicators, delegate);
+ framer.set_visitor(&visitor);
+ if (!framer.ProcessPacket(packet)) {
+ return false;
+ }
+ return visitor.found_chlo() || visitor.chlo_contains_tags();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.h b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.h
new file mode 100644
index 00000000000..4e53a10c300
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor.h
@@ -0,0 +1,45 @@
+// 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 QUICHE_QUIC_CORE_CHLO_EXTRACTOR_H_
+#define QUICHE_QUIC_CORE_CHLO_EXTRACTOR_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+// A utility for extracting QUIC Client Hello messages from packets,
+// without needs to spin up a full QuicSession.
+class ChloExtractor {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called when a CHLO message is found in the packets.
+ virtual void OnChlo(QuicTransportVersion version,
+ QuicConnectionId connection_id,
+ const CryptoHandshakeMessage& chlo) = 0;
+ };
+
+ // Extracts a CHLO message from |packet| and invokes the OnChlo
+ // method of |delegate|. Return true if a CHLO message was found,
+ // and false otherwise. If non-empty,
+ // |create_session_tag_indicators| contains a list of QUIC tags that
+ // if found will result in the session being created early, to
+ // enable support for multi-packet CHLOs.
+ static bool Extract(const QuicEncryptedPacket& packet,
+ const ParsedQuicVersionVector& versions,
+ const QuicTagVector& create_session_tag_indicators,
+ Delegate* delegate,
+ uint8_t connection_id_length);
+
+ ChloExtractor(const ChloExtractor&) = delete;
+ ChloExtractor operator=(const ChloExtractor&) = delete;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CHLO_EXTRACTOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/chlo_extractor_test.cc b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor_test.cc
new file mode 100644
index 00000000000..cea445dabaa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/chlo_extractor_test.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/chlo_extractor.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestDelegate : public ChloExtractor::Delegate {
+ public:
+ TestDelegate() = default;
+ ~TestDelegate() override = default;
+
+ // ChloExtractor::Delegate implementation
+ void OnChlo(QuicTransportVersion version,
+ QuicConnectionId connection_id,
+ const CryptoHandshakeMessage& chlo) override {
+ version_ = version;
+ connection_id_ = connection_id;
+ chlo_ = chlo.DebugString();
+ }
+
+ QuicConnectionId connection_id() const { return connection_id_; }
+ QuicTransportVersion transport_version() const { return version_; }
+ const std::string& chlo() const { return chlo_; }
+
+ private:
+ QuicConnectionId connection_id_;
+ QuicTransportVersion version_;
+ std::string chlo_;
+};
+
+class ChloExtractorTest : public QuicTest {
+ public:
+ ChloExtractorTest() {
+ header_.destination_connection_id = TestConnectionId();
+ header_.destination_connection_id_included = CONNECTION_ID_PRESENT;
+ header_.version_flag = true;
+ header_.version = AllSupportedVersions().front();
+ header_.reset_flag = false;
+ header_.packet_number_length = PACKET_4BYTE_PACKET_NUMBER;
+ header_.packet_number = QuicPacketNumber(1);
+ if (QuicVersionHasLongHeaderLengths(header_.version.transport_version)) {
+ header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+ }
+
+ void MakePacket(ParsedQuicVersion version,
+ QuicStringPiece data,
+ bool munge_offset,
+ bool munge_stream_id) {
+ QuicFrames frames;
+ size_t offset = 0;
+ if (munge_offset) {
+ offset++;
+ }
+ QuicFramer framer(SupportedVersions(header_.version), QuicTime::Zero(),
+ Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength);
+ if (!QuicVersionUsesCryptoFrames(version.transport_version) ||
+ munge_stream_id) {
+ QuicStreamId stream_id =
+ QuicUtils::GetCryptoStreamId(version.transport_version);
+ if (munge_stream_id) {
+ stream_id++;
+ }
+ frames.push_back(
+ QuicFrame(QuicStreamFrame(stream_id, false, offset, data)));
+ } else {
+ frames.push_back(
+ QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, offset, data)));
+ }
+ std::unique_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header_, frames));
+ EXPECT_TRUE(packet != nullptr);
+ size_t encrypted_length =
+ framer.EncryptPayload(ENCRYPTION_INITIAL, header_.packet_number,
+ *packet, buffer_, QUIC_ARRAYSIZE(buffer_));
+ ASSERT_NE(0u, encrypted_length);
+ packet_ = QuicMakeUnique<QuicEncryptedPacket>(buffer_, encrypted_length);
+ EXPECT_TRUE(packet_ != nullptr);
+ DeleteFrames(&frames);
+ }
+
+ protected:
+ TestDelegate delegate_;
+ QuicPacketHeader header_;
+ std::unique_ptr<QuicEncryptedPacket> packet_;
+ char buffer_[kMaxOutgoingPacketSize];
+};
+
+TEST_F(ChloExtractorTest, FindsValidChlo) {
+ CryptoHandshakeMessage client_hello;
+ client_hello.set_tag(kCHLO);
+
+ std::string client_hello_str(client_hello.GetSerialized().AsStringPiece());
+ // Construct a CHLO with each supported version
+ for (ParsedQuicVersion version : AllSupportedVersions()) {
+ SCOPED_TRACE(version);
+ ParsedQuicVersionVector versions(SupportedVersions(version));
+ header_.version = version;
+ if (QuicVersionHasLongHeaderLengths(version.transport_version) &&
+ header_.version_flag) {
+ header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ } else {
+ header_.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ header_.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ }
+ MakePacket(version, client_hello_str, /*munge_offset*/ false,
+ /*munge_stream_id*/ false);
+ EXPECT_TRUE(ChloExtractor::Extract(*packet_, versions, {}, &delegate_,
+ kQuicDefaultConnectionIdLength))
+ << ParsedQuicVersionToString(version);
+ EXPECT_EQ(version.transport_version, delegate_.transport_version());
+ EXPECT_EQ(header_.destination_connection_id, delegate_.connection_id());
+ EXPECT_EQ(client_hello.DebugString(), delegate_.chlo())
+ << ParsedQuicVersionToString(version);
+ }
+}
+
+TEST_F(ChloExtractorTest, DoesNotFindValidChloOnWrongStream) {
+ CryptoHandshakeMessage client_hello;
+ client_hello.set_tag(kCHLO);
+
+ std::string client_hello_str(client_hello.GetSerialized().AsStringPiece());
+ MakePacket(AllSupportedVersions()[0], client_hello_str,
+ /*munge_offset*/ false, /*munge_stream_id*/ true);
+ EXPECT_FALSE(ChloExtractor::Extract(*packet_, AllSupportedVersions(), {},
+ &delegate_,
+ kQuicDefaultConnectionIdLength));
+}
+
+TEST_F(ChloExtractorTest, DoesNotFindValidChloOnWrongOffset) {
+ CryptoHandshakeMessage client_hello;
+ client_hello.set_tag(kCHLO);
+
+ std::string client_hello_str(client_hello.GetSerialized().AsStringPiece());
+ MakePacket(AllSupportedVersions()[0], client_hello_str, /*munge_offset*/ true,
+ /*munge_stream_id*/ false);
+ EXPECT_FALSE(ChloExtractor::Extract(*packet_, AllSupportedVersions(), {},
+ &delegate_,
+ kQuicDefaultConnectionIdLength));
+}
+
+TEST_F(ChloExtractorTest, DoesNotFindInvalidChlo) {
+ MakePacket(AllSupportedVersions()[0], "foo", /*munge_offset*/ false,
+ /*munge_stream_id*/ true);
+ EXPECT_FALSE(ChloExtractor::Extract(*packet_, AllSupportedVersions(), {},
+ &delegate_,
+ kQuicDefaultConnectionIdLength));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc
new file mode 100644
index 00000000000..88343b41b4e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.cc
@@ -0,0 +1,214 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h"
+
+#include <algorithm>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+BandwidthSampler::BandwidthSampler()
+ : total_bytes_sent_(0),
+ total_bytes_acked_(0),
+ total_bytes_lost_(0),
+ total_bytes_sent_at_last_acked_packet_(0),
+ last_acked_packet_sent_time_(QuicTime::Zero()),
+ last_acked_packet_ack_time_(QuicTime::Zero()),
+ is_app_limited_(false),
+ connection_state_map_() {}
+
+BandwidthSampler::~BandwidthSampler() {}
+
+void BandwidthSampler::OnPacketSent(
+ QuicTime sent_time,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) {
+ last_sent_packet_ = packet_number;
+
+ if (has_retransmittable_data != HAS_RETRANSMITTABLE_DATA) {
+ return;
+ }
+
+ total_bytes_sent_ += bytes;
+
+ // If there are no packets in flight, the time at which the new transmission
+ // opens can be treated as the A_0 point for the purpose of bandwidth
+ // sampling. This underestimates bandwidth to some extent, and produces some
+ // artificially low samples for most packets in flight, but it provides with
+ // samples at important points where we would not have them otherwise, most
+ // importantly at the beginning of the connection.
+ if (bytes_in_flight == 0) {
+ last_acked_packet_ack_time_ = sent_time;
+ total_bytes_sent_at_last_acked_packet_ = total_bytes_sent_;
+
+ // In this situation ack compression is not a concern, set send rate to
+ // effectively infinite.
+ last_acked_packet_sent_time_ = sent_time;
+ }
+
+ if (!connection_state_map_.IsEmpty() &&
+ packet_number >
+ connection_state_map_.last_packet() + kMaxTrackedPackets) {
+ QUIC_BUG << "BandwidthSampler in-flight packet map has exceeded maximum "
+ "number "
+ "of tracked packets.";
+ }
+
+ bool success =
+ connection_state_map_.Emplace(packet_number, sent_time, bytes, *this);
+ QUIC_BUG_IF(!success) << "BandwidthSampler failed to insert the packet "
+ "into the map, most likely because it's already "
+ "in it.";
+}
+
+BandwidthSample BandwidthSampler::OnPacketAcknowledged(
+ QuicTime ack_time,
+ QuicPacketNumber packet_number) {
+ ConnectionStateOnSentPacket* sent_packet_pointer =
+ connection_state_map_.GetEntry(packet_number);
+ if (sent_packet_pointer == nullptr) {
+ // See the TODO below.
+ return BandwidthSample();
+ }
+ BandwidthSample sample =
+ OnPacketAcknowledgedInner(ack_time, packet_number, *sent_packet_pointer);
+ connection_state_map_.Remove(packet_number);
+ return sample;
+}
+
+BandwidthSample BandwidthSampler::OnPacketAcknowledgedInner(
+ QuicTime ack_time,
+ QuicPacketNumber packet_number,
+ const ConnectionStateOnSentPacket& sent_packet) {
+ total_bytes_acked_ += sent_packet.size;
+ total_bytes_sent_at_last_acked_packet_ =
+ sent_packet.send_time_state.total_bytes_sent;
+ last_acked_packet_sent_time_ = sent_packet.sent_time;
+ last_acked_packet_ack_time_ = ack_time;
+
+ // Exit app-limited phase once a packet that was sent while the connection is
+ // not app-limited is acknowledged.
+ if (is_app_limited_ && packet_number > end_of_app_limited_phase_) {
+ is_app_limited_ = false;
+ }
+
+ // There might have been no packets acknowledged at the moment when the
+ // current packet was sent. In that case, there is no bandwidth sample to
+ // make.
+ if (sent_packet.last_acked_packet_sent_time == QuicTime::Zero()) {
+ return BandwidthSample();
+ }
+
+ // Infinite rate indicates that the sampler is supposed to discard the
+ // current send rate sample and use only the ack rate.
+ QuicBandwidth send_rate = QuicBandwidth::Infinite();
+ if (sent_packet.sent_time > sent_packet.last_acked_packet_sent_time) {
+ send_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ sent_packet.send_time_state.total_bytes_sent -
+ sent_packet.total_bytes_sent_at_last_acked_packet,
+ sent_packet.sent_time - sent_packet.last_acked_packet_sent_time);
+ }
+
+ // During the slope calculation, ensure that ack time of the current packet is
+ // always larger than the time of the previous packet, otherwise division by
+ // zero or integer underflow can occur.
+ if (ack_time <= sent_packet.last_acked_packet_ack_time) {
+ // TODO(wub): Compare this code count before and after fixing clock jitter
+ // issue.
+ if (sent_packet.last_acked_packet_ack_time == sent_packet.sent_time) {
+ // This is the 1st packet after quiescense.
+ QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 1, 2);
+ } else {
+ QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 2, 2);
+ }
+ QUIC_LOG(ERROR) << "Time of the previously acked packet:"
+ << sent_packet.last_acked_packet_ack_time.ToDebuggingValue()
+ << " is larger than the ack time of the current packet:"
+ << ack_time.ToDebuggingValue();
+ return BandwidthSample();
+ }
+ QuicBandwidth ack_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ total_bytes_acked_ - sent_packet.send_time_state.total_bytes_acked,
+ ack_time - sent_packet.last_acked_packet_ack_time);
+
+ BandwidthSample sample;
+ sample.bandwidth = std::min(send_rate, ack_rate);
+ // Note: this sample does not account for delayed acknowledgement time. This
+ // means that the RTT measurements here can be artificially high, especially
+ // on low bandwidth connections.
+ sample.rtt = ack_time - sent_packet.sent_time;
+ SentPacketToSendTimeState(sent_packet, &sample.state_at_send);
+ return sample;
+}
+
+SendTimeState BandwidthSampler::OnPacketLost(QuicPacketNumber packet_number) {
+ // TODO(vasilvv): see the comment for the case of missing packets in
+ // BandwidthSampler::OnPacketAcknowledged on why this does not raise a
+ // QUIC_BUG when removal fails.
+ SendTimeState send_time_state;
+ send_time_state.is_valid = connection_state_map_.Remove(
+ packet_number, [&](const ConnectionStateOnSentPacket& sent_packet) {
+ total_bytes_lost_ += sent_packet.size;
+ SentPacketToSendTimeState(sent_packet, &send_time_state);
+ });
+ return send_time_state;
+}
+
+void BandwidthSampler::SentPacketToSendTimeState(
+ const ConnectionStateOnSentPacket& sent_packet,
+ SendTimeState* send_time_state) const {
+ *send_time_state = sent_packet.send_time_state;
+ send_time_state->is_valid = true;
+}
+
+void BandwidthSampler::OnAppLimited() {
+ is_app_limited_ = true;
+ end_of_app_limited_phase_ = last_sent_packet_;
+}
+
+void BandwidthSampler::RemoveObsoletePackets(QuicPacketNumber least_unacked) {
+ // A packet can become obsolete when it is removed from QuicUnackedPacketMap's
+ // view of inflight before it is acked or marked as lost. For example, when
+ // QuicSentPacketManager::RetransmitCryptoPackets retransmits a crypto packet,
+ // the packet is removed from QuicUnackedPacketMap's inflight, but is not
+ // marked as acked or lost in the BandwidthSampler.
+ while (!connection_state_map_.IsEmpty() &&
+ connection_state_map_.first_packet() < least_unacked) {
+ connection_state_map_.Remove(
+ connection_state_map_.first_packet(),
+ [&](const ConnectionStateOnSentPacket& sent_packet) {
+ // Obsoleted packets as either acked or lost but the sampler doesn't
+ // know. We count them as acked here, since most packets are acked.
+ total_bytes_acked_ += sent_packet.size;
+ });
+ }
+}
+
+QuicByteCount BandwidthSampler::total_bytes_sent() const {
+ return total_bytes_sent_;
+}
+
+QuicByteCount BandwidthSampler::total_bytes_acked() const {
+ return total_bytes_acked_;
+}
+
+QuicByteCount BandwidthSampler::total_bytes_lost() const {
+ return total_bytes_lost_;
+}
+
+bool BandwidthSampler::is_app_limited() const {
+ return is_app_limited_;
+}
+
+QuicPacketNumber BandwidthSampler::end_of_app_limited_phase() const {
+ return end_of_app_limited_phase_;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..4de05b08251
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h
@@ -0,0 +1,337 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_
+
+#include "net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class BandwidthSamplerPeer;
+} // namespace test
+
+// A subset of BandwidthSampler::ConnectionStateOnSentPacket which is returned
+// to the caller when the packet is acked or lost.
+struct QUIC_EXPORT_PRIVATE SendTimeState {
+ SendTimeState()
+ : is_valid(false),
+ is_app_limited(false),
+ total_bytes_sent(0),
+ total_bytes_acked(0),
+ total_bytes_lost(0) {}
+
+ SendTimeState(bool is_app_limited,
+ QuicByteCount total_bytes_sent,
+ QuicByteCount total_bytes_acked,
+ QuicByteCount total_bytes_lost)
+ : is_valid(true),
+ is_app_limited(is_app_limited),
+ total_bytes_sent(total_bytes_sent),
+ total_bytes_acked(total_bytes_acked),
+ total_bytes_lost(total_bytes_lost) {}
+
+ SendTimeState(const SendTimeState& other) = default;
+
+ // Whether other states in this object is valid.
+ bool is_valid;
+
+ // Whether the sender is app limited at the time the packet was sent.
+ // App limited bandwidth sample might be artificially low because the sender
+ // did not have enough data to send in order to saturate the link.
+ bool is_app_limited;
+
+ // Total number of sent bytes at the time the packet was sent.
+ // Includes the packet itself.
+ QuicByteCount total_bytes_sent;
+
+ // Total number of acked bytes at the time the packet was sent.
+ QuicByteCount total_bytes_acked;
+
+ // Total number of lost bytes at the time the packet was sent.
+ QuicByteCount total_bytes_lost;
+};
+
+struct QUIC_EXPORT_PRIVATE BandwidthSample {
+ // The bandwidth at that particular sample. Zero if no valid bandwidth sample
+ // is available.
+ QuicBandwidth bandwidth;
+
+ // The RTT measurement at this particular sample. Zero if no RTT sample is
+ // available. Does not correct for delayed ack time.
+ QuicTime::Delta rtt;
+
+ // States captured when the packet was sent.
+ SendTimeState state_at_send;
+
+ BandwidthSample()
+ : bandwidth(QuicBandwidth::Zero()), rtt(QuicTime::Delta::Zero()) {}
+};
+
+// An interface common to any class that can provide bandwidth samples from the
+// information per individual acknowledged packet.
+class QUIC_EXPORT_PRIVATE BandwidthSamplerInterface {
+ public:
+ virtual ~BandwidthSamplerInterface() {}
+
+ // Inputs the sent packet information into the sampler. Assumes that all
+ // packets are sent in order. The information about the packet will not be
+ // released from the sampler until it the packet is either acknowledged or
+ // declared lost.
+ virtual void OnPacketSent(
+ QuicTime sent_time,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) = 0;
+
+ // Notifies the sampler that the |packet_number| is acknowledged. Returns a
+ // bandwidth sample. If no bandwidth sample is available,
+ // QuicBandwidth::Zero() is returned.
+ virtual BandwidthSample OnPacketAcknowledged(
+ QuicTime ack_time,
+ QuicPacketNumber packet_number) = 0;
+
+ // Informs the sampler that a packet is considered lost and it should no
+ // longer keep track of it.
+ virtual SendTimeState OnPacketLost(QuicPacketNumber packet_number) = 0;
+
+ // Informs the sampler that the connection is currently app-limited, causing
+ // the sampler to enter the app-limited phase. The phase will expire by
+ // itself.
+ virtual void OnAppLimited() = 0;
+
+ // Remove all the packets lower than the specified packet number.
+ virtual void RemoveObsoletePackets(QuicPacketNumber least_unacked) = 0;
+
+ // Total number of bytes sent/acked/lost in the connection.
+ virtual QuicByteCount total_bytes_sent() const = 0;
+ virtual QuicByteCount total_bytes_acked() const = 0;
+ virtual QuicByteCount total_bytes_lost() const = 0;
+
+ // Application-limited information exported for debugging.
+ virtual bool is_app_limited() const = 0;
+
+ virtual QuicPacketNumber end_of_app_limited_phase() const = 0;
+};
+
+// BandwidthSampler keeps track of sent and acknowledged packets and outputs a
+// bandwidth sample for every packet acknowledged. The samples are taken for
+// individual packets, and are not filtered; the consumer has to filter the
+// bandwidth samples itself. In certain cases, the sampler will locally severely
+// underestimate the bandwidth, hence a maximum filter with a size of at least
+// one RTT is recommended.
+//
+// This class bases its samples on the slope of two curves: the number of bytes
+// sent over time, and the number of bytes acknowledged as received over time.
+// It produces a sample of both slopes for every packet that gets acknowledged,
+// based on a slope between two points on each of the corresponding curves. Note
+// that due to the packet loss, the number of bytes on each curve might get
+// further and further away from each other, meaning that it is not feasible to
+// compare byte values coming from different curves with each other.
+//
+// The obvious points for measuring slope sample are the ones corresponding to
+// the packet that was just acknowledged. Let us denote them as S_1 (point at
+// which the current packet was sent) and A_1 (point at which the current packet
+// was acknowledged). However, taking a slope requires two points on each line,
+// so estimating bandwidth requires picking a packet in the past with respect to
+// which the slope is measured.
+//
+// For that purpose, BandwidthSampler always keeps track of the most recently
+// acknowledged packet, and records it together with every outgoing packet.
+// When a packet gets acknowledged (A_1), it has not only information about when
+// it itself was sent (S_1), but also the information about the latest
+// acknowledged packet right before it was sent (S_0 and A_0).
+//
+// Based on that data, send and ack rate are estimated as:
+// send_rate = (bytes(S_1) - bytes(S_0)) / (time(S_1) - time(S_0))
+// ack_rate = (bytes(A_1) - bytes(A_0)) / (time(A_1) - time(A_0))
+//
+// Here, the ack rate is intuitively the rate we want to treat as bandwidth.
+// However, in certain cases (e.g. ack compression) the ack rate at a point may
+// end up higher than the rate at which the data was originally sent, which is
+// not indicative of the real bandwidth. Hence, we use the send rate as an upper
+// bound, and the sample value is
+// rate_sample = min(send_rate, ack_rate)
+//
+// An important edge case handled by the sampler is tracking the app-limited
+// samples. There are multiple meaning of "app-limited" used interchangeably,
+// hence it is important to understand and to be able to distinguish between
+// them.
+//
+// Meaning 1: connection state. The connection is said to be app-limited when
+// there is no outstanding data to send. This means that certain bandwidth
+// samples in the future would not be an accurate indication of the link
+// capacity, and it is important to inform consumer about that. Whenever
+// connection becomes app-limited, the sampler is notified via OnAppLimited()
+// method.
+//
+// Meaning 2: a phase in the bandwidth sampler. As soon as the bandwidth
+// sampler becomes notified about the connection being app-limited, it enters
+// app-limited phase. In that phase, all *sent* packets are marked as
+// app-limited. Note that the connection itself does not have to be
+// app-limited during the app-limited phase, and in fact it will not be
+// (otherwise how would it send packets?). The boolean flag below indicates
+// whether the sampler is in that phase.
+//
+// Meaning 3: a flag on the sent packet and on the sample. If a sent packet is
+// sent during the app-limited phase, the resulting sample related to the
+// packet will be marked as app-limited.
+//
+// With the terminology issue out of the way, let us consider the question of
+// what kind of situation it addresses.
+//
+// Consider a scenario where we first send packets 1 to 20 at a regular
+// bandwidth, and then immediately run out of data. After a few seconds, we send
+// packets 21 to 60, and only receive ack for 21 between sending packets 40 and
+// 41. In this case, when we sample bandwidth for packets 21 to 40, the S_0/A_0
+// we use to compute the slope is going to be packet 20, a few seconds apart
+// from the current packet, hence the resulting estimate would be extremely low
+// and not indicative of anything. Only at packet 41 the S_0/A_0 will become 21,
+// meaning that the bandwidth sample would exclude the quiescence.
+//
+// Based on the analysis of that scenario, we implement the following rule: once
+// OnAppLimited() is called, all sent packets will produce app-limited samples
+// up until an ack for a packet that was sent after OnAppLimited() was called.
+// Note that while the scenario above is not the only scenario when the
+// connection is app-limited, the approach works in other cases too.
+class QUIC_EXPORT_PRIVATE BandwidthSampler : public BandwidthSamplerInterface {
+ public:
+ BandwidthSampler();
+ ~BandwidthSampler() override;
+
+ void OnPacketSent(QuicTime sent_time,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) override;
+ BandwidthSample OnPacketAcknowledged(QuicTime ack_time,
+ QuicPacketNumber packet_number) override;
+ SendTimeState OnPacketLost(QuicPacketNumber packet_number) override;
+
+ void OnAppLimited() override;
+
+ void RemoveObsoletePackets(QuicPacketNumber least_unacked) override;
+
+ QuicByteCount total_bytes_sent() const override;
+ QuicByteCount total_bytes_acked() const override;
+ QuicByteCount total_bytes_lost() const override;
+
+ bool is_app_limited() const override;
+
+ QuicPacketNumber end_of_app_limited_phase() const override;
+
+ private:
+ friend class test::BandwidthSamplerPeer;
+
+ // ConnectionStateOnSentPacket represents the information about a sent packet
+ // and the state of the connection at the moment the packet was sent,
+ // specifically the information about the most recently acknowledged packet at
+ // that moment.
+ struct ConnectionStateOnSentPacket {
+ // Time at which the packet is sent.
+ QuicTime sent_time;
+
+ // Size of the packet.
+ QuicByteCount size;
+
+ // The value of |total_bytes_sent_at_last_acked_packet_| at the time the
+ // packet was sent.
+ QuicByteCount total_bytes_sent_at_last_acked_packet;
+
+ // The value of |last_acked_packet_sent_time_| at the time the packet was
+ // sent.
+ QuicTime last_acked_packet_sent_time;
+
+ // The value of |last_acked_packet_ack_time_| at the time the packet was
+ // sent.
+ QuicTime last_acked_packet_ack_time;
+
+ // Send time states that are returned to the congestion controller when the
+ // packet is acked or lost.
+ SendTimeState send_time_state;
+
+ // Snapshot constructor. Records the current state of the bandwidth
+ // sampler.
+ ConnectionStateOnSentPacket(QuicTime sent_time,
+ QuicByteCount size,
+ const BandwidthSampler& sampler)
+ : sent_time(sent_time),
+ size(size),
+ total_bytes_sent_at_last_acked_packet(
+ sampler.total_bytes_sent_at_last_acked_packet_),
+ last_acked_packet_sent_time(sampler.last_acked_packet_sent_time_),
+ last_acked_packet_ack_time(sampler.last_acked_packet_ack_time_),
+ send_time_state(sampler.is_app_limited_,
+ sampler.total_bytes_sent_,
+ sampler.total_bytes_acked_,
+ sampler.total_bytes_lost_) {}
+
+ // Default constructor. Required to put this structure into
+ // PacketNumberIndexedQueue.
+ ConnectionStateOnSentPacket()
+ : sent_time(QuicTime::Zero()),
+ size(0),
+ total_bytes_sent_at_last_acked_packet(0),
+ last_acked_packet_sent_time(QuicTime::Zero()),
+ last_acked_packet_ack_time(QuicTime::Zero()) {}
+ };
+
+ // Copy a subset of the (private) ConnectionStateOnSentPacket to the (public)
+ // SendTimeState. Always set send_time_state->is_valid to true.
+ void SentPacketToSendTimeState(const ConnectionStateOnSentPacket& sent_packet,
+ SendTimeState* send_time_state) const;
+
+ // The total number of congestion controlled bytes sent during the connection.
+ QuicByteCount total_bytes_sent_;
+
+ // The total number of congestion controlled bytes which were acknowledged.
+ QuicByteCount total_bytes_acked_;
+
+ // The total number of congestion controlled bytes which were lost.
+ QuicByteCount total_bytes_lost_;
+
+ // The value of |total_bytes_sent_| at the time the last acknowledged packet
+ // was sent. Valid only when |last_acked_packet_sent_time_| is valid.
+ QuicByteCount total_bytes_sent_at_last_acked_packet_;
+
+ // The time at which the last acknowledged packet was sent. Set to
+ // QuicTime::Zero() if no valid timestamp is available.
+ QuicTime last_acked_packet_sent_time_;
+
+ // The time at which the most recent packet was acknowledged.
+ QuicTime last_acked_packet_ack_time_;
+
+ // The most recently sent packet.
+ QuicPacketNumber last_sent_packet_;
+
+ // Indicates whether the bandwidth sampler is currently in an app-limited
+ // phase.
+ bool is_app_limited_;
+
+ // The packet that will be acknowledged after this one will cause the sampler
+ // to exit the app-limited phase.
+ QuicPacketNumber end_of_app_limited_phase_;
+
+ // Record of the connection state at the point where each packet in flight was
+ // sent, indexed by the packet number.
+ PacketNumberIndexedQueue<ConnectionStateOnSentPacket> connection_state_map_;
+
+ // Handles the actual bandwidth calculations, whereas the outer method handles
+ // retrieving and removing |sent_packet|.
+ BandwidthSample OnPacketAcknowledgedInner(
+ QuicTime ack_time,
+ QuicPacketNumber packet_number,
+ const ConnectionStateOnSentPacket& sent_packet);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc
new file mode 100644
index 00000000000..750b14e6922
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -0,0 +1,464 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+namespace test {
+
+class BandwidthSamplerPeer {
+ public:
+ static size_t GetNumberOfTrackedPackets(const BandwidthSampler& sampler) {
+ return sampler.connection_state_map_.number_of_present_entries();
+ }
+
+ static QuicByteCount GetPacketSize(const BandwidthSampler& sampler,
+ QuicPacketNumber packet_number) {
+ return sampler.connection_state_map_.GetEntry(packet_number)->size;
+ }
+};
+
+const QuicByteCount kRegularPacketSize = 1280;
+// Enforce divisibility for some of the tests.
+static_assert((kRegularPacketSize & 31) == 0,
+ "kRegularPacketSize has to be five times divisible by 2");
+
+// A test fixture with utility methods for BandwidthSampler tests.
+class BandwidthSamplerTest : public QuicTest {
+ protected:
+ BandwidthSamplerTest() : bytes_in_flight_(0) {
+ // Ensure that the clock does not start at zero.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ MockClock clock_;
+ BandwidthSampler sampler_;
+ QuicByteCount bytes_in_flight_;
+
+ QuicByteCount PacketsToBytes(QuicPacketCount packet_count) {
+ return packet_count * kRegularPacketSize;
+ }
+
+ void SendPacketInner(uint64_t packet_number,
+ QuicByteCount bytes,
+ HasRetransmittableData has_retransmittable_data) {
+ sampler_.OnPacketSent(clock_.Now(), QuicPacketNumber(packet_number), bytes,
+ bytes_in_flight_, has_retransmittable_data);
+ if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA) {
+ bytes_in_flight_ += bytes;
+ }
+ }
+
+ void SendPacket(uint64_t packet_number) {
+ SendPacketInner(packet_number, kRegularPacketSize,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ BandwidthSample AckPacketInner(uint64_t packet_number) {
+ QuicByteCount size = BandwidthSamplerPeer::GetPacketSize(
+ sampler_, QuicPacketNumber(packet_number));
+ bytes_in_flight_ -= size;
+ return sampler_.OnPacketAcknowledged(clock_.Now(),
+ QuicPacketNumber(packet_number));
+ }
+
+ // Acknowledge receipt of a packet and expect it to be not app-limited.
+ QuicBandwidth AckPacket(uint64_t packet_number) {
+ BandwidthSample sample = AckPacketInner(packet_number);
+ EXPECT_TRUE(sample.state_at_send.is_valid);
+ EXPECT_FALSE(sample.state_at_send.is_app_limited);
+ return sample.bandwidth;
+ }
+
+ SendTimeState LosePacket(uint64_t packet_number) {
+ QuicByteCount size = BandwidthSamplerPeer::GetPacketSize(
+ sampler_, QuicPacketNumber(packet_number));
+ bytes_in_flight_ -= size;
+ SendTimeState send_time_state =
+ sampler_.OnPacketLost(QuicPacketNumber(packet_number));
+ EXPECT_TRUE(send_time_state.is_valid);
+ return send_time_state;
+ }
+
+ // Sends one packet and acks it. Then, send 20 packets. Finally, send
+ // another 20 packets while acknowledging previous 20.
+ void Send40PacketsAndAckFirst20(QuicTime::Delta time_between_packets) {
+ // Send 20 packets at a constant inter-packet time.
+ for (int i = 1; i <= 20; i++) {
+ SendPacket(i);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ack packets 1 to 20, while sending new packets at the same rate as
+ // before.
+ for (int i = 1; i <= 20; i++) {
+ AckPacket(i);
+ SendPacket(i + 20);
+ clock_.AdvanceTime(time_between_packets);
+ }
+ }
+};
+
+// Test the sampler in a simple stop-and-wait sender setting.
+TEST_F(BandwidthSamplerTest, SendAndWait) {
+ QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10);
+ QuicBandwidth expected_bandwidth =
+ QuicBandwidth::FromBytesPerSecond(kRegularPacketSize * 100);
+
+ // Send packets at the constant bandwidth.
+ for (int i = 1; i < 20; i++) {
+ SendPacket(i);
+ clock_.AdvanceTime(time_between_packets);
+ QuicBandwidth current_sample = AckPacket(i);
+ EXPECT_EQ(expected_bandwidth, current_sample);
+ }
+
+ // Send packets at the exponentially decreasing bandwidth.
+ for (int i = 20; i < 25; i++) {
+ time_between_packets = time_between_packets * 2;
+ expected_bandwidth = expected_bandwidth * 0.5;
+
+ SendPacket(i);
+ clock_.AdvanceTime(time_between_packets);
+ QuicBandwidth current_sample = AckPacket(i);
+ EXPECT_EQ(expected_bandwidth, current_sample);
+ }
+ EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ EXPECT_EQ(0u, bytes_in_flight_);
+}
+
+TEST_F(BandwidthSamplerTest, SendTimeState) {
+ QuicTime::Delta time_between_packets = QuicTime::Delta::FromMilliseconds(10);
+
+ // Send packets 1-5.
+ for (int i = 1; i <= 5; i++) {
+ SendPacket(i);
+ EXPECT_EQ(PacketsToBytes(i), sampler_.total_bytes_sent());
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ack packet 1.
+ SendTimeState send_time_state = AckPacketInner(1).state_at_send;
+ EXPECT_EQ(PacketsToBytes(1), send_time_state.total_bytes_sent);
+ EXPECT_EQ(0u, send_time_state.total_bytes_acked);
+ EXPECT_EQ(0u, send_time_state.total_bytes_lost);
+ EXPECT_EQ(PacketsToBytes(1), sampler_.total_bytes_acked());
+
+ // Lose packet 2.
+ send_time_state = LosePacket(2);
+ EXPECT_EQ(PacketsToBytes(2), send_time_state.total_bytes_sent);
+ EXPECT_EQ(0u, send_time_state.total_bytes_acked);
+ EXPECT_EQ(0u, send_time_state.total_bytes_lost);
+ EXPECT_EQ(PacketsToBytes(1), sampler_.total_bytes_lost());
+
+ // Lose packet 3.
+ send_time_state = LosePacket(3);
+ EXPECT_EQ(PacketsToBytes(3), send_time_state.total_bytes_sent);
+ EXPECT_EQ(0u, send_time_state.total_bytes_acked);
+ EXPECT_EQ(0u, send_time_state.total_bytes_lost);
+ EXPECT_EQ(PacketsToBytes(2), sampler_.total_bytes_lost());
+
+ // Send packets 6-10.
+ for (int i = 6; i <= 10; i++) {
+ SendPacket(i);
+ EXPECT_EQ(PacketsToBytes(i), sampler_.total_bytes_sent());
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ack all inflight packets.
+ QuicPacketCount acked_packet_count = 1;
+ EXPECT_EQ(PacketsToBytes(acked_packet_count), sampler_.total_bytes_acked());
+ for (int i = 4; i <= 10; i++) {
+ send_time_state = AckPacketInner(i).state_at_send;
+ ++acked_packet_count;
+ EXPECT_EQ(PacketsToBytes(acked_packet_count), sampler_.total_bytes_acked());
+ EXPECT_EQ(PacketsToBytes(i), send_time_state.total_bytes_sent);
+ if (i <= 5) {
+ EXPECT_EQ(0u, send_time_state.total_bytes_acked);
+ EXPECT_EQ(0u, send_time_state.total_bytes_lost);
+ } else {
+ EXPECT_EQ(PacketsToBytes(1), send_time_state.total_bytes_acked);
+ EXPECT_EQ(PacketsToBytes(2), send_time_state.total_bytes_lost);
+ }
+ clock_.AdvanceTime(time_between_packets);
+ }
+}
+
+// Test the sampler during regular windowed sender scenario with fixed
+// CWND of 20.
+TEST_F(BandwidthSamplerTest, SendPaced) {
+ const QuicTime::Delta time_between_packets =
+ QuicTime::Delta::FromMilliseconds(1);
+ QuicBandwidth expected_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize);
+
+ Send40PacketsAndAckFirst20(time_between_packets);
+
+ // Ack the packets 21 to 40, arriving at the correct bandwidth.
+ QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
+ for (int i = 21; i <= 40; i++) {
+ last_bandwidth = AckPacket(i);
+ EXPECT_EQ(expected_bandwidth, last_bandwidth);
+ clock_.AdvanceTime(time_between_packets);
+ }
+ EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ EXPECT_EQ(0u, bytes_in_flight_);
+}
+
+// Test the sampler in a scenario where 50% of packets is consistently lost.
+TEST_F(BandwidthSamplerTest, SendWithLosses) {
+ const QuicTime::Delta time_between_packets =
+ QuicTime::Delta::FromMilliseconds(1);
+ QuicBandwidth expected_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize) * 0.5;
+
+ // Send 20 packets, each 1 ms apart.
+ for (int i = 1; i <= 20; i++) {
+ SendPacket(i);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ack packets 1 to 20, losing every even-numbered packet, while sending new
+ // packets at the same rate as before.
+ for (int i = 1; i <= 20; i++) {
+ if (i % 2 == 0) {
+ AckPacket(i);
+ } else {
+ LosePacket(i);
+ }
+ SendPacket(i + 20);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ack the packets 21 to 40 with the same loss pattern.
+ QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
+ for (int i = 21; i <= 40; i++) {
+ if (i % 2 == 0) {
+ last_bandwidth = AckPacket(i);
+ EXPECT_EQ(expected_bandwidth, last_bandwidth);
+ } else {
+ LosePacket(i);
+ }
+ clock_.AdvanceTime(time_between_packets);
+ }
+ EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ EXPECT_EQ(0u, bytes_in_flight_);
+}
+
+// Test the sampler in a scenario where the 50% of packets are not
+// congestion controlled (specifically, non-retransmittable data is not
+// congestion controlled). Should be functionally consistent in behavior with
+// the SendWithLosses test.
+TEST_F(BandwidthSamplerTest, NotCongestionControlled) {
+ const QuicTime::Delta time_between_packets =
+ QuicTime::Delta::FromMilliseconds(1);
+ QuicBandwidth expected_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize) * 0.5;
+
+ // Send 20 packets, each 1 ms apart. Every even packet is not congestion
+ // controlled.
+ for (int i = 1; i <= 20; i++) {
+ SendPacketInner(
+ i, kRegularPacketSize,
+ i % 2 == 0 ? HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ensure only congestion controlled packets are tracked.
+ EXPECT_EQ(10u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+
+ // Ack packets 2 to 21, ignoring every even-numbered packet, while sending new
+ // packets at the same rate as before.
+ for (int i = 1; i <= 20; i++) {
+ if (i % 2 == 0) {
+ AckPacket(i);
+ }
+ SendPacketInner(
+ i + 20, kRegularPacketSize,
+ i % 2 == 0 ? HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ack the packets 22 to 41 with the same congestion controlled pattern.
+ QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
+ for (int i = 21; i <= 40; i++) {
+ if (i % 2 == 0) {
+ last_bandwidth = AckPacket(i);
+ EXPECT_EQ(expected_bandwidth, last_bandwidth);
+ }
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Since only congestion controlled packets are entered into the map, it has
+ // to be empty at this point.
+ EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ EXPECT_EQ(0u, bytes_in_flight_);
+}
+
+// Simulate a situation where ACKs arrive in burst and earlier than usual, thus
+// producing an ACK rate which is higher than the original send rate.
+TEST_F(BandwidthSamplerTest, CompressedAck) {
+ const QuicTime::Delta time_between_packets =
+ QuicTime::Delta::FromMilliseconds(1);
+ QuicBandwidth expected_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize);
+
+ Send40PacketsAndAckFirst20(time_between_packets);
+
+ // Simulate an RTT somewhat lower than the one for 1-to-21 transmission.
+ clock_.AdvanceTime(time_between_packets * 15);
+
+ // Ack the packets 21 to 40 almost immediately at once.
+ QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
+ QuicTime::Delta ridiculously_small_time_delta =
+ QuicTime::Delta::FromMicroseconds(20);
+ for (int i = 21; i <= 40; i++) {
+ last_bandwidth = AckPacket(i);
+ clock_.AdvanceTime(ridiculously_small_time_delta);
+ }
+ EXPECT_EQ(expected_bandwidth, last_bandwidth);
+ EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ EXPECT_EQ(0u, bytes_in_flight_);
+}
+
+// Tests receiving ACK packets in the reverse order.
+TEST_F(BandwidthSamplerTest, ReorderedAck) {
+ const QuicTime::Delta time_between_packets =
+ QuicTime::Delta::FromMilliseconds(1);
+ QuicBandwidth expected_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize);
+
+ Send40PacketsAndAckFirst20(time_between_packets);
+
+ // Ack the packets 21 to 40 in the reverse order, while sending packets 41 to
+ // 60.
+ QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
+ for (int i = 0; i < 20; i++) {
+ last_bandwidth = AckPacket(40 - i);
+ EXPECT_EQ(expected_bandwidth, last_bandwidth);
+ SendPacket(41 + i);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ack the packets 41 to 60, now in the regular order.
+ for (int i = 41; i <= 60; i++) {
+ last_bandwidth = AckPacket(i);
+ EXPECT_EQ(expected_bandwidth, last_bandwidth);
+ clock_.AdvanceTime(time_between_packets);
+ }
+ EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ EXPECT_EQ(0u, bytes_in_flight_);
+}
+
+// Test the app-limited logic.
+TEST_F(BandwidthSamplerTest, AppLimited) {
+ const QuicTime::Delta time_between_packets =
+ QuicTime::Delta::FromMilliseconds(1);
+ QuicBandwidth expected_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(kRegularPacketSize);
+
+ Send40PacketsAndAckFirst20(time_between_packets);
+
+ // We are now app-limited. Ack 21 to 40 as usual, but do not send anything for
+ // now.
+ sampler_.OnAppLimited();
+ for (int i = 21; i <= 40; i++) {
+ QuicBandwidth current_sample = AckPacket(i);
+ EXPECT_EQ(expected_bandwidth, current_sample);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Enter quiescence.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+
+ // Send packets 41 to 60, all of which would be marked as app-limited.
+ for (int i = 41; i <= 60; i++) {
+ SendPacket(i);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Ack packets 41 to 60, while sending packets 61 to 80. 41 to 60 should be
+ // app-limited and underestimate the bandwidth due to that.
+ for (int i = 41; i <= 60; i++) {
+ BandwidthSample sample = AckPacketInner(i);
+ EXPECT_TRUE(sample.state_at_send.is_app_limited);
+ EXPECT_LT(sample.bandwidth, 0.7f * expected_bandwidth);
+
+ SendPacket(i + 20);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // Run out of packets, and then ack packet 61 to 80, all of which should have
+ // correct non-app-limited samples.
+ for (int i = 61; i <= 80; i++) {
+ QuicBandwidth last_bandwidth = AckPacket(i);
+ EXPECT_EQ(expected_bandwidth, last_bandwidth);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ EXPECT_EQ(0u, bytes_in_flight_);
+}
+
+// Test the samples taken at the first flight of packets sent.
+TEST_F(BandwidthSamplerTest, FirstRoundTrip) {
+ const QuicTime::Delta time_between_packets =
+ QuicTime::Delta::FromMilliseconds(1);
+ const QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(800);
+ const int num_packets = 10;
+ const QuicByteCount num_bytes = kRegularPacketSize * num_packets;
+ const QuicBandwidth real_bandwidth =
+ QuicBandwidth::FromBytesAndTimeDelta(num_bytes, rtt);
+
+ for (int i = 1; i <= 10; i++) {
+ SendPacket(i);
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ clock_.AdvanceTime(rtt - num_packets * time_between_packets);
+
+ QuicBandwidth last_sample = QuicBandwidth::Zero();
+ for (int i = 1; i <= 10; i++) {
+ QuicBandwidth sample = AckPacket(i);
+ EXPECT_GT(sample, last_sample);
+ last_sample = sample;
+ clock_.AdvanceTime(time_between_packets);
+ }
+
+ // The final measured sample for the first flight of sample is expected to be
+ // smaller than the real bandwidth, yet it should not lose more than 10%. The
+ // specific value of the error depends on the difference between the RTT and
+ // the time it takes to exhaust the congestion window (i.e. in the limit when
+ // all packets are sent simultaneously, last sample would indicate the real
+ // bandwidth).
+ EXPECT_LT(last_sample, real_bandwidth);
+ EXPECT_GT(last_sample, 0.9f * real_bandwidth);
+}
+
+// Test sampler's ability to remove obsolete packets.
+TEST_F(BandwidthSamplerTest, RemoveObsoletePackets) {
+ SendPacket(1);
+ SendPacket(2);
+ SendPacket(3);
+ SendPacket(4);
+ SendPacket(5);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+
+ EXPECT_EQ(5u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ sampler_.RemoveObsoletePackets(QuicPacketNumber(4));
+ EXPECT_EQ(2u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ sampler_.OnPacketLost(QuicPacketNumber(4));
+ EXPECT_EQ(1u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+ AckPacket(5);
+ EXPECT_EQ(0u, BandwidthSamplerPeer::GetNumberOfTrackedPackets(sampler_));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc
new file mode 100644
index 00000000000..1c0d274fe1d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc
@@ -0,0 +1,976 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+// Constants based on TCP defaults.
+// The minimum CWND to ensure delayed acks don't reduce bandwidth measurements.
+// Does not inflate the pacing rate.
+const QuicByteCount kDefaultMinimumCongestionWindow = 4 * kMaxSegmentSize;
+
+// The gain used for the STARTUP, equal to 2/ln(2).
+const float kDefaultHighGain = 2.885f;
+// The newly derived gain for STARTUP, equal to 4 * ln(2)
+const float kDerivedHighGain = 2.773f;
+// The newly derived CWND gain for STARTUP, 2.
+const float kDerivedHighCWNDGain = 2.773f;
+// The gain used in STARTUP after loss has been detected.
+// 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth
+// in measured bandwidth.
+const float kStartupAfterLossGain = 1.5f;
+// The cycle of gains used during the PROBE_BW stage.
+const float kPacingGain[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
+
+// The length of the gain cycle.
+const size_t kGainCycleLength = sizeof(kPacingGain) / sizeof(kPacingGain[0]);
+// The size of the bandwidth filter window, in round-trips.
+const QuicRoundTripCount kBandwidthWindowSize = kGainCycleLength + 2;
+
+// The time after which the current min_rtt value expires.
+const QuicTime::Delta kMinRttExpiry = QuicTime::Delta::FromSeconds(10);
+// The minimum time the connection can spend in PROBE_RTT mode.
+const QuicTime::Delta kProbeRttTime = QuicTime::Delta::FromMilliseconds(200);
+// If the bandwidth does not increase by the factor of |kStartupGrowthTarget|
+// within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection
+// will exit the STARTUP mode.
+const float kStartupGrowthTarget = 1.25;
+const QuicRoundTripCount kRoundTripsWithoutGrowthBeforeExitingStartup = 3;
+// Coefficient of target congestion window to use when basing PROBE_RTT on BDP.
+const float kModerateProbeRttMultiplier = 0.75;
+// Coefficient to determine if a new RTT is sufficiently similar to min_rtt that
+// we don't need to enter PROBE_RTT.
+const float kSimilarMinRttThreshold = 1.125;
+
+} // namespace
+
+BbrSender::DebugState::DebugState(const BbrSender& sender)
+ : mode(sender.mode_),
+ max_bandwidth(sender.max_bandwidth_.GetBest()),
+ round_trip_count(sender.round_trip_count_),
+ gain_cycle_index(sender.cycle_current_offset_),
+ congestion_window(sender.congestion_window_),
+ is_at_full_bandwidth(sender.is_at_full_bandwidth_),
+ bandwidth_at_last_round(sender.bandwidth_at_last_round_),
+ rounds_without_bandwidth_gain(sender.rounds_without_bandwidth_gain_),
+ min_rtt(sender.min_rtt_),
+ min_rtt_timestamp(sender.min_rtt_timestamp_),
+ recovery_state(sender.recovery_state_),
+ recovery_window(sender.recovery_window_),
+ last_sample_is_app_limited(sender.last_sample_is_app_limited_),
+ end_of_app_limited_phase(sender.sampler_.end_of_app_limited_phase()) {}
+
+BbrSender::DebugState::DebugState(const DebugState& state) = default;
+
+BbrSender::BbrSender(QuicTime now,
+ const RttStats* rtt_stats,
+ const QuicUnackedPacketMap* unacked_packets,
+ QuicPacketCount initial_tcp_congestion_window,
+ QuicPacketCount max_tcp_congestion_window,
+ QuicRandom* random,
+ QuicConnectionStats* stats)
+ : rtt_stats_(rtt_stats),
+ unacked_packets_(unacked_packets),
+ random_(random),
+ stats_(stats),
+ mode_(STARTUP),
+ round_trip_count_(0),
+ max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0),
+ max_ack_height_(kBandwidthWindowSize, 0, 0),
+ aggregation_epoch_start_time_(QuicTime::Zero()),
+ aggregation_epoch_bytes_(0),
+ min_rtt_(QuicTime::Delta::Zero()),
+ min_rtt_timestamp_(QuicTime::Zero()),
+ congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS),
+ initial_congestion_window_(initial_tcp_congestion_window *
+ kDefaultTCPMSS),
+ max_congestion_window_(max_tcp_congestion_window * kDefaultTCPMSS),
+ min_congestion_window_(kDefaultMinimumCongestionWindow),
+ high_gain_(kDefaultHighGain),
+ high_cwnd_gain_(kDefaultHighGain),
+ drain_gain_(1.f / kDefaultHighGain),
+ pacing_rate_(QuicBandwidth::Zero()),
+ pacing_gain_(1),
+ congestion_window_gain_(1),
+ congestion_window_gain_constant_(
+ static_cast<float>(FLAGS_quic_bbr_cwnd_gain)),
+ num_startup_rtts_(kRoundTripsWithoutGrowthBeforeExitingStartup),
+ exit_startup_on_loss_(false),
+ cycle_current_offset_(0),
+ last_cycle_start_(QuicTime::Zero()),
+ is_at_full_bandwidth_(false),
+ rounds_without_bandwidth_gain_(0),
+ bandwidth_at_last_round_(QuicBandwidth::Zero()),
+ exiting_quiescence_(false),
+ exit_probe_rtt_at_(QuicTime::Zero()),
+ probe_rtt_round_passed_(false),
+ last_sample_is_app_limited_(false),
+ has_non_app_limited_sample_(false),
+ flexible_app_limited_(false),
+ recovery_state_(NOT_IN_RECOVERY),
+ recovery_window_(max_congestion_window_),
+ is_app_limited_recovery_(false),
+ slower_startup_(false),
+ rate_based_startup_(false),
+ startup_rate_reduction_multiplier_(0),
+ startup_bytes_lost_(0),
+ enable_ack_aggregation_during_startup_(false),
+ expire_ack_aggregation_in_startup_(false),
+ drain_to_target_(false),
+ probe_rtt_based_on_bdp_(false),
+ probe_rtt_skipped_if_similar_rtt_(false),
+ probe_rtt_disabled_if_app_limited_(false),
+ app_limited_since_last_probe_rtt_(false),
+ min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()),
+ always_get_bw_sample_when_acked_(
+ GetQuicReloadableFlag(quic_always_get_bw_sample_when_acked)) {
+ if (stats_) {
+ stats_->slowstart_count = 0;
+ stats_->slowstart_start_time = QuicTime::Zero();
+ }
+ EnterStartupMode(now);
+}
+
+BbrSender::~BbrSender() {}
+
+void BbrSender::SetInitialCongestionWindowInPackets(
+ QuicPacketCount congestion_window) {
+ if (mode_ == STARTUP) {
+ initial_congestion_window_ = congestion_window * kDefaultTCPMSS;
+ congestion_window_ = congestion_window * kDefaultTCPMSS;
+ }
+}
+
+bool BbrSender::InSlowStart() const {
+ return mode_ == STARTUP;
+}
+
+void BbrSender::OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) {
+ if (stats_ && InSlowStart()) {
+ ++stats_->slowstart_packets_sent;
+ stats_->slowstart_bytes_sent += bytes;
+ }
+
+ last_sent_packet_ = packet_number;
+
+ if (bytes_in_flight == 0 && sampler_.is_app_limited()) {
+ exiting_quiescence_ = true;
+ }
+
+ if (!aggregation_epoch_start_time_.IsInitialized()) {
+ aggregation_epoch_start_time_ = sent_time;
+ }
+
+ sampler_.OnPacketSent(sent_time, packet_number, bytes, bytes_in_flight,
+ is_retransmittable);
+}
+
+bool BbrSender::CanSend(QuicByteCount bytes_in_flight) {
+ return bytes_in_flight < GetCongestionWindow();
+}
+
+QuicBandwidth BbrSender::PacingRate(QuicByteCount bytes_in_flight) const {
+ if (pacing_rate_.IsZero()) {
+ return high_gain_ * QuicBandwidth::FromBytesAndTimeDelta(
+ initial_congestion_window_, GetMinRtt());
+ }
+ return pacing_rate_;
+}
+
+QuicBandwidth BbrSender::BandwidthEstimate() const {
+ return max_bandwidth_.GetBest();
+}
+
+QuicByteCount BbrSender::GetCongestionWindow() const {
+ if (mode_ == PROBE_RTT) {
+ return ProbeRttCongestionWindow();
+ }
+
+ if (InRecovery() && !(rate_based_startup_ && mode_ == STARTUP)) {
+ return std::min(congestion_window_, recovery_window_);
+ }
+
+ return congestion_window_;
+}
+
+QuicByteCount BbrSender::GetSlowStartThreshold() const {
+ return 0;
+}
+
+bool BbrSender::InRecovery() const {
+ return recovery_state_ != NOT_IN_RECOVERY;
+}
+
+bool BbrSender::ShouldSendProbingPacket() const {
+ if (pacing_gain_ <= 1) {
+ return false;
+ }
+
+ // TODO(b/77975811): If the pipe is highly under-utilized, consider not
+ // sending a probing transmission, because the extra bandwidth is not needed.
+ // If flexible_app_limited is enabled, check if the pipe is sufficiently full.
+ if (flexible_app_limited_) {
+ return !IsPipeSufficientlyFull();
+ } else {
+ return true;
+ }
+}
+
+bool BbrSender::IsPipeSufficientlyFull() const {
+ // See if we need more bytes in flight to see more bandwidth.
+ if (mode_ == STARTUP) {
+ // STARTUP exits if it doesn't observe a 25% bandwidth increase, so the CWND
+ // must be more than 25% above the target.
+ return unacked_packets_->bytes_in_flight() >=
+ GetTargetCongestionWindow(1.5);
+ }
+ if (pacing_gain_ > 1) {
+ // Super-unity PROBE_BW doesn't exit until 1.25 * BDP is achieved.
+ return unacked_packets_->bytes_in_flight() >=
+ GetTargetCongestionWindow(pacing_gain_);
+ }
+ // If bytes_in_flight are above the target congestion window, it should be
+ // possible to observe the same or more bandwidth if it's available.
+ return unacked_packets_->bytes_in_flight() >= GetTargetCongestionWindow(1.1);
+}
+
+void BbrSender::SetFromConfig(const QuicConfig& config,
+ Perspective perspective) {
+ if (config.HasClientRequestedIndependentOption(kLRTT, perspective)) {
+ exit_startup_on_loss_ = true;
+ }
+ if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) {
+ num_startup_rtts_ = 1;
+ }
+ if (config.HasClientRequestedIndependentOption(k2RTT, perspective)) {
+ num_startup_rtts_ = 2;
+ }
+ if (config.HasClientRequestedIndependentOption(kBBRS, perspective)) {
+ slower_startup_ = true;
+ }
+ if (config.HasClientRequestedIndependentOption(kBBR3, perspective)) {
+ drain_to_target_ = true;
+ }
+ if (config.HasClientRequestedIndependentOption(kBBS1, perspective)) {
+ rate_based_startup_ = true;
+ }
+ if (GetQuicReloadableFlag(quic_bbr_startup_rate_reduction) &&
+ config.HasClientRequestedIndependentOption(kBBS4, perspective)) {
+ rate_based_startup_ = true;
+ // Hits 1.25x pacing multiplier when ~2/3 CWND is lost.
+ startup_rate_reduction_multiplier_ = 1;
+ }
+ if (GetQuicReloadableFlag(quic_bbr_startup_rate_reduction) &&
+ config.HasClientRequestedIndependentOption(kBBS5, perspective)) {
+ rate_based_startup_ = true;
+ // Hits 1.25x pacing multiplier when ~1/3 CWND is lost.
+ startup_rate_reduction_multiplier_ = 2;
+ }
+ if (config.HasClientRequestedIndependentOption(kBBR4, perspective)) {
+ max_ack_height_.SetWindowLength(2 * kBandwidthWindowSize);
+ }
+ if (config.HasClientRequestedIndependentOption(kBBR5, perspective)) {
+ max_ack_height_.SetWindowLength(4 * kBandwidthWindowSize);
+ }
+ if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) &&
+ config.HasClientRequestedIndependentOption(kBBR6, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 1, 3);
+ probe_rtt_based_on_bdp_ = true;
+ }
+ if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) &&
+ config.HasClientRequestedIndependentOption(kBBR7, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 2, 3);
+ probe_rtt_skipped_if_similar_rtt_ = true;
+ }
+ if (GetQuicReloadableFlag(quic_bbr_less_probe_rtt) &&
+ config.HasClientRequestedIndependentOption(kBBR8, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_less_probe_rtt, 3, 3);
+ probe_rtt_disabled_if_app_limited_ = true;
+ }
+ if (GetQuicReloadableFlag(quic_bbr_flexible_app_limited) &&
+ config.HasClientRequestedIndependentOption(kBBR9, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_flexible_app_limited);
+ flexible_app_limited_ = true;
+ }
+ if (GetQuicReloadableFlag(quic_bbr_slower_startup3) &&
+ config.HasClientRequestedIndependentOption(kBBQ1, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 1, 4);
+ set_high_gain(kDerivedHighGain);
+ set_high_cwnd_gain(kDerivedHighGain);
+ set_drain_gain(1.f / kDerivedHighGain);
+ }
+ if (GetQuicReloadableFlag(quic_bbr_slower_startup3) &&
+ config.HasClientRequestedIndependentOption(kBBQ2, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 2, 4);
+ set_high_cwnd_gain(kDerivedHighCWNDGain);
+ }
+ if (GetQuicReloadableFlag(quic_bbr_slower_startup3) &&
+ config.HasClientRequestedIndependentOption(kBBQ3, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 3, 4);
+ enable_ack_aggregation_during_startup_ = true;
+ }
+ if (GetQuicReloadableFlag(quic_bbr_slower_startup3) &&
+ config.HasClientRequestedIndependentOption(kBBQ4, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_slower_startup3, 4, 4);
+ set_drain_gain(kModerateProbeRttMultiplier);
+ }
+ if (GetQuicReloadableFlag(quic_bbr_slower_startup4) &&
+ config.HasClientRequestedIndependentOption(kBBQ5, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_slower_startup4);
+ expire_ack_aggregation_in_startup_ = true;
+ }
+ if (config.HasClientRequestedIndependentOption(kMIN1, perspective)) {
+ min_congestion_window_ = kMaxSegmentSize;
+ }
+}
+
+void BbrSender::AdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) {
+ if (!bandwidth.IsZero()) {
+ max_bandwidth_.Update(bandwidth, round_trip_count_);
+ }
+ if (!rtt.IsZero() && (min_rtt_ > rtt || min_rtt_.IsZero())) {
+ min_rtt_ = rtt;
+ }
+}
+
+void BbrSender::OnCongestionEvent(bool /*rtt_updated*/,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets) {
+ const QuicByteCount total_bytes_acked_before = sampler_.total_bytes_acked();
+
+ bool is_round_start = false;
+ bool min_rtt_expired = false;
+
+ DiscardLostPackets(lost_packets);
+
+ // Input the new data into the BBR model of the connection.
+ QuicByteCount excess_acked = 0;
+ if (!acked_packets.empty()) {
+ QuicPacketNumber last_acked_packet = acked_packets.rbegin()->packet_number;
+ is_round_start = UpdateRoundTripCounter(last_acked_packet);
+ min_rtt_expired = UpdateBandwidthAndMinRtt(event_time, acked_packets);
+ UpdateRecoveryState(last_acked_packet, !lost_packets.empty(),
+ is_round_start);
+
+ const QuicByteCount bytes_acked =
+ sampler_.total_bytes_acked() - total_bytes_acked_before;
+
+ excess_acked = UpdateAckAggregationBytes(event_time, bytes_acked);
+ }
+
+ // Handle logic specific to PROBE_BW mode.
+ if (mode_ == PROBE_BW) {
+ UpdateGainCyclePhase(event_time, prior_in_flight, !lost_packets.empty());
+ }
+
+ // Handle logic specific to STARTUP and DRAIN modes.
+ if (is_round_start && !is_at_full_bandwidth_) {
+ CheckIfFullBandwidthReached();
+ }
+ MaybeExitStartupOrDrain(event_time);
+
+ // Handle logic specific to PROBE_RTT.
+ MaybeEnterOrExitProbeRtt(event_time, is_round_start, min_rtt_expired);
+
+ // Calculate number of packets acked and lost.
+ QuicByteCount bytes_acked =
+ sampler_.total_bytes_acked() - total_bytes_acked_before;
+ QuicByteCount bytes_lost = 0;
+ for (const auto& packet : lost_packets) {
+ bytes_lost += packet.bytes_lost;
+ }
+
+ // After the model is updated, recalculate the pacing rate and congestion
+ // window.
+ CalculatePacingRate();
+ CalculateCongestionWindow(bytes_acked, excess_acked);
+ CalculateRecoveryWindow(bytes_acked, bytes_lost);
+
+ // Cleanup internal state.
+ sampler_.RemoveObsoletePackets(unacked_packets_->GetLeastUnacked());
+}
+
+CongestionControlType BbrSender::GetCongestionControlType() const {
+ return kBBR;
+}
+
+QuicTime::Delta BbrSender::GetMinRtt() const {
+ return !min_rtt_.IsZero() ? min_rtt_ : rtt_stats_->initial_rtt();
+}
+
+QuicByteCount BbrSender::GetTargetCongestionWindow(float gain) const {
+ QuicByteCount bdp = GetMinRtt() * BandwidthEstimate();
+ QuicByteCount congestion_window = gain * bdp;
+
+ // BDP estimate will be zero if no bandwidth samples are available yet.
+ if (congestion_window == 0) {
+ congestion_window = gain * initial_congestion_window_;
+ }
+
+ return std::max(congestion_window, min_congestion_window_);
+}
+
+QuicByteCount BbrSender::ProbeRttCongestionWindow() const {
+ if (probe_rtt_based_on_bdp_) {
+ return GetTargetCongestionWindow(kModerateProbeRttMultiplier);
+ }
+ return min_congestion_window_;
+}
+
+void BbrSender::EnterStartupMode(QuicTime now) {
+ if (stats_) {
+ ++stats_->slowstart_count;
+ DCHECK_EQ(stats_->slowstart_start_time, QuicTime::Zero()) << mode_;
+ stats_->slowstart_start_time = now;
+ }
+ mode_ = STARTUP;
+ pacing_gain_ = high_gain_;
+ congestion_window_gain_ = high_cwnd_gain_;
+}
+
+void BbrSender::EnterProbeBandwidthMode(QuicTime now) {
+ mode_ = PROBE_BW;
+ congestion_window_gain_ = congestion_window_gain_constant_;
+
+ // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
+ // excluded because in that case increased gain and decreased gain would not
+ // follow each other.
+ cycle_current_offset_ = random_->RandUint64() % (kGainCycleLength - 1);
+ if (cycle_current_offset_ >= 1) {
+ cycle_current_offset_ += 1;
+ }
+
+ last_cycle_start_ = now;
+ pacing_gain_ = kPacingGain[cycle_current_offset_];
+}
+
+void BbrSender::DiscardLostPackets(const LostPacketVector& lost_packets) {
+ for (const LostPacket& packet : lost_packets) {
+ sampler_.OnPacketLost(packet.packet_number);
+ if (mode_ == STARTUP) {
+ if (stats_) {
+ ++stats_->slowstart_packets_lost;
+ stats_->slowstart_bytes_lost += packet.bytes_lost;
+ }
+ if (startup_rate_reduction_multiplier_ != 0) {
+ startup_bytes_lost_ += packet.bytes_lost;
+ }
+ }
+ }
+}
+
+bool BbrSender::UpdateRoundTripCounter(QuicPacketNumber last_acked_packet) {
+ if (!current_round_trip_end_.IsInitialized() ||
+ last_acked_packet > current_round_trip_end_) {
+ round_trip_count_++;
+ current_round_trip_end_ = last_sent_packet_;
+ if (stats_ && InSlowStart()) {
+ ++stats_->slowstart_num_rtts;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool BbrSender::UpdateBandwidthAndMinRtt(
+ QuicTime now,
+ const AckedPacketVector& acked_packets) {
+ QuicTime::Delta sample_min_rtt = QuicTime::Delta::Infinite();
+ for (const auto& packet : acked_packets) {
+ if (!always_get_bw_sample_when_acked_ && packet.bytes_acked == 0) {
+ // Skip acked packets with 0 in flight bytes when updating bandwidth.
+ continue;
+ }
+ BandwidthSample bandwidth_sample =
+ sampler_.OnPacketAcknowledged(now, packet.packet_number);
+ if (always_get_bw_sample_when_acked_ &&
+ !bandwidth_sample.state_at_send.is_valid) {
+ // From the sampler's perspective, the packet has never been sent, or the
+ // packet has been acked or marked as lost previously.
+ continue;
+ }
+
+ last_sample_is_app_limited_ = bandwidth_sample.state_at_send.is_app_limited;
+ has_non_app_limited_sample_ |=
+ !bandwidth_sample.state_at_send.is_app_limited;
+ if (!bandwidth_sample.rtt.IsZero()) {
+ sample_min_rtt = std::min(sample_min_rtt, bandwidth_sample.rtt);
+ }
+
+ if (!bandwidth_sample.state_at_send.is_app_limited ||
+ bandwidth_sample.bandwidth > BandwidthEstimate()) {
+ max_bandwidth_.Update(bandwidth_sample.bandwidth, round_trip_count_);
+ }
+ }
+
+ // If none of the RTT samples are valid, return immediately.
+ if (sample_min_rtt.IsInfinite()) {
+ return false;
+ }
+ min_rtt_since_last_probe_rtt_ =
+ std::min(min_rtt_since_last_probe_rtt_, sample_min_rtt);
+
+ // Do not expire min_rtt if none was ever available.
+ bool min_rtt_expired =
+ !min_rtt_.IsZero() && (now > (min_rtt_timestamp_ + kMinRttExpiry));
+
+ if (min_rtt_expired || sample_min_rtt < min_rtt_ || min_rtt_.IsZero()) {
+ QUIC_DVLOG(2) << "Min RTT updated, old value: " << min_rtt_
+ << ", new value: " << sample_min_rtt
+ << ", current time: " << now.ToDebuggingValue();
+
+ if (min_rtt_expired && ShouldExtendMinRttExpiry()) {
+ min_rtt_expired = false;
+ } else {
+ min_rtt_ = sample_min_rtt;
+ }
+ min_rtt_timestamp_ = now;
+ // Reset since_last_probe_rtt fields.
+ min_rtt_since_last_probe_rtt_ = QuicTime::Delta::Infinite();
+ app_limited_since_last_probe_rtt_ = false;
+ }
+ DCHECK(!min_rtt_.IsZero());
+
+ return min_rtt_expired;
+}
+
+bool BbrSender::ShouldExtendMinRttExpiry() const {
+ if (probe_rtt_disabled_if_app_limited_ && app_limited_since_last_probe_rtt_) {
+ // Extend the current min_rtt if we've been app limited recently.
+ return true;
+ }
+ const bool min_rtt_increased_since_last_probe =
+ min_rtt_since_last_probe_rtt_ > min_rtt_ * kSimilarMinRttThreshold;
+ if (probe_rtt_skipped_if_similar_rtt_ && app_limited_since_last_probe_rtt_ &&
+ !min_rtt_increased_since_last_probe) {
+ // Extend the current min_rtt if we've been app limited recently and an rtt
+ // has been measured in that time that's less than 12.5% more than the
+ // current min_rtt.
+ return true;
+ }
+ return false;
+}
+
+void BbrSender::UpdateGainCyclePhase(QuicTime now,
+ QuicByteCount prior_in_flight,
+ bool has_losses) {
+ const QuicByteCount bytes_in_flight = unacked_packets_->bytes_in_flight();
+ // In most cases, the cycle is advanced after an RTT passes.
+ bool should_advance_gain_cycling = now - last_cycle_start_ > GetMinRtt();
+
+ // If the pacing gain is above 1.0, the connection is trying to probe the
+ // bandwidth by increasing the number of bytes in flight to at least
+ // pacing_gain * BDP. Make sure that it actually reaches the target, as long
+ // as there are no losses suggesting that the buffers are not able to hold
+ // that much.
+ if (pacing_gain_ > 1.0 && !has_losses &&
+ prior_in_flight < GetTargetCongestionWindow(pacing_gain_)) {
+ should_advance_gain_cycling = false;
+ }
+
+ // If pacing gain is below 1.0, the connection is trying to drain the extra
+ // queue which could have been incurred by probing prior to it. If the number
+ // of bytes in flight falls down to the estimated BDP value earlier, conclude
+ // that the queue has been successfully drained and exit this cycle early.
+ if (pacing_gain_ < 1.0 && bytes_in_flight <= GetTargetCongestionWindow(1)) {
+ should_advance_gain_cycling = true;
+ }
+
+ if (should_advance_gain_cycling) {
+ cycle_current_offset_ = (cycle_current_offset_ + 1) % kGainCycleLength;
+ last_cycle_start_ = now;
+ // Stay in low gain mode until the target BDP is hit.
+ // Low gain mode will be exited immediately when the target BDP is achieved.
+ if (drain_to_target_ && pacing_gain_ < 1 &&
+ kPacingGain[cycle_current_offset_] == 1 &&
+ bytes_in_flight > GetTargetCongestionWindow(1)) {
+ return;
+ }
+ pacing_gain_ = kPacingGain[cycle_current_offset_];
+ }
+}
+
+void BbrSender::CheckIfFullBandwidthReached() {
+ if (last_sample_is_app_limited_) {
+ return;
+ }
+
+ QuicBandwidth target = bandwidth_at_last_round_ * kStartupGrowthTarget;
+ if (BandwidthEstimate() >= target) {
+ bandwidth_at_last_round_ = BandwidthEstimate();
+ rounds_without_bandwidth_gain_ = 0;
+ if (expire_ack_aggregation_in_startup_) {
+ // Expire old excess delivery measurements now that bandwidth increased.
+ max_ack_height_.Reset(0, round_trip_count_);
+ }
+ return;
+ }
+
+ rounds_without_bandwidth_gain_++;
+ if ((rounds_without_bandwidth_gain_ >= num_startup_rtts_) ||
+ (exit_startup_on_loss_ && InRecovery())) {
+ DCHECK(has_non_app_limited_sample_);
+ is_at_full_bandwidth_ = true;
+ }
+}
+
+void BbrSender::MaybeExitStartupOrDrain(QuicTime now) {
+ if (mode_ == STARTUP && is_at_full_bandwidth_) {
+ OnExitStartup(now);
+ mode_ = DRAIN;
+ pacing_gain_ = drain_gain_;
+ congestion_window_gain_ = high_cwnd_gain_;
+ }
+ if (mode_ == DRAIN &&
+ unacked_packets_->bytes_in_flight() <= GetTargetCongestionWindow(1)) {
+ EnterProbeBandwidthMode(now);
+ }
+}
+
+void BbrSender::OnExitStartup(QuicTime now) {
+ DCHECK_EQ(mode_, STARTUP);
+ if (stats_) {
+ DCHECK_NE(stats_->slowstart_start_time, QuicTime::Zero());
+ if (now > stats_->slowstart_start_time) {
+ stats_->slowstart_duration =
+ now - stats_->slowstart_start_time + stats_->slowstart_duration;
+ }
+ stats_->slowstart_start_time = QuicTime::Zero();
+ }
+}
+
+void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now,
+ bool is_round_start,
+ bool min_rtt_expired) {
+ if (min_rtt_expired && !exiting_quiescence_ && mode_ != PROBE_RTT) {
+ if (InSlowStart()) {
+ OnExitStartup(now);
+ }
+ mode_ = PROBE_RTT;
+ pacing_gain_ = 1;
+ // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight|
+ // is at the target small value.
+ exit_probe_rtt_at_ = QuicTime::Zero();
+ }
+
+ if (mode_ == PROBE_RTT) {
+ sampler_.OnAppLimited();
+
+ if (exit_probe_rtt_at_ == QuicTime::Zero()) {
+ // If the window has reached the appropriate size, schedule exiting
+ // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but
+ // we allow an extra packet since QUIC checks CWND before sending a
+ // packet.
+ if (unacked_packets_->bytes_in_flight() <
+ ProbeRttCongestionWindow() + kMaxOutgoingPacketSize) {
+ exit_probe_rtt_at_ = now + kProbeRttTime;
+ probe_rtt_round_passed_ = false;
+ }
+ } else {
+ if (is_round_start) {
+ probe_rtt_round_passed_ = true;
+ }
+ if (now >= exit_probe_rtt_at_ && probe_rtt_round_passed_) {
+ min_rtt_timestamp_ = now;
+ if (!is_at_full_bandwidth_) {
+ EnterStartupMode(now);
+ } else {
+ EnterProbeBandwidthMode(now);
+ }
+ }
+ }
+ }
+
+ exiting_quiescence_ = false;
+}
+
+void BbrSender::UpdateRecoveryState(QuicPacketNumber last_acked_packet,
+ bool has_losses,
+ bool is_round_start) {
+ // Exit recovery when there are no losses for a round.
+ if (has_losses) {
+ end_recovery_at_ = last_sent_packet_;
+ }
+
+ switch (recovery_state_) {
+ case NOT_IN_RECOVERY:
+ // Enter conservation on the first loss.
+ if (has_losses) {
+ recovery_state_ = CONSERVATION;
+ // This will cause the |recovery_window_| to be set to the correct
+ // value in CalculateRecoveryWindow().
+ recovery_window_ = 0;
+ // Since the conservation phase is meant to be lasting for a whole
+ // round, extend the current round as if it were started right now.
+ current_round_trip_end_ = last_sent_packet_;
+ if (GetQuicReloadableFlag(quic_bbr_app_limited_recovery) &&
+ last_sample_is_app_limited_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_bbr_app_limited_recovery);
+ is_app_limited_recovery_ = true;
+ }
+ }
+ break;
+
+ case CONSERVATION:
+ if (is_round_start) {
+ recovery_state_ = GROWTH;
+ }
+ QUIC_FALLTHROUGH_INTENDED;
+
+ case GROWTH:
+ // Exit recovery if appropriate.
+ if (!has_losses && last_acked_packet > end_recovery_at_) {
+ recovery_state_ = NOT_IN_RECOVERY;
+ is_app_limited_recovery_ = false;
+ }
+
+ break;
+ }
+ if (recovery_state_ != NOT_IN_RECOVERY && is_app_limited_recovery_) {
+ sampler_.OnAppLimited();
+ }
+}
+
+// TODO(ianswett): Move this logic into BandwidthSampler.
+QuicByteCount BbrSender::UpdateAckAggregationBytes(
+ QuicTime ack_time,
+ QuicByteCount newly_acked_bytes) {
+ // Compute how many bytes are expected to be delivered, assuming max bandwidth
+ // is correct.
+ QuicByteCount expected_bytes_acked =
+ max_bandwidth_.GetBest() * (ack_time - aggregation_epoch_start_time_);
+ // Reset the current aggregation epoch as soon as the ack arrival rate is less
+ // than or equal to the max bandwidth.
+ if (aggregation_epoch_bytes_ <= expected_bytes_acked) {
+ // Reset to start measuring a new aggregation epoch.
+ aggregation_epoch_bytes_ = newly_acked_bytes;
+ aggregation_epoch_start_time_ = ack_time;
+ return 0;
+ }
+
+ // Compute how many extra bytes were delivered vs max bandwidth.
+ // Include the bytes most recently acknowledged to account for stretch acks.
+ aggregation_epoch_bytes_ += newly_acked_bytes;
+ max_ack_height_.Update(aggregation_epoch_bytes_ - expected_bytes_acked,
+ round_trip_count_);
+ return aggregation_epoch_bytes_ - expected_bytes_acked;
+}
+
+void BbrSender::CalculatePacingRate() {
+ if (BandwidthEstimate().IsZero()) {
+ return;
+ }
+
+ QuicBandwidth target_rate = pacing_gain_ * BandwidthEstimate();
+ if (is_at_full_bandwidth_) {
+ pacing_rate_ = target_rate;
+ return;
+ }
+
+ // Pace at the rate of initial_window / RTT as soon as RTT measurements are
+ // available.
+ if (pacing_rate_.IsZero() && !rtt_stats_->min_rtt().IsZero()) {
+ pacing_rate_ = QuicBandwidth::FromBytesAndTimeDelta(
+ initial_congestion_window_, rtt_stats_->min_rtt());
+ return;
+ }
+ // Slow the pacing rate in STARTUP once loss has ever been detected.
+ const bool has_ever_detected_loss = end_recovery_at_.IsInitialized();
+ if (slower_startup_ && has_ever_detected_loss &&
+ has_non_app_limited_sample_) {
+ pacing_rate_ = kStartupAfterLossGain * BandwidthEstimate();
+ return;
+ }
+
+ // Slow the pacing rate in STARTUP by the bytes_lost / CWND.
+ if (startup_rate_reduction_multiplier_ != 0 && has_ever_detected_loss &&
+ has_non_app_limited_sample_) {
+ pacing_rate_ =
+ (1 - (startup_bytes_lost_ * startup_rate_reduction_multiplier_ * 1.0f /
+ congestion_window_)) *
+ target_rate;
+ // Ensure the pacing rate doesn't drop below the startup growth target times
+ // the bandwidth estimate.
+ pacing_rate_ =
+ std::max(pacing_rate_, kStartupGrowthTarget * BandwidthEstimate());
+ return;
+ }
+
+ // Do not decrease the pacing rate during startup.
+ pacing_rate_ = std::max(pacing_rate_, target_rate);
+}
+
+void BbrSender::CalculateCongestionWindow(QuicByteCount bytes_acked,
+ QuicByteCount excess_acked) {
+ if (mode_ == PROBE_RTT) {
+ return;
+ }
+
+ QuicByteCount target_window =
+ GetTargetCongestionWindow(congestion_window_gain_);
+ if (is_at_full_bandwidth_) {
+ // Add the max recently measured ack aggregation to CWND.
+ target_window += max_ack_height_.GetBest();
+ } else if (enable_ack_aggregation_during_startup_) {
+ // Add the most recent excess acked. Because CWND never decreases in
+ // STARTUP, this will automatically create a very localized max filter.
+ target_window += excess_acked;
+ }
+
+ // Instead of immediately setting the target CWND as the new one, BBR grows
+ // the CWND towards |target_window| by only increasing it |bytes_acked| at a
+ // time.
+ const bool add_bytes_acked =
+ !GetQuicReloadableFlag(quic_bbr_no_bytes_acked_in_startup_recovery) ||
+ !InRecovery();
+ if (is_at_full_bandwidth_) {
+ congestion_window_ =
+ std::min(target_window, congestion_window_ + bytes_acked);
+ } else if (add_bytes_acked &&
+ (congestion_window_ < target_window ||
+ sampler_.total_bytes_acked() < initial_congestion_window_)) {
+ // If the connection is not yet out of startup phase, do not decrease the
+ // window.
+ congestion_window_ = congestion_window_ + bytes_acked;
+ }
+
+ // Enforce the limits on the congestion window.
+ congestion_window_ = std::max(congestion_window_, min_congestion_window_);
+ congestion_window_ = std::min(congestion_window_, max_congestion_window_);
+}
+
+void BbrSender::CalculateRecoveryWindow(QuicByteCount bytes_acked,
+ QuicByteCount bytes_lost) {
+ if (rate_based_startup_ && mode_ == STARTUP) {
+ return;
+ }
+
+ if (recovery_state_ == NOT_IN_RECOVERY) {
+ return;
+ }
+
+ // Set up the initial recovery window.
+ if (recovery_window_ == 0) {
+ recovery_window_ = unacked_packets_->bytes_in_flight() + bytes_acked;
+ recovery_window_ = std::max(min_congestion_window_, recovery_window_);
+ return;
+ }
+
+ // Remove losses from the recovery window, while accounting for a potential
+ // integer underflow.
+ recovery_window_ = recovery_window_ >= bytes_lost
+ ? recovery_window_ - bytes_lost
+ : kMaxSegmentSize;
+
+ // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH,
+ // release additional |bytes_acked| to achieve a slow-start-like behavior.
+ if (recovery_state_ == GROWTH) {
+ recovery_window_ += bytes_acked;
+ }
+
+ // Sanity checks. Ensure that we always allow to send at least an MSS or
+ // |bytes_acked| in response, whichever is larger.
+ recovery_window_ = std::max(
+ recovery_window_, unacked_packets_->bytes_in_flight() + bytes_acked);
+ if (GetQuicReloadableFlag(quic_bbr_one_mss_conservation)) {
+ recovery_window_ =
+ std::max(recovery_window_,
+ unacked_packets_->bytes_in_flight() + kMaxSegmentSize);
+ }
+ recovery_window_ = std::max(min_congestion_window_, recovery_window_);
+}
+
+std::string BbrSender::GetDebugState() const {
+ std::ostringstream stream;
+ stream << ExportDebugState();
+ return stream.str();
+}
+
+void BbrSender::OnApplicationLimited(QuicByteCount bytes_in_flight) {
+ if (bytes_in_flight >= GetCongestionWindow()) {
+ return;
+ }
+ if (flexible_app_limited_ && IsPipeSufficientlyFull()) {
+ return;
+ }
+
+ app_limited_since_last_probe_rtt_ = true;
+ sampler_.OnAppLimited();
+ QUIC_DVLOG(2) << "Becoming application limited. Last sent packet: "
+ << last_sent_packet_ << ", CWND: " << GetCongestionWindow();
+}
+
+BbrSender::DebugState BbrSender::ExportDebugState() const {
+ return DebugState(*this);
+}
+
+static std::string ModeToString(BbrSender::Mode mode) {
+ switch (mode) {
+ case BbrSender::STARTUP:
+ return "STARTUP";
+ case BbrSender::DRAIN:
+ return "DRAIN";
+ case BbrSender::PROBE_BW:
+ return "PROBE_BW";
+ case BbrSender::PROBE_RTT:
+ return "PROBE_RTT";
+ }
+ return "???";
+}
+
+std::ostream& operator<<(std::ostream& os, const BbrSender::Mode& mode) {
+ os << ModeToString(mode);
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const BbrSender::DebugState& state) {
+ os << "Mode: " << ModeToString(state.mode) << std::endl;
+ os << "Maximum bandwidth: " << state.max_bandwidth << std::endl;
+ os << "Round trip counter: " << state.round_trip_count << std::endl;
+ os << "Gain cycle index: " << static_cast<int>(state.gain_cycle_index)
+ << std::endl;
+ os << "Congestion window: " << state.congestion_window << " bytes"
+ << std::endl;
+
+ if (state.mode == BbrSender::STARTUP) {
+ os << "(startup) Bandwidth at last round: " << state.bandwidth_at_last_round
+ << std::endl;
+ os << "(startup) Rounds without gain: "
+ << state.rounds_without_bandwidth_gain << std::endl;
+ }
+
+ os << "Minimum RTT: " << state.min_rtt << std::endl;
+ os << "Minimum RTT timestamp: " << state.min_rtt_timestamp.ToDebuggingValue()
+ << std::endl;
+
+ os << "Last sample is app-limited: "
+ << (state.last_sample_is_app_limited ? "yes" : "no");
+
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h
new file mode 100644
index 00000000000..d01dbfe4dc2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h
@@ -0,0 +1,416 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// BBR (Bottleneck Bandwidth and RTT) congestion control algorithm.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_
+
+#include <cstdint>
+#include <ostream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/bandwidth_sampler.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class RttStats;
+
+typedef uint64_t QuicRoundTripCount;
+
+// BbrSender implements BBR congestion control algorithm. BBR aims to estimate
+// the current available Bottleneck Bandwidth and RTT (hence the name), and
+// regulates the pacing rate and the size of the congestion window based on
+// those signals.
+//
+// BBR relies on pacing in order to function properly. Do not use BBR when
+// pacing is disabled.
+//
+// TODO(vasilvv): implement traffic policer (long-term sampling) mode.
+class QUIC_EXPORT_PRIVATE BbrSender : public SendAlgorithmInterface {
+ public:
+ enum Mode {
+ // Startup phase of the connection.
+ STARTUP,
+ // After achieving the highest possible bandwidth during the startup, lower
+ // the pacing rate in order to drain the queue.
+ DRAIN,
+ // Cruising mode.
+ PROBE_BW,
+ // Temporarily slow down sending in order to empty the buffer and measure
+ // the real minimum RTT.
+ PROBE_RTT,
+ };
+
+ // Indicates how the congestion control limits the amount of bytes in flight.
+ enum RecoveryState {
+ // Do not limit.
+ NOT_IN_RECOVERY,
+ // Allow an extra outstanding byte for each byte acknowledged.
+ CONSERVATION,
+ // Allow two extra outstanding bytes for each byte acknowledged (slow
+ // start).
+ GROWTH
+ };
+
+ // Debug state can be exported in order to troubleshoot potential congestion
+ // control issues.
+ struct DebugState {
+ explicit DebugState(const BbrSender& sender);
+ DebugState(const DebugState& state);
+
+ Mode mode;
+ QuicBandwidth max_bandwidth;
+ QuicRoundTripCount round_trip_count;
+ int gain_cycle_index;
+ QuicByteCount congestion_window;
+
+ bool is_at_full_bandwidth;
+ QuicBandwidth bandwidth_at_last_round;
+ QuicRoundTripCount rounds_without_bandwidth_gain;
+
+ QuicTime::Delta min_rtt;
+ QuicTime min_rtt_timestamp;
+
+ RecoveryState recovery_state;
+ QuicByteCount recovery_window;
+
+ bool last_sample_is_app_limited;
+ QuicPacketNumber end_of_app_limited_phase;
+ };
+
+ BbrSender(QuicTime now,
+ const RttStats* rtt_stats,
+ const QuicUnackedPacketMap* unacked_packets,
+ QuicPacketCount initial_tcp_congestion_window,
+ QuicPacketCount max_tcp_congestion_window,
+ QuicRandom* random,
+ QuicConnectionStats* stats);
+ BbrSender(const BbrSender&) = delete;
+ BbrSender& operator=(const BbrSender&) = delete;
+ ~BbrSender() override;
+
+ // Start implementation of SendAlgorithmInterface.
+ bool InSlowStart() const override;
+ bool InRecovery() const override;
+ bool ShouldSendProbingPacket() const override;
+
+ void SetFromConfig(const QuicConfig& config,
+ Perspective perspective) override;
+
+ void AdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) override;
+ void SetNumEmulatedConnections(int num_connections) override {}
+ void SetInitialCongestionWindowInPackets(
+ QuicPacketCount congestion_window) override;
+ void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets) override;
+ void OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) override;
+ void OnRetransmissionTimeout(bool packets_retransmitted) override {}
+ void OnConnectionMigration() override {}
+ bool CanSend(QuicByteCount bytes_in_flight) override;
+ QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const override;
+ QuicBandwidth BandwidthEstimate() const override;
+ QuicByteCount GetCongestionWindow() const override;
+ QuicByteCount GetSlowStartThreshold() const override;
+ CongestionControlType GetCongestionControlType() const override;
+ std::string GetDebugState() const override;
+ void OnApplicationLimited(QuicByteCount bytes_in_flight) override;
+ // End implementation of SendAlgorithmInterface.
+
+ // Gets the number of RTTs BBR remains in STARTUP phase.
+ QuicRoundTripCount num_startup_rtts() const { return num_startup_rtts_; }
+ bool has_non_app_limited_sample() const {
+ return has_non_app_limited_sample_;
+ }
+
+ // Sets the pacing gain used in STARTUP. Must be greater than 1.
+ void set_high_gain(float high_gain) {
+ DCHECK_LT(1.0f, high_gain);
+ high_gain_ = high_gain;
+ if (mode_ == STARTUP) {
+ pacing_gain_ = high_gain;
+ }
+ }
+
+ // Sets the CWND gain used in STARTUP. Must be greater than 1.
+ void set_high_cwnd_gain(float high_cwnd_gain) {
+ DCHECK_LT(1.0f, high_cwnd_gain);
+ high_cwnd_gain_ = high_cwnd_gain;
+ if (mode_ == STARTUP) {
+ congestion_window_gain_ = high_cwnd_gain;
+ }
+ }
+
+ // Sets the gain used in DRAIN. Must be less than 1.
+ void set_drain_gain(float drain_gain) {
+ DCHECK_GT(1.0f, drain_gain);
+ drain_gain_ = drain_gain;
+ }
+
+ DebugState ExportDebugState() const;
+
+ private:
+ typedef WindowedFilter<QuicBandwidth,
+ MaxFilter<QuicBandwidth>,
+ QuicRoundTripCount,
+ QuicRoundTripCount>
+ MaxBandwidthFilter;
+
+ typedef WindowedFilter<QuicByteCount,
+ MaxFilter<QuicByteCount>,
+ QuicRoundTripCount,
+ QuicRoundTripCount>
+ MaxAckHeightFilter;
+
+ // Returns the current estimate of the RTT of the connection. Outside of the
+ // edge cases, this is minimum RTT.
+ QuicTime::Delta GetMinRtt() const;
+ // Returns whether the connection has achieved full bandwidth required to exit
+ // the slow start.
+ bool IsAtFullBandwidth() const;
+ // Computes the target congestion window using the specified gain.
+ QuicByteCount GetTargetCongestionWindow(float gain) const;
+ // The target congestion window during PROBE_RTT.
+ QuicByteCount ProbeRttCongestionWindow() const;
+ // Returns true if the current min_rtt should be kept and we should not enter
+ // PROBE_RTT immediately.
+ bool ShouldExtendMinRttExpiry() const;
+
+ // Enters the STARTUP mode.
+ void EnterStartupMode(QuicTime now);
+ // Enters the PROBE_BW mode.
+ void EnterProbeBandwidthMode(QuicTime now);
+
+ // Discards the lost packets from BandwidthSampler state.
+ void DiscardLostPackets(const LostPacketVector& lost_packets);
+ // Updates the round-trip counter if a round-trip has passed. Returns true if
+ // the counter has been advanced.
+ bool UpdateRoundTripCounter(QuicPacketNumber last_acked_packet);
+ // Updates the current bandwidth and min_rtt estimate based on the samples for
+ // the received acknowledgements. Returns true if min_rtt has expired.
+ bool UpdateBandwidthAndMinRtt(QuicTime now,
+ const AckedPacketVector& acked_packets);
+ // Updates the current gain used in PROBE_BW mode.
+ void UpdateGainCyclePhase(QuicTime now,
+ QuicByteCount prior_in_flight,
+ bool has_losses);
+ // Tracks for how many round-trips the bandwidth has not increased
+ // significantly.
+ void CheckIfFullBandwidthReached();
+ // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if
+ // appropriate.
+ void MaybeExitStartupOrDrain(QuicTime now);
+ // Decides whether to enter or exit PROBE_RTT.
+ void MaybeEnterOrExitProbeRtt(QuicTime now,
+ bool is_round_start,
+ bool min_rtt_expired);
+ // Determines whether BBR needs to enter, exit or advance state of the
+ // recovery.
+ void UpdateRecoveryState(QuicPacketNumber last_acked_packet,
+ bool has_losses,
+ bool is_round_start);
+
+ // Updates the ack aggregation max filter in bytes.
+ // Returns the most recent addition to the filter, or |newly_acked_bytes| if
+ // nothing was fed in to the filter.
+ QuicByteCount UpdateAckAggregationBytes(QuicTime ack_time,
+ QuicByteCount newly_acked_bytes);
+
+ // Determines the appropriate pacing rate for the connection.
+ void CalculatePacingRate();
+ // Determines the appropriate congestion window for the connection.
+ void CalculateCongestionWindow(QuicByteCount bytes_acked,
+ QuicByteCount excess_acked);
+ // Determines the appropriate window that constrains the in-flight during
+ // recovery.
+ void CalculateRecoveryWindow(QuicByteCount bytes_acked,
+ QuicByteCount bytes_lost);
+
+ // Returns true if there are enough bytes in flight to ensure more bandwidth
+ // will be observed if present.
+ bool IsPipeSufficientlyFull() const;
+
+ // Called right before exiting STARTUP.
+ void OnExitStartup(QuicTime now);
+
+ const RttStats* rtt_stats_;
+ const QuicUnackedPacketMap* unacked_packets_;
+ QuicRandom* random_;
+ QuicConnectionStats* stats_;
+
+ Mode mode_;
+
+ // Bandwidth sampler provides BBR with the bandwidth measurements at
+ // individual points.
+ BandwidthSampler sampler_;
+
+ // The number of the round trips that have occurred during the connection.
+ QuicRoundTripCount round_trip_count_;
+
+ // The packet number of the most recently sent packet.
+ QuicPacketNumber last_sent_packet_;
+ // Acknowledgement of any packet after |current_round_trip_end_| will cause
+ // the round trip counter to advance.
+ QuicPacketNumber current_round_trip_end_;
+
+ // The filter that tracks the maximum bandwidth over the multiple recent
+ // round-trips.
+ MaxBandwidthFilter max_bandwidth_;
+
+ // Tracks the maximum number of bytes acked faster than the sending rate.
+ MaxAckHeightFilter max_ack_height_;
+
+ // The time this aggregation started and the number of bytes acked during it.
+ QuicTime aggregation_epoch_start_time_;
+ QuicByteCount aggregation_epoch_bytes_;
+
+ // Minimum RTT estimate. Automatically expires within 10 seconds (and
+ // triggers PROBE_RTT mode) if no new value is sampled during that period.
+ QuicTime::Delta min_rtt_;
+ // The time at which the current value of |min_rtt_| was assigned.
+ QuicTime min_rtt_timestamp_;
+
+ // The maximum allowed number of bytes in flight.
+ QuicByteCount congestion_window_;
+
+ // The initial value of the |congestion_window_|.
+ QuicByteCount initial_congestion_window_;
+
+ // The largest value the |congestion_window_| can achieve.
+ QuicByteCount max_congestion_window_;
+
+ // The smallest value the |congestion_window_| can achieve.
+ QuicByteCount min_congestion_window_;
+
+ // The pacing gain applied during the STARTUP phase.
+ float high_gain_;
+
+ // The CWND gain applied during the STARTUP phase.
+ float high_cwnd_gain_;
+
+ // The pacing gain applied during the DRAIN phase.
+ float drain_gain_;
+
+ // The current pacing rate of the connection.
+ QuicBandwidth pacing_rate_;
+
+ // The gain currently applied to the pacing rate.
+ float pacing_gain_;
+ // The gain currently applied to the congestion window.
+ float congestion_window_gain_;
+
+ // The gain used for the congestion window during PROBE_BW. Latched from
+ // quic_bbr_cwnd_gain flag.
+ const float congestion_window_gain_constant_;
+ // The number of RTTs to stay in STARTUP mode. Defaults to 3.
+ QuicRoundTripCount num_startup_rtts_;
+ // If true, exit startup if 1RTT has passed with no bandwidth increase and
+ // the connection is in recovery.
+ bool exit_startup_on_loss_;
+
+ // Number of round-trips in PROBE_BW mode, used for determining the current
+ // pacing gain cycle.
+ int cycle_current_offset_;
+ // The time at which the last pacing gain cycle was started.
+ QuicTime last_cycle_start_;
+
+ // Indicates whether the connection has reached the full bandwidth mode.
+ bool is_at_full_bandwidth_;
+ // Number of rounds during which there was no significant bandwidth increase.
+ QuicRoundTripCount rounds_without_bandwidth_gain_;
+ // The bandwidth compared to which the increase is measured.
+ QuicBandwidth bandwidth_at_last_round_;
+
+ // Set to true upon exiting quiescence.
+ bool exiting_quiescence_;
+
+ // Time at which PROBE_RTT has to be exited. Setting it to zero indicates
+ // that the time is yet unknown as the number of packets in flight has not
+ // reached the required value.
+ QuicTime exit_probe_rtt_at_;
+ // Indicates whether a round-trip has passed since PROBE_RTT became active.
+ bool probe_rtt_round_passed_;
+
+ // Indicates whether the most recent bandwidth sample was marked as
+ // app-limited.
+ bool last_sample_is_app_limited_;
+ // Indicates whether any non app-limited samples have been recorded.
+ bool has_non_app_limited_sample_;
+ // Indicates app-limited calls should be ignored as long as there's
+ // enough data inflight to see more bandwidth when necessary.
+ bool flexible_app_limited_;
+
+ // Current state of recovery.
+ RecoveryState recovery_state_;
+ // Receiving acknowledgement of a packet after |end_recovery_at_| will cause
+ // BBR to exit the recovery mode. A value above zero indicates at least one
+ // loss has been detected, so it must not be set back to zero.
+ QuicPacketNumber end_recovery_at_;
+ // A window used to limit the number of bytes in flight during loss recovery.
+ QuicByteCount recovery_window_;
+ // If true, consider all samples in recovery app-limited.
+ bool is_app_limited_recovery_;
+
+ // When true, pace at 1.5x and disable packet conservation in STARTUP.
+ bool slower_startup_;
+ // When true, disables packet conservation in STARTUP.
+ bool rate_based_startup_;
+ // When non-zero, decreases the rate in STARTUP by the total number of bytes
+ // lost in STARTUP divided by CWND.
+ uint8_t startup_rate_reduction_multiplier_;
+ // Sum of bytes lost in STARTUP.
+ QuicByteCount startup_bytes_lost_;
+
+ // When true, add the most recent ack aggregation measurement during STARTUP.
+ bool enable_ack_aggregation_during_startup_;
+ // When true, expire the windowed ack aggregation values in STARTUP when
+ // bandwidth increases more than 25%.
+ bool expire_ack_aggregation_in_startup_;
+
+ // If true, will not exit low gain mode until bytes_in_flight drops below BDP
+ // or it's time for high gain mode.
+ bool drain_to_target_;
+
+ // If true, use a CWND of 0.75*BDP during probe_rtt instead of 4 packets.
+ bool probe_rtt_based_on_bdp_;
+ // If true, skip probe_rtt and update the timestamp of the existing min_rtt to
+ // now if min_rtt over the last cycle is within 12.5% of the current min_rtt.
+ // Even if the min_rtt is 12.5% too low, the 25% gain cycling and 2x CWND gain
+ // should overcome an overly small min_rtt.
+ bool probe_rtt_skipped_if_similar_rtt_;
+ // If true, disable PROBE_RTT entirely as long as the connection was recently
+ // app limited.
+ bool probe_rtt_disabled_if_app_limited_;
+ bool app_limited_since_last_probe_rtt_;
+ QuicTime::Delta min_rtt_since_last_probe_rtt_;
+
+ // Latched value of --quic_always_get_bw_sample_when_acked.
+ const bool always_get_bw_sample_when_acked_;
+};
+
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+ const BbrSender::Mode& mode);
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const BbrSender::DebugState& state);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_BBR_SENDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender_test.cc
new file mode 100644
index 00000000000..3d40e0a2217
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/bbr_sender_test.cc
@@ -0,0 +1,1342 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+
+using testing::AllOf;
+using testing::Ge;
+using testing::Le;
+
+namespace quic {
+namespace test {
+
+// Use the initial CWND of 10, as 32 is too much for the test network.
+const uint32_t kInitialCongestionWindowPackets = 10;
+const uint32_t kDefaultWindowTCP =
+ kInitialCongestionWindowPackets * kDefaultTCPMSS;
+
+// Test network parameters. Here, the topology of the network is:
+//
+// BBR sender
+// |
+// | <-- local link (10 Mbps, 2 ms delay)
+// |
+// Network switch
+// * <-- the bottleneck queue in the direction
+// | of the receiver
+// |
+// | <-- test link (4 Mbps, 30 ms delay)
+// |
+// |
+// Receiver
+//
+// The reason the bandwidths chosen are relatively low is the fact that the
+// connection simulator uses QuicTime for its internal clock, and as such has
+// the granularity of 1us, meaning that at bandwidth higher than 20 Mbps the
+// packets can start to land on the same timestamp.
+const QuicBandwidth kTestLinkBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(4000);
+const QuicBandwidth kLocalLinkBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(10000);
+const QuicTime::Delta kTestPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(30);
+const QuicTime::Delta kLocalPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(2);
+const QuicTime::Delta kTestTransferTime =
+ kTestLinkBandwidth.TransferTime(kMaxOutgoingPacketSize) +
+ kLocalLinkBandwidth.TransferTime(kMaxOutgoingPacketSize);
+const QuicTime::Delta kTestRtt =
+ (kTestPropagationDelay + kLocalPropagationDelay + kTestTransferTime) * 2;
+const QuicByteCount kTestBdp = kTestRtt * kTestLinkBandwidth;
+
+class BbrSenderTest : public QuicTest {
+ protected:
+ BbrSenderTest()
+ : simulator_(),
+ bbr_sender_(&simulator_,
+ "BBR sender",
+ "Receiver",
+ Perspective::IS_CLIENT,
+ /*connection_id=*/TestConnectionId(42)),
+ competing_sender_(&simulator_,
+ "Competing sender",
+ "Competing receiver",
+ Perspective::IS_CLIENT,
+ /*connection_id=*/TestConnectionId(43)),
+ receiver_(&simulator_,
+ "Receiver",
+ "BBR sender",
+ Perspective::IS_SERVER,
+ /*connection_id=*/TestConnectionId(42)),
+ competing_receiver_(&simulator_,
+ "Competing receiver",
+ "Competing sender",
+ Perspective::IS_SERVER,
+ /*connection_id=*/TestConnectionId(43)),
+ receiver_multiplexer_("Receiver multiplexer",
+ {&receiver_, &competing_receiver_}) {
+ rtt_stats_ = bbr_sender_.connection()->sent_packet_manager().GetRttStats();
+ sender_ = SetupBbrSender(&bbr_sender_);
+
+ clock_ = simulator_.GetClock();
+ simulator_.set_random_generator(&random_);
+
+ uint64_t seed = QuicRandom::GetInstance()->RandUint64();
+ random_.set_seed(seed);
+ QUIC_LOG(INFO) << "BbrSenderTest simulator set up. Seed: " << seed;
+ }
+
+ simulator::Simulator simulator_;
+ simulator::QuicEndpoint bbr_sender_;
+ simulator::QuicEndpoint competing_sender_;
+ simulator::QuicEndpoint receiver_;
+ simulator::QuicEndpoint competing_receiver_;
+ simulator::QuicEndpointMultiplexer receiver_multiplexer_;
+ std::unique_ptr<simulator::Switch> switch_;
+ std::unique_ptr<simulator::SymmetricLink> bbr_sender_link_;
+ std::unique_ptr<simulator::SymmetricLink> competing_sender_link_;
+ std::unique_ptr<simulator::SymmetricLink> receiver_link_;
+
+ SimpleRandom random_;
+
+ // Owned by different components of the connection.
+ const QuicClock* clock_;
+ const RttStats* rtt_stats_;
+ BbrSender* sender_;
+
+ // Enables BBR on |endpoint| and returns the associated BBR congestion
+ // controller.
+ BbrSender* SetupBbrSender(simulator::QuicEndpoint* endpoint) {
+ const RttStats* rtt_stats =
+ endpoint->connection()->sent_packet_manager().GetRttStats();
+ // Ownership of the sender will be overtaken by the endpoint.
+ BbrSender* sender = new BbrSender(
+ endpoint->connection()->clock()->Now(), rtt_stats,
+ QuicSentPacketManagerPeer::GetUnackedPacketMap(
+ QuicConnectionPeer::GetSentPacketManager(endpoint->connection())),
+ kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets,
+ &random_, QuicConnectionPeer::GetStats(endpoint->connection()));
+ QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender);
+ endpoint->RecordTrace();
+ return sender;
+ }
+
+ // Creates a default setup, which is a network with a bottleneck between the
+ // receiver and the switch. The switch has the buffers four times larger than
+ // the bottleneck BDP, which should guarantee a lack of losses.
+ void CreateDefaultSetup() {
+ switch_ = QuicMakeUnique<simulator::Switch>(&simulator_, "Switch", 8,
+ 2 * kTestBdp);
+ bbr_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &bbr_sender_, switch_->port(1), kLocalLinkBandwidth,
+ kLocalPropagationDelay);
+ receiver_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &receiver_, switch_->port(2), kTestLinkBandwidth,
+ kTestPropagationDelay);
+ }
+
+ // Same as the default setup, except the buffer now is half of the BDP.
+ void CreateSmallBufferSetup() {
+ switch_ = QuicMakeUnique<simulator::Switch>(&simulator_, "Switch", 8,
+ 0.5 * kTestBdp);
+ bbr_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &bbr_sender_, switch_->port(1), kLocalLinkBandwidth,
+ kTestPropagationDelay);
+ receiver_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &receiver_, switch_->port(2), kTestLinkBandwidth,
+ kTestPropagationDelay);
+ }
+
+ // Creates the variation of the default setup in which there is another sender
+ // that competes for the same bottleneck link.
+ void CreateCompetitionSetup() {
+ switch_ = QuicMakeUnique<simulator::Switch>(&simulator_, "Switch", 8,
+ 2 * kTestBdp);
+
+ // Add a small offset to the competing link in order to avoid
+ // synchronization effects.
+ const QuicTime::Delta small_offset = QuicTime::Delta::FromMicroseconds(3);
+ bbr_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &bbr_sender_, switch_->port(1), kLocalLinkBandwidth,
+ kLocalPropagationDelay);
+ competing_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &competing_sender_, switch_->port(3), kLocalLinkBandwidth,
+ kLocalPropagationDelay + small_offset);
+ receiver_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &receiver_multiplexer_, switch_->port(2), kTestLinkBandwidth,
+ kTestPropagationDelay);
+ }
+
+ // Creates a BBR vs BBR competition setup.
+ void CreateBbrVsBbrSetup() {
+ SetupBbrSender(&competing_sender_);
+ CreateCompetitionSetup();
+ }
+
+ void EnableAggregation(QuicByteCount aggregation_bytes,
+ QuicTime::Delta aggregation_timeout) {
+ // Enable aggregation on the path from the receiver to the sender.
+ switch_->port_queue(1)->EnableAggregation(aggregation_bytes,
+ aggregation_timeout);
+ }
+
+ void DoSimpleTransfer(QuicByteCount transfer_size, QuicTime::Delta deadline) {
+ bbr_sender_.AddBytesToTransfer(transfer_size);
+ // TODO(vasilvv): consider rewriting this to run until the receiver actually
+ // receives the intended amount of bytes.
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return bbr_sender_.bytes_to_transfer() == 0; }, deadline);
+ EXPECT_TRUE(simulator_result)
+ << "Simple transfer failed. Bytes remaining: "
+ << bbr_sender_.bytes_to_transfer();
+ QUIC_LOG(INFO) << "Simple transfer state: " << sender_->ExportDebugState();
+ }
+
+ // Drive the simulator by sending enough data to enter PROBE_BW.
+ void DriveOutOfStartup() {
+ ASSERT_FALSE(sender_->ExportDebugState().is_at_full_bandwidth);
+ DoSimpleTransfer(1024 * 1024, QuicTime::Delta::FromSeconds(15));
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ ExpectApproxEq(kTestLinkBandwidth,
+ sender_->ExportDebugState().max_bandwidth, 0.02f);
+ }
+
+ // Send |bytes|-sized bursts of data |number_of_bursts| times, waiting for
+ // |wait_time| between each burst.
+ void SendBursts(size_t number_of_bursts,
+ QuicByteCount bytes,
+ QuicTime::Delta wait_time) {
+ ASSERT_EQ(0u, bbr_sender_.bytes_to_transfer());
+ for (size_t i = 0; i < number_of_bursts; i++) {
+ bbr_sender_.AddBytesToTransfer(bytes);
+
+ // Transfer data and wait for three seconds between each transfer.
+ simulator_.RunFor(wait_time);
+
+ // Ensure the connection did not time out.
+ ASSERT_TRUE(bbr_sender_.connection()->connected());
+ ASSERT_TRUE(receiver_.connection()->connected());
+ }
+
+ simulator_.RunFor(wait_time + kTestRtt);
+ ASSERT_EQ(0u, bbr_sender_.bytes_to_transfer());
+ }
+
+ void SetConnectionOption(QuicTag option) {
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(option);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ sender_->SetFromConfig(config, Perspective::IS_SERVER);
+ }
+};
+
+TEST_F(BbrSenderTest, SetInitialCongestionWindow) {
+ EXPECT_NE(3u * kDefaultTCPMSS, sender_->GetCongestionWindow());
+ sender_->SetInitialCongestionWindowInPackets(3);
+ EXPECT_EQ(3u * kDefaultTCPMSS, sender_->GetCongestionWindow());
+}
+
+// Test a simple long data transfer in the default setup.
+TEST_F(BbrSenderTest, SimpleTransfer) {
+ // Disable Ack Decimation on the receiver, because it can increase srtt.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
+ CreateDefaultSetup();
+
+ // At startup make sure we are at the default.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+ // And that window is un-affected.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+
+ // Verify that Sender is in slow start.
+ EXPECT_TRUE(sender_->InSlowStart());
+
+ // Verify that pacing rate is based on the initial RTT.
+ QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ 2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
+ ExpectApproxEq(expected_pacing_rate.ToBitsPerSecond(),
+ sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);
+
+ ASSERT_GE(kTestBdp, kDefaultWindowTCP + kDefaultTCPMSS);
+
+ DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+
+ // The margin here is quite high, since there exists a possibility that the
+ // connection just exited high gain cycle.
+ ExpectApproxEq(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f);
+}
+
+// Test a simple transfer in a situation when the buffer is less than BDP.
+TEST_F(BbrSenderTest, SimpleTransferSmallBuffer) {
+ CreateSmallBufferSetup();
+
+ DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth,
+ 0.01f);
+ EXPECT_GE(bbr_sender_.connection()->GetStats().packets_lost, 0u);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+TEST_F(BbrSenderTest, SimpleTransferEarlyPacketLoss) {
+ SetQuicReloadableFlag(quic_bbr_no_bytes_acked_in_startup_recovery, true);
+ // Enable rate based startup so the recovery window doesn't hide the true
+ // congestion_window_ in GetCongestionWindow().
+ SetConnectionOption(kBBS1);
+ // Disable Ack Decimation on the receiver, because it can increase srtt.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
+ CreateDefaultSetup();
+
+ // At startup make sure we are at the default.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+ // Verify that Sender is in slow start.
+ EXPECT_TRUE(sender_->InSlowStart());
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+ // And that window is un-affected.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+
+ // Transfer 12MB.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ // Drop the first packet.
+ receiver_.DropNextIncomingPacket();
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ if (sender_->InRecovery()) {
+ // Two packets are acked before the first is declared lost.
+ EXPECT_LE(sender_->GetCongestionWindow(),
+ (kDefaultWindowTCP + 2 * kDefaultTCPMSS));
+ }
+ return bbr_sender_.bytes_to_transfer() == 0 || !sender_->InSlowStart();
+ },
+ QuicTime::Delta::FromSeconds(30));
+ EXPECT_TRUE(simulator_result) << "Simple transfer failed. Bytes remaining: "
+ << bbr_sender_.bytes_to_transfer();
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(1u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Test a simple long data transfer with 2 rtts of aggregation.
+TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes) {
+ CreateDefaultSetup();
+ // 2 RTTs of aggregation, with a max of 10kb.
+ EnableAggregation(10 * 1024, 2 * kTestRtt);
+
+ // Transfer 12MB.
+ DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ // It's possible to read a bandwidth as much as 50% too high with aggregation.
+ EXPECT_LE(kTestLinkBandwidth * 0.99f,
+ sender_->ExportDebugState().max_bandwidth);
+ // TODO(ianswett): Tighten this bound once we understand why BBR is
+ // overestimating bandwidth with aggregation. b/36022633
+ EXPECT_GE(kTestLinkBandwidth * 1.5f,
+ sender_->ExportDebugState().max_bandwidth);
+ // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
+ // bandwidth higher than the link rate.
+ // The margin here is high, because the aggregation greatly increases
+ // smoothed rtt.
+ EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
+ ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.2f);
+}
+
+// Test a simple long data transfer with 2 rtts of aggregation.
+TEST_F(BbrSenderTest, SimpleTransferAckDecimation) {
+ // Decrease the CWND gain so extra CWND is required with stretch acks.
+ FLAGS_quic_bbr_cwnd_gain = 1.0;
+ sender_ = new BbrSender(
+ bbr_sender_.connection()->clock()->Now(), rtt_stats_,
+ QuicSentPacketManagerPeer::GetUnackedPacketMap(
+ QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())),
+ kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets,
+ &random_, QuicConnectionPeer::GetStats(bbr_sender_.connection()));
+ QuicConnectionPeer::SetSendAlgorithm(bbr_sender_.connection(), sender_);
+ // Enable Ack Decimation on the receiver.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(),
+ AckMode::ACK_DECIMATION);
+ CreateDefaultSetup();
+
+ // Transfer 12MB.
+ DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ // It's possible to read a bandwidth as much as 50% too high with aggregation.
+ EXPECT_LE(kTestLinkBandwidth * 0.99f,
+ sender_->ExportDebugState().max_bandwidth);
+ // TODO(ianswett): Tighten this bound once we understand why BBR is
+ // overestimating bandwidth with aggregation. b/36022633
+ EXPECT_GE(kTestLinkBandwidth * 1.5f,
+ sender_->ExportDebugState().max_bandwidth);
+ // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
+ // bandwidth higher than the link rate.
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+ // The margin here is high, because the aggregation greatly increases
+ // smoothed rtt.
+ EXPECT_GE(kTestRtt * 2, rtt_stats_->smoothed_rtt());
+ ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.1f);
+}
+
+// Test a simple long data transfer with 2 rtts of aggregation.
+TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes20RTTWindow) {
+ // Disable Ack Decimation on the receiver, because it can increase srtt.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
+ CreateDefaultSetup();
+ SetConnectionOption(kBBR4);
+ // 2 RTTs of aggregation, with a max of 10kb.
+ EnableAggregation(10 * 1024, 2 * kTestRtt);
+
+ // Transfer 12MB.
+ DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ // It's possible to read a bandwidth as much as 50% too high with aggregation.
+ EXPECT_LE(kTestLinkBandwidth * 0.99f,
+ sender_->ExportDebugState().max_bandwidth);
+ // TODO(ianswett): Tighten this bound once we understand why BBR is
+ // overestimating bandwidth with aggregation. b/36022633
+ EXPECT_GE(kTestLinkBandwidth * 1.5f,
+ sender_->ExportDebugState().max_bandwidth);
+ // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
+ // bandwidth higher than the link rate.
+ // The margin here is high, because the aggregation greatly increases
+ // smoothed rtt.
+ EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
+ ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.12f);
+}
+
+// Test a simple long data transfer with 2 rtts of aggregation.
+TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes40RTTWindow) {
+ // Disable Ack Decimation on the receiver, because it can increase srtt.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
+ CreateDefaultSetup();
+ SetConnectionOption(kBBR5);
+ // 2 RTTs of aggregation, with a max of 10kb.
+ EnableAggregation(10 * 1024, 2 * kTestRtt);
+
+ // Transfer 12MB.
+ DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ // It's possible to read a bandwidth as much as 50% too high with aggregation.
+ EXPECT_LE(kTestLinkBandwidth * 0.99f,
+ sender_->ExportDebugState().max_bandwidth);
+ // TODO(ianswett): Tighten this bound once we understand why BBR is
+ // overestimating bandwidth with aggregation. b/36022633
+ EXPECT_GE(kTestLinkBandwidth * 1.5f,
+ sender_->ExportDebugState().max_bandwidth);
+ // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
+ // bandwidth higher than the link rate.
+ // The margin here is high, because the aggregation greatly increases
+ // smoothed rtt.
+ EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
+ ExpectApproxEq(kTestRtt, rtt_stats_->min_rtt(), 0.12f);
+}
+
+// Test the number of losses incurred by the startup phase in a situation when
+// the buffer is less than BDP.
+TEST_F(BbrSenderTest, PacketLossOnSmallBufferStartup) {
+ CreateSmallBufferSetup();
+
+ DriveOutOfStartup();
+ float loss_rate =
+ static_cast<float>(bbr_sender_.connection()->GetStats().packets_lost) /
+ bbr_sender_.connection()->GetStats().packets_sent;
+ EXPECT_LE(loss_rate, 0.31);
+}
+
+// Ensures the code transitions loss recovery states correctly (NOT_IN_RECOVERY
+// -> CONSERVATION -> GROWTH -> NOT_IN_RECOVERY).
+TEST_F(BbrSenderTest, RecoveryStates) {
+ // Set seed to the position where the gain cycling causes the sender go
+ // into conservation upon entering PROBE_BW.
+ //
+ // TODO(vasilvv): there should be a better way to test this.
+ random_.set_seed(UINT64_C(14719894707049085006));
+
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
+ bool simulator_result;
+ CreateSmallBufferSetup();
+
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+ ASSERT_EQ(BbrSender::NOT_IN_RECOVERY,
+ sender_->ExportDebugState().recovery_state);
+
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().recovery_state !=
+ BbrSender::NOT_IN_RECOVERY;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::CONSERVATION,
+ sender_->ExportDebugState().recovery_state);
+
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().recovery_state !=
+ BbrSender::CONSERVATION;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::GROWTH, sender_->ExportDebugState().recovery_state);
+
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().recovery_state != BbrSender::GROWTH;
+ },
+ timeout);
+
+ ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ ASSERT_EQ(BbrSender::NOT_IN_RECOVERY,
+ sender_->ExportDebugState().recovery_state);
+ ASSERT_TRUE(simulator_result);
+}
+
+// Verify the behavior of the algorithm in the case when the connection sends
+// small bursts of data after sending continuously for a while.
+TEST_F(BbrSenderTest, ApplicationLimitedBursts) {
+ CreateDefaultSetup();
+
+ DriveOutOfStartup();
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+
+ SendBursts(20, 512, QuicTime::Delta::FromSeconds(3));
+ EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited);
+ ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth,
+ 0.01f);
+}
+
+// Verify the behavior of the algorithm in the case when the connection sends
+// small bursts of data and then starts sending continuously.
+TEST_F(BbrSenderTest, ApplicationLimitedBurstsWithoutPrior) {
+ CreateDefaultSetup();
+
+ SendBursts(40, 512, QuicTime::Delta::FromSeconds(3));
+ EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited);
+
+ DriveOutOfStartup();
+ ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth,
+ 0.01f);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Verify that the DRAIN phase works correctly.
+TEST_F(BbrSenderTest, Drain) {
+ // Disable Ack Decimation on the receiver, because it can increase srtt.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
+ CreateDefaultSetup();
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
+ // Get the queue at the bottleneck, which is the outgoing queue at the port to
+ // which the receiver is connected.
+ const simulator::Queue* queue = switch_->port_queue(2);
+ bool simulator_result;
+
+ // We have no intention of ever finishing this transfer.
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+
+ // Run the startup, and verify that it fills up the queue.
+ ASSERT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().mode != BbrSender::STARTUP;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ ExpectApproxEq(sender_->BandwidthEstimate() * (1 / 2.885f),
+ sender_->PacingRate(0), 0.01f);
+ // BBR uses CWND gain of 2.88 during STARTUP, hence it will fill the buffer
+ // with approximately 1.88 BDPs. Here, we use 1.5 to give some margin for
+ // error.
+ EXPECT_GE(queue->bytes_queued(), 1.5 * kTestBdp);
+
+ // Observe increased RTT due to bufferbloat.
+ const QuicTime::Delta queueing_delay =
+ kTestLinkBandwidth.TransferTime(queue->bytes_queued());
+ ExpectApproxEq(kTestRtt + queueing_delay, rtt_stats_->latest_rtt(), 0.1f);
+
+ // Transition to the drain phase and verify that it makes the queue
+ // have at most a BDP worth of packets.
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return sender_->ExportDebugState().mode != BbrSender::DRAIN; },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_LE(queue->bytes_queued(), kTestBdp);
+
+ // Wait for a few round trips and ensure we're in appropriate phase of gain
+ // cycling before taking an RTT measurement.
+ const QuicRoundTripCount start_round_trip =
+ sender_->ExportDebugState().round_trip_count;
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this, start_round_trip]() {
+ QuicRoundTripCount rounds_passed =
+ sender_->ExportDebugState().round_trip_count - start_round_trip;
+ return rounds_passed >= 4 &&
+ sender_->ExportDebugState().gain_cycle_index == 7;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+
+ // Observe the bufferbloat go away.
+ ExpectApproxEq(kTestRtt, rtt_stats_->smoothed_rtt(), 0.1f);
+}
+
+// Verify that the DRAIN phase works correctly.
+TEST_F(BbrSenderTest, ShallowDrain) {
+ SetQuicReloadableFlag(quic_bbr_slower_startup3, true);
+ // Disable Ack Decimation on the receiver, because it can increase srtt.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
+
+ CreateDefaultSetup();
+ // BBQ4 increases the pacing gain in DRAIN to 0.75
+ SetConnectionOption(kBBQ4);
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
+ // Get the queue at the bottleneck, which is the outgoing queue at the port to
+ // which the receiver is connected.
+ const simulator::Queue* queue = switch_->port_queue(2);
+ bool simulator_result;
+
+ // We have no intention of ever finishing this transfer.
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+
+ // Run the startup, and verify that it fills up the queue.
+ ASSERT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().mode != BbrSender::STARTUP;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(0.75 * sender_->BandwidthEstimate(), sender_->PacingRate(0));
+ // BBR uses CWND gain of 2.88 during STARTUP, hence it will fill the buffer
+ // with approximately 1.88 BDPs. Here, we use 1.5 to give some margin for
+ // error.
+ EXPECT_GE(queue->bytes_queued(), 1.5 * kTestBdp);
+
+ // Observe increased RTT due to bufferbloat.
+ const QuicTime::Delta queueing_delay =
+ kTestLinkBandwidth.TransferTime(queue->bytes_queued());
+ ExpectApproxEq(kTestRtt + queueing_delay, rtt_stats_->latest_rtt(), 0.1f);
+
+ // Transition to the drain phase and verify that it makes the queue
+ // have at most a BDP worth of packets.
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return sender_->ExportDebugState().mode != BbrSender::DRAIN; },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_LE(queue->bytes_queued(), kTestBdp);
+
+ // Wait for a few round trips and ensure we're in appropriate phase of gain
+ // cycling before taking an RTT measurement.
+ const QuicRoundTripCount start_round_trip =
+ sender_->ExportDebugState().round_trip_count;
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this, start_round_trip]() {
+ QuicRoundTripCount rounds_passed =
+ sender_->ExportDebugState().round_trip_count - start_round_trip;
+ return rounds_passed >= 4 &&
+ sender_->ExportDebugState().gain_cycle_index == 7;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+
+ // Observe the bufferbloat go away.
+ ExpectApproxEq(kTestRtt, rtt_stats_->smoothed_rtt(), 0.1f);
+}
+
+// Verify that the connection enters and exits PROBE_RTT correctly.
+TEST_F(BbrSenderTest, ProbeRtt) {
+ CreateDefaultSetup();
+ DriveOutOfStartup();
+
+ // We have no intention of ever finishing this transfer.
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+
+ // Wait until the connection enters PROBE_RTT.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode);
+
+ // Exit PROBE_RTT.
+ const QuicTime probe_rtt_start = clock_->Now();
+ const QuicTime::Delta time_to_exit_probe_rtt =
+ kTestRtt + QuicTime::Delta::FromMilliseconds(200);
+ simulator_.RunFor(1.5 * time_to_exit_probe_rtt);
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start);
+}
+
+// Verify that the first sample after PROBE_RTT is not used as the bandwidth,
+// because the round counter doesn't advance during PROBE_RTT.
+TEST_F(BbrSenderTest, AppLimitedRecoveryNoBandwidthDecrease) {
+ SetQuicReloadableFlag(quic_bbr_app_limited_recovery, true);
+ CreateDefaultSetup();
+ DriveOutOfStartup();
+
+ // We have no intention of ever finishing this transfer.
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+
+ // Wait until the connection enters PROBE_RTT.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode);
+
+ const QuicBandwidth beginning_bw = sender_->BandwidthEstimate();
+
+ // Run for most of PROBE_RTT.
+ const QuicTime probe_rtt_start = clock_->Now();
+ const QuicTime::Delta time_to_exit_probe_rtt =
+ kTestRtt + QuicTime::Delta::FromMilliseconds(200);
+ simulator_.RunFor(0.60 * time_to_exit_probe_rtt);
+ EXPECT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode);
+ // Lose a packet before exiting PROBE_RTT, which puts us in packet
+ // conservation and then continue there for a while and ensure the bandwidth
+ // estimate doesn't decrease.
+ for (int i = 0; i < 20; ++i) {
+ receiver_.DropNextIncomingPacket();
+ simulator_.RunFor(0.9 * kTestRtt);
+ // Ensure the bandwidth didn't decrease and the samples are app limited.
+ EXPECT_LE(beginning_bw, sender_->BandwidthEstimate());
+ EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited);
+ }
+ EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start);
+}
+
+// Verify that the connection enters and exits PROBE_RTT correctly.
+TEST_F(BbrSenderTest, ProbeRttBDPBasedCWNDTarget) {
+ CreateDefaultSetup();
+ SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true);
+ SetConnectionOption(kBBR6);
+ DriveOutOfStartup();
+
+ // We have no intention of ever finishing this transfer.
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+
+ // Wait until the connection enters PROBE_RTT.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode);
+
+ // Exit PROBE_RTT.
+ const QuicTime probe_rtt_start = clock_->Now();
+ const QuicTime::Delta time_to_exit_probe_rtt =
+ kTestRtt + QuicTime::Delta::FromMilliseconds(200);
+ simulator_.RunFor(1.5 * time_to_exit_probe_rtt);
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start);
+}
+
+// Verify that the connection enters does not enter PROBE_RTT.
+TEST_F(BbrSenderTest, ProbeRttSkippedAfterAppLimitedAndStableRtt) {
+ CreateDefaultSetup();
+ SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true);
+ SetConnectionOption(kBBR7);
+ DriveOutOfStartup();
+
+ // We have no intention of ever finishing this transfer.
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+
+ // Wait until the connection enters PROBE_RTT.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT;
+ },
+ timeout);
+ ASSERT_FALSE(simulator_result);
+ ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+}
+
+// Verify that the connection enters does not enter PROBE_RTT.
+TEST_F(BbrSenderTest, ProbeRttSkippedAfterAppLimited) {
+ CreateDefaultSetup();
+ SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true);
+ SetConnectionOption(kBBR8);
+ DriveOutOfStartup();
+
+ // We have no intention of ever finishing this transfer.
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+
+ // Wait until the connection enters PROBE_RTT.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT;
+ },
+ timeout);
+ ASSERT_FALSE(simulator_result);
+ ASSERT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+}
+
+// Ensure that a connection that is app-limited and is at sufficiently low
+// bandwidth will not exit high gain phase, and similarly ensure that the
+// connection will exit low gain early if the number of bytes in flight is low.
+TEST_F(BbrSenderTest, InFlightAwareGainCycling) {
+ // Disable Ack Decimation on the receiver, because it can increase srtt.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
+ CreateDefaultSetup();
+ DriveOutOfStartup();
+
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
+ bool simulator_result;
+
+ // Start a few cycles prior to the high gain one.
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return sender_->ExportDebugState().gain_cycle_index == 6; },
+ timeout);
+
+ // Send at 10% of available rate. Run for 3 seconds, checking in the middle
+ // and at the end. The pacing gain should be high throughout.
+ QuicBandwidth target_bandwidth = 0.1f * kTestLinkBandwidth;
+ QuicTime::Delta burst_interval = QuicTime::Delta::FromMilliseconds(300);
+ for (int i = 0; i < 2; i++) {
+ SendBursts(5, target_bandwidth * burst_interval, burst_interval);
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_EQ(0, sender_->ExportDebugState().gain_cycle_index);
+ ExpectApproxEq(kTestLinkBandwidth,
+ sender_->ExportDebugState().max_bandwidth, 0.01f);
+ }
+
+ // Now that in-flight is almost zero and the pacing gain is still above 1,
+ // send approximately 1.25 BDPs worth of data. This should cause the
+ // PROBE_BW mode to enter low gain cycle, and exit it earlier than one min_rtt
+ // due to running out of data to send.
+ bbr_sender_.AddBytesToTransfer(1.3 * kTestBdp);
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return sender_->ExportDebugState().gain_cycle_index == 1; },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ simulator_.RunFor(0.75 * sender_->ExportDebugState().min_rtt);
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_EQ(2, sender_->ExportDebugState().gain_cycle_index);
+}
+
+// Ensure that the pacing rate does not drop at startup.
+TEST_F(BbrSenderTest, NoBandwidthDropOnStartup) {
+ CreateDefaultSetup();
+
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
+ bool simulator_result;
+
+ QuicBandwidth initial_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ kInitialCongestionWindowPackets * kDefaultTCPMSS,
+ rtt_stats_->initial_rtt());
+ EXPECT_GE(sender_->PacingRate(0), initial_rate);
+
+ // Send a packet.
+ bbr_sender_.AddBytesToTransfer(1000);
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return receiver_.bytes_received() == 1000; }, timeout);
+ ASSERT_TRUE(simulator_result);
+ EXPECT_GE(sender_->PacingRate(0), initial_rate);
+
+ // Wait for a while.
+ simulator_.RunFor(QuicTime::Delta::FromSeconds(2));
+ EXPECT_GE(sender_->PacingRate(0), initial_rate);
+
+ // Send another packet.
+ bbr_sender_.AddBytesToTransfer(1000);
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return receiver_.bytes_received() == 2000; }, timeout);
+ ASSERT_TRUE(simulator_result);
+ EXPECT_GE(sender_->PacingRate(0), initial_rate);
+}
+
+// Test exiting STARTUP earlier due to the 1RTT connection option.
+TEST_F(BbrSenderTest, SimpleTransfer1RTTStartup) {
+ CreateDefaultSetup();
+
+ SetConnectionOption(k1RTT);
+ EXPECT_EQ(1u, sender_->num_startup_rtts());
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ QuicRoundTripCount max_bw_round = 0;
+ QuicBandwidth max_bw(QuicBandwidth::Zero());
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &max_bw, &max_bw_round]() {
+ if (max_bw < sender_->ExportDebugState().max_bandwidth) {
+ max_bw = sender_->ExportDebugState().max_bandwidth;
+ max_bw_round = sender_->ExportDebugState().round_trip_count;
+ }
+ return sender_->ExportDebugState().is_at_full_bandwidth;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(1u, sender_->ExportDebugState().round_trip_count - max_bw_round);
+ EXPECT_EQ(1u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Test exiting STARTUP earlier due to the 2RTT connection option.
+TEST_F(BbrSenderTest, SimpleTransfer2RTTStartup) {
+ CreateDefaultSetup();
+
+ SetConnectionOption(k2RTT);
+ EXPECT_EQ(2u, sender_->num_startup_rtts());
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ QuicRoundTripCount max_bw_round = 0;
+ QuicBandwidth max_bw(QuicBandwidth::Zero());
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &max_bw, &max_bw_round]() {
+ if (max_bw < sender_->ExportDebugState().max_bandwidth) {
+ max_bw = sender_->ExportDebugState().max_bandwidth;
+ max_bw_round = sender_->ExportDebugState().round_trip_count;
+ }
+ return sender_->ExportDebugState().is_at_full_bandwidth;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(2u, sender_->ExportDebugState().round_trip_count - max_bw_round);
+ EXPECT_EQ(2u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Test exiting STARTUP earlier upon loss due to the LRTT connection option.
+TEST_F(BbrSenderTest, SimpleTransferLRTTStartup) {
+ CreateDefaultSetup();
+
+ SetConnectionOption(kLRTT);
+ EXPECT_EQ(3u, sender_->num_startup_rtts());
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ QuicRoundTripCount max_bw_round = 0;
+ QuicBandwidth max_bw(QuicBandwidth::Zero());
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &max_bw, &max_bw_round]() {
+ if (max_bw < sender_->ExportDebugState().max_bandwidth) {
+ max_bw = sender_->ExportDebugState().max_bandwidth;
+ max_bw_round = sender_->ExportDebugState().round_trip_count;
+ }
+ return sender_->ExportDebugState().is_at_full_bandwidth;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(3u, sender_->ExportDebugState().round_trip_count - max_bw_round);
+ EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Test exiting STARTUP earlier upon loss due to the LRTT connection option.
+TEST_F(BbrSenderTest, SimpleTransferLRTTStartupSmallBuffer) {
+ CreateSmallBufferSetup();
+
+ SetConnectionOption(kLRTT);
+ EXPECT_EQ(3u, sender_->num_startup_rtts());
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ QuicRoundTripCount max_bw_round = 0;
+ QuicBandwidth max_bw(QuicBandwidth::Zero());
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &max_bw, &max_bw_round]() {
+ if (max_bw < sender_->ExportDebugState().max_bandwidth) {
+ max_bw = sender_->ExportDebugState().max_bandwidth;
+ max_bw_round = sender_->ExportDebugState().round_trip_count;
+ }
+ return sender_->ExportDebugState().is_at_full_bandwidth;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_GE(2u, sender_->ExportDebugState().round_trip_count - max_bw_round);
+ EXPECT_EQ(1u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Test slower pacing after loss in STARTUP due to the BBRS connection option.
+TEST_F(BbrSenderTest, SimpleTransferSlowerStartup) {
+ CreateSmallBufferSetup();
+
+ SetConnectionOption(kBBRS);
+ EXPECT_EQ(3u, sender_->num_startup_rtts());
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ QuicRoundTripCount max_bw_round = 0;
+ QuicBandwidth max_bw(QuicBandwidth::Zero());
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &max_bw, &max_bw_round]() {
+ if (max_bw < sender_->ExportDebugState().max_bandwidth) {
+ max_bw = sender_->ExportDebugState().max_bandwidth;
+ max_bw_round = sender_->ExportDebugState().round_trip_count;
+ }
+ // Expect the pacing rate in STARTUP to decrease once packet loss
+ // is observed, but the CWND does not.
+ if (bbr_sender_.connection()->GetStats().packets_lost > 0 &&
+ !sender_->ExportDebugState().is_at_full_bandwidth &&
+ sender_->has_non_app_limited_sample()) {
+ EXPECT_EQ(1.5f * max_bw, sender_->PacingRate(0));
+ }
+ return sender_->ExportDebugState().is_at_full_bandwidth;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_GE(3u, sender_->ExportDebugState().round_trip_count - max_bw_round);
+ EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Ensures no change in congestion window in STARTUP after loss.
+TEST_F(BbrSenderTest, SimpleTransferNoConservationInStartup) {
+ CreateSmallBufferSetup();
+
+ SetConnectionOption(kBBS1);
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ bool used_conservation_cwnd = false;
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &used_conservation_cwnd]() {
+ if (!sender_->ExportDebugState().is_at_full_bandwidth &&
+ sender_->GetCongestionWindow() <
+ sender_->ExportDebugState().congestion_window) {
+ used_conservation_cwnd = true;
+ }
+ return sender_->ExportDebugState().is_at_full_bandwidth;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_FALSE(used_conservation_cwnd);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Ensures no change in congestion window in STARTUP after loss, but that the
+// rate decreases.
+TEST_F(BbrSenderTest, SimpleTransferStartupRateReduction) {
+ SetQuicReloadableFlag(quic_bbr_startup_rate_reduction, true);
+ CreateSmallBufferSetup();
+
+ SetConnectionOption(kBBS4);
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ bool used_conservation_cwnd = false;
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &used_conservation_cwnd]() {
+ if (!sender_->ExportDebugState().is_at_full_bandwidth &&
+ sender_->GetCongestionWindow() <
+ sender_->ExportDebugState().congestion_window) {
+ used_conservation_cwnd = true;
+ }
+ // Exit once a loss is hit.
+ return bbr_sender_.connection()->GetStats().packets_lost > 0 ||
+ sender_->ExportDebugState().is_at_full_bandwidth;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_TRUE(sender_->InRecovery());
+ EXPECT_FALSE(used_conservation_cwnd);
+ EXPECT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
+ EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost);
+
+ // Lose each outstanding packet and the pacing rate decreases.
+ const QuicBandwidth original_pacing_rate = sender_->PacingRate(0);
+ QuicBandwidth pacing_rate = original_pacing_rate;
+ const QuicByteCount original_cwnd = sender_->GetCongestionWindow();
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(), kMaxOutgoingPacketSize));
+ QuicPacketNumber largest_sent =
+ bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket();
+ for (QuicPacketNumber packet_number =
+ bbr_sender_.connection()->sent_packet_manager().GetLeastUnacked();
+ packet_number <= largest_sent; ++packet_number) {
+ lost_packets[0].packet_number = packet_number;
+ sender_->OnCongestionEvent(false, 0, clock_->Now(), {}, lost_packets);
+ EXPECT_EQ(original_cwnd, sender_->GetCongestionWindow());
+ EXPECT_GT(original_pacing_rate, sender_->PacingRate(0));
+ EXPECT_GE(pacing_rate, sender_->PacingRate(0));
+ EXPECT_LE(1.25 * sender_->BandwidthEstimate(), sender_->PacingRate(0));
+ pacing_rate = sender_->PacingRate(0);
+ }
+}
+
+// Ensures no change in congestion window in STARTUP after loss, but that the
+// rate decreases twice as fast as BBS4.
+TEST_F(BbrSenderTest, SimpleTransferDoubleStartupRateReduction) {
+ SetQuicReloadableFlag(quic_bbr_startup_rate_reduction, true);
+ CreateSmallBufferSetup();
+
+ SetConnectionOption(kBBS5);
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ bool used_conservation_cwnd = false;
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this, &used_conservation_cwnd]() {
+ if (!sender_->ExportDebugState().is_at_full_bandwidth &&
+ sender_->GetCongestionWindow() <
+ sender_->ExportDebugState().congestion_window) {
+ used_conservation_cwnd = true;
+ }
+ // Exit once a loss is hit.
+ return bbr_sender_.connection()->GetStats().packets_lost > 0 ||
+ sender_->ExportDebugState().is_at_full_bandwidth;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_TRUE(sender_->InRecovery());
+ EXPECT_FALSE(used_conservation_cwnd);
+ EXPECT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode);
+ EXPECT_NE(0u, bbr_sender_.connection()->GetStats().packets_lost);
+
+ // Lose each outstanding packet and the pacing rate decreases.
+ const QuicBandwidth original_pacing_rate = sender_->PacingRate(0);
+ QuicBandwidth pacing_rate = original_pacing_rate;
+ const QuicByteCount original_cwnd = sender_->GetCongestionWindow();
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(), kMaxOutgoingPacketSize));
+ QuicPacketNumber largest_sent =
+ bbr_sender_.connection()->sent_packet_manager().GetLargestSentPacket();
+ for (QuicPacketNumber packet_number =
+ bbr_sender_.connection()->sent_packet_manager().GetLeastUnacked();
+ packet_number <= largest_sent; ++packet_number) {
+ lost_packets[0].packet_number = packet_number;
+ sender_->OnCongestionEvent(false, 0, clock_->Now(), {}, lost_packets);
+ EXPECT_EQ(original_cwnd, sender_->GetCongestionWindow());
+ EXPECT_GT(original_pacing_rate, sender_->PacingRate(0));
+ EXPECT_GE(pacing_rate, sender_->PacingRate(0));
+ EXPECT_LE(1.25 * sender_->BandwidthEstimate(), sender_->PacingRate(0));
+ pacing_rate = sender_->PacingRate(0);
+ }
+}
+
+TEST_F(BbrSenderTest, DerivedPacingGainStartup) {
+ SetQuicReloadableFlag(quic_bbr_slower_startup3, true);
+ CreateDefaultSetup();
+
+ SetConnectionOption(kBBQ1);
+ EXPECT_EQ(3u, sender_->num_startup_rtts());
+ // Verify that Sender is in slow start.
+ EXPECT_TRUE(sender_->InSlowStart());
+ // Verify that pacing rate is based on the initial RTT.
+ QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ 2.773 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
+ ExpectApproxEq(expected_pacing_rate.ToBitsPerSecond(),
+ sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth,
+ 0.01f);
+ EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+TEST_F(BbrSenderTest, DerivedCWNDGainStartup) {
+ SetQuicReloadableFlag(quic_bbr_slower_startup3, true);
+ CreateDefaultSetup();
+
+ SetConnectionOption(kBBQ2);
+ EXPECT_EQ(3u, sender_->num_startup_rtts());
+ // Verify that Sender is in slow start.
+ EXPECT_TRUE(sender_->InSlowStart());
+ // Verify that pacing rate is based on the initial RTT.
+ QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ 2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
+ ExpectApproxEq(expected_pacing_rate.ToBitsPerSecond(),
+ sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth,
+ 0.01f);
+ EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+ // Expect an SRTT less than 2.7 * Min RTT on exit from STARTUP.
+ EXPECT_GT(kTestRtt * 2.7, rtt_stats_->smoothed_rtt());
+}
+
+TEST_F(BbrSenderTest, AckAggregationInStartup) {
+ SetQuicReloadableFlag(quic_bbr_slower_startup3, true);
+ // Disable Ack Decimation on the receiver to avoid loss and make results
+ // consistent.
+ QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
+ CreateDefaultSetup();
+
+ SetConnectionOption(kBBQ3);
+ EXPECT_EQ(3u, sender_->num_startup_rtts());
+ // Verify that Sender is in slow start.
+ EXPECT_TRUE(sender_->InSlowStart());
+ // Verify that pacing rate is based on the initial RTT.
+ QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
+ 2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
+ ExpectApproxEq(expected_pacing_rate.ToBitsPerSecond(),
+ sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);
+
+ // Run until the full bandwidth is reached and check how many rounds it was.
+ bbr_sender_.AddBytesToTransfer(12 * 1024 * 1024);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return sender_->ExportDebugState().is_at_full_bandwidth; },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(BbrSender::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(3u, sender_->ExportDebugState().rounds_without_bandwidth_gain);
+ ExpectApproxEq(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth,
+ 0.01f);
+ EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+ EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+}
+
+// Test that two BBR flows started slightly apart from each other terminate.
+TEST_F(BbrSenderTest, SimpleCompetition) {
+ const QuicByteCount transfer_size = 10 * 1024 * 1024;
+ const QuicTime::Delta transfer_time =
+ kTestLinkBandwidth.TransferTime(transfer_size);
+ CreateBbrVsBbrSetup();
+
+ // Transfer 10% of data in first transfer.
+ bbr_sender_.AddBytesToTransfer(transfer_size);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return receiver_.bytes_received() >= 0.1 * transfer_size; },
+ transfer_time);
+ ASSERT_TRUE(simulator_result);
+
+ // Start the second transfer and wait until both finish.
+ competing_sender_.AddBytesToTransfer(transfer_size);
+ simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return receiver_.bytes_received() == transfer_size &&
+ competing_receiver_.bytes_received() == transfer_size;
+ },
+ 3 * transfer_time);
+ ASSERT_TRUE(simulator_result);
+}
+
+// Test that BBR can resume bandwidth from cached network parameters.
+TEST_F(BbrSenderTest, ResumeConnectionState) {
+ CreateDefaultSetup();
+
+ bbr_sender_.connection()->AdjustNetworkParameters(kTestLinkBandwidth,
+ kTestRtt);
+ EXPECT_EQ(kTestLinkBandwidth, sender_->ExportDebugState().max_bandwidth);
+ EXPECT_EQ(kTestLinkBandwidth, sender_->BandwidthEstimate());
+ ExpectApproxEq(kTestRtt, sender_->ExportDebugState().min_rtt, 0.01f);
+
+ DriveOutOfStartup();
+}
+
+// Test with a min CWND of 1 instead of 4 packets.
+TEST_F(BbrSenderTest, ProbeRTTMinCWND1) {
+ CreateDefaultSetup();
+ SetConnectionOption(kMIN1);
+ DriveOutOfStartup();
+
+ // We have no intention of ever finishing this transfer.
+ bbr_sender_.AddBytesToTransfer(100 * 1024 * 1024);
+
+ // Wait until the connection enters PROBE_RTT.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(12);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() {
+ return sender_->ExportDebugState().mode == BbrSender::PROBE_RTT;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ ASSERT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode);
+ // The PROBE_RTT CWND should be 1 if the min CWND is 1.
+ EXPECT_EQ(kDefaultTCPMSS, sender_->GetCongestionWindow());
+
+ // Exit PROBE_RTT.
+ const QuicTime probe_rtt_start = clock_->Now();
+ const QuicTime::Delta time_to_exit_probe_rtt =
+ kTestRtt + QuicTime::Delta::FromMilliseconds(200);
+ simulator_.RunFor(1.5 * time_to_exit_probe_rtt);
+ EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+ EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start);
+}
+
+TEST_F(BbrSenderTest, StartupStats) {
+ CreateDefaultSetup();
+
+ DriveOutOfStartup();
+ ASSERT_FALSE(sender_->InSlowStart());
+
+ const QuicConnectionStats& stats = bbr_sender_.connection()->GetStats();
+ EXPECT_EQ(1u, stats.slowstart_count);
+ EXPECT_THAT(stats.slowstart_num_rtts, AllOf(Ge(5u), Le(15u)));
+ EXPECT_THAT(stats.slowstart_packets_sent, AllOf(Ge(100u), Le(1000u)));
+ EXPECT_THAT(stats.slowstart_bytes_sent, AllOf(Ge(100000u), Le(1000000u)));
+ EXPECT_LE(stats.slowstart_packets_lost, 10u);
+ EXPECT_LE(stats.slowstart_bytes_lost, 10000u);
+ EXPECT_THAT(stats.slowstart_duration,
+ AllOf(Ge(QuicTime::Delta::FromMilliseconds(500)),
+ Le(QuicTime::Delta::FromMilliseconds(1500))));
+ EXPECT_EQ(QuicTime::Zero(), stats.slowstart_start_time);
+ EXPECT_EQ(stats.slowstart_duration,
+ QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())
+ ->GetSlowStartDuration());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.cc
new file mode 100644
index 00000000000..9300a790eb1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Constants based on TCP defaults.
+// The following constants are in 2^10 fractions of a second instead of ms to
+// allow a 10 shift right to divide.
+const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3)
+ // where 0.100 is 100 ms which is the scaling
+ // round trip time.
+const int kCubeCongestionWindowScale = 410;
+// The cube factor for packets in bytes.
+const uint64_t kCubeFactor =
+ (UINT64_C(1) << kCubeScale) / kCubeCongestionWindowScale / kDefaultTCPMSS;
+
+const float kDefaultCubicBackoffFactor = 0.7f; // Default Cubic backoff factor.
+// Additional backoff factor when loss occurs in the concave part of the Cubic
+// curve. This additional backoff factor is expected to give up bandwidth to
+// new concurrent flows and speed up convergence.
+const float kBetaLastMax = 0.85f;
+
+} // namespace
+
+CubicBytes::CubicBytes(const QuicClock* clock)
+ : clock_(clock),
+ num_connections_(kDefaultNumConnections),
+ epoch_(QuicTime::Zero()) {
+ ResetCubicState();
+}
+
+void CubicBytes::SetNumConnections(int num_connections) {
+ num_connections_ = num_connections;
+}
+
+float CubicBytes::Alpha() const {
+ // TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that
+ // beta here is a cwnd multiplier, and is equal to 1-beta from the paper.
+ // We derive the equivalent alpha for an N-connection emulation as:
+ const float beta = Beta();
+ return 3 * num_connections_ * num_connections_ * (1 - beta) / (1 + beta);
+}
+
+float CubicBytes::Beta() const {
+ // kNConnectionBeta is the backoff factor after loss for our N-connection
+ // emulation, which emulates the effective backoff of an ensemble of N
+ // TCP-Reno connections on a single loss event. The effective multiplier is
+ // computed as:
+ return (num_connections_ - 1 + kDefaultCubicBackoffFactor) / num_connections_;
+}
+
+float CubicBytes::BetaLastMax() const {
+ // BetaLastMax is the additional backoff factor after loss for our
+ // N-connection emulation, which emulates the additional backoff of
+ // an ensemble of N TCP-Reno connections on a single loss event. The
+ // effective multiplier is computed as:
+ return (num_connections_ - 1 + kBetaLastMax) / num_connections_;
+}
+
+void CubicBytes::ResetCubicState() {
+ epoch_ = QuicTime::Zero(); // Reset time.
+ last_max_congestion_window_ = 0;
+ acked_bytes_count_ = 0;
+ estimated_tcp_congestion_window_ = 0;
+ origin_point_congestion_window_ = 0;
+ time_to_origin_point_ = 0;
+ last_target_congestion_window_ = 0;
+}
+
+void CubicBytes::OnApplicationLimited() {
+ // When sender is not using the available congestion window, the window does
+ // not grow. But to be RTT-independent, Cubic assumes that the sender has been
+ // using the entire window during the time since the beginning of the current
+ // "epoch" (the end of the last loss recovery period). Since
+ // application-limited periods break this assumption, we reset the epoch when
+ // in such a period. This reset effectively freezes congestion window growth
+ // through application-limited periods and allows Cubic growth to continue
+ // when the entire window is being used.
+ epoch_ = QuicTime::Zero();
+}
+
+QuicByteCount CubicBytes::CongestionWindowAfterPacketLoss(
+ QuicByteCount current_congestion_window) {
+ // Since bytes-mode Reno mode slightly under-estimates the cwnd, we
+ // may never reach precisely the last cwnd over the course of an
+ // RTT. Do not interpret a slight under-estimation as competing traffic.
+ if (current_congestion_window + kDefaultTCPMSS <
+ last_max_congestion_window_) {
+ // We never reached the old max, so assume we are competing with
+ // another flow. Use our extra back off factor to allow the other
+ // flow to go up.
+ last_max_congestion_window_ =
+ static_cast<int>(BetaLastMax() * current_congestion_window);
+ } else {
+ last_max_congestion_window_ = current_congestion_window;
+ }
+ epoch_ = QuicTime::Zero(); // Reset time.
+ return static_cast<int>(current_congestion_window * Beta());
+}
+
+QuicByteCount CubicBytes::CongestionWindowAfterAck(
+ QuicByteCount acked_bytes,
+ QuicByteCount current_congestion_window,
+ QuicTime::Delta delay_min,
+ QuicTime event_time) {
+ acked_bytes_count_ += acked_bytes;
+
+ if (!epoch_.IsInitialized()) {
+ // First ACK after a loss event.
+ QUIC_DVLOG(1) << "Start of epoch";
+ epoch_ = event_time; // Start of epoch.
+ acked_bytes_count_ = acked_bytes; // Reset count.
+ // Reset estimated_tcp_congestion_window_ to be in sync with cubic.
+ estimated_tcp_congestion_window_ = current_congestion_window;
+ if (last_max_congestion_window_ <= current_congestion_window) {
+ time_to_origin_point_ = 0;
+ origin_point_congestion_window_ = current_congestion_window;
+ } else {
+ time_to_origin_point_ = static_cast<uint32_t>(
+ cbrt(kCubeFactor *
+ (last_max_congestion_window_ - current_congestion_window)));
+ origin_point_congestion_window_ = last_max_congestion_window_;
+ }
+ }
+ // Change the time unit from microseconds to 2^10 fractions per second. Take
+ // the round trip time in account. This is done to allow us to use shift as a
+ // divide operator.
+ int64_t elapsed_time =
+ ((event_time + delay_min - epoch_).ToMicroseconds() << 10) /
+ kNumMicrosPerSecond;
+
+ // Right-shifts of negative, signed numbers have implementation-dependent
+ // behavior, so force the offset to be positive, as is done in the kernel.
+ uint64_t offset = std::abs(time_to_origin_point_ - elapsed_time);
+
+ QuicByteCount delta_congestion_window = (kCubeCongestionWindowScale * offset *
+ offset * offset * kDefaultTCPMSS) >>
+ kCubeScale;
+
+ const bool add_delta = elapsed_time > time_to_origin_point_;
+ DCHECK(add_delta ||
+ (origin_point_congestion_window_ > delta_congestion_window));
+ QuicByteCount target_congestion_window =
+ add_delta ? origin_point_congestion_window_ + delta_congestion_window
+ : origin_point_congestion_window_ - delta_congestion_window;
+ // Limit the CWND increase to half the acked bytes.
+ target_congestion_window =
+ std::min(target_congestion_window,
+ current_congestion_window + acked_bytes_count_ / 2);
+
+ DCHECK_LT(0u, estimated_tcp_congestion_window_);
+ // Increase the window by approximately Alpha * 1 MSS of bytes every
+ // time we ack an estimated tcp window of bytes. For small
+ // congestion windows (less than 25), the formula below will
+ // increase slightly slower than linearly per estimated tcp window
+ // of bytes.
+ estimated_tcp_congestion_window_ += acked_bytes_count_ *
+ (Alpha() * kDefaultTCPMSS) /
+ estimated_tcp_congestion_window_;
+ acked_bytes_count_ = 0;
+
+ // We have a new cubic congestion window.
+ last_target_congestion_window_ = target_congestion_window;
+
+ // Compute target congestion_window based on cubic target and estimated TCP
+ // congestion_window, use highest (fastest).
+ if (target_congestion_window < estimated_tcp_congestion_window_) {
+ target_congestion_window = estimated_tcp_congestion_window_;
+ }
+
+ QUIC_DVLOG(1) << "Final target congestion_window: "
+ << target_congestion_window;
+ return target_congestion_window;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.h
new file mode 100644
index 00000000000..2bbd04c6573
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Cubic algorithm, helper class to TCP cubic.
+// For details see http://netsrv.csc.ncsu.edu/export/cubic_a_new_tcp_2008.pdf.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_CUBIC_BYTES_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_CUBIC_BYTES_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class CubicBytesTest;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE CubicBytes {
+ public:
+ explicit CubicBytes(const QuicClock* clock);
+ CubicBytes(const CubicBytes&) = delete;
+ CubicBytes& operator=(const CubicBytes&) = delete;
+
+ void SetNumConnections(int num_connections);
+
+ // Call after a timeout to reset the cubic state.
+ void ResetCubicState();
+
+ // Compute a new congestion window to use after a loss event.
+ // Returns the new congestion window in packets. The new congestion window is
+ // a multiplicative decrease of our current window.
+ QuicByteCount CongestionWindowAfterPacketLoss(QuicPacketCount current);
+
+ // Compute a new congestion window to use after a received ACK.
+ // Returns the new congestion window in bytes. The new congestion window
+ // follows a cubic function that depends on the time passed since last packet
+ // loss.
+ QuicByteCount CongestionWindowAfterAck(QuicByteCount acked_bytes,
+ QuicByteCount current,
+ QuicTime::Delta delay_min,
+ QuicTime event_time);
+
+ // Call on ack arrival when sender is unable to use the available congestion
+ // window. Resets Cubic state during quiescence.
+ void OnApplicationLimited();
+
+ private:
+ friend class test::CubicBytesTest;
+
+ static const QuicTime::Delta MaxCubicTimeInterval() {
+ return QuicTime::Delta::FromMilliseconds(30);
+ }
+
+ // Compute the TCP Cubic alpha, beta, and beta-last-max based on the
+ // current number of connections.
+ float Alpha() const;
+ float Beta() const;
+ float BetaLastMax() const;
+
+ QuicByteCount last_max_congestion_window() const {
+ return last_max_congestion_window_;
+ }
+
+ const QuicClock* clock_;
+
+ // Number of connections to simulate.
+ int num_connections_;
+
+ // Time when this cycle started, after last loss event.
+ QuicTime epoch_;
+
+ // Max congestion window used just before last loss event.
+ // Note: to improve fairness to other streams an additional back off is
+ // applied to this value if the new value is below our latest value.
+ QuicByteCount last_max_congestion_window_;
+
+ // Number of acked bytes since the cycle started (epoch).
+ QuicByteCount acked_bytes_count_;
+
+ // TCP Reno equivalent congestion window in packets.
+ QuicByteCount estimated_tcp_congestion_window_;
+
+ // Origin point of cubic function.
+ QuicByteCount origin_point_congestion_window_;
+
+ // Time to origin point of cubic function in 2^10 fractions of a second.
+ uint32_t time_to_origin_point_;
+
+ // Last congestion window in packets computed by cubic function.
+ QuicByteCount last_target_congestion_window_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_CUBIC_BYTES_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes_test.cc
new file mode 100644
index 00000000000..4f3e9b1044a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes_test.cc
@@ -0,0 +1,388 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.h"
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const float kBeta = 0.7f; // Default Cubic backoff factor.
+const float kBetaLastMax = 0.85f; // Default Cubic backoff factor.
+const uint32_t kNumConnections = 2;
+const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections;
+const float kNConnectionBetaLastMax =
+ (kNumConnections - 1 + kBetaLastMax) / kNumConnections;
+const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
+ (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
+
+} // namespace
+
+class CubicBytesTest : public QuicTest {
+ protected:
+ CubicBytesTest()
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
+ cubic_(&clock_) {}
+
+ QuicByteCount RenoCwndInBytes(QuicByteCount current_cwnd) {
+ QuicByteCount reno_estimated_cwnd =
+ current_cwnd +
+ kDefaultTCPMSS * (kNConnectionAlpha * kDefaultTCPMSS) / current_cwnd;
+ return reno_estimated_cwnd;
+ }
+
+ QuicByteCount ConservativeCwndInBytes(QuicByteCount current_cwnd) {
+ QuicByteCount conservative_cwnd = current_cwnd + kDefaultTCPMSS / 2;
+ return conservative_cwnd;
+ }
+
+ QuicByteCount CubicConvexCwndInBytes(QuicByteCount initial_cwnd,
+ QuicTime::Delta rtt,
+ QuicTime::Delta elapsed_time) {
+ const int64_t offset =
+ ((elapsed_time + rtt).ToMicroseconds() << 10) / 1000000;
+ const QuicByteCount delta_congestion_window =
+ ((410 * offset * offset * offset) * kDefaultTCPMSS >> 40);
+ const QuicByteCount cubic_cwnd = initial_cwnd + delta_congestion_window;
+ return cubic_cwnd;
+ }
+
+ QuicByteCount LastMaxCongestionWindow() {
+ return cubic_.last_max_congestion_window();
+ }
+
+ QuicTime::Delta MaxCubicTimeInterval() {
+ return cubic_.MaxCubicTimeInterval();
+ }
+
+ const QuicTime::Delta one_ms_;
+ const QuicTime::Delta hundred_ms_;
+ MockClock clock_;
+ CubicBytes cubic_;
+};
+
+// TODO(jokulik): The original "AboveOrigin" test, below, is very
+// loose. It's nearly impossible to make the test tighter without
+// deploying the fix for convex mode. Once cubic convex is deployed,
+// replace "AboveOrigin" with this test.
+TEST_F(CubicBytesTest, AboveOriginWithTighterBounds) {
+ // Convex growth.
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ int64_t rtt_min_ms = rtt_min.ToMilliseconds();
+ float rtt_min_s = rtt_min_ms / 1000.0;
+ QuicByteCount current_cwnd = 10 * kDefaultTCPMSS;
+ const QuicByteCount initial_cwnd = current_cwnd;
+
+ clock_.AdvanceTime(one_ms_);
+ const QuicTime initial_time = clock_.ApproximateNow();
+ const QuicByteCount expected_first_cwnd = RenoCwndInBytes(current_cwnd);
+ current_cwnd = cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd,
+ rtt_min, initial_time);
+ ASSERT_EQ(expected_first_cwnd, current_cwnd);
+
+ // Normal TCP phase.
+ // The maximum number of expected Reno RTTs is calculated by
+ // finding the point where the cubic curve and the reno curve meet.
+ const int max_reno_rtts =
+ std::sqrt(kNConnectionAlpha / (.4 * rtt_min_s * rtt_min_s * rtt_min_s)) -
+ 2;
+ for (int i = 0; i < max_reno_rtts; ++i) {
+ // Alternatively, we expect it to increase by one, every time we
+ // receive current_cwnd/Alpha acks back. (This is another way of
+ // saying we expect cwnd to increase by approximately Alpha once
+ // we receive current_cwnd number ofacks back).
+ const uint64_t num_acks_this_epoch =
+ current_cwnd / kDefaultTCPMSS / kNConnectionAlpha;
+ const QuicByteCount initial_cwnd_this_epoch = current_cwnd;
+ for (QuicPacketCount n = 0; n < num_acks_this_epoch; ++n) {
+ // Call once per ACK.
+ const QuicByteCount expected_next_cwnd = RenoCwndInBytes(current_cwnd);
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ ASSERT_EQ(expected_next_cwnd, current_cwnd);
+ }
+ // Our byte-wise Reno implementation is an estimate. We expect
+ // the cwnd to increase by approximately one MSS every
+ // cwnd/kDefaultTCPMSS/Alpha acks, but it may be off by as much as
+ // half a packet for smaller values of current_cwnd.
+ const QuicByteCount cwnd_change_this_epoch =
+ current_cwnd - initial_cwnd_this_epoch;
+ ASSERT_NEAR(kDefaultTCPMSS, cwnd_change_this_epoch, kDefaultTCPMSS / 2);
+ clock_.AdvanceTime(hundred_ms_);
+ }
+
+ for (int i = 0; i < 54; ++i) {
+ const uint64_t max_acks_this_epoch = current_cwnd / kDefaultTCPMSS;
+ const QuicTime::Delta interval = QuicTime::Delta::FromMicroseconds(
+ hundred_ms_.ToMicroseconds() / max_acks_this_epoch);
+ for (QuicPacketCount n = 0; n < max_acks_this_epoch; ++n) {
+ clock_.AdvanceTime(interval);
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ const QuicByteCount expected_cwnd = CubicConvexCwndInBytes(
+ initial_cwnd, rtt_min, (clock_.ApproximateNow() - initial_time));
+ // If we allow per-ack updates, every update is a small cubic update.
+ ASSERT_EQ(expected_cwnd, current_cwnd);
+ }
+ }
+ const QuicByteCount expected_cwnd = CubicConvexCwndInBytes(
+ initial_cwnd, rtt_min, (clock_.ApproximateNow() - initial_time));
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ ASSERT_EQ(expected_cwnd, current_cwnd);
+}
+
+// TODO(ianswett): This test was disabled when all fixes were enabled, but it
+// may be worth fixing.
+TEST_F(CubicBytesTest, DISABLED_AboveOrigin) {
+ // Convex growth.
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ QuicByteCount current_cwnd = 10 * kDefaultTCPMSS;
+ // Without the signed-integer, cubic-convex fix, we start out in the
+ // wrong mode.
+ QuicPacketCount expected_cwnd = RenoCwndInBytes(current_cwnd);
+ // Initialize the state.
+ clock_.AdvanceTime(one_ms_);
+ ASSERT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd,
+ rtt_min, clock_.ApproximateNow()));
+ current_cwnd = expected_cwnd;
+ const QuicPacketCount initial_cwnd = expected_cwnd;
+ // Normal TCP phase.
+ for (int i = 0; i < 48; ++i) {
+ for (QuicPacketCount n = 1;
+ n < current_cwnd / kDefaultTCPMSS / kNConnectionAlpha; ++n) {
+ // Call once per ACK.
+ ASSERT_NEAR(
+ current_cwnd,
+ cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min,
+ clock_.ApproximateNow()),
+ kDefaultTCPMSS);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ // When we fix convex mode and the uint64 arithmetic, we
+ // increase the expected_cwnd only after after the first 100ms,
+ // rather than after the initial 1ms.
+ expected_cwnd += kDefaultTCPMSS;
+ ASSERT_NEAR(expected_cwnd, current_cwnd, kDefaultTCPMSS);
+ }
+ // Cubic phase.
+ for (int i = 0; i < 52; ++i) {
+ for (QuicPacketCount n = 1; n < current_cwnd / kDefaultTCPMSS; ++n) {
+ // Call once per ACK.
+ ASSERT_NEAR(
+ current_cwnd,
+ cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd, rtt_min,
+ clock_.ApproximateNow()),
+ kDefaultTCPMSS);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ }
+ // Total time elapsed so far; add min_rtt (0.1s) here as well.
+ float elapsed_time_s = 10.0f + 0.1f;
+ // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
+ expected_cwnd =
+ initial_cwnd / kDefaultTCPMSS +
+ (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) / 1024;
+ EXPECT_EQ(expected_cwnd, current_cwnd / kDefaultTCPMSS);
+}
+
+// Constructs an artificial scenario to ensure that cubic-convex
+// increases are truly fine-grained:
+//
+// - After starting the epoch, this test advances the elapsed time
+// sufficiently far that cubic will do small increases at less than
+// MaxCubicTimeInterval() intervals.
+//
+// - Sets an artificially large initial cwnd to prevent Reno from the
+// convex increases on every ack.
+TEST_F(CubicBytesTest, AboveOriginFineGrainedCubing) {
+ // Start the test with an artificially large cwnd to prevent Reno
+ // from over-taking cubic.
+ QuicByteCount current_cwnd = 1000 * kDefaultTCPMSS;
+ const QuicByteCount initial_cwnd = current_cwnd;
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ clock_.AdvanceTime(one_ms_);
+ QuicTime initial_time = clock_.ApproximateNow();
+
+ // Start the epoch and then artificially advance the time.
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(600));
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+
+ // We expect the algorithm to perform only non-zero, fine-grained cubic
+ // increases on every ack in this case.
+ for (int i = 0; i < 100; ++i) {
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ const QuicByteCount expected_cwnd = CubicConvexCwndInBytes(
+ initial_cwnd, rtt_min, (clock_.ApproximateNow() - initial_time));
+ const QuicByteCount next_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ // Make sure we are performing cubic increases.
+ ASSERT_EQ(expected_cwnd, next_cwnd);
+ // Make sure that these are non-zero, less-than-packet sized
+ // increases.
+ ASSERT_GT(next_cwnd, current_cwnd);
+ const QuicByteCount cwnd_delta = next_cwnd - current_cwnd;
+ ASSERT_GT(kDefaultTCPMSS * .1, cwnd_delta);
+
+ current_cwnd = next_cwnd;
+ }
+}
+
+// Constructs an artificial scenario to show what happens when we
+// allow per-ack updates, rather than limititing update freqency. In
+// this scenario, the first two acks of the epoch produce the same
+// cwnd. When we limit per-ack updates, this would cause the
+// cessation of cubic updates for 30ms. When we allow per-ack
+// updates, the window continues to grow on every ack.
+TEST_F(CubicBytesTest, PerAckUpdates) {
+ // Start the test with a large cwnd and RTT, to force the first
+ // increase to be a cubic increase.
+ QuicPacketCount initial_cwnd_packets = 150;
+ QuicByteCount current_cwnd = initial_cwnd_packets * kDefaultTCPMSS;
+ const QuicTime::Delta rtt_min = 350 * one_ms_;
+
+ // Initialize the epoch
+ clock_.AdvanceTime(one_ms_);
+ // Keep track of the growth of the reno-equivalent cwnd.
+ QuicByteCount reno_cwnd = RenoCwndInBytes(current_cwnd);
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ const QuicByteCount initial_cwnd = current_cwnd;
+
+ // Simulate the return of cwnd packets in less than
+ // MaxCubicInterval() time.
+ const QuicPacketCount max_acks = initial_cwnd_packets / kNConnectionAlpha;
+ const QuicTime::Delta interval = QuicTime::Delta::FromMicroseconds(
+ MaxCubicTimeInterval().ToMicroseconds() / (max_acks + 1));
+
+ // In this scenario, the first increase is dictated by the cubic
+ // equation, but it is less than one byte, so the cwnd doesn't
+ // change. Normally, without per-ack increases, any cwnd plateau
+ // will cause the cwnd to be pinned for MaxCubicTimeInterval(). If
+ // we enable per-ack updates, the cwnd will continue to grow,
+ // regardless of the temporary plateau.
+ clock_.AdvanceTime(interval);
+ reno_cwnd = RenoCwndInBytes(reno_cwnd);
+ ASSERT_EQ(current_cwnd,
+ cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd,
+ rtt_min, clock_.ApproximateNow()));
+ for (QuicPacketCount i = 1; i < max_acks; ++i) {
+ clock_.AdvanceTime(interval);
+ const QuicByteCount next_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ reno_cwnd = RenoCwndInBytes(reno_cwnd);
+ // The window shoud increase on every ack.
+ ASSERT_LT(current_cwnd, next_cwnd);
+ ASSERT_EQ(reno_cwnd, next_cwnd);
+ current_cwnd = next_cwnd;
+ }
+
+ // After all the acks are returned from the epoch, we expect the
+ // cwnd to have increased by nearly one packet. (Not exactly one
+ // packet, because our byte-wise Reno algorithm is always a slight
+ // under-estimation). Without per-ack updates, the current_cwnd
+ // would otherwise be unchanged.
+ const QuicByteCount minimum_expected_increase = kDefaultTCPMSS * .9;
+ EXPECT_LT(minimum_expected_increase + initial_cwnd, current_cwnd);
+}
+
+TEST_F(CubicBytesTest, LossEvents) {
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ QuicByteCount current_cwnd = 422 * kDefaultTCPMSS;
+ // Without the signed-integer, cubic-convex fix, we mistakenly
+ // increment cwnd after only one_ms_ and a single ack.
+ QuicPacketCount expected_cwnd = RenoCwndInBytes(current_cwnd);
+ // Initialize the state.
+ clock_.AdvanceTime(one_ms_);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd,
+ rtt_min, clock_.ApproximateNow()));
+
+ // On the first loss, the last max congestion window is set to the
+ // congestion window before the loss.
+ QuicByteCount pre_loss_cwnd = current_cwnd;
+ ASSERT_EQ(0u, LastMaxCongestionWindow());
+ expected_cwnd = static_cast<QuicByteCount>(current_cwnd * kNConnectionBeta);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+ ASSERT_EQ(pre_loss_cwnd, LastMaxCongestionWindow());
+ current_cwnd = expected_cwnd;
+
+ // On the second loss, the current congestion window has not yet
+ // reached the last max congestion window. The last max congestion
+ // window will be reduced by an additional backoff factor to allow
+ // for competition.
+ pre_loss_cwnd = current_cwnd;
+ expected_cwnd = static_cast<QuicByteCount>(current_cwnd * kNConnectionBeta);
+ ASSERT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+ current_cwnd = expected_cwnd;
+ EXPECT_GT(pre_loss_cwnd, LastMaxCongestionWindow());
+ QuicByteCount expected_last_max =
+ static_cast<QuicByteCount>(pre_loss_cwnd * kNConnectionBetaLastMax);
+ EXPECT_EQ(expected_last_max, LastMaxCongestionWindow());
+ EXPECT_LT(expected_cwnd, LastMaxCongestionWindow());
+ // Simulate an increase, and check that we are below the origin.
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ EXPECT_GT(LastMaxCongestionWindow(), current_cwnd);
+
+ // On the final loss, simulate the condition where the congestion
+ // window had a chance to grow nearly to the last congestion window.
+ current_cwnd = LastMaxCongestionWindow() - 1;
+ pre_loss_cwnd = current_cwnd;
+ expected_cwnd = static_cast<QuicByteCount>(current_cwnd * kNConnectionBeta);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+ expected_last_max = pre_loss_cwnd;
+ ASSERT_EQ(expected_last_max, LastMaxCongestionWindow());
+}
+
+TEST_F(CubicBytesTest, BelowOrigin) {
+ // Concave growth.
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ QuicByteCount current_cwnd = 422 * kDefaultTCPMSS;
+ // Without the signed-integer, cubic-convex fix, we mistakenly
+ // increment cwnd after only one_ms_ and a single ack.
+ QuicPacketCount expected_cwnd = RenoCwndInBytes(current_cwnd);
+ // Initialize the state.
+ clock_.AdvanceTime(one_ms_);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterAck(kDefaultTCPMSS, current_cwnd,
+ rtt_min, clock_.ApproximateNow()));
+ expected_cwnd = static_cast<QuicPacketCount>(current_cwnd * kNConnectionBeta);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+ current_cwnd = expected_cwnd;
+ // First update after loss to initialize the epoch.
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ // Cubic phase.
+ for (int i = 0; i < 40; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(
+ kDefaultTCPMSS, current_cwnd, rtt_min, clock_.ApproximateNow());
+ }
+ expected_cwnd = 553632;
+ EXPECT_EQ(expected_cwnd, current_cwnd);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc
new file mode 100644
index 00000000000..8cd5e712b3c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.cc
@@ -0,0 +1,236 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+namespace {
+
+// The minimum delay before a packet will be considered lost,
+// regardless of SRTT. Half of the minimum TLP, since the loss algorithm only
+// triggers when a nack has been receieved for the packet.
+static const size_t kMinLossDelayMs = 5;
+
+// Default fraction of an RTT the algorithm waits before determining a packet is
+// lost due to early retransmission by time based loss detection.
+static const int kDefaultLossDelayShift = 2;
+// Default fraction of an RTT when doing adaptive loss detection.
+static const int kDefaultAdaptiveLossDelayShift = 4;
+
+} // namespace
+
+GeneralLossAlgorithm::GeneralLossAlgorithm() : GeneralLossAlgorithm(kNack) {}
+
+GeneralLossAlgorithm::GeneralLossAlgorithm(LossDetectionType loss_type)
+ : loss_detection_timeout_(QuicTime::Zero()),
+ least_in_flight_(1),
+ packet_number_space_(NUM_PACKET_NUMBER_SPACES) {
+ SetLossDetectionType(loss_type);
+}
+
+void GeneralLossAlgorithm::SetLossDetectionType(LossDetectionType loss_type) {
+ loss_detection_timeout_ = QuicTime::Zero();
+ largest_sent_on_spurious_retransmit_.Clear();
+ loss_type_ = loss_type;
+ reordering_shift_ = loss_type == kAdaptiveTime
+ ? kDefaultAdaptiveLossDelayShift
+ : kDefaultLossDelayShift;
+ if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection) &&
+ loss_type == kTime) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_eighth_rtt_loss_detection);
+ reordering_shift_ = 3;
+ }
+ largest_previously_acked_.Clear();
+}
+
+LossDetectionType GeneralLossAlgorithm::GetLossDetectionType() const {
+ return loss_type_;
+}
+
+// Uses nack counts to decide when packets are lost.
+void GeneralLossAlgorithm::DetectLosses(
+ const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber largest_newly_acked,
+ const AckedPacketVector& packets_acked,
+ LostPacketVector* packets_lost) {
+ loss_detection_timeout_ = QuicTime::Zero();
+ if (!packets_acked.empty() &&
+ packets_acked.front().packet_number == least_in_flight_) {
+ if (least_in_flight_ + packets_acked.size() - 1 == largest_newly_acked) {
+ // Optimization for the case when no packet is missing.
+ least_in_flight_ = largest_newly_acked + 1;
+ largest_previously_acked_ = largest_newly_acked;
+ return;
+ }
+ // There is hole in acked_packets, increment least_in_flight_ if possible.
+ for (const auto& acked : packets_acked) {
+ if (acked.packet_number != least_in_flight_) {
+ break;
+ }
+ ++least_in_flight_;
+ }
+ }
+ QuicTime::Delta max_rtt =
+ std::max(rtt_stats.previous_srtt(), rtt_stats.latest_rtt());
+ QuicTime::Delta loss_delay =
+ std::max(QuicTime::Delta::FromMilliseconds(kMinLossDelayMs),
+ max_rtt + (max_rtt >> reordering_shift_));
+ QuicPacketNumber packet_number = unacked_packets.GetLeastUnacked();
+ auto it = unacked_packets.begin();
+ if (least_in_flight_.IsInitialized() && least_in_flight_ >= packet_number) {
+ if (least_in_flight_ > unacked_packets.largest_sent_packet() + 1) {
+ QUIC_BUG << "least_in_flight: " << least_in_flight_
+ << " is greater than largest_sent_packet + 1: "
+ << unacked_packets.largest_sent_packet() + 1;
+ } else {
+ it += (least_in_flight_ - packet_number);
+ packet_number = least_in_flight_;
+ }
+ }
+ // Clear least_in_flight_.
+ least_in_flight_.Clear();
+ DCHECK(!unacked_packets.use_uber_loss_algorithm() ||
+ packet_number_space_ ==
+ unacked_packets.GetPacketNumberSpace(largest_newly_acked));
+ for (; it != unacked_packets.end() && packet_number <= largest_newly_acked;
+ ++it, ++packet_number) {
+ if (unacked_packets.use_uber_loss_algorithm() &&
+ unacked_packets.GetPacketNumberSpace(it->encryption_level) !=
+ packet_number_space_) {
+ // Skip packets of different packet number space.
+ continue;
+ }
+ if (!it->in_flight) {
+ continue;
+ }
+
+ if (loss_type_ == kNack) {
+ // FACK based loss detection.
+ if (largest_newly_acked - packet_number >=
+ kNumberOfNacksBeforeRetransmission) {
+ packets_lost->push_back(LostPacket(packet_number, it->bytes_sent));
+ continue;
+ }
+ } else if (loss_type_ == kLazyFack) {
+ // Require two in order acks to invoke FACK, which avoids spuriously
+ // retransmitting packets when one packet is reordered by a large amount.
+ if (largest_previously_acked_.IsInitialized() &&
+ largest_newly_acked > largest_previously_acked_ &&
+ largest_previously_acked_ > packet_number &&
+ largest_previously_acked_ - packet_number >=
+ (kNumberOfNacksBeforeRetransmission - 1)) {
+ packets_lost->push_back(LostPacket(packet_number, it->bytes_sent));
+ continue;
+ }
+ }
+
+ // Only early retransmit(RFC5827) when the last packet gets acked and
+ // there are retransmittable packets in flight.
+ // This also implements a timer-protected variant of FACK.
+ QuicPacketNumber largest_sent_retransmittable_packet;
+ if (unacked_packets.use_uber_loss_algorithm()) {
+ // Use largest_sent_retransmittable_packet of corresponding packet number
+ // space for timer based loss detection.
+ largest_sent_retransmittable_packet =
+ unacked_packets.GetLargestSentRetransmittableOfPacketNumberSpace(
+ packet_number_space_);
+ } else {
+ largest_sent_retransmittable_packet =
+ unacked_packets.largest_sent_retransmittable_packet();
+ }
+ if (largest_sent_retransmittable_packet <= largest_newly_acked ||
+ loss_type_ == kTime || loss_type_ == kAdaptiveTime) {
+ QuicTime when_lost = it->sent_time + loss_delay;
+ if (time < when_lost) {
+ loss_detection_timeout_ = when_lost;
+ if (!least_in_flight_.IsInitialized()) {
+ // At this point, packet_number is in flight and not detected as lost.
+ least_in_flight_ = packet_number;
+ }
+ break;
+ }
+ packets_lost->push_back(LostPacket(packet_number, it->bytes_sent));
+ continue;
+ }
+
+ // NACK-based loss detection allows for a max reordering window of 1 RTT.
+ if (it->sent_time + rtt_stats.smoothed_rtt() <
+ unacked_packets.GetTransmissionInfo(largest_newly_acked).sent_time) {
+ packets_lost->push_back(LostPacket(packet_number, it->bytes_sent));
+ continue;
+ }
+ if (!least_in_flight_.IsInitialized()) {
+ // At this point, packet_number is in flight and not detected as lost.
+ least_in_flight_ = packet_number;
+ }
+ }
+ if (!least_in_flight_.IsInitialized()) {
+ // There is no in flight packet.
+ least_in_flight_ = largest_newly_acked + 1;
+ }
+ largest_previously_acked_ = largest_newly_acked;
+}
+
+QuicTime GeneralLossAlgorithm::GetLossTimeout() const {
+ return loss_detection_timeout_;
+}
+
+void GeneralLossAlgorithm::SpuriousRetransmitDetected(
+ const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber spurious_retransmission) {
+ if (loss_type_ != kAdaptiveTime || reordering_shift_ == 0) {
+ return;
+ }
+ // Calculate the extra time needed so this wouldn't have been declared lost.
+ // Extra time needed is based on how long it's been since the spurious
+ // retransmission was sent, because the SRTT and latest RTT may have changed.
+ QuicTime::Delta extra_time_needed =
+ time -
+ unacked_packets.GetTransmissionInfo(spurious_retransmission).sent_time;
+ // Increase the reordering fraction until enough time would be allowed.
+ QuicTime::Delta max_rtt =
+ std::max(rtt_stats.previous_srtt(), rtt_stats.latest_rtt());
+ if (GetQuicReloadableFlag(quic_fix_adaptive_time_loss)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_fix_adaptive_time_loss);
+ while ((max_rtt >> reordering_shift_) <= extra_time_needed &&
+ reordering_shift_ > 0) {
+ --reordering_shift_;
+ }
+ return;
+ }
+
+ if (largest_sent_on_spurious_retransmit_.IsInitialized() &&
+ spurious_retransmission <= largest_sent_on_spurious_retransmit_) {
+ return;
+ }
+ largest_sent_on_spurious_retransmit_ = unacked_packets.largest_sent_packet();
+ QuicTime::Delta proposed_extra_time(QuicTime::Delta::Zero());
+ do {
+ proposed_extra_time = max_rtt >> reordering_shift_;
+ --reordering_shift_;
+ } while (proposed_extra_time < extra_time_needed && reordering_shift_ > 0);
+}
+
+void GeneralLossAlgorithm::SetPacketNumberSpace(
+ PacketNumberSpace packet_number_space) {
+ if (packet_number_space_ < NUM_PACKET_NUMBER_SPACES) {
+ QUIC_BUG << "Cannot switch packet_number_space";
+ return;
+ }
+
+ packet_number_space_ = packet_number_space;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h
new file mode 100644
index 00000000000..319203a9bdc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h
@@ -0,0 +1,84 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_GENERAL_LOSS_ALGORITHM_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_GENERAL_LOSS_ALGORITHM_H_
+
+#include <algorithm>
+#include <map>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Class which can be configured to implement's TCP's approach of detecting loss
+// when 3 nacks have been received for a packet or with a time threshold.
+// Also implements TCP's early retransmit(RFC5827).
+class QUIC_EXPORT_PRIVATE GeneralLossAlgorithm : public LossDetectionInterface {
+ public:
+ // TCP retransmits after 3 nacks.
+ static const QuicPacketCount kNumberOfNacksBeforeRetransmission = 3;
+
+ GeneralLossAlgorithm();
+ explicit GeneralLossAlgorithm(LossDetectionType loss_type);
+ GeneralLossAlgorithm(const GeneralLossAlgorithm&) = delete;
+ GeneralLossAlgorithm& operator=(const GeneralLossAlgorithm&) = delete;
+ ~GeneralLossAlgorithm() override {}
+
+ LossDetectionType GetLossDetectionType() const override;
+
+ // Switches the loss detection type to |loss_type| and resets the loss
+ // algorithm.
+ void SetLossDetectionType(LossDetectionType loss_type);
+
+ // Uses |largest_acked| and time to decide when packets are lost.
+ void DetectLosses(const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber largest_newly_acked,
+ const AckedPacketVector& packets_acked,
+ LostPacketVector* packets_lost) override;
+
+ // Returns a non-zero value when the early retransmit timer is active.
+ QuicTime GetLossTimeout() const override;
+
+ // Increases the loss detection threshold for time loss detection.
+ void SpuriousRetransmitDetected(
+ const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber spurious_retransmission) override;
+
+ void SetPacketNumberSpace(PacketNumberSpace packet_number_space);
+
+ int reordering_shift() const { return reordering_shift_; }
+
+ private:
+ QuicTime loss_detection_timeout_;
+ // Largest sent packet when a spurious retransmit is detected.
+ // Prevents increasing the reordering threshold multiple times per epoch.
+ // TODO(ianswett): Deprecate when quic_fix_adaptive_time_loss flag is
+ // deprecated.
+ QuicPacketNumber largest_sent_on_spurious_retransmit_;
+ LossDetectionType loss_type_;
+ // Fraction of a max(SRTT, latest_rtt) to permit reordering before declaring
+ // loss. Fraction calculated by shifting max(SRTT, latest_rtt) to the right
+ // by reordering_shift.
+ int reordering_shift_;
+ // The largest newly acked from the previous call to DetectLosses.
+ QuicPacketNumber largest_previously_acked_;
+ // The least in flight packet. Loss detection should start from this. Please
+ // note, least_in_flight_ could be largest packet ever sent + 1.
+ QuicPacketNumber least_in_flight_;
+ // This is only used when quic_use_uber_loss_algorithm is true.
+ PacketNumberSpace packet_number_space_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_GENERAL_LOSS_ALGORITHM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc
new file mode 100644
index 00000000000..a2496793893
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm_test.cc
@@ -0,0 +1,578 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h"
+
+#include <algorithm>
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+// Default packet length.
+const uint32_t kDefaultLength = 1000;
+
+class GeneralLossAlgorithmTest : public QuicTest {
+ protected:
+ GeneralLossAlgorithmTest() : unacked_packets_(Perspective::IS_CLIENT) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(), clock_.Now());
+ EXPECT_LT(0, rtt_stats_.smoothed_rtt().ToMicroseconds());
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ loss_algorithm_.SetPacketNumberSpace(HANDSHAKE_DATA);
+ }
+ }
+
+ ~GeneralLossAlgorithmTest() override {}
+
+ void SendDataPacket(uint64_t packet_number) {
+ QuicStreamFrame frame;
+ frame.stream_id = QuicUtils::GetHeadersStreamId(
+ CurrentSupportedVersions()[0].transport_version);
+ SerializedPacket packet(QuicPacketNumber(packet_number),
+ PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+ false, false);
+ packet.retransmittable_frames.push_back(QuicFrame(frame));
+ unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+ NOT_RETRANSMISSION, clock_.Now(), true);
+ }
+
+ void SendAckPacket(uint64_t packet_number) {
+ SerializedPacket packet(QuicPacketNumber(packet_number),
+ PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+ true, false);
+ unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+ NOT_RETRANSMISSION, clock_.Now(), false);
+ }
+
+ void VerifyLosses(uint64_t largest_newly_acked,
+ const AckedPacketVector& packets_acked,
+ const std::vector<uint64_t>& losses_expected) {
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(largest_newly_acked));
+ } else if (!unacked_packets_.largest_acked().IsInitialized() ||
+ QuicPacketNumber(largest_newly_acked) >
+ unacked_packets_.largest_acked()) {
+ unacked_packets_.IncreaseLargestAcked(
+ QuicPacketNumber(largest_newly_acked));
+ }
+ LostPacketVector lost_packets;
+ loss_algorithm_.DetectLosses(unacked_packets_, clock_.Now(), rtt_stats_,
+ QuicPacketNumber(largest_newly_acked),
+ packets_acked, &lost_packets);
+ ASSERT_EQ(losses_expected.size(), lost_packets.size());
+ for (size_t i = 0; i < losses_expected.size(); ++i) {
+ EXPECT_EQ(lost_packets[i].packet_number,
+ QuicPacketNumber(losses_expected[i]));
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ GeneralLossAlgorithm loss_algorithm_;
+ RttStats rtt_stats_;
+ MockClock clock_;
+};
+
+TEST_F(GeneralLossAlgorithmTest, NackRetransmit1Packet) {
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ // No loss on one ack.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ // No loss on two acks.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(3), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(3, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ // Loss on three acks.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(4), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(4, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// A stretch ack is an ack that covers more than 1 packet of previously
+// unacknowledged data.
+TEST_F(GeneralLossAlgorithmTest, NackRetransmit1PacketWith1StretchAck) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ // Nack the first packet 3 times in a single StretchAck.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(3), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(4), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(4, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// Ack a packet 3 packets ahead, causing a retransmit.
+TEST_F(GeneralLossAlgorithmTest, NackRetransmit1PacketSingleAck) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ // Nack the first packet 3 times in an AckFrame with three missing packets.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(4), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(4, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(GeneralLossAlgorithmTest, EarlyRetransmit1Packet) {
+ const size_t kNumSentPackets = 2;
+ // Transmit 2 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout());
+
+ clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt());
+ VerifyLosses(2, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(GeneralLossAlgorithmTest, EarlyRetransmitAllPackets) {
+ const size_t kNumSentPackets = 5;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ // Advance the time 1/4 RTT between 3 and 4.
+ if (i == 3) {
+ clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
+ }
+ }
+ AckedPacketVector packets_acked;
+ // Early retransmit when the final packet gets acked and 1.25 RTTs have
+ // elapsed since the packets were sent.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(kNumSentPackets));
+ packets_acked.push_back(AckedPacket(QuicPacketNumber(kNumSentPackets),
+ kMaxOutgoingPacketSize,
+ QuicTime::Zero()));
+ // This simulates a single ack following multiple missing packets with FACK.
+ VerifyLosses(kNumSentPackets, packets_acked, {1, 2});
+ packets_acked.clear();
+ // The time has already advanced 1/4 an RTT, so ensure the timeout is set
+ // 1.25 RTTs after the earliest pending packet(3), not the last(4).
+ EXPECT_EQ(clock_.Now() + rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout());
+
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
+ VerifyLosses(kNumSentPackets, packets_acked, {3});
+ EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout());
+ clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
+ VerifyLosses(kNumSentPackets, packets_acked, {4});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(GeneralLossAlgorithmTest, DontEarlyRetransmitNeuteredPacket) {
+ const size_t kNumSentPackets = 2;
+ // Transmit 2 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ // Neuter packet 1.
+ unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1));
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
+
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(2));
+ } else {
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+ }
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(GeneralLossAlgorithmTest, EarlyRetransmitWithLargerUnackablePackets) {
+ // Transmit 2 data packets and one ack.
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendAckPacket(3);
+ AckedPacketVector packets_acked;
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
+
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(2));
+ } else {
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+ }
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ EXPECT_EQ(clock_.Now() + 0.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout());
+
+ // The packet should be lost once the loss timeout is reached.
+ clock_.AdvanceTime(0.25 * rtt_stats_.latest_rtt());
+ VerifyLosses(2, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(GeneralLossAlgorithmTest, AlwaysLosePacketSent1RTTEarlier) {
+ // Transmit 1 packet and then wait an rtt plus 1ms.
+ SendDataPacket(1);
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt() +
+ QuicTime::Delta::FromMilliseconds(1));
+
+ // Transmit 2 packets.
+ SendDataPacket(2);
+ SendDataPacket(3);
+ AckedPacketVector packets_acked;
+ // Wait another RTT and ack 2.
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(2));
+ } else {
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+ }
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(2, packets_acked, {1});
+}
+
+// NoFack loss detection tests.
+TEST_F(GeneralLossAlgorithmTest, LazyFackNackRetransmit1Packet) {
+ loss_algorithm_.SetLossDetectionType(kLazyFack);
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ // No loss on one ack.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ // No loss on two acks.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(3), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(3, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ // Loss on three acks.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(4), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(4, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// A stretch ack is an ack that covers more than 1 packet of previously
+// unacknowledged data.
+TEST_F(GeneralLossAlgorithmTest,
+ LazyFackNoNackRetransmit1PacketWith1StretchAck) {
+ loss_algorithm_.SetLossDetectionType(kLazyFack);
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ // Nack the first packet 3 times in a single StretchAck.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(3), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(4), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(4, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ // The timer isn't set because we expect more acks.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // Process another ack and then packet 1 will be lost.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(5), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(5, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// Ack a packet 3 packets ahead does not cause a retransmit.
+TEST_F(GeneralLossAlgorithmTest, LazyFackNackRetransmit1PacketSingleAck) {
+ loss_algorithm_.SetLossDetectionType(kLazyFack);
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ // Nack the first packet 3 times in an AckFrame with three missing packets.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(4), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(4, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ // The timer isn't set because we expect more acks.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // Process another ack and then packet 1 and 2 will be lost.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(5), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(5, packets_acked, {1, 2});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// Time-based loss detection tests.
+TEST_F(GeneralLossAlgorithmTest, NoLossFor500Nacks) {
+ loss_algorithm_.SetLossDetectionType(kTime);
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ for (size_t i = 1; i < 500; ++i) {
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ }
+ if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) {
+ EXPECT_EQ(1.125 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ } else {
+ EXPECT_EQ(1.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ }
+}
+
+TEST_F(GeneralLossAlgorithmTest, NoLossUntilTimeout) {
+ loss_algorithm_.SetLossDetectionType(kTime);
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(0.1 * rtt_stats_.smoothed_rtt());
+ }
+ AckedPacketVector packets_acked;
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) {
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(0.125 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ } else {
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(0.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ }
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
+ VerifyLosses(2, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(GeneralLossAlgorithmTest, NoLossWithoutNack) {
+ loss_algorithm_.SetLossDetectionType(kTime);
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(0.1 * rtt_stats_.smoothed_rtt());
+ }
+ AckedPacketVector packets_acked;
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost without a nack.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(1), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(1, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ // The timer should still not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
+ VerifyLosses(1, packets_acked, std::vector<uint64_t>{});
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
+ VerifyLosses(1, packets_acked, std::vector<uint64_t>{});
+
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(GeneralLossAlgorithmTest, MultipleLossesAtOnce) {
+ loss_algorithm_.SetLossDetectionType(kTime);
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at once and then go forward an RTT.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(10));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(10), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(10, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) {
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(0.125 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ } else {
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(0.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ }
+ clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
+ VerifyLosses(10, packets_acked, {1, 2, 3, 4, 5, 6, 7, 8, 9});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(GeneralLossAlgorithmTest, NoSpuriousLossesFromLargeReordering) {
+ loss_algorithm_.SetLossDetectionType(kTime);
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at once and then go forward an RTT.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ AckedPacketVector packets_acked;
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt());
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(10));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(10), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(10, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ if (GetQuicReloadableFlag(quic_eighth_rtt_loss_detection)) {
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(0.125 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ } else {
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(0.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ }
+ clock_.AdvanceTime(0.25 * rtt_stats_.smoothed_rtt());
+ // Now ack packets 1 to 9 and ensure the timer is no longer set and no packets
+ // are lost.
+ for (uint64_t i = 1; i <= 9; ++i) {
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(i));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(i), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(i, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ }
+}
+
+TEST_F(GeneralLossAlgorithmTest, IncreaseThresholdUponSpuriousLoss) {
+ loss_algorithm_.SetLossDetectionType(kAdaptiveTime);
+ EXPECT_EQ(4, loss_algorithm_.reordering_shift());
+ const size_t kNumSentPackets = 10;
+ // Transmit 2 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(0.1 * rtt_stats_.smoothed_rtt());
+ }
+ EXPECT_EQ(QuicTime::Zero() + rtt_stats_.smoothed_rtt(), clock_.Now());
+ AckedPacketVector packets_acked;
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // Packet 1 should not be lost until 1/16 RTTs pass.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ packets_acked.push_back(AckedPacket(
+ QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ packets_acked.clear();
+ // Expect the timer to be set to 1/16 RTT's in the future.
+ EXPECT_EQ(rtt_stats_.smoothed_rtt() * (1.0f / 16),
+ loss_algorithm_.GetLossTimeout() - clock_.Now());
+ VerifyLosses(2, packets_acked, std::vector<uint64_t>{});
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt() * (1.0f / 16));
+ VerifyLosses(2, packets_acked, {1});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // Retransmit packet 1 as 11 and 2 as 12.
+ SendDataPacket(11);
+ SendDataPacket(12);
+
+ // Advance the time 1/4 RTT and indicate the loss was spurious.
+ // The new threshold should be 1/2 RTT.
+ clock_.AdvanceTime(rtt_stats_.smoothed_rtt() * (1.0f / 4));
+ if (GetQuicReloadableFlag(quic_fix_adaptive_time_loss)) {
+ // The flag fixes an issue where adaptive time loss would increase the
+ // reordering threshold by an extra factor of two.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ }
+ loss_algorithm_.SpuriousRetransmitDetected(unacked_packets_, clock_.Now(),
+ rtt_stats_, QuicPacketNumber(11));
+ EXPECT_EQ(1, loss_algorithm_.reordering_shift());
+
+ // Detect another spurious retransmit and ensure the threshold doesn't
+ // increase again.
+ loss_algorithm_.SpuriousRetransmitDetected(unacked_packets_, clock_.Now(),
+ rtt_stats_, QuicPacketNumber(12));
+ EXPECT_EQ(1, loss_algorithm_.reordering_shift());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.cc
new file mode 100644
index 00000000000..28963b11105
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.h"
+
+#include <algorithm>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+// Note(pwestin): the magic clamping numbers come from the original code in
+// tcp_cubic.c.
+const int64_t kHybridStartLowWindow = 16;
+// Number of delay samples for detecting the increase of delay.
+const uint32_t kHybridStartMinSamples = 8;
+// Exit slow start if the min rtt has increased by more than 1/8th.
+const int kHybridStartDelayFactorExp = 3; // 2^3 = 8
+// The original paper specifies 2 and 8ms, but those have changed over time.
+const int64_t kHybridStartDelayMinThresholdUs = 4000;
+const int64_t kHybridStartDelayMaxThresholdUs = 16000;
+
+HybridSlowStart::HybridSlowStart()
+ : started_(false),
+ hystart_found_(NOT_FOUND),
+ rtt_sample_count_(0),
+ current_min_rtt_(QuicTime::Delta::Zero()) {}
+
+void HybridSlowStart::OnPacketAcked(QuicPacketNumber acked_packet_number) {
+ // OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end
+ // the round when the final packet of the burst is received and start it on
+ // the next incoming ack.
+ if (IsEndOfRound(acked_packet_number)) {
+ started_ = false;
+ }
+}
+
+void HybridSlowStart::OnPacketSent(QuicPacketNumber packet_number) {
+ last_sent_packet_number_ = packet_number;
+}
+
+void HybridSlowStart::Restart() {
+ started_ = false;
+ hystart_found_ = NOT_FOUND;
+}
+
+void HybridSlowStart::StartReceiveRound(QuicPacketNumber last_sent) {
+ QUIC_DVLOG(1) << "Reset hybrid slow start @" << last_sent;
+ end_packet_number_ = last_sent;
+ current_min_rtt_ = QuicTime::Delta::Zero();
+ rtt_sample_count_ = 0;
+ started_ = true;
+}
+
+bool HybridSlowStart::IsEndOfRound(QuicPacketNumber ack) const {
+ return !end_packet_number_.IsInitialized() || end_packet_number_ <= ack;
+}
+
+bool HybridSlowStart::ShouldExitSlowStart(QuicTime::Delta latest_rtt,
+ QuicTime::Delta min_rtt,
+ QuicPacketCount congestion_window) {
+ if (!started_) {
+ // Time to start the hybrid slow start.
+ StartReceiveRound(last_sent_packet_number_);
+ }
+ if (hystart_found_ != NOT_FOUND) {
+ return true;
+ }
+ // Second detection parameter - delay increase detection.
+ // Compare the minimum delay (current_min_rtt_) of the current
+ // burst of packets relative to the minimum delay during the session.
+ // Note: we only look at the first few(8) packets in each burst, since we
+ // only want to compare the lowest RTT of the burst relative to previous
+ // bursts.
+ rtt_sample_count_++;
+ if (rtt_sample_count_ <= kHybridStartMinSamples) {
+ if (current_min_rtt_.IsZero() || current_min_rtt_ > latest_rtt) {
+ current_min_rtt_ = latest_rtt;
+ }
+ }
+ // We only need to check this once per round.
+ if (rtt_sample_count_ == kHybridStartMinSamples) {
+ // Divide min_rtt by 8 to get a rtt increase threshold for exiting.
+ int64_t min_rtt_increase_threshold_us =
+ min_rtt.ToMicroseconds() >> kHybridStartDelayFactorExp;
+ // Ensure the rtt threshold is never less than 2ms or more than 16ms.
+ min_rtt_increase_threshold_us = std::min(min_rtt_increase_threshold_us,
+ kHybridStartDelayMaxThresholdUs);
+ QuicTime::Delta min_rtt_increase_threshold =
+ QuicTime::Delta::FromMicroseconds(std::max(
+ min_rtt_increase_threshold_us, kHybridStartDelayMinThresholdUs));
+
+ if (current_min_rtt_ > min_rtt + min_rtt_increase_threshold) {
+ hystart_found_ = DELAY;
+ }
+ }
+ // Exit from slow start if the cwnd is greater than 16 and
+ // increasing delay is found.
+ return congestion_window >= kHybridStartLowWindow &&
+ hystart_found_ != NOT_FOUND;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.h
new file mode 100644
index 00000000000..0017500482e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This class is a helper class to TcpCubicSender.
+// Slow start is the initial startup phase of TCP, it lasts until first packet
+// loss. This class implements hybrid slow start of the TCP cubic send side
+// congestion algorithm. The key feaure of hybrid slow start is that it tries to
+// avoid running into the wall too hard during the slow start phase, which
+// the traditional TCP implementation does.
+// This does not implement ack train detection because it interacts poorly with
+// pacing.
+// http://netsrv.csc.ncsu.edu/export/hybridstart_pfldnet08.pdf
+// http://research.csc.ncsu.edu/netsrv/sites/default/files/hystart_techreport_2008.pdf
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_HYBRID_SLOW_START_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_HYBRID_SLOW_START_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE HybridSlowStart {
+ public:
+ HybridSlowStart();
+ HybridSlowStart(const HybridSlowStart&) = delete;
+ HybridSlowStart& operator=(const HybridSlowStart&) = delete;
+
+ void OnPacketAcked(QuicPacketNumber acked_packet_number);
+
+ void OnPacketSent(QuicPacketNumber packet_number);
+
+ // ShouldExitSlowStart should be called on every new ack frame, since a new
+ // RTT measurement can be made then.
+ // rtt: the RTT for this ack packet.
+ // min_rtt: is the lowest delay (RTT) we have seen during the session.
+ // congestion_window: the congestion window in packets.
+ bool ShouldExitSlowStart(QuicTime::Delta rtt,
+ QuicTime::Delta min_rtt,
+ QuicPacketCount congestion_window);
+
+ // Start a new slow start phase.
+ void Restart();
+
+ // TODO(ianswett): The following methods should be private, but that requires
+ // a follow up CL to update the unit test.
+ // Returns true if this ack the last packet number of our current slow start
+ // round.
+ // Call Reset if this returns true.
+ bool IsEndOfRound(QuicPacketNumber ack) const;
+
+ // Call for the start of each receive round (burst) in the slow start phase.
+ void StartReceiveRound(QuicPacketNumber last_sent);
+
+ // Whether slow start has started.
+ bool started() const { return started_; }
+
+ private:
+ // Whether a condition for exiting slow start has been found.
+ enum HystartState {
+ NOT_FOUND,
+ DELAY, // Too much increase in the round's min_rtt was observed.
+ };
+
+ // Whether the hybrid slow start has been started.
+ bool started_;
+ HystartState hystart_found_;
+ // Last packet number sent which was CWND limited.
+ QuicPacketNumber last_sent_packet_number_;
+
+ // Variables for tracking acks received during a slow start round.
+ QuicPacketNumber end_packet_number_; // End of the receive round.
+ uint32_t rtt_sample_count_; // Number of rtt samples in the current round.
+ QuicTime::Delta current_min_rtt_; // The minimum rtt of current round.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_HYBRID_SLOW_START_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start_test.cc
new file mode 100644
index 00000000000..aa2f849f3a9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start_test.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.h"
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class HybridSlowStartTest : public QuicTest {
+ protected:
+ HybridSlowStartTest()
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ rtt_(QuicTime::Delta::FromMilliseconds(60)) {}
+ void SetUp() override { slow_start_ = QuicMakeUnique<HybridSlowStart>(); }
+ const QuicTime::Delta one_ms_;
+ const QuicTime::Delta rtt_;
+ std::unique_ptr<HybridSlowStart> slow_start_;
+};
+
+TEST_F(HybridSlowStartTest, Simple) {
+ QuicPacketNumber packet_number(1);
+ QuicPacketNumber end_packet_number(3);
+ slow_start_->StartReceiveRound(end_packet_number);
+
+ EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number++));
+
+ // Test duplicates.
+ EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number));
+
+ EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number++));
+ EXPECT_TRUE(slow_start_->IsEndOfRound(packet_number++));
+
+ // Test without a new registered end_packet_number;
+ EXPECT_TRUE(slow_start_->IsEndOfRound(packet_number++));
+
+ end_packet_number = QuicPacketNumber(20);
+ slow_start_->StartReceiveRound(end_packet_number);
+ while (packet_number < end_packet_number) {
+ EXPECT_FALSE(slow_start_->IsEndOfRound(packet_number++));
+ }
+ EXPECT_TRUE(slow_start_->IsEndOfRound(packet_number++));
+}
+
+TEST_F(HybridSlowStartTest, Delay) {
+ // We expect to detect the increase at +1/8 of the RTT; hence at a typical
+ // RTT of 60ms the detection will happen at 67.5 ms.
+ const int kHybridStartMinSamples = 8; // Number of acks required to trigger.
+
+ QuicPacketNumber end_packet_number(1);
+ slow_start_->StartReceiveRound(end_packet_number++);
+
+ // Will not trigger since our lowest RTT in our burst is the same as the long
+ // term RTT provided.
+ for (int n = 0; n < kHybridStartMinSamples; ++n) {
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(
+ rtt_ + QuicTime::Delta::FromMilliseconds(n), rtt_, 100));
+ }
+ slow_start_->StartReceiveRound(end_packet_number++);
+ for (int n = 1; n < kHybridStartMinSamples; ++n) {
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(
+ rtt_ + QuicTime::Delta::FromMilliseconds(n + 10), rtt_, 100));
+ }
+ // Expect to trigger since all packets in this burst was above the long term
+ // RTT provided.
+ EXPECT_TRUE(slow_start_->ShouldExitSlowStart(
+ rtt_ + QuicTime::Delta::FromMilliseconds(10), rtt_, 100));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h
new file mode 100644
index 00000000000..7439d3f5e36
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The pure virtual class for send side loss detection algorithm.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicUnackedPacketMap;
+class RttStats;
+
+class QUIC_EXPORT_PRIVATE LossDetectionInterface {
+ public:
+ virtual ~LossDetectionInterface() {}
+
+ virtual LossDetectionType GetLossDetectionType() const = 0;
+
+ // Called when a new ack arrives or the loss alarm fires.
+ virtual void DetectLosses(const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber largest_newly_acked,
+ const AckedPacketVector& packets_acked,
+ LostPacketVector* packets_lost) = 0;
+
+ // Get the time the LossDetectionAlgorithm wants to re-evaluate losses.
+ // Returns QuicTime::Zero if no alarm needs to be set.
+ virtual QuicTime GetLossTimeout() const = 0;
+
+ // Called when a |spurious_retransmission| is detected. The original
+ // transmission must have been caused by DetectLosses.
+ virtual void SpuriousRetransmitDetected(
+ const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber spurious_retransmission) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
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
new file mode 100644
index 00000000000..45e8bae6fdc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+namespace {
+
+// Configured maximum size of the burst coming out of quiescence. The burst
+// is never larger than the current CWND in packets.
+static const uint32_t kInitialUnpacedBurst = 10;
+
+} // namespace
+
+PacingSender::PacingSender()
+ : sender_(nullptr),
+ max_pacing_rate_(QuicBandwidth::Zero()),
+ burst_tokens_(kInitialUnpacedBurst),
+ ideal_next_packet_send_time_(QuicTime::Zero()),
+ initial_burst_size_(kInitialUnpacedBurst),
+ lumpy_tokens_(0),
+ alarm_granularity_(QuicTime::Delta::FromMilliseconds(1)),
+ pacing_limited_(false) {
+ if (GetQuicReloadableFlag(quic_donot_reset_ideal_next_packet_send_time)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_donot_reset_ideal_next_packet_send_time);
+ }
+}
+
+PacingSender::~PacingSender() {}
+
+void PacingSender::set_sender(SendAlgorithmInterface* sender) {
+ DCHECK(sender != nullptr);
+ sender_ = sender;
+}
+
+void PacingSender::OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets) {
+ DCHECK(sender_ != nullptr);
+ if (!lost_packets.empty()) {
+ // Clear any burst tokens when entering recovery.
+ burst_tokens_ = 0;
+ }
+ sender_->OnCongestionEvent(rtt_updated, bytes_in_flight, event_time,
+ acked_packets, lost_packets);
+}
+
+void PacingSender::OnPacketSent(
+ QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ HasRetransmittableData has_retransmittable_data) {
+ DCHECK(sender_ != nullptr);
+ sender_->OnPacketSent(sent_time, bytes_in_flight, packet_number, bytes,
+ has_retransmittable_data);
+ if (has_retransmittable_data != HAS_RETRANSMITTABLE_DATA) {
+ return;
+ }
+ // If in recovery, the connection is not coming out of quiescence.
+ if (bytes_in_flight == 0 && !sender_->InRecovery()) {
+ // Add more burst tokens anytime the connection is leaving quiescence, but
+ // limit it to the equivalent of a single bulk write, not exceeding the
+ // current CWND in packets.
+ burst_tokens_ = std::min(
+ initial_burst_size_,
+ static_cast<uint32_t>(sender_->GetCongestionWindow() / kDefaultTCPMSS));
+ }
+ if (burst_tokens_ > 0) {
+ --burst_tokens_;
+ if (!GetQuicReloadableFlag(quic_donot_reset_ideal_next_packet_send_time)) {
+ ideal_next_packet_send_time_ = QuicTime::Zero();
+ }
+ pacing_limited_ = false;
+ return;
+ }
+ // The next packet should be sent as soon as the current packet has been
+ // transferred. PacingRate is based on bytes in flight including this packet.
+ QuicTime::Delta delay =
+ PacingRate(bytes_in_flight + bytes).TransferTime(bytes);
+ if (!pacing_limited_ || lumpy_tokens_ == 0) {
+ // Reset lumpy_tokens_ if either application or cwnd throttles sending or
+ // token runs out.
+ lumpy_tokens_ = std::max(
+ 1u, std::min(static_cast<uint32_t>(
+ GetQuicFlag(FLAGS_quic_lumpy_pacing_size)),
+ static_cast<uint32_t>(
+ (sender_->GetCongestionWindow() *
+ GetQuicFlag(FLAGS_quic_lumpy_pacing_cwnd_fraction)) /
+ kDefaultTCPMSS)));
+ if (GetQuicReloadableFlag(quic_no_lumpy_pacing_at_low_bw) &&
+ sender_->BandwidthEstimate() <
+ QuicBandwidth::FromKBitsPerSecond(1200)) {
+ // Below 1.2Mbps, send 1 packet at once, because one full-sized packet
+ // is about 10ms of queueing.
+ lumpy_tokens_ = 1u;
+ }
+ }
+ --lumpy_tokens_;
+ if (pacing_limited_) {
+ // Make up for lost time since pacing throttles the sending.
+ ideal_next_packet_send_time_ = ideal_next_packet_send_time_ + delay;
+ } else {
+ ideal_next_packet_send_time_ =
+ std::max(ideal_next_packet_send_time_ + delay, sent_time + delay);
+ }
+ // Stop making up for lost time if underlying sender prevents sending.
+ pacing_limited_ = sender_->CanSend(bytes_in_flight + bytes);
+}
+
+void PacingSender::OnApplicationLimited() {
+ // The send is application limited, stop making up for lost time.
+ pacing_limited_ = false;
+}
+
+QuicTime::Delta PacingSender::TimeUntilSend(
+ QuicTime now,
+ QuicByteCount bytes_in_flight) const {
+ DCHECK(sender_ != nullptr);
+
+ if (!sender_->CanSend(bytes_in_flight)) {
+ // The underlying sender prevents sending.
+ return QuicTime::Delta::Infinite();
+ }
+
+ if (burst_tokens_ > 0 || bytes_in_flight == 0 || lumpy_tokens_ > 0) {
+ // Don't pace if we have burst tokens available or leaving quiescence.
+ return QuicTime::Delta::Zero();
+ }
+
+ // If the next send time is within the alarm granularity, send immediately.
+ if (ideal_next_packet_send_time_ > now + alarm_granularity_) {
+ QUIC_DVLOG(1) << "Delaying packet: "
+ << (ideal_next_packet_send_time_ - now).ToMicroseconds();
+ return ideal_next_packet_send_time_ - now;
+ }
+
+ QUIC_DVLOG(1) << "Sending packet now. ideal_next_packet_send_time: "
+ << ideal_next_packet_send_time_ << ", now: " << now;
+ return QuicTime::Delta::Zero();
+}
+
+QuicBandwidth PacingSender::PacingRate(QuicByteCount bytes_in_flight) const {
+ DCHECK(sender_ != nullptr);
+ if (!max_pacing_rate_.IsZero()) {
+ return QuicBandwidth::FromBitsPerSecond(
+ std::min(max_pacing_rate_.ToBitsPerSecond(),
+ sender_->PacingRate(bytes_in_flight).ToBitsPerSecond()));
+ }
+ return sender_->PacingRate(bytes_in_flight);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h
new file mode 100644
index 00000000000..8420c7f1650
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A send algorithm that adds pacing on top of an another send algorithm.
+// It uses the underlying sender's pacing rate to schedule packets.
+// It also takes into consideration the expected granularity of the underlying
+// alarm to ensure that alarms are not set too aggressively, and err towards
+// sending packets too early instead of too late.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_PACING_SENDER_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_PACING_SENDER_H_
+
+#include <cstdint>
+#include <map>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class QuicSentPacketManagerPeer;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE PacingSender {
+ public:
+ PacingSender();
+ PacingSender(const PacingSender&) = delete;
+ PacingSender& operator=(const PacingSender&) = delete;
+ ~PacingSender();
+
+ // Sets the underlying sender. Does not take ownership of |sender|. |sender|
+ // must not be null. This must be called before any of the
+ // SendAlgorithmInterface wrapper methods are called.
+ void set_sender(SendAlgorithmInterface* sender);
+
+ void set_max_pacing_rate(QuicBandwidth max_pacing_rate) {
+ max_pacing_rate_ = max_pacing_rate;
+ }
+
+ void set_alarm_granularity(QuicTime::Delta alarm_granularity) {
+ alarm_granularity_ = alarm_granularity;
+ }
+
+ QuicBandwidth max_pacing_rate() const { return max_pacing_rate_; }
+
+ void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets);
+
+ void OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ HasRetransmittableData has_retransmittable_data);
+
+ // Called when application throttles the sending, so that pacing sender stops
+ // making up for lost time.
+ void OnApplicationLimited();
+
+ QuicTime::Delta TimeUntilSend(QuicTime now,
+ QuicByteCount bytes_in_flight) const;
+
+ QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const;
+
+ QuicTime ideal_next_packet_send_time() const {
+ return ideal_next_packet_send_time_;
+ }
+
+ private:
+ friend class test::QuicSentPacketManagerPeer;
+
+ // Underlying sender. Not owned.
+ SendAlgorithmInterface* sender_;
+ // If not QuicBandidth::Zero, the maximum rate the PacingSender will use.
+ QuicBandwidth max_pacing_rate_;
+
+ // Number of unpaced packets to be sent before packets are delayed.
+ uint32_t burst_tokens_;
+ QuicTime ideal_next_packet_send_time_; // When can the next packet be sent.
+ uint32_t initial_burst_size_;
+
+ // Number of unpaced packets to be sent before packets are delayed. This token
+ // is consumed after burst_tokens_ ran out.
+ uint32_t lumpy_tokens_;
+
+ // If the next send time is within alarm_granularity_, send immediately.
+ // TODO(fayang): Remove alarm_granularity_ when deprecating
+ // quic_offload_pacing_to_usps2 flag.
+ QuicTime::Delta alarm_granularity_;
+
+ // Indicates whether pacing throttles the sending. If true, make up for lost
+ // time.
+ bool pacing_limited_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_PACING_SENDER_H_
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
new file mode 100644
index 00000000000..ea9debf302d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/pacing_sender_test.cc
@@ -0,0 +1,471 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h"
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::AtMost;
+using testing::IsEmpty;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+
+const QuicByteCount kBytesInFlight = 1024;
+const int kInitialBurstPackets = 10;
+
+class PacingSenderTest : public QuicTest {
+ protected:
+ PacingSenderTest()
+ : zero_time_(QuicTime::Delta::Zero()),
+ infinite_time_(QuicTime::Delta::Infinite()),
+ packet_number_(1),
+ mock_sender_(new StrictMock<MockSendAlgorithm>()),
+ pacing_sender_(new PacingSender) {
+ pacing_sender_->set_sender(mock_sender_.get());
+ // Pick arbitrary time.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(9));
+ }
+
+ ~PacingSenderTest() override {}
+
+ void InitPacingRate(QuicPacketCount burst_size, QuicBandwidth bandwidth) {
+ mock_sender_ = QuicMakeUnique<StrictMock<MockSendAlgorithm>>();
+ pacing_sender_ = QuicMakeUnique<PacingSender>();
+ pacing_sender_->set_sender(mock_sender_.get());
+ EXPECT_CALL(*mock_sender_, PacingRate(_)).WillRepeatedly(Return(bandwidth));
+ EXPECT_CALL(*mock_sender_, BandwidthEstimate())
+ .WillRepeatedly(Return(bandwidth));
+ if (burst_size == 0) {
+ EXPECT_CALL(*mock_sender_, OnCongestionEvent(_, _, _, _, _));
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(1), kMaxOutgoingPacketSize));
+ AckedPacketVector empty;
+ pacing_sender_->OnCongestionEvent(true, 1234, clock_.Now(), empty,
+ lost_packets);
+ } else if (burst_size != kInitialBurstPackets) {
+ QUIC_LOG(FATAL) << "Unsupported burst_size " << burst_size
+ << " specificied, only 0 and " << kInitialBurstPackets
+ << " are supported.";
+ }
+ }
+
+ void CheckPacketIsSentImmediately(HasRetransmittableData retransmittable_data,
+ QuicByteCount bytes_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))
+ .WillOnce(Return(true));
+ // Verify that the packet can be sent immediately.
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(), bytes_in_flight));
+ }
+
+ // Actually send the packet.
+ if (bytes_in_flight == 0) {
+ EXPECT_CALL(*mock_sender_, InRecovery()).WillOnce(Return(in_recovery));
+ }
+ EXPECT_CALL(*mock_sender_,
+ OnPacketSent(clock_.Now(), bytes_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))
+ .Times(AtMost(1))
+ .WillRepeatedly(Return(!cwnd_limited));
+ pacing_sender_->OnPacketSent(clock_.Now(), bytes_in_flight,
+ packet_number_++, kMaxOutgoingPacketSize,
+ retransmittable_data);
+ }
+
+ void CheckPacketIsSentImmediately() {
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight,
+ false, false, 10);
+ }
+
+ void CheckPacketIsDelayed(QuicTime::Delta delay) {
+ // 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(kBytesInFlight))
+ .WillOnce(Return(true));
+ // Verify that the packet is delayed.
+ EXPECT_EQ(delay.ToMicroseconds(),
+ pacing_sender_->TimeUntilSend(clock_.Now(), kBytesInFlight)
+ .ToMicroseconds());
+ }
+ }
+
+ void UpdateRtt() {
+ EXPECT_CALL(*mock_sender_,
+ OnCongestionEvent(true, kBytesInFlight, _, _, _));
+ AckedPacketVector empty_acked;
+ LostPacketVector empty_lost;
+ pacing_sender_->OnCongestionEvent(true, kBytesInFlight, clock_.Now(),
+ empty_acked, empty_lost);
+ }
+
+ void OnApplicationLimited() { pacing_sender_->OnApplicationLimited(); }
+
+ const QuicTime::Delta zero_time_;
+ const QuicTime::Delta infinite_time_;
+ MockClock clock_;
+ QuicPacketNumber packet_number_;
+ std::unique_ptr<StrictMock<MockSendAlgorithm>> mock_sender_;
+ std::unique_ptr<PacingSender> pacing_sender_;
+};
+
+TEST_F(PacingSenderTest, NoSend) {
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, CanSend(kBytesInFlight)).WillOnce(Return(false));
+ EXPECT_EQ(infinite_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(), kBytesInFlight));
+ }
+}
+
+TEST_F(PacingSenderTest, SendNow) {
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, CanSend(kBytesInFlight)).WillOnce(Return(true));
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(), kBytesInFlight));
+ }
+}
+
+TEST_F(PacingSenderTest, VariousSending) {
+ // Configure pacing rate of 1 packet per 1 ms, no initial burst.
+ InitPacingRate(
+ 0, QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxOutgoingPacketSize, QuicTime::Delta::FromMilliseconds(1)));
+
+ // Now update the RTT and verify that packets are actually paced.
+ UpdateRtt();
+
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 2.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up on time.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up late.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(4));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up really late.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up really late again, but application pause partway through.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ OnApplicationLimited();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+ // Wake up early, but after enough time has passed to permit a send.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ CheckPacketIsSentImmediately();
+}
+
+TEST_F(PacingSenderTest, InitialBurst) {
+ // Configure pacing rate of 1 packet per 1 ms.
+ InitPacingRate(
+ 10, QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxOutgoingPacketSize, QuicTime::Delta::FromMilliseconds(1)));
+
+ // Update the RTT and verify that the first 10 packets aren't paced.
+ UpdateRtt();
+
+ // Send 10 packets, and verify that they are not paced.
+ for (int i = 0; i < kInitialBurstPackets; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 2ms.
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ CheckPacketIsSentImmediately();
+
+ // 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);
+ for (int i = 0; i < kInitialBurstPackets - 1; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 2ms.
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+}
+
+TEST_F(PacingSenderTest, InitialBurstNoRttMeasurement) {
+ // Configure pacing rate of 1 packet per 1 ms.
+ InitPacingRate(
+ 10, QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxOutgoingPacketSize, QuicTime::Delta::FromMilliseconds(1)));
+
+ // Send 10 packets, and verify that they are not paced.
+ for (int i = 0; i < kInitialBurstPackets; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 2ms.
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ CheckPacketIsSentImmediately();
+
+ // 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);
+ // Send 10 packets, and verify that they are not paced.
+ for (int i = 0; i < kInitialBurstPackets - 1; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 2ms.
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+}
+
+TEST_F(PacingSenderTest, FastSending) {
+ // Ensure the pacing sender paces, even when the inter-packet spacing is less
+ // than the pacing granularity.
+ InitPacingRate(10, QuicBandwidth::FromBytesAndTimeDelta(
+ 2 * kMaxOutgoingPacketSize,
+ QuicTime::Delta::FromMilliseconds(1)));
+ // Update the RTT and verify that the first 10 packets aren't paced.
+ UpdateRtt();
+
+ // Send 10 packets, and verify that they are not paced.
+ for (int i = 0; i < kInitialBurstPackets; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", since it's 2 packets/ms, so the delay should be 1.5ms.
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(1500));
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ CheckPacketIsSentImmediately();
+
+ // 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);
+ for (int i = 0; i < kInitialBurstPackets - 1; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 1.5ms.
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(1500));
+}
+
+TEST_F(PacingSenderTest, NoBurstEnteringRecovery) {
+ // Configure pacing rate of 1 packet per 1 ms with no burst tokens.
+ InitPacingRate(
+ 0, QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxOutgoingPacketSize, QuicTime::Delta::FromMilliseconds(1)));
+ // Sending a packet will set burst tokens.
+ CheckPacketIsSentImmediately();
+
+ // Losing a packet will set clear burst tokens.
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(1), kMaxOutgoingPacketSize));
+ AckedPacketVector empty_acked;
+ EXPECT_CALL(*mock_sender_,
+ OnCongestionEvent(true, kMaxOutgoingPacketSize, _, IsEmpty(), _));
+ pacing_sender_->OnCongestionEvent(true, kMaxOutgoingPacketSize, clock_.Now(),
+ empty_acked, lost_packets);
+ // 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));
+ // Verify the next packet is paced and delayed 2ms due to granularity.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2),
+ pacing_sender_->TimeUntilSend(clock_.Now(), kDefaultTCPMSS));
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+}
+
+TEST_F(PacingSenderTest, NoBurstInRecovery) {
+ // Configure pacing rate of 1 packet per 1 ms with no burst tokens.
+ InitPacingRate(
+ 0, QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxOutgoingPacketSize, QuicTime::Delta::FromMilliseconds(1)));
+
+ UpdateRtt();
+
+ // Ensure only one packet is sent immediately and the rest are paced.
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, 0, true, false, 10);
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+}
+
+TEST_F(PacingSenderTest, CwndLimited) {
+ // Configure pacing rate of 1 packet per 1 ms, no initial burst.
+ InitPacingRate(
+ 0, QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxOutgoingPacketSize, QuicTime::Delta::FromMilliseconds(1)));
+
+ UpdateRtt();
+
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ // Packet 3 will be delayed 2ms.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // 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);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ // Verify pacing sender stops making up for lost time after sending packet 3.
+ // Packet 6 will be delayed 2ms.
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+}
+
+TEST_F(PacingSenderTest, LumpyPacingWithInitialBurstToken) {
+ // Set lumpy size to be 3, and cwnd faction to 0.5
+ SetQuicFlag(&FLAGS_quic_lumpy_pacing_size, 3);
+ SetQuicFlag(&FLAGS_quic_lumpy_pacing_cwnd_fraction, 0.5f);
+ // Configure pacing rate of 1 packet per 1 ms.
+ InitPacingRate(
+ 10, QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxOutgoingPacketSize, QuicTime::Delta::FromMilliseconds(1)));
+ UpdateRtt();
+
+ // Send 10 packets, and verify that they are not paced.
+ for (int i = 0; i < kInitialBurstPackets; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ // Packet 14 will be delayed 3ms.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(3));
+
+ // Wake up on time.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ // Packet 17 will be delayed 3ms.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(3));
+
+ // Application throttles sending.
+ OnApplicationLimited();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ // Packet 20 will be delayed 3ms.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(3));
+
+ // Wake up on time.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3));
+ CheckPacketIsSentImmediately();
+ // After sending packet 21, cwnd is limited.
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA, kBytesInFlight, false,
+ true, 10);
+
+ 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);
+ 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
+ SetQuicFlag(&FLAGS_quic_lumpy_pacing_size, 3);
+ SetQuicFlag(&FLAGS_quic_lumpy_pacing_cwnd_fraction, 0.5f);
+ SetQuicReloadableFlag(quic_no_lumpy_pacing_at_low_bw, true);
+
+ // Configure pacing rate of 1 packet per 100 ms.
+ QuicTime::Delta inter_packet_delay = QuicTime::Delta::FromMilliseconds(100);
+ 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 first packet after burst token exhausted is also sent immediately,
+ // because ideal_next_packet_send_time has not been set yet.
+ CheckPacketIsSentImmediately();
+
+ for (int i = 0; i < 200; ++i) {
+ CheckPacketIsDelayed(inter_packet_delay);
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender.cc
new file mode 100644
index 00000000000..c09b312eb75
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+PrrSender::PrrSender()
+ : bytes_sent_since_loss_(0),
+ bytes_delivered_since_loss_(0),
+ ack_count_since_loss_(0),
+ bytes_in_flight_before_loss_(0) {}
+
+void PrrSender::OnPacketSent(QuicByteCount sent_bytes) {
+ bytes_sent_since_loss_ += sent_bytes;
+}
+
+void PrrSender::OnPacketLost(QuicByteCount prior_in_flight) {
+ bytes_sent_since_loss_ = 0;
+ bytes_in_flight_before_loss_ = prior_in_flight;
+ bytes_delivered_since_loss_ = 0;
+ ack_count_since_loss_ = 0;
+}
+
+void PrrSender::OnPacketAcked(QuicByteCount acked_bytes) {
+ bytes_delivered_since_loss_ += acked_bytes;
+ ++ack_count_since_loss_;
+}
+
+bool PrrSender::CanSend(QuicByteCount congestion_window,
+ QuicByteCount bytes_in_flight,
+ QuicByteCount slowstart_threshold) const {
+ // Return QuicTime::Zero in order to ensure limited transmit always works.
+ if (bytes_sent_since_loss_ == 0 || bytes_in_flight < kMaxSegmentSize) {
+ return true;
+ }
+ if (congestion_window > bytes_in_flight) {
+ // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead
+ // of sending the entire available window. This prevents burst retransmits
+ // when more packets are lost than the CWND reduction.
+ // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS
+ if (bytes_delivered_since_loss_ + ack_count_since_loss_ * kMaxSegmentSize <=
+ bytes_sent_since_loss_) {
+ return false;
+ }
+ return true;
+ }
+ // Implement Proportional Rate Reduction (RFC6937).
+ // Checks a simplified version of the PRR formula that doesn't use division:
+ // AvailableSendWindow =
+ // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent
+ if (bytes_delivered_since_loss_ * slowstart_threshold >
+ bytes_sent_since_loss_ * bytes_in_flight_before_loss_) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h
new file mode 100644
index 00000000000..21909129dae
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implements Proportional Rate Reduction (PRR) per RFC 6937.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_PRR_SENDER_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_PRR_SENDER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE PrrSender {
+ public:
+ PrrSender();
+ // OnPacketLost should be called on the first loss that triggers a recovery
+ // period and all other methods in this class should only be called when in
+ // recovery.
+ void OnPacketLost(QuicByteCount prior_in_flight);
+ void OnPacketSent(QuicByteCount sent_bytes);
+ void OnPacketAcked(QuicByteCount acked_bytes);
+ bool CanSend(QuicByteCount congestion_window,
+ QuicByteCount bytes_in_flight,
+ QuicByteCount slowstart_threshold) const;
+
+ private:
+ // Bytes sent and acked since the last loss event.
+ // |bytes_sent_since_loss_| is the same as "prr_out_" in RFC 6937,
+ // and |bytes_delivered_since_loss_| is the same as "prr_delivered_".
+ QuicByteCount bytes_sent_since_loss_;
+ QuicByteCount bytes_delivered_since_loss_;
+ size_t ack_count_since_loss_;
+
+ // The congestion window before the last loss event.
+ QuicByteCount bytes_in_flight_before_loss_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_PRR_SENDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender_test.cc
new file mode 100644
index 00000000000..45910659880
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/prr_sender_test.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h"
+
+#include <algorithm>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+// Constant based on TCP defaults.
+const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS;
+} // namespace
+
+class PrrSenderTest : public QuicTest {};
+
+TEST_F(PrrSenderTest, SingleLossResultsInSendOnEveryOtherAck) {
+ PrrSender prr;
+ QuicPacketCount num_packets_in_flight = 50;
+ QuicByteCount bytes_in_flight = num_packets_in_flight * kMaxSegmentSize;
+ const QuicPacketCount ssthresh_after_loss = num_packets_in_flight / 2;
+ const QuicByteCount congestion_window = ssthresh_after_loss * kMaxSegmentSize;
+
+ prr.OnPacketLost(bytes_in_flight);
+ // Ack a packet. PRR allows one packet to leave immediately.
+ prr.OnPacketAcked(kMaxSegmentSize);
+ bytes_in_flight -= kMaxSegmentSize;
+ EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+ // Send retransmission.
+ prr.OnPacketSent(kMaxSegmentSize);
+ // PRR shouldn't allow sending any more packets.
+ EXPECT_FALSE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+
+ // One packet is lost, and one ack was consumed above. PRR now paces
+ // transmissions through the remaining 48 acks. PRR will alternatively
+ // disallow and allow a packet to be sent in response to an ack.
+ for (uint64_t i = 0; i < ssthresh_after_loss - 1; ++i) {
+ // Ack a packet. PRR shouldn't allow sending a packet in response.
+ prr.OnPacketAcked(kMaxSegmentSize);
+ bytes_in_flight -= kMaxSegmentSize;
+ EXPECT_FALSE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+ // Ack another packet. PRR should now allow sending a packet in response.
+ prr.OnPacketAcked(kMaxSegmentSize);
+ bytes_in_flight -= kMaxSegmentSize;
+ EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+ // Send a packet in response.
+ prr.OnPacketSent(kMaxSegmentSize);
+ bytes_in_flight += kMaxSegmentSize;
+ }
+
+ // Since bytes_in_flight is now equal to congestion_window, PRR now maintains
+ // packet conservation, allowing one packet to be sent in response to an ack.
+ EXPECT_EQ(congestion_window, bytes_in_flight);
+ for (int i = 0; i < 10; ++i) {
+ // Ack a packet.
+ prr.OnPacketAcked(kMaxSegmentSize);
+ bytes_in_flight -= kMaxSegmentSize;
+ EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+ // Send a packet in response, since PRR allows it.
+ prr.OnPacketSent(kMaxSegmentSize);
+ bytes_in_flight += kMaxSegmentSize;
+
+ // Since bytes_in_flight is equal to the congestion_window,
+ // PRR disallows sending.
+ EXPECT_EQ(congestion_window, bytes_in_flight);
+ EXPECT_FALSE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+ }
+}
+
+TEST_F(PrrSenderTest, BurstLossResultsInSlowStart) {
+ PrrSender prr;
+ QuicByteCount bytes_in_flight = 20 * kMaxSegmentSize;
+ const QuicPacketCount num_packets_lost = 13;
+ const QuicPacketCount ssthresh_after_loss = 10;
+ const QuicByteCount congestion_window = ssthresh_after_loss * kMaxSegmentSize;
+
+ // Lose 13 packets.
+ bytes_in_flight -= num_packets_lost * kMaxSegmentSize;
+ prr.OnPacketLost(bytes_in_flight);
+
+ // PRR-SSRB will allow the following 3 acks to send up to 2 packets.
+ for (int i = 0; i < 3; ++i) {
+ prr.OnPacketAcked(kMaxSegmentSize);
+ bytes_in_flight -= kMaxSegmentSize;
+ // PRR-SSRB should allow two packets to be sent.
+ for (int j = 0; j < 2; ++j) {
+ EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+ // Send a packet in response.
+ prr.OnPacketSent(kMaxSegmentSize);
+ bytes_in_flight += kMaxSegmentSize;
+ }
+ // PRR should allow no more than 2 packets in response to an ack.
+ EXPECT_FALSE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+ }
+
+ // Out of SSRB mode, PRR allows one send in response to each ack.
+ for (int i = 0; i < 10; ++i) {
+ prr.OnPacketAcked(kMaxSegmentSize);
+ bytes_in_flight -= kMaxSegmentSize;
+ EXPECT_TRUE(prr.CanSend(congestion_window, bytes_in_flight,
+ ssthresh_after_loss * kMaxSegmentSize));
+ // Send a packet in response.
+ prr.OnPacketSent(kMaxSegmentSize);
+ bytes_in_flight += kMaxSegmentSize;
+ }
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..e1a63722cb8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+
+#include <cstdlib> // std::abs
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Default initial rtt used before any samples are received.
+const int kInitialRttMs = 100;
+const float kAlpha = 0.125f;
+const float kOneMinusAlpha = (1 - kAlpha);
+const float kBeta = 0.25f;
+const float kOneMinusBeta = (1 - kBeta);
+
+} // namespace
+
+RttStats::RttStats()
+ : latest_rtt_(QuicTime::Delta::Zero()),
+ min_rtt_(QuicTime::Delta::Zero()),
+ smoothed_rtt_(QuicTime::Delta::Zero()),
+ previous_srtt_(QuicTime::Delta::Zero()),
+ mean_deviation_(QuicTime::Delta::Zero()),
+ initial_rtt_(QuicTime::Delta::FromMilliseconds(kInitialRttMs)),
+ max_ack_delay_(QuicTime::Delta::Zero()),
+ ignore_max_ack_delay_(false) {}
+
+void RttStats::ExpireSmoothedMetrics() {
+ mean_deviation_ = std::max(
+ mean_deviation_, QuicTime::Delta::FromMicroseconds(std::abs(
+ (smoothed_rtt_ - latest_rtt_).ToMicroseconds())));
+ smoothed_rtt_ = std::max(smoothed_rtt_, latest_rtt_);
+}
+
+// Updates the RTT based on a new sample.
+void RttStats::UpdateRtt(QuicTime::Delta send_delta,
+ QuicTime::Delta ack_delay,
+ QuicTime now) {
+ if (send_delta.IsInfinite() || send_delta <= QuicTime::Delta::Zero()) {
+ QUIC_LOG_FIRST_N(WARNING, 3)
+ << "Ignoring measured send_delta, because it's is "
+ << "either infinite, zero, or negative. send_delta = "
+ << send_delta.ToMicroseconds();
+ return;
+ }
+
+ // Update min_rtt_ first. min_rtt_ does not use an rtt_sample corrected for
+ // ack_delay but the raw observed send_delta, since poor clock granularity at
+ // the client may cause a high ack_delay to result in underestimation of the
+ // min_rtt_.
+ if (min_rtt_.IsZero() || min_rtt_ > send_delta) {
+ min_rtt_ = 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.
+ if (rtt_sample > ack_delay) {
+ if (rtt_sample - min_rtt_ >= ack_delay) {
+ max_ack_delay_ = std::max(max_ack_delay_, ack_delay);
+ rtt_sample = rtt_sample - ack_delay;
+ }
+ }
+ latest_rtt_ = rtt_sample;
+ // First time call.
+ if (smoothed_rtt_.IsZero()) {
+ smoothed_rtt_ = rtt_sample;
+ mean_deviation_ =
+ QuicTime::Delta::FromMicroseconds(rtt_sample.ToMicroseconds() / 2);
+ } else {
+ mean_deviation_ = QuicTime::Delta::FromMicroseconds(static_cast<int64_t>(
+ kOneMinusBeta * mean_deviation_.ToMicroseconds() +
+ kBeta * std::abs((smoothed_rtt_ - rtt_sample).ToMicroseconds())));
+ smoothed_rtt_ = kOneMinusAlpha * smoothed_rtt_ + kAlpha * rtt_sample;
+ QUIC_DVLOG(1) << " smoothed_rtt(us):" << smoothed_rtt_.ToMicroseconds()
+ << " mean_deviation(us):" << mean_deviation_.ToMicroseconds();
+ }
+}
+
+void RttStats::OnConnectionMigration() {
+ latest_rtt_ = QuicTime::Delta::Zero();
+ min_rtt_ = QuicTime::Delta::Zero();
+ smoothed_rtt_ = QuicTime::Delta::Zero();
+ mean_deviation_ = QuicTime::Delta::Zero();
+ initial_rtt_ = QuicTime::Delta::FromMilliseconds(kInitialRttMs);
+ max_ack_delay_ = QuicTime::Delta::Zero();
+}
+
+} // 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
new file mode 100644
index 00000000000..65791b1ae3e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A convenience class to store rtt samples and calculate smoothed rtt.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_RTT_STATS_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_RTT_STATS_H_
+
+#include <algorithm>
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class RttStatsPeer;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE RttStats {
+ public:
+ RttStats();
+ RttStats(const RttStats&) = delete;
+ RttStats& operator=(const RttStats&) = delete;
+
+ // Updates the RTT from an incoming ack which is received |send_delta| after
+ // the packet is sent and the peer reports the ack being delayed |ack_delay|.
+ void UpdateRtt(QuicTime::Delta send_delta,
+ QuicTime::Delta ack_delay,
+ QuicTime now);
+
+ // Causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt
+ // is larger. The mean deviation is increased to the most recent deviation if
+ // it's larger.
+ void ExpireSmoothedMetrics();
+
+ // Called when connection migrates and rtt measurement needs to be reset.
+ void OnConnectionMigration();
+
+ // Returns the EWMA smoothed RTT for the connection.
+ // May return Zero if no valid updates have occurred.
+ QuicTime::Delta smoothed_rtt() const { return smoothed_rtt_; }
+
+ // Returns the EWMA smoothed RTT prior to the most recent RTT sample.
+ QuicTime::Delta previous_srtt() const { return previous_srtt_; }
+
+ QuicTime::Delta initial_rtt() const { return initial_rtt_; }
+
+ QuicTime::Delta SmoothedOrInitialRtt() const {
+ return smoothed_rtt_.IsZero() ? initial_rtt_ : smoothed_rtt_;
+ }
+
+ // Sets an initial RTT to be used for SmoothedRtt before any RTT updates.
+ void set_initial_rtt(QuicTime::Delta initial_rtt) {
+ if (initial_rtt.ToMicroseconds() <= 0) {
+ QUIC_BUG << "Attempt to set initial rtt to <= 0.";
+ return;
+ }
+ initial_rtt_ = initial_rtt;
+ }
+
+ // The most recent rtt measurement.
+ // May return Zero if no valid updates have occurred.
+ QuicTime::Delta latest_rtt() const { return latest_rtt_; }
+
+ // Returns the min_rtt for the entire connection.
+ // May return Zero if no valid updates have occurred.
+ QuicTime::Delta min_rtt() const { return min_rtt_; }
+
+ QuicTime::Delta mean_deviation() const { return mean_deviation_; }
+
+ QuicTime::Delta max_ack_delay() const { return max_ack_delay_; }
+
+ 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 set_initial_max_ack_delay(QuicTime::Delta initial_max_ack_delay) {
+ max_ack_delay_ = std::max(max_ack_delay_, initial_max_ack_delay);
+ }
+
+ private:
+ friend class test::RttStatsPeer;
+
+ QuicTime::Delta latest_rtt_;
+ QuicTime::Delta min_rtt_;
+ QuicTime::Delta smoothed_rtt_;
+ QuicTime::Delta previous_srtt_;
+ // Mean RTT deviation during this session.
+ // Approximation of standard deviation, the error is roughly 1.25 times
+ // larger than the standard deviation, for a normally distributed signal.
+ QuicTime::Delta mean_deviation_;
+ QuicTime::Delta initial_rtt_;
+ // The maximum ack delay observed over the connection after excluding ack
+ // delays that were too large to be included in an RTT measurement.
+ QuicTime::Delta max_ack_delay_;
+ // Whether to ignore the peer's max ack delay.
+ bool ignore_max_ack_delay_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_RTT_STATS_H_
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
new file mode 100644
index 00000000000..83cb15578a8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc
@@ -0,0 +1,230 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+
+#include <cmath>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mock_log.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.h"
+
+using testing::_;
+using testing::Message;
+
+namespace quic {
+namespace test {
+
+class RttStatsTest : public QuicTest {
+ protected:
+ RttStats rtt_stats_;
+};
+
+TEST_F(RttStatsTest, DefaultsBeforeUpdate) {
+ EXPECT_LT(QuicTime::Delta::Zero(), rtt_stats_.initial_rtt());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.smoothed_rtt());
+}
+
+TEST_F(RttStatsTest, SmoothedRtt) {
+ // 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());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay());
+ // Verify that a plausible ack delay increases the max ack delay.
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(400),
+ 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());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay());
+ // Verify that Smoothed RTT includes max ack delay if it's reasonable.
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(350),
+ 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());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay());
+ // 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());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay());
+}
+
+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());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay());
+ // 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());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay());
+ // 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());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay());
+}
+
+// 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) {
+ for (size_t time = 3; time < 20000; time++) {
+ RttStats stats;
+ for (size_t i = 0; i < 100; i++) {
+ stats.UpdateRtt(QuicTime::Delta::FromMicroseconds(time),
+ QuicTime::Delta::FromMilliseconds(0), QuicTime::Zero());
+ int64_t time_delta_us = stats.smoothed_rtt().ToMicroseconds() - time;
+ ASSERT_LE(std::abs(time_delta_us), 1);
+ }
+ }
+}
+
+TEST_F(RttStatsTest, PreviousSmoothedRtt) {
+ // Verify that ack_delay is corrected for in Smoothed RTT.
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200),
+ QuicTime::Delta::FromMilliseconds(0), QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.smoothed_rtt());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.previous_srtt());
+ // Ensure the previous SRTT is 200ms after a 100ms sample.
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(187500).ToMicroseconds(),
+ rtt_stats_.smoothed_rtt().ToMicroseconds());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.previous_srtt());
+}
+
+TEST_F(RttStatsTest, MinRtt) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.min_rtt());
+ rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(10), QuicTime::Delta::Zero(),
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(10));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(50), QuicTime::Delta::Zero(),
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(20));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(50), QuicTime::Delta::Zero(),
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(30));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(50), QuicTime::Delta::Zero(),
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(40));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ // Verify that ack_delay does not go into recording of min_rtt_.
+ rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(7),
+ QuicTime::Delta::FromMilliseconds(2),
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(50));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(7), rtt_stats_.min_rtt());
+}
+
+TEST_F(RttStatsTest, ExpireSmoothedMetrics) {
+ QuicTime::Delta initial_rtt = QuicTime::Delta::FromMilliseconds(10);
+ rtt_stats_.UpdateRtt(initial_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt());
+ EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt());
+
+ EXPECT_EQ(0.5 * initial_rtt, rtt_stats_.mean_deviation());
+
+ // Update once with a 20ms RTT.
+ QuicTime::Delta doubled_rtt = 2 * initial_rtt;
+ rtt_stats_.UpdateRtt(doubled_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(1.125 * initial_rtt, rtt_stats_.smoothed_rtt());
+
+ // Expire the smoothed metrics, increasing smoothed rtt and mean deviation.
+ rtt_stats_.ExpireSmoothedMetrics();
+ EXPECT_EQ(doubled_rtt, rtt_stats_.smoothed_rtt());
+ EXPECT_EQ(0.875 * initial_rtt, rtt_stats_.mean_deviation());
+
+ // Now go back down to 5ms and expire the smoothed metrics, and ensure the
+ // mean deviation increases to 15ms.
+ QuicTime::Delta half_rtt = 0.5 * initial_rtt;
+ rtt_stats_.UpdateRtt(half_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_GT(doubled_rtt, rtt_stats_.smoothed_rtt());
+ EXPECT_LT(initial_rtt, rtt_stats_.mean_deviation());
+}
+
+TEST_F(RttStatsTest, UpdateRttWithBadSendDeltas) {
+ // Make sure we ignore bad RTTs.
+ CREATE_QUIC_MOCK_LOG(log);
+
+ QuicTime::Delta initial_rtt = QuicTime::Delta::FromMilliseconds(10);
+ rtt_stats_.UpdateRtt(initial_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt());
+ EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt());
+
+ std::vector<QuicTime::Delta> bad_send_deltas;
+ bad_send_deltas.push_back(QuicTime::Delta::Zero());
+ bad_send_deltas.push_back(QuicTime::Delta::Infinite());
+ bad_send_deltas.push_back(QuicTime::Delta::FromMicroseconds(-1000));
+ log.StartCapturingLogs();
+
+ for (QuicTime::Delta bad_send_delta : bad_send_deltas) {
+ SCOPED_TRACE(Message() << "bad_send_delta = "
+ << bad_send_delta.ToMicroseconds());
+ if (QUIC_LOG_WARNING_IS_ON()) {
+ EXPECT_QUIC_LOG_CALL_CONTAINS(log, WARNING, "Ignoring");
+ }
+ rtt_stats_.UpdateRtt(bad_send_delta, QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt());
+ EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt());
+ }
+}
+
+TEST_F(RttStatsTest, ResetAfterConnectionMigrations) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(200),
+ QuicTime::Delta::FromMilliseconds(0), QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.smoothed_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(0), rtt_stats_.max_ack_delay());
+
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(300),
+ QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.smoothed_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.max_ack_delay());
+
+ // Reset rtt stats on connection migrations.
+ rtt_stats_.OnConnectionMigration();
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.latest_rtt());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.smoothed_rtt());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::Zero(), rtt_stats_.max_ack_delay());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.cc
new file mode 100644
index 00000000000..0b3c6c85339
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h"
+
+namespace quic {
+
+class RttStats;
+
+// Factory for send side congestion control algorithm.
+SendAlgorithmInterface* SendAlgorithmInterface::Create(
+ const QuicClock* clock,
+ const RttStats* rtt_stats,
+ const QuicUnackedPacketMap* unacked_packets,
+ CongestionControlType congestion_control_type,
+ QuicRandom* random,
+ QuicConnectionStats* stats,
+ QuicPacketCount initial_congestion_window) {
+ QuicPacketCount max_congestion_window = kDefaultMaxCongestionWindowPackets;
+ switch (congestion_control_type) {
+ case kGoogCC: // GoogCC is not supported by quic/core, fall back to BBR.
+ case kBBR:
+ return new BbrSender(clock->ApproximateNow(), rtt_stats, unacked_packets,
+ initial_congestion_window, max_congestion_window,
+ random, stats);
+ case kPCC:
+ if (GetQuicReloadableFlag(quic_enable_pcc3)) {
+ return CreatePccSender(clock, rtt_stats, unacked_packets, random, stats,
+ initial_congestion_window,
+ max_congestion_window);
+ }
+ // Fall back to CUBIC if PCC is disabled.
+ QUIC_FALLTHROUGH_INTENDED;
+ case kCubicBytes:
+ return new TcpCubicSenderBytes(
+ clock, rtt_stats, false /* don't use Reno */,
+ initial_congestion_window, max_congestion_window, stats);
+ case kRenoBytes:
+ return new TcpCubicSenderBytes(clock, rtt_stats, true /* use Reno */,
+ initial_congestion_window,
+ max_congestion_window, stats);
+ }
+ return nullptr;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h
new file mode 100644
index 00000000000..620b23f879d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The pure virtual class for send side congestion control algorithm.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
+
+#include <algorithm>
+#include <map>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class CachedNetworkParameters;
+class RttStats;
+
+const QuicPacketCount kDefaultMaxCongestionWindowPackets = 2000;
+
+class QUIC_EXPORT_PRIVATE SendAlgorithmInterface {
+ public:
+ static SendAlgorithmInterface* Create(
+ const QuicClock* clock,
+ const RttStats* rtt_stats,
+ const QuicUnackedPacketMap* unacked_packets,
+ CongestionControlType type,
+ QuicRandom* random,
+ QuicConnectionStats* stats,
+ QuicPacketCount initial_congestion_window);
+
+ virtual ~SendAlgorithmInterface() {}
+
+ virtual void SetFromConfig(const QuicConfig& config,
+ Perspective perspective) = 0;
+
+ // Sets the number of connections to emulate when doing congestion control,
+ // particularly for congestion avoidance. Can be set any time.
+ virtual void SetNumEmulatedConnections(int num_connections) = 0;
+
+ // Sets the initial congestion window in number of packets. May be ignored
+ // if called after the initial congestion window is no longer relevant.
+ virtual void SetInitialCongestionWindowInPackets(QuicPacketCount packets) = 0;
+
+ // Indicates an update to the congestion state, caused either by an incoming
+ // ack or loss event timeout. |rtt_updated| indicates whether a new
+ // latest_rtt sample has been taken, |prior_in_flight| the bytes in flight
+ // prior to the congestion event. |acked_packets| and |lost_packets| are any
+ // packets considered acked or lost as a result of the congestion event.
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets) = 0;
+
+ // Inform that we sent |bytes| to the wire, and if the packet is
+ // retransmittable. |bytes_in_flight| is the number of bytes in flight before
+ // the packet was sent.
+ // Note: this function must be called for every packet sent to the wire.
+ virtual void OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) = 0;
+
+ // Called when the retransmission timeout fires. Neither OnPacketAbandoned
+ // nor OnPacketLost will be called for these packets.
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) = 0;
+
+ // Called when connection migrates and cwnd needs to be reset.
+ virtual void OnConnectionMigration() = 0;
+
+ // Make decision on whether the sender can send right now. Note that even
+ // when this method returns true, the sending can be delayed due to pacing.
+ virtual bool CanSend(QuicByteCount bytes_in_flight) = 0;
+
+ // The pacing rate of the send algorithm. May be zero if the rate is unknown.
+ virtual QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const = 0;
+
+ // What's the current estimated bandwidth in bytes per second.
+ // Returns 0 when it does not have an estimate.
+ virtual QuicBandwidth BandwidthEstimate() const = 0;
+
+ // Returns the size of the current congestion window in bytes. Note, this is
+ // not the *available* window. Some send algorithms may not use a congestion
+ // window and will return 0.
+ virtual QuicByteCount GetCongestionWindow() const = 0;
+
+ // Whether the send algorithm is currently in slow start. When true, the
+ // BandwidthEstimate is expected to be too low.
+ virtual bool InSlowStart() const = 0;
+
+ // Whether the send algorithm is currently in recovery.
+ virtual bool InRecovery() const = 0;
+
+ // True when the congestion control is probing for more bandwidth and needs
+ // enough data to not be app-limited to do so.
+ // TODO(ianswett): In the future, this API may want to indicate the size of
+ // the probing packet.
+ virtual bool ShouldSendProbingPacket() const = 0;
+
+ // Returns the size of the slow start congestion window in bytes,
+ // aka ssthresh. Only defined for Cubic and Reno, other algorithms return 0.
+ virtual QuicByteCount GetSlowStartThreshold() const = 0;
+
+ virtual CongestionControlType GetCongestionControlType() const = 0;
+
+ // Notifies the congestion control algorithm of an external network
+ // measurement or prediction. Either |bandwidth| or |rtt| may be zero if no
+ // sample is available.
+ virtual void AdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) = 0;
+
+ // Retrieves debugging information about the current state of the
+ // send algorithm.
+ virtual std::string GetDebugState() const = 0;
+
+ // Called when the connection has no outstanding data to send. Specifically,
+ // this means that none of the data streams are write-blocked, there are no
+ // packets in the connection queue, and there are no pending retransmissins,
+ // i.e. the sender cannot send anything for reasons other than being blocked
+ // by congestion controller. This includes cases when the connection is
+ // blocked by the flow controller.
+ //
+ // The fact that this method is called does not necessarily imply that the
+ // connection would not be blocked by the congestion control if it actually
+ // tried to send data. If the congestion control algorithm needs to exclude
+ // such cases, it should use the internal state it uses for congestion control
+ // for that.
+ virtual void OnApplicationLimited(QuicByteCount bytes_in_flight) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc
new file mode 100644
index 00000000000..c07508c41bf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc
@@ -0,0 +1,371 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+// Use the initial CWND of 10, as 32 is too much for the test network.
+const uint32_t kInitialCongestionWindowPackets = 10;
+
+// Test network parameters. Here, the topology of the network is:
+//
+// QUIC Sender
+// |
+// | <-- local link
+// |
+// Network switch
+// * <-- the bottleneck queue in the direction
+// | of the receiver
+// |
+// | <-- test link
+// |
+// |
+// Receiver
+//
+// When setting the bandwidth of the local link and test link, choose
+// a bandwidth lower than 20Mbps, as the clock-granularity of the
+// simulator can only handle a granularity of 1us.
+
+// Default settings between the switch and the sender.
+const QuicBandwidth kLocalLinkBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(10000);
+const QuicTime::Delta kLocalPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(2);
+
+// Wired network settings. A typical desktop network setup, a
+// high-bandwidth, 30ms test link to the receiver.
+const QuicBandwidth kTestLinkWiredBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(4000);
+const QuicTime::Delta kTestLinkWiredPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(50);
+const QuicTime::Delta kTestWiredTransferTime =
+ kTestLinkWiredBandwidth.TransferTime(kMaxOutgoingPacketSize) +
+ kLocalLinkBandwidth.TransferTime(kMaxOutgoingPacketSize);
+const QuicTime::Delta kTestWiredRtt =
+ (kTestLinkWiredPropagationDelay + kLocalPropagationDelay +
+ kTestWiredTransferTime) *
+ 2;
+const QuicByteCount kTestWiredBdp = kTestWiredRtt * kTestLinkWiredBandwidth;
+
+// Small BDP, Bandwidth-policed network settings. In this scenario,
+// the receiver has a low-bandwidth, short propagation-delay link,
+// resulting in a small BDP. We model the policer by setting the
+// queue size to only one packet.
+const QuicBandwidth kTestLinkLowBdpBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(200);
+const QuicTime::Delta kTestLinkLowBdpPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(50);
+const QuicByteCount kTestPolicerQueue = kMaxOutgoingPacketSize;
+
+// Satellite network settings. In a satellite network, the bottleneck
+// buffer is typically sized for non-satellite links , but the
+// propagation delay of the test link to the receiver is as much as a
+// quarter second.
+const QuicTime::Delta kTestSatellitePropagationDelay =
+ QuicTime::Delta::FromMilliseconds(250);
+
+// Cellular scenarios. In a cellular network, the bottleneck queue at
+// the edge of the network can be as great as 3MB.
+const QuicBandwidth kTestLink2GBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(100);
+const QuicBandwidth kTestLink3GBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(1500);
+const QuicByteCount kCellularQueue = 3 * 1024 * 1024;
+const QuicTime::Delta kTestCellularPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(40);
+
+// Small RTT scenario, below the per-ack-update threshold of 30ms.
+const QuicTime::Delta kTestLinkSmallRTTDelay =
+ QuicTime::Delta::FromMilliseconds(10);
+
+const char* CongestionControlTypeToString(CongestionControlType cc_type) {
+ switch (cc_type) {
+ case kCubicBytes:
+ return "CUBIC_BYTES";
+ case kRenoBytes:
+ return "RENO_BYTES";
+ case kBBR:
+ return "BBR";
+ case kPCC:
+ return "PCC";
+ default:
+ QUIC_DLOG(FATAL) << "Unexpected CongestionControlType";
+ return nullptr;
+ }
+}
+
+struct TestParams {
+ explicit TestParams(CongestionControlType congestion_control_type)
+ : congestion_control_type(congestion_control_type) {}
+
+ friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+ os << "{ congestion_control_type: "
+ << CongestionControlTypeToString(p.congestion_control_type);
+ os << " }";
+ return os;
+ }
+
+ const CongestionControlType congestion_control_type;
+};
+
+std::string TestParamToString(
+ const testing::TestParamInfo<TestParams>& params) {
+ return QuicStrCat(
+ CongestionControlTypeToString(params.param.congestion_control_type), "_");
+}
+
+// Constructs various test permutations.
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ for (const CongestionControlType congestion_control_type :
+ {kBBR, kCubicBytes, kRenoBytes, kPCC}) {
+ params.push_back(TestParams(congestion_control_type));
+ }
+ return params;
+}
+
+} // namespace
+
+class SendAlgorithmTest : public QuicTestWithParam<TestParams> {
+ protected:
+ SendAlgorithmTest()
+ : simulator_(),
+ quic_sender_(&simulator_,
+ "QUIC sender",
+ "Receiver",
+ Perspective::IS_CLIENT,
+ TestConnectionId()),
+ receiver_(&simulator_,
+ "Receiver",
+ "QUIC sender",
+ Perspective::IS_SERVER,
+ TestConnectionId()) {
+ rtt_stats_ = quic_sender_.connection()->sent_packet_manager().GetRttStats();
+ sender_ = SendAlgorithmInterface::Create(
+ simulator_.GetClock(), rtt_stats_,
+ QuicSentPacketManagerPeer::GetUnackedPacketMap(
+ QuicConnectionPeer::GetSentPacketManager(
+ quic_sender_.connection())),
+ GetParam().congestion_control_type, &random_, &stats_,
+ kInitialCongestionWindowPackets);
+ quic_sender_.RecordTrace();
+
+ QuicConnectionPeer::SetSendAlgorithm(quic_sender_.connection(), sender_);
+ clock_ = simulator_.GetClock();
+ simulator_.set_random_generator(&random_);
+
+ uint64_t seed = QuicRandom::GetInstance()->RandUint64();
+ random_.set_seed(seed);
+ QUIC_LOG(INFO) << "SendAlgorithmTest simulator set up. Seed: " << seed;
+ }
+
+ // Creates a simulated network, with default settings between the
+ // sender and the switch and the given settings from the switch to
+ // the receiver.
+ void CreateSetup(const QuicBandwidth& test_bandwidth,
+ const QuicTime::Delta& test_link_delay,
+ QuicByteCount bottleneck_queue_length) {
+ switch_ = QuicMakeUnique<simulator::Switch>(&simulator_, "Switch", 8,
+ bottleneck_queue_length);
+ quic_sender_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &quic_sender_, switch_->port(1), kLocalLinkBandwidth,
+ kLocalPropagationDelay);
+ receiver_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ &receiver_, switch_->port(2), test_bandwidth, test_link_delay);
+ }
+
+ void DoSimpleTransfer(QuicByteCount transfer_size, QuicTime::Delta deadline) {
+ quic_sender_.AddBytesToTransfer(transfer_size);
+ bool simulator_result = simulator_.RunUntilOrTimeout(
+ [this]() { return quic_sender_.bytes_to_transfer() == 0; }, deadline);
+ EXPECT_TRUE(simulator_result)
+ << "Simple transfer failed. Bytes remaining: "
+ << quic_sender_.bytes_to_transfer();
+ }
+
+ void SendBursts(size_t number_of_bursts,
+ QuicByteCount bytes,
+ QuicTime::Delta rtt,
+ QuicTime::Delta wait_time) {
+ ASSERT_EQ(0u, quic_sender_.bytes_to_transfer());
+ for (size_t i = 0; i < number_of_bursts; i++) {
+ quic_sender_.AddBytesToTransfer(bytes);
+
+ // Transfer data and wait for three seconds between each transfer.
+ simulator_.RunFor(wait_time);
+
+ // Ensure the connection did not time out.
+ ASSERT_TRUE(quic_sender_.connection()->connected());
+ ASSERT_TRUE(receiver_.connection()->connected());
+ }
+
+ simulator_.RunFor(wait_time + rtt);
+ EXPECT_EQ(0u, quic_sender_.bytes_to_transfer());
+ }
+
+ // Estimates the elapsed time for a given transfer size, given the
+ // bottleneck bandwidth and link propagation delay.
+ QuicTime::Delta EstimatedElapsedTime(
+ QuicByteCount transfer_size_bytes,
+ QuicBandwidth test_link_bandwidth,
+ const QuicTime::Delta& test_link_delay) const {
+ return test_link_bandwidth.TransferTime(transfer_size_bytes) +
+ 2 * test_link_delay;
+ }
+
+ QuicTime QuicSenderStartTime() {
+ return quic_sender_.connection()->GetStats().connection_creation_time;
+ }
+
+ void PrintTransferStats() {
+ const QuicConnectionStats& stats = quic_sender_.connection()->GetStats();
+ QUIC_LOG(INFO) << "Summary for scenario " << GetParam();
+ QUIC_LOG(INFO) << "Sender stats is " << stats;
+ const double rtx_rate =
+ static_cast<double>(stats.bytes_retransmitted) / stats.bytes_sent;
+ QUIC_LOG(INFO) << "Retransmit rate (num_rtx/num_total_sent): " << rtx_rate;
+ QUIC_LOG(INFO) << "Connection elapsed time: "
+ << (clock_->Now() - QuicSenderStartTime()).ToMilliseconds()
+ << " (ms)";
+ }
+
+ simulator::Simulator simulator_;
+ simulator::QuicEndpoint quic_sender_;
+ simulator::QuicEndpoint receiver_;
+ std::unique_ptr<simulator::Switch> switch_;
+ std::unique_ptr<simulator::SymmetricLink> quic_sender_link_;
+ std::unique_ptr<simulator::SymmetricLink> receiver_link_;
+ QuicConnectionStats stats_;
+
+ SimpleRandom random_;
+
+ // Owned by different components of the connection.
+ const QuicClock* clock_;
+ const RttStats* rtt_stats_;
+ SendAlgorithmInterface* sender_;
+};
+
+INSTANTIATE_TEST_SUITE_P(SendAlgorithmTests,
+ SendAlgorithmTest,
+ ::testing::ValuesIn(GetTestParams()),
+ TestParamToString);
+
+// Test a simple long data transfer in the default setup.
+TEST_P(SendAlgorithmTest, SimpleWiredNetworkTransfer) {
+ CreateSetup(kTestLinkWiredBandwidth, kTestLinkWiredPropagationDelay,
+ kTestWiredBdp);
+ const QuicByteCount kTransferSizeBytes = 12 * 1024 * 1024;
+ const QuicTime::Delta maximum_elapsed_time =
+ EstimatedElapsedTime(kTransferSizeBytes, kTestLinkWiredBandwidth,
+ kTestLinkWiredPropagationDelay) *
+ 1.2;
+ DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+ PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, LowBdpPolicedNetworkTransfer) {
+ CreateSetup(kTestLinkLowBdpBandwidth, kTestLinkLowBdpPropagationDelay,
+ kTestPolicerQueue);
+ const QuicByteCount kTransferSizeBytes = 5 * 1024 * 1024;
+ const QuicTime::Delta maximum_elapsed_time =
+ EstimatedElapsedTime(kTransferSizeBytes, kTestLinkLowBdpBandwidth,
+ kTestLinkLowBdpPropagationDelay) *
+ 1.2;
+ DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+ PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, AppLimitedBurstsOverWiredNetwork) {
+ CreateSetup(kTestLinkWiredBandwidth, kTestLinkWiredPropagationDelay,
+ kTestWiredBdp);
+ const QuicByteCount kBurstSizeBytes = 512;
+ const int kNumBursts = 20;
+ const QuicTime::Delta kWaitTime = QuicTime::Delta::FromSeconds(3);
+ SendBursts(kNumBursts, kBurstSizeBytes, kTestWiredRtt, kWaitTime);
+ PrintTransferStats();
+
+ const QuicTime::Delta estimated_burst_time =
+ EstimatedElapsedTime(kBurstSizeBytes, kTestLinkWiredBandwidth,
+ kTestLinkWiredPropagationDelay) +
+ kWaitTime;
+ const QuicTime::Delta max_elapsed_time =
+ kNumBursts * estimated_burst_time + kWaitTime;
+ const QuicTime::Delta actual_elapsed_time =
+ clock_->Now() - QuicSenderStartTime();
+ EXPECT_GE(max_elapsed_time, actual_elapsed_time);
+}
+
+TEST_P(SendAlgorithmTest, SatelliteNetworkTransfer) {
+ CreateSetup(kTestLinkWiredBandwidth, kTestSatellitePropagationDelay,
+ kTestWiredBdp);
+ const QuicByteCount kTransferSizeBytes = 12 * 1024 * 1024;
+ const QuicTime::Delta maximum_elapsed_time =
+ EstimatedElapsedTime(kTransferSizeBytes, kTestLinkWiredBandwidth,
+ kTestSatellitePropagationDelay) *
+ 1.25;
+ DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+ PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, 2GNetworkTransfer) {
+ CreateSetup(kTestLink2GBandwidth, kTestCellularPropagationDelay,
+ kCellularQueue);
+ const QuicByteCount kTransferSizeBytes = 1024 * 1024;
+ const QuicTime::Delta maximum_elapsed_time =
+ EstimatedElapsedTime(kTransferSizeBytes, kTestLink2GBandwidth,
+ kTestCellularPropagationDelay) *
+ 1.2;
+ DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+ PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, 3GNetworkTransfer) {
+ CreateSetup(kTestLink3GBandwidth, kTestCellularPropagationDelay,
+ kCellularQueue);
+ const QuicByteCount kTransferSizeBytes = 5 * 1024 * 1024;
+ const QuicTime::Delta maximum_elapsed_time =
+ EstimatedElapsedTime(kTransferSizeBytes, kTestLink3GBandwidth,
+ kTestCellularPropagationDelay) *
+ 1.2;
+ DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+ PrintTransferStats();
+}
+
+TEST_P(SendAlgorithmTest, LowRTTTransfer) {
+ CreateSetup(kTestLinkWiredBandwidth, kTestLinkSmallRTTDelay, kCellularQueue);
+
+ const QuicByteCount kTransferSizeBytes = 12 * 1024 * 1024;
+ const QuicTime::Delta maximum_elapsed_time =
+ EstimatedElapsedTime(kTransferSizeBytes, kTestLinkWiredBandwidth,
+ kTestLinkSmallRTTDelay) *
+ 1.2;
+ DoSimpleTransfer(kTransferSizeBytes, maximum_elapsed_time);
+ PrintTransferStats();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
new file mode 100644
index 00000000000..fde8d750101
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.cc
@@ -0,0 +1,431 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+// Constants based on TCP defaults.
+const QuicByteCount kMaxBurstBytes = 3 * kDefaultTCPMSS;
+const float kRenoBeta = 0.7f; // Reno backoff factor.
+// The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a
+// fast retransmission.
+const QuicByteCount kDefaultMinimumCongestionWindow = 2 * kDefaultTCPMSS;
+} // namespace
+
+TcpCubicSenderBytes::TcpCubicSenderBytes(
+ const QuicClock* clock,
+ const RttStats* rtt_stats,
+ bool reno,
+ QuicPacketCount initial_tcp_congestion_window,
+ QuicPacketCount max_congestion_window,
+ QuicConnectionStats* stats)
+ : rtt_stats_(rtt_stats),
+ stats_(stats),
+ reno_(reno),
+ num_connections_(kDefaultNumConnections),
+ min4_mode_(false),
+ last_cutback_exited_slowstart_(false),
+ slow_start_large_reduction_(false),
+ no_prr_(false),
+ cubic_(clock),
+ num_acked_packets_(0),
+ congestion_window_(initial_tcp_congestion_window * kDefaultTCPMSS),
+ min_congestion_window_(kDefaultMinimumCongestionWindow),
+ max_congestion_window_(max_congestion_window * kDefaultTCPMSS),
+ slowstart_threshold_(max_congestion_window * kDefaultTCPMSS),
+ initial_tcp_congestion_window_(initial_tcp_congestion_window *
+ kDefaultTCPMSS),
+ initial_max_tcp_congestion_window_(max_congestion_window *
+ kDefaultTCPMSS),
+ min_slow_start_exit_window_(min_congestion_window_) {}
+
+TcpCubicSenderBytes::~TcpCubicSenderBytes() {}
+
+void TcpCubicSenderBytes::SetFromConfig(const QuicConfig& config,
+ Perspective perspective) {
+ if (perspective == Perspective::IS_SERVER) {
+ if (!GetQuicReloadableFlag(quic_unified_iw_options)) {
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kIW03)) {
+ // Initial window experiment.
+ SetInitialCongestionWindowInPackets(3);
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) {
+ // Initial window experiment.
+ SetInitialCongestionWindowInPackets(10);
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kIW20)) {
+ // Initial window experiment.
+ SetInitialCongestionWindowInPackets(20);
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kIW50)) {
+ // Initial window experiment.
+ SetInitialCongestionWindowInPackets(50);
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN1)) {
+ // Min CWND experiment.
+ SetMinCongestionWindowInPackets(1);
+ }
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kMIN4)) {
+ // Min CWND of 4 experiment.
+ min4_mode_ = true;
+ SetMinCongestionWindowInPackets(1);
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kSSLR)) {
+ // Slow Start Fast Exit experiment.
+ slow_start_large_reduction_ = true;
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kNPRR)) {
+ // Use unity pacing instead of PRR.
+ no_prr_ = true;
+ }
+ }
+}
+
+void TcpCubicSenderBytes::AdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) {
+ if (bandwidth.IsZero() || rtt.IsZero()) {
+ return;
+ }
+
+ SetCongestionWindowFromBandwidthAndRtt(bandwidth, rtt);
+}
+
+float TcpCubicSenderBytes::RenoBeta() const {
+ // kNConnectionBeta is the backoff factor after loss for our N-connection
+ // emulation, which emulates the effective backoff of an ensemble of N
+ // TCP-Reno connections on a single loss event. The effective multiplier is
+ // computed as:
+ return (num_connections_ - 1 + kRenoBeta) / num_connections_;
+}
+
+void TcpCubicSenderBytes::OnCongestionEvent(
+ bool rtt_updated,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets) {
+ if (rtt_updated && InSlowStart() &&
+ hybrid_slow_start_.ShouldExitSlowStart(
+ rtt_stats_->latest_rtt(), rtt_stats_->min_rtt(),
+ GetCongestionWindow() / kDefaultTCPMSS)) {
+ ExitSlowstart();
+ }
+ for (const LostPacket& lost_packet : lost_packets) {
+ OnPacketLost(lost_packet.packet_number, lost_packet.bytes_lost,
+ prior_in_flight);
+ }
+ for (const AckedPacket acked_packet : acked_packets) {
+ OnPacketAcked(acked_packet.packet_number, acked_packet.bytes_acked,
+ prior_in_flight, event_time);
+ }
+}
+
+void TcpCubicSenderBytes::OnPacketAcked(QuicPacketNumber acked_packet_number,
+ QuicByteCount acked_bytes,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time) {
+ largest_acked_packet_number_.UpdateMax(acked_packet_number);
+ if (InRecovery()) {
+ if (!no_prr_) {
+ // PRR is used when in recovery.
+ prr_.OnPacketAcked(acked_bytes);
+ }
+ return;
+ }
+ MaybeIncreaseCwnd(acked_packet_number, acked_bytes, prior_in_flight,
+ event_time);
+ if (InSlowStart()) {
+ hybrid_slow_start_.OnPacketAcked(acked_packet_number);
+ }
+}
+
+void TcpCubicSenderBytes::OnPacketSent(
+ QuicTime /*sent_time*/,
+ QuicByteCount /*bytes_in_flight*/,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) {
+ if (InSlowStart()) {
+ ++(stats_->slowstart_packets_sent);
+ }
+
+ if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
+ return;
+ }
+ if (InRecovery()) {
+ // PRR is used when in recovery.
+ prr_.OnPacketSent(bytes);
+ }
+ DCHECK(!largest_sent_packet_number_.IsInitialized() ||
+ largest_sent_packet_number_ < packet_number);
+ largest_sent_packet_number_ = packet_number;
+ hybrid_slow_start_.OnPacketSent(packet_number);
+}
+
+bool TcpCubicSenderBytes::CanSend(QuicByteCount bytes_in_flight) {
+ if (!no_prr_ && InRecovery()) {
+ // PRR is used when in recovery.
+ return prr_.CanSend(GetCongestionWindow(), bytes_in_flight,
+ GetSlowStartThreshold());
+ }
+ if (GetCongestionWindow() > bytes_in_flight) {
+ return true;
+ }
+ if (min4_mode_ && bytes_in_flight < 4 * kDefaultTCPMSS) {
+ return true;
+ }
+ return false;
+}
+
+QuicBandwidth TcpCubicSenderBytes::PacingRate(
+ QuicByteCount /* bytes_in_flight */) const {
+ // We pace at twice the rate of the underlying sender's bandwidth estimate
+ // during slow start and 1.25x during congestion avoidance to ensure pacing
+ // doesn't prevent us from filling the window.
+ QuicTime::Delta srtt = rtt_stats_->SmoothedOrInitialRtt();
+ const QuicBandwidth bandwidth =
+ QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt);
+ return bandwidth * (InSlowStart() ? 2 : (no_prr_ && InRecovery() ? 1 : 1.25));
+}
+
+QuicBandwidth TcpCubicSenderBytes::BandwidthEstimate() const {
+ QuicTime::Delta srtt = rtt_stats_->smoothed_rtt();
+ if (srtt.IsZero()) {
+ // If we haven't measured an rtt, the bandwidth estimate is unknown.
+ return QuicBandwidth::Zero();
+ }
+ return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(), srtt);
+}
+
+bool TcpCubicSenderBytes::InSlowStart() const {
+ return GetCongestionWindow() < GetSlowStartThreshold();
+}
+
+bool TcpCubicSenderBytes::IsCwndLimited(QuicByteCount bytes_in_flight) const {
+ const QuicByteCount congestion_window = GetCongestionWindow();
+ if (bytes_in_flight >= congestion_window) {
+ return true;
+ }
+ const QuicByteCount available_bytes = congestion_window - bytes_in_flight;
+ const bool slow_start_limited =
+ InSlowStart() && bytes_in_flight > congestion_window / 2;
+ return slow_start_limited || available_bytes <= kMaxBurstBytes;
+}
+
+bool TcpCubicSenderBytes::InRecovery() const {
+ return largest_acked_packet_number_.IsInitialized() &&
+ largest_sent_at_last_cutback_.IsInitialized() &&
+ largest_acked_packet_number_ <= largest_sent_at_last_cutback_;
+}
+
+bool TcpCubicSenderBytes::ShouldSendProbingPacket() const {
+ return false;
+}
+
+void TcpCubicSenderBytes::OnRetransmissionTimeout(bool packets_retransmitted) {
+ largest_sent_at_last_cutback_.Clear();
+ if (!packets_retransmitted) {
+ return;
+ }
+ hybrid_slow_start_.Restart();
+ HandleRetransmissionTimeout();
+}
+
+std::string TcpCubicSenderBytes::GetDebugState() const {
+ return "";
+}
+
+void TcpCubicSenderBytes::OnApplicationLimited(QuicByteCount bytes_in_flight) {}
+
+void TcpCubicSenderBytes::SetCongestionWindowFromBandwidthAndRtt(
+ QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) {
+ QuicByteCount new_congestion_window = bandwidth.ToBytesPerPeriod(rtt);
+ // Limit new CWND if needed.
+ congestion_window_ =
+ std::max(min_congestion_window_,
+ std::min(new_congestion_window,
+ kMaxResumptionCongestionWindow * kDefaultTCPMSS));
+}
+
+void TcpCubicSenderBytes::SetInitialCongestionWindowInPackets(
+ QuicPacketCount congestion_window) {
+ congestion_window_ = congestion_window * kDefaultTCPMSS;
+}
+
+void TcpCubicSenderBytes::SetMinCongestionWindowInPackets(
+ QuicPacketCount congestion_window) {
+ min_congestion_window_ = congestion_window * kDefaultTCPMSS;
+}
+
+void TcpCubicSenderBytes::SetNumEmulatedConnections(int num_connections) {
+ num_connections_ = std::max(1, num_connections);
+ cubic_.SetNumConnections(num_connections_);
+}
+
+void TcpCubicSenderBytes::ExitSlowstart() {
+ slowstart_threshold_ = congestion_window_;
+}
+
+void TcpCubicSenderBytes::OnPacketLost(QuicPacketNumber packet_number,
+ QuicByteCount lost_bytes,
+ QuicByteCount prior_in_flight) {
+ // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
+ // already sent should be treated as a single loss event, since it's expected.
+ if (largest_sent_at_last_cutback_.IsInitialized() &&
+ packet_number <= largest_sent_at_last_cutback_) {
+ if (last_cutback_exited_slowstart_) {
+ ++stats_->slowstart_packets_lost;
+ stats_->slowstart_bytes_lost += lost_bytes;
+ if (slow_start_large_reduction_) {
+ // Reduce congestion window by lost_bytes for every loss.
+ congestion_window_ = std::max(congestion_window_ - lost_bytes,
+ min_slow_start_exit_window_);
+ slowstart_threshold_ = congestion_window_;
+ }
+ }
+ QUIC_DVLOG(1) << "Ignoring loss for largest_missing:" << packet_number
+ << " because it was sent prior to the last CWND cutback.";
+ return;
+ }
+ ++stats_->tcp_loss_events;
+ last_cutback_exited_slowstart_ = InSlowStart();
+ if (InSlowStart()) {
+ ++stats_->slowstart_packets_lost;
+ }
+
+ if (!no_prr_) {
+ prr_.OnPacketLost(prior_in_flight);
+ }
+
+ // TODO(b/77268641): Separate out all of slow start into a separate class.
+ if (slow_start_large_reduction_ && InSlowStart()) {
+ DCHECK_LT(kDefaultTCPMSS, congestion_window_);
+ if (congestion_window_ >= 2 * initial_tcp_congestion_window_) {
+ min_slow_start_exit_window_ = congestion_window_ / 2;
+ }
+ congestion_window_ = congestion_window_ - kDefaultTCPMSS;
+ } else if (reno_) {
+ congestion_window_ = congestion_window_ * RenoBeta();
+ } else {
+ congestion_window_ =
+ cubic_.CongestionWindowAfterPacketLoss(congestion_window_);
+ }
+ if (congestion_window_ < min_congestion_window_) {
+ congestion_window_ = min_congestion_window_;
+ }
+ slowstart_threshold_ = congestion_window_;
+ largest_sent_at_last_cutback_ = largest_sent_packet_number_;
+ // Reset packet count from congestion avoidance mode. We start counting again
+ // when we're out of recovery.
+ num_acked_packets_ = 0;
+ QUIC_DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
+}
+
+QuicByteCount TcpCubicSenderBytes::GetCongestionWindow() const {
+ return congestion_window_;
+}
+
+QuicByteCount TcpCubicSenderBytes::GetSlowStartThreshold() const {
+ return slowstart_threshold_;
+}
+
+// Called when we receive an ack. Normal TCP tracks how many packets one ack
+// represents, but quic has a separate ack for each packet.
+void TcpCubicSenderBytes::MaybeIncreaseCwnd(
+ QuicPacketNumber acked_packet_number,
+ QuicByteCount acked_bytes,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time) {
+ QUIC_BUG_IF(InRecovery()) << "Never increase the CWND during recovery.";
+ // Do not increase the congestion window unless the sender is close to using
+ // the current window.
+ if (!IsCwndLimited(prior_in_flight)) {
+ cubic_.OnApplicationLimited();
+ return;
+ }
+ if (congestion_window_ >= max_congestion_window_) {
+ return;
+ }
+ if (InSlowStart()) {
+ // TCP slow start, exponential growth, increase by one for each ACK.
+ congestion_window_ += kDefaultTCPMSS;
+ QUIC_DVLOG(1) << "Slow start; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
+ return;
+ }
+ // Congestion avoidance.
+ if (reno_) {
+ // Classic Reno congestion avoidance.
+ ++num_acked_packets_;
+ // Divide by num_connections to smoothly increase the CWND at a faster rate
+ // than conventional Reno.
+ if (num_acked_packets_ * num_connections_ >=
+ congestion_window_ / kDefaultTCPMSS) {
+ congestion_window_ += kDefaultTCPMSS;
+ num_acked_packets_ = 0;
+ }
+
+ QUIC_DVLOG(1) << "Reno; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_
+ << " congestion window count: " << num_acked_packets_;
+ } else {
+ congestion_window_ = std::min(
+ max_congestion_window_,
+ cubic_.CongestionWindowAfterAck(acked_bytes, congestion_window_,
+ rtt_stats_->min_rtt(), event_time));
+ QUIC_DVLOG(1) << "Cubic; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
+ }
+}
+
+void TcpCubicSenderBytes::HandleRetransmissionTimeout() {
+ cubic_.ResetCubicState();
+ slowstart_threshold_ = congestion_window_ / 2;
+ congestion_window_ = min_congestion_window_;
+}
+
+void TcpCubicSenderBytes::OnConnectionMigration() {
+ hybrid_slow_start_.Restart();
+ prr_ = PrrSender();
+ largest_sent_packet_number_.Clear();
+ largest_acked_packet_number_.Clear();
+ largest_sent_at_last_cutback_.Clear();
+ last_cutback_exited_slowstart_ = false;
+ cubic_.ResetCubicState();
+ num_acked_packets_ = 0;
+ congestion_window_ = initial_tcp_congestion_window_;
+ max_congestion_window_ = initial_max_tcp_congestion_window_;
+ slowstart_threshold_ = initial_max_tcp_congestion_window_;
+}
+
+CongestionControlType TcpCubicSenderBytes::GetCongestionControlType() const {
+ return reno_ ? kRenoBytes : kCubicBytes;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.h
new file mode 100644
index 00000000000..a437426ae5e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.h
@@ -0,0 +1,172 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TCP cubic send side congestion algorithm, emulates the behavior of TCP cubic.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BYTES_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BYTES_H_
+
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/cubic_bytes.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/hybrid_slow_start.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/prr_sender.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class RttStats;
+
+// Maximum window to allow when doing bandwidth resumption.
+const QuicPacketCount kMaxResumptionCongestionWindow = 200;
+
+namespace test {
+class TcpCubicSenderBytesPeer;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE TcpCubicSenderBytes : public SendAlgorithmInterface {
+ public:
+ TcpCubicSenderBytes(const QuicClock* clock,
+ const RttStats* rtt_stats,
+ bool reno,
+ QuicPacketCount initial_tcp_congestion_window,
+ QuicPacketCount max_congestion_window,
+ QuicConnectionStats* stats);
+ TcpCubicSenderBytes(const TcpCubicSenderBytes&) = delete;
+ TcpCubicSenderBytes& operator=(const TcpCubicSenderBytes&) = delete;
+ ~TcpCubicSenderBytes() override;
+
+ // Start implementation of SendAlgorithmInterface.
+ void SetFromConfig(const QuicConfig& config,
+ Perspective perspective) override;
+ void AdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) override;
+ void SetNumEmulatedConnections(int num_connections) override;
+ void SetInitialCongestionWindowInPackets(
+ QuicPacketCount congestion_window) override;
+ void OnConnectionMigration() override;
+ void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets) override;
+ void OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketNumber packet_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) override;
+ void OnRetransmissionTimeout(bool packets_retransmitted) override;
+ bool CanSend(QuicByteCount bytes_in_flight) override;
+ QuicBandwidth PacingRate(QuicByteCount bytes_in_flight) const override;
+ QuicBandwidth BandwidthEstimate() const override;
+ QuicByteCount GetCongestionWindow() const override;
+ QuicByteCount GetSlowStartThreshold() const override;
+ CongestionControlType GetCongestionControlType() const override;
+ bool InSlowStart() const override;
+ bool InRecovery() const override;
+ bool ShouldSendProbingPacket() const override;
+ std::string GetDebugState() const override;
+ void OnApplicationLimited(QuicByteCount bytes_in_flight) override;
+ // End implementation of SendAlgorithmInterface.
+
+ QuicByteCount min_congestion_window() const { return min_congestion_window_; }
+
+ protected:
+ // Compute the TCP Reno beta based on the current number of connections.
+ float RenoBeta() const;
+
+ bool IsCwndLimited(QuicByteCount bytes_in_flight) const;
+
+ // TODO(ianswett): Remove these and migrate to OnCongestionEvent.
+ void OnPacketAcked(QuicPacketNumber acked_packet_number,
+ QuicByteCount acked_bytes,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time);
+ void SetCongestionWindowFromBandwidthAndRtt(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt);
+ void SetMinCongestionWindowInPackets(QuicPacketCount congestion_window);
+ void ExitSlowstart();
+ void OnPacketLost(QuicPacketNumber largest_loss,
+ QuicByteCount lost_bytes,
+ QuicByteCount prior_in_flight);
+ void MaybeIncreaseCwnd(QuicPacketNumber acked_packet_number,
+ QuicByteCount acked_bytes,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time);
+ void HandleRetransmissionTimeout();
+
+ private:
+ friend class test::TcpCubicSenderBytesPeer;
+
+ HybridSlowStart hybrid_slow_start_;
+ PrrSender prr_;
+ const RttStats* rtt_stats_;
+ QuicConnectionStats* stats_;
+
+ // If true, Reno congestion control is used instead of Cubic.
+ const bool reno_;
+
+ // Number of connections to simulate.
+ uint32_t num_connections_;
+
+ // Track the largest packet that has been sent.
+ QuicPacketNumber largest_sent_packet_number_;
+
+ // Track the largest packet that has been acked.
+ QuicPacketNumber largest_acked_packet_number_;
+
+ // Track the largest packet number outstanding when a CWND cutback occurs.
+ QuicPacketNumber largest_sent_at_last_cutback_;
+
+ // Whether to use 4 packets as the actual min, but pace lower.
+ bool min4_mode_;
+
+ // Whether the last loss event caused us to exit slowstart.
+ // Used for stats collection of slowstart_packets_lost
+ bool last_cutback_exited_slowstart_;
+
+ // When true, exit slow start with large cutback of congestion window.
+ bool slow_start_large_reduction_;
+
+ // When true, use unity pacing instead of PRR.
+ bool no_prr_;
+
+ CubicBytes cubic_;
+
+ // ACK counter for the Reno implementation.
+ uint64_t num_acked_packets_;
+
+ // Congestion window in bytes.
+ QuicByteCount congestion_window_;
+
+ // Minimum congestion window in bytes.
+ QuicByteCount min_congestion_window_;
+
+ // Maximum congestion window in bytes.
+ QuicByteCount max_congestion_window_;
+
+ // Slow start congestion window in bytes, aka ssthresh.
+ QuicByteCount slowstart_threshold_;
+
+ // Initial TCP congestion window in bytes. This variable can only be set when
+ // this algorithm is created.
+ const QuicByteCount initial_tcp_congestion_window_;
+
+ // Initial maximum TCP congestion window in bytes. This variable can only be
+ // set when this algorithm is created.
+ const QuicByteCount initial_max_tcp_congestion_window_;
+
+ // The minimum window when exiting slow start with large reduction.
+ QuicByteCount min_slow_start_exit_window_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_TCP_CUBIC_SENDER_BYTES_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc
new file mode 100644
index 00000000000..5eb6226b33e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc
@@ -0,0 +1,834 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+
+namespace quic {
+namespace test {
+
+// TODO(ianswett): A number of theses tests were written with the assumption of
+// an initial CWND of 10. They have carefully calculated values which should be
+// updated to be based on kInitialCongestionWindow.
+const uint32_t kInitialCongestionWindowPackets = 10;
+const uint32_t kMaxCongestionWindowPackets = 200;
+const uint32_t kDefaultWindowTCP =
+ kInitialCongestionWindowPackets * kDefaultTCPMSS;
+const float kRenoBeta = 0.7f; // Reno backoff factor.
+
+class TcpCubicSenderBytesPeer : public TcpCubicSenderBytes {
+ public:
+ TcpCubicSenderBytesPeer(const QuicClock* clock, bool reno)
+ : TcpCubicSenderBytes(clock,
+ &rtt_stats_,
+ reno,
+ kInitialCongestionWindowPackets,
+ kMaxCongestionWindowPackets,
+ &stats_) {}
+
+ const HybridSlowStart& hybrid_slow_start() const {
+ return hybrid_slow_start_;
+ }
+
+ float GetRenoBeta() const { return RenoBeta(); }
+
+ RttStats rtt_stats_;
+ QuicConnectionStats stats_;
+};
+
+class TcpCubicSenderBytesTest : public QuicTest {
+ protected:
+ TcpCubicSenderBytesTest()
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ sender_(new TcpCubicSenderBytesPeer(&clock_, true)),
+ packet_number_(1),
+ acked_packet_number_(0),
+ bytes_in_flight_(0) {}
+
+ int SendAvailableSendWindow() {
+ return SendAvailableSendWindow(kDefaultTCPMSS);
+ }
+
+ int SendAvailableSendWindow(QuicPacketLength packet_length) {
+ // Send as long as TimeUntilSend returns Zero.
+ int packets_sent = 0;
+ bool can_send = sender_->CanSend(bytes_in_flight_);
+ while (can_send) {
+ sender_->OnPacketSent(clock_.Now(), bytes_in_flight_,
+ QuicPacketNumber(packet_number_++), kDefaultTCPMSS,
+ HAS_RETRANSMITTABLE_DATA);
+ ++packets_sent;
+ bytes_in_flight_ += kDefaultTCPMSS;
+ can_send = sender_->CanSend(bytes_in_flight_);
+ }
+ return packets_sent;
+ }
+
+ // Normal is that TCP acks every other segment.
+ void AckNPackets(int n) {
+ sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(60),
+ QuicTime::Delta::Zero(), clock_.Now());
+ AckedPacketVector acked_packets;
+ LostPacketVector lost_packets;
+ for (int i = 0; i < n; ++i) {
+ ++acked_packet_number_;
+ acked_packets.push_back(
+ AckedPacket(QuicPacketNumber(acked_packet_number_), kDefaultTCPMSS,
+ QuicTime::Zero()));
+ }
+ sender_->OnCongestionEvent(true, bytes_in_flight_, clock_.Now(),
+ acked_packets, lost_packets);
+ bytes_in_flight_ -= n * kDefaultTCPMSS;
+ clock_.AdvanceTime(one_ms_);
+ }
+
+ void LoseNPackets(int n) { LoseNPackets(n, kDefaultTCPMSS); }
+
+ void LoseNPackets(int n, QuicPacketLength packet_length) {
+ AckedPacketVector acked_packets;
+ LostPacketVector lost_packets;
+ for (int i = 0; i < n; ++i) {
+ ++acked_packet_number_;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(acked_packet_number_), packet_length));
+ }
+ sender_->OnCongestionEvent(false, bytes_in_flight_, clock_.Now(),
+ acked_packets, lost_packets);
+ bytes_in_flight_ -= n * packet_length;
+ }
+
+ // Does not increment acked_packet_number_.
+ void LosePacket(uint64_t packet_number) {
+ AckedPacketVector acked_packets;
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(packet_number), kDefaultTCPMSS));
+ sender_->OnCongestionEvent(false, bytes_in_flight_, clock_.Now(),
+ acked_packets, lost_packets);
+ bytes_in_flight_ -= kDefaultTCPMSS;
+ }
+
+ const QuicTime::Delta one_ms_;
+ MockClock clock_;
+ std::unique_ptr<TcpCubicSenderBytesPeer> sender_;
+ uint64_t packet_number_;
+ uint64_t acked_packet_number_;
+ QuicByteCount bytes_in_flight_;
+};
+
+TEST_F(TcpCubicSenderBytesTest, SimpleSender) {
+ // At startup make sure we are at the default.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+ // And that window is un-affected.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+
+ // Fill the send window with data, then verify that we can't send.
+ SendAvailableSendWindow();
+ EXPECT_FALSE(sender_->CanSend(sender_->GetCongestionWindow()));
+}
+
+TEST_F(TcpCubicSenderBytesTest, ApplicationLimitedSlowStart) {
+ // Send exactly 10 packets and ensure the CWND ends at 14 packets.
+ const int kNumberOfAcks = 5;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+
+ SendAvailableSendWindow();
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ AckNPackets(2);
+ }
+ QuicByteCount bytes_to_send = sender_->GetCongestionWindow();
+ // It's expected 2 acks will arrive when the bytes_in_flight are greater than
+ // half the CWND.
+ EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * 2, bytes_to_send);
+}
+
+TEST_F(TcpCubicSenderBytesTest, ExponentialSlowStart) {
+ const int kNumberOfAcks = 20;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+ EXPECT_EQ(QuicBandwidth::Zero(), sender_->BandwidthEstimate());
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->CanSend(0));
+
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ const QuicByteCount cwnd = sender_->GetCongestionWindow();
+ EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAcks, cwnd);
+ EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(
+ cwnd, sender_->rtt_stats_.smoothed_rtt()),
+ sender_->BandwidthEstimate());
+}
+
+TEST_F(TcpCubicSenderBytesTest, SlowStartPacketLoss) {
+ sender_->SetNumEmulatedConnections(1);
+ const int kNumberOfAcks = 10;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose a packet to exit slow start.
+ LoseNPackets(1);
+ size_t packets_in_recovery_window = expected_send_window / kDefaultTCPMSS;
+
+ // We should now have fallen out of slow start with a reduced window.
+ expected_send_window *= kRenoBeta;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Recovery phase. We need to ack every packet in the recovery window before
+ // we exit recovery.
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ QUIC_DLOG(INFO) << "number_packets: " << number_of_packets_in_window;
+ AckNPackets(packets_in_recovery_window);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // We need to ack an entire window before we increase CWND by 1.
+ AckNPackets(number_of_packets_in_window - 2);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Next ack should increase cwnd by 1.
+ AckNPackets(1);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Now RTO and ensure slow start gets reset.
+ EXPECT_TRUE(sender_->hybrid_slow_start().started());
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
+}
+
+TEST_F(TcpCubicSenderBytesTest, SlowStartPacketLossWithLargeReduction) {
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kSSLR);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ sender_->SetFromConfig(config, Perspective::IS_SERVER);
+
+ sender_->SetNumEmulatedConnections(1);
+ const int kNumberOfAcks = (kDefaultWindowTCP / (2 * kDefaultTCPMSS)) - 1;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose a packet to exit slow start. We should now have fallen out of
+ // slow start with a window reduced by 1.
+ LoseNPackets(1);
+ expected_send_window -= kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose 5 packets in recovery and verify that congestion window is reduced
+ // further.
+ LoseNPackets(5);
+ expected_send_window -= 5 * kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ // Lose another 10 packets and ensure it reduces below half the peak CWND,
+ // because we never acked the full IW.
+ LoseNPackets(10);
+ expected_send_window -= 10 * kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ size_t packets_in_recovery_window = expected_send_window / kDefaultTCPMSS;
+
+ // Recovery phase. We need to ack every packet in the recovery window before
+ // we exit recovery.
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ QUIC_DLOG(INFO) << "number_packets: " << number_of_packets_in_window;
+ AckNPackets(packets_in_recovery_window);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // We need to ack an entire window before we increase CWND by 1.
+ AckNPackets(number_of_packets_in_window - 1);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Next ack should increase cwnd by 1.
+ AckNPackets(1);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Now RTO and ensure slow start gets reset.
+ EXPECT_TRUE(sender_->hybrid_slow_start().started());
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
+}
+
+TEST_F(TcpCubicSenderBytesTest, SlowStartHalfPacketLossWithLargeReduction) {
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kSSLR);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ sender_->SetFromConfig(config, Perspective::IS_SERVER);
+
+ sender_->SetNumEmulatedConnections(1);
+ const int kNumberOfAcks = 10;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window in half sized packets.
+ SendAvailableSendWindow(kDefaultTCPMSS / 2);
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow(kDefaultTCPMSS / 2);
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose a packet to exit slow start. We should now have fallen out of
+ // slow start with a window reduced by 1.
+ LoseNPackets(1);
+ expected_send_window -= kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose 10 packets in recovery and verify that congestion window is reduced
+ // by 5 packets.
+ LoseNPackets(10, kDefaultTCPMSS / 2);
+ expected_send_window -= 5 * kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, SlowStartPacketLossWithMaxHalfReduction) {
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kSSLR);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ sender_->SetFromConfig(config, Perspective::IS_SERVER);
+
+ sender_->SetNumEmulatedConnections(1);
+ const int kNumberOfAcks = kInitialCongestionWindowPackets / 2;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose a packet to exit slow start. We should now have fallen out of
+ // slow start with a window reduced by 1.
+ LoseNPackets(1);
+ expected_send_window -= kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose half the outstanding packets in recovery and verify the congestion
+ // window is only reduced by a max of half.
+ LoseNPackets(kNumberOfAcks * 2);
+ expected_send_window -= (kNumberOfAcks * 2 - 1) * kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ LoseNPackets(5);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, NoPRRWhenLessThanOnePacketInFlight) {
+ SendAvailableSendWindow();
+ LoseNPackets(kInitialCongestionWindowPackets - 1);
+ AckNPackets(1);
+ // PRR will allow 2 packets for every ack during recovery.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+ // Simulate abandoning all packets by supplying a bytes_in_flight of 0.
+ // PRR should now allow a packet to be sent, even though prr's state variables
+ // believe it has sent enough packets.
+ EXPECT_TRUE(sender_->CanSend(0));
+}
+
+TEST_F(TcpCubicSenderBytesTest, SlowStartPacketLossPRR) {
+ sender_->SetNumEmulatedConnections(1);
+ // Test based on the first example in RFC6937.
+ // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start with a reduced window.
+ size_t send_window_before_loss = expected_send_window;
+ expected_send_window *= kRenoBeta;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Testing TCP proportional rate reduction.
+ // We should send packets paced over the received acks for the remaining
+ // outstanding packets. The number of packets before we exit recovery is the
+ // original CWND minus the packet that has been lost and the one which
+ // triggered the loss.
+ size_t remaining_packets_in_recovery =
+ send_window_before_loss / kDefaultTCPMSS - 2;
+
+ for (size_t i = 0; i < remaining_packets_in_recovery; ++i) {
+ AckNPackets(1);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // We need to ack another window before we increase CWND by 1.
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ for (size_t i = 0; i < number_of_packets_in_window; ++i) {
+ AckNPackets(1);
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ AckNPackets(1);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, SlowStartBurstPacketLossPRR) {
+ sender_->SetNumEmulatedConnections(1);
+ // Test based on the second example in RFC6937, though we also implement
+ // forward acknowledgements, so the first two incoming acks will trigger
+ // PRR immediately.
+ // Ack 20 packets in 10 acks to raise the CWND to 30.
+ const int kNumberOfAcks = 10;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose one more than the congestion window reduction, so that after loss,
+ // bytes_in_flight is lesser than the congestion window.
+ size_t send_window_after_loss = kRenoBeta * expected_send_window;
+ size_t num_packets_to_lose =
+ (expected_send_window - send_window_after_loss) / kDefaultTCPMSS + 1;
+ LoseNPackets(num_packets_to_lose);
+ // Immediately after the loss, ensure at least one packet can be sent.
+ // Losses without subsequent acks can occur with timer based loss detection.
+ EXPECT_TRUE(sender_->CanSend(bytes_in_flight_));
+ AckNPackets(1);
+
+ // We should now have fallen out of slow start with a reduced window.
+ expected_send_window *= kRenoBeta;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Only 2 packets should be allowed to be sent, per PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Ack the next packet, which triggers another loss.
+ LoseNPackets(1);
+ AckNPackets(1);
+
+ // Send 2 packets to simulate PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Ack the next packet, which triggers another loss.
+ LoseNPackets(1);
+ AckNPackets(1);
+
+ // Send 2 packets to simulate PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Exit recovery and return to sending at the new rate.
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ AckNPackets(1);
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ }
+}
+
+TEST_F(TcpCubicSenderBytesTest, RTOCongestionWindow) {
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+ // Expect the window to decrease to the minimum once the RTO fires and slow
+ // start threshold to be set to 1/2 of the CWND.
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_EQ(2 * kDefaultTCPMSS, sender_->GetCongestionWindow());
+ EXPECT_EQ(5u * kDefaultTCPMSS, sender_->GetSlowStartThreshold());
+}
+
+TEST_F(TcpCubicSenderBytesTest, RTOCongestionWindowNoRetransmission) {
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+
+ // Expect the window to remain unchanged if the RTO fires but no packets are
+ // retransmitted.
+ sender_->OnRetransmissionTimeout(false);
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, TcpCubicResetEpochOnQuiescence) {
+ const int kMaxCongestionWindow = 50;
+ const QuicByteCount kMaxCongestionWindowBytes =
+ kMaxCongestionWindow * kDefaultTCPMSS;
+ int num_sent = SendAvailableSendWindow();
+
+ // Make sure we fall out of slow start.
+ QuicByteCount saved_cwnd = sender_->GetCongestionWindow();
+ LoseNPackets(1);
+ EXPECT_GT(saved_cwnd, sender_->GetCongestionWindow());
+
+ // Ack the rest of the outstanding packets to get out of recovery.
+ for (int i = 1; i < num_sent; ++i) {
+ AckNPackets(1);
+ }
+ EXPECT_EQ(0u, bytes_in_flight_);
+
+ // Send a new window of data and ack all; cubic growth should occur.
+ saved_cwnd = sender_->GetCongestionWindow();
+ num_sent = SendAvailableSendWindow();
+ for (int i = 0; i < num_sent; ++i) {
+ AckNPackets(1);
+ }
+ EXPECT_LT(saved_cwnd, sender_->GetCongestionWindow());
+ EXPECT_GT(kMaxCongestionWindowBytes, sender_->GetCongestionWindow());
+ EXPECT_EQ(0u, bytes_in_flight_);
+
+ // Quiescent time of 100 seconds
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100000));
+
+ // Send new window of data and ack one packet. Cubic epoch should have
+ // been reset; ensure cwnd increase is not dramatic.
+ saved_cwnd = sender_->GetCongestionWindow();
+ SendAvailableSendWindow();
+ AckNPackets(1);
+ EXPECT_NEAR(saved_cwnd, sender_->GetCongestionWindow(), kDefaultTCPMSS);
+ EXPECT_GT(kMaxCongestionWindowBytes, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, MultipleLossesInOneWindow) {
+ SendAvailableSendWindow();
+ const QuicByteCount initial_window = sender_->GetCongestionWindow();
+ LosePacket(acked_packet_number_ + 1);
+ const QuicByteCount post_loss_window = sender_->GetCongestionWindow();
+ EXPECT_GT(initial_window, post_loss_window);
+ LosePacket(acked_packet_number_ + 3);
+ EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow());
+ LosePacket(packet_number_ - 1);
+ EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow());
+
+ // Lose a later packet and ensure the window decreases.
+ LosePacket(packet_number_);
+ EXPECT_GT(post_loss_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, ConfigureMaxInitialWindow) {
+ SetQuicReloadableFlag(quic_unified_iw_options, false);
+ QuicConfig config;
+
+ // Verify that kCOPT: kIW10 forces the congestion window to the default of 10.
+ QuicTagVector options;
+ options.push_back(kIW10);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ sender_->SetFromConfig(config, Perspective::IS_SERVER);
+ EXPECT_EQ(10u * kDefaultTCPMSS, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, SetInitialCongestionWindow) {
+ EXPECT_NE(3u * kDefaultTCPMSS, sender_->GetCongestionWindow());
+ sender_->SetInitialCongestionWindowInPackets(3);
+ EXPECT_EQ(3u * kDefaultTCPMSS, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, 2ConnectionCongestionAvoidanceAtEndOfRecovery) {
+ sender_->SetNumEmulatedConnections(2);
+ // Ack 10 packets in 5 acks to raise the CWND to 20.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start with a reduced window.
+ expected_send_window = expected_send_window * sender_->GetRenoBeta();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // No congestion window growth should occur in recovery phase, i.e., until the
+ // currently outstanding 20 packets are acked.
+ for (int i = 0; i < 10; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ EXPECT_TRUE(sender_->InRecovery());
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+ EXPECT_FALSE(sender_->InRecovery());
+
+ // Out of recovery now. Congestion window should not grow for half an RTT.
+ size_t packets_in_send_window = expected_send_window / kDefaultTCPMSS;
+ SendAvailableSendWindow();
+ AckNPackets(packets_in_send_window / 2 - 2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Next ack should increase congestion window by 1MSS.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ expected_send_window += kDefaultTCPMSS;
+ packets_in_send_window += 1;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Congestion window should remain steady again for half an RTT.
+ SendAvailableSendWindow();
+ AckNPackets(packets_in_send_window / 2 - 1);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Next ack should cause congestion window to grow by 1MSS.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, 1ConnectionCongestionAvoidanceAtEndOfRecovery) {
+ sender_->SetNumEmulatedConnections(1);
+ // Ack 10 packets in 5 acks to raise the CWND to 20.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start with a reduced window.
+ expected_send_window *= kRenoBeta;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // No congestion window growth should occur in recovery phase, i.e., until the
+ // currently outstanding 20 packets are acked.
+ for (int i = 0; i < 10; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ EXPECT_TRUE(sender_->InRecovery());
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+ EXPECT_FALSE(sender_->InRecovery());
+
+ // Out of recovery now. Congestion window should not grow during RTT.
+ for (uint64_t i = 0; i < expected_send_window / kDefaultTCPMSS - 2; i += 2) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // Next ack should cause congestion window to grow by 1MSS.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, BandwidthResumption) {
+ // Test that when provided with CachedNetworkParameters and opted in to the
+ // bandwidth resumption experiment, that the TcpCubicSenderPackets sets
+ // initial CWND appropriately.
+
+ // Set some common values.
+ const QuicPacketCount kNumberOfPackets = 123;
+ const QuicBandwidth kBandwidthEstimate =
+ QuicBandwidth::FromBytesPerSecond(kNumberOfPackets * kDefaultTCPMSS);
+ const QuicTime::Delta kRttEstimate = QuicTime::Delta::FromSeconds(1);
+ sender_->AdjustNetworkParameters(kBandwidthEstimate, kRttEstimate);
+ EXPECT_EQ(kNumberOfPackets * kDefaultTCPMSS, sender_->GetCongestionWindow());
+
+ // Resume with an illegal value of 0 and verify the server ignores it.
+ sender_->AdjustNetworkParameters(QuicBandwidth::Zero(), kRttEstimate);
+ EXPECT_EQ(kNumberOfPackets * kDefaultTCPMSS, sender_->GetCongestionWindow());
+
+ // Resumed CWND is limited to be in a sensible range.
+ const QuicBandwidth kUnreasonableBandwidth =
+ QuicBandwidth::FromBytesPerSecond((kMaxCongestionWindowPackets + 1) *
+ kDefaultTCPMSS);
+ sender_->AdjustNetworkParameters(kUnreasonableBandwidth,
+ QuicTime::Delta::FromSeconds(1));
+ EXPECT_EQ(kMaxCongestionWindowPackets * kDefaultTCPMSS,
+ sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderBytesTest, PaceBelowCWND) {
+ QuicConfig config;
+
+ // Verify that kCOPT: kMIN4 forces the min CWND to 1 packet, but allows up
+ // to 4 to be sent.
+ QuicTagVector options;
+ options.push_back(kMIN4);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ sender_->SetFromConfig(config, Perspective::IS_SERVER);
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_EQ(kDefaultTCPMSS, sender_->GetCongestionWindow());
+ EXPECT_TRUE(sender_->CanSend(kDefaultTCPMSS));
+ EXPECT_TRUE(sender_->CanSend(2 * kDefaultTCPMSS));
+ EXPECT_TRUE(sender_->CanSend(3 * kDefaultTCPMSS));
+ EXPECT_FALSE(sender_->CanSend(4 * kDefaultTCPMSS));
+}
+
+TEST_F(TcpCubicSenderBytesTest, NoPRR) {
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
+ sender_->rtt_stats_.UpdateRtt(rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+
+ sender_->SetNumEmulatedConnections(1);
+ // Verify that kCOPT: kNPRR allows all packets to be sent, even if only one
+ // ack has been received.
+ QuicTagVector options;
+ options.push_back(kNPRR);
+ QuicConfig config;
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ sender_->SetFromConfig(config, Perspective::IS_SERVER);
+ SendAvailableSendWindow();
+ LoseNPackets(9);
+ AckNPackets(1);
+
+ // We should now have fallen out of slow start with a reduced window.
+ EXPECT_EQ(kRenoBeta * kDefaultWindowTCP, sender_->GetCongestionWindow());
+ const QuicPacketCount window_in_packets =
+ kRenoBeta * kDefaultWindowTCP / kDefaultTCPMSS;
+ const QuicBandwidth expected_pacing_rate =
+ QuicBandwidth::FromBytesAndTimeDelta(kRenoBeta * kDefaultWindowTCP,
+ sender_->rtt_stats_.smoothed_rtt());
+ EXPECT_EQ(expected_pacing_rate, sender_->PacingRate(0));
+ EXPECT_EQ(window_in_packets,
+ static_cast<uint64_t>(SendAvailableSendWindow()));
+ EXPECT_EQ(expected_pacing_rate,
+ sender_->PacingRate(kRenoBeta * kDefaultWindowTCP));
+}
+
+TEST_F(TcpCubicSenderBytesTest, ResetAfterConnectionMigration) {
+ // Starts from slow start.
+ sender_->SetNumEmulatedConnections(1);
+ const int kNumberOfAcks = 10;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Loses a packet to exit slow start.
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start with a reduced window. Slow
+ // start threshold is also updated.
+ expected_send_window *= kRenoBeta;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetSlowStartThreshold());
+
+ // Resets cwnd and slow start threshold on connection migrations.
+ sender_->OnConnectionMigration();
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+ EXPECT_EQ(kMaxCongestionWindowPackets * kDefaultTCPMSS,
+ sender_->GetSlowStartThreshold());
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
+}
+
+TEST_F(TcpCubicSenderBytesTest, DefaultMaxCwnd) {
+ RttStats rtt_stats;
+ QuicConnectionStats stats;
+ std::unique_ptr<SendAlgorithmInterface> sender(SendAlgorithmInterface::Create(
+ &clock_, &rtt_stats, /*unacked_packets=*/nullptr, kCubicBytes,
+ QuicRandom::GetInstance(), &stats, kInitialCongestionWindow));
+
+ AckedPacketVector acked_packets;
+ LostPacketVector missing_packets;
+ for (uint64_t i = 1; i < kDefaultMaxCongestionWindowPackets; ++i) {
+ acked_packets.clear();
+ acked_packets.push_back(
+ AckedPacket(QuicPacketNumber(i), 1350, QuicTime::Zero()));
+ sender->OnCongestionEvent(true, sender->GetCongestionWindow(), clock_.Now(),
+ acked_packets, missing_packets);
+ }
+ EXPECT_EQ(kDefaultMaxCongestionWindowPackets,
+ sender->GetCongestionWindow() / kDefaultTCPMSS);
+}
+
+TEST_F(TcpCubicSenderBytesTest, LimitCwndIncreaseInCongestionAvoidance) {
+ // Enable Cubic.
+ sender_ = QuicMakeUnique<TcpCubicSenderBytesPeer>(&clock_, false);
+
+ int num_sent = SendAvailableSendWindow();
+
+ // Make sure we fall out of slow start.
+ QuicByteCount saved_cwnd = sender_->GetCongestionWindow();
+ LoseNPackets(1);
+ EXPECT_GT(saved_cwnd, sender_->GetCongestionWindow());
+
+ // Ack the rest of the outstanding packets to get out of recovery.
+ for (int i = 1; i < num_sent; ++i) {
+ AckNPackets(1);
+ }
+ EXPECT_EQ(0u, bytes_in_flight_);
+ // Send a new window of data and ack all; cubic growth should occur.
+ saved_cwnd = sender_->GetCongestionWindow();
+ num_sent = SendAvailableSendWindow();
+
+ // Ack packets until the CWND increases.
+ while (sender_->GetCongestionWindow() == saved_cwnd) {
+ AckNPackets(1);
+ SendAvailableSendWindow();
+ }
+ // Bytes in flight may be larger than the CWND if the CWND isn't an exact
+ // multiple of the packet sizes being sent.
+ EXPECT_GE(bytes_in_flight_, sender_->GetCongestionWindow());
+ saved_cwnd = sender_->GetCongestionWindow();
+
+ // Advance time 2 seconds waiting for an ack.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2000));
+
+ // Ack two packets. The CWND should increase by only one packet.
+ AckNPackets(2);
+ EXPECT_EQ(saved_cwnd + kDefaultTCPMSS, sender_->GetCongestionWindow());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc
new file mode 100644
index 00000000000..8db151b2eff
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.cc
@@ -0,0 +1,86 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h"
+
+#include <algorithm>
+
+namespace quic {
+
+UberLossAlgorithm::UberLossAlgorithm() : UberLossAlgorithm(kNack) {}
+
+UberLossAlgorithm::UberLossAlgorithm(LossDetectionType loss_type)
+ : loss_type_(loss_type) {
+ SetLossDetectionType(loss_type);
+ for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+ general_loss_algorithms_[i].SetPacketNumberSpace(
+ static_cast<PacketNumberSpace>(i));
+ }
+}
+
+LossDetectionType UberLossAlgorithm::GetLossDetectionType() const {
+ return loss_type_;
+}
+
+void UberLossAlgorithm::SetLossDetectionType(LossDetectionType loss_type) {
+ loss_type_ = loss_type;
+ for (auto& loss_algorithm : general_loss_algorithms_) {
+ loss_algorithm.SetLossDetectionType(loss_type);
+ }
+}
+
+void UberLossAlgorithm::DetectLosses(
+ const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber /*largest_newly_acked*/,
+ const AckedPacketVector& packets_acked,
+ LostPacketVector* packets_lost) {
+ DCHECK(unacked_packets.use_uber_loss_algorithm());
+ for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+ const QuicPacketNumber largest_acked =
+ unacked_packets.GetLargestAckedOfPacketNumberSpace(
+ static_cast<PacketNumberSpace>(i));
+ if (!largest_acked.IsInitialized() ||
+ unacked_packets.GetLeastUnacked() > largest_acked) {
+ // Skip detecting losses if no packet has been received for this packet
+ // number space or the least_unacked is greater than largest_acked.
+ continue;
+ }
+
+ general_loss_algorithms_[i].DetectLosses(unacked_packets, time, rtt_stats,
+ largest_acked, packets_acked,
+ packets_lost);
+ }
+}
+
+QuicTime UberLossAlgorithm::GetLossTimeout() const {
+ QuicTime loss_timeout = QuicTime::Zero();
+ // Returns the earliest non-zero loss timeout.
+ for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+ const QuicTime timeout = general_loss_algorithms_[i].GetLossTimeout();
+ if (!loss_timeout.IsInitialized()) {
+ loss_timeout = timeout;
+ continue;
+ }
+ if (timeout.IsInitialized()) {
+ loss_timeout = std::min(loss_timeout, timeout);
+ }
+ }
+ return loss_timeout;
+}
+
+void UberLossAlgorithm::SpuriousRetransmitDetected(
+ const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber spurious_retransmission) {
+ DCHECK(unacked_packets.use_uber_loss_algorithm());
+ general_loss_algorithms_[unacked_packets.GetPacketNumberSpace(
+ spurious_retransmission)]
+ .SpuriousRetransmitDetected(unacked_packets, time, rtt_stats,
+ spurious_retransmission);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h
new file mode 100644
index 00000000000..dddbcb3ddcb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h
@@ -0,0 +1,53 @@
+// Copyright 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.
+
+#ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h"
+
+namespace quic {
+
+// This class comprises multiple loss algorithms, each per packet number space.
+class QUIC_EXPORT_PRIVATE UberLossAlgorithm : public LossDetectionInterface {
+ public:
+ UberLossAlgorithm();
+ explicit UberLossAlgorithm(LossDetectionType loss_type);
+ UberLossAlgorithm(const UberLossAlgorithm&) = delete;
+ UberLossAlgorithm& operator=(const UberLossAlgorithm&) = delete;
+ ~UberLossAlgorithm() override {}
+
+ LossDetectionType GetLossDetectionType() const override;
+
+ // Switches the loss detection type to |loss_type| and resets loss algorithm
+ // for all packet number spaces.
+ void SetLossDetectionType(LossDetectionType loss_type);
+
+ // Detects lost packets.
+ void DetectLosses(const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber largest_newly_acked,
+ const AckedPacketVector& packets_acked,
+ LostPacketVector* packets_lost) override;
+
+ // Returns the earliest time the early retransmit timer should be active.
+ QuicTime GetLossTimeout() const override;
+
+ // Increases the loss detection threshold for time loss detection.
+ void SpuriousRetransmitDetected(
+ const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber spurious_retransmission) override;
+
+ private:
+ LossDetectionType loss_type_;
+ // One loss algorithm per packet number space.
+ GeneralLossAlgorithm general_loss_algorithms_[NUM_PACKET_NUMBER_SPACES];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_UBER_LOSS_ALGORITHM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc
new file mode 100644
index 00000000000..33b93930db2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc
@@ -0,0 +1,159 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+// Default packet length.
+const uint32_t kDefaultLength = 1000;
+
+class UberLossAlgorithmTest : public QuicTest {
+ protected:
+ UberLossAlgorithmTest() {
+ SetQuicReloadableFlag(quic_use_uber_loss_algorithm, true);
+ unacked_packets_ =
+ QuicMakeUnique<QuicUnackedPacketMap>(Perspective::IS_CLIENT);
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(), clock_.Now());
+ EXPECT_LT(0, rtt_stats_.smoothed_rtt().ToMicroseconds());
+ }
+
+ void SendPacket(uint64_t packet_number, EncryptionLevel encryption_level) {
+ QuicStreamFrame frame;
+ frame.stream_id =
+ encryption_level == ENCRYPTION_INITIAL
+ ? QuicUtils::GetCryptoStreamId(
+ CurrentSupportedVersions()[0].transport_version)
+ : QuicUtils::GetHeadersStreamId(
+ CurrentSupportedVersions()[0].transport_version);
+ SerializedPacket packet(QuicPacketNumber(packet_number),
+ PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+ false, false);
+ packet.encryption_level = encryption_level;
+ packet.retransmittable_frames.push_back(QuicFrame(frame));
+ unacked_packets_->AddSentPacket(&packet, QuicPacketNumber(),
+ NOT_RETRANSMISSION, clock_.Now(), true);
+ }
+
+ void AckPackets(const std::vector<uint64_t>& packets_acked) {
+ packets_acked_.clear();
+ for (uint64_t acked : packets_acked) {
+ unacked_packets_->RemoveFromInFlight(QuicPacketNumber(acked));
+ packets_acked_.push_back(AckedPacket(
+ QuicPacketNumber(acked), kMaxOutgoingPacketSize, QuicTime::Zero()));
+ }
+ }
+
+ void VerifyLosses(uint64_t largest_newly_acked,
+ const AckedPacketVector& packets_acked,
+ const std::vector<uint64_t>& losses_expected) {
+ LostPacketVector lost_packets;
+ loss_algorithm_.DetectLosses(*unacked_packets_, clock_.Now(), rtt_stats_,
+ QuicPacketNumber(largest_newly_acked),
+ packets_acked, &lost_packets);
+ ASSERT_EQ(losses_expected.size(), lost_packets.size());
+ for (size_t i = 0; i < losses_expected.size(); ++i) {
+ EXPECT_EQ(lost_packets[i].packet_number,
+ QuicPacketNumber(losses_expected[i]));
+ }
+ }
+
+ MockClock clock_;
+ std::unique_ptr<QuicUnackedPacketMap> unacked_packets_;
+ RttStats rtt_stats_;
+ UberLossAlgorithm loss_algorithm_;
+ AckedPacketVector packets_acked_;
+};
+
+TEST_F(UberLossAlgorithmTest, ScenarioA) {
+ // This test mimics a scenario: client sends 1-CHLO, 2-0RTT, 3-0RTT,
+ // timeout and retransmits 4-CHLO. Server acks packet 1 (ack gets lost).
+ // Server receives and buffers packets 2 and 3. Server receives packet 4 and
+ // processes handshake asynchronously, so server acks 4 and cannot process
+ // packets 2 and 3.
+ SendPacket(1, ENCRYPTION_INITIAL);
+ SendPacket(2, ENCRYPTION_ZERO_RTT);
+ SendPacket(3, ENCRYPTION_ZERO_RTT);
+ unacked_packets_->RemoveFromInFlight(QuicPacketNumber(1));
+ SendPacket(4, ENCRYPTION_INITIAL);
+
+ AckPackets({1, 4});
+ unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+ HANDSHAKE_DATA, QuicPacketNumber(4));
+ // Verify no packet is detected lost.
+ VerifyLosses(4, packets_acked_, std::vector<uint64_t>{});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(UberLossAlgorithmTest, ScenarioB) {
+ // This test mimics a scenario: client sends 3-0RTT, 4-0RTT, receives SHLO,
+ // sends 5-1RTT, 6-1RTT.
+ SendPacket(3, ENCRYPTION_ZERO_RTT);
+ SendPacket(4, ENCRYPTION_ZERO_RTT);
+ SendPacket(5, ENCRYPTION_FORWARD_SECURE);
+ SendPacket(6, ENCRYPTION_FORWARD_SECURE);
+
+ AckPackets({4});
+ unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(4));
+ // No packet loss by acking 4.
+ VerifyLosses(4, packets_acked_, std::vector<uint64_t>{});
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+
+ // Acking 6 causes 3 to be detected loss.
+ AckPackets({6});
+ unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(6));
+ VerifyLosses(6, packets_acked_, std::vector<uint64_t>{3});
+ EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout());
+ packets_acked_.clear();
+
+ clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt());
+ // Verify 5 will be early retransmitted.
+ VerifyLosses(6, packets_acked_, {5});
+}
+
+TEST_F(UberLossAlgorithmTest, ScenarioC) {
+ // This test mimics a scenario: server sends 1-SHLO, 2-1RTT, 3-1RTT, 4-1RTT
+ // and retransmit 4-SHLO. Client receives and buffers packet 4. Client
+ // receives packet 5 and processes 4.
+ QuicUnackedPacketMapPeer::SetPerspective(unacked_packets_.get(),
+ Perspective::IS_SERVER);
+ SendPacket(1, ENCRYPTION_ZERO_RTT);
+ SendPacket(2, ENCRYPTION_FORWARD_SECURE);
+ SendPacket(3, ENCRYPTION_FORWARD_SECURE);
+ SendPacket(4, ENCRYPTION_FORWARD_SECURE);
+ unacked_packets_->RemoveFromInFlight(QuicPacketNumber(1));
+ SendPacket(5, ENCRYPTION_ZERO_RTT);
+
+ AckPackets({4, 5});
+ unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(4));
+ unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+ HANDSHAKE_DATA, QuicPacketNumber(5));
+ // No packet loss by acking 5.
+ VerifyLosses(5, packets_acked_, std::vector<uint64_t>{});
+ EXPECT_EQ(clock_.Now() + 1.25 * rtt_stats_.smoothed_rtt(),
+ loss_algorithm_.GetLossTimeout());
+ packets_acked_.clear();
+
+ clock_.AdvanceTime(1.25 * rtt_stats_.latest_rtt());
+ // Verify 2 and 3 will be early retransmitted.
+ VerifyLosses(5, packets_acked_, std::vector<uint64_t>{2, 3});
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h b/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h
new file mode 100644
index 00000000000..8729895d9e0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h
@@ -0,0 +1,160 @@
+// 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 QUICHE_QUIC_CORE_CONGESTION_CONTROL_WINDOWED_FILTER_H_
+#define QUICHE_QUIC_CORE_CONGESTION_CONTROL_WINDOWED_FILTER_H_
+
+// Implements Kathleen Nichols' algorithm for tracking the minimum (or maximum)
+// estimate of a stream of samples over some fixed time interval. (E.g.,
+// the minimum RTT over the past five minutes.) The algorithm keeps track of
+// the best, second best, and third best min (or max) estimates, maintaining an
+// invariant that the measurement time of the n'th best >= n-1'th best.
+
+// The algorithm works as follows. On a reset, all three estimates are set to
+// the same sample. The second best estimate is then recorded in the second
+// quarter of the window, and a third best estimate is recorded in the second
+// half of the window, bounding the worst case error when the true min is
+// monotonically increasing (or true max is monotonically decreasing) over the
+// window.
+//
+// A new best sample replaces all three estimates, since the new best is lower
+// (or higher) than everything else in the window and it is the most recent.
+// The window thus effectively gets reset on every new min. The same property
+// holds true for second best and third best estimates. Specifically, when a
+// sample arrives that is better than the second best but not better than the
+// best, it replaces the second and third best estimates but not the best
+// estimate. Similarly, a sample that is better than the third best estimate
+// but not the other estimates replaces only the third best estimate.
+//
+// Finally, when the best expires, it is replaced by the second best, which in
+// turn is replaced by the third best. The newest sample replaces the third
+// best.
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+
+namespace quic {
+
+// Compares two values and returns true if the first is less than or equal
+// to the second.
+template <class T>
+struct MinFilter {
+ bool operator()(const T& lhs, const T& rhs) const { return lhs <= rhs; }
+};
+
+// Compares two values and returns true if the first is greater than or equal
+// to the second.
+template <class T>
+struct MaxFilter {
+ bool operator()(const T& lhs, const T& rhs) const { return lhs >= rhs; }
+};
+
+// Use the following to construct a windowed filter object of type T.
+// For example, a min filter using QuicTime as the time type:
+// WindowedFilter<T, MinFilter<T>, QuicTime, QuicTime::Delta> ObjectName;
+// A max filter using 64-bit integers as the time type:
+// WindowedFilter<T, MaxFilter<T>, uint64_t, int64_t> ObjectName;
+// Specifically, this template takes four arguments:
+// 1. T -- type of the measurement that is being filtered.
+// 2. Compare -- MinFilter<T> or MaxFilter<T>, depending on the type of filter
+// desired.
+// 3. TimeT -- the type used to represent timestamps.
+// 4. TimeDeltaT -- the type used to represent continuous time intervals between
+// two timestamps. Has to be the type of (a - b) if both |a| and |b| are
+// of type TimeT.
+template <class T, class Compare, typename TimeT, typename TimeDeltaT>
+class WindowedFilter {
+ public:
+ // |window_length| is the period after which a best estimate expires.
+ // |zero_value| is used as the uninitialized value for objects of T.
+ // Importantly, |zero_value| should be an invalid value for a true sample.
+ WindowedFilter(TimeDeltaT window_length, T zero_value, TimeT zero_time)
+ : window_length_(window_length),
+ zero_value_(zero_value),
+ estimates_{Sample(zero_value_, zero_time),
+ Sample(zero_value_, zero_time),
+ Sample(zero_value_, zero_time)} {}
+
+ // Changes the window length. Does not update any current samples.
+ void SetWindowLength(TimeDeltaT window_length) {
+ window_length_ = window_length;
+ }
+
+ // Updates best estimates with |sample|, and expires and updates best
+ // estimates as necessary.
+ void Update(T new_sample, TimeT new_time) {
+ // Reset all estimates if they have not yet been initialized, if new sample
+ // is a new best, or if the newest recorded estimate is too old.
+ if (estimates_[0].sample == zero_value_ ||
+ Compare()(new_sample, estimates_[0].sample) ||
+ new_time - estimates_[2].time > window_length_) {
+ Reset(new_sample, new_time);
+ return;
+ }
+
+ if (Compare()(new_sample, estimates_[1].sample)) {
+ estimates_[1] = Sample(new_sample, new_time);
+ estimates_[2] = estimates_[1];
+ } else if (Compare()(new_sample, estimates_[2].sample)) {
+ estimates_[2] = Sample(new_sample, new_time);
+ }
+
+ // Expire and update estimates as necessary.
+ if (new_time - estimates_[0].time > window_length_) {
+ // The best estimate hasn't been updated for an entire window, so promote
+ // second and third best estimates.
+ estimates_[0] = estimates_[1];
+ estimates_[1] = estimates_[2];
+ estimates_[2] = Sample(new_sample, new_time);
+ // Need to iterate one more time. Check if the new best estimate is
+ // outside the window as well, since it may also have been recorded a
+ // long time ago. Don't need to iterate once more since we cover that
+ // case at the beginning of the method.
+ if (new_time - estimates_[0].time > window_length_) {
+ estimates_[0] = estimates_[1];
+ estimates_[1] = estimates_[2];
+ }
+ return;
+ }
+ if (estimates_[1].sample == estimates_[0].sample &&
+ new_time - estimates_[1].time > window_length_ >> 2) {
+ // A quarter of the window has passed without a better sample, so the
+ // second-best estimate is taken from the second quarter of the window.
+ estimates_[2] = estimates_[1] = Sample(new_sample, new_time);
+ return;
+ }
+
+ if (estimates_[2].sample == estimates_[1].sample &&
+ new_time - estimates_[2].time > window_length_ >> 1) {
+ // We've passed a half of the window without a better estimate, so take
+ // a third-best estimate from the second half of the window.
+ estimates_[2] = Sample(new_sample, new_time);
+ }
+ }
+
+ // Resets all estimates to new sample.
+ void Reset(T new_sample, TimeT new_time) {
+ estimates_[0] = estimates_[1] = estimates_[2] =
+ Sample(new_sample, new_time);
+ }
+
+ T GetBest() const { return estimates_[0].sample; }
+ T GetSecondBest() const { return estimates_[1].sample; }
+ T GetThirdBest() const { return estimates_[2].sample; }
+
+ private:
+ struct Sample {
+ T sample;
+ TimeT time;
+ Sample(T init_sample, TimeT init_time)
+ : sample(init_sample), time(init_time) {}
+ };
+
+ TimeDeltaT window_length_; // Time length of window.
+ T zero_value_; // Uninitialized value of T.
+ Sample estimates_[3]; // Best estimate is element 0.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_WINDOWED_FILTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter_test.cc b/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter_test.cc
new file mode 100644
index 00000000000..d9f3655d203
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/congestion_control/windowed_filter_test.cc
@@ -0,0 +1,387 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class WindowedFilterTest : public QuicTest {
+ public:
+ // Set the window to 99ms, so 25ms is more than a quarter rtt.
+ WindowedFilterTest()
+ : windowed_min_rtt_(QuicTime::Delta::FromMilliseconds(99),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero()),
+ windowed_max_bw_(QuicTime::Delta::FromMilliseconds(99),
+ QuicBandwidth::Zero(),
+ QuicTime::Zero()) {}
+
+ // Sets up windowed_min_rtt_ to have the following values:
+ // Best = 20ms, recorded at 25ms
+ // Second best = 40ms, recorded at 75ms
+ // Third best = 50ms, recorded at 100ms
+ void InitializeMinFilter() {
+ QuicTime now = QuicTime::Zero();
+ QuicTime::Delta rtt_sample = QuicTime::Delta::FromMilliseconds(10);
+ for (int i = 0; i < 5; ++i) {
+ windowed_min_rtt_.Update(rtt_sample, now);
+ VLOG(1) << "i: " << i << " sample: " << rtt_sample.ToMilliseconds()
+ << " mins: "
+ << " " << windowed_min_rtt_.GetBest().ToMilliseconds() << " "
+ << windowed_min_rtt_.GetSecondBest().ToMilliseconds() << " "
+ << windowed_min_rtt_.GetThirdBest().ToMilliseconds();
+ now = now + QuicTime::Delta::FromMilliseconds(25);
+ rtt_sample = rtt_sample + QuicTime::Delta::FromMilliseconds(10);
+ }
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20),
+ windowed_min_rtt_.GetBest());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40),
+ windowed_min_rtt_.GetSecondBest());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50),
+ windowed_min_rtt_.GetThirdBest());
+ }
+
+ // Sets up windowed_max_bw_ to have the following values:
+ // Best = 900 bps, recorded at 25ms
+ // Second best = 700 bps, recorded at 75ms
+ // Third best = 600 bps, recorded at 100ms
+ void InitializeMaxFilter() {
+ QuicTime now = QuicTime::Zero();
+ QuicBandwidth bw_sample = QuicBandwidth::FromBitsPerSecond(1000);
+ for (int i = 0; i < 5; ++i) {
+ windowed_max_bw_.Update(bw_sample, now);
+ VLOG(1) << "i: " << i << " sample: " << bw_sample.ToBitsPerSecond()
+ << " maxs: "
+ << " " << windowed_max_bw_.GetBest().ToBitsPerSecond() << " "
+ << windowed_max_bw_.GetSecondBest().ToBitsPerSecond() << " "
+ << windowed_max_bw_.GetThirdBest().ToBitsPerSecond();
+ now = now + QuicTime::Delta::FromMilliseconds(25);
+ bw_sample = bw_sample - QuicBandwidth::FromBitsPerSecond(100);
+ }
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(900),
+ windowed_max_bw_.GetBest());
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(700),
+ windowed_max_bw_.GetSecondBest());
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(600),
+ windowed_max_bw_.GetThirdBest());
+ }
+
+ protected:
+ WindowedFilter<QuicTime::Delta,
+ MinFilter<QuicTime::Delta>,
+ QuicTime,
+ QuicTime::Delta>
+ windowed_min_rtt_;
+ WindowedFilter<QuicBandwidth,
+ MaxFilter<QuicBandwidth>,
+ QuicTime,
+ QuicTime::Delta>
+ windowed_max_bw_;
+};
+
+namespace {
+// Test helper function: updates the filter with a lot of small values in order
+// to ensure that it is not susceptible to noise.
+void UpdateWithIrrelevantSamples(
+ WindowedFilter<uint64_t, MaxFilter<uint64_t>, uint64_t, uint64_t>* filter,
+ uint64_t max_value,
+ uint64_t time) {
+ for (uint64_t i = 0; i < 1000; i++) {
+ filter->Update(i % max_value, time);
+ }
+}
+} // namespace
+
+TEST_F(WindowedFilterTest, UninitializedEstimates) {
+ EXPECT_EQ(QuicTime::Delta::Zero(), windowed_min_rtt_.GetBest());
+ EXPECT_EQ(QuicTime::Delta::Zero(), windowed_min_rtt_.GetSecondBest());
+ EXPECT_EQ(QuicTime::Delta::Zero(), windowed_min_rtt_.GetThirdBest());
+ EXPECT_EQ(QuicBandwidth::Zero(), windowed_max_bw_.GetBest());
+ EXPECT_EQ(QuicBandwidth::Zero(), windowed_max_bw_.GetSecondBest());
+ EXPECT_EQ(QuicBandwidth::Zero(), windowed_max_bw_.GetThirdBest());
+}
+
+TEST_F(WindowedFilterTest, MonotonicallyIncreasingMin) {
+ QuicTime now = QuicTime::Zero();
+ QuicTime::Delta rtt_sample = QuicTime::Delta::FromMilliseconds(10);
+ windowed_min_rtt_.Update(rtt_sample, now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), windowed_min_rtt_.GetBest());
+
+ // Gradually increase the rtt samples and ensure the windowed min rtt starts
+ // rising.
+ for (int i = 0; i < 6; ++i) {
+ now = now + QuicTime::Delta::FromMilliseconds(25);
+ rtt_sample = rtt_sample + QuicTime::Delta::FromMilliseconds(10);
+ windowed_min_rtt_.Update(rtt_sample, now);
+ VLOG(1) << "i: " << i << " sample: " << rtt_sample.ToMilliseconds()
+ << " mins: "
+ << " " << windowed_min_rtt_.GetBest().ToMilliseconds() << " "
+ << windowed_min_rtt_.GetSecondBest().ToMilliseconds() << " "
+ << windowed_min_rtt_.GetThirdBest().ToMilliseconds();
+ if (i < 3) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ windowed_min_rtt_.GetBest());
+ } else if (i == 3) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20),
+ windowed_min_rtt_.GetBest());
+ } else if (i < 6) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40),
+ windowed_min_rtt_.GetBest());
+ }
+ }
+}
+
+TEST_F(WindowedFilterTest, MonotonicallyDecreasingMax) {
+ QuicTime now = QuicTime::Zero();
+ QuicBandwidth bw_sample = QuicBandwidth::FromBitsPerSecond(1000);
+ windowed_max_bw_.Update(bw_sample, now);
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(1000), windowed_max_bw_.GetBest());
+
+ // Gradually decrease the bw samples and ensure the windowed max bw starts
+ // decreasing.
+ for (int i = 0; i < 6; ++i) {
+ now = now + QuicTime::Delta::FromMilliseconds(25);
+ bw_sample = bw_sample - QuicBandwidth::FromBitsPerSecond(100);
+ windowed_max_bw_.Update(bw_sample, now);
+ VLOG(1) << "i: " << i << " sample: " << bw_sample.ToBitsPerSecond()
+ << " maxs: "
+ << " " << windowed_max_bw_.GetBest().ToBitsPerSecond() << " "
+ << windowed_max_bw_.GetSecondBest().ToBitsPerSecond() << " "
+ << windowed_max_bw_.GetThirdBest().ToBitsPerSecond();
+ if (i < 3) {
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(1000),
+ windowed_max_bw_.GetBest());
+ } else if (i == 3) {
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(900),
+ windowed_max_bw_.GetBest());
+ } else if (i < 6) {
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(700),
+ windowed_max_bw_.GetBest());
+ }
+ }
+}
+
+TEST_F(WindowedFilterTest, SampleChangesThirdBestMin) {
+ InitializeMinFilter();
+ // RTT sample lower than the third-choice min-rtt sets that, but nothing else.
+ QuicTime::Delta rtt_sample =
+ windowed_min_rtt_.GetThirdBest() - QuicTime::Delta::FromMilliseconds(5);
+ // This assert is necessary to avoid triggering -Wstrict-overflow
+ // See crbug/616957
+ ASSERT_GT(windowed_min_rtt_.GetThirdBest(),
+ QuicTime::Delta::FromMilliseconds(5));
+ // Latest sample was recorded at 100ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101);
+ windowed_min_rtt_.Update(rtt_sample, now);
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40),
+ windowed_min_rtt_.GetSecondBest());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), windowed_min_rtt_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, SampleChangesThirdBestMax) {
+ InitializeMaxFilter();
+ // BW sample higher than the third-choice max sets that, but nothing else.
+ QuicBandwidth bw_sample =
+ windowed_max_bw_.GetThirdBest() + QuicBandwidth::FromBitsPerSecond(50);
+ // Latest sample was recorded at 100ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101);
+ windowed_max_bw_.Update(bw_sample, now);
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest());
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(700),
+ windowed_max_bw_.GetSecondBest());
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(900), windowed_max_bw_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, SampleChangesSecondBestMin) {
+ InitializeMinFilter();
+ // RTT sample lower than the second-choice min sets that and also
+ // the third-choice min.
+ QuicTime::Delta rtt_sample =
+ windowed_min_rtt_.GetSecondBest() - QuicTime::Delta::FromMilliseconds(5);
+ // This assert is necessary to avoid triggering -Wstrict-overflow
+ // See crbug/616957
+ ASSERT_GT(windowed_min_rtt_.GetSecondBest(),
+ QuicTime::Delta::FromMilliseconds(5));
+ // Latest sample was recorded at 100ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101);
+ windowed_min_rtt_.Update(rtt_sample, now);
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest());
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), windowed_min_rtt_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, SampleChangesSecondBestMax) {
+ InitializeMaxFilter();
+ // BW sample higher than the second-choice max sets that and also
+ // the third-choice max.
+ QuicBandwidth bw_sample =
+ windowed_max_bw_.GetSecondBest() + QuicBandwidth::FromBitsPerSecond(50);
+ // Latest sample was recorded at 100ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101);
+ windowed_max_bw_.Update(bw_sample, now);
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest());
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest());
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(900), windowed_max_bw_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, SampleChangesAllMins) {
+ InitializeMinFilter();
+ // RTT sample lower than the first-choice min-rtt sets that and also
+ // the second and third-choice mins.
+ QuicTime::Delta rtt_sample =
+ windowed_min_rtt_.GetBest() - QuicTime::Delta::FromMilliseconds(5);
+ // This assert is necessary to avoid triggering -Wstrict-overflow
+ // See crbug/616957
+ ASSERT_GT(windowed_min_rtt_.GetBest(), QuicTime::Delta::FromMilliseconds(5));
+ // Latest sample was recorded at 100ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101);
+ windowed_min_rtt_.Update(rtt_sample, now);
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest());
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest());
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, SampleChangesAllMaxs) {
+ InitializeMaxFilter();
+ // BW sample higher than the first-choice max sets that and also
+ // the second and third-choice maxs.
+ QuicBandwidth bw_sample =
+ windowed_max_bw_.GetBest() + QuicBandwidth::FromBitsPerSecond(50);
+ // Latest sample was recorded at 100ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(101);
+ windowed_max_bw_.Update(bw_sample, now);
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest());
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest());
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, ExpireBestMin) {
+ InitializeMinFilter();
+ QuicTime::Delta old_third_best = windowed_min_rtt_.GetThirdBest();
+ QuicTime::Delta old_second_best = windowed_min_rtt_.GetSecondBest();
+ QuicTime::Delta rtt_sample =
+ old_third_best + QuicTime::Delta::FromMilliseconds(5);
+ // Best min sample was recorded at 25ms, so expiry time is 124ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(125);
+ windowed_min_rtt_.Update(rtt_sample, now);
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest());
+ EXPECT_EQ(old_third_best, windowed_min_rtt_.GetSecondBest());
+ EXPECT_EQ(old_second_best, windowed_min_rtt_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, ExpireBestMax) {
+ InitializeMaxFilter();
+ QuicBandwidth old_third_best = windowed_max_bw_.GetThirdBest();
+ QuicBandwidth old_second_best = windowed_max_bw_.GetSecondBest();
+ QuicBandwidth bw_sample =
+ old_third_best - QuicBandwidth::FromBitsPerSecond(50);
+ // Best max sample was recorded at 25ms, so expiry time is 124ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(125);
+ windowed_max_bw_.Update(bw_sample, now);
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest());
+ EXPECT_EQ(old_third_best, windowed_max_bw_.GetSecondBest());
+ EXPECT_EQ(old_second_best, windowed_max_bw_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, ExpireSecondBestMin) {
+ InitializeMinFilter();
+ QuicTime::Delta old_third_best = windowed_min_rtt_.GetThirdBest();
+ QuicTime::Delta rtt_sample =
+ old_third_best + QuicTime::Delta::FromMilliseconds(5);
+ // Second best min sample was recorded at 75ms, so expiry time is 174ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(175);
+ windowed_min_rtt_.Update(rtt_sample, now);
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest());
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest());
+ EXPECT_EQ(old_third_best, windowed_min_rtt_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, ExpireSecondBestMax) {
+ InitializeMaxFilter();
+ QuicBandwidth old_third_best = windowed_max_bw_.GetThirdBest();
+ QuicBandwidth bw_sample =
+ old_third_best - QuicBandwidth::FromBitsPerSecond(50);
+ // Second best max sample was recorded at 75ms, so expiry time is 174ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(175);
+ windowed_max_bw_.Update(bw_sample, now);
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest());
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest());
+ EXPECT_EQ(old_third_best, windowed_max_bw_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, ExpireAllMins) {
+ InitializeMinFilter();
+ QuicTime::Delta rtt_sample =
+ windowed_min_rtt_.GetThirdBest() + QuicTime::Delta::FromMilliseconds(5);
+ // This assert is necessary to avoid triggering -Wstrict-overflow
+ // See crbug/616957
+ ASSERT_LT(windowed_min_rtt_.GetThirdBest(),
+ QuicTime::Delta::Infinite() - QuicTime::Delta::FromMilliseconds(5));
+ // Third best min sample was recorded at 100ms, so expiry time is 199ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(200);
+ windowed_min_rtt_.Update(rtt_sample, now);
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetThirdBest());
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetSecondBest());
+ EXPECT_EQ(rtt_sample, windowed_min_rtt_.GetBest());
+}
+
+TEST_F(WindowedFilterTest, ExpireAllMaxs) {
+ InitializeMaxFilter();
+ QuicBandwidth bw_sample =
+ windowed_max_bw_.GetThirdBest() - QuicBandwidth::FromBitsPerSecond(50);
+ // Third best max sample was recorded at 100ms, so expiry time is 199ms.
+ QuicTime now = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(200);
+ windowed_max_bw_.Update(bw_sample, now);
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetThirdBest());
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetSecondBest());
+ EXPECT_EQ(bw_sample, windowed_max_bw_.GetBest());
+}
+
+// Test the windowed filter where the time used is an exact counter instead of a
+// timestamp. This is useful if, for example, the time is measured in round
+// trips.
+TEST_F(WindowedFilterTest, ExpireCounterBasedMax) {
+ // Create a window which starts at t = 0 and expires after two cycles.
+ WindowedFilter<uint64_t, MaxFilter<uint64_t>, uint64_t, uint64_t> max_filter(
+ 2, 0, 0);
+
+ const uint64_t kBest = 50000;
+ // Insert 50000 at t = 1.
+ max_filter.Update(50000, 1);
+ EXPECT_EQ(kBest, max_filter.GetBest());
+ UpdateWithIrrelevantSamples(&max_filter, 20, 1);
+ EXPECT_EQ(kBest, max_filter.GetBest());
+
+ // Insert 40000 at t = 2. Nothing is expected to expire.
+ max_filter.Update(40000, 2);
+ EXPECT_EQ(kBest, max_filter.GetBest());
+ UpdateWithIrrelevantSamples(&max_filter, 20, 2);
+ EXPECT_EQ(kBest, max_filter.GetBest());
+
+ // Insert 30000 at t = 3. Nothing is expected to expire yet.
+ max_filter.Update(30000, 3);
+ EXPECT_EQ(kBest, max_filter.GetBest());
+ UpdateWithIrrelevantSamples(&max_filter, 20, 3);
+ EXPECT_EQ(kBest, max_filter.GetBest());
+ VLOG(0) << max_filter.GetSecondBest();
+ VLOG(0) << max_filter.GetThirdBest();
+
+ // Insert 20000 at t = 4. 50000 at t = 1 expires, so 40000 becomes the new
+ // maximum.
+ const uint64_t kNewBest = 40000;
+ max_filter.Update(20000, 4);
+ EXPECT_EQ(kNewBest, max_filter.GetBest());
+ UpdateWithIrrelevantSamples(&max_filter, 20, 4);
+ EXPECT_EQ(kNewBest, max_filter.GetBest());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.cc
new file mode 100644
index 00000000000..8fec27309d9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+
+#include <cstdint>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/crypto.h"
+#include "third_party/boringssl/src/include/openssl/err.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Clear OpenSSL error stack.
+void ClearOpenSslErrors() {
+ while (ERR_get_error()) {
+ }
+}
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ ClearOpenSslErrors();
+#else
+ while (uint32_t error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, QUIC_ARRAYSIZE(buf));
+ QUIC_DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+const EVP_AEAD* InitAndCall(const EVP_AEAD* (*aead_getter)()) {
+ // Ensure BoringSSL is initialized before calling |aead_getter|. In Chromium,
+ // the static initializer is disabled.
+ CRYPTO_library_init();
+ return aead_getter();
+}
+
+} // namespace
+
+AeadBaseDecrypter::AeadBaseDecrypter(const EVP_AEAD* (*aead_getter)(),
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_size,
+ bool use_ietf_nonce_construction)
+ : aead_alg_(InitAndCall(aead_getter)),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_size_(nonce_size),
+ use_ietf_nonce_construction_(use_ietf_nonce_construction),
+ have_preliminary_key_(false) {
+ DCHECK_GT(256u, key_size);
+ DCHECK_GT(256u, auth_tag_size);
+ DCHECK_GT(256u, nonce_size);
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_size_, sizeof(iv_));
+}
+
+AeadBaseDecrypter::~AeadBaseDecrypter() {}
+
+bool AeadBaseDecrypter::SetKey(QuicStringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_, auth_tag_size_,
+ nullptr)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseDecrypter::SetNoncePrefix(QuicStringPiece nonce_prefix) {
+ if (use_ietf_nonce_construction_) {
+ QUIC_BUG << "Attempted to set nonce prefix on IETF QUIC crypter";
+ return false;
+ }
+ DCHECK_EQ(nonce_prefix.size(), nonce_size_ - sizeof(QuicPacketNumber));
+ if (nonce_prefix.size() != nonce_size_ - sizeof(QuicPacketNumber)) {
+ return false;
+ }
+ memcpy(iv_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::SetIV(QuicStringPiece iv) {
+ if (!use_ietf_nonce_construction_) {
+ QUIC_BUG << "Attempted to set IV on Google QUIC crypter";
+ return false;
+ }
+ DCHECK_EQ(iv.size(), nonce_size_);
+ if (iv.size() != nonce_size_) {
+ return false;
+ }
+ memcpy(iv_, iv.data(), iv.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::SetPreliminaryKey(QuicStringPiece key) {
+ DCHECK(!have_preliminary_key_);
+ SetKey(key);
+ have_preliminary_key_ = true;
+
+ return true;
+}
+
+bool AeadBaseDecrypter::SetDiversificationNonce(
+ const DiversificationNonce& nonce) {
+ if (!have_preliminary_key_) {
+ return true;
+ }
+
+ std::string key, nonce_prefix;
+ size_t prefix_size = nonce_size_ - sizeof(QuicPacketNumber);
+ DiversifyPreliminaryKey(
+ QuicStringPiece(reinterpret_cast<const char*>(key_), key_size_),
+ QuicStringPiece(reinterpret_cast<const char*>(iv_), prefix_size), nonce,
+ key_size_, prefix_size, &key, &nonce_prefix);
+
+ if (!SetKey(key) || !SetNoncePrefix(nonce_prefix)) {
+ DCHECK(false);
+ return false;
+ }
+
+ have_preliminary_key_ = false;
+ return true;
+}
+
+bool AeadBaseDecrypter::DecryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) {
+ if (ciphertext.length() < auth_tag_size_) {
+ return false;
+ }
+
+ if (have_preliminary_key_) {
+ QUIC_BUG << "Unable to decrypt while key diversification is pending";
+ return false;
+ }
+
+ uint8_t nonce[kMaxNonceSize];
+ memcpy(nonce, iv_, nonce_size_);
+ size_t prefix_len = nonce_size_ - sizeof(packet_number);
+ if (use_ietf_nonce_construction_) {
+ for (size_t i = 0; i < sizeof(packet_number); ++i) {
+ nonce[prefix_len + i] ^=
+ (packet_number >> ((sizeof(packet_number) - i - 1) * 8)) & 0xff;
+ }
+ } else {
+ memcpy(nonce + prefix_len, &packet_number, sizeof(packet_number));
+ }
+ if (!EVP_AEAD_CTX_open(
+ ctx_.get(), reinterpret_cast<uint8_t*>(output), output_length,
+ max_output_length, reinterpret_cast<const uint8_t*>(nonce),
+ nonce_size_, reinterpret_cast<const uint8_t*>(ciphertext.data()),
+ ciphertext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size())) {
+ // Because QuicFramer does trial decryption, decryption errors are expected
+ // when encryption level changes. So we don't log decryption errors.
+ ClearOpenSslErrors();
+ return false;
+ }
+ return true;
+}
+
+size_t AeadBaseDecrypter::GetKeySize() const {
+ return key_size_;
+}
+
+size_t AeadBaseDecrypter::GetIVSize() const {
+ return nonce_size_;
+}
+
+QuicStringPiece AeadBaseDecrypter::GetKey() const {
+ return QuicStringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+QuicStringPiece AeadBaseDecrypter::GetNoncePrefix() const {
+ return QuicStringPiece(reinterpret_cast<const char*>(iv_),
+ nonce_size_ - sizeof(QuicPacketNumber));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h
new file mode 100644
index 00000000000..8c7fa158c82
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_DECRYPTER_H_
+
+#include <cstddef>
+
+#include "third_party/boringssl/src/include/openssl/aead.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// AeadBaseDecrypter is the base class of AEAD QuicDecrypter subclasses.
+class QUIC_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter {
+ public:
+ // This takes the function pointer rather than the EVP_AEAD itself so
+ // subclasses do not need to call CRYPTO_library_init.
+ AeadBaseDecrypter(const EVP_AEAD* (*aead_getter)(),
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_size,
+ bool use_ietf_nonce_construction);
+ AeadBaseDecrypter(const AeadBaseDecrypter&) = delete;
+ AeadBaseDecrypter& operator=(const AeadBaseDecrypter&) = delete;
+ ~AeadBaseDecrypter() override;
+
+ // QuicDecrypter implementation
+ bool SetKey(QuicStringPiece key) override;
+ bool SetNoncePrefix(QuicStringPiece nonce_prefix) override;
+ bool SetIV(QuicStringPiece iv) override;
+ bool SetPreliminaryKey(QuicStringPiece key) override;
+ bool SetDiversificationNonce(const DiversificationNonce& nonce) override;
+ bool DecryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) override;
+ size_t GetKeySize() const override;
+ size_t GetIVSize() const override;
+ QuicStringPiece GetKey() const override;
+ QuicStringPiece GetNoncePrefix() const override;
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ static const size_t kMaxNonceSize = 12;
+
+ private:
+ const EVP_AEAD* const aead_alg_;
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_size_;
+ const bool use_ietf_nonce_construction_;
+ bool have_preliminary_key_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The IV used to construct the nonce.
+ unsigned char iv_[kMaxNonceSize];
+
+ bssl::ScopedEVP_AEAD_CTX ctx_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.cc
new file mode 100644
index 00000000000..405292ea083
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.cc
@@ -0,0 +1,187 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/crypto.h"
+#include "third_party/boringssl/src/include/openssl/err.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ while (ERR_get_error()) {
+ }
+#else
+ while (unsigned long error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, QUIC_ARRAYSIZE(buf));
+ QUIC_DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+const EVP_AEAD* InitAndCall(const EVP_AEAD* (*aead_getter)()) {
+ // Ensure BoringSSL is initialized before calling |aead_getter|. In Chromium,
+ // the static initializer is disabled.
+ CRYPTO_library_init();
+ return aead_getter();
+}
+
+} // namespace
+
+AeadBaseEncrypter::AeadBaseEncrypter(const EVP_AEAD* (*aead_getter)(),
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_size,
+ bool use_ietf_nonce_construction)
+ : aead_alg_(InitAndCall(aead_getter)),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_size_(nonce_size),
+ use_ietf_nonce_construction_(use_ietf_nonce_construction) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_size_, sizeof(iv_));
+ DCHECK_GE(kMaxNonceSize, nonce_size_);
+}
+
+AeadBaseEncrypter::~AeadBaseEncrypter() {}
+
+bool AeadBaseEncrypter::SetKey(QuicStringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_, auth_tag_size_,
+ nullptr)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseEncrypter::SetNoncePrefix(QuicStringPiece nonce_prefix) {
+ if (use_ietf_nonce_construction_) {
+ QUIC_BUG << "Attempted to set nonce prefix on IETF QUIC crypter";
+ return false;
+ }
+ DCHECK_EQ(nonce_prefix.size(), nonce_size_ - sizeof(QuicPacketNumber));
+ if (nonce_prefix.size() != nonce_size_ - sizeof(QuicPacketNumber)) {
+ return false;
+ }
+ memcpy(iv_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::SetIV(QuicStringPiece iv) {
+ if (!use_ietf_nonce_construction_) {
+ QUIC_BUG << "Attempted to set IV on Google QUIC crypter";
+ return false;
+ }
+ DCHECK_EQ(iv.size(), nonce_size_);
+ if (iv.size() != nonce_size_) {
+ return false;
+ }
+ memcpy(iv_, iv.data(), iv.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::Encrypt(QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ unsigned char* output) {
+ DCHECK_EQ(nonce.size(), nonce_size_);
+
+ size_t ciphertext_len;
+ if (!EVP_AEAD_CTX_seal(
+ ctx_.get(), output, &ciphertext_len,
+ plaintext.size() + auth_tag_size_,
+ reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(),
+ reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size())) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseEncrypter::EncryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) {
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+ if (max_output_length < ciphertext_size) {
+ return false;
+ }
+ // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+ // same packet number twice.
+ QUIC_ALIGNED(4) char nonce_buffer[kMaxNonceSize];
+ memcpy(nonce_buffer, iv_, nonce_size_);
+ size_t prefix_len = nonce_size_ - sizeof(packet_number);
+ if (use_ietf_nonce_construction_) {
+ for (size_t i = 0; i < sizeof(packet_number); ++i) {
+ nonce_buffer[prefix_len + i] ^=
+ (packet_number >> ((sizeof(packet_number) - i - 1) * 8)) & 0xff;
+ }
+ } else {
+ memcpy(nonce_buffer + prefix_len, &packet_number, sizeof(packet_number));
+ }
+
+ if (!Encrypt(QuicStringPiece(nonce_buffer, nonce_size_), associated_data,
+ plaintext, reinterpret_cast<unsigned char*>(output))) {
+ return false;
+ }
+ *output_length = ciphertext_size;
+ return true;
+}
+
+size_t AeadBaseEncrypter::GetKeySize() const {
+ return key_size_;
+}
+
+size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
+ return nonce_size_ - sizeof(QuicPacketNumber);
+}
+
+size_t AeadBaseEncrypter::GetIVSize() const {
+ return nonce_size_;
+}
+
+size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - auth_tag_size_;
+}
+
+size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + auth_tag_size_;
+}
+
+QuicStringPiece AeadBaseEncrypter::GetKey() const {
+ return QuicStringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+QuicStringPiece AeadBaseEncrypter::GetNoncePrefix() const {
+ return QuicStringPiece(reinterpret_cast<const char*>(iv_),
+ GetNoncePrefixSize());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h
new file mode 100644
index 00000000000..51ac8e34690
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "third_party/boringssl/src/include/openssl/aead.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// AeadBaseEncrypter is the base class of AEAD QuicEncrypter subclasses.
+class QUIC_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter {
+ public:
+ // This takes the function pointer rather than the EVP_AEAD itself so
+ // subclasses do not need to call CRYPTO_library_init.
+ AeadBaseEncrypter(const EVP_AEAD* (*aead_getter)(),
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_size,
+ bool use_ietf_nonce_construction);
+ AeadBaseEncrypter(const AeadBaseEncrypter&) = delete;
+ AeadBaseEncrypter& operator=(const AeadBaseEncrypter&) = delete;
+ ~AeadBaseEncrypter() override;
+
+ // QuicEncrypter implementation
+ bool SetKey(QuicStringPiece key) override;
+ bool SetNoncePrefix(QuicStringPiece nonce_prefix) override;
+ bool SetIV(QuicStringPiece iv) override;
+ bool EncryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) override;
+ size_t GetKeySize() const override;
+ size_t GetNoncePrefixSize() const override;
+ size_t GetIVSize() const override;
+ size_t GetMaxPlaintextSize(size_t ciphertext_size) const override;
+ size_t GetCiphertextSize(size_t plaintext_size) const override;
+ QuicStringPiece GetKey() const override;
+ QuicStringPiece GetNoncePrefix() const override;
+
+ // Necessary so unit tests can explicitly specify a nonce, instead of an IV
+ // (or nonce prefix) and packet number.
+ bool Encrypt(QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ unsigned char* output);
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ enum : size_t { kMaxNonceSize = 12 };
+
+ private:
+ const EVP_AEAD* const aead_alg_;
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_size_;
+ const bool use_ietf_nonce_construction_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The IV used to construct the nonce.
+ unsigned char iv_[kMaxNonceSize];
+
+ bssl::ScopedEVP_AEAD_CTX ctx_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AEAD_BASE_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.cc
new file mode 100644
index 00000000000..344280771f1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.cc
@@ -0,0 +1,35 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aead.h"
+#include "third_party/boringssl/src/include/openssl/tls1.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
+ : AesBaseDecrypter(EVP_aead_aes_128_gcm,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ false) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
+
+uint32_t Aes128Gcm12Decrypter::cipher_id() const {
+ return TLS1_CK_AES_128_GCM_SHA256;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h
new file mode 100644
index 00000000000..d5686e81807
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h
@@ -0,0 +1,38 @@
+// 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_CORE_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes128Gcm12Decrypter is a QuicDecrypter that implements the
+// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
+// calling QuicDecrypter::Create(kAESG).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
+// of the nonce is four bytes.
+class QUIC_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AesBaseDecrypter {
+ public:
+ enum {
+ // Authentication tags are truncated to 96 bits.
+ kAuthTagSize = 12,
+ };
+
+ Aes128Gcm12Decrypter();
+ Aes128Gcm12Decrypter(const Aes128Gcm12Decrypter&) = delete;
+ Aes128Gcm12Decrypter& operator=(const Aes128Gcm12Decrypter&) = delete;
+ ~Aes128Gcm12Decrypter() override;
+
+ uint32_t cipher_id() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
new file mode 100644
index 00000000000..fefd09fc109
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc
@@ -0,0 +1,286 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmDecrypt128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = cf063a34d4a9a76c2c86787d3f96db71
+// IV = 113b9785971864c83b01c787
+// CT =
+// AAD =
+// Tag = 72ac8493e3a5228b5d130a69d2510e42
+// PT =
+//
+// Count = 1
+// Key = a49a5e26a2f8cb63d05546c2a62f5343
+// IV = 907763b19b9b4ab6bd4f0281
+// CT =
+// AAD =
+// Tag = a2be08210d8c470a8df6e8fbd79ec5cf
+// FAIL
+//
+// ...
+//
+// The gcmDecrypt128.rsp file is huge (2.6 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* ct;
+ const char* aad;
+ const char* tag;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestGroupInfo test_group_info[] = {
+ {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128},
+ {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"cf063a34d4a9a76c2c86787d3f96db71", "113b9785971864c83b01c787", "", "",
+ "72ac8493e3a5228b5d130a69d2510e42", ""},
+ {
+ "a49a5e26a2f8cb63d05546c2a62f5343", "907763b19b9b4ab6bd4f0281", "", "",
+ "a2be08210d8c470a8df6e8fbd79ec5cf",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_1[] = {
+ {
+ "d1f6af919cde85661208bdce0c27cb22", "898c6929b435017bf031c3c5", "",
+ "7c5faa40e636bbc91107e68010c92b9f", "ae45f11777540a2caeb128be8092468a",
+ nullptr // FAIL
+ },
+ {"2370e320d4344208e0ff5683f243b213", "04dbb82f044d30831c441228", "",
+ "d43a8e5089eea0d026c03a85178b27da", "2a049c049d25aa95969b451d93c31c6e",
+ ""},
+ {nullptr}};
+
+const TestVector test_group_2[] = {
+ {"e98b72a9881a84ca6b76e0f43e68647a", "8b23299fde174053f3d652ba",
+ "5a3c1cf1985dbb8bed818036fdd5ab42", "", "23c7ab0f952b7091cd324835043b5eb5",
+ "28286a321293253c3e0aa2704a278032"},
+ {"33240636cd3236165f1a553b773e728e", "17c4d61493ecdc8f31700b12",
+ "47bb7e23f7bdfe05a8091ac90e4f8b2e", "", "b723c70e931d9785f40fd4ab1d612dc9",
+ "95695a5b12f2870b9cc5fdc8f218a97d"},
+ {
+ "5164df856f1e9cac04a79b808dc5be39", "e76925d5355e0584ce871b2b",
+ "0216c899c88d6e32c958c7e553daa5bc", "",
+ "a145319896329c96df291f64efbe0e3a",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_3[] = {
+ {"af57f42c60c0fc5a09adb81ab86ca1c3", "a2dc01871f37025dc0fc9a79",
+ "b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947"
+ "338b22f9bad09093276a331e9c79c7f4",
+ "41dc38988945fcb44faf2ef72d0061289ef8efd8",
+ "4f71e72bde0018f555c5adcce062e005",
+ "3803a0727eeb0ade441e0ec107161ded2d425ec0d102f21f51bf2cf9947c7ec4aa7279"
+ "5b2f69b041596e8817d0a3c16f8fadeb"},
+ {"ebc753e5422b377d3cb64b58ffa41b61", "2e1821efaced9acf1f241c9b",
+ "069567190554e9ab2b50a4e1fbf9c147340a5025fdbd201929834eaf6532325899ccb9"
+ "f401823e04b05817243d2142a3589878",
+ "b9673412fd4f88ba0e920f46dd6438ff791d8eef",
+ "534d9234d2351cf30e565de47baece0b",
+ "39077edb35e9c5a4b1e4c2a6b9bb1fce77f00f5023af40333d6d699014c2bcf4209c18"
+ "353a18017f5b36bfc00b1f6dcb7ed485"},
+ {
+ "52bdbbf9cf477f187ec010589cb39d58", "d3be36d3393134951d324b31",
+ "700188da144fa692cf46e4a8499510a53d90903c967f7f13e8a1bd8151a74adc4fe63e"
+ "32b992760b3a5f99e9a47838867000a9",
+ "93c4fc6a4135f54d640b0c976bf755a06a292c33",
+ "8ca4e38aa3dfa6b1d0297021ccf3ea5f",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_4[] = {
+ {"da2bb7d581493d692380c77105590201", "44aa3e7856ca279d2eb020c6",
+ "9290d430c9e89c37f0446dbd620c9a6b34b1274aeb6f911f75867efcf95b6feda69f1a"
+ "f4ee16c761b3c9aeac3da03aa9889c88",
+ "4cd171b23bddb3a53cdf959d5c1710b481eb3785a90eb20a2345ee00d0bb7868c367ab"
+ "12e6f4dd1dee72af4eee1d197777d1d6499cc541f34edbf45cda6ef90b3c024f9272d7"
+ "2ec1909fb8fba7db88a4d6f7d3d925980f9f9f72",
+ "9e3ac938d3eb0cadd6f5c9e35d22ba38",
+ "9bbf4c1a2742f6ac80cb4e8a052e4a8f4f07c43602361355b717381edf9fabd4cb7e3a"
+ "d65dbd1378b196ac270588dd0621f642"},
+ {"d74e4958717a9d5c0e235b76a926cae8", "0b7471141e0c70b1995fd7b1",
+ "e701c57d2330bf066f9ff8cf3ca4343cafe4894651cd199bdaaa681ba486b4a65c5a22"
+ "b0f1420be29ea547d42c713bc6af66aa",
+ "4a42b7aae8c245c6f1598a395316e4b8484dbd6e64648d5e302021b1d3fa0a38f46e22"
+ "bd9c8080b863dc0016482538a8562a4bd0ba84edbe2697c76fd039527ac179ec5506cf"
+ "34a6039312774cedebf4961f3978b14a26509f96",
+ "e192c23cb036f0b31592989119eed55d",
+ "840d9fb95e32559fb3602e48590280a172ca36d9b49ab69510f5bd552bfab7a306f85f"
+ "f0a34bc305b88b804c60b90add594a17"},
+ {
+ "1986310c725ac94ecfe6422e75fc3ee7", "93ec4214fa8e6dc4e3afc775",
+ "b178ec72f85a311ac4168f42a4b2c23113fbea4b85f4b9dabb74e143eb1b8b0a361e02"
+ "43edfd365b90d5b325950df0ada058f9",
+ "e80b88e62c49c958b5e0b8b54f532d9ff6aa84c8a40132e93e55b59fc24e8decf28463"
+ "139f155d1e8ce4ee76aaeefcd245baa0fc519f83a5fb9ad9aa40c4b21126013f576c42"
+ "72c2cb136c8fd091cc4539877a5d1e72d607f960",
+ "8b347853f11d75e81e8a95010be81f17",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_5[] = {
+ {"387218b246c1a8257748b56980e50c94", "dd7e014198672be39f95b69d",
+ "cdba9e73eaf3d38eceb2b04a8d", "", "ecf90f4a47c9c626d6fb2c765d201556",
+ "48f5b426baca03064554cc2b30"},
+ {"294de463721e359863887c820524b3d4", "3338b35c9d57a5d28190e8c9",
+ "2f46634e74b8e4c89812ac83b9", "", "dabd506764e68b82a7e720aa18da0abe",
+ "46a2e55c8e264df211bd112685"},
+ {"28ead7fd2179e0d12aa6d5d88c58c2dc", "5055347f18b4d5add0ae5c41",
+ "142d8210c3fb84774cdbd0447a", "", "5fd321d9cdb01952dc85f034736c2a7d",
+ "3b95b981086ee73cc4d0cc1422"},
+ {
+ "7d7b6c988137b8d470c57bf674a09c87", "9edf2aa970d016ac962e1fd8",
+ "a85b66c3cb5eab91d5bdc8bc0e", "", "dc054efc01f3afd21d9c2484819f569a",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(Aes128Gcm12Decrypter* decrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext) {
+ uint64_t packet_number;
+ QuicStringPiece nonce_prefix(nonce.data(),
+ nonce.size() - sizeof(packet_number));
+ decrypter->SetNoncePrefix(nonce_prefix);
+ memcpy(&packet_number, nonce.data() + nonce_prefix.size(),
+ sizeof(packet_number));
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success = decrypter->DecryptPacket(
+ packet_number, associated_data, ciphertext, output.get(), &output_length,
+ ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class Aes128Gcm12DecrypterTest : public QuicTest {};
+
+TEST_F(Aes128Gcm12DecrypterTest, Decrypt) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[j].pt;
+
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[j].key);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[j].iv);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[j].ct);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[j].aad);
+ std::string tag = QuicTextUtils::HexDecode(test_vectors[j].tag);
+ std::string pt;
+ if (has_pt) {
+ pt = QuicTextUtils::HexDecode(test_vectors[j].pt);
+ }
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
+
+ // The test vectors have 16 byte authenticators but this code only uses
+ // the first 12.
+ ASSERT_LE(static_cast<size_t>(Aes128Gcm12Decrypter::kAuthTagSize),
+ tag.length());
+ tag.resize(Aes128Gcm12Decrypter::kAuthTagSize);
+ std::string ciphertext = ct + tag;
+
+ Aes128Gcm12Decrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+
+ std::unique_ptr<QuicData> decrypted(
+ DecryptWithNonce(&decrypter, iv,
+ // This deliberately tests that the decrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : QuicStringPiece(), ciphertext));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.cc
new file mode 100644
index 00000000000..d48d5a583f2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
+ : AesBaseEncrypter(EVP_aead_aes_128_gcm,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ false) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h
new file mode 100644
index 00000000000..cd80fbfc9f5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes128Gcm12Encrypter is a QuicEncrypter that implements the
+// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
+// calling QuicEncrypter::Create(kAESG).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
+// of the nonce is four bytes.
+class QUIC_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AesBaseEncrypter {
+ public:
+ enum {
+ // Authentication tags are truncated to 96 bits.
+ kAuthTagSize = 12,
+ };
+
+ Aes128Gcm12Encrypter();
+ Aes128Gcm12Encrypter(const Aes128Gcm12Encrypter&) = delete;
+ Aes128Gcm12Encrypter& operator=(const Aes128Gcm12Encrypter&) = delete;
+ ~Aes128Gcm12Encrypter() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
new file mode 100644
index 00000000000..578c3be33cb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -0,0 +1,241 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmEncryptExtIV128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = 11754cd72aec309bf52f7687212e8957
+// IV = 3c819d9a9bed087615030b65
+// PT =
+// AAD =
+// CT =
+// Tag = 250327c674aaf477aef2675748cf6971
+//
+// Count = 1
+// Key = ca47248ac0b6f8372a97ac43508308ed
+// IV = ffd2b598feabc9019262d2be
+// PT =
+// AAD =
+// CT =
+// Tag = 60d20404af527d248d893ae495707d1a
+//
+// ...
+//
+// The gcmEncryptExtIV128.rsp file is huge (2.8 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* iv;
+ const char* pt;
+ const char* aad;
+ const char* ct;
+ const char* tag;
+};
+
+const TestGroupInfo test_group_info[] = {
+ {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128},
+ {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "", "", "",
+ "250327c674aaf477aef2675748cf6971"},
+ {"ca47248ac0b6f8372a97ac43508308ed", "ffd2b598feabc9019262d2be", "", "", "",
+ "60d20404af527d248d893ae495707d1a"},
+ {nullptr}};
+
+const TestVector test_group_1[] = {
+ {"77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3", "",
+ "7a43ec1d9c0a5a78a0b16533a6213cab", "",
+ "209fcc8d3675ed938e9c7166709dd946"},
+ {"7680c5d3ca6154758e510f4d25b98820", "f8f105f9c3df4965780321f8", "",
+ "c94c410194c765e3dcc7964379758ed3", "",
+ "94dca8edfcf90bb74b153c8d48a17930"},
+ {nullptr}};
+
+const TestVector test_group_2[] = {
+ {"7fddb57453c241d03efbed3ac44e371c", "ee283a3fc75575e33efd4887",
+ "d5de42b461646c255c87bd2962d3b9a2", "", "2ccda4a5415cb91e135c2a0f78c9b2fd",
+ "b36d1df9b9d5e596f83e8b7f52971cb3"},
+ {"ab72c77b97cb5fe9a382d9fe81ffdbed", "54cc7dc2c37ec006bcc6d1da",
+ "007c5e5b3e59df24a7c355584fc1518d", "", "0e1bde206a07a9c2c1b65300f8c64997",
+ "2b4401346697138c7a4891ee59867d0c"},
+ {nullptr}};
+
+const TestVector test_group_3[] = {
+ {"fe47fcce5fc32665d2ae399e4eec72ba", "5adb9609dbaeb58cbd6e7275",
+ "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1"
+ "b840382c4bccaf3bafb4ca8429bea063",
+ "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a",
+ "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf539304373636525"
+ "3ddbc5db8778371495da76d269e5db3e",
+ "291ef1982e4defedaa2249f898556b47"},
+ {"ec0c2ba17aa95cd6afffe949da9cc3a8", "296bce5b50b7d66096d627ef",
+ "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987"
+ "b764b9611f6c0f8641843d5d58f3a242",
+ "f8d00f05d22bf68599bcdeb131292ad6e2df5d14",
+ "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a0716299"
+ "5506fde6309ffc19e716eddf1a828c5a",
+ "890147971946b627c40016da1ecf3e77"},
+ {nullptr}};
+
+const TestVector test_group_4[] = {
+ {"2c1f21cf0f6fb3661943155c3e3d8492", "23cb5ff362e22426984d1907",
+ "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d6"
+ "8b5615ba7c1220ff6510e259f06655d8",
+ "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e"
+ "3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f"
+ "4488f33cfb5e979e42b6e1cfc0a60238982a7aec",
+ "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222"
+ "b6ad57af43e1895df9dca2a5344a62cc",
+ "57a3ee28136e94c74838997ae9823f3a"},
+ {"d9f7d2411091f947b4d6f1e2d1f0fb2e", "e1934f5db57cc983e6b180e7",
+ "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490"
+ "c2c6f6166f4a59431e182663fcaea05a",
+ "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d"
+ "0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a201"
+ "15d2e51398344b16bee1ed7c499b353d6c597af8",
+ "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d57"
+ "3c7891c2a91fbc48db29967ec9542b23",
+ "21b51ca862cb637cdd03b99a0f93b134"},
+ {nullptr}};
+
+const TestVector test_group_5[] = {
+ {"fe9bb47deb3a61e423c2231841cfd1fb", "4d328eb776f500a2f7fb47aa",
+ "f1cc3818e421876bb6b8bbd6c9", "", "b88c5c1977b35b517b0aeae967",
+ "43fd4727fe5cdb4b5b42818dea7ef8c9"},
+ {"6703df3701a7f54911ca72e24dca046a", "12823ab601c350ea4bc2488c",
+ "793cd125b0b84a043e3ac67717", "", "b2051c80014f42f08735a7b0cd",
+ "38e6bcd29962e5f2c13626b85a877101"},
+ {nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(Aes128Gcm12Encrypter* encrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class Aes128Gcm12EncrypterTest : public QuicTest {};
+
+TEST_F(Aes128Gcm12EncrypterTest, Encrypt) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[j].key);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[j].iv);
+ std::string pt = QuicTextUtils::HexDecode(test_vectors[j].pt);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[j].aad);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[j].ct);
+ std::string tag = QuicTextUtils::HexDecode(test_vectors[j].tag);
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+
+ Aes128Gcm12Encrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(
+ EncryptWithNonce(&encrypter, iv,
+ // This deliberately tests that the encrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : QuicStringPiece(), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ // The test vectors have 16 byte authenticators but this code only uses
+ // the first 12.
+ ASSERT_LE(static_cast<size_t>(Aes128Gcm12Encrypter::kAuthTagSize),
+ tag.length());
+ tag.resize(Aes128Gcm12Encrypter::kAuthTagSize);
+
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ ct.length(), ct.data(), ct.length());
+ test::CompareCharArraysWithHexError(
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
+ }
+ }
+}
+
+TEST_F(Aes128Gcm12EncrypterTest, GetMaxPlaintextSize) {
+ Aes128Gcm12Encrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST_F(Aes128Gcm12EncrypterTest, GetCiphertextSize) {
+ Aes128Gcm12Encrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.cc
new file mode 100644
index 00000000000..650b245e7b6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.cc
@@ -0,0 +1,37 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aead.h"
+#include "third_party/boringssl/src/include/openssl/tls1.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes128GcmDecrypter::Aes128GcmDecrypter()
+ : AesBaseDecrypter(EVP_aead_aes_128_gcm,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes128GcmDecrypter::~Aes128GcmDecrypter() {}
+
+uint32_t Aes128GcmDecrypter::cipher_id() const {
+ return TLS1_CK_AES_128_GCM_SHA256;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h
new file mode 100644
index 00000000000..6ddbe01e5aa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h
@@ -0,0 +1,36 @@
+// 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_CORE_CRYPTO_AES_128_GCM_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes128GcmDecrypter is a QuicDecrypter that implements the
+// AEAD_AES_128_GCM algorithm specified in RFC 5116 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE Aes128GcmDecrypter : public AesBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ Aes128GcmDecrypter();
+ Aes128GcmDecrypter(const Aes128GcmDecrypter&) = delete;
+ Aes128GcmDecrypter& operator=(const Aes128GcmDecrypter&) = delete;
+ ~Aes128GcmDecrypter() override;
+
+ uint32_t cipher_id() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter_test.cc
new file mode 100644
index 00000000000..13c6827f006
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter_test.cc
@@ -0,0 +1,291 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmDecrypt128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = cf063a34d4a9a76c2c86787d3f96db71
+// IV = 113b9785971864c83b01c787
+// CT =
+// AAD =
+// Tag = 72ac8493e3a5228b5d130a69d2510e42
+// PT =
+//
+// Count = 1
+// Key = a49a5e26a2f8cb63d05546c2a62f5343
+// IV = 907763b19b9b4ab6bd4f0281
+// CT =
+// AAD =
+// Tag = a2be08210d8c470a8df6e8fbd79ec5cf
+// FAIL
+//
+// ...
+//
+// The gcmDecrypt128.rsp file is huge (2.6 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* ct;
+ const char* aad;
+ const char* tag;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestGroupInfo test_group_info[] = {
+ {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128},
+ {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"cf063a34d4a9a76c2c86787d3f96db71", "113b9785971864c83b01c787", "", "",
+ "72ac8493e3a5228b5d130a69d2510e42", ""},
+ {
+ "a49a5e26a2f8cb63d05546c2a62f5343", "907763b19b9b4ab6bd4f0281", "", "",
+ "a2be08210d8c470a8df6e8fbd79ec5cf",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_1[] = {
+ {
+ "d1f6af919cde85661208bdce0c27cb22", "898c6929b435017bf031c3c5", "",
+ "7c5faa40e636bbc91107e68010c92b9f", "ae45f11777540a2caeb128be8092468a",
+ nullptr // FAIL
+ },
+ {"2370e320d4344208e0ff5683f243b213", "04dbb82f044d30831c441228", "",
+ "d43a8e5089eea0d026c03a85178b27da", "2a049c049d25aa95969b451d93c31c6e",
+ ""},
+ {nullptr}};
+
+const TestVector test_group_2[] = {
+ {"e98b72a9881a84ca6b76e0f43e68647a", "8b23299fde174053f3d652ba",
+ "5a3c1cf1985dbb8bed818036fdd5ab42", "", "23c7ab0f952b7091cd324835043b5eb5",
+ "28286a321293253c3e0aa2704a278032"},
+ {"33240636cd3236165f1a553b773e728e", "17c4d61493ecdc8f31700b12",
+ "47bb7e23f7bdfe05a8091ac90e4f8b2e", "", "b723c70e931d9785f40fd4ab1d612dc9",
+ "95695a5b12f2870b9cc5fdc8f218a97d"},
+ {
+ "5164df856f1e9cac04a79b808dc5be39", "e76925d5355e0584ce871b2b",
+ "0216c899c88d6e32c958c7e553daa5bc", "",
+ "a145319896329c96df291f64efbe0e3a",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_3[] = {
+ {"af57f42c60c0fc5a09adb81ab86ca1c3", "a2dc01871f37025dc0fc9a79",
+ "b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947"
+ "338b22f9bad09093276a331e9c79c7f4",
+ "41dc38988945fcb44faf2ef72d0061289ef8efd8",
+ "4f71e72bde0018f555c5adcce062e005",
+ "3803a0727eeb0ade441e0ec107161ded2d425ec0d102f21f51bf2cf9947c7ec4aa7279"
+ "5b2f69b041596e8817d0a3c16f8fadeb"},
+ {"ebc753e5422b377d3cb64b58ffa41b61", "2e1821efaced9acf1f241c9b",
+ "069567190554e9ab2b50a4e1fbf9c147340a5025fdbd201929834eaf6532325899ccb9"
+ "f401823e04b05817243d2142a3589878",
+ "b9673412fd4f88ba0e920f46dd6438ff791d8eef",
+ "534d9234d2351cf30e565de47baece0b",
+ "39077edb35e9c5a4b1e4c2a6b9bb1fce77f00f5023af40333d6d699014c2bcf4209c18"
+ "353a18017f5b36bfc00b1f6dcb7ed485"},
+ {
+ "52bdbbf9cf477f187ec010589cb39d58", "d3be36d3393134951d324b31",
+ "700188da144fa692cf46e4a8499510a53d90903c967f7f13e8a1bd8151a74adc4fe63e"
+ "32b992760b3a5f99e9a47838867000a9",
+ "93c4fc6a4135f54d640b0c976bf755a06a292c33",
+ "8ca4e38aa3dfa6b1d0297021ccf3ea5f",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_4[] = {
+ {"da2bb7d581493d692380c77105590201", "44aa3e7856ca279d2eb020c6",
+ "9290d430c9e89c37f0446dbd620c9a6b34b1274aeb6f911f75867efcf95b6feda69f1a"
+ "f4ee16c761b3c9aeac3da03aa9889c88",
+ "4cd171b23bddb3a53cdf959d5c1710b481eb3785a90eb20a2345ee00d0bb7868c367ab"
+ "12e6f4dd1dee72af4eee1d197777d1d6499cc541f34edbf45cda6ef90b3c024f9272d7"
+ "2ec1909fb8fba7db88a4d6f7d3d925980f9f9f72",
+ "9e3ac938d3eb0cadd6f5c9e35d22ba38",
+ "9bbf4c1a2742f6ac80cb4e8a052e4a8f4f07c43602361355b717381edf9fabd4cb7e3a"
+ "d65dbd1378b196ac270588dd0621f642"},
+ {"d74e4958717a9d5c0e235b76a926cae8", "0b7471141e0c70b1995fd7b1",
+ "e701c57d2330bf066f9ff8cf3ca4343cafe4894651cd199bdaaa681ba486b4a65c5a22"
+ "b0f1420be29ea547d42c713bc6af66aa",
+ "4a42b7aae8c245c6f1598a395316e4b8484dbd6e64648d5e302021b1d3fa0a38f46e22"
+ "bd9c8080b863dc0016482538a8562a4bd0ba84edbe2697c76fd039527ac179ec5506cf"
+ "34a6039312774cedebf4961f3978b14a26509f96",
+ "e192c23cb036f0b31592989119eed55d",
+ "840d9fb95e32559fb3602e48590280a172ca36d9b49ab69510f5bd552bfab7a306f85f"
+ "f0a34bc305b88b804c60b90add594a17"},
+ {
+ "1986310c725ac94ecfe6422e75fc3ee7", "93ec4214fa8e6dc4e3afc775",
+ "b178ec72f85a311ac4168f42a4b2c23113fbea4b85f4b9dabb74e143eb1b8b0a361e02"
+ "43edfd365b90d5b325950df0ada058f9",
+ "e80b88e62c49c958b5e0b8b54f532d9ff6aa84c8a40132e93e55b59fc24e8decf28463"
+ "139f155d1e8ce4ee76aaeefcd245baa0fc519f83a5fb9ad9aa40c4b21126013f576c42"
+ "72c2cb136c8fd091cc4539877a5d1e72d607f960",
+ "8b347853f11d75e81e8a95010be81f17",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_5[] = {
+ {"387218b246c1a8257748b56980e50c94", "dd7e014198672be39f95b69d",
+ "cdba9e73eaf3d38eceb2b04a8d", "", "ecf90f4a47c9c626d6fb2c765d201556",
+ "48f5b426baca03064554cc2b30"},
+ {"294de463721e359863887c820524b3d4", "3338b35c9d57a5d28190e8c9",
+ "2f46634e74b8e4c89812ac83b9", "", "dabd506764e68b82a7e720aa18da0abe",
+ "46a2e55c8e264df211bd112685"},
+ {"28ead7fd2179e0d12aa6d5d88c58c2dc", "5055347f18b4d5add0ae5c41",
+ "142d8210c3fb84774cdbd0447a", "", "5fd321d9cdb01952dc85f034736c2a7d",
+ "3b95b981086ee73cc4d0cc1422"},
+ {
+ "7d7b6c988137b8d470c57bf674a09c87", "9edf2aa970d016ac962e1fd8",
+ "a85b66c3cb5eab91d5bdc8bc0e", "", "dc054efc01f3afd21d9c2484819f569a",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(Aes128GcmDecrypter* decrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext) {
+ decrypter->SetIV(nonce);
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success =
+ decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+ &output_length, ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class Aes128GcmDecrypterTest : public QuicTest {};
+
+TEST_F(Aes128GcmDecrypterTest, Decrypt) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[j].pt;
+
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[j].key);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[j].iv);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[j].ct);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[j].aad);
+ std::string tag = QuicTextUtils::HexDecode(test_vectors[j].tag);
+ std::string pt;
+ if (has_pt) {
+ pt = QuicTextUtils::HexDecode(test_vectors[j].pt);
+ }
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
+ std::string ciphertext = ct + tag;
+
+ Aes128GcmDecrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+
+ std::unique_ptr<QuicData> decrypted(
+ DecryptWithNonce(&decrypter, iv,
+ // This deliberately tests that the decrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : QuicStringPiece(), ciphertext));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+ }
+}
+
+TEST_F(Aes128GcmDecrypterTest, GenerateHeaderProtectionMask) {
+ Aes128GcmDecrypter decrypter;
+ std::string key =
+ QuicTextUtils::HexDecode("d9132370cb18476ab833649cf080d970");
+ std::string sample =
+ QuicTextUtils::HexDecode("d1d7998068517adb769b48b924a32c47");
+ QuicDataReader sample_reader(sample.data(), sample.size());
+ ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+ std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+ std::string expected_mask =
+ QuicTextUtils::HexDecode("b132c37d6164da4ea4dc9b763aceec27");
+ test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+ mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.cc
new file mode 100644
index 00000000000..71265782fdc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.cc
@@ -0,0 +1,30 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes128GcmEncrypter::Aes128GcmEncrypter()
+ : AesBaseEncrypter(EVP_aead_aes_128_gcm,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes128GcmEncrypter::~Aes128GcmEncrypter() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h
new file mode 100644
index 00000000000..0610eac2638
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h
@@ -0,0 +1,32 @@
+// 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_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes128GcmEncrypter is a QuicEncrypter that implements the
+// AEAD_AES_128_GCM algorithm specified in RFC 5116 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE Aes128GcmEncrypter : public AesBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ Aes128GcmEncrypter();
+ Aes128GcmEncrypter(const Aes128GcmEncrypter&) = delete;
+ Aes128GcmEncrypter& operator=(const Aes128GcmEncrypter&) = delete;
+ ~Aes128GcmEncrypter() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter_test.cc
new file mode 100644
index 00000000000..3efe5291136
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter_test.cc
@@ -0,0 +1,274 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmEncryptExtIV128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = 11754cd72aec309bf52f7687212e8957
+// IV = 3c819d9a9bed087615030b65
+// PT =
+// AAD =
+// CT =
+// Tag = 250327c674aaf477aef2675748cf6971
+//
+// Count = 1
+// Key = ca47248ac0b6f8372a97ac43508308ed
+// IV = ffd2b598feabc9019262d2be
+// PT =
+// AAD =
+// CT =
+// Tag = 60d20404af527d248d893ae495707d1a
+//
+// ...
+//
+// The gcmEncryptExtIV128.rsp file is huge (2.8 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* iv;
+ const char* pt;
+ const char* aad;
+ const char* ct;
+ const char* tag;
+};
+
+const TestGroupInfo test_group_info[] = {
+ {128, 96, 0, 0, 128}, {128, 96, 0, 128, 128}, {128, 96, 128, 0, 128},
+ {128, 96, 408, 160, 128}, {128, 96, 408, 720, 128}, {128, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "", "", "",
+ "250327c674aaf477aef2675748cf6971"},
+ {"ca47248ac0b6f8372a97ac43508308ed", "ffd2b598feabc9019262d2be", "", "", "",
+ "60d20404af527d248d893ae495707d1a"},
+ {nullptr}};
+
+const TestVector test_group_1[] = {
+ {"77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3", "",
+ "7a43ec1d9c0a5a78a0b16533a6213cab", "",
+ "209fcc8d3675ed938e9c7166709dd946"},
+ {"7680c5d3ca6154758e510f4d25b98820", "f8f105f9c3df4965780321f8", "",
+ "c94c410194c765e3dcc7964379758ed3", "",
+ "94dca8edfcf90bb74b153c8d48a17930"},
+ {nullptr}};
+
+const TestVector test_group_2[] = {
+ {"7fddb57453c241d03efbed3ac44e371c", "ee283a3fc75575e33efd4887",
+ "d5de42b461646c255c87bd2962d3b9a2", "", "2ccda4a5415cb91e135c2a0f78c9b2fd",
+ "b36d1df9b9d5e596f83e8b7f52971cb3"},
+ {"ab72c77b97cb5fe9a382d9fe81ffdbed", "54cc7dc2c37ec006bcc6d1da",
+ "007c5e5b3e59df24a7c355584fc1518d", "", "0e1bde206a07a9c2c1b65300f8c64997",
+ "2b4401346697138c7a4891ee59867d0c"},
+ {nullptr}};
+
+const TestVector test_group_3[] = {
+ {"fe47fcce5fc32665d2ae399e4eec72ba", "5adb9609dbaeb58cbd6e7275",
+ "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1"
+ "b840382c4bccaf3bafb4ca8429bea063",
+ "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a",
+ "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf539304373636525"
+ "3ddbc5db8778371495da76d269e5db3e",
+ "291ef1982e4defedaa2249f898556b47"},
+ {"ec0c2ba17aa95cd6afffe949da9cc3a8", "296bce5b50b7d66096d627ef",
+ "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987"
+ "b764b9611f6c0f8641843d5d58f3a242",
+ "f8d00f05d22bf68599bcdeb131292ad6e2df5d14",
+ "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a0716299"
+ "5506fde6309ffc19e716eddf1a828c5a",
+ "890147971946b627c40016da1ecf3e77"},
+ {nullptr}};
+
+const TestVector test_group_4[] = {
+ {"2c1f21cf0f6fb3661943155c3e3d8492", "23cb5ff362e22426984d1907",
+ "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d6"
+ "8b5615ba7c1220ff6510e259f06655d8",
+ "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e"
+ "3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f"
+ "4488f33cfb5e979e42b6e1cfc0a60238982a7aec",
+ "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222"
+ "b6ad57af43e1895df9dca2a5344a62cc",
+ "57a3ee28136e94c74838997ae9823f3a"},
+ {"d9f7d2411091f947b4d6f1e2d1f0fb2e", "e1934f5db57cc983e6b180e7",
+ "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490"
+ "c2c6f6166f4a59431e182663fcaea05a",
+ "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d"
+ "0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a201"
+ "15d2e51398344b16bee1ed7c499b353d6c597af8",
+ "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d57"
+ "3c7891c2a91fbc48db29967ec9542b23",
+ "21b51ca862cb637cdd03b99a0f93b134"},
+ {nullptr}};
+
+const TestVector test_group_5[] = {
+ {"fe9bb47deb3a61e423c2231841cfd1fb", "4d328eb776f500a2f7fb47aa",
+ "f1cc3818e421876bb6b8bbd6c9", "", "b88c5c1977b35b517b0aeae967",
+ "43fd4727fe5cdb4b5b42818dea7ef8c9"},
+ {"6703df3701a7f54911ca72e24dca046a", "12823ab601c350ea4bc2488c",
+ "793cd125b0b84a043e3ac67717", "", "b2051c80014f42f08735a7b0cd",
+ "38e6bcd29962e5f2c13626b85a877101"},
+ {nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(Aes128GcmEncrypter* encrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class Aes128GcmEncrypterTest : public QuicTest {};
+
+TEST_F(Aes128GcmEncrypterTest, Encrypt) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[j].key);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[j].iv);
+ std::string pt = QuicTextUtils::HexDecode(test_vectors[j].pt);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[j].aad);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[j].ct);
+ std::string tag = QuicTextUtils::HexDecode(test_vectors[j].tag);
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+
+ Aes128GcmEncrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(
+ EncryptWithNonce(&encrypter, iv,
+ // This deliberately tests that the encrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : QuicStringPiece(), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ ct.length(), ct.data(), ct.length());
+ test::CompareCharArraysWithHexError(
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
+ }
+ }
+}
+
+TEST_F(Aes128GcmEncrypterTest, EncryptPacket) {
+ std::string key =
+ QuicTextUtils::HexDecode("d95a145250826c25a77b6a84fd4d34fc");
+ std::string iv = QuicTextUtils::HexDecode("50c4431ebb18283448e276e2");
+ uint64_t packet_num = 0x13278f44;
+ std::string aad =
+ QuicTextUtils::HexDecode("875d49f64a70c9cbe713278f44ff000005");
+ std::string pt = QuicTextUtils::HexDecode("aa0003a250bd000000000001");
+ std::string ct = QuicTextUtils::HexDecode(
+ "7dd4708b989ee7d38a013e3656e9b37beefd05808fe1ab41e3b4f2c0");
+
+ std::vector<char> out(ct.size());
+ size_t out_size;
+
+ Aes128GcmEncrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ ASSERT_TRUE(encrypter.SetIV(iv));
+ ASSERT_TRUE(encrypter.EncryptPacket(packet_num, aad, pt, out.data(),
+ &out_size, out.size()));
+ EXPECT_EQ(out_size, out.size());
+ test::CompareCharArraysWithHexError("ciphertext", out.data(), out.size(),
+ ct.data(), ct.size());
+}
+
+TEST_F(Aes128GcmEncrypterTest, GetMaxPlaintextSize) {
+ Aes128GcmEncrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26));
+}
+
+TEST_F(Aes128GcmEncrypterTest, GetCiphertextSize) {
+ Aes128GcmEncrypter encrypter;
+ EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(116u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
+}
+
+TEST_F(Aes128GcmEncrypterTest, GenerateHeaderProtectionMask) {
+ Aes128GcmEncrypter encrypter;
+ std::string key =
+ QuicTextUtils::HexDecode("d9132370cb18476ab833649cf080d970");
+ std::string sample =
+ QuicTextUtils::HexDecode("d1d7998068517adb769b48b924a32c47");
+ ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+ std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+ std::string expected_mask =
+ QuicTextUtils::HexDecode("b132c37d6164da4ea4dc9b763aceec27");
+ test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+ mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.cc
new file mode 100644
index 00000000000..a5f09e5d617
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.cc
@@ -0,0 +1,37 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aead.h"
+#include "third_party/boringssl/src/include/openssl/tls1.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes256GcmDecrypter::Aes256GcmDecrypter()
+ : AesBaseDecrypter(EVP_aead_aes_256_gcm,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes256GcmDecrypter::~Aes256GcmDecrypter() {}
+
+uint32_t Aes256GcmDecrypter::cipher_id() const {
+ return TLS1_CK_AES_256_GCM_SHA384;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.h
new file mode 100644
index 00000000000..260fecef7d9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.h
@@ -0,0 +1,36 @@
+// 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_CORE_CRYPTO_AES_256_GCM_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes256GcmDecrypter is a QuicDecrypter that implements the
+// AEAD_AES_256_GCM algorithm specified in RFC 5116 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE Aes256GcmDecrypter : public AesBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ Aes256GcmDecrypter();
+ Aes256GcmDecrypter(const Aes256GcmDecrypter&) = delete;
+ Aes256GcmDecrypter& operator=(const Aes256GcmDecrypter&) = delete;
+ ~Aes256GcmDecrypter() override;
+
+ uint32_t cipher_id() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter_test.cc
new file mode 100644
index 00000000000..4e7e05b5e81
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter_test.cc
@@ -0,0 +1,295 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmDecrypt256.rsp
+// downloaded from
+// https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES#GCMVS
+// on 2017-09-27. The test vectors in that file look like this:
+//
+// [Keylen = 256]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = f5a2b27c74355872eb3ef6c5feafaa740e6ae990d9d48c3bd9bb8235e589f010
+// IV = 58d2240f580a31c1d24948e9
+// CT =
+// AAD =
+// Tag = 15e051a5e4a5f5da6cea92e2ebee5bac
+// PT =
+//
+// Count = 1
+// Key = e5a8123f2e2e007d4e379ba114a2fb66e6613f57c72d4e4f024964053028a831
+// IV = 51e43385bf533e168427e1ad
+// CT =
+// AAD =
+// Tag = 38fe845c66e66bdd884c2aecafd280e6
+// FAIL
+//
+// ...
+//
+// The gcmDecrypt256.rsp file is huge (3.0 MB), so a few test vectors were
+// selected for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* ct;
+ const char* aad;
+ const char* tag;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestGroupInfo test_group_info[] = {
+ {256, 96, 0, 0, 128}, {256, 96, 0, 128, 128}, {256, 96, 128, 0, 128},
+ {256, 96, 408, 160, 128}, {256, 96, 408, 720, 128}, {256, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"f5a2b27c74355872eb3ef6c5feafaa740e6ae990d9d48c3bd9bb8235e589f010",
+ "58d2240f580a31c1d24948e9", "", "", "15e051a5e4a5f5da6cea92e2ebee5bac",
+ ""},
+ {
+ "e5a8123f2e2e007d4e379ba114a2fb66e6613f57c72d4e4f024964053028a831",
+ "51e43385bf533e168427e1ad", "", "", "38fe845c66e66bdd884c2aecafd280e6",
+ nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_1[] = {
+ {"6dfdafd6703c285c01f14fd10a6012862b2af950d4733abb403b2e745b26945d",
+ "3749d0b3d5bacb71be06ade6", "", "c0d249871992e70302ae008193d1e89f",
+ "4aa4cc69f84ee6ac16d9bfb4e05de500", ""},
+ {
+ "2c392a5eb1a9c705371beda3a901c7c61dca4d93b4291de1dd0dd15ec11ffc45",
+ "0723fb84a08f4ea09841f32a", "", "140be561b6171eab942c486a94d33d43",
+ "aa0e1c9b57975bfc91aa137231977d2c", nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_2[] = {
+ {"4c8ebfe1444ec1b2d503c6986659af2c94fafe945f72c1e8486a5acfedb8a0f8",
+ "473360e0ad24889959858995", "d2c78110ac7e8f107c0df0570bd7c90c", "",
+ "c26a379b6d98ef2852ead8ce83a833a7", "7789b41cb3ee548814ca0b388c10b343"},
+ {"3934f363fd9f771352c4c7a060682ed03c2864223a1573b3af997e2ababd60ab",
+ "efe2656d878c586e41c539c4", "e0de64302ac2d04048d65a87d2ad09fe", "",
+ "33cbd8d2fb8a3a03e30c1eb1b53c1d99", "697aff2d6b77e5ed6232770e400c1ead"},
+ {
+ "c997768e2d14e3d38259667a6649079de77beb4543589771e5068e6cd7cd0b14",
+ "835090aed9552dbdd45277e2", "9f6607d68e22ccf21928db0986be126e", "",
+ "f32617f67c574fd9f44ef76ff880ab9f", nullptr // FAIL
+ },
+ {nullptr}};
+
+const TestVector test_group_3[] = {
+ {
+ "e9d381a9c413bee66175d5586a189836e5c20f5583535ab4d3f3e612dc21700e",
+ "23e81571da1c7821c681c7ca",
+ "a25f3f580306cd5065d22a6b7e9660110af7204bb77d370f7f34bee547feeff7b32a59"
+ "6fce29c9040e68b1589aad48da881990",
+ "6f39c9ae7b8e8a58a95f0dd8ea6a9087cbccdfd6",
+ "5b6dcd70eefb0892fab1539298b92a4b",
+ nullptr // FAIL
+ },
+ {"6450d4501b1e6cfbe172c4c8570363e96b496591b842661c28c2f6c908379cad",
+ "7e4262035e0bf3d60e91668a",
+ "5a99b336fd3cfd82f10fb08f7045012415f0d9a06bb92dcf59c6f0dbe62d433671aacb8a1"
+ "c52ce7bbf6aea372bf51e2ba79406",
+ "f1c522f026e4c5d43851da516a1b78768ab18171",
+ "fe93b01636f7bb0458041f213e98de65",
+ "17449e236ef5858f6d891412495ead4607bfae2a2d735182a2a0242f9d52fc5345ef912db"
+ "e16f3bb4576fe3bcafe336dee6085"},
+ {"90f2e71ccb1148979cb742efc8f921de95457d898c84ce28edeed701650d3a26",
+ "aba58ad60047ba553f6e4c98",
+ "3fc77a5fe9203d091c7916587c9763cf2e4d0d53ca20b078b851716f1dab4873fe342b7b3"
+ "01402f015d00263bf3f77c58a99d6",
+ "2abe465df6e5be47f05b92c9a93d76ae3611fac5",
+ "9cb3d04637048bc0bddef803ffbb56cf",
+ "1d21639640e11638a2769e3fab78778f84be3f4a8ce28dfd99cb2e75171e05ea8e94e30aa"
+ "78b54bb402b39d613616a8ed951dc"},
+ {nullptr}};
+
+const TestVector test_group_4[] = {
+ {
+ "e36aca93414b13f5313e76a7244588ee116551d1f34c32859166f2eb0ac1a9b7",
+ "e9e701b1ccef6bddd03391d8",
+ "5b059ac6733b6de0e8cf5b88b7301c02c993426f71bb12abf692e9deeacfac1ff1644c"
+ "87d4df130028f515f0feda636309a24d",
+ "6a08fe6e55a08f283cec4c4b37676e770f402af6102f548ad473ec6236da764f7076ff"
+ "d41bbd9611b439362d899682b7b0f839fc5a68d9df54afd1e2b3c4e7d072454ee27111"
+ "d52193d28b9c4f925d2a8b451675af39191a2cba",
+ "43c7c9c93cc265fc8e192000e0417b5b",
+ nullptr // FAIL
+ },
+ {"5f72046245d3f4a0877e50a86554bfd57d1c5e073d1ed3b5451f6d0fc2a8507a",
+ "ea6f5b391e44b751b26bce6f",
+ "0e6e0b2114c40769c15958d965a14dcf50b680e0185a4409d77d894ca15b1e698dd83b353"
+ "6b18c05d8cd0873d1edce8150ecb5",
+ "9b3a68c941d42744673fb60fea49075eae77322e7e70e34502c115b6495ebfc796d629080"
+ "7653c6b53cd84281bd0311656d0013f44619d2748177e99e8f8347c989a7b59f9d8dcf00f"
+ "31db0684a4a83e037e8777bae55f799b0d",
+ "fdaaff86ceb937502cd9012d03585800",
+ "b0a881b751cc1eb0c912a4cf9bd971983707dbd2411725664503455c55db25cdb19bc669c"
+ "2654a3a8011de6bf7eff3f9f07834"},
+ {"ab639bae205547607506522bd3cdca7861369e2b42ef175ff135f6ba435d5a8e",
+ "5fbb63eb44bd59fee458d8f6",
+ "9a34c62bed0972285503a32812877187a54dedbd55d2317fed89282bf1af4ba0b6bb9f9e1"
+ "6dd86da3b441deb7841262bc6bd63",
+ "1ef2b1768b805587935ffaf754a11bd2a305076d6374f1f5098b1284444b78f55408a786d"
+ "a37e1b7f1401c330d3585ef56f3e4d35eaaac92e1381d636477dc4f4beaf559735e902d6b"
+ "e58723257d4ac1ed9bd213de387f35f3c4",
+ "e0299e079bff46fd12e36d1c60e41434",
+ "e5a3ce804a8516cdd12122c091256b789076576040dbf3c55e8be3c016025896b8a72532b"
+ "fd51196cc82efca47aa0fd8e2e0dc"},
+ {nullptr}};
+
+const TestVector test_group_5[] = {
+ {
+ "8b37c4b8cf634704920059866ad96c49e9da502c63fca4a3a7a4dcec74cb0610",
+ "cb59344d2b06c4ae57cd0ea4", "66ab935c93555e786b775637a3", "",
+ "d8733acbb564d8afaa99d7ca2e2f92a9", nullptr // FAIL
+ },
+ {"a71dac1377a3bf5d7fb1b5e36bee70d2e01de2a84a1c1009ba7448f7f26131dc",
+ "c5b60dda3f333b1146e9da7c", "43af49ec1ae3738a20755034d6", "",
+ "6f80b6ef2d8830a55eb63680a8dff9e0", "5b87141335f2becac1a559e05f"},
+ {"dc1f64681014be221b00793bbcf5a5bc675b968eb7a3a3d5aa5978ef4fa45ecc",
+ "056ae9a1a69e38af603924fe", "33013a48d9ea0df2911d583271", "",
+ "5b8f9cc22303e979cd1524187e9f70fe", "2a7e05612191c8bce2f529dca9"},
+ {nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(Aes256GcmDecrypter* decrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext) {
+ decrypter->SetIV(nonce);
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success =
+ decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+ &output_length, ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class Aes256GcmDecrypterTest : public QuicTest {};
+
+TEST_F(Aes256GcmDecrypterTest, Decrypt) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[j].pt;
+
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[j].key);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[j].iv);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[j].ct);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[j].aad);
+ std::string tag = QuicTextUtils::HexDecode(test_vectors[j].tag);
+ std::string pt;
+ if (has_pt) {
+ pt = QuicTextUtils::HexDecode(test_vectors[j].pt);
+ }
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
+ std::string ciphertext = ct + tag;
+
+ Aes256GcmDecrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+
+ std::unique_ptr<QuicData> decrypted(
+ DecryptWithNonce(&decrypter, iv,
+ // This deliberately tests that the decrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : QuicStringPiece(), ciphertext));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+ }
+}
+
+TEST_F(Aes256GcmDecrypterTest, GenerateHeaderProtectionMask) {
+ Aes256GcmDecrypter decrypter;
+ std::string key = QuicTextUtils::HexDecode(
+ "ed23ecbf54d426def5c52c3dcfc84434e62e57781d3125bb21ed91b7d3e07788");
+ std::string sample =
+ QuicTextUtils::HexDecode("4d190c474be2b8babafb49ec4e38e810");
+ QuicDataReader sample_reader(sample.data(), sample.size());
+ ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+ std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+ std::string expected_mask =
+ QuicTextUtils::HexDecode("db9ed4e6ccd033af2eae01407199c56e");
+ test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+ mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.cc
new file mode 100644
index 00000000000..146e3de8519
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.cc
@@ -0,0 +1,30 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+Aes256GcmEncrypter::Aes256GcmEncrypter()
+ : AesBaseEncrypter(EVP_aead_aes_256_gcm,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+Aes256GcmEncrypter::~Aes256GcmEncrypter() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.h
new file mode 100644
index 00000000000..0e458226375
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.h
@@ -0,0 +1,32 @@
+// 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_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An Aes256GcmEncrypter is a QuicEncrypter that implements the
+// AEAD_AES_256_GCM algorithm specified in RFC 5116 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE Aes256GcmEncrypter : public AesBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ Aes256GcmEncrypter();
+ Aes256GcmEncrypter(const Aes256GcmEncrypter&) = delete;
+ Aes256GcmEncrypter& operator=(const Aes256GcmEncrypter&) = delete;
+ ~Aes256GcmEncrypter() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter_test.cc
new file mode 100644
index 00000000000..86bf0c430cb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter_test.cc
@@ -0,0 +1,257 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmEncryptExtIV256.rsp
+// downloaded from
+// https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES#GCMVS
+// on 2017-09-27. The test vectors in that file look like this:
+//
+// [Keylen = 256]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4
+// IV = 516c33929df5a3284ff463d7
+// PT =
+// AAD =
+// CT =
+// Tag = bdc1ac884d332457a1d2664f168c76f0
+//
+// Count = 1
+// Key = 5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f
+// IV = 770ac1a5a3d476d5d96944a1
+// PT =
+// AAD =
+// CT =
+// Tag = 196d691e1047093ca4b3d2ef4baba216
+//
+// ...
+//
+// The gcmEncryptExtIV256.rsp file is huge (3.2 MB), so a few test vectors were
+// selected for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* iv;
+ const char* pt;
+ const char* aad;
+ const char* ct;
+ const char* tag;
+};
+
+const TestGroupInfo test_group_info[] = {
+ {256, 96, 0, 0, 128}, {256, 96, 0, 128, 128}, {256, 96, 128, 0, 128},
+ {256, 96, 408, 160, 128}, {256, 96, 408, 720, 128}, {256, 96, 104, 0, 128},
+};
+
+const TestVector test_group_0[] = {
+ {"b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4",
+ "516c33929df5a3284ff463d7", "", "", "",
+ "bdc1ac884d332457a1d2664f168c76f0"},
+ {"5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f",
+ "770ac1a5a3d476d5d96944a1", "", "", "",
+ "196d691e1047093ca4b3d2ef4baba216"},
+ {nullptr}};
+
+const TestVector test_group_1[] = {
+ {"78dc4e0aaf52d935c3c01eea57428f00ca1fd475f5da86a49c8dd73d68c8e223",
+ "d79cf22d504cc793c3fb6c8a", "", "b96baa8c1c75a671bfb2d08d06be5f36", "",
+ "3e5d486aa2e30b22e040b85723a06e76"},
+ {"4457ff33683cca6ca493878bdc00373893a9763412eef8cddb54f91318e0da88",
+ "699d1f29d7b8c55300bb1fd2", "", "6749daeea367d0e9809e2dc2f309e6e3", "",
+ "d60c74d2517fde4a74e0cd4709ed43a9"},
+ {nullptr}};
+
+const TestVector test_group_2[] = {
+ {"31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22",
+ "0d18e06c7c725ac9e362e1ce", "2db5168e932556f8089a0622981d017d", "",
+ "fa4362189661d163fcd6a56d8bf0405a", "d636ac1bbedd5cc3ee727dc2ab4a9489"},
+ {"460fc864972261c2560e1eb88761ff1c992b982497bd2ac36c04071cbb8e5d99",
+ "8a4a16b9e210eb68bcb6f58d", "99e4e926ffe927f691893fb79a96b067", "",
+ "133fc15751621b5f325c7ff71ce08324", "ec4e87e0cf74a13618d0b68636ba9fa7"},
+ {nullptr}};
+
+const TestVector test_group_3[] = {
+ {"24501ad384e473963d476edcfe08205237acfd49b5b8f33857f8114e863fec7f",
+ "9ff18563b978ec281b3f2794",
+ "27f348f9cdc0c5bd5e66b1ccb63ad920ff2219d14e8d631b3872265cf117ee86757accb15"
+ "8bd9abb3868fdc0d0b074b5f01b2c",
+ "adb5ec720ccf9898500028bf34afccbcaca126ef",
+ "eb7cb754c824e8d96f7c6d9b76c7d26fb874ffbf1d65c6f64a698d839b0b06145dae82057"
+ "ad55994cf59ad7f67c0fa5e85fab8",
+ "bc95c532fecc594c36d1550286a7a3f0"},
+ {"fb43f5ab4a1738a30c1e053d484a94254125d55dccee1ad67c368bc1a985d235",
+ "9fbb5f8252db0bca21f1c230",
+ "34b797bb82250e23c5e796db2c37e488b3b99d1b981cea5e5b0c61a0b39adb6bd6ef1f507"
+ "22e2e4f81115cfcf53f842e2a6c08",
+ "98f8ae1735c39f732e2cbee1156dabeb854ec7a2",
+ "871cd53d95a8b806bd4821e6c4456204d27fd704ba3d07ce25872dc604ea5c5ea13322186"
+ "b7489db4fa060c1fd4159692612c8",
+ "07b48e4a32fac47e115d7ac7445d8330"},
+ {nullptr}};
+
+const TestVector test_group_4[] = {
+ {"148579a3cbca86d5520d66c0ec71ca5f7e41ba78e56dc6eebd566fed547fe691",
+ "b08a5ea1927499c6ecbfd4e0",
+ "9d0b15fdf1bd595f91f8b3abc0f7dec927dfd4799935a1795d9ce00c9b879434420fe42c2"
+ "75a7cd7b39d638fb81ca52b49dc41",
+ "e4f963f015ffbb99ee3349bbaf7e8e8e6c2a71c230a48f9d59860a29091d2747e01a5ca57"
+ "2347e247d25f56ba7ae8e05cde2be3c97931292c02370208ecd097ef692687fecf2f419d3"
+ "200162a6480a57dad408a0dfeb492e2c5d",
+ "2097e372950a5e9383c675e89eea1c314f999159f5611344b298cda45e62843716f215f82"
+ "ee663919c64002a5c198d7878fd3f",
+ "adbecdb0d5c2224d804d2886ff9a5760"},
+ {"e49af19182faef0ebeeba9f2d3be044e77b1212358366e4ef59e008aebcd9788",
+ "e7f37d79a6a487a5a703edbb",
+ "461cd0caf7427a3d44408d825ed719237272ecd503b9094d1f62c97d63ed83a0b50bdc804"
+ "ffdd7991da7a5b6dcf48d4bcd2cbc",
+ "19a9a1cfc647346781bef51ed9070d05f99a0e0192a223c5cd2522dbdf97d9739dd39fb17"
+ "8ade3339e68774b058aa03e9a20a9a205bc05f32381df4d63396ef691fefd5a71b49a2ad8"
+ "2d5ea428778ca47ee1398792762413cff4",
+ "32ca3588e3e56eb4c8301b009d8b84b8a900b2b88ca3c21944205e9dd7311757b51394ae9"
+ "0d8bb3807b471677614f4198af909",
+ "3e403d035c71d88f1be1a256c89ba6ad"},
+ {nullptr}};
+
+const TestVector test_group_5[] = {
+ {"82c4f12eeec3b2d3d157b0f992d292b237478d2cecc1d5f161389b97f999057a",
+ "7b40b20f5f397177990ef2d1", "982a296ee1cd7086afad976945", "",
+ "ec8e05a0471d6b43a59ca5335f", "113ddeafc62373cac2f5951bb9165249"},
+ {"db4340af2f835a6c6d7ea0ca9d83ca81ba02c29b7410f221cb6071114e393240",
+ "40e438357dd80a85cac3349e", "8ddb3397bd42853193cb0f80c9", "",
+ "b694118c85c41abf69e229cb0f", "c07f1b8aafbd152f697eb67f2a85fe45"},
+ {nullptr}};
+
+const TestVector* const test_group_array[] = {
+ test_group_0, test_group_1, test_group_2,
+ test_group_3, test_group_4, test_group_5,
+};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(Aes256GcmEncrypter* encrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class Aes256GcmEncrypterTest : public QuicTest {};
+
+TEST_F(Aes256GcmEncrypterTest, Encrypt) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[j].key);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[j].iv);
+ std::string pt = QuicTextUtils::HexDecode(test_vectors[j].pt);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[j].aad);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[j].ct);
+ std::string tag = QuicTextUtils::HexDecode(test_vectors[j].tag);
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+
+ Aes256GcmEncrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(
+ EncryptWithNonce(&encrypter, iv,
+ // This deliberately tests that the encrypter can
+ // handle an AAD that is set to nullptr, as opposed
+ // to a zero-length, non-nullptr pointer.
+ aad.length() ? aad : QuicStringPiece(), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ ct.length(), ct.data(), ct.length());
+ test::CompareCharArraysWithHexError(
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
+ }
+ }
+}
+
+TEST_F(Aes256GcmEncrypterTest, GetMaxPlaintextSize) {
+ Aes256GcmEncrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26));
+}
+
+TEST_F(Aes256GcmEncrypterTest, GetCiphertextSize) {
+ Aes256GcmEncrypter encrypter;
+ EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(116u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
+}
+
+TEST_F(Aes256GcmEncrypterTest, GenerateHeaderProtectionMask) {
+ Aes256GcmEncrypter encrypter;
+ std::string key = QuicTextUtils::HexDecode(
+ "ed23ecbf54d426def5c52c3dcfc84434e62e57781d3125bb21ed91b7d3e07788");
+ std::string sample =
+ QuicTextUtils::HexDecode("4d190c474be2b8babafb49ec4e38e810");
+ ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+ std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+ std::string expected_mask =
+ QuicTextUtils::HexDecode("db9ed4e6ccd033af2eae01407199c56e");
+ test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+ mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.cc
new file mode 100644
index 00000000000..6888ab22f6c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aes.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool AesBaseDecrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+ if (key.size() != GetKeySize()) {
+ QUIC_BUG << "Invalid key size for header protection";
+ return false;
+ }
+ if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()),
+ key.size() * 8, &pne_key_) != 0) {
+ QUIC_BUG << "Unexpected failure of AES_set_encrypt_key";
+ return false;
+ }
+ return true;
+}
+
+std::string AesBaseDecrypter::GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) {
+ QuicStringPiece sample;
+ if (!sample_reader->ReadStringPiece(&sample, AES_BLOCK_SIZE)) {
+ return std::string();
+ }
+ std::string out(AES_BLOCK_SIZE, 0);
+ AES_encrypt(reinterpret_cast<const uint8_t*>(sample.data()),
+ reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+ &pne_key_);
+ return out;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h
new file mode 100644
index 00000000000..e8e1cc80aa8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
+
+#include <cstddef>
+
+#include "third_party/boringssl/src/include/openssl/aes.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE AesBaseDecrypter : public AeadBaseDecrypter {
+ public:
+ using AeadBaseDecrypter::AeadBaseDecrypter;
+
+ bool SetHeaderProtectionKey(QuicStringPiece key) override;
+ std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) override;
+
+ private:
+ // The key used for packet number encryption.
+ AES_KEY pne_key_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.cc
new file mode 100644
index 00000000000..49fc71d4602
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aes.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool AesBaseEncrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+ if (key.size() != GetKeySize()) {
+ QUIC_BUG << "Invalid key size for header protection";
+ return false;
+ }
+ if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()),
+ key.size() * 8, &pne_key_) != 0) {
+ QUIC_BUG << "Unexpected failure of AES_set_encrypt_key";
+ return false;
+ }
+ return true;
+}
+
+std::string AesBaseEncrypter::GenerateHeaderProtectionMask(
+ QuicStringPiece sample) {
+ if (sample.size() != AES_BLOCK_SIZE) {
+ return std::string();
+ }
+ std::string out(AES_BLOCK_SIZE, 0);
+ AES_encrypt(reinterpret_cast<const uint8_t*>(sample.data()),
+ reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+ &pne_key_);
+ return out;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h
new file mode 100644
index 00000000000..e115ebac820
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "third_party/boringssl/src/include/openssl/aes.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE AesBaseEncrypter : public AeadBaseEncrypter {
+ public:
+ using AeadBaseEncrypter::AeadBaseEncrypter;
+
+ bool SetHeaderProtectionKey(QuicStringPiece key) override;
+ std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override;
+
+ private:
+ // The key used for packet number encryption.
+ AES_KEY pne_key_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor.cc
new file mode 100644
index 00000000000..9ad2fc80b16
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor.cc
@@ -0,0 +1,642 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "third_party/zlib/zlib.h"
+
+namespace quic {
+
+namespace {
+
+// kCommonCertSubstrings contains ~1500 bytes of common certificate substrings
+// in order to help zlib. This was generated via a fairly dumb algorithm from
+// the Alexa Top 5000 set - we could probably do better.
+static const unsigned char kCommonCertSubstrings[] = {
+ 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
+ 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
+ 0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01,
+ 0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07,
+ 0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
+ 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34,
+ 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x32, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x2d, 0x61, 0x69, 0x61, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x45, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x2e, 0x63, 0x65,
+ 0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4a, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x7b, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd2,
+ 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0xb4, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x30, 0x0b, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
+ 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74,
+ 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33,
+ 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
+ 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+ 0x30, 0x1d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
+ 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
+ 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x67, 0x64, 0x73, 0x31, 0x2d, 0x32, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
+ 0x70, 0x73, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
+ 0x0d, 0x31, 0x33, 0x30, 0x35, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x73, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x45, 0x01, 0x07, 0x17, 0x06, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x53, 0x31, 0x17,
+ 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, 0x39,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, 0x73,
+ 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x31, 0x10, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x47, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01,
+ 0x03, 0x13, 0x02, 0x55, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x14, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0f, 0x13, 0x14, 0x50,
+ 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e,
+ 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, 0x61,
+ 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x14, 0x31, 0x31,
+ 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65,
+ 0x20, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x2e, 0x67, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
+ 0x69, 0x67, 0x6e, 0x31, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
+ 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x64, 0x31, 0x1a,
+ 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x45, 0x56, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63, 0x63, 0x72,
+ 0x74, 0x2e, 0x67, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x39, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x2f, 0x30, 0x81, 0x80, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x74, 0x30, 0x72, 0x30, 0x24, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
+ 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x4a, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
+ 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74,
+ 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72,
+ 0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee,
+ 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x27,
+ 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x86, 0x30,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
+};
+
+// CertEntry represents a certificate in compressed form. Each entry is one of
+// the three types enumerated in |Type|.
+struct CertEntry {
+ public:
+ enum Type {
+ // Type 0 is reserved to mean "end of list" in the wire format.
+
+ // COMPRESSED means that the certificate is included in the trailing zlib
+ // data.
+ COMPRESSED = 1,
+ // CACHED means that the certificate is already known to the peer and will
+ // be replaced by its 64-bit hash (in |hash|).
+ CACHED = 2,
+ // COMMON means that the certificate is in a common certificate set known
+ // to the peer with hash |set_hash| and certificate index |index|.
+ COMMON = 3,
+ };
+
+ Type type;
+ uint64_t hash;
+ uint64_t set_hash;
+ uint32_t index;
+};
+
+// MatchCerts returns a vector of CertEntries describing how to most
+// efficiently represent |certs| to a peer who has the common sets identified
+// by |client_common_set_hashes| and who has cached the certificates with the
+// 64-bit, FNV-1a hashes in |client_cached_cert_hashes|.
+std::vector<CertEntry> MatchCerts(const std::vector<std::string>& certs,
+ QuicStringPiece client_common_set_hashes,
+ QuicStringPiece client_cached_cert_hashes,
+ const CommonCertSets* common_sets) {
+ std::vector<CertEntry> entries;
+ entries.reserve(certs.size());
+
+ const bool cached_valid =
+ client_cached_cert_hashes.size() % sizeof(uint64_t) == 0 &&
+ !client_cached_cert_hashes.empty();
+
+ for (auto i = certs.begin(); i != certs.end(); ++i) {
+ CertEntry entry;
+
+ if (cached_valid) {
+ bool cached = false;
+
+ uint64_t hash = QuicUtils::FNV1a_64_Hash(*i);
+ // This assumes that the machine is little-endian.
+ for (size_t j = 0; j < client_cached_cert_hashes.size();
+ j += sizeof(uint64_t)) {
+ uint64_t cached_hash;
+ memcpy(&cached_hash, client_cached_cert_hashes.data() + j,
+ sizeof(uint64_t));
+ if (hash != cached_hash) {
+ continue;
+ }
+
+ entry.type = CertEntry::CACHED;
+ entry.hash = hash;
+ entries.push_back(entry);
+ cached = true;
+ break;
+ }
+
+ if (cached) {
+ continue;
+ }
+ }
+
+ if (common_sets && common_sets->MatchCert(*i, client_common_set_hashes,
+ &entry.set_hash, &entry.index)) {
+ entry.type = CertEntry::COMMON;
+ entries.push_back(entry);
+ continue;
+ }
+
+ entry.type = CertEntry::COMPRESSED;
+ entries.push_back(entry);
+ }
+
+ return entries;
+}
+
+// CertEntriesSize returns the size, in bytes, of the serialised form of
+// |entries|.
+size_t CertEntriesSize(const std::vector<CertEntry>& entries) {
+ size_t entries_size = 0;
+
+ for (auto i = entries.begin(); i != entries.end(); ++i) {
+ entries_size++;
+ switch (i->type) {
+ case CertEntry::COMPRESSED:
+ break;
+ case CertEntry::CACHED:
+ entries_size += sizeof(uint64_t);
+ break;
+ case CertEntry::COMMON:
+ entries_size += sizeof(uint64_t) + sizeof(uint32_t);
+ break;
+ }
+ }
+
+ entries_size++; // for end marker
+
+ return entries_size;
+}
+
+// SerializeCertEntries serialises |entries| to |out|, which must have enough
+// space to contain them.
+void SerializeCertEntries(uint8_t* out, const std::vector<CertEntry>& entries) {
+ for (auto i = entries.begin(); i != entries.end(); ++i) {
+ *out++ = static_cast<uint8_t>(i->type);
+ switch (i->type) {
+ case CertEntry::COMPRESSED:
+ break;
+ case CertEntry::CACHED:
+ memcpy(out, &i->hash, sizeof(i->hash));
+ out += sizeof(uint64_t);
+ break;
+ case CertEntry::COMMON:
+ // Assumes a little-endian machine.
+ memcpy(out, &i->set_hash, sizeof(i->set_hash));
+ out += sizeof(i->set_hash);
+ memcpy(out, &i->index, sizeof(uint32_t));
+ out += sizeof(uint32_t);
+ break;
+ }
+ }
+
+ *out++ = 0; // end marker
+}
+
+// ZlibDictForEntries returns a string that contains the zlib pre-shared
+// dictionary to use in order to decompress a zlib block following |entries|.
+// |certs| is one-to-one with |entries| and contains the certificates for those
+// entries that are CACHED or COMMON.
+std::string ZlibDictForEntries(const std::vector<CertEntry>& entries,
+ const std::vector<std::string>& certs) {
+ std::string zlib_dict;
+
+ // The dictionary starts with the common and cached certs in reverse order.
+ size_t zlib_dict_size = 0;
+ for (size_t i = certs.size() - 1; i < certs.size(); i--) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ zlib_dict_size += certs[i].size();
+ }
+ }
+
+ // At the end of the dictionary is a block of common certificate substrings.
+ zlib_dict_size += sizeof(kCommonCertSubstrings);
+
+ zlib_dict.reserve(zlib_dict_size);
+
+ for (size_t i = certs.size() - 1; i < certs.size(); i--) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ zlib_dict += certs[i];
+ }
+ }
+
+ zlib_dict += std::string(reinterpret_cast<const char*>(kCommonCertSubstrings),
+ sizeof(kCommonCertSubstrings));
+
+ DCHECK_EQ(zlib_dict.size(), zlib_dict_size);
+
+ return zlib_dict;
+}
+
+// HashCerts returns the FNV-1a hashes of |certs|.
+std::vector<uint64_t> HashCerts(const std::vector<std::string>& certs) {
+ std::vector<uint64_t> ret;
+ ret.reserve(certs.size());
+
+ for (auto i = certs.begin(); i != certs.end(); ++i) {
+ ret.push_back(QuicUtils::FNV1a_64_Hash(*i));
+ }
+
+ return ret;
+}
+
+// ParseEntries parses the serialised form of a vector of CertEntries from
+// |in_out| and writes them to |out_entries|. CACHED and COMMON entries are
+// resolved using |cached_certs| and |common_sets| and written to |out_certs|.
+// |in_out| is updated to contain the trailing data.
+bool ParseEntries(QuicStringPiece* in_out,
+ const std::vector<std::string>& cached_certs,
+ const CommonCertSets* common_sets,
+ std::vector<CertEntry>* out_entries,
+ std::vector<std::string>* out_certs) {
+ QuicStringPiece in = *in_out;
+ std::vector<uint64_t> cached_hashes;
+
+ out_entries->clear();
+ out_certs->clear();
+
+ for (;;) {
+ if (in.empty()) {
+ return false;
+ }
+ CertEntry entry;
+ const uint8_t type_byte = in[0];
+ in.remove_prefix(1);
+
+ if (type_byte == 0) {
+ break;
+ }
+
+ entry.type = static_cast<CertEntry::Type>(type_byte);
+
+ switch (entry.type) {
+ case CertEntry::COMPRESSED:
+ out_certs->push_back(std::string());
+ break;
+ case CertEntry::CACHED: {
+ if (in.size() < sizeof(uint64_t)) {
+ return false;
+ }
+ memcpy(&entry.hash, in.data(), sizeof(uint64_t));
+ in.remove_prefix(sizeof(uint64_t));
+
+ if (cached_hashes.size() != cached_certs.size()) {
+ cached_hashes = HashCerts(cached_certs);
+ }
+ bool found = false;
+ for (size_t i = 0; i < cached_hashes.size(); i++) {
+ if (cached_hashes[i] == entry.hash) {
+ out_certs->push_back(cached_certs[i]);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ break;
+ }
+ case CertEntry::COMMON: {
+ if (!common_sets) {
+ return false;
+ }
+ if (in.size() < sizeof(uint64_t) + sizeof(uint32_t)) {
+ return false;
+ }
+ memcpy(&entry.set_hash, in.data(), sizeof(uint64_t));
+ in.remove_prefix(sizeof(uint64_t));
+ memcpy(&entry.index, in.data(), sizeof(uint32_t));
+ in.remove_prefix(sizeof(uint32_t));
+
+ QuicStringPiece cert =
+ common_sets->GetCert(entry.set_hash, entry.index);
+ if (cert.empty()) {
+ return false;
+ }
+ out_certs->push_back(std::string(cert));
+ break;
+ }
+ default:
+ return false;
+ }
+ out_entries->push_back(entry);
+ }
+
+ *in_out = in;
+ return true;
+}
+
+// ScopedZLib deals with the automatic destruction of a zlib context.
+class ScopedZLib {
+ public:
+ enum Type {
+ INFLATE,
+ DEFLATE,
+ };
+
+ explicit ScopedZLib(Type type) : z_(nullptr), type_(type) {}
+
+ void reset(z_stream* z) {
+ Clear();
+ z_ = z;
+ }
+
+ ~ScopedZLib() { Clear(); }
+
+ private:
+ void Clear() {
+ if (!z_) {
+ return;
+ }
+
+ if (type_ == DEFLATE) {
+ deflateEnd(z_);
+ } else {
+ inflateEnd(z_);
+ }
+ z_ = nullptr;
+ }
+
+ z_stream* z_;
+ const Type type_;
+};
+
+} // anonymous namespace
+
+// static
+std::string CertCompressor::CompressChain(
+ const std::vector<std::string>& certs,
+ QuicStringPiece client_common_set_hashes,
+ QuicStringPiece client_cached_cert_hashes,
+ const CommonCertSets* common_sets) {
+ const std::vector<CertEntry> entries = MatchCerts(
+ certs, client_common_set_hashes, client_cached_cert_hashes, common_sets);
+ DCHECK_EQ(entries.size(), certs.size());
+
+ size_t uncompressed_size = 0;
+ for (size_t i = 0; i < entries.size(); i++) {
+ if (entries[i].type == CertEntry::COMPRESSED) {
+ uncompressed_size += 4 /* uint32_t length */ + certs[i].size();
+ }
+ }
+
+ size_t compressed_size = 0;
+ z_stream z;
+ ScopedZLib scoped_z(ScopedZLib::DEFLATE);
+
+ if (uncompressed_size > 0) {
+ memset(&z, 0, sizeof(z));
+ int rv = deflateInit(&z, Z_DEFAULT_COMPRESSION);
+ DCHECK_EQ(Z_OK, rv);
+ if (rv != Z_OK) {
+ return "";
+ }
+ scoped_z.reset(&z);
+
+ std::string zlib_dict = ZlibDictForEntries(entries, certs);
+
+ rv = deflateSetDictionary(
+ &z, reinterpret_cast<const uint8_t*>(&zlib_dict[0]), zlib_dict.size());
+ DCHECK_EQ(Z_OK, rv);
+ if (rv != Z_OK) {
+ return "";
+ }
+
+ compressed_size = deflateBound(&z, uncompressed_size);
+ }
+
+ const size_t entries_size = CertEntriesSize(entries);
+
+ std::string result;
+ result.resize(entries_size + (uncompressed_size > 0 ? 4 : 0) +
+ compressed_size);
+
+ uint8_t* j = reinterpret_cast<uint8_t*>(&result[0]);
+ SerializeCertEntries(j, entries);
+ j += entries_size;
+
+ if (uncompressed_size == 0) {
+ return result;
+ }
+
+ uint32_t uncompressed_size_32 = uncompressed_size;
+ memcpy(j, &uncompressed_size_32, sizeof(uint32_t));
+ j += sizeof(uint32_t);
+
+ int rv;
+
+ z.next_out = j;
+ z.avail_out = compressed_size;
+
+ for (size_t i = 0; i < certs.size(); i++) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ continue;
+ }
+
+ uint32_t length32 = certs[i].size();
+ z.next_in = reinterpret_cast<uint8_t*>(&length32);
+ z.avail_in = sizeof(length32);
+ rv = deflate(&z, Z_NO_FLUSH);
+ DCHECK_EQ(Z_OK, rv);
+ DCHECK_EQ(0u, z.avail_in);
+ if (rv != Z_OK || z.avail_in) {
+ return "";
+ }
+
+ z.next_in =
+ const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(certs[i].data()));
+ z.avail_in = certs[i].size();
+ rv = deflate(&z, Z_NO_FLUSH);
+ DCHECK_EQ(Z_OK, rv);
+ DCHECK_EQ(0u, z.avail_in);
+ if (rv != Z_OK || z.avail_in) {
+ return "";
+ }
+ }
+
+ z.avail_in = 0;
+ rv = deflate(&z, Z_FINISH);
+ DCHECK_EQ(Z_STREAM_END, rv);
+ if (rv != Z_STREAM_END) {
+ return "";
+ }
+
+ result.resize(result.size() - z.avail_out);
+ return result;
+}
+
+// static
+bool CertCompressor::DecompressChain(
+ QuicStringPiece in,
+ const std::vector<std::string>& cached_certs,
+ const CommonCertSets* common_sets,
+ std::vector<std::string>* out_certs) {
+ std::vector<CertEntry> entries;
+ if (!ParseEntries(&in, cached_certs, common_sets, &entries, out_certs)) {
+ return false;
+ }
+ DCHECK_EQ(entries.size(), out_certs->size());
+
+ std::unique_ptr<uint8_t[]> uncompressed_data;
+ QuicStringPiece uncompressed;
+
+ if (!in.empty()) {
+ if (in.size() < sizeof(uint32_t)) {
+ return false;
+ }
+
+ uint32_t uncompressed_size;
+ memcpy(&uncompressed_size, in.data(), sizeof(uncompressed_size));
+ in.remove_prefix(sizeof(uint32_t));
+
+ if (uncompressed_size > 128 * 1024) {
+ return false;
+ }
+
+ uncompressed_data = QuicMakeUnique<uint8_t[]>(uncompressed_size);
+ z_stream z;
+ ScopedZLib scoped_z(ScopedZLib::INFLATE);
+
+ memset(&z, 0, sizeof(z));
+ z.next_out = uncompressed_data.get();
+ z.avail_out = uncompressed_size;
+ z.next_in =
+ const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(in.data()));
+ z.avail_in = in.size();
+
+ if (Z_OK != inflateInit(&z)) {
+ return false;
+ }
+ scoped_z.reset(&z);
+
+ int rv = inflate(&z, Z_FINISH);
+ if (rv == Z_NEED_DICT) {
+ std::string zlib_dict = ZlibDictForEntries(entries, *out_certs);
+ const uint8_t* dict = reinterpret_cast<const uint8_t*>(zlib_dict.data());
+ if (Z_OK != inflateSetDictionary(&z, dict, zlib_dict.size())) {
+ return false;
+ }
+ rv = inflate(&z, Z_FINISH);
+ }
+
+ if (Z_STREAM_END != rv || z.avail_out > 0 || z.avail_in > 0) {
+ return false;
+ }
+
+ uncompressed = QuicStringPiece(
+ reinterpret_cast<char*>(uncompressed_data.get()), uncompressed_size);
+ }
+
+ for (size_t i = 0; i < entries.size(); i++) {
+ switch (entries[i].type) {
+ case CertEntry::COMPRESSED:
+ if (uncompressed.size() < sizeof(uint32_t)) {
+ return false;
+ }
+ uint32_t cert_len;
+ memcpy(&cert_len, uncompressed.data(), sizeof(cert_len));
+ uncompressed.remove_prefix(sizeof(uint32_t));
+ if (uncompressed.size() < cert_len) {
+ return false;
+ }
+ (*out_certs)[i] = std::string(uncompressed.substr(0, cert_len));
+ uncompressed.remove_prefix(cert_len);
+ break;
+ case CertEntry::CACHED:
+ case CertEntry::COMMON:
+ break;
+ }
+ }
+
+ if (!uncompressed.empty()) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor.h b/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor.h
new file mode 100644
index 00000000000..ca696bdf79c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CERT_COMPRESSOR_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CERT_COMPRESSOR_H_
+
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// CertCompressor provides functions for compressing and decompressing
+// certificate chains using three techniquies:
+// 1) The peer may provide a list of a 64-bit, FNV-1a hashes of certificates
+// that they already have. In the event that one of them is to be
+// compressed, it can be replaced with just the hash.
+// 2) The peer may provide a number of hashes that represent sets of
+// pre-shared certificates (CommonCertSets). If one of those certificates
+// is to be compressed, and it's known to the given CommonCertSets, then it
+// can be replaced with a set hash and certificate index.
+// 3) Otherwise the certificates are compressed with zlib using a pre-shared
+// dictionary that consists of the certificates handled with the above
+// methods and a small chunk of common substrings.
+class QUIC_EXPORT_PRIVATE CertCompressor {
+ public:
+ CertCompressor() = delete;
+
+ // CompressChain compresses the certificates in |certs| and returns a
+ // compressed representation. |common_sets| contains the common certificate
+ // sets known locally and |client_common_set_hashes| contains the hashes of
+ // the common sets known to the peer. |client_cached_cert_hashes| contains
+ // 64-bit, FNV-1a hashes of certificates that the peer already possesses.
+ static std::string CompressChain(const std::vector<std::string>& certs,
+ QuicStringPiece client_common_set_hashes,
+ QuicStringPiece client_cached_cert_hashes,
+ const CommonCertSets* common_sets);
+
+ // DecompressChain decompresses the result of |CompressChain|, given in |in|,
+ // into a series of certificates that are written to |out_certs|.
+ // |cached_certs| contains certificates that the peer may have omitted and
+ // |common_sets| contains the common certificate sets known locally.
+ static bool DecompressChain(QuicStringPiece in,
+ const std::vector<std::string>& cached_certs,
+ const CommonCertSets* common_sets,
+ std::vector<std::string>* out_certs);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CERT_COMPRESSOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor_test.cc
new file mode 100644
index 00000000000..1341f790d56
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/cert_compressor_test.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+namespace quic {
+namespace test {
+
+class CertCompressorTest : public QuicTest {};
+
+TEST_F(CertCompressorTest, EmptyChain) {
+ std::vector<std::string> chain;
+ const std::string compressed = CertCompressor::CompressChain(
+ chain, QuicStringPiece(), QuicStringPiece(), nullptr);
+ EXPECT_EQ("00", QuicTextUtils::HexEncode(compressed));
+
+ std::vector<std::string> chain2, cached_certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr,
+ &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+}
+
+TEST_F(CertCompressorTest, Compressed) {
+ std::vector<std::string> chain;
+ chain.push_back("testcert");
+ const std::string compressed = CertCompressor::CompressChain(
+ chain, QuicStringPiece(), QuicStringPiece(), nullptr);
+ ASSERT_GE(compressed.size(), 2u);
+ EXPECT_EQ("0100", QuicTextUtils::HexEncode(compressed.substr(0, 2)));
+
+ std::vector<std::string> chain2, cached_certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr,
+ &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST_F(CertCompressorTest, Common) {
+ std::vector<std::string> chain;
+ chain.push_back("testcert");
+ static const uint64_t set_hash = 42;
+ std::unique_ptr<CommonCertSets> common_sets(
+ crypto_test_utils::MockCommonCertSets(chain[0], set_hash, 1));
+ const std::string compressed = CertCompressor::CompressChain(
+ chain,
+ QuicStringPiece(reinterpret_cast<const char*>(&set_hash),
+ sizeof(set_hash)),
+ QuicStringPiece(), common_sets.get());
+ EXPECT_EQ(
+ "03" /* common */
+ "2a00000000000000" /* set hash 42 */
+ "01000000" /* index 1 */
+ "00" /* end of list */,
+ QuicTextUtils::HexEncode(compressed));
+
+ std::vector<std::string> chain2, cached_certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs,
+ common_sets.get(), &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST_F(CertCompressorTest, Cached) {
+ std::vector<std::string> chain;
+ chain.push_back("testcert");
+ uint64_t hash = QuicUtils::FNV1a_64_Hash(chain[0]);
+ QuicStringPiece hash_bytes(reinterpret_cast<char*>(&hash), sizeof(hash));
+ const std::string compressed = CertCompressor::CompressChain(
+ chain, QuicStringPiece(), hash_bytes, nullptr);
+
+ EXPECT_EQ("02" /* cached */ + QuicTextUtils::HexEncode(hash_bytes) +
+ "00" /* end of list */,
+ QuicTextUtils::HexEncode(compressed));
+
+ std::vector<std::string> cached_certs, chain2;
+ cached_certs.push_back(chain[0]);
+ ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr,
+ &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST_F(CertCompressorTest, BadInputs) {
+ std::vector<std::string> cached_certs, chain;
+
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ QuicTextUtils::HexEncode("04") /* bad entry type */, cached_certs,
+ nullptr, &chain));
+
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ QuicTextUtils::HexEncode("01") /* no terminator */, cached_certs, nullptr,
+ &chain));
+
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ QuicTextUtils::HexEncode("0200") /* hash truncated */, cached_certs,
+ nullptr, &chain));
+
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ QuicTextUtils::HexEncode("0300") /* hash and index truncated */,
+ cached_certs, nullptr, &chain));
+
+ /* without a CommonCertSets */
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ QuicTextUtils::HexEncode("03"
+ "0000000000000000"
+ "00000000"),
+ cached_certs, nullptr, &chain));
+
+ std::unique_ptr<CommonCertSets> common_sets(
+ crypto_test_utils::MockCommonCertSets("foo", 42, 1));
+
+ /* incorrect hash and index */
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ QuicTextUtils::HexEncode("03"
+ "a200000000000000"
+ "00000000"),
+ cached_certs, nullptr, &chain));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.cc
new file mode 100644
index 00000000000..e2f55aa32ab
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aead.h"
+#include "third_party/boringssl/src/include/openssl/tls1.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : ChaChaBaseDecrypter(EVP_aead_chacha20_poly1305,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ false) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+uint32_t ChaCha20Poly1305Decrypter::cipher_id() const {
+ return TLS1_CK_CHACHA20_POLY1305_SHA256;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h
new file mode 100644
index 00000000000..50bb348e98c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ChaCha20Poly1305Decrypter is a QuicDecrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539, except that
+// it truncates the Poly1305 authenticator to 12 bytes. Create an instance
+// by calling QuicDecrypter::Create(kCC20).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix of the
+// nonce is four bytes.
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Decrypter
+ : public ChaChaBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Decrypter();
+ ChaCha20Poly1305Decrypter(const ChaCha20Poly1305Decrypter&) = delete;
+ ChaCha20Poly1305Decrypter& operator=(const ChaCha20Poly1305Decrypter&) =
+ delete;
+ ~ChaCha20Poly1305Decrypter() override;
+
+ uint32_t cipher_id() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
new file mode 100644
index 00000000000..39c0e8ab7a0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter_test.cc
@@ -0,0 +1,176 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The test vectors come from RFC 7539 Section 2.8.2.
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* fixed;
+ const char* aad;
+ const char* ct;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestVector test_vectors[] = {
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecb", // "d0600691" truncated
+
+ "4c616469657320616e642047656e746c"
+ "656d656e206f662074686520636c6173"
+ "73206f66202739393a20496620492063"
+ "6f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220"
+ "746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069"
+ "742e"},
+ // Modify the ciphertext (Poly1305 authenticator).
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecc", // "d0600691" truncated
+
+ nullptr},
+ // Modify the associated data.
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "60515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecb", // "d0600691" truncated
+
+ nullptr},
+ {nullptr}};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(ChaCha20Poly1305Decrypter* decrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext) {
+ uint64_t packet_number;
+ QuicStringPiece nonce_prefix(nonce.data(),
+ nonce.size() - sizeof(packet_number));
+ decrypter->SetNoncePrefix(nonce_prefix);
+ memcpy(&packet_number, nonce.data() + nonce_prefix.size(),
+ sizeof(packet_number));
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success = decrypter->DecryptPacket(
+ packet_number, associated_data, ciphertext, output.get(), &output_length,
+ ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class ChaCha20Poly1305DecrypterTest : public QuicTest {};
+
+TEST_F(ChaCha20Poly1305DecrypterTest, Decrypt) {
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[i].pt;
+
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[i].key);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[i].iv);
+ std::string fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[i].aad);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[i].ct);
+ std::string pt;
+ if (has_pt) {
+ pt = QuicTextUtils::HexDecode(test_vectors[i].pt);
+ }
+
+ ChaCha20Poly1305Decrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+ std::unique_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, fixed + iv,
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ QuicStringPiece(aad.length() ? aad.data() : nullptr, aad.length()),
+ ct));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ EXPECT_EQ(12u, ct.size() - decrypted->length());
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.cc
new file mode 100644
index 00000000000..37b899f90f6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : ChaChaBaseEncrypter(EVP_aead_chacha20_poly1305,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ false) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h
new file mode 100644
index 00000000000..72007883ffd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539, except that
+// it truncates the Poly1305 authenticator to 12 bytes. Create an instance
+// by calling QuicEncrypter::Create(kCC20).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix of the
+// nonce is four bytes.
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Encrypter
+ : public ChaChaBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Encrypter();
+ ChaCha20Poly1305Encrypter(const ChaCha20Poly1305Encrypter&) = delete;
+ ChaCha20Poly1305Encrypter& operator=(const ChaCha20Poly1305Encrypter&) =
+ delete;
+ ~ChaCha20Poly1305Encrypter() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
new file mode 100644
index 00000000000..e04d864202e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The test vectors come from RFC 7539 Section 2.8.2.
+
+// Each test vector consists of five strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* pt;
+ const char* iv;
+ const char* fixed;
+ const char* aad;
+ const char* ct;
+};
+
+const TestVector test_vectors[] = {
+ {
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4c616469657320616e642047656e746c"
+ "656d656e206f662074686520636c6173"
+ "73206f66202739393a20496620492063"
+ "6f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220"
+ "746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069"
+ "742e",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecb", // "d0600691" truncated
+ },
+ {nullptr}};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(ChaCha20Poly1305Encrypter* encrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class ChaCha20Poly1305EncrypterTest : public QuicTest {};
+
+TEST_F(ChaCha20Poly1305EncrypterTest, EncryptThenDecrypt) {
+ ChaCha20Poly1305Encrypter encrypter;
+ ChaCha20Poly1305Decrypter decrypter;
+
+ std::string key = QuicTextUtils::HexDecode(test_vectors[0].key);
+ ASSERT_TRUE(encrypter.SetKey(key));
+ ASSERT_TRUE(decrypter.SetKey(key));
+ ASSERT_TRUE(encrypter.SetNoncePrefix("abcd"));
+ ASSERT_TRUE(decrypter.SetNoncePrefix("abcd"));
+
+ uint64_t packet_number = UINT64_C(0x123456789ABC);
+ std::string associated_data = "associated_data";
+ std::string plaintext = "plaintext";
+ char encrypted[1024];
+ size_t len;
+ ASSERT_TRUE(encrypter.EncryptPacket(packet_number, associated_data, plaintext,
+ encrypted, &len,
+ QUIC_ARRAYSIZE(encrypted)));
+ QuicStringPiece ciphertext(encrypted, len);
+ char decrypted[1024];
+ ASSERT_TRUE(decrypter.DecryptPacket(packet_number, associated_data,
+ ciphertext, decrypted, &len,
+ QUIC_ARRAYSIZE(decrypted)));
+}
+
+TEST_F(ChaCha20Poly1305EncrypterTest, Encrypt) {
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[i].key);
+ std::string pt = QuicTextUtils::HexDecode(test_vectors[i].pt);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[i].iv);
+ std::string fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[i].aad);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[i].ct);
+
+ ChaCha20Poly1305Encrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(EncryptWithNonce(
+ &encrypter, fixed + iv,
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ QuicStringPiece(aad.length() ? aad.data() : nullptr, aad.length()),
+ pt));
+ ASSERT_TRUE(encrypted.get());
+ EXPECT_EQ(12u, ct.size() - pt.size());
+ EXPECT_EQ(12u, encrypted->length() - pt.size());
+
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ encrypted->length(), ct.data(),
+ ct.length());
+ }
+}
+
+TEST_F(ChaCha20Poly1305EncrypterTest, GetMaxPlaintextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST_F(ChaCha20Poly1305EncrypterTest, GetCiphertextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
new file mode 100644
index 00000000000..8d98da89bf1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
@@ -0,0 +1,37 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aead.h"
+#include "third_party/boringssl/src/include/openssl/tls1.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+ChaCha20Poly1305TlsDecrypter::ChaCha20Poly1305TlsDecrypter()
+ : ChaChaBaseDecrypter(EVP_aead_chacha20_poly1305,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+ChaCha20Poly1305TlsDecrypter::~ChaCha20Poly1305TlsDecrypter() {}
+
+uint32_t ChaCha20Poly1305TlsDecrypter::cipher_id() const {
+ return TLS1_CK_CHACHA20_POLY1305_SHA256;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
new file mode 100644
index 00000000000..702fb8c7bcf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
@@ -0,0 +1,38 @@
+// 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_CORE_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ChaCha20Poly1305TlsDecrypter is a QuicDecrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 bytes IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305TlsDecrypter
+ : public ChaChaBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ ChaCha20Poly1305TlsDecrypter();
+ ChaCha20Poly1305TlsDecrypter(const ChaCha20Poly1305TlsDecrypter&) = delete;
+ ChaCha20Poly1305TlsDecrypter& operator=(const ChaCha20Poly1305TlsDecrypter&) =
+ delete;
+ ~ChaCha20Poly1305TlsDecrypter() override;
+
+ uint32_t cipher_id() const override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
new file mode 100644
index 00000000000..02ecc730c64
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
@@ -0,0 +1,186 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The test vectors come from RFC 7539 Section 2.8.2.
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* fixed;
+ const char* aad;
+ const char* ct;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. nullptr means decryption
+ // failed.
+};
+
+const TestVector test_vectors[] = {
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecbd0600691",
+
+ "4c616469657320616e642047656e746c"
+ "656d656e206f662074686520636c6173"
+ "73206f66202739393a20496620492063"
+ "6f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220"
+ "746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069"
+ "742e"},
+ // Modify the ciphertext (Poly1305 authenticator).
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902eccd0600691",
+
+ nullptr},
+ // Modify the associated data.
+ {"808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "60515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecbd0600691",
+
+ nullptr},
+ {nullptr}};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(ChaCha20Poly1305TlsDecrypter* decrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext) {
+ decrypter->SetIV(nonce);
+ std::unique_ptr<char[]> output(new char[ciphertext.length()]);
+ size_t output_length = 0;
+ const bool success =
+ decrypter->DecryptPacket(0, associated_data, ciphertext, output.get(),
+ &output_length, ciphertext.length());
+ if (!success) {
+ return nullptr;
+ }
+ return new QuicData(output.release(), output_length, true);
+}
+
+class ChaCha20Poly1305TlsDecrypterTest : public QuicTest {};
+
+TEST_F(ChaCha20Poly1305TlsDecrypterTest, Decrypt) {
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[i].pt;
+
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[i].key);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[i].iv);
+ std::string fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[i].aad);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[i].ct);
+ std::string pt;
+ if (has_pt) {
+ pt = QuicTextUtils::HexDecode(test_vectors[i].pt);
+ }
+
+ ChaCha20Poly1305TlsDecrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+ std::unique_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, fixed + iv,
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ QuicStringPiece(aad.length() ? aad.data() : nullptr, aad.length()),
+ ct));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ EXPECT_EQ(16u, ct.size() - decrypted->length());
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+}
+
+TEST_F(ChaCha20Poly1305TlsDecrypterTest, GenerateHeaderProtectionMask) {
+ ChaCha20Poly1305TlsDecrypter decrypter;
+ std::string key = QuicTextUtils::HexDecode(
+ "6a067f432787bd6034dd3f08f07fc9703a27e58c70e2d88d948b7f6489923cc7");
+ std::string sample =
+ QuicTextUtils::HexDecode("1210d91cceb45c716b023f492c29e612");
+ QuicDataReader sample_reader(sample.data(), sample.size());
+ ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+ std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+ std::string expected_mask = QuicTextUtils::HexDecode("1cc2cd98dc");
+ test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+ mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc
new file mode 100644
index 00000000000..c650b27f40e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc
@@ -0,0 +1,30 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNonceSize = 12;
+
+} // namespace
+
+ChaCha20Poly1305TlsEncrypter::ChaCha20Poly1305TlsEncrypter()
+ : ChaChaBaseEncrypter(EVP_aead_chacha20_poly1305,
+ kKeySize,
+ kAuthTagSize,
+ kNonceSize,
+ /* use_ietf_nonce_construction */ true) {
+ static_assert(kKeySize <= kMaxKeySize, "key size too big");
+ static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
+}
+
+ChaCha20Poly1305TlsEncrypter::~ChaCha20Poly1305TlsEncrypter() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.h
new file mode 100644
index 00000000000..0ef7ae8e21a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.h
@@ -0,0 +1,34 @@
+// 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_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in RFC 7539 for use in IETF QUIC.
+//
+// It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
+// that is XOR'd with the packet number to compute the nonce.
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305TlsEncrypter
+ : public ChaChaBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 16,
+ };
+
+ ChaCha20Poly1305TlsEncrypter();
+ ChaCha20Poly1305TlsEncrypter(const ChaCha20Poly1305TlsEncrypter&) = delete;
+ ChaCha20Poly1305TlsEncrypter& operator=(const ChaCha20Poly1305TlsEncrypter&) =
+ delete;
+ ~ChaCha20Poly1305TlsEncrypter() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
new file mode 100644
index 00000000000..32fde507a77
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
@@ -0,0 +1,170 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace {
+
+// The test vectors come from RFC 7539 Section 2.8.2.
+
+// Each test vector consists of five strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a nullptr |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* pt;
+ const char* iv;
+ const char* fixed;
+ const char* aad;
+ const char* ct;
+};
+
+const TestVector test_vectors[] = {{
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f",
+
+ "4c616469657320616e642047656e746c"
+ "656d656e206f662074686520636c6173"
+ "73206f66202739393a20496620492063"
+ "6f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220"
+ "746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069"
+ "742e",
+
+ "4041424344454647",
+
+ "07000000",
+
+ "50515253c0c1c2c3c4c5c6c7",
+
+ "d31a8d34648e60db7b86afbc53ef7ec2"
+ "a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b"
+ "1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58"
+ "fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b"
+ "6116"
+ "1ae10b594f09e26a7e902ecbd0600691",
+ },
+ {nullptr}};
+
+} // namespace
+
+namespace quic {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(ChaCha20Poly1305TlsEncrypter* encrypter,
+ QuicStringPiece nonce,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ std::unique_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+class ChaCha20Poly1305TlsEncrypterTest : public QuicTest {};
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, EncryptThenDecrypt) {
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ ChaCha20Poly1305TlsDecrypter decrypter;
+
+ std::string key = QuicTextUtils::HexDecode(test_vectors[0].key);
+ ASSERT_TRUE(encrypter.SetKey(key));
+ ASSERT_TRUE(decrypter.SetKey(key));
+ ASSERT_TRUE(encrypter.SetIV("abcdefghijkl"));
+ ASSERT_TRUE(decrypter.SetIV("abcdefghijkl"));
+
+ uint64_t packet_number = UINT64_C(0x123456789ABC);
+ std::string associated_data = "associated_data";
+ std::string plaintext = "plaintext";
+ char encrypted[1024];
+ size_t len;
+ ASSERT_TRUE(encrypter.EncryptPacket(packet_number, associated_data, plaintext,
+ encrypted, &len,
+ QUIC_ARRAYSIZE(encrypted)));
+ QuicStringPiece ciphertext(encrypted, len);
+ char decrypted[1024];
+ ASSERT_TRUE(decrypter.DecryptPacket(packet_number, associated_data,
+ ciphertext, decrypted, &len,
+ QUIC_ARRAYSIZE(decrypted)));
+}
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, Encrypt) {
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // Decode the test vector.
+ std::string key = QuicTextUtils::HexDecode(test_vectors[i].key);
+ std::string pt = QuicTextUtils::HexDecode(test_vectors[i].pt);
+ std::string iv = QuicTextUtils::HexDecode(test_vectors[i].iv);
+ std::string fixed = QuicTextUtils::HexDecode(test_vectors[i].fixed);
+ std::string aad = QuicTextUtils::HexDecode(test_vectors[i].aad);
+ std::string ct = QuicTextUtils::HexDecode(test_vectors[i].ct);
+
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ std::unique_ptr<QuicData> encrypted(EncryptWithNonce(
+ &encrypter, fixed + iv,
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ QuicStringPiece(aad.length() ? aad.data() : nullptr, aad.length()),
+ pt));
+ ASSERT_TRUE(encrypted.get());
+ EXPECT_EQ(16u, ct.size() - pt.size());
+ EXPECT_EQ(16u, encrypted->length() - pt.size());
+
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ encrypted->length(), ct.data(),
+ ct.length());
+ }
+}
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, GetMaxPlaintextSize) {
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26));
+}
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, GetCiphertextSize) {
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(116u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
+}
+
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, GenerateHeaderProtectionMask) {
+ ChaCha20Poly1305TlsEncrypter encrypter;
+ std::string key = QuicTextUtils::HexDecode(
+ "6a067f432787bd6034dd3f08f07fc9703a27e58c70e2d88d948b7f6489923cc7");
+ std::string sample =
+ QuicTextUtils::HexDecode("1210d91cceb45c716b023f492c29e612");
+ ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+ std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+ std::string expected_mask = QuicTextUtils::HexDecode("1cc2cd98dc");
+ test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+ mask.size(), expected_mask.data(),
+ expected_mask.size());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.cc
new file mode 100644
index 00000000000..eb1e95fb98c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h"
+
+#include <cstdint>
+
+#include "third_party/boringssl/src/include/openssl/chacha.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool ChaChaBaseDecrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+ if (key.size() != GetKeySize()) {
+ QUIC_BUG << "Invalid key size for header protection";
+ return false;
+ }
+ memcpy(pne_key_, key.data(), key.size());
+ return true;
+}
+
+std::string ChaChaBaseDecrypter::GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) {
+ QuicStringPiece sample;
+ if (!sample_reader->ReadStringPiece(&sample, 16)) {
+ return std::string();
+ }
+ const uint8_t* nonce = reinterpret_cast<const uint8_t*>(sample.data()) + 4;
+ uint32_t counter;
+ QuicDataReader(sample.data(), 4, Endianness::HOST_BYTE_ORDER)
+ .ReadUInt32(&counter);
+ const uint8_t zeroes[] = {0, 0, 0, 0, 0};
+ std::string out(QUIC_ARRAYSIZE(zeroes), 0);
+ CRYPTO_chacha_20(reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+ zeroes, QUIC_ARRAYSIZE(zeroes), pne_key_, nonce, counter);
+ return out;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h
new file mode 100644
index 00000000000..d7ad7419526
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE ChaChaBaseDecrypter : public AeadBaseDecrypter {
+ public:
+ using AeadBaseDecrypter::AeadBaseDecrypter;
+
+ bool SetHeaderProtectionKey(QuicStringPiece key) override;
+ std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) override;
+
+ private:
+ // The key used for packet number encryption.
+ unsigned char pne_key_[kMaxKeySize];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.cc
new file mode 100644
index 00000000000..04d902f4b4c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/chacha.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool ChaChaBaseEncrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+ if (key.size() != GetKeySize()) {
+ QUIC_BUG << "Invalid key size for header protection";
+ return false;
+ }
+ memcpy(pne_key_, key.data(), key.size());
+ return true;
+}
+
+std::string ChaChaBaseEncrypter::GenerateHeaderProtectionMask(
+ QuicStringPiece sample) {
+ if (sample.size() != 16) {
+ return std::string();
+ }
+ const uint8_t* nonce = reinterpret_cast<const uint8_t*>(sample.data()) + 4;
+ uint32_t counter;
+ QuicDataReader(sample.data(), 4, Endianness::HOST_BYTE_ORDER)
+ .ReadUInt32(&counter);
+ const uint8_t zeroes[] = {0, 0, 0, 0, 0};
+ std::string out(QUIC_ARRAYSIZE(zeroes), 0);
+ CRYPTO_chacha_20(reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+ zeroes, QUIC_ARRAYSIZE(zeroes), pne_key_, nonce, counter);
+ return out;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h
new file mode 100644
index 00000000000..7c086acf81f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE ChaChaBaseEncrypter : public AeadBaseEncrypter {
+ public:
+ using AeadBaseEncrypter::AeadBaseEncrypter;
+
+ bool SetHeaderProtectionKey(QuicStringPiece key) override;
+ std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override;
+
+ private:
+ // The key used for packet number encryption.
+ unsigned char pne_key_[kMaxKeySize];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id.cc
new file mode 100644
index 00000000000..0c4a3e94e8d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id.cc
@@ -0,0 +1,89 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+
+#include <cstdint>
+
+#include "third_party/boringssl/src/include/openssl/bn.h"
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ecdsa.h"
+#include "third_party/boringssl/src/include/openssl/nid.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+
+namespace quic {
+
+// static
+const char ChannelIDVerifier::kContextStr[] = "QUIC ChannelID";
+// static
+const char ChannelIDVerifier::kClientToServerStr[] = "client -> server";
+
+// static
+bool ChannelIDVerifier::Verify(QuicStringPiece key,
+ QuicStringPiece signed_data,
+ QuicStringPiece signature) {
+ return VerifyRaw(key, signed_data, signature, true);
+}
+
+// static
+bool ChannelIDVerifier::VerifyRaw(QuicStringPiece key,
+ QuicStringPiece signed_data,
+ QuicStringPiece signature,
+ bool is_channel_id_signature) {
+ if (key.size() != 32 * 2 || signature.size() != 32 * 2) {
+ return false;
+ }
+
+ bssl::UniquePtr<EC_GROUP> p256(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ if (p256.get() == nullptr) {
+ return false;
+ }
+
+ bssl::UniquePtr<BIGNUM> x(BN_new()), y(BN_new()), r(BN_new()), s(BN_new());
+
+ ECDSA_SIG sig;
+ sig.r = r.get();
+ sig.s = s.get();
+
+ const uint8_t* key_bytes = reinterpret_cast<const uint8_t*>(key.data());
+ const uint8_t* signature_bytes =
+ reinterpret_cast<const uint8_t*>(signature.data());
+
+ if (BN_bin2bn(key_bytes + 0, 32, x.get()) == nullptr ||
+ BN_bin2bn(key_bytes + 32, 32, y.get()) == nullptr ||
+ BN_bin2bn(signature_bytes + 0, 32, sig.r) == nullptr ||
+ BN_bin2bn(signature_bytes + 32, 32, sig.s) == nullptr) {
+ return false;
+ }
+
+ bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
+ if (point.get() == nullptr ||
+ !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(),
+ y.get(), nullptr)) {
+ return false;
+ }
+
+ bssl::UniquePtr<EC_KEY> ecdsa_key(EC_KEY_new());
+ if (ecdsa_key.get() == nullptr ||
+ !EC_KEY_set_group(ecdsa_key.get(), p256.get()) ||
+ !EC_KEY_set_public_key(ecdsa_key.get(), point.get())) {
+ return false;
+ }
+
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ if (is_channel_id_signature) {
+ SHA256_Update(&sha256, kContextStr, strlen(kContextStr) + 1);
+ SHA256_Update(&sha256, kClientToServerStr, strlen(kClientToServerStr) + 1);
+ }
+ SHA256_Update(&sha256, signed_data.data(), signed_data.size());
+
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_Final(digest, &sha256);
+
+ return ECDSA_do_verify(digest, sizeof(digest), &sig, ecdsa_key.get()) == 1;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id.h b/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id.h
new file mode 100644
index 00000000000..309aaf207d8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id.h
@@ -0,0 +1,97 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHANNEL_ID_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHANNEL_ID_H_
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// ChannelIDKey is an interface that supports signing with and serializing a
+// ChannelID key.
+class QUIC_EXPORT_PRIVATE ChannelIDKey {
+ public:
+ virtual ~ChannelIDKey() {}
+
+ // Sign signs |signed_data| using the ChannelID private key and puts the
+ // signature into |out_signature|. It returns true on success.
+ virtual bool Sign(QuicStringPiece signed_data,
+ std::string* out_signature) const = 0;
+
+ // SerializeKey returns the serialized ChannelID public key.
+ virtual std::string SerializeKey() const = 0;
+};
+
+// ChannelIDSourceCallback provides a generic mechanism for a ChannelIDSource
+// to call back after an asynchronous GetChannelIDKey operation.
+class ChannelIDSourceCallback {
+ public:
+ virtual ~ChannelIDSourceCallback() {}
+
+ // Run is called on the original thread to mark the completion of an
+ // asynchonous GetChannelIDKey operation. If |*channel_id_key| is not nullptr
+ // then the channel ID lookup is successful. |Run| may take ownership of
+ // |*channel_id_key| by calling |release| on it.
+ virtual void Run(std::unique_ptr<ChannelIDKey>* channel_id_key) = 0;
+};
+
+// ChannelIDSource is an abstract interface by which a QUIC client can obtain
+// a ChannelIDKey for a given hostname.
+class QUIC_EXPORT_PRIVATE ChannelIDSource {
+ public:
+ virtual ~ChannelIDSource() {}
+
+ // GetChannelIDKey looks up the ChannelIDKey for |hostname|. On success it
+ // returns QUIC_SUCCESS and stores the ChannelIDKey in |*channel_id_key|,
+ // which the caller takes ownership of. On failure, it returns QUIC_FAILURE.
+ //
+ // This function may also return QUIC_PENDING, in which case the
+ // ChannelIDSource will call back, on the original thread, via |callback|
+ // when complete. In this case, the ChannelIDSource will take ownership of
+ // |callback|.
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const std::string& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) = 0;
+};
+
+// ChannelIDVerifier verifies ChannelID signatures.
+class QUIC_EXPORT_PRIVATE ChannelIDVerifier {
+ public:
+ ChannelIDVerifier() = delete;
+
+ // kContextStr is prepended to the data to be signed in order to ensure that
+ // a ChannelID signature cannot be used in a different context. (The
+ // terminating NUL byte is inclued.)
+ static const char kContextStr[];
+ // kClientToServerStr follows kContextStr to specify that the ChannelID is
+ // being used in the client to server direction. (The terminating NUL byte is
+ // included.)
+ static const char kClientToServerStr[];
+
+ // Verify returns true iff |signature| is a valid signature of |signed_data|
+ // by |key|.
+ static bool Verify(QuicStringPiece key,
+ QuicStringPiece signed_data,
+ QuicStringPiece signature);
+
+ // FOR TESTING ONLY: VerifyRaw returns true iff |signature| is a valid
+ // signature of |signed_data| by |key|. |is_channel_id_signature| indicates
+ // whether |signature| is a ChannelID signature (with kContextStr prepended
+ // to the data to be signed).
+ static bool VerifyRaw(QuicStringPiece key,
+ QuicStringPiece signed_data,
+ QuicStringPiece signature,
+ bool is_channel_id_signature);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CHANNEL_ID_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id_test.cc
new file mode 100644
index 00000000000..99a84dd5909
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/channel_id_test.cc
@@ -0,0 +1,321 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+
+// The following ECDSA signature verification test vectors for P-256,SHA-256
+// come from the SigVer.rsp file in
+// http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
+// downloaded on 2013-06-11.
+struct TestVector {
+ // Input:
+ const char* msg;
+ const char* qx;
+ const char* qy;
+ const char* r;
+ const char* s;
+
+ // Expected output:
+ bool result; // true means "P", false means "F"
+};
+
+const TestVector test_vector[] = {
+ {
+ "e4796db5f785f207aa30d311693b3702821dff1168fd2e04c0836825aefd850d"
+ "9aa60326d88cde1a23c7745351392ca2288d632c264f197d05cd424a30336c19"
+ "fd09bb229654f0222fcb881a4b35c290a093ac159ce13409111ff0358411133c"
+ "24f5b8e2090d6db6558afc36f06ca1f6ef779785adba68db27a409859fc4c4a0",
+ "87f8f2b218f49845f6f10eec3877136269f5c1a54736dbdf69f89940cad41555",
+ "e15f369036f49842fac7a86c8a2b0557609776814448b8f5e84aa9f4395205e9",
+ "d19ff48b324915576416097d2544f7cbdf8768b1454ad20e0baac50e211f23b0",
+ "a3e81e59311cdfff2d4784949f7a2cb50ba6c3a91fa54710568e61aca3e847c6",
+ false // F (3 - S changed)
+ },
+ {
+ "069a6e6b93dfee6df6ef6997cd80dd2182c36653cef10c655d524585655462d6"
+ "83877f95ecc6d6c81623d8fac4e900ed0019964094e7de91f1481989ae187300"
+ "4565789cbf5dc56c62aedc63f62f3b894c9c6f7788c8ecaadc9bd0e81ad91b2b"
+ "3569ea12260e93924fdddd3972af5273198f5efda0746219475017557616170e",
+ "5cf02a00d205bdfee2016f7421807fc38ae69e6b7ccd064ee689fc1a94a9f7d2",
+ "ec530ce3cc5c9d1af463f264d685afe2b4db4b5828d7e61b748930f3ce622a85",
+ "dc23d130c6117fb5751201455e99f36f59aba1a6a21cf2d0e7481a97451d6693",
+ "d6ce7708c18dbf35d4f8aa7240922dc6823f2e7058cbc1484fcad1599db5018c",
+ false // F (2 - R changed)
+ },
+ {
+ "df04a346cf4d0e331a6db78cca2d456d31b0a000aa51441defdb97bbeb20b94d"
+ "8d746429a393ba88840d661615e07def615a342abedfa4ce912e562af7149598"
+ "96858af817317a840dcff85a057bb91a3c2bf90105500362754a6dd321cdd861"
+ "28cfc5f04667b57aa78c112411e42da304f1012d48cd6a7052d7de44ebcc01de",
+ "2ddfd145767883ffbb0ac003ab4a44346d08fa2570b3120dcce94562422244cb",
+ "5f70c7d11ac2b7a435ccfbbae02c3df1ea6b532cc0e9db74f93fffca7c6f9a64",
+ "9913111cff6f20c5bf453a99cd2c2019a4e749a49724a08774d14e4c113edda8",
+ "9467cd4cd21ecb56b0cab0a9a453b43386845459127a952421f5c6382866c5cc",
+ false // F (4 - Q changed)
+ },
+ {
+ "e1130af6a38ccb412a9c8d13e15dbfc9e69a16385af3c3f1e5da954fd5e7c45f"
+ "d75e2b8c36699228e92840c0562fbf3772f07e17f1add56588dd45f7450e1217"
+ "ad239922dd9c32695dc71ff2424ca0dec1321aa47064a044b7fe3c2b97d03ce4"
+ "70a592304c5ef21eed9f93da56bb232d1eeb0035f9bf0dfafdcc4606272b20a3",
+ "e424dc61d4bb3cb7ef4344a7f8957a0c5134e16f7a67c074f82e6e12f49abf3c",
+ "970eed7aa2bc48651545949de1dddaf0127e5965ac85d1243d6f60e7dfaee927",
+ "bf96b99aa49c705c910be33142017c642ff540c76349b9dab72f981fd9347f4f",
+ "17c55095819089c2e03b9cd415abdf12444e323075d98f31920b9e0f57ec871c",
+ true // P (0 )
+ },
+ {
+ "73c5f6a67456ae48209b5f85d1e7de7758bf235300c6ae2bdceb1dcb27a7730f"
+ "b68c950b7fcada0ecc4661d3578230f225a875e69aaa17f1e71c6be5c831f226"
+ "63bac63d0c7a9635edb0043ff8c6f26470f02a7bc56556f1437f06dfa27b487a"
+ "6c4290d8bad38d4879b334e341ba092dde4e4ae694a9c09302e2dbf443581c08",
+ "e0fc6a6f50e1c57475673ee54e3a57f9a49f3328e743bf52f335e3eeaa3d2864",
+ "7f59d689c91e463607d9194d99faf316e25432870816dde63f5d4b373f12f22a",
+ "1d75830cd36f4c9aa181b2c4221e87f176b7f05b7c87824e82e396c88315c407",
+ "cb2acb01dac96efc53a32d4a0d85d0c2e48955214783ecf50a4f0414a319c05a",
+ true // P (0 )
+ },
+ {
+ "666036d9b4a2426ed6585a4e0fd931a8761451d29ab04bd7dc6d0c5b9e38e6c2"
+ "b263ff6cb837bd04399de3d757c6c7005f6d7a987063cf6d7e8cb38a4bf0d74a"
+ "282572bd01d0f41e3fd066e3021575f0fa04f27b700d5b7ddddf50965993c3f9"
+ "c7118ed78888da7cb221849b3260592b8e632d7c51e935a0ceae15207bedd548",
+ "a849bef575cac3c6920fbce675c3b787136209f855de19ffe2e8d29b31a5ad86",
+ "bf5fe4f7858f9b805bd8dcc05ad5e7fb889de2f822f3d8b41694e6c55c16b471",
+ "25acc3aa9d9e84c7abf08f73fa4195acc506491d6fc37cb9074528a7db87b9d6",
+ "9b21d5b5259ed3f2ef07dfec6cc90d3a37855d1ce122a85ba6a333f307d31537",
+ false // F (2 - R changed)
+ },
+ {
+ "7e80436bce57339ce8da1b5660149a20240b146d108deef3ec5da4ae256f8f89"
+ "4edcbbc57b34ce37089c0daa17f0c46cd82b5a1599314fd79d2fd2f446bd5a25"
+ "b8e32fcf05b76d644573a6df4ad1dfea707b479d97237a346f1ec632ea5660ef"
+ "b57e8717a8628d7f82af50a4e84b11f21bdff6839196a880ae20b2a0918d58cd",
+ "3dfb6f40f2471b29b77fdccba72d37c21bba019efa40c1c8f91ec405d7dcc5df",
+ "f22f953f1e395a52ead7f3ae3fc47451b438117b1e04d613bc8555b7d6e6d1bb",
+ "548886278e5ec26bed811dbb72db1e154b6f17be70deb1b210107decb1ec2a5a",
+ "e93bfebd2f14f3d827ca32b464be6e69187f5edbd52def4f96599c37d58eee75",
+ false // F (4 - Q changed)
+ },
+ {
+ "1669bfb657fdc62c3ddd63269787fc1c969f1850fb04c933dda063ef74a56ce1"
+ "3e3a649700820f0061efabf849a85d474326c8a541d99830eea8131eaea584f2"
+ "2d88c353965dabcdc4bf6b55949fd529507dfb803ab6b480cd73ca0ba00ca19c"
+ "438849e2cea262a1c57d8f81cd257fb58e19dec7904da97d8386e87b84948169",
+ "69b7667056e1e11d6caf6e45643f8b21e7a4bebda463c7fdbc13bc98efbd0214",
+ "d3f9b12eb46c7c6fda0da3fc85bc1fd831557f9abc902a3be3cb3e8be7d1aa2f",
+ "288f7a1cd391842cce21f00e6f15471c04dc182fe4b14d92dc18910879799790",
+ "247b3c4e89a3bcadfea73c7bfd361def43715fa382b8c3edf4ae15d6e55e9979",
+ false // F (1 - Message changed)
+ },
+ {
+ "3fe60dd9ad6caccf5a6f583b3ae65953563446c4510b70da115ffaa0ba04c076"
+ "115c7043ab8733403cd69c7d14c212c655c07b43a7c71b9a4cffe22c2684788e"
+ "c6870dc2013f269172c822256f9e7cc674791bf2d8486c0f5684283e1649576e"
+ "fc982ede17c7b74b214754d70402fb4bb45ad086cf2cf76b3d63f7fce39ac970",
+ "bf02cbcf6d8cc26e91766d8af0b164fc5968535e84c158eb3bc4e2d79c3cc682",
+ "069ba6cb06b49d60812066afa16ecf7b51352f2c03bd93ec220822b1f3dfba03",
+ "f5acb06c59c2b4927fb852faa07faf4b1852bbb5d06840935e849c4d293d1bad",
+ "049dab79c89cc02f1484c437f523e080a75f134917fda752f2d5ca397addfe5d",
+ false // F (3 - S changed)
+ },
+ {
+ "983a71b9994d95e876d84d28946a041f8f0a3f544cfcc055496580f1dfd4e312"
+ "a2ad418fe69dbc61db230cc0c0ed97e360abab7d6ff4b81ee970a7e97466acfd"
+ "9644f828ffec538abc383d0e92326d1c88c55e1f46a668a039beaa1be631a891"
+ "29938c00a81a3ae46d4aecbf9707f764dbaccea3ef7665e4c4307fa0b0a3075c",
+ "224a4d65b958f6d6afb2904863efd2a734b31798884801fcab5a590f4d6da9de",
+ "178d51fddada62806f097aa615d33b8f2404e6b1479f5fd4859d595734d6d2b9",
+ "87b93ee2fecfda54deb8dff8e426f3c72c8864991f8ec2b3205bb3b416de93d2",
+ "4044a24df85be0cc76f21a4430b75b8e77b932a87f51e4eccbc45c263ebf8f66",
+ false // F (2 - R changed)
+ },
+ {
+ "4a8c071ac4fd0d52faa407b0fe5dab759f7394a5832127f2a3498f34aac28733"
+ "9e043b4ffa79528faf199dc917f7b066ad65505dab0e11e6948515052ce20cfd"
+ "b892ffb8aa9bf3f1aa5be30a5bbe85823bddf70b39fd7ebd4a93a2f75472c1d4"
+ "f606247a9821f1a8c45a6cb80545de2e0c6c0174e2392088c754e9c8443eb5af",
+ "43691c7795a57ead8c5c68536fe934538d46f12889680a9cb6d055a066228369",
+ "f8790110b3c3b281aa1eae037d4f1234aff587d903d93ba3af225c27ddc9ccac",
+ "8acd62e8c262fa50dd9840480969f4ef70f218ebf8ef9584f199031132c6b1ce",
+ "cfca7ed3d4347fb2a29e526b43c348ae1ce6c60d44f3191b6d8ea3a2d9c92154",
+ false // F (3 - S changed)
+ },
+ {
+ "0a3a12c3084c865daf1d302c78215d39bfe0b8bf28272b3c0b74beb4b7409db0"
+ "718239de700785581514321c6440a4bbaea4c76fa47401e151e68cb6c29017f0"
+ "bce4631290af5ea5e2bf3ed742ae110b04ade83a5dbd7358f29a85938e23d87a"
+ "c8233072b79c94670ff0959f9c7f4517862ff829452096c78f5f2e9a7e4e9216",
+ "9157dbfcf8cf385f5bb1568ad5c6e2a8652ba6dfc63bc1753edf5268cb7eb596",
+ "972570f4313d47fc96f7c02d5594d77d46f91e949808825b3d31f029e8296405",
+ "dfaea6f297fa320b707866125c2a7d5d515b51a503bee817de9faa343cc48eeb",
+ "8f780ad713f9c3e5a4f7fa4c519833dfefc6a7432389b1e4af463961f09764f2",
+ false // F (1 - Message changed)
+ },
+ {
+ "785d07a3c54f63dca11f5d1a5f496ee2c2f9288e55007e666c78b007d95cc285"
+ "81dce51f490b30fa73dc9e2d45d075d7e3a95fb8a9e1465ad191904124160b7c"
+ "60fa720ef4ef1c5d2998f40570ae2a870ef3e894c2bc617d8a1dc85c3c557749"
+ "28c38789b4e661349d3f84d2441a3b856a76949b9f1f80bc161648a1cad5588e",
+ "072b10c081a4c1713a294f248aef850e297991aca47fa96a7470abe3b8acfdda",
+ "9581145cca04a0fb94cedce752c8f0370861916d2a94e7c647c5373ce6a4c8f5",
+ "09f5483eccec80f9d104815a1be9cc1a8e5b12b6eb482a65c6907b7480cf4f19",
+ "a4f90e560c5e4eb8696cb276e5165b6a9d486345dedfb094a76e8442d026378d",
+ false // F (4 - Q changed)
+ },
+ {
+ "76f987ec5448dd72219bd30bf6b66b0775c80b394851a43ff1f537f140a6e722"
+ "9ef8cd72ad58b1d2d20298539d6347dd5598812bc65323aceaf05228f738b5ad"
+ "3e8d9fe4100fd767c2f098c77cb99c2992843ba3eed91d32444f3b6db6cd212d"
+ "d4e5609548f4bb62812a920f6e2bf1581be1ebeebdd06ec4e971862cc42055ca",
+ "09308ea5bfad6e5adf408634b3d5ce9240d35442f7fe116452aaec0d25be8c24",
+ "f40c93e023ef494b1c3079b2d10ef67f3170740495ce2cc57f8ee4b0618b8ee5",
+ "5cc8aa7c35743ec0c23dde88dabd5e4fcd0192d2116f6926fef788cddb754e73",
+ "9c9c045ebaa1b828c32f82ace0d18daebf5e156eb7cbfdc1eff4399a8a900ae7",
+ false // F (1 - Message changed)
+ },
+ {
+ "60cd64b2cd2be6c33859b94875120361a24085f3765cb8b2bf11e026fa9d8855"
+ "dbe435acf7882e84f3c7857f96e2baab4d9afe4588e4a82e17a78827bfdb5ddb"
+ "d1c211fbc2e6d884cddd7cb9d90d5bf4a7311b83f352508033812c776a0e00c0"
+ "03c7e0d628e50736c7512df0acfa9f2320bd102229f46495ae6d0857cc452a84",
+ "2d98ea01f754d34bbc3003df5050200abf445ec728556d7ed7d5c54c55552b6d",
+ "9b52672742d637a32add056dfd6d8792f2a33c2e69dafabea09b960bc61e230a",
+ "06108e525f845d0155bf60193222b3219c98e3d49424c2fb2a0987f825c17959",
+ "62b5cdd591e5b507e560167ba8f6f7cda74673eb315680cb89ccbc4eec477dce",
+ true // P (0 )
+ },
+ {nullptr}};
+
+// Returns true if |ch| is a lowercase hexadecimal digit.
+bool IsHexDigit(char ch) {
+ return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f');
+}
+
+// Converts a lowercase hexadecimal digit to its integer value.
+int HexDigitToInt(char ch) {
+ if ('0' <= ch && ch <= '9') {
+ return ch - '0';
+ }
+ return ch - 'a' + 10;
+}
+
+// |in| is a string consisting of lowercase hexadecimal digits, where
+// every two digits represent one byte. |out| is a buffer of size |max_len|.
+// Converts |in| to bytes and stores the bytes in the |out| buffer. The
+// number of bytes converted is returned in |*out_len|. Returns true on
+// success, false on failure.
+bool DecodeHexString(const char* in,
+ char* out,
+ size_t* out_len,
+ size_t max_len) {
+ if (!in) {
+ *out_len = static_cast<size_t>(-1);
+ return true;
+ }
+ *out_len = 0;
+ while (*in != '\0') {
+ if (!IsHexDigit(*in) || !IsHexDigit(*(in + 1))) {
+ return false;
+ }
+ if (*out_len >= max_len) {
+ return false;
+ }
+ out[*out_len] = HexDigitToInt(*in) * 16 + HexDigitToInt(*(in + 1));
+ (*out_len)++;
+ in += 2;
+ }
+ return true;
+}
+
+} // namespace
+
+class ChannelIDTest : public QuicTest {};
+
+// A known answer test for ChannelIDVerifier.
+TEST_F(ChannelIDTest, VerifyKnownAnswerTest) {
+ char msg[1024];
+ size_t msg_len;
+ char key[64];
+ size_t qx_len;
+ size_t qy_len;
+ char signature[64];
+ size_t r_len;
+ size_t s_len;
+
+ for (size_t i = 0; test_vector[i].msg != nullptr; i++) {
+ SCOPED_TRACE(i);
+ // Decode the test vector.
+ ASSERT_TRUE(
+ DecodeHexString(test_vector[i].msg, msg, &msg_len, sizeof(msg)));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].qx, key, &qx_len, sizeof(key)));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].qy, key + qx_len, &qy_len,
+ sizeof(key) - qx_len));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].r, signature, &r_len,
+ sizeof(signature)));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].s, signature + r_len, &s_len,
+ sizeof(signature) - r_len));
+
+ // The test vector's lengths should look sane.
+ EXPECT_EQ(sizeof(key) / 2, qx_len);
+ EXPECT_EQ(sizeof(key) / 2, qy_len);
+ EXPECT_EQ(sizeof(signature) / 2, r_len);
+ EXPECT_EQ(sizeof(signature) / 2, s_len);
+
+ EXPECT_EQ(
+ test_vector[i].result,
+ ChannelIDVerifier::VerifyRaw(
+ QuicStringPiece(key, sizeof(key)), QuicStringPiece(msg, msg_len),
+ QuicStringPiece(signature, sizeof(signature)), false));
+ }
+}
+
+TEST_F(ChannelIDTest, SignAndVerify) {
+ std::unique_ptr<ChannelIDSource> source(
+ crypto_test_utils::ChannelIDSourceForTesting());
+
+ const std::string signed_data = "signed data";
+ const std::string hostname = "foo.example.com";
+ std::unique_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ source->GetChannelIDKey(hostname, &channel_id_key, nullptr);
+ ASSERT_EQ(QUIC_SUCCESS, status);
+
+ std::string signature;
+ ASSERT_TRUE(channel_id_key->Sign(signed_data, &signature));
+
+ std::string key = channel_id_key->SerializeKey();
+ EXPECT_TRUE(ChannelIDVerifier::Verify(key, signed_data, signature));
+
+ EXPECT_FALSE(ChannelIDVerifier::Verify("a" + key, signed_data, signature));
+ EXPECT_FALSE(ChannelIDVerifier::Verify(key, "a" + signed_data, signature));
+
+ std::unique_ptr<char[]> bad_key(new char[key.size()]);
+ memcpy(bad_key.get(), key.data(), key.size());
+ bad_key[1] ^= 0x80;
+ EXPECT_FALSE(ChannelIDVerifier::Verify(std::string(bad_key.get(), key.size()),
+ signed_data, signature));
+
+ std::unique_ptr<char[]> bad_signature(new char[signature.size()]);
+ memcpy(bad_signature.get(), signature.data(), signature.size());
+ bad_signature[1] ^= 0x80;
+ EXPECT_FALSE(ChannelIDVerifier::Verify(
+ key, signed_data, std::string(bad_signature.get(), signature.size())));
+
+ EXPECT_FALSE(ChannelIDVerifier::Verify(key, "wrong signed data", signature));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set.cc
new file mode 100644
index 00000000000..2f337f4bf95
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+
+namespace quic {
+
+namespace common_cert_set_2 {
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_2.c"
+}
+
+namespace common_cert_set_3 {
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_3.c"
+}
+
+namespace {
+
+struct CertSet {
+ // num_certs contains the number of certificates in this set.
+ size_t num_certs;
+ // certs is an array of |num_certs| pointers to the DER encoded certificates.
+ const unsigned char* const* certs;
+ // lens is an array of |num_certs| integers describing the length, in bytes,
+ // of each certificate.
+ const size_t* lens;
+ // hash contains the 64-bit, FNV-1a hash of this set.
+ uint64_t hash;
+};
+
+const CertSet kSets[] = {
+ {
+ common_cert_set_2::kNumCerts,
+ common_cert_set_2::kCerts,
+ common_cert_set_2::kLens,
+ common_cert_set_2::kHash,
+ },
+ {
+ common_cert_set_3::kNumCerts,
+ common_cert_set_3::kCerts,
+ common_cert_set_3::kLens,
+ common_cert_set_3::kHash,
+ },
+};
+
+const uint64_t kSetHashes[] = {
+ common_cert_set_2::kHash,
+ common_cert_set_3::kHash,
+};
+
+// Compare returns a value less than, equal to or greater than zero if |a| is
+// lexicographically less than, equal to or greater than |b|, respectively.
+int Compare(QuicStringPiece a, const unsigned char* b, size_t b_len) {
+ size_t len = a.size();
+ if (len > b_len) {
+ len = b_len;
+ }
+ int n = memcmp(a.data(), b, len);
+ if (n != 0) {
+ return n;
+ }
+
+ if (a.size() < b_len) {
+ return -1;
+ } else if (a.size() > b_len) {
+ return 1;
+ }
+ return 0;
+}
+
+// CommonCertSetsQUIC implements the CommonCertSets interface using the default
+// certificate sets.
+class CommonCertSetsQUIC : public CommonCertSets {
+ public:
+ // CommonCertSets interface.
+ QuicStringPiece GetCommonHashes() const override {
+ return QuicStringPiece(reinterpret_cast<const char*>(kSetHashes),
+ sizeof(uint64_t) * QUIC_ARRAYSIZE(kSetHashes));
+ }
+
+ QuicStringPiece GetCert(uint64_t hash, uint32_t index) const override {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kSets); i++) {
+ if (kSets[i].hash == hash) {
+ if (index < kSets[i].num_certs) {
+ return QuicStringPiece(
+ reinterpret_cast<const char*>(kSets[i].certs[index]),
+ kSets[i].lens[index]);
+ }
+ break;
+ }
+ }
+
+ return QuicStringPiece();
+ }
+
+ bool MatchCert(QuicStringPiece cert,
+ QuicStringPiece common_set_hashes,
+ uint64_t* out_hash,
+ uint32_t* out_index) const override {
+ if (common_set_hashes.size() % sizeof(uint64_t) != 0) {
+ return false;
+ }
+
+ for (size_t i = 0; i < common_set_hashes.size() / sizeof(uint64_t); i++) {
+ uint64_t hash;
+ memcpy(&hash, common_set_hashes.data() + i * sizeof(uint64_t),
+ sizeof(uint64_t));
+
+ for (size_t j = 0; j < QUIC_ARRAYSIZE(kSets); j++) {
+ if (kSets[j].hash != hash) {
+ continue;
+ }
+
+ if (kSets[j].num_certs == 0) {
+ continue;
+ }
+
+ // Binary search for a matching certificate.
+ size_t min = 0;
+ size_t max = kSets[j].num_certs - 1;
+ while (max >= min) {
+ size_t mid = min + ((max - min) / 2);
+ int n = Compare(cert, kSets[j].certs[mid], kSets[j].lens[mid]);
+ if (n < 0) {
+ if (mid == 0) {
+ break;
+ }
+ max = mid - 1;
+ } else if (n > 0) {
+ min = mid + 1;
+ } else {
+ *out_hash = hash;
+ *out_index = mid;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ CommonCertSetsQUIC() {}
+ CommonCertSetsQUIC(const CommonCertSetsQUIC&) = delete;
+ CommonCertSetsQUIC& operator=(const CommonCertSetsQUIC&) = delete;
+ ~CommonCertSetsQUIC() override {}
+};
+
+} // anonymous namespace
+
+CommonCertSets::~CommonCertSets() {}
+
+// static
+const CommonCertSets* CommonCertSets::GetInstanceQUIC() {
+ static CommonCertSetsQUIC* certs = new CommonCertSetsQUIC();
+ return certs;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set.h b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set.h
new file mode 100644
index 00000000000..57c0feafc9a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_COMMON_CERT_SET_H_
+#define QUICHE_QUIC_CORE_CRYPTO_COMMON_CERT_SET_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// CommonCertSets is an interface to an object that contains a number of common
+// certificate sets and can match against them.
+class QUIC_EXPORT_PRIVATE CommonCertSets {
+ public:
+ virtual ~CommonCertSets();
+
+ // GetInstanceQUIC returns the standard QUIC common certificate sets.
+ static const CommonCertSets* GetInstanceQUIC();
+
+ // GetCommonHashes returns a QuicStringPiece containing the hashes of common
+ // sets supported by this object. The 64-bit hashes are concatenated in the
+ // QuicStringPiece.
+ virtual QuicStringPiece GetCommonHashes() const = 0;
+
+ // GetCert returns a specific certificate (at index |index|) in the common
+ // set identified by |hash|. If no such certificate is known, an empty
+ // QuicStringPiece is returned.
+ virtual QuicStringPiece GetCert(uint64_t hash, uint32_t index) const = 0;
+
+ // MatchCert tries to find |cert| in one of the common certificate sets
+ // identified by |common_set_hashes|. On success it puts the hash of the
+ // set in |out_hash|, the index of |cert| in the set in |out_index| and
+ // returns true. Otherwise it returns false.
+ virtual bool MatchCert(QuicStringPiece cert,
+ QuicStringPiece common_set_hashes,
+ uint64_t* out_hash,
+ uint32_t* out_index) const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_COMMON_CERT_SET_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2.c b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2.c
new file mode 100644
index 00000000000..ec205fc7fb1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2.c
@@ -0,0 +1,122 @@
+/* This file contains common certificates. It's designed to be #included in
+ * another file, in a namespace. */
+
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_2a.inc"
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_2b.inc"
+
+static const size_t kNumCerts = 54;
+static const unsigned char* const kCerts[] = {
+ kDERCert0,
+ kDERCert1,
+ kDERCert2,
+ kDERCert3,
+ kDERCert4,
+ kDERCert5,
+ kDERCert6,
+ kDERCert7,
+ kDERCert8,
+ kDERCert9,
+ kDERCert10,
+ kDERCert11,
+ kDERCert12,
+ kDERCert13,
+ kDERCert14,
+ kDERCert15,
+ kDERCert16,
+ kDERCert17,
+ kDERCert18,
+ kDERCert19,
+ kDERCert20,
+ kDERCert21,
+ kDERCert22,
+ kDERCert23,
+ kDERCert24,
+ kDERCert25,
+ kDERCert26,
+ kDERCert27,
+ kDERCert28,
+ kDERCert29,
+ kDERCert30,
+ kDERCert31,
+ kDERCert32,
+ kDERCert33,
+ kDERCert34,
+ kDERCert35,
+ kDERCert36,
+ kDERCert37,
+ kDERCert38,
+ kDERCert39,
+ kDERCert40,
+ kDERCert41,
+ kDERCert42,
+ kDERCert43,
+ kDERCert44,
+ kDERCert45,
+ kDERCert46,
+ kDERCert47,
+ kDERCert48,
+ kDERCert49,
+ kDERCert50,
+ kDERCert51,
+ kDERCert52,
+ kDERCert53,
+};
+
+static const size_t kLens[] = {
+ 897,
+ 911,
+ 985,
+ 1012,
+ 1049,
+ 1062,
+ 1065,
+ 1071,
+ 1084,
+ 1096,
+ 1097,
+ 1105,
+ 1107,
+ 1117,
+ 1127,
+ 1133,
+ 1136,
+ 1138,
+ 1153,
+ 1171,
+ 1172,
+ 1176,
+ 1182,
+ 1188,
+ 1194,
+ 1203,
+ 1205,
+ 1206,
+ 1210,
+ 1222,
+ 1226,
+ 1236,
+ 1236,
+ 1236,
+ 1238,
+ 1256,
+ 1270,
+ 1280,
+ 1283,
+ 1284,
+ 1287,
+ 1315,
+ 1327,
+ 1340,
+ 1418,
+ 1447,
+ 1509,
+ 1520,
+ 1570,
+ 1581,
+ 1592,
+ 1628,
+ 1632,
+ 1770,
+};
+
+static const uint64_t kHash = UINT64_C(0xe81a92926081e801);
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2a.inc b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2a.inc
new file mode 100644
index 00000000000..75e648c9228
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2a.inc
@@ -0,0 +1,5622 @@
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1227750 (0x12bbe6)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+ Validity
+ Not Before: May 21 04:00:00 2002 GMT
+ Not After : Aug 21 04:00:00 2018 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df:
+ 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8:
+ 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29:
+ bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4:
+ 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3:
+ ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92:
+ 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d:
+ 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14:
+ 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd:
+ d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6:
+ d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5:
+ 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39:
+ 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05:
+ 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2:
+ fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32:
+ eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07:
+ 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b:
+ e4:f9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
+
+ X509v3 Subject Key Identifier:
+ C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/secureca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.geotrust.com/resources/repository
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 76:e1:12:6e:4e:4b:16:12:86:30:06:b2:81:08:cf:f0:08:c7:
+ c7:71:7e:66:ee:c2:ed:d4:3b:1f:ff:f0:f0:c8:4e:d6:43:38:
+ b0:b9:30:7d:18:d0:55:83:a2:6a:cb:36:11:9c:e8:48:66:a3:
+ 6d:7f:b8:13:d4:47:fe:8b:5a:5c:73:fc:ae:d9:1b:32:19:38:
+ ab:97:34:14:aa:96:d2:eb:a3:1c:14:08:49:b6:bb:e5:91:ef:
+ 83:36:eb:1d:56:6f:ca:da:bc:73:63:90:e4:7f:7b:3e:22:cb:
+ 3d:07:ed:5f:38:74:9c:e3:03:50:4e:a1:af:98:ee:61:f2:84:
+ 3f:12
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
+WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
+AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
+OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
+T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
+JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
+Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
+PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
+aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
+TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
+LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
+BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
+dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
+AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
+NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
+b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert0[] = {
+ 0x30, 0x82, 0x03, 0x7d, 0x30, 0x82, 0x02, 0xe6, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x12, 0xbb, 0xe6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x32, 0x30,
+ 0x35, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d,
+ 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x30, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x12, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0xcc, 0x18, 0x63, 0x30, 0xfd,
+ 0xf4, 0x17, 0x23, 0x1a, 0x56, 0x7e, 0x5b, 0xdf, 0x3c, 0x6c, 0x38, 0xe4,
+ 0x71, 0xb7, 0x78, 0x91, 0xd4, 0xbc, 0xa1, 0xd8, 0x4c, 0xf8, 0xa8, 0x43,
+ 0xb6, 0x03, 0xe9, 0x4d, 0x21, 0x07, 0x08, 0x88, 0xda, 0x58, 0x2f, 0x66,
+ 0x39, 0x29, 0xbd, 0x05, 0x78, 0x8b, 0x9d, 0x38, 0xe8, 0x05, 0xb7, 0x6a,
+ 0x7e, 0x71, 0xa4, 0xe6, 0xc4, 0x60, 0xa6, 0xb0, 0xef, 0x80, 0xe4, 0x89,
+ 0x28, 0x0f, 0x9e, 0x25, 0xd6, 0xed, 0x83, 0xf3, 0xad, 0xa6, 0x91, 0xc7,
+ 0x98, 0xc9, 0x42, 0x18, 0x35, 0x14, 0x9d, 0xad, 0x98, 0x46, 0x92, 0x2e,
+ 0x4f, 0xca, 0xf1, 0x87, 0x43, 0xc1, 0x16, 0x95, 0x57, 0x2d, 0x50, 0xef,
+ 0x89, 0x2d, 0x80, 0x7a, 0x57, 0xad, 0xf2, 0xee, 0x5f, 0x6b, 0xd2, 0x00,
+ 0x8d, 0xb9, 0x14, 0xf8, 0x14, 0x15, 0x35, 0xd9, 0xc0, 0x46, 0xa3, 0x7b,
+ 0x72, 0xc8, 0x91, 0xbf, 0xc9, 0x55, 0x2b, 0xcd, 0xd0, 0x97, 0x3e, 0x9c,
+ 0x26, 0x64, 0xcc, 0xdf, 0xce, 0x83, 0x19, 0x71, 0xca, 0x4e, 0xe6, 0xd4,
+ 0xd5, 0x7b, 0xa9, 0x19, 0xcd, 0x55, 0xde, 0xc8, 0xec, 0xd2, 0x5e, 0x38,
+ 0x53, 0xe5, 0x5c, 0x4f, 0x8c, 0x2d, 0xfe, 0x50, 0x23, 0x36, 0xfc, 0x66,
+ 0xe6, 0xcb, 0x8e, 0xa4, 0x39, 0x19, 0x00, 0xb7, 0x95, 0x02, 0x39, 0x91,
+ 0x0b, 0x0e, 0xfe, 0x38, 0x2e, 0xd1, 0x1d, 0x05, 0x9a, 0xf6, 0x4d, 0x3e,
+ 0x6f, 0x0f, 0x07, 0x1d, 0xaf, 0x2c, 0x1e, 0x8f, 0x60, 0x39, 0xe2, 0xfa,
+ 0x36, 0x53, 0x13, 0x39, 0xd4, 0x5e, 0x26, 0x2b, 0xdb, 0x3d, 0xa8, 0x14,
+ 0xbd, 0x32, 0xeb, 0x18, 0x03, 0x28, 0x52, 0x04, 0x71, 0xe5, 0xab, 0x33,
+ 0x3d, 0xe1, 0x38, 0xbb, 0x07, 0x36, 0x84, 0x62, 0x9c, 0x79, 0xea, 0x16,
+ 0x30, 0xf4, 0x5f, 0xc0, 0x2b, 0xe8, 0x71, 0x6b, 0xe4, 0xf9, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xf0, 0x30, 0x81, 0xed, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6,
+ 0x68, 0xf9, 0x2b, 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10,
+ 0x4f, 0x33, 0x98, 0x90, 0x9f, 0xd4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0,
+ 0x2d, 0xa0, 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4e,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81,
+ 0x00, 0x76, 0xe1, 0x12, 0x6e, 0x4e, 0x4b, 0x16, 0x12, 0x86, 0x30, 0x06,
+ 0xb2, 0x81, 0x08, 0xcf, 0xf0, 0x08, 0xc7, 0xc7, 0x71, 0x7e, 0x66, 0xee,
+ 0xc2, 0xed, 0xd4, 0x3b, 0x1f, 0xff, 0xf0, 0xf0, 0xc8, 0x4e, 0xd6, 0x43,
+ 0x38, 0xb0, 0xb9, 0x30, 0x7d, 0x18, 0xd0, 0x55, 0x83, 0xa2, 0x6a, 0xcb,
+ 0x36, 0x11, 0x9c, 0xe8, 0x48, 0x66, 0xa3, 0x6d, 0x7f, 0xb8, 0x13, 0xd4,
+ 0x47, 0xfe, 0x8b, 0x5a, 0x5c, 0x73, 0xfc, 0xae, 0xd9, 0x1b, 0x32, 0x19,
+ 0x38, 0xab, 0x97, 0x34, 0x14, 0xaa, 0x96, 0xd2, 0xeb, 0xa3, 0x1c, 0x14,
+ 0x08, 0x49, 0xb6, 0xbb, 0xe5, 0x91, 0xef, 0x83, 0x36, 0xeb, 0x1d, 0x56,
+ 0x6f, 0xca, 0xda, 0xbc, 0x73, 0x63, 0x90, 0xe4, 0x7f, 0x7b, 0x3e, 0x22,
+ 0xcb, 0x3d, 0x07, 0xed, 0x5f, 0x38, 0x74, 0x9c, 0xe3, 0x03, 0x50, 0x4e,
+ 0xa1, 0xaf, 0x98, 0xee, 0x61, 0xf2, 0x84, 0x3f, 0x12,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 880226 (0xd6e62)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+ Validity
+ Not Before: Nov 27 00:00:00 2006 GMT
+ Not After : Aug 21 16:15:00 2018 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:be:b8:15:7b:ff:d4:7c:7d:67:ad:83:64:7b:c8:
+ 42:53:2d:df:f6:84:08:20:61:d6:01:59:6a:9c:44:
+ 11:af:ef:76:fd:95:7e:ce:61:30:bb:7a:83:5f:02:
+ bd:01:66:ca:ee:15:8d:6f:a1:30:9c:bd:a1:85:9e:
+ 94:3a:f3:56:88:00:31:cf:d8:ee:6a:96:02:d9:ed:
+ 03:8c:fb:75:6d:e7:ea:b8:55:16:05:16:9a:f4:e0:
+ 5e:b1:88:c0:64:85:5c:15:4d:88:c7:b7:ba:e0:75:
+ e9:ad:05:3d:9d:c7:89:48:e0:bb:28:c8:03:e1:30:
+ 93:64:5e:52:c0:59:70:22:35:57:88:8a:f1:95:0a:
+ 83:d7:bc:31:73:01:34:ed:ef:46:71:e0:6b:02:a8:
+ 35:72:6b:97:9b:66:e0:cb:1c:79:5f:d8:1a:04:68:
+ 1e:47:02:e6:9d:60:e2:36:97:01:df:ce:35:92:df:
+ be:67:c7:6d:77:59:3b:8f:9d:d6:90:15:94:bc:42:
+ 34:10:c1:39:f9:b1:27:3e:7e:d6:8a:75:c5:b2:af:
+ 96:d3:a2:de:9b:e4:98:be:7d:e1:e9:81:ad:b6:6f:
+ fc:d7:0e:da:e0:34:b0:0d:1a:77:e7:e3:08:98:ef:
+ 58:fa:9c:84:b7:36:af:c2:df:ac:d2:f4:10:06:70:
+ 71:35
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92
+ X509v3 Authority Key Identifier:
+ keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/secureca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha1WithRSAEncryption
+ af:f3:0e:d6:72:ab:c7:a9:97:ca:2a:6b:84:39:de:79:a9:f0:
+ 81:e5:08:67:ab:d7:2f:20:02:01:71:0c:04:22:c9:1e:88:95:
+ 03:c9:49:3a:af:67:08:49:b0:d5:08:f5:20:3d:80:91:a0:c5:
+ 87:a3:fb:c9:a3:17:91:f9:a8:2f:ae:e9:0f:df:96:72:0f:75:
+ 17:80:5d:78:01:4d:9f:1f:6d:7b:d8:f5:42:38:23:1a:99:93:
+ f4:83:be:3b:35:74:e7:37:13:35:7a:ac:b4:b6:90:82:6c:27:
+ a4:e0:ec:9e:35:bd:bf:e5:29:a1:47:9f:5b:32:fc:e9:99:7d:
+ 2b:39
+-----BEGIN CERTIFICATE-----
+MIIDizCCAvSgAwIBAgIDDW5iMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMTI3MDAwMDAwWhcNMTgwODIxMTYxNTAw
+WjBYMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UE
+AxMoR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64FXv/1Hx9Z62DZHvIQlMt3/aE
+CCBh1gFZapxEEa/vdv2Vfs5hMLt6g18CvQFmyu4VjW+hMJy9oYWelDrzVogAMc/Y
+7mqWAtntA4z7dW3n6rhVFgUWmvTgXrGIwGSFXBVNiMe3uuB16a0FPZ3HiUjguyjI
+A+Ewk2ReUsBZcCI1V4iK8ZUKg9e8MXMBNO3vRnHgawKoNXJrl5tm4MsceV/YGgRo
+HkcC5p1g4jaXAd/ONZLfvmfHbXdZO4+d1pAVlLxCNBDBOfmxJz5+1op1xbKvltOi
+3pvkmL594emBrbZv/NcO2uA0sA0ad+fjCJjvWPqchLc2r8LfrNL0EAZwcTUCAwEA
+AaOB6DCB5TAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFCzVUEGXFYvwjzZhW0r7
+a9mZyTOSMB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMA8GA1UdEwEB
+/wQFMAMBAf8wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5j
+b20vY3Jscy9zZWN1cmVjYS5jcmwwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYB
+BQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwDQYJ
+KoZIhvcNAQEFBQADgYEAr/MO1nKrx6mXyiprhDneeanwgeUIZ6vXLyACAXEMBCLJ
+HoiVA8lJOq9nCEmw1Qj1ID2AkaDFh6P7yaMXkfmoL67pD9+Wcg91F4BdeAFNnx9t
+e9j1QjgjGpmT9IO+OzV05zcTNXqstLaQgmwnpODsnjW9v+UpoUefWzL86Zl9Kzk=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert1[] = {
+ 0x30, 0x82, 0x03, 0x8b, 0x30, 0x82, 0x02, 0xf4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x0d, 0x6e, 0x62, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31,
+ 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d,
+ 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x31, 0x36, 0x31, 0x35, 0x30, 0x30,
+ 0x5a, 0x30, 0x58, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x28, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xbe, 0xb8, 0x15, 0x7b, 0xff, 0xd4, 0x7c, 0x7d,
+ 0x67, 0xad, 0x83, 0x64, 0x7b, 0xc8, 0x42, 0x53, 0x2d, 0xdf, 0xf6, 0x84,
+ 0x08, 0x20, 0x61, 0xd6, 0x01, 0x59, 0x6a, 0x9c, 0x44, 0x11, 0xaf, 0xef,
+ 0x76, 0xfd, 0x95, 0x7e, 0xce, 0x61, 0x30, 0xbb, 0x7a, 0x83, 0x5f, 0x02,
+ 0xbd, 0x01, 0x66, 0xca, 0xee, 0x15, 0x8d, 0x6f, 0xa1, 0x30, 0x9c, 0xbd,
+ 0xa1, 0x85, 0x9e, 0x94, 0x3a, 0xf3, 0x56, 0x88, 0x00, 0x31, 0xcf, 0xd8,
+ 0xee, 0x6a, 0x96, 0x02, 0xd9, 0xed, 0x03, 0x8c, 0xfb, 0x75, 0x6d, 0xe7,
+ 0xea, 0xb8, 0x55, 0x16, 0x05, 0x16, 0x9a, 0xf4, 0xe0, 0x5e, 0xb1, 0x88,
+ 0xc0, 0x64, 0x85, 0x5c, 0x15, 0x4d, 0x88, 0xc7, 0xb7, 0xba, 0xe0, 0x75,
+ 0xe9, 0xad, 0x05, 0x3d, 0x9d, 0xc7, 0x89, 0x48, 0xe0, 0xbb, 0x28, 0xc8,
+ 0x03, 0xe1, 0x30, 0x93, 0x64, 0x5e, 0x52, 0xc0, 0x59, 0x70, 0x22, 0x35,
+ 0x57, 0x88, 0x8a, 0xf1, 0x95, 0x0a, 0x83, 0xd7, 0xbc, 0x31, 0x73, 0x01,
+ 0x34, 0xed, 0xef, 0x46, 0x71, 0xe0, 0x6b, 0x02, 0xa8, 0x35, 0x72, 0x6b,
+ 0x97, 0x9b, 0x66, 0xe0, 0xcb, 0x1c, 0x79, 0x5f, 0xd8, 0x1a, 0x04, 0x68,
+ 0x1e, 0x47, 0x02, 0xe6, 0x9d, 0x60, 0xe2, 0x36, 0x97, 0x01, 0xdf, 0xce,
+ 0x35, 0x92, 0xdf, 0xbe, 0x67, 0xc7, 0x6d, 0x77, 0x59, 0x3b, 0x8f, 0x9d,
+ 0xd6, 0x90, 0x15, 0x94, 0xbc, 0x42, 0x34, 0x10, 0xc1, 0x39, 0xf9, 0xb1,
+ 0x27, 0x3e, 0x7e, 0xd6, 0x8a, 0x75, 0xc5, 0xb2, 0xaf, 0x96, 0xd3, 0xa2,
+ 0xde, 0x9b, 0xe4, 0x98, 0xbe, 0x7d, 0xe1, 0xe9, 0x81, 0xad, 0xb6, 0x6f,
+ 0xfc, 0xd7, 0x0e, 0xda, 0xe0, 0x34, 0xb0, 0x0d, 0x1a, 0x77, 0xe7, 0xe3,
+ 0x08, 0x98, 0xef, 0x58, 0xfa, 0x9c, 0x84, 0xb7, 0x36, 0xaf, 0xc2, 0xdf,
+ 0xac, 0xd2, 0xf4, 0x10, 0x06, 0x70, 0x71, 0x35, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x81, 0xe8, 0x30, 0x81, 0xe5, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2c, 0xd5,
+ 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b, 0x4a, 0xfb,
+ 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b,
+ 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98,
+ 0x90, 0x9f, 0xd4, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3a, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0,
+ 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x81, 0x81, 0x00, 0xaf, 0xf3, 0x0e, 0xd6, 0x72, 0xab, 0xc7, 0xa9, 0x97,
+ 0xca, 0x2a, 0x6b, 0x84, 0x39, 0xde, 0x79, 0xa9, 0xf0, 0x81, 0xe5, 0x08,
+ 0x67, 0xab, 0xd7, 0x2f, 0x20, 0x02, 0x01, 0x71, 0x0c, 0x04, 0x22, 0xc9,
+ 0x1e, 0x88, 0x95, 0x03, 0xc9, 0x49, 0x3a, 0xaf, 0x67, 0x08, 0x49, 0xb0,
+ 0xd5, 0x08, 0xf5, 0x20, 0x3d, 0x80, 0x91, 0xa0, 0xc5, 0x87, 0xa3, 0xfb,
+ 0xc9, 0xa3, 0x17, 0x91, 0xf9, 0xa8, 0x2f, 0xae, 0xe9, 0x0f, 0xdf, 0x96,
+ 0x72, 0x0f, 0x75, 0x17, 0x80, 0x5d, 0x78, 0x01, 0x4d, 0x9f, 0x1f, 0x6d,
+ 0x7b, 0xd8, 0xf5, 0x42, 0x38, 0x23, 0x1a, 0x99, 0x93, 0xf4, 0x83, 0xbe,
+ 0x3b, 0x35, 0x74, 0xe7, 0x37, 0x13, 0x35, 0x7a, 0xac, 0xb4, 0xb6, 0x90,
+ 0x82, 0x6c, 0x27, 0xa4, 0xe0, 0xec, 0x9e, 0x35, 0xbd, 0xbf, 0xe5, 0x29,
+ 0xa1, 0x47, 0x9f, 0x5b, 0x32, 0xfc, 0xe9, 0x99, 0x7d, 0x2b, 0x39,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 145105 (0x236d1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Feb 19 22:45:05 2010 GMT
+ Not After : Feb 18 22:45:05 2020 GMT
+ Subject: C=US, O=GeoTrust, Inc., CN=RapidSSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c7:71:f8:56:c7:1e:d9:cc:b5:ad:f6:b4:97:a3:
+ fb:a1:e6:0b:50:5f:50:aa:3a:da:0f:fc:3d:29:24:
+ 43:c6:10:29:c1:fc:55:40:72:ee:bd:ea:df:9f:b6:
+ 41:f4:48:4b:c8:6e:fe:4f:57:12:8b:5b:fa:92:dd:
+ 5e:e8:ad:f3:f0:1b:b1:7b:4d:fb:cf:fd:d1:e5:f8:
+ e3:dc:e7:f5:73:7f:df:01:49:cf:8c:56:c1:bd:37:
+ e3:5b:be:b5:4f:8b:8b:f0:da:4f:c7:e3:dd:55:47:
+ 69:df:f2:5b:7b:07:4f:3d:e5:ac:21:c1:c8:1d:7a:
+ e8:e7:f6:0f:a1:aa:f5:6f:de:a8:65:4f:10:89:9c:
+ 03:f3:89:7a:a5:5e:01:72:33:ed:a9:e9:5a:1e:79:
+ f3:87:c8:df:c8:c5:fc:37:c8:9a:9a:d7:b8:76:cc:
+ b0:3e:e7:fd:e6:54:ea:df:5f:52:41:78:59:57:ad:
+ f1:12:d6:7f:bc:d5:9f:70:d3:05:6c:fa:a3:7d:67:
+ 58:dd:26:62:1d:31:92:0c:79:79:1c:8e:cf:ca:7b:
+ c1:66:af:a8:74:48:fb:8e:82:c2:9e:2c:99:5c:7b:
+ 2d:5d:9b:bc:5b:57:9e:7c:3a:7a:13:ad:f2:a3:18:
+ 5b:2b:59:0f:cd:5c:3a:eb:68:33:c6:28:1d:82:d1:
+ 50:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.geotrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ ab:bc:bc:0a:5d:18:94:e3:c1:b1:c3:a8:4c:55:d6:be:b4:98:
+ f1:ee:3c:1c:cd:cf:f3:24:24:5c:96:03:27:58:fc:36:ae:a2:
+ 2f:8f:f1:fe:da:2b:02:c3:33:bd:c8:dd:48:22:2b:60:0f:a5:
+ 03:10:fd:77:f8:d0:ed:96:67:4f:fd:ea:47:20:70:54:dc:a9:
+ 0c:55:7e:e1:96:25:8a:d9:b5:da:57:4a:be:8d:8e:49:43:63:
+ a5:6c:4e:27:87:25:eb:5b:6d:fe:a2:7f:38:28:e0:36:ab:ad:
+ 39:a5:a5:62:c4:b7:5c:58:2c:aa:5d:01:60:a6:62:67:a3:c0:
+ c7:62:23:f4:e7:6c:46:ee:b5:d3:80:6a:22:13:d2:2d:3f:74:
+ 4f:ea:af:8c:5f:b4:38:9c:db:ae:ce:af:84:1e:a6:f6:34:51:
+ 59:79:d3:e3:75:dc:bc:d7:f3:73:df:92:ec:d2:20:59:6f:9c:
+ fb:95:f8:92:76:18:0a:7c:0f:2c:a6:ca:de:8a:62:7b:d8:f3:
+ ce:5f:68:bd:8f:3e:c1:74:bb:15:72:3a:16:83:a9:0b:e6:4d:
+ 99:9c:d8:57:ec:a8:01:51:c7:6f:57:34:5e:ab:4a:2c:42:f6:
+ 4f:1c:89:78:de:26:4e:f5:6f:93:4c:15:6b:27:56:4d:00:54:
+ 6c:7a:b7:b7
+-----BEGIN CERTIFICATE-----
+MIID1TCCAr2gAwIBAgIDAjbRMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTAwMjE5MjI0NTA1WhcNMjAwMjE4MjI0NTA1WjA8MQswCQYDVQQG
+EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xFDASBgNVBAMTC1JhcGlkU1NM
+IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3H4Vsce2cy1rfa0
+l6P7oeYLUF9QqjraD/w9KSRDxhApwfxVQHLuverfn7ZB9EhLyG7+T1cSi1v6kt1e
+6K3z8Buxe037z/3R5fjj3Of1c3/fAUnPjFbBvTfjW761T4uL8NpPx+PdVUdp3/Jb
+ewdPPeWsIcHIHXro5/YPoar1b96oZU8QiZwD84l6pV4BcjPtqelaHnnzh8jfyMX8
+N8iamte4dsywPuf95lTq319SQXhZV63xEtZ/vNWfcNMFbPqjfWdY3SZiHTGSDHl5
+HI7PynvBZq+odEj7joLCniyZXHstXZu8W1eefDp6E63yoxhbK1kPzVw662gzxigd
+gtFQiwIDAQABo4HZMIHWMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUa2k9ahhC
+St2PAmU5/TUkhniRFjAwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4w
+EgYDVR0TAQH/BAgwBgEB/wIBADA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3Js
+Lmdlb3RydXN0LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDA0BggrBgEFBQcBAQQoMCYw
+JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdlb3RydXN0LmNvbTANBgkqhkiG9w0B
+AQUFAAOCAQEAq7y8Cl0YlOPBscOoTFXWvrSY8e48HM3P8yQkXJYDJ1j8Nq6iL4/x
+/torAsMzvcjdSCIrYA+lAxD9d/jQ7ZZnT/3qRyBwVNypDFV+4ZYlitm12ldKvo2O
+SUNjpWxOJ4cl61tt/qJ/OCjgNqutOaWlYsS3XFgsql0BYKZiZ6PAx2Ij9OdsRu61
+04BqIhPSLT90T+qvjF+0OJzbrs6vhB6m9jRRWXnT43XcvNfzc9+S7NIgWW+c+5X4
+knYYCnwPLKbK3opie9jzzl9ovY8+wXS7FXI6FoOpC+ZNmZzYV+yoAVHHb1c0XqtK
+LEL2TxyJeN4mTvVvk0wVaydWTQBUbHq3tw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert2[] = {
+ 0x30, 0x82, 0x03, 0xd5, 0x30, 0x82, 0x02, 0xbd, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x36, 0xd1, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30,
+ 0x32, 0x31, 0x39, 0x32, 0x32, 0x34, 0x35, 0x30, 0x35, 0x5a, 0x17, 0x0d,
+ 0x32, 0x30, 0x30, 0x32, 0x31, 0x38, 0x32, 0x32, 0x34, 0x35, 0x30, 0x35,
+ 0x5a, 0x30, 0x3c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0e, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x0b, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c,
+ 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xc7, 0x71, 0xf8, 0x56, 0xc7, 0x1e, 0xd9, 0xcc, 0xb5, 0xad, 0xf6, 0xb4,
+ 0x97, 0xa3, 0xfb, 0xa1, 0xe6, 0x0b, 0x50, 0x5f, 0x50, 0xaa, 0x3a, 0xda,
+ 0x0f, 0xfc, 0x3d, 0x29, 0x24, 0x43, 0xc6, 0x10, 0x29, 0xc1, 0xfc, 0x55,
+ 0x40, 0x72, 0xee, 0xbd, 0xea, 0xdf, 0x9f, 0xb6, 0x41, 0xf4, 0x48, 0x4b,
+ 0xc8, 0x6e, 0xfe, 0x4f, 0x57, 0x12, 0x8b, 0x5b, 0xfa, 0x92, 0xdd, 0x5e,
+ 0xe8, 0xad, 0xf3, 0xf0, 0x1b, 0xb1, 0x7b, 0x4d, 0xfb, 0xcf, 0xfd, 0xd1,
+ 0xe5, 0xf8, 0xe3, 0xdc, 0xe7, 0xf5, 0x73, 0x7f, 0xdf, 0x01, 0x49, 0xcf,
+ 0x8c, 0x56, 0xc1, 0xbd, 0x37, 0xe3, 0x5b, 0xbe, 0xb5, 0x4f, 0x8b, 0x8b,
+ 0xf0, 0xda, 0x4f, 0xc7, 0xe3, 0xdd, 0x55, 0x47, 0x69, 0xdf, 0xf2, 0x5b,
+ 0x7b, 0x07, 0x4f, 0x3d, 0xe5, 0xac, 0x21, 0xc1, 0xc8, 0x1d, 0x7a, 0xe8,
+ 0xe7, 0xf6, 0x0f, 0xa1, 0xaa, 0xf5, 0x6f, 0xde, 0xa8, 0x65, 0x4f, 0x10,
+ 0x89, 0x9c, 0x03, 0xf3, 0x89, 0x7a, 0xa5, 0x5e, 0x01, 0x72, 0x33, 0xed,
+ 0xa9, 0xe9, 0x5a, 0x1e, 0x79, 0xf3, 0x87, 0xc8, 0xdf, 0xc8, 0xc5, 0xfc,
+ 0x37, 0xc8, 0x9a, 0x9a, 0xd7, 0xb8, 0x76, 0xcc, 0xb0, 0x3e, 0xe7, 0xfd,
+ 0xe6, 0x54, 0xea, 0xdf, 0x5f, 0x52, 0x41, 0x78, 0x59, 0x57, 0xad, 0xf1,
+ 0x12, 0xd6, 0x7f, 0xbc, 0xd5, 0x9f, 0x70, 0xd3, 0x05, 0x6c, 0xfa, 0xa3,
+ 0x7d, 0x67, 0x58, 0xdd, 0x26, 0x62, 0x1d, 0x31, 0x92, 0x0c, 0x79, 0x79,
+ 0x1c, 0x8e, 0xcf, 0xca, 0x7b, 0xc1, 0x66, 0xaf, 0xa8, 0x74, 0x48, 0xfb,
+ 0x8e, 0x82, 0xc2, 0x9e, 0x2c, 0x99, 0x5c, 0x7b, 0x2d, 0x5d, 0x9b, 0xbc,
+ 0x5b, 0x57, 0x9e, 0x7c, 0x3a, 0x7a, 0x13, 0xad, 0xf2, 0xa3, 0x18, 0x5b,
+ 0x2b, 0x59, 0x0f, 0xcd, 0x5c, 0x3a, 0xeb, 0x68, 0x33, 0xc6, 0x28, 0x1d,
+ 0x82, 0xd1, 0x50, 0x8b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xd9,
+ 0x30, 0x81, 0xd6, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6b, 0x69, 0x3d, 0x6a, 0x18, 0x42,
+ 0x4a, 0xdd, 0x8f, 0x02, 0x65, 0x39, 0xfd, 0x35, 0x24, 0x86, 0x78, 0x91,
+ 0x16, 0x30, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05,
+ 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3a, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b,
+ 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30,
+ 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86,
+ 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70,
+ 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xab, 0xbc, 0xbc,
+ 0x0a, 0x5d, 0x18, 0x94, 0xe3, 0xc1, 0xb1, 0xc3, 0xa8, 0x4c, 0x55, 0xd6,
+ 0xbe, 0xb4, 0x98, 0xf1, 0xee, 0x3c, 0x1c, 0xcd, 0xcf, 0xf3, 0x24, 0x24,
+ 0x5c, 0x96, 0x03, 0x27, 0x58, 0xfc, 0x36, 0xae, 0xa2, 0x2f, 0x8f, 0xf1,
+ 0xfe, 0xda, 0x2b, 0x02, 0xc3, 0x33, 0xbd, 0xc8, 0xdd, 0x48, 0x22, 0x2b,
+ 0x60, 0x0f, 0xa5, 0x03, 0x10, 0xfd, 0x77, 0xf8, 0xd0, 0xed, 0x96, 0x67,
+ 0x4f, 0xfd, 0xea, 0x47, 0x20, 0x70, 0x54, 0xdc, 0xa9, 0x0c, 0x55, 0x7e,
+ 0xe1, 0x96, 0x25, 0x8a, 0xd9, 0xb5, 0xda, 0x57, 0x4a, 0xbe, 0x8d, 0x8e,
+ 0x49, 0x43, 0x63, 0xa5, 0x6c, 0x4e, 0x27, 0x87, 0x25, 0xeb, 0x5b, 0x6d,
+ 0xfe, 0xa2, 0x7f, 0x38, 0x28, 0xe0, 0x36, 0xab, 0xad, 0x39, 0xa5, 0xa5,
+ 0x62, 0xc4, 0xb7, 0x5c, 0x58, 0x2c, 0xaa, 0x5d, 0x01, 0x60, 0xa6, 0x62,
+ 0x67, 0xa3, 0xc0, 0xc7, 0x62, 0x23, 0xf4, 0xe7, 0x6c, 0x46, 0xee, 0xb5,
+ 0xd3, 0x80, 0x6a, 0x22, 0x13, 0xd2, 0x2d, 0x3f, 0x74, 0x4f, 0xea, 0xaf,
+ 0x8c, 0x5f, 0xb4, 0x38, 0x9c, 0xdb, 0xae, 0xce, 0xaf, 0x84, 0x1e, 0xa6,
+ 0xf6, 0x34, 0x51, 0x59, 0x79, 0xd3, 0xe3, 0x75, 0xdc, 0xbc, 0xd7, 0xf3,
+ 0x73, 0xdf, 0x92, 0xec, 0xd2, 0x20, 0x59, 0x6f, 0x9c, 0xfb, 0x95, 0xf8,
+ 0x92, 0x76, 0x18, 0x0a, 0x7c, 0x0f, 0x2c, 0xa6, 0xca, 0xde, 0x8a, 0x62,
+ 0x7b, 0xd8, 0xf3, 0xce, 0x5f, 0x68, 0xbd, 0x8f, 0x3e, 0xc1, 0x74, 0xbb,
+ 0x15, 0x72, 0x3a, 0x16, 0x83, 0xa9, 0x0b, 0xe6, 0x4d, 0x99, 0x9c, 0xd8,
+ 0x57, 0xec, 0xa8, 0x01, 0x51, 0xc7, 0x6f, 0x57, 0x34, 0x5e, 0xab, 0x4a,
+ 0x2c, 0x42, 0xf6, 0x4f, 0x1c, 0x89, 0x78, 0xde, 0x26, 0x4e, 0xf5, 0x6f,
+ 0x93, 0x4c, 0x15, 0x6b, 0x27, 0x56, 0x4d, 0x00, 0x54, 0x6c, 0x7a, 0xb7,
+ 0xb7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146051 (0x23a83)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Apr 5 15:15:56 2013 GMT
+ Not After : Dec 31 23:59:59 2016 GMT
+ Subject: C=US, O=Google Inc, CN=Google Internet Authority G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:9c:2a:04:77:5c:d8:50:91:3a:06:a3:82:e0:d8:
+ 50:48:bc:89:3f:f1:19:70:1a:88:46:7e:e0:8f:c5:
+ f1:89:ce:21:ee:5a:fe:61:0d:b7:32:44:89:a0:74:
+ 0b:53:4f:55:a4:ce:82:62:95:ee:eb:59:5f:c6:e1:
+ 05:80:12:c4:5e:94:3f:bc:5b:48:38:f4:53:f7:24:
+ e6:fb:91:e9:15:c4:cf:f4:53:0d:f4:4a:fc:9f:54:
+ de:7d:be:a0:6b:6f:87:c0:d0:50:1f:28:30:03:40:
+ da:08:73:51:6c:7f:ff:3a:3c:a7:37:06:8e:bd:4b:
+ 11:04:eb:7d:24:de:e6:f9:fc:31:71:fb:94:d5:60:
+ f3:2e:4a:af:42:d2:cb:ea:c4:6a:1a:b2:cc:53:dd:
+ 15:4b:8b:1f:c8:19:61:1f:cd:9d:a8:3e:63:2b:84:
+ 35:69:65:84:c8:19:c5:46:22:f8:53:95:be:e3:80:
+ 4a:10:c6:2a:ec:ba:97:20:11:c7:39:99:10:04:a0:
+ f0:61:7a:95:25:8c:4e:52:75:e2:b6:ed:08:ca:14:
+ fc:ce:22:6a:b3:4e:cf:46:03:97:97:03:7e:c0:b1:
+ de:7b:af:45:33:cf:ba:3e:71:b7:de:f4:25:25:c2:
+ 0d:35:89:9d:9d:fb:0e:11:79:89:1e:37:c5:af:8e:
+ 72:69
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ 4A:DD:06:16:1B:BC:F6:68:B5:76:F5:81:B6:BB:62:1A:BA:5A:81:2F
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/crls/gtglobal.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.11129.2.5.1
+
+ Signature Algorithm: sha256WithRSAEncryption
+ aa:fa:a9:20:cd:6a:67:83:ed:5e:d4:7e:de:1d:c4:7f:e0:25:
+ 06:00:c5:24:fb:a9:c8:2d:6d:7e:de:9d:82:65:2c:81:63:34:
+ 66:3e:e9:52:c2:08:b4:cb:2f:f7:5f:99:3a:6a:9c:50:7a:85:
+ 05:8c:7d:d1:2a:48:84:d3:09:6c:7c:c2:cd:35:9f:f3:82:ee:
+ 52:de:68:5f:e4:00:8a:17:20:96:f7:29:8d:9a:4d:cb:a8:de:
+ 86:c8:0d:6f:56:87:03:7d:03:3f:dc:fa:79:7d:21:19:f9:c8:
+ 3a:2f:51:76:8c:c7:41:92:71:8f:25:ce:37:f8:4a:4c:00:23:
+ ef:c4:35:10:ae:e0:23:80:73:7c:4d:34:2e:c8:6e:90:d6:10:
+ 1e:99:84:73:1a:70:f2:ed:55:0e:ee:17:06:ea:67:ee:32:eb:
+ 2c:dd:67:07:3f:f6:8b:c2:70:de:5b:00:e6:bb:1b:d3:36:1a:
+ 22:6c:6c:b0:35:42:6c:90:09:3d:93:e9:64:09:22:0e:85:06:
+ 9f:c2:73:21:d3:e6:5f:80:e4:8d:85:22:3a:73:03:b1:60:8e:
+ ae:68:e2:f4:3e:97:e7:60:12:09:68:36:de:3a:d6:e2:43:95:
+ 5b:37:81:92:81:1f:bb:8d:d7:ad:52:64:16:57:96:d9:5e:34:
+ 7e:c8:35:d8
+-----BEGIN CERTIFICATE-----
+MIID8DCCAtigAwIBAgIDAjqDMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTMwNDA1MTUxNTU2WhcNMTYxMjMxMjM1OTU5WjBJMQswCQYDVQQG
+EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
+bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
+VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
+h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
+ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
+EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
+DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7
+qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYD
+VR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDov
+L2cuc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCig
+JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMBcGA1UdIAQQ
+MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQsFAAOCAQEAqvqpIM1qZ4PtXtR+
+3h3Ef+AlBgDFJPupyC1tft6dgmUsgWM0Zj7pUsIItMsv91+ZOmqcUHqFBYx90SpI
+hNMJbHzCzTWf84LuUt5oX+QAihcglvcpjZpNy6jehsgNb1aHA30DP9z6eX0hGfnI
+Oi9RdozHQZJxjyXON/hKTAAj78Q1EK7gI4BzfE00LshukNYQHpmEcxpw8u1VDu4X
+Bupn7jLrLN1nBz/2i8Jw3lsA5rsb0zYaImxssDVCbJAJPZPpZAkiDoUGn8JzIdPm
+X4DkjYUiOnMDsWCOrmji9D6X52ASCWg23jrW4kOVWzeBkoEfu43XrVJkFleW2V40
+fsg12A==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert3[] = {
+ 0x30, 0x82, 0x03, 0xf0, 0x30, 0x82, 0x02, 0xd8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x83, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30,
+ 0x34, 0x30, 0x35, 0x31, 0x35, 0x31, 0x35, 0x35, 0x36, 0x5a, 0x17, 0x0d,
+ 0x31, 0x36, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39,
+ 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e,
+ 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c,
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3,
+ 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a,
+ 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a,
+ 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f,
+ 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1,
+ 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4,
+ 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53,
+ 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f,
+ 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73,
+ 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b,
+ 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb,
+ 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4,
+ 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19,
+ 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65,
+ 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80,
+ 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99,
+ 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75,
+ 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e,
+ 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf,
+ 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2,
+ 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37,
+ 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81,
+ 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x35, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0,
+ 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e,
+ 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72,
+ 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10,
+ 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
+ 0x79, 0x02, 0x05, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0xaa, 0xfa, 0xa9, 0x20, 0xcd, 0x6a, 0x67, 0x83, 0xed, 0x5e, 0xd4, 0x7e,
+ 0xde, 0x1d, 0xc4, 0x7f, 0xe0, 0x25, 0x06, 0x00, 0xc5, 0x24, 0xfb, 0xa9,
+ 0xc8, 0x2d, 0x6d, 0x7e, 0xde, 0x9d, 0x82, 0x65, 0x2c, 0x81, 0x63, 0x34,
+ 0x66, 0x3e, 0xe9, 0x52, 0xc2, 0x08, 0xb4, 0xcb, 0x2f, 0xf7, 0x5f, 0x99,
+ 0x3a, 0x6a, 0x9c, 0x50, 0x7a, 0x85, 0x05, 0x8c, 0x7d, 0xd1, 0x2a, 0x48,
+ 0x84, 0xd3, 0x09, 0x6c, 0x7c, 0xc2, 0xcd, 0x35, 0x9f, 0xf3, 0x82, 0xee,
+ 0x52, 0xde, 0x68, 0x5f, 0xe4, 0x00, 0x8a, 0x17, 0x20, 0x96, 0xf7, 0x29,
+ 0x8d, 0x9a, 0x4d, 0xcb, 0xa8, 0xde, 0x86, 0xc8, 0x0d, 0x6f, 0x56, 0x87,
+ 0x03, 0x7d, 0x03, 0x3f, 0xdc, 0xfa, 0x79, 0x7d, 0x21, 0x19, 0xf9, 0xc8,
+ 0x3a, 0x2f, 0x51, 0x76, 0x8c, 0xc7, 0x41, 0x92, 0x71, 0x8f, 0x25, 0xce,
+ 0x37, 0xf8, 0x4a, 0x4c, 0x00, 0x23, 0xef, 0xc4, 0x35, 0x10, 0xae, 0xe0,
+ 0x23, 0x80, 0x73, 0x7c, 0x4d, 0x34, 0x2e, 0xc8, 0x6e, 0x90, 0xd6, 0x10,
+ 0x1e, 0x99, 0x84, 0x73, 0x1a, 0x70, 0xf2, 0xed, 0x55, 0x0e, 0xee, 0x17,
+ 0x06, 0xea, 0x67, 0xee, 0x32, 0xeb, 0x2c, 0xdd, 0x67, 0x07, 0x3f, 0xf6,
+ 0x8b, 0xc2, 0x70, 0xde, 0x5b, 0x00, 0xe6, 0xbb, 0x1b, 0xd3, 0x36, 0x1a,
+ 0x22, 0x6c, 0x6c, 0xb0, 0x35, 0x42, 0x6c, 0x90, 0x09, 0x3d, 0x93, 0xe9,
+ 0x64, 0x09, 0x22, 0x0e, 0x85, 0x06, 0x9f, 0xc2, 0x73, 0x21, 0xd3, 0xe6,
+ 0x5f, 0x80, 0xe4, 0x8d, 0x85, 0x22, 0x3a, 0x73, 0x03, 0xb1, 0x60, 0x8e,
+ 0xae, 0x68, 0xe2, 0xf4, 0x3e, 0x97, 0xe7, 0x60, 0x12, 0x09, 0x68, 0x36,
+ 0xde, 0x3a, 0xd6, 0xe2, 0x43, 0x95, 0x5b, 0x37, 0x81, 0x92, 0x81, 0x1f,
+ 0xbb, 0x8d, 0xd7, 0xad, 0x52, 0x64, 0x16, 0x57, 0x96, 0xd9, 0x5e, 0x34,
+ 0x7e, 0xc8, 0x35, 0xd8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120033005 (0x7278eed)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Apr 18 16:36:18 2012 GMT
+ Not After : Aug 13 16:35:17 2018 GMT
+ Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79:
+ d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a:
+ 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2:
+ 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01:
+ 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7:
+ 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6:
+ 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c:
+ a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70:
+ 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77:
+ d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae:
+ 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18:
+ 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85:
+ ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9:
+ 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5:
+ c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a:
+ ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0:
+ 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27:
+ 1a:39
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:3
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://cybertrust.omniroot.com/repository
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 93:1d:fe:8b:ae:46:ec:cb:a9:0f:ab:e5:ef:ca:b2:68:16:68:
+ d8:8f:fa:13:a9:af:b3:cb:2d:e7:4b:6e:8e:69:2a:c2:2b:10:
+ 0a:8d:f6:ae:73:b6:b9:fb:14:fd:5f:6d:b8:50:b6:c4:8a:d6:
+ 40:7e:d7:c3:cb:73:dc:c9:5d:5b:af:b0:41:b5:37:eb:ea:dc:
+ 20:91:c4:34:6a:f4:a1:f3:96:9d:37:86:97:e1:71:a4:dd:7d:
+ fa:44:84:94:ae:d7:09:04:22:76:0f:64:51:35:a9:24:0f:f9:
+ 0b:db:32:da:c2:fe:c1:b9:2a:5c:7a:27:13:ca:b1:48:3a:71:
+ d0:43
+-----BEGIN CERTIFICATE-----
+MIIEFTCCA36gAwIBAgIEByeO7TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTEyMDQxODE2MzYxOFoXDTE4MDgxMzE2MzUxN1owWjELMAkG
+A1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVz
+dDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uO
+KymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnn
+c+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP
+wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPg
+kAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFc
+B5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaOCAUcw
+ggFDMBIGA1UdEwEB/wQIMAYBAf8CAQMwSgYDVR0gBEMwQTA/BgRVHSAAMDcwNQYI
+KwYBBQUHAgEWKWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9yZXBvc2l0
+b3J5MA4GA1UdDwEB/wQEAwIBBjCBiQYDVR0jBIGBMH+heaR3MHUxCzAJBgNVBAYT
+AlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJl
+clRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg
+R2xvYmFsIFJvb3SCAgGlMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVi
+bGljLXRydXN0LmNvbS9jZ2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwDQYJKoZIhvcN
+AQEFBQADgYEAkx3+i65G7MupD6vl78qyaBZo2I/6E6mvs8st50tujmkqwisQCo32
+rnO2ufsU/V9tuFC2xIrWQH7Xw8tz3MldW6+wQbU36+rcIJHENGr0ofOWnTeGl+Fx
+pN19+kSElK7XCQQidg9kUTWpJA/5C9sy2sL+wbkqXHonE8qxSDpx0EM=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert4[] = {
+ 0x30, 0x82, 0x04, 0x15, 0x30, 0x82, 0x03, 0x7e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x8e, 0xed, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x32, 0x30, 0x34, 0x31, 0x38, 0x31, 0x36, 0x33, 0x36, 0x31,
+ 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x33, 0x31, 0x36,
+ 0x33, 0x35, 0x31, 0x37, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69,
+ 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79,
+ 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f,
+ 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+ 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x04,
+ 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a, 0xb5, 0x79,
+ 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3, 0x5b, 0x8e,
+ 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09, 0x05, 0x6d,
+ 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88, 0xda, 0x12,
+ 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52, 0x7b, 0x88,
+ 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a, 0x09, 0xe7,
+ 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d, 0x2d, 0xe5,
+ 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea, 0xf5, 0xab,
+ 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f, 0x0c, 0xd5,
+ 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70, 0xf0, 0x8f,
+ 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33, 0x7a, 0x77,
+ 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13, 0xd2, 0xc0,
+ 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc, 0xb4, 0xdd,
+ 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5, 0x63, 0xe0,
+ 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea, 0xeb, 0xd4,
+ 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69, 0xbc, 0xf9,
+ 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9, 0x90, 0x2c,
+ 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98, 0x21, 0x5c,
+ 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86, 0x3a, 0x6b,
+ 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78, 0x8d, 0x76,
+ 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90, 0xdc, 0x27,
+ 0x1a, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x47, 0x30,
+ 0x82, 0x01, 0x43, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x03, 0x30,
+ 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x43, 0x30, 0x41, 0x30, 0x3f,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x37, 0x30, 0x35, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x29, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x81, 0x89, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77,
+ 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23,
+ 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45,
+ 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82,
+ 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3e,
+ 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86, 0x34, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e, 0x2f, 0x43, 0x52,
+ 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64, 0x70, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x93, 0x1d, 0xfe,
+ 0x8b, 0xae, 0x46, 0xec, 0xcb, 0xa9, 0x0f, 0xab, 0xe5, 0xef, 0xca, 0xb2,
+ 0x68, 0x16, 0x68, 0xd8, 0x8f, 0xfa, 0x13, 0xa9, 0xaf, 0xb3, 0xcb, 0x2d,
+ 0xe7, 0x4b, 0x6e, 0x8e, 0x69, 0x2a, 0xc2, 0x2b, 0x10, 0x0a, 0x8d, 0xf6,
+ 0xae, 0x73, 0xb6, 0xb9, 0xfb, 0x14, 0xfd, 0x5f, 0x6d, 0xb8, 0x50, 0xb6,
+ 0xc4, 0x8a, 0xd6, 0x40, 0x7e, 0xd7, 0xc3, 0xcb, 0x73, 0xdc, 0xc9, 0x5d,
+ 0x5b, 0xaf, 0xb0, 0x41, 0xb5, 0x37, 0xeb, 0xea, 0xdc, 0x20, 0x91, 0xc4,
+ 0x34, 0x6a, 0xf4, 0xa1, 0xf3, 0x96, 0x9d, 0x37, 0x86, 0x97, 0xe1, 0x71,
+ 0xa4, 0xdd, 0x7d, 0xfa, 0x44, 0x84, 0x94, 0xae, 0xd7, 0x09, 0x04, 0x22,
+ 0x76, 0x0f, 0x64, 0x51, 0x35, 0xa9, 0x24, 0x0f, 0xf9, 0x0b, 0xdb, 0x32,
+ 0xda, 0xc2, 0xfe, 0xc1, 0xb9, 0x2a, 0x5c, 0x7a, 0x27, 0x13, 0xca, 0xb1,
+ 0x48, 0x3a, 0x71, 0xd0, 0x43,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146041 (0x23a79)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Sep 8 20:41:10 2014 GMT
+ Not After : May 20 20:41:10 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:9a:7d:98:68:11:40:c1:5f:72:ec:55:b3:b1:63:
+ f3:32:22:72:91:c6:16:05:bb:08:82:31:b4:f6:ee:
+ d4:18:39:11:2f:2e:da:47:fe:51:31:6e:5b:f2:a9:
+ 0a:eb:2f:bb:f5:61:59:65:57:02:cd:80:ff:c7:70:
+ 32:54:89:fd:db:ae:99:72:d4:4f:0c:26:b9:2e:63:
+ 30:7d:de:14:5b:6a:d7:52:78:21:f9:bf:bc:50:d5:
+ 54:12:59:d8:b5:36:d9:21:47:b8:3f:6a:58:1d:8c:
+ 72:e1:97:95:d3:e1:45:a8:f1:5a:e5:be:fe:e3:53:
+ 7c:a5:f0:52:e0:cf:39:94:0c:19:71:f2:c0:25:07:
+ 48:7d:1c:e6:f1:39:25:2f:98:79:43:e8:18:72:f4:
+ 65:86:98:5a:00:04:47:da:4b:58:1e:7c:86:b1:4b:
+ 35:a6:20:00:1c:cd:1b:3b:22:5d:d1:93:28:33:12:
+ 23:94:08:aa:c3:3a:f5:d1:c6:8c:7e:99:d3:18:a0:
+ ad:9d:18:cf:49:ad:10:03:f7:99:33:26:86:46:9a:
+ 2f:a0:ba:6c:6e:c8:88:02:b7:6e:fa:7a:9e:98:4a:
+ ee:9a:31:7d:19:14:60:0c:ec:8f:20:23:3c:da:97:
+ 26:b6:ea:80:6c:8a:57:9e:20:ee:6f:17:25:4a:32:
+ ad:35
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ AC:32:ED:5A:C9:E0:DE:30:9C:90:58:55:26:63:F6:72:A6:54:5F:E3
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 61:40:ad:21:0f:03:bb:95:dc:89:fc:a3:cb:05:71:e9:1c:59:
+ 97:35:c2:fa:6b:05:a4:16:c6:56:46:37:74:1b:1b:f1:3e:2c:
+ e8:37:19:b7:94:d2:0f:0e:c5:bf:14:07:2b:34:cd:5b:b4:8d:
+ c7:56:9d:19:fc:02:b4:9e:90:31:fa:a4:44:c6:75:dd:dd:1f:
+ 25:54:a3:30:4c:ac:db:fe:c4:88:f7:31:26:18:47:ae:4c:20:
+ 19:1a:c7:ae:3e:98:0a:16:3d:d2:c2:a6:5d:0d:2e:29:7d:b2:
+ 9d:c7:41:32:17:ca:9d:ae:39:bf:91:98:de:e7:44:e2:95:9c:
+ 94:5c:6c:42:1b:59:c9:7b:68:13:a8:96:09:74:ee:40:14:a4:
+ d5:d7:c9:7b:33:a3:0f:5a:69:9c:1a:fa:6f:12:47:1c:df:1e:
+ 4c:70:4e:6d:dd:fe:1c:87:b5:9d:e1:54:07:09:8a:cd:be:aa:
+ a8:46:78:6e:16:f2:e7:91:0e:c3:af:da:76:00:d1:d8:a2:46:
+ 24:03:a5:1a:85:81:56:83:63:27:ba:90:8e:f9:62:11:ba:a7:
+ 7c:90:a9:1a:66:b4:c5:bc:8f:29:41:ab:eb:8d:99:a6:cc:91:
+ 64:ba:dc:c6:a6:4c:b3:b4:23:26:51:72:56:f9:f3:74:55:9f:
+ 25:75:4f:2b
+-----BEGIN CERTIFICATE-----
+MIIEIjCCAwqgAwIBAgIDAjp5MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTQwOTA4MjA0MTEwWhcNMjIwNTIwMjA0MTEwWjBEMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
+U1NMIENBIC0gRzQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCafZho
+EUDBX3LsVbOxY/MyInKRxhYFuwiCMbT27tQYOREvLtpH/lExblvyqQrrL7v1YVll
+VwLNgP/HcDJUif3brply1E8MJrkuYzB93hRbatdSeCH5v7xQ1VQSWdi1NtkhR7g/
+algdjHLhl5XT4UWo8Vrlvv7jU3yl8FLgzzmUDBlx8sAlB0h9HObxOSUvmHlD6Bhy
+9GWGmFoABEfaS1gefIaxSzWmIAAczRs7Il3RkygzEiOUCKrDOvXRxox+mdMYoK2d
+GM9JrRAD95kzJoZGmi+gumxuyIgCt276ep6YSu6aMX0ZFGAM7I8gIzzalya26oBs
+ileeIO5vFyVKMq01AgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjAdBgNVHQ4EFgQUrDLtWsng3jCckFhVJmP2cqZUX+MwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCigJoYk
+aHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUFBwEB
+BCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARFMEMw
+QQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3RydXN0
+LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQBhQK0hDwO7ldyJ
+/KPLBXHpHFmXNcL6awWkFsZWRjd0GxvxPizoNxm3lNIPDsW/FAcrNM1btI3HVp0Z
+/AK0npAx+qRExnXd3R8lVKMwTKzb/sSI9zEmGEeuTCAZGseuPpgKFj3SwqZdDS4p
+fbKdx0EyF8qdrjm/kZje50TilZyUXGxCG1nJe2gTqJYJdO5AFKTV18l7M6MPWmmc
+GvpvEkcc3x5McE5t3f4ch7Wd4VQHCYrNvqqoRnhuFvLnkQ7Dr9p2ANHYokYkA6Ua
+hYFWg2MnupCO+WIRuqd8kKkaZrTFvI8pQavrjZmmzJFkutzGpkyztCMmUXJW+fN0
+VZ8ldU8r
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert5[] = {
+ 0x30, 0x82, 0x04, 0x22, 0x30, 0x82, 0x03, 0x0a, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x79, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30,
+ 0x39, 0x30, 0x38, 0x32, 0x30, 0x34, 0x31, 0x31, 0x30, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x30, 0x34, 0x31, 0x31, 0x30,
+ 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9a, 0x7d, 0x98, 0x68,
+ 0x11, 0x40, 0xc1, 0x5f, 0x72, 0xec, 0x55, 0xb3, 0xb1, 0x63, 0xf3, 0x32,
+ 0x22, 0x72, 0x91, 0xc6, 0x16, 0x05, 0xbb, 0x08, 0x82, 0x31, 0xb4, 0xf6,
+ 0xee, 0xd4, 0x18, 0x39, 0x11, 0x2f, 0x2e, 0xda, 0x47, 0xfe, 0x51, 0x31,
+ 0x6e, 0x5b, 0xf2, 0xa9, 0x0a, 0xeb, 0x2f, 0xbb, 0xf5, 0x61, 0x59, 0x65,
+ 0x57, 0x02, 0xcd, 0x80, 0xff, 0xc7, 0x70, 0x32, 0x54, 0x89, 0xfd, 0xdb,
+ 0xae, 0x99, 0x72, 0xd4, 0x4f, 0x0c, 0x26, 0xb9, 0x2e, 0x63, 0x30, 0x7d,
+ 0xde, 0x14, 0x5b, 0x6a, 0xd7, 0x52, 0x78, 0x21, 0xf9, 0xbf, 0xbc, 0x50,
+ 0xd5, 0x54, 0x12, 0x59, 0xd8, 0xb5, 0x36, 0xd9, 0x21, 0x47, 0xb8, 0x3f,
+ 0x6a, 0x58, 0x1d, 0x8c, 0x72, 0xe1, 0x97, 0x95, 0xd3, 0xe1, 0x45, 0xa8,
+ 0xf1, 0x5a, 0xe5, 0xbe, 0xfe, 0xe3, 0x53, 0x7c, 0xa5, 0xf0, 0x52, 0xe0,
+ 0xcf, 0x39, 0x94, 0x0c, 0x19, 0x71, 0xf2, 0xc0, 0x25, 0x07, 0x48, 0x7d,
+ 0x1c, 0xe6, 0xf1, 0x39, 0x25, 0x2f, 0x98, 0x79, 0x43, 0xe8, 0x18, 0x72,
+ 0xf4, 0x65, 0x86, 0x98, 0x5a, 0x00, 0x04, 0x47, 0xda, 0x4b, 0x58, 0x1e,
+ 0x7c, 0x86, 0xb1, 0x4b, 0x35, 0xa6, 0x20, 0x00, 0x1c, 0xcd, 0x1b, 0x3b,
+ 0x22, 0x5d, 0xd1, 0x93, 0x28, 0x33, 0x12, 0x23, 0x94, 0x08, 0xaa, 0xc3,
+ 0x3a, 0xf5, 0xd1, 0xc6, 0x8c, 0x7e, 0x99, 0xd3, 0x18, 0xa0, 0xad, 0x9d,
+ 0x18, 0xcf, 0x49, 0xad, 0x10, 0x03, 0xf7, 0x99, 0x33, 0x26, 0x86, 0x46,
+ 0x9a, 0x2f, 0xa0, 0xba, 0x6c, 0x6e, 0xc8, 0x88, 0x02, 0xb7, 0x6e, 0xfa,
+ 0x7a, 0x9e, 0x98, 0x4a, 0xee, 0x9a, 0x31, 0x7d, 0x19, 0x14, 0x60, 0x0c,
+ 0xec, 0x8f, 0x20, 0x23, 0x3c, 0xda, 0x97, 0x26, 0xb6, 0xea, 0x80, 0x6c,
+ 0x8a, 0x57, 0x9e, 0x20, 0xee, 0x6f, 0x17, 0x25, 0x4a, 0x32, 0xad, 0x35,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01,
+ 0x19, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64,
+ 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xac, 0x32, 0xed,
+ 0x5a, 0xc9, 0xe0, 0xde, 0x30, 0x9c, 0x90, 0x58, 0x55, 0x26, 0x63, 0xf6,
+ 0x72, 0xa6, 0x54, 0x5f, 0xe3, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d,
+ 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f,
+ 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30,
+ 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07,
+ 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x61, 0x40, 0xad, 0x21, 0x0f, 0x03, 0xbb, 0x95, 0xdc, 0x89,
+ 0xfc, 0xa3, 0xcb, 0x05, 0x71, 0xe9, 0x1c, 0x59, 0x97, 0x35, 0xc2, 0xfa,
+ 0x6b, 0x05, 0xa4, 0x16, 0xc6, 0x56, 0x46, 0x37, 0x74, 0x1b, 0x1b, 0xf1,
+ 0x3e, 0x2c, 0xe8, 0x37, 0x19, 0xb7, 0x94, 0xd2, 0x0f, 0x0e, 0xc5, 0xbf,
+ 0x14, 0x07, 0x2b, 0x34, 0xcd, 0x5b, 0xb4, 0x8d, 0xc7, 0x56, 0x9d, 0x19,
+ 0xfc, 0x02, 0xb4, 0x9e, 0x90, 0x31, 0xfa, 0xa4, 0x44, 0xc6, 0x75, 0xdd,
+ 0xdd, 0x1f, 0x25, 0x54, 0xa3, 0x30, 0x4c, 0xac, 0xdb, 0xfe, 0xc4, 0x88,
+ 0xf7, 0x31, 0x26, 0x18, 0x47, 0xae, 0x4c, 0x20, 0x19, 0x1a, 0xc7, 0xae,
+ 0x3e, 0x98, 0x0a, 0x16, 0x3d, 0xd2, 0xc2, 0xa6, 0x5d, 0x0d, 0x2e, 0x29,
+ 0x7d, 0xb2, 0x9d, 0xc7, 0x41, 0x32, 0x17, 0xca, 0x9d, 0xae, 0x39, 0xbf,
+ 0x91, 0x98, 0xde, 0xe7, 0x44, 0xe2, 0x95, 0x9c, 0x94, 0x5c, 0x6c, 0x42,
+ 0x1b, 0x59, 0xc9, 0x7b, 0x68, 0x13, 0xa8, 0x96, 0x09, 0x74, 0xee, 0x40,
+ 0x14, 0xa4, 0xd5, 0xd7, 0xc9, 0x7b, 0x33, 0xa3, 0x0f, 0x5a, 0x69, 0x9c,
+ 0x1a, 0xfa, 0x6f, 0x12, 0x47, 0x1c, 0xdf, 0x1e, 0x4c, 0x70, 0x4e, 0x6d,
+ 0xdd, 0xfe, 0x1c, 0x87, 0xb5, 0x9d, 0xe1, 0x54, 0x07, 0x09, 0x8a, 0xcd,
+ 0xbe, 0xaa, 0xa8, 0x46, 0x78, 0x6e, 0x16, 0xf2, 0xe7, 0x91, 0x0e, 0xc3,
+ 0xaf, 0xda, 0x76, 0x00, 0xd1, 0xd8, 0xa2, 0x46, 0x24, 0x03, 0xa5, 0x1a,
+ 0x85, 0x81, 0x56, 0x83, 0x63, 0x27, 0xba, 0x90, 0x8e, 0xf9, 0x62, 0x11,
+ 0xba, 0xa7, 0x7c, 0x90, 0xa9, 0x1a, 0x66, 0xb4, 0xc5, 0xbc, 0x8f, 0x29,
+ 0x41, 0xab, 0xeb, 0x8d, 0x99, 0xa6, 0xcc, 0x91, 0x64, 0xba, 0xdc, 0xc6,
+ 0xa6, 0x4c, 0xb3, 0xb4, 0x23, 0x26, 0x51, 0x72, 0x56, 0xf9, 0xf3, 0x74,
+ 0x55, 0x9f, 0x25, 0x75, 0x4f, 0x2b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146039 (0x23a77)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Aug 29 21:39:32 2014 GMT
+ Not After : May 20 21:39:32 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:54:9b:d9:58:5d:1e:2c:56:c6:d5:e8:7f:f4:
+ 7d:16:03:ff:d0:8b:5a:e4:8e:a7:dd:54:2e:d4:04:
+ c0:5d:98:9c:8d:90:0f:bc:10:65:5f:da:9a:d6:44:
+ 7c:c0:9f:b5:e9:4a:8c:0b:06:43:04:bb:f4:96:e2:
+ 26:f6:61:01:91:66:31:22:c3:34:34:5f:3f:3f:91:
+ 2f:44:5f:dc:c7:14:b6:03:9f:86:4b:0e:a3:ff:a0:
+ 80:02:83:c3:d3:1f:69:52:d6:9d:64:0f:c9:83:e7:
+ 1b:c4:70:ac:94:e7:c3:a4:6a:2c:bd:b8:9e:69:d8:
+ be:0a:8f:16:63:5a:68:71:80:7b:30:de:15:04:bf:
+ cc:d3:bf:3e:48:05:55:7a:b3:d7:10:0c:03:fc:9b:
+ fd:08:a7:8c:8c:db:a7:8e:f1:1e:63:dc:b3:01:2f:
+ 7f:af:57:c3:3c:48:a7:83:68:21:a7:2f:e7:a7:3f:
+ f0:b5:0c:fc:f5:84:d1:53:bc:0e:72:4f:60:0c:42:
+ b8:98:ad:19:88:57:d7:04:ec:87:bf:7e:87:4e:a3:
+ 21:f9:53:fd:36:98:48:8d:d6:f8:bb:48:f2:29:c8:
+ 64:d1:cc:54:48:53:8b:af:b7:65:1e:bf:29:33:29:
+ d9:29:60:48:f8:ff:91:bc:57:58:e5:35:2e:bb:69:
+ b6:59
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ C3:9C:F3:FC:D3:46:08:34:BB:CE:46:7F:A0:7C:5B:F3:E2:08:CB:59
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha256WithRSAEncryption
+ a3:58:1e:c6:43:32:ac:ac:2f:93:78:b7:ea:ae:54:40:47:2d:
+ 7e:78:8d:50:f6:f8:66:ac:d6:4f:73:d6:44:ef:af:0b:cc:5b:
+ c1:f4:4f:9a:8f:49:7e:60:af:c2:27:c7:16:f1:fb:93:81:90:
+ a9:7c:ef:6f:7e:6e:45:94:16:84:bd:ec:49:f1:c4:0e:f4:af:
+ 04:59:83:87:0f:2c:3b:97:c3:5a:12:9b:7b:04:35:7b:a3:95:
+ 33:08:7b:93:71:22:42:b3:a9:d9:6f:4f:81:92:fc:07:b6:79:
+ bc:84:4a:9d:77:09:f1:c5:89:f2:f0:b4:9c:54:aa:12:7b:0d:
+ ba:4f:ef:93:19:ec:ef:7d:4e:61:a3:8e:76:9c:59:cf:8c:94:
+ b1:84:97:f7:1a:b9:07:b8:b2:c6:4f:13:79:db:bf:4f:51:1b:
+ 7f:69:0d:51:2a:c1:d6:15:ff:37:51:34:65:51:f4:1e:be:38:
+ 6a:ec:0e:ab:bf:3d:7b:39:05:7b:f4:f3:fb:1a:a1:d0:c8:7e:
+ 4e:64:8d:cd:8c:61:55:90:fe:3a:ca:5d:25:0f:f8:1d:a3:4a:
+ 74:56:4f:1a:55:40:70:75:25:a6:33:2e:ba:4b:a5:5d:53:9a:
+ 0d:30:e1:8d:5f:61:2c:af:cc:ef:b0:99:a1:80:ff:0b:f2:62:
+ 4c:70:26:98
+-----BEGIN CERTIFICATE-----
+MIIEJTCCAw2gAwIBAgIDAjp3MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTQwODI5MjEzOTMyWhcNMjIwNTIwMjEzOTMyWjBHMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXUmFwaWRTU0wg
+U0hBMjU2IENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv
+VJvZWF0eLFbG1eh/9H0WA//Qi1rkjqfdVC7UBMBdmJyNkA+8EGVf2prWRHzAn7Xp
+SowLBkMEu/SW4ib2YQGRZjEiwzQ0Xz8/kS9EX9zHFLYDn4ZLDqP/oIACg8PTH2lS
+1p1kD8mD5xvEcKyU58Okaiy9uJ5p2L4KjxZjWmhxgHsw3hUEv8zTvz5IBVV6s9cQ
+DAP8m/0Ip4yM26eO8R5j3LMBL3+vV8M8SKeDaCGnL+enP/C1DPz1hNFTvA5yT2AM
+QriYrRmIV9cE7Ie/fodOoyH5U/02mEiN1vi7SPIpyGTRzFRIU4uvt2UevykzKdkp
+YEj4/5G8V1jlNS67abZZAgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7
+qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUw5zz/NNGCDS7zkZ/oHxb8+IIy1kwEgYD
+VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCig
+JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUF
+BwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARF
+MEMwQQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3Ry
+dXN0LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQCjWB7GQzKs
+rC+TeLfqrlRARy1+eI1Q9vhmrNZPc9ZE768LzFvB9E+aj0l+YK/CJ8cW8fuTgZCp
+fO9vfm5FlBaEvexJ8cQO9K8EWYOHDyw7l8NaEpt7BDV7o5UzCHuTcSJCs6nZb0+B
+kvwHtnm8hEqddwnxxYny8LScVKoSew26T++TGezvfU5ho452nFnPjJSxhJf3GrkH
+uLLGTxN5279PURt/aQ1RKsHWFf83UTRlUfQevjhq7A6rvz17OQV79PP7GqHQyH5O
+ZI3NjGFVkP46yl0lD/gdo0p0Vk8aVUBwdSWmMy66S6VdU5oNMOGNX2Esr8zvsJmh
+gP8L8mJMcCaY
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert6[] = {
+ 0x30, 0x82, 0x04, 0x25, 0x30, 0x82, 0x03, 0x0d, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x77, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30,
+ 0x38, 0x32, 0x39, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32,
+ 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20,
+ 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20,
+ 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf,
+ 0x54, 0x9b, 0xd9, 0x58, 0x5d, 0x1e, 0x2c, 0x56, 0xc6, 0xd5, 0xe8, 0x7f,
+ 0xf4, 0x7d, 0x16, 0x03, 0xff, 0xd0, 0x8b, 0x5a, 0xe4, 0x8e, 0xa7, 0xdd,
+ 0x54, 0x2e, 0xd4, 0x04, 0xc0, 0x5d, 0x98, 0x9c, 0x8d, 0x90, 0x0f, 0xbc,
+ 0x10, 0x65, 0x5f, 0xda, 0x9a, 0xd6, 0x44, 0x7c, 0xc0, 0x9f, 0xb5, 0xe9,
+ 0x4a, 0x8c, 0x0b, 0x06, 0x43, 0x04, 0xbb, 0xf4, 0x96, 0xe2, 0x26, 0xf6,
+ 0x61, 0x01, 0x91, 0x66, 0x31, 0x22, 0xc3, 0x34, 0x34, 0x5f, 0x3f, 0x3f,
+ 0x91, 0x2f, 0x44, 0x5f, 0xdc, 0xc7, 0x14, 0xb6, 0x03, 0x9f, 0x86, 0x4b,
+ 0x0e, 0xa3, 0xff, 0xa0, 0x80, 0x02, 0x83, 0xc3, 0xd3, 0x1f, 0x69, 0x52,
+ 0xd6, 0x9d, 0x64, 0x0f, 0xc9, 0x83, 0xe7, 0x1b, 0xc4, 0x70, 0xac, 0x94,
+ 0xe7, 0xc3, 0xa4, 0x6a, 0x2c, 0xbd, 0xb8, 0x9e, 0x69, 0xd8, 0xbe, 0x0a,
+ 0x8f, 0x16, 0x63, 0x5a, 0x68, 0x71, 0x80, 0x7b, 0x30, 0xde, 0x15, 0x04,
+ 0xbf, 0xcc, 0xd3, 0xbf, 0x3e, 0x48, 0x05, 0x55, 0x7a, 0xb3, 0xd7, 0x10,
+ 0x0c, 0x03, 0xfc, 0x9b, 0xfd, 0x08, 0xa7, 0x8c, 0x8c, 0xdb, 0xa7, 0x8e,
+ 0xf1, 0x1e, 0x63, 0xdc, 0xb3, 0x01, 0x2f, 0x7f, 0xaf, 0x57, 0xc3, 0x3c,
+ 0x48, 0xa7, 0x83, 0x68, 0x21, 0xa7, 0x2f, 0xe7, 0xa7, 0x3f, 0xf0, 0xb5,
+ 0x0c, 0xfc, 0xf5, 0x84, 0xd1, 0x53, 0xbc, 0x0e, 0x72, 0x4f, 0x60, 0x0c,
+ 0x42, 0xb8, 0x98, 0xad, 0x19, 0x88, 0x57, 0xd7, 0x04, 0xec, 0x87, 0xbf,
+ 0x7e, 0x87, 0x4e, 0xa3, 0x21, 0xf9, 0x53, 0xfd, 0x36, 0x98, 0x48, 0x8d,
+ 0xd6, 0xf8, 0xbb, 0x48, 0xf2, 0x29, 0xc8, 0x64, 0xd1, 0xcc, 0x54, 0x48,
+ 0x53, 0x8b, 0xaf, 0xb7, 0x65, 0x1e, 0xbf, 0x29, 0x33, 0x29, 0xd9, 0x29,
+ 0x60, 0x48, 0xf8, 0xff, 0x91, 0xbc, 0x57, 0x58, 0xe5, 0x35, 0x2e, 0xbb,
+ 0x69, 0xb6, 0x59, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d,
+ 0x30, 0x82, 0x01, 0x19, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0xc3, 0x9c, 0xf3, 0xfc, 0xd3, 0x46, 0x08, 0x34, 0xbb, 0xce, 0x46, 0x7f,
+ 0xa0, 0x7c, 0x5b, 0xf3, 0xe2, 0x08, 0xcb, 0x59, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0,
+ 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e,
+ 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72,
+ 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45,
+ 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8,
+ 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x58, 0x1e, 0xc6, 0x43, 0x32, 0xac,
+ 0xac, 0x2f, 0x93, 0x78, 0xb7, 0xea, 0xae, 0x54, 0x40, 0x47, 0x2d, 0x7e,
+ 0x78, 0x8d, 0x50, 0xf6, 0xf8, 0x66, 0xac, 0xd6, 0x4f, 0x73, 0xd6, 0x44,
+ 0xef, 0xaf, 0x0b, 0xcc, 0x5b, 0xc1, 0xf4, 0x4f, 0x9a, 0x8f, 0x49, 0x7e,
+ 0x60, 0xaf, 0xc2, 0x27, 0xc7, 0x16, 0xf1, 0xfb, 0x93, 0x81, 0x90, 0xa9,
+ 0x7c, 0xef, 0x6f, 0x7e, 0x6e, 0x45, 0x94, 0x16, 0x84, 0xbd, 0xec, 0x49,
+ 0xf1, 0xc4, 0x0e, 0xf4, 0xaf, 0x04, 0x59, 0x83, 0x87, 0x0f, 0x2c, 0x3b,
+ 0x97, 0xc3, 0x5a, 0x12, 0x9b, 0x7b, 0x04, 0x35, 0x7b, 0xa3, 0x95, 0x33,
+ 0x08, 0x7b, 0x93, 0x71, 0x22, 0x42, 0xb3, 0xa9, 0xd9, 0x6f, 0x4f, 0x81,
+ 0x92, 0xfc, 0x07, 0xb6, 0x79, 0xbc, 0x84, 0x4a, 0x9d, 0x77, 0x09, 0xf1,
+ 0xc5, 0x89, 0xf2, 0xf0, 0xb4, 0x9c, 0x54, 0xaa, 0x12, 0x7b, 0x0d, 0xba,
+ 0x4f, 0xef, 0x93, 0x19, 0xec, 0xef, 0x7d, 0x4e, 0x61, 0xa3, 0x8e, 0x76,
+ 0x9c, 0x59, 0xcf, 0x8c, 0x94, 0xb1, 0x84, 0x97, 0xf7, 0x1a, 0xb9, 0x07,
+ 0xb8, 0xb2, 0xc6, 0x4f, 0x13, 0x79, 0xdb, 0xbf, 0x4f, 0x51, 0x1b, 0x7f,
+ 0x69, 0x0d, 0x51, 0x2a, 0xc1, 0xd6, 0x15, 0xff, 0x37, 0x51, 0x34, 0x65,
+ 0x51, 0xf4, 0x1e, 0xbe, 0x38, 0x6a, 0xec, 0x0e, 0xab, 0xbf, 0x3d, 0x7b,
+ 0x39, 0x05, 0x7b, 0xf4, 0xf3, 0xfb, 0x1a, 0xa1, 0xd0, 0xc8, 0x7e, 0x4e,
+ 0x64, 0x8d, 0xcd, 0x8c, 0x61, 0x55, 0x90, 0xfe, 0x3a, 0xca, 0x5d, 0x25,
+ 0x0f, 0xf8, 0x1d, 0xa3, 0x4a, 0x74, 0x56, 0x4f, 0x1a, 0x55, 0x40, 0x70,
+ 0x75, 0x25, 0xa6, 0x33, 0x2e, 0xba, 0x4b, 0xa5, 0x5d, 0x53, 0x9a, 0x0d,
+ 0x30, 0xe1, 0x8d, 0x5f, 0x61, 0x2c, 0xaf, 0xcc, 0xef, 0xb0, 0x99, 0xa1,
+ 0x80, 0xff, 0x0b, 0xf2, 0x62, 0x4c, 0x70, 0x26, 0x98,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 11:20:96:f6:c8:03:7c:9e:07:b1:38:bf:2e:72:10:8a:d7:ed
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=FR, O=Certplus, CN=Class 2 Primary CA
+ Validity
+ Not Before: Jun 5 00:00:00 2007 GMT
+ Not After : Jun 20 00:00:00 2019 GMT
+ Subject: C=FR, O=KEYNECTIS, CN=CLASS 2 KEYNECTIS CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c6:be:fe:44:23:04:d4:ef:2f:3b:86:aa:35:58:
+ 81:d1:e1:9a:d6:b1:d4:27:45:28:fc:d1:1e:46:85:
+ ba:54:23:11:7d:e0:66:3f:d4:a3:57:66:78:f9:6b:
+ eb:74:7c:2a:b8:37:a5:e8:70:ae:82:b5:4e:d4:81:
+ fe:5b:e2:ea:e7:22:16:f8:f9:d7:ba:3a:f6:88:56:
+ dc:c4:f2:a0:a4:e5:75:06:60:72:2b:fb:f5:94:ee:
+ 2c:83:28:de:91:9a:b3:83:3a:b0:9f:08:fa:dd:d8:
+ 9e:8c:24:e6:df:66:5b:c8:7e:a3:62:4d:3f:3a:85:
+ 23:ec:e8:71:8f:0a:00:ac:89:6d:7e:d8:72:e5:dd:
+ c1:94:8e:5f:e4:73:e6:c1:c6:0c:87:58:4f:37:da:
+ d1:a9:88:26:76:b4:ee:11:8d:f6:ad:b2:a7:bc:73:
+ c4:cd:1c:6e:1a:e6:8d:72:56:44:a0:98:f7:92:f9:
+ d7:79:9b:03:e6:68:5f:a4:5c:7c:3d:50:b4:83:cc:
+ e5:ac:0d:e1:3e:4f:14:f2:b4:e4:7d:bf:71:a4:c3:
+ 97:73:38:d6:52:7c:c8:a4:b5:ea:e9:b2:54:56:d4:
+ eb:b8:57:3a:40:52:5a:5e:46:27:a3:7b:30:2d:08:
+ 3d:85:1e:9a:f0:32:a8:f2:10:a2:83:9b:e2:28:f6:
+ 9d:cb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.4.1.22234.2.5.3.3
+ CPS: http://www.keynectis.com/PC
+ Policy: 1.3.6.4.1.22234.2.5.1.3
+ CPS: http://www.keynectis.com/PC
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.certplus.com/CRL/class2.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 00:11:41:DF:3B:9D:3B:CB:B8:A2:C1:33:92:A8:81:CC:E5:7D:E7:99
+ X509v3 Authority Key Identifier:
+ keyid:E3:73:2D:DF:CB:0E:28:0C:DE:DD:B3:A4:CA:79:B8:8E:BB:E8:30:89
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 08:88:fe:1f:a2:ca:cd:e2:a0:f1:2e:7c:67:49:fb:dc:94:ac:
+ 7f:41:0d:78:01:ba:31:f7:9b:fb:31:18:77:2f:66:25:94:b8:
+ 6d:16:74:81:f1:c0:ae:67:c6:14:45:7a:01:d1:13:88:fc:e2:
+ 8d:22:1d:bd:1e:0c:c7:a9:7e:d0:c3:97:f6:37:5b:41:5e:67:
+ 94:8e:ab:69:02:17:18:f5:4d:38:c2:49:28:09:6e:5a:9b:a6:
+ 27:db:c0:5f:8f:44:9c:90:65:99:d8:b3:2e:c1:92:ee:1a:9d:
+ 0f:72:45:20:fa:2c:0c:9c:5d:cd:5b:54:41:54:4f:d3:e2:c7:
+ 59:84:3f:17:7b:7d:0e:c2:ef:62:c7:ba:b1:26:6c:83:4e:d3:
+ 19:c5:ff:56:a7:b4:45:3f:7a:9e:fa:d0:39:3e:80:46:75:5d:
+ 5a:79:7a:33:c5:01:bc:02:44:ce:1b:c0:31:4e:47:96:15:6e:
+ e7:e4:76:f0:c2:90:0d:a1:78:f4:38:00:91:2b:65:7c:79:13:
+ a8:3e:91:14:dc:88:05:08:d7:6f:53:f6:15:43:ee:c5:53:56:
+ 1a:02:b5:a6:a2:46:8d:1e:13:e4:67:c2:45:5f:40:5e:10:42:
+ 58:b5:cd:44:a3:94:4c:1c:54:90:4d:91:9a:26:8b:ad:a2:80:
+ 50:8d:14:14
+-----BEGIN CERTIFICATE-----
+MIIEKzCCAxOgAwIBAgISESCW9sgDfJ4HsTi/LnIQitftMA0GCSqGSIb3DQEBBQUA
+MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xh
+c3MgMiBQcmltYXJ5IENBMB4XDTA3MDYwNTAwMDAwMFoXDTE5MDYyMDAwMDAwMFow
+QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoTCUtFWU5FQ1RJUzEdMBsGA1UEAxMUQ0xB
+U1MgMiBLRVlORUNUSVMgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDGvv5EIwTU7y87hqo1WIHR4ZrWsdQnRSj80R5GhbpUIxF94GY/1KNXZnj5a+t0
+fCq4N6XocK6CtU7Ugf5b4urnIhb4+de6OvaIVtzE8qCk5XUGYHIr+/WU7iyDKN6R
+mrODOrCfCPrd2J6MJObfZlvIfqNiTT86hSPs6HGPCgCsiW1+2HLl3cGUjl/kc+bB
+xgyHWE832tGpiCZ2tO4Rjfatsqe8c8TNHG4a5o1yVkSgmPeS+dd5mwPmaF+kXHw9
+ULSDzOWsDeE+TxTytOR9v3Gkw5dzONZSfMikterpslRW1Ou4VzpAUlpeRiejezAt
+CD2FHprwMqjyEKKDm+Io9p3LAgMBAAGjggEgMIIBHDASBgNVHRMBAf8ECDAGAQH/
+AgEAMH0GA1UdIAR2MHQwOAYLKwYEAYGtWgIFAwMwKTAnBggrBgEFBQcCARYbaHR0
+cDovL3d3dy5rZXluZWN0aXMuY29tL1BDMDgGCysGBAGBrVoCBQEDMCkwJwYIKwYB
+BQUHAgEWG2h0dHA6Ly93d3cua2V5bmVjdGlzLmNvbS9QQzA3BgNVHR8EMDAuMCyg
+KqAohiZodHRwOi8vd3d3LmNlcnRwbHVzLmNvbS9DUkwvY2xhc3MyLmNybDAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAARQd87nTvLuKLBM5KogczlfeeZMB8GA1Ud
+IwQYMBaAFONzLd/LDigM3t2zpMp5uI676DCJMA0GCSqGSIb3DQEBBQUAA4IBAQAI
+iP4fosrN4qDxLnxnSfvclKx/QQ14Abox95v7MRh3L2YllLhtFnSB8cCuZ8YURXoB
+0ROI/OKNIh29HgzHqX7Qw5f2N1tBXmeUjqtpAhcY9U04wkkoCW5am6Yn28Bfj0Sc
+kGWZ2LMuwZLuGp0PckUg+iwMnF3NW1RBVE/T4sdZhD8Xe30Owu9ix7qxJmyDTtMZ
+xf9Wp7RFP3qe+tA5PoBGdV1aeXozxQG8AkTOG8AxTkeWFW7n5HbwwpANoXj0OACR
+K2V8eROoPpEU3IgFCNdvU/YVQ+7FU1YaArWmokaNHhPkZ8JFX0BeEEJYtc1Eo5RM
+HFSQTZGaJoutooBQjRQU
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert7[] = {
+ 0x30, 0x82, 0x04, 0x2b, 0x30, 0x82, 0x03, 0x13, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x12, 0x11, 0x20, 0x96, 0xf6, 0xc8, 0x03, 0x7c, 0x9e, 0x07,
+ 0xb1, 0x38, 0xbf, 0x2e, 0x72, 0x10, 0x8a, 0xd7, 0xed, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x30, 0x3d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x46, 0x52, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x08, 0x43, 0x65, 0x72, 0x74, 0x70, 0x6c, 0x75, 0x73, 0x31, 0x1b,
+ 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x43, 0x6c, 0x61,
+ 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79,
+ 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x30, 0x36, 0x30,
+ 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39,
+ 0x30, 0x36, 0x32, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30,
+ 0x40, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x46, 0x52, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x09, 0x4b, 0x45, 0x59, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x53, 0x31, 0x1d,
+ 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x14, 0x43, 0x4c, 0x41,
+ 0x53, 0x53, 0x20, 0x32, 0x20, 0x4b, 0x45, 0x59, 0x4e, 0x45, 0x43, 0x54,
+ 0x49, 0x53, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xc6, 0xbe, 0xfe, 0x44, 0x23, 0x04, 0xd4, 0xef, 0x2f, 0x3b,
+ 0x86, 0xaa, 0x35, 0x58, 0x81, 0xd1, 0xe1, 0x9a, 0xd6, 0xb1, 0xd4, 0x27,
+ 0x45, 0x28, 0xfc, 0xd1, 0x1e, 0x46, 0x85, 0xba, 0x54, 0x23, 0x11, 0x7d,
+ 0xe0, 0x66, 0x3f, 0xd4, 0xa3, 0x57, 0x66, 0x78, 0xf9, 0x6b, 0xeb, 0x74,
+ 0x7c, 0x2a, 0xb8, 0x37, 0xa5, 0xe8, 0x70, 0xae, 0x82, 0xb5, 0x4e, 0xd4,
+ 0x81, 0xfe, 0x5b, 0xe2, 0xea, 0xe7, 0x22, 0x16, 0xf8, 0xf9, 0xd7, 0xba,
+ 0x3a, 0xf6, 0x88, 0x56, 0xdc, 0xc4, 0xf2, 0xa0, 0xa4, 0xe5, 0x75, 0x06,
+ 0x60, 0x72, 0x2b, 0xfb, 0xf5, 0x94, 0xee, 0x2c, 0x83, 0x28, 0xde, 0x91,
+ 0x9a, 0xb3, 0x83, 0x3a, 0xb0, 0x9f, 0x08, 0xfa, 0xdd, 0xd8, 0x9e, 0x8c,
+ 0x24, 0xe6, 0xdf, 0x66, 0x5b, 0xc8, 0x7e, 0xa3, 0x62, 0x4d, 0x3f, 0x3a,
+ 0x85, 0x23, 0xec, 0xe8, 0x71, 0x8f, 0x0a, 0x00, 0xac, 0x89, 0x6d, 0x7e,
+ 0xd8, 0x72, 0xe5, 0xdd, 0xc1, 0x94, 0x8e, 0x5f, 0xe4, 0x73, 0xe6, 0xc1,
+ 0xc6, 0x0c, 0x87, 0x58, 0x4f, 0x37, 0xda, 0xd1, 0xa9, 0x88, 0x26, 0x76,
+ 0xb4, 0xee, 0x11, 0x8d, 0xf6, 0xad, 0xb2, 0xa7, 0xbc, 0x73, 0xc4, 0xcd,
+ 0x1c, 0x6e, 0x1a, 0xe6, 0x8d, 0x72, 0x56, 0x44, 0xa0, 0x98, 0xf7, 0x92,
+ 0xf9, 0xd7, 0x79, 0x9b, 0x03, 0xe6, 0x68, 0x5f, 0xa4, 0x5c, 0x7c, 0x3d,
+ 0x50, 0xb4, 0x83, 0xcc, 0xe5, 0xac, 0x0d, 0xe1, 0x3e, 0x4f, 0x14, 0xf2,
+ 0xb4, 0xe4, 0x7d, 0xbf, 0x71, 0xa4, 0xc3, 0x97, 0x73, 0x38, 0xd6, 0x52,
+ 0x7c, 0xc8, 0xa4, 0xb5, 0xea, 0xe9, 0xb2, 0x54, 0x56, 0xd4, 0xeb, 0xb8,
+ 0x57, 0x3a, 0x40, 0x52, 0x5a, 0x5e, 0x46, 0x27, 0xa3, 0x7b, 0x30, 0x2d,
+ 0x08, 0x3d, 0x85, 0x1e, 0x9a, 0xf0, 0x32, 0xa8, 0xf2, 0x10, 0xa2, 0x83,
+ 0x9b, 0xe2, 0x28, 0xf6, 0x9d, 0xcb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x20, 0x30, 0x82, 0x01, 0x1c, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x7d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x76,
+ 0x30, 0x74, 0x30, 0x38, 0x06, 0x0b, 0x2b, 0x06, 0x04, 0x01, 0x81, 0xad,
+ 0x5a, 0x02, 0x05, 0x03, 0x03, 0x30, 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1b, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6b, 0x65, 0x79, 0x6e,
+ 0x65, 0x63, 0x74, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x43,
+ 0x30, 0x38, 0x06, 0x0b, 0x2b, 0x06, 0x04, 0x01, 0x81, 0xad, 0x5a, 0x02,
+ 0x05, 0x01, 0x03, 0x30, 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6b, 0x65, 0x79, 0x6e, 0x65, 0x63,
+ 0x74, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x43, 0x30, 0x37,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30, 0x30, 0x2e, 0x30, 0x2c, 0xa0,
+ 0x2a, 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x70, 0x6c, 0x75, 0x73,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x63, 0x6c, 0x61,
+ 0x73, 0x73, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x00, 0x11,
+ 0x41, 0xdf, 0x3b, 0x9d, 0x3b, 0xcb, 0xb8, 0xa2, 0xc1, 0x33, 0x92, 0xa8,
+ 0x81, 0xcc, 0xe5, 0x7d, 0xe7, 0x99, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe3, 0x73, 0x2d, 0xdf, 0xcb,
+ 0x0e, 0x28, 0x0c, 0xde, 0xdd, 0xb3, 0xa4, 0xca, 0x79, 0xb8, 0x8e, 0xbb,
+ 0xe8, 0x30, 0x89, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x08,
+ 0x88, 0xfe, 0x1f, 0xa2, 0xca, 0xcd, 0xe2, 0xa0, 0xf1, 0x2e, 0x7c, 0x67,
+ 0x49, 0xfb, 0xdc, 0x94, 0xac, 0x7f, 0x41, 0x0d, 0x78, 0x01, 0xba, 0x31,
+ 0xf7, 0x9b, 0xfb, 0x31, 0x18, 0x77, 0x2f, 0x66, 0x25, 0x94, 0xb8, 0x6d,
+ 0x16, 0x74, 0x81, 0xf1, 0xc0, 0xae, 0x67, 0xc6, 0x14, 0x45, 0x7a, 0x01,
+ 0xd1, 0x13, 0x88, 0xfc, 0xe2, 0x8d, 0x22, 0x1d, 0xbd, 0x1e, 0x0c, 0xc7,
+ 0xa9, 0x7e, 0xd0, 0xc3, 0x97, 0xf6, 0x37, 0x5b, 0x41, 0x5e, 0x67, 0x94,
+ 0x8e, 0xab, 0x69, 0x02, 0x17, 0x18, 0xf5, 0x4d, 0x38, 0xc2, 0x49, 0x28,
+ 0x09, 0x6e, 0x5a, 0x9b, 0xa6, 0x27, 0xdb, 0xc0, 0x5f, 0x8f, 0x44, 0x9c,
+ 0x90, 0x65, 0x99, 0xd8, 0xb3, 0x2e, 0xc1, 0x92, 0xee, 0x1a, 0x9d, 0x0f,
+ 0x72, 0x45, 0x20, 0xfa, 0x2c, 0x0c, 0x9c, 0x5d, 0xcd, 0x5b, 0x54, 0x41,
+ 0x54, 0x4f, 0xd3, 0xe2, 0xc7, 0x59, 0x84, 0x3f, 0x17, 0x7b, 0x7d, 0x0e,
+ 0xc2, 0xef, 0x62, 0xc7, 0xba, 0xb1, 0x26, 0x6c, 0x83, 0x4e, 0xd3, 0x19,
+ 0xc5, 0xff, 0x56, 0xa7, 0xb4, 0x45, 0x3f, 0x7a, 0x9e, 0xfa, 0xd0, 0x39,
+ 0x3e, 0x80, 0x46, 0x75, 0x5d, 0x5a, 0x79, 0x7a, 0x33, 0xc5, 0x01, 0xbc,
+ 0x02, 0x44, 0xce, 0x1b, 0xc0, 0x31, 0x4e, 0x47, 0x96, 0x15, 0x6e, 0xe7,
+ 0xe4, 0x76, 0xf0, 0xc2, 0x90, 0x0d, 0xa1, 0x78, 0xf4, 0x38, 0x00, 0x91,
+ 0x2b, 0x65, 0x7c, 0x79, 0x13, 0xa8, 0x3e, 0x91, 0x14, 0xdc, 0x88, 0x05,
+ 0x08, 0xd7, 0x6f, 0x53, 0xf6, 0x15, 0x43, 0xee, 0xc5, 0x53, 0x56, 0x1a,
+ 0x02, 0xb5, 0xa6, 0xa2, 0x46, 0x8d, 0x1e, 0x13, 0xe4, 0x67, 0xc2, 0x45,
+ 0x5f, 0x40, 0x5e, 0x10, 0x42, 0x58, 0xb5, 0xcd, 0x44, 0xa3, 0x94, 0x4c,
+ 0x1c, 0x54, 0x90, 0x4d, 0x91, 0x9a, 0x26, 0x8b, 0xad, 0xa2, 0x80, 0x50,
+ 0x8d, 0x14, 0x14,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120024505 (0x7276db9)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Nov 30 16:35:21 2010 GMT
+ Not After : Aug 10 15:34:26 2018 GMT
+ Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79:
+ d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a:
+ 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2:
+ 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01:
+ 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7:
+ 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6:
+ 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c:
+ a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70:
+ 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77:
+ d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae:
+ 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18:
+ 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85:
+ ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9:
+ 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5:
+ c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a:
+ ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0:
+ 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27:
+ 1a:39
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:3
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ X509v3 Subject Key Identifier:
+ E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+ Signature Algorithm: sha1WithRSAEncryption
+ 16:b4:2c:c9:f1:5e:e1:a2:7b:9b:78:20:7a:4a:70:70:86:19:
+ 00:b7:05:2a:e8:c9:25:39:0f:c3:64:3c:75:09:d9:89:15:80:
+ 07:c2:8d:bc:29:a5:64:50:cf:71:75:47:23:bd:4d:d8:7f:77:
+ 9a:51:10:6e:4e:1f:20:3c:47:9c:43:74:7f:96:84:10:4c:13:
+ 43:be:f8:e0:72:2e:ff:bf:ae:3c:0a:03:60:82:4b:6f:f9:9a:
+ c5:1e:f6:af:90:3b:9f:61:3b:3e:de:9b:05:1a:c6:2c:3c:57:
+ 21:08:0f:54:fa:28:63:6c:e8:1b:9c:0f:cf:dd:30:44:13:b9:
+ 57:fe
+-----BEGIN CERTIFICATE-----
+MIIEODCCA6GgAwIBAgIEBydtuTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTEwMTEzMDE2MzUyMVoXDTE4MDgxMDE1MzQyNlowWjELMAkG
+A1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVz
+dDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uO
+KymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnn
+c+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP
+wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPg
+kAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFc
+B5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaOCAWow
+ggFmMBIGA1UdEwEB/wQIMAYBAf8CAQMwTgYDVR0gBEcwRTBDBgRVHSAAMDswOQYI
+KwYBBQUHAgEWLWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9yZXBvc2l0
+b3J5LmNmbTAOBgNVHQ8BAf8EBAMCAQYwgYkGA1UdIwSBgTB/oXmkdzB1MQswCQYD
+VQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUg
+Q3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRy
+dXN0IEdsb2JhbCBSb290ggIBpTBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vd3d3
+LnB1YmxpYy10cnVzdC5jb20vY2dpLWJpbi9DUkwvMjAxOC9jZHAuY3JsMB0GA1Ud
+DgQWBBTlnVkwgkdYzKz6CFQ2hns6tQRN8DANBgkqhkiG9w0BAQUFAAOBgQAWtCzJ
+8V7honubeCB6SnBwhhkAtwUq6MklOQ/DZDx1CdmJFYAHwo28KaVkUM9xdUcjvU3Y
+f3eaURBuTh8gPEecQ3R/loQQTBNDvvjgci7/v648CgNggktv+ZrFHvavkDufYTs+
+3psFGsYsPFchCA9U+ihjbOgbnA/P3TBEE7lX/g==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert8[] = {
+ 0x30, 0x82, 0x04, 0x38, 0x30, 0x82, 0x03, 0xa1, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x6d, 0xb9, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x30, 0x31, 0x31, 0x33, 0x30, 0x31, 0x36, 0x33, 0x35, 0x32,
+ 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x30, 0x31, 0x35,
+ 0x33, 0x34, 0x32, 0x36, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69,
+ 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79,
+ 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f,
+ 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+ 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x04,
+ 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a, 0xb5, 0x79,
+ 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3, 0x5b, 0x8e,
+ 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09, 0x05, 0x6d,
+ 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88, 0xda, 0x12,
+ 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52, 0x7b, 0x88,
+ 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a, 0x09, 0xe7,
+ 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d, 0x2d, 0xe5,
+ 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea, 0xf5, 0xab,
+ 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f, 0x0c, 0xd5,
+ 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70, 0xf0, 0x8f,
+ 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33, 0x7a, 0x77,
+ 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13, 0xd2, 0xc0,
+ 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc, 0xb4, 0xdd,
+ 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5, 0x63, 0xe0,
+ 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea, 0xeb, 0xd4,
+ 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69, 0xbc, 0xf9,
+ 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9, 0x90, 0x2c,
+ 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98, 0x21, 0x5c,
+ 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86, 0x3a, 0x6b,
+ 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78, 0x8d, 0x76,
+ 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90, 0xdc, 0x27,
+ 0x1a, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x6a, 0x30,
+ 0x82, 0x01, 0x66, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x03, 0x30,
+ 0x4e, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x81, 0x89, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f,
+ 0xa1, 0x79, 0xa4, 0x77, 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f,
+ 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30,
+ 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20,
+ 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53,
+ 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x82, 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36,
+ 0x86, 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69,
+ 0x6e, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63,
+ 0x64, 0x70, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58,
+ 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d,
+ 0xf0, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x16, 0xb4, 0x2c, 0xc9,
+ 0xf1, 0x5e, 0xe1, 0xa2, 0x7b, 0x9b, 0x78, 0x20, 0x7a, 0x4a, 0x70, 0x70,
+ 0x86, 0x19, 0x00, 0xb7, 0x05, 0x2a, 0xe8, 0xc9, 0x25, 0x39, 0x0f, 0xc3,
+ 0x64, 0x3c, 0x75, 0x09, 0xd9, 0x89, 0x15, 0x80, 0x07, 0xc2, 0x8d, 0xbc,
+ 0x29, 0xa5, 0x64, 0x50, 0xcf, 0x71, 0x75, 0x47, 0x23, 0xbd, 0x4d, 0xd8,
+ 0x7f, 0x77, 0x9a, 0x51, 0x10, 0x6e, 0x4e, 0x1f, 0x20, 0x3c, 0x47, 0x9c,
+ 0x43, 0x74, 0x7f, 0x96, 0x84, 0x10, 0x4c, 0x13, 0x43, 0xbe, 0xf8, 0xe0,
+ 0x72, 0x2e, 0xff, 0xbf, 0xae, 0x3c, 0x0a, 0x03, 0x60, 0x82, 0x4b, 0x6f,
+ 0xf9, 0x9a, 0xc5, 0x1e, 0xf6, 0xaf, 0x90, 0x3b, 0x9f, 0x61, 0x3b, 0x3e,
+ 0xde, 0x9b, 0x05, 0x1a, 0xc6, 0x2c, 0x3c, 0x57, 0x21, 0x08, 0x0f, 0x54,
+ 0xfa, 0x28, 0x63, 0x6c, 0xe8, 0x1b, 0x9c, 0x0f, 0xcf, 0xdd, 0x30, 0x44,
+ 0x13, 0xb9, 0x57, 0xfe,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146040 (0x23a78)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Aug 29 22:24:58 2014 GMT
+ Not After : May 20 22:24:58 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., OU=Domain Validated SSL, CN=GeoTrust DV SSL CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:df:41:94:7a:da:f7:e4:31:43:b6:ea:01:1b:5c:
+ ce:63:ea:fa:6d:a3:d9:6a:ee:2d:9a:75:f9:d5:9c:
+ 5b:bd:34:df:d8:1c:c9:6d:d8:04:88:da:6e:b5:b7:
+ b5:f0:30:ae:40:d6:5d:fa:c4:53:c1:d4:22:9d:04:
+ 4e:11:a6:95:d5:45:7c:41:05:58:e0:4c:dd:f9:ee:
+ 55:bd:5f:46:dc:ad:13:08:9d:2c:e4:f7:82:e6:07:
+ 2b:9e:0e:8c:34:a1:ce:c4:a1:e0:81:70:86:00:06:
+ 3f:2d:ea:7c:9b:28:ae:1b:28:8b:39:09:d3:e7:f0:
+ 45:a4:b1:ba:11:67:90:55:7b:8f:de:ed:38:5c:a1:
+ e1:e3:83:c4:c3:72:91:4f:98:ee:1c:c2:80:aa:64:
+ a5:3e:83:62:1c:cc:e0:9e:f8:5a:c0:13:12:7d:a2:
+ a7:8b:a3:e7:9f:2a:d7:9b:ca:cb:ed:97:01:9c:28:
+ 84:51:04:50:41:bc:b4:fc:78:e9:1b:cf:14:ea:1f:
+ 0f:fc:2e:01:32:8d:b6:35:cb:0a:18:3b:ec:5a:3e:
+ 3c:1b:d3:99:43:1e:2f:f7:bd:f3:5b:12:b9:07:5e:
+ ed:3e:d1:a9:87:cc:77:72:27:d4:d9:75:a2:63:4b:
+ 93:36:bd:e5:5c:d7:bf:5f:79:0d:b3:32:a7:0b:b2:
+ 63:23
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ 0B:50:EC:77:EF:2A:9B:FF:EC:03:A1:0A:FF:AD:C6:E4:2A:18:C7:3E
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 33:24:d5:90:aa:29:0c:35:b9:2f:c3:c7:42:93:c0:c6:10:4b:
+ 03:08:76:84:10:a2:e0:e7:53:12:27:f2:0a:da:7f:3a:dc:fd:
+ 5c:79:5a:8f:17:74:43:53:b1:d5:d1:5d:59:b9:a6:84:64:ca:
+ f1:3a:0a:59:96:10:bf:a9:81:57:8b:5c:87:dc:7f:e3:e4:bb:
+ 05:7a:a0:32:09:13:4e:10:81:28:1f:9c:03:62:bc:f4:01:b5:
+ 29:83:46:07:b9:e7:b8:5d:c8:e9:d1:dd:ad:3b:f8:34:db:c1:
+ d1:95:a9:91:18:ed:3c:2c:37:11:4d:cc:fe:53:3e:50:43:f9:
+ c3:56:41:ac:53:9b:6c:05:b2:9a:e2:e0:59:57:30:32:b6:26:
+ 4e:13:25:cd:fa:48:70:0f:75:55:60:11:f5:3b:d5:5e:5a:3c:
+ 8b:5b:0f:0f:62:42:48:61:85:8b:10:f4:c1:88:bf:7f:5f:8a:
+ c2:d7:cd:2b:94:5c:1f:34:4a:08:af:eb:ae:89:a8:48:75:55:
+ 95:1d:bb:c0:9a:01:b9:f4:03:22:3e:d4:e6:52:30:0d:67:b9:
+ c0:91:fd:2d:4c:30:8e:bd:8c:a5:04:91:bb:a4:ab:7f:0f:d8:
+ 6f:f0:66:00:c9:a3:5c:f5:b0:8f:83:e6:9c:5a:e6:b6:b9:c5:
+ bc:be:e4:02
+-----BEGIN CERTIFICATE-----
+MIIERDCCAyygAwIBAgIDAjp4MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTQwODI5MjIyNDU4WhcNMjIwNTIwMjIyNDU4WjBmMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UECxMURG9tYWluIFZh
+bGlkYXRlZCBTU0wxIDAeBgNVBAMTF0dlb1RydXN0IERWIFNTTCBDQSAtIEc0MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA30GUetr35DFDtuoBG1zOY+r6
+baPZau4tmnX51ZxbvTTf2BzJbdgEiNputbe18DCuQNZd+sRTwdQinQROEaaV1UV8
+QQVY4Ezd+e5VvV9G3K0TCJ0s5PeC5gcrng6MNKHOxKHggXCGAAY/Lep8myiuGyiL
+OQnT5/BFpLG6EWeQVXuP3u04XKHh44PEw3KRT5juHMKAqmSlPoNiHMzgnvhawBMS
+faKni6PnnyrXm8rL7ZcBnCiEUQRQQby0/HjpG88U6h8P/C4BMo22NcsKGDvsWj48
+G9OZQx4v973zWxK5B17tPtGph8x3cifU2XWiY0uTNr3lXNe/X3kNszKnC7JjIwID
+AQABo4IBHTCCARkwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4wHQYD
+VR0OBBYEFAtQ7HfvKpv/7AOhCv+txuQqGMc+MBIGA1UdEwEB/wQIMAYBAf8CAQAw
+DgYDVR0PAQH/BAQDAgEGMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9nLnN5bWNi
+LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAuBggrBgEFBQcBAQQiMCAwHgYIKwYBBQUH
+MAGGEmh0dHA6Ly9nLnN5bWNkLmNvbTBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYw
+MzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2Vz
+L2NwczANBgkqhkiG9w0BAQsFAAOCAQEAMyTVkKopDDW5L8PHQpPAxhBLAwh2hBCi
+4OdTEifyCtp/Otz9XHlajxd0Q1Ox1dFdWbmmhGTK8ToKWZYQv6mBV4tch9x/4+S7
+BXqgMgkTThCBKB+cA2K89AG1KYNGB7nnuF3I6dHdrTv4NNvB0ZWpkRjtPCw3EU3M
+/lM+UEP5w1ZBrFObbAWymuLgWVcwMrYmThMlzfpIcA91VWAR9TvVXlo8i1sPD2JC
+SGGFixD0wYi/f1+KwtfNK5RcHzRKCK/rromoSHVVlR27wJoBufQDIj7U5lIwDWe5
+wJH9LUwwjr2MpQSRu6Srfw/Yb/BmAMmjXPWwj4PmnFrmtrnFvL7kAg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert9[] = {
+ 0x30, 0x82, 0x04, 0x44, 0x30, 0x82, 0x03, 0x2c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30,
+ 0x38, 0x32, 0x39, 0x32, 0x32, 0x32, 0x34, 0x35, 0x38, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x32, 0x32, 0x34, 0x35, 0x38,
+ 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x14, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61,
+ 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31,
+ 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x47, 0x65,
+ 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x44, 0x56, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdf, 0x41, 0x94, 0x7a, 0xda, 0xf7,
+ 0xe4, 0x31, 0x43, 0xb6, 0xea, 0x01, 0x1b, 0x5c, 0xce, 0x63, 0xea, 0xfa,
+ 0x6d, 0xa3, 0xd9, 0x6a, 0xee, 0x2d, 0x9a, 0x75, 0xf9, 0xd5, 0x9c, 0x5b,
+ 0xbd, 0x34, 0xdf, 0xd8, 0x1c, 0xc9, 0x6d, 0xd8, 0x04, 0x88, 0xda, 0x6e,
+ 0xb5, 0xb7, 0xb5, 0xf0, 0x30, 0xae, 0x40, 0xd6, 0x5d, 0xfa, 0xc4, 0x53,
+ 0xc1, 0xd4, 0x22, 0x9d, 0x04, 0x4e, 0x11, 0xa6, 0x95, 0xd5, 0x45, 0x7c,
+ 0x41, 0x05, 0x58, 0xe0, 0x4c, 0xdd, 0xf9, 0xee, 0x55, 0xbd, 0x5f, 0x46,
+ 0xdc, 0xad, 0x13, 0x08, 0x9d, 0x2c, 0xe4, 0xf7, 0x82, 0xe6, 0x07, 0x2b,
+ 0x9e, 0x0e, 0x8c, 0x34, 0xa1, 0xce, 0xc4, 0xa1, 0xe0, 0x81, 0x70, 0x86,
+ 0x00, 0x06, 0x3f, 0x2d, 0xea, 0x7c, 0x9b, 0x28, 0xae, 0x1b, 0x28, 0x8b,
+ 0x39, 0x09, 0xd3, 0xe7, 0xf0, 0x45, 0xa4, 0xb1, 0xba, 0x11, 0x67, 0x90,
+ 0x55, 0x7b, 0x8f, 0xde, 0xed, 0x38, 0x5c, 0xa1, 0xe1, 0xe3, 0x83, 0xc4,
+ 0xc3, 0x72, 0x91, 0x4f, 0x98, 0xee, 0x1c, 0xc2, 0x80, 0xaa, 0x64, 0xa5,
+ 0x3e, 0x83, 0x62, 0x1c, 0xcc, 0xe0, 0x9e, 0xf8, 0x5a, 0xc0, 0x13, 0x12,
+ 0x7d, 0xa2, 0xa7, 0x8b, 0xa3, 0xe7, 0x9f, 0x2a, 0xd7, 0x9b, 0xca, 0xcb,
+ 0xed, 0x97, 0x01, 0x9c, 0x28, 0x84, 0x51, 0x04, 0x50, 0x41, 0xbc, 0xb4,
+ 0xfc, 0x78, 0xe9, 0x1b, 0xcf, 0x14, 0xea, 0x1f, 0x0f, 0xfc, 0x2e, 0x01,
+ 0x32, 0x8d, 0xb6, 0x35, 0xcb, 0x0a, 0x18, 0x3b, 0xec, 0x5a, 0x3e, 0x3c,
+ 0x1b, 0xd3, 0x99, 0x43, 0x1e, 0x2f, 0xf7, 0xbd, 0xf3, 0x5b, 0x12, 0xb9,
+ 0x07, 0x5e, 0xed, 0x3e, 0xd1, 0xa9, 0x87, 0xcc, 0x77, 0x72, 0x27, 0xd4,
+ 0xd9, 0x75, 0xa2, 0x63, 0x4b, 0x93, 0x36, 0xbd, 0xe5, 0x5c, 0xd7, 0xbf,
+ 0x5f, 0x79, 0x0d, 0xb3, 0x32, 0xa7, 0x0b, 0xb2, 0x63, 0x23, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, 0x19, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11,
+ 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0b, 0x50, 0xec, 0x77, 0xef,
+ 0x2a, 0x9b, 0xff, 0xec, 0x03, 0xa1, 0x0a, 0xff, 0xad, 0xc6, 0xe4, 0x2a,
+ 0x18, 0xc7, 0x3e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e,
+ 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74,
+ 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x2e,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22,
+ 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67,
+ 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4c,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06,
+ 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30,
+ 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x33, 0x24, 0xd5, 0x90, 0xaa, 0x29, 0x0c, 0x35, 0xb9, 0x2f, 0xc3, 0xc7,
+ 0x42, 0x93, 0xc0, 0xc6, 0x10, 0x4b, 0x03, 0x08, 0x76, 0x84, 0x10, 0xa2,
+ 0xe0, 0xe7, 0x53, 0x12, 0x27, 0xf2, 0x0a, 0xda, 0x7f, 0x3a, 0xdc, 0xfd,
+ 0x5c, 0x79, 0x5a, 0x8f, 0x17, 0x74, 0x43, 0x53, 0xb1, 0xd5, 0xd1, 0x5d,
+ 0x59, 0xb9, 0xa6, 0x84, 0x64, 0xca, 0xf1, 0x3a, 0x0a, 0x59, 0x96, 0x10,
+ 0xbf, 0xa9, 0x81, 0x57, 0x8b, 0x5c, 0x87, 0xdc, 0x7f, 0xe3, 0xe4, 0xbb,
+ 0x05, 0x7a, 0xa0, 0x32, 0x09, 0x13, 0x4e, 0x10, 0x81, 0x28, 0x1f, 0x9c,
+ 0x03, 0x62, 0xbc, 0xf4, 0x01, 0xb5, 0x29, 0x83, 0x46, 0x07, 0xb9, 0xe7,
+ 0xb8, 0x5d, 0xc8, 0xe9, 0xd1, 0xdd, 0xad, 0x3b, 0xf8, 0x34, 0xdb, 0xc1,
+ 0xd1, 0x95, 0xa9, 0x91, 0x18, 0xed, 0x3c, 0x2c, 0x37, 0x11, 0x4d, 0xcc,
+ 0xfe, 0x53, 0x3e, 0x50, 0x43, 0xf9, 0xc3, 0x56, 0x41, 0xac, 0x53, 0x9b,
+ 0x6c, 0x05, 0xb2, 0x9a, 0xe2, 0xe0, 0x59, 0x57, 0x30, 0x32, 0xb6, 0x26,
+ 0x4e, 0x13, 0x25, 0xcd, 0xfa, 0x48, 0x70, 0x0f, 0x75, 0x55, 0x60, 0x11,
+ 0xf5, 0x3b, 0xd5, 0x5e, 0x5a, 0x3c, 0x8b, 0x5b, 0x0f, 0x0f, 0x62, 0x42,
+ 0x48, 0x61, 0x85, 0x8b, 0x10, 0xf4, 0xc1, 0x88, 0xbf, 0x7f, 0x5f, 0x8a,
+ 0xc2, 0xd7, 0xcd, 0x2b, 0x94, 0x5c, 0x1f, 0x34, 0x4a, 0x08, 0xaf, 0xeb,
+ 0xae, 0x89, 0xa8, 0x48, 0x75, 0x55, 0x95, 0x1d, 0xbb, 0xc0, 0x9a, 0x01,
+ 0xb9, 0xf4, 0x03, 0x22, 0x3e, 0xd4, 0xe6, 0x52, 0x30, 0x0d, 0x67, 0xb9,
+ 0xc0, 0x91, 0xfd, 0x2d, 0x4c, 0x30, 0x8e, 0xbd, 0x8c, 0xa5, 0x04, 0x91,
+ 0xbb, 0xa4, 0xab, 0x7f, 0x0f, 0xd8, 0x6f, 0xf0, 0x66, 0x00, 0xc9, 0xa3,
+ 0x5c, 0xf5, 0xb0, 0x8f, 0x83, 0xe6, 0x9c, 0x5a, 0xe6, 0xb6, 0xb9, 0xc5,
+ 0xbc, 0xbe, 0xe4, 0x02,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 33:65:50:08:79:ad:73:e2:30:b9:e0:1d:0d:7f:ac:91
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
+ Validity
+ Not Before: Nov 17 00:00:00 2006 GMT
+ Not After : Dec 30 23:59:59 2020 GMT
+ Subject: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ac:a0:f0:fb:80:59:d4:9c:c7:a4:cf:9d:a1:59:
+ 73:09:10:45:0c:0d:2c:6e:68:f1:6c:5b:48:68:49:
+ 59:37:fc:0b:33:19:c2:77:7f:cc:10:2d:95:34:1c:
+ e6:eb:4d:09:a7:1c:d2:b8:c9:97:36:02:b7:89:d4:
+ 24:5f:06:c0:cc:44:94:94:8d:02:62:6f:eb:5a:dd:
+ 11:8d:28:9a:5c:84:90:10:7a:0d:bd:74:66:2f:6a:
+ 38:a0:e2:d5:54:44:eb:1d:07:9f:07:ba:6f:ee:e9:
+ fd:4e:0b:29:f5:3e:84:a0:01:f1:9c:ab:f8:1c:7e:
+ 89:a4:e8:a1:d8:71:65:0d:a3:51:7b:ee:bc:d2:22:
+ 60:0d:b9:5b:9d:df:ba:fc:51:5b:0b:af:98:b2:e9:
+ 2e:e9:04:e8:62:87:de:2b:c8:d7:4e:c1:4c:64:1e:
+ dd:cf:87:58:ba:4a:4f:ca:68:07:1d:1c:9d:4a:c6:
+ d5:2f:91:cc:7c:71:72:1c:c5:c0:67:eb:32:fd:c9:
+ 92:5c:94:da:85:c0:9b:bf:53:7d:2b:09:f4:8c:9d:
+ 91:1f:97:6a:52:cb:de:09:36:a4:77:d8:7b:87:50:
+ 44:d5:3e:6e:29:69:fb:39:49:26:1e:09:a5:80:7b:
+ 40:2d:eb:e8:27:85:c9:fe:61:fd:7e:e6:7c:97:1d:
+ d5:9d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.thawte.com/cps
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePremiumServerCA.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 84:a8:4c:c9:3e:2a:bc:9a:e2:cc:8f:0b:b2:25:77:c4:61:89:
+ 89:63:5a:d4:a3:15:40:d4:fb:5e:3f:b4:43:ea:63:17:2b:6b:
+ 99:74:9e:09:a8:dd:d4:56:15:2e:7a:79:31:5f:63:96:53:1b:
+ 34:d9:15:ea:4f:6d:70:ca:be:f6:82:a9:ed:da:85:77:cc:76:
+ 1c:6a:81:0a:21:d8:41:99:7f:5e:2e:82:c1:e8:aa:f7:93:81:
+ 05:aa:92:b4:1f:b7:9a:c0:07:17:f5:cb:c6:b4:4c:0e:d7:56:
+ dc:71:20:74:38:d6:74:c6:d6:8f:6b:af:8b:8d:a0:6c:29:0b:
+ 61:e0
+-----BEGIN CERTIFICATE-----
+MIIERTCCA66gAwIBAgIQM2VQCHmtc+IwueAdDX+skTANBgkqhkiG9w0BAQUFADCB
+zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
+Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
+d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTA2MTExNzAwMDAwMFoXDTIwMTIzMDIzNTk1OVow
+gakxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xKDAmBgNVBAsT
+H0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xODA2BgNVBAsTLyhjKSAy
+MDA2IHRoYXd0ZSwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYD
+VQQDExZ0aGF3dGUgUHJpbWFyeSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEArKDw+4BZ1JzHpM+doVlzCRBFDA0sbmjxbFtIaElZN/wLMxnC
+d3/MEC2VNBzm600JpxzSuMmXNgK3idQkXwbAzESUlI0CYm/rWt0RjSiaXISQEHoN
+vXRmL2o4oOLVVETrHQefB7pv7un9Tgsp9T6EoAHxnKv4HH6JpOih2HFlDaNRe+68
+0iJgDblbnd+6/FFbC6+Ysuku6QToYofeK8jXTsFMZB7dz4dYukpPymgHHRydSsbV
+L5HMfHFyHMXAZ+sy/cmSXJTahcCbv1N9Kwn0jJ2RH5dqUsveCTakd9h7h1BE1T5u
+KWn7OUkmHgmlgHtALevoJ4XJ/mH9fuZ8lx3VnQIDAQABo4HCMIG/MA8GA1UdEwEB
+/wQFMAMBAf8wOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBz
+Oi8vd3d3LnRoYXd0ZS5jb20vY3BzMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
+e1tFz6/Oy3r9MZIaarbzRutXSFAwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL2Ny
+bC50aGF3dGUuY29tL1RoYXd0ZVByZW1pdW1TZXJ2ZXJDQS5jcmwwDQYJKoZIhvcN
+AQEFBQADgYEAhKhMyT4qvJrizI8LsiV3xGGJiWNa1KMVQNT7Xj+0Q+pjFytrmXSe
+Cajd1FYVLnp5MV9jllMbNNkV6k9tcMq+9oKp7dqFd8x2HGqBCiHYQZl/Xi6Cweiq
+95OBBaqStB+3msAHF/XLxrRMDtdW3HEgdDjWdMbWj2uvi42gbCkLYeA=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert10[] = {
+ 0x30, 0x82, 0x04, 0x45, 0x30, 0x82, 0x03, 0xae, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x33, 0x65, 0x50, 0x08, 0x79, 0xad, 0x73, 0xe2, 0x30,
+ 0xb9, 0xe0, 0x1d, 0x0d, 0x7f, 0xac, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xce, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x5a, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0c, 0x57, 0x65, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x43, 0x61, 0x70,
+ 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09,
+ 0x43, 0x61, 0x70, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31, 0x1d, 0x30,
+ 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e,
+ 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61,
+ 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30,
+ 0x26, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x19, 0x70, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31,
+ 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x31, 0x32, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30,
+ 0x81, 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20,
+ 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32,
+ 0x30, 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73,
+ 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20,
+ 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74,
+ 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xac, 0xa0, 0xf0, 0xfb, 0x80, 0x59, 0xd4, 0x9c, 0xc7, 0xa4, 0xcf, 0x9d,
+ 0xa1, 0x59, 0x73, 0x09, 0x10, 0x45, 0x0c, 0x0d, 0x2c, 0x6e, 0x68, 0xf1,
+ 0x6c, 0x5b, 0x48, 0x68, 0x49, 0x59, 0x37, 0xfc, 0x0b, 0x33, 0x19, 0xc2,
+ 0x77, 0x7f, 0xcc, 0x10, 0x2d, 0x95, 0x34, 0x1c, 0xe6, 0xeb, 0x4d, 0x09,
+ 0xa7, 0x1c, 0xd2, 0xb8, 0xc9, 0x97, 0x36, 0x02, 0xb7, 0x89, 0xd4, 0x24,
+ 0x5f, 0x06, 0xc0, 0xcc, 0x44, 0x94, 0x94, 0x8d, 0x02, 0x62, 0x6f, 0xeb,
+ 0x5a, 0xdd, 0x11, 0x8d, 0x28, 0x9a, 0x5c, 0x84, 0x90, 0x10, 0x7a, 0x0d,
+ 0xbd, 0x74, 0x66, 0x2f, 0x6a, 0x38, 0xa0, 0xe2, 0xd5, 0x54, 0x44, 0xeb,
+ 0x1d, 0x07, 0x9f, 0x07, 0xba, 0x6f, 0xee, 0xe9, 0xfd, 0x4e, 0x0b, 0x29,
+ 0xf5, 0x3e, 0x84, 0xa0, 0x01, 0xf1, 0x9c, 0xab, 0xf8, 0x1c, 0x7e, 0x89,
+ 0xa4, 0xe8, 0xa1, 0xd8, 0x71, 0x65, 0x0d, 0xa3, 0x51, 0x7b, 0xee, 0xbc,
+ 0xd2, 0x22, 0x60, 0x0d, 0xb9, 0x5b, 0x9d, 0xdf, 0xba, 0xfc, 0x51, 0x5b,
+ 0x0b, 0xaf, 0x98, 0xb2, 0xe9, 0x2e, 0xe9, 0x04, 0xe8, 0x62, 0x87, 0xde,
+ 0x2b, 0xc8, 0xd7, 0x4e, 0xc1, 0x4c, 0x64, 0x1e, 0xdd, 0xcf, 0x87, 0x58,
+ 0xba, 0x4a, 0x4f, 0xca, 0x68, 0x07, 0x1d, 0x1c, 0x9d, 0x4a, 0xc6, 0xd5,
+ 0x2f, 0x91, 0xcc, 0x7c, 0x71, 0x72, 0x1c, 0xc5, 0xc0, 0x67, 0xeb, 0x32,
+ 0xfd, 0xc9, 0x92, 0x5c, 0x94, 0xda, 0x85, 0xc0, 0x9b, 0xbf, 0x53, 0x7d,
+ 0x2b, 0x09, 0xf4, 0x8c, 0x9d, 0x91, 0x1f, 0x97, 0x6a, 0x52, 0xcb, 0xde,
+ 0x09, 0x36, 0xa4, 0x77, 0xd8, 0x7b, 0x87, 0x50, 0x44, 0xd5, 0x3e, 0x6e,
+ 0x29, 0x69, 0xfb, 0x39, 0x49, 0x26, 0x1e, 0x09, 0xa5, 0x80, 0x7b, 0x40,
+ 0x2d, 0xeb, 0xe8, 0x27, 0x85, 0xc9, 0xfe, 0x61, 0xfd, 0x7e, 0xe6, 0x7c,
+ 0x97, 0x1d, 0xd5, 0x9d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xc2,
+ 0x30, 0x81, 0xbf, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3b, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74,
+ 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a,
+ 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x40, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xa0, 0x33, 0xa0,
+ 0x31, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x72, 0x65, 0x6d, 0x69,
+ 0x75, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x84, 0xa8, 0x4c,
+ 0xc9, 0x3e, 0x2a, 0xbc, 0x9a, 0xe2, 0xcc, 0x8f, 0x0b, 0xb2, 0x25, 0x77,
+ 0xc4, 0x61, 0x89, 0x89, 0x63, 0x5a, 0xd4, 0xa3, 0x15, 0x40, 0xd4, 0xfb,
+ 0x5e, 0x3f, 0xb4, 0x43, 0xea, 0x63, 0x17, 0x2b, 0x6b, 0x99, 0x74, 0x9e,
+ 0x09, 0xa8, 0xdd, 0xd4, 0x56, 0x15, 0x2e, 0x7a, 0x79, 0x31, 0x5f, 0x63,
+ 0x96, 0x53, 0x1b, 0x34, 0xd9, 0x15, 0xea, 0x4f, 0x6d, 0x70, 0xca, 0xbe,
+ 0xf6, 0x82, 0xa9, 0xed, 0xda, 0x85, 0x77, 0xcc, 0x76, 0x1c, 0x6a, 0x81,
+ 0x0a, 0x21, 0xd8, 0x41, 0x99, 0x7f, 0x5e, 0x2e, 0x82, 0xc1, 0xe8, 0xaa,
+ 0xf7, 0x93, 0x81, 0x05, 0xaa, 0x92, 0xb4, 0x1f, 0xb7, 0x9a, 0xc0, 0x07,
+ 0x17, 0xf5, 0xcb, 0xc6, 0xb4, 0x4c, 0x0e, 0xd7, 0x56, 0xdc, 0x71, 0x20,
+ 0x74, 0x38, 0xd6, 0x74, 0xc6, 0xd6, 0x8f, 0x6b, 0xaf, 0x8b, 0x8d, 0xa0,
+ 0x6c, 0x29, 0x0b, 0x61, 0xe0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:44:4e:f0:36:31
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Feb 20 10:00:00 2014 GMT
+ Not After : Feb 20 10:00:00 2024 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=AlphaSSL CA - SHA256 - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:da:01:ec:e4:ec:73:60:fb:7e:8f:6a:b7:c6:17:
+ e3:92:64:32:d4:ac:00:d9:a2:0f:b9:ed:ee:6b:8a:
+ 86:ca:92:67:d9:74:d7:5d:47:02:3c:8f:40:d6:9e:
+ 6d:14:cd:c3:da:29:39:a7:0f:05:0a:68:a2:66:1a:
+ 1e:c4:b2:8b:76:58:e5:ab:5d:1d:8f:40:b3:39:8b:
+ ef:1e:83:7d:22:d0:e3:a9:00:2e:ec:53:cf:62:19:
+ 85:44:28:4c:c0:27:cb:7b:0e:ec:10:64:00:10:a4:
+ 05:cc:a0:72:be:41:6c:31:5b:48:e4:b1:ec:b9:23:
+ eb:55:4d:d0:7d:62:4a:a5:b4:a5:a4:59:85:c5:25:
+ 91:a6:fe:a6:09:9f:06:10:6d:8f:81:0c:64:40:5e:
+ 73:00:9a:e0:2e:65:98:54:10:00:70:98:c8:e1:ed:
+ 34:5f:d8:9c:c7:0d:c0:d6:23:59:45:fc:fe:55:7a:
+ 86:ee:94:60:22:f1:ae:d1:e6:55:46:f6:99:c5:1b:
+ 08:74:5f:ac:b0:64:84:8f:89:38:1c:a1:a7:90:21:
+ 4f:02:6e:bd:e0:61:67:d4:f8:42:87:0f:0a:f7:c9:
+ 04:6d:2a:a9:2f:ef:42:a5:df:dd:a3:53:db:98:1e:
+ 81:f9:9a:72:7b:5a:de:4f:3e:7f:a2:58:a0:e2:17:
+ ad:67
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ F5:CD:D5:3C:08:50:F9:6A:4F:3A:B7:97:DA:56:83:E6:69:D2:68:F7
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.alphassl.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 60:40:68:16:47:e7:16:8d:db:5c:a1:56:2a:cb:f4:5c:9b:b0:
+ 1e:a2:4b:f5:cb:02:3f:f8:0b:a1:f2:a7:42:d4:b7:4c:eb:e3:
+ 66:80:f3:25:43:78:2e:1b:17:56:07:52:18:cb:d1:a8:ec:e6:
+ fb:73:3e:a4:62:8c:80:b4:d2:c5:12:73:a3:d3:fa:02:38:be:
+ 63:3d:84:b8:99:c1:f1:ba:f7:9f:c3:40:d1:58:18:53:c1:62:
+ dd:af:18:42:7f:34:4e:c5:43:d5:71:b0:30:00:c7:e3:90:ae:
+ 3f:57:86:97:ce:ea:0c:12:8e:22:70:e3:66:a7:54:7f:2e:28:
+ cb:d4:54:d0:b3:1e:62:67:08:f9:27:e1:cb:e3:66:b8:24:1b:
+ 89:6a:89:44:65:f2:d9:4c:d2:58:1c:8c:4e:c0:95:a1:d4:ef:
+ 67:2f:38:20:e8:2e:ff:96:51:f0:ba:d8:3d:92:70:47:65:1c:
+ 9e:73:72:b4:60:0c:5c:e2:d1:73:76:e0:af:4e:e2:e5:37:a5:
+ 45:2f:8a:23:3e:87:c7:30:e6:31:38:7c:f4:dd:52:ca:f3:53:
+ 04:25:57:56:66:94:e8:0b:ee:e6:03:14:4e:ee:fd:6d:94:64:
+ 9e:5e:ce:79:d4:b2:a6:cf:40:b1:44:a8:3e:87:19:5e:e9:f8:
+ 21:16:59:53
+-----BEGIN CERTIFICATE-----
+MIIETTCCAzWgAwIBAgILBAAAAAABRE7wNjEwDQYJKoZIhvcNAQELBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw
+MDBaFw0yNDAyMjAxMDAwMDBaMEwxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMSIwIAYDVQQDExlBbHBoYVNTTCBDQSAtIFNIQTI1NiAtIEcy
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2gHs5OxzYPt+j2q3xhfj
+kmQy1KwA2aIPue3ua4qGypJn2XTXXUcCPI9A1p5tFM3D2ik5pw8FCmiiZhoexLKL
+dljlq10dj0CzOYvvHoN9ItDjqQAu7FPPYhmFRChMwCfLew7sEGQAEKQFzKByvkFs
+MVtI5LHsuSPrVU3QfWJKpbSlpFmFxSWRpv6mCZ8GEG2PgQxkQF5zAJrgLmWYVBAA
+cJjI4e00X9icxw3A1iNZRfz+VXqG7pRgIvGu0eZVRvaZxRsIdF+ssGSEj4k4HKGn
+kCFPAm694GFn1PhChw8K98kEbSqpL+9Cpd/do1PbmB6B+Zpye1reTz5/olig4het
+ZwIDAQABo4IBIzCCAR8wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwHQYDVR0OBBYEFPXN1TwIUPlqTzq3l9pWg+Zp0mj3MEUGA1UdIAQ+MDwwOgYE
+VR0gADAyMDAGCCsGAQUFBwIBFiRodHRwczovL3d3dy5hbHBoYXNzbC5jb20vcmVw
+b3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWdu
+Lm5ldC9yb290LmNybDA9BggrBgEFBQcBAQQxMC8wLQYIKwYBBQUHMAGGIWh0dHA6
+Ly9vY3NwLmdsb2JhbHNpZ24uY29tL3Jvb3RyMTAfBgNVHSMEGDAWgBRge2YaRQ2X
+yolQL30EzTSo//z9SzANBgkqhkiG9w0BAQsFAAOCAQEAYEBoFkfnFo3bXKFWKsv0
+XJuwHqJL9csCP/gLofKnQtS3TOvjZoDzJUN4LhsXVgdSGMvRqOzm+3M+pGKMgLTS
+xRJzo9P6Aji+Yz2EuJnB8br3n8NA0VgYU8Fi3a8YQn80TsVD1XGwMADH45CuP1eG
+l87qDBKOInDjZqdUfy4oy9RU0LMeYmcI+Sfhy+NmuCQbiWqJRGXy2UzSWByMTsCV
+odTvZy84IOgu/5ZR8LrYPZJwR2UcnnNytGAMXOLRc3bgr07i5TelRS+KIz6HxzDm
+MTh89N1SyvNTBCVXVmaU6Avu5gMUTu79bZRknl7OedSyps9AsUSoPocZXun4IRZZ
+Uw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert11[] = {
+ 0x30, 0x82, 0x04, 0x4d, 0x30, 0x82, 0x03, 0x35, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0,
+ 0x36, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4c, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x41,
+ 0x6c, 0x70, 0x68, 0x61, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d,
+ 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0x01, 0xec,
+ 0xe4, 0xec, 0x73, 0x60, 0xfb, 0x7e, 0x8f, 0x6a, 0xb7, 0xc6, 0x17, 0xe3,
+ 0x92, 0x64, 0x32, 0xd4, 0xac, 0x00, 0xd9, 0xa2, 0x0f, 0xb9, 0xed, 0xee,
+ 0x6b, 0x8a, 0x86, 0xca, 0x92, 0x67, 0xd9, 0x74, 0xd7, 0x5d, 0x47, 0x02,
+ 0x3c, 0x8f, 0x40, 0xd6, 0x9e, 0x6d, 0x14, 0xcd, 0xc3, 0xda, 0x29, 0x39,
+ 0xa7, 0x0f, 0x05, 0x0a, 0x68, 0xa2, 0x66, 0x1a, 0x1e, 0xc4, 0xb2, 0x8b,
+ 0x76, 0x58, 0xe5, 0xab, 0x5d, 0x1d, 0x8f, 0x40, 0xb3, 0x39, 0x8b, 0xef,
+ 0x1e, 0x83, 0x7d, 0x22, 0xd0, 0xe3, 0xa9, 0x00, 0x2e, 0xec, 0x53, 0xcf,
+ 0x62, 0x19, 0x85, 0x44, 0x28, 0x4c, 0xc0, 0x27, 0xcb, 0x7b, 0x0e, 0xec,
+ 0x10, 0x64, 0x00, 0x10, 0xa4, 0x05, 0xcc, 0xa0, 0x72, 0xbe, 0x41, 0x6c,
+ 0x31, 0x5b, 0x48, 0xe4, 0xb1, 0xec, 0xb9, 0x23, 0xeb, 0x55, 0x4d, 0xd0,
+ 0x7d, 0x62, 0x4a, 0xa5, 0xb4, 0xa5, 0xa4, 0x59, 0x85, 0xc5, 0x25, 0x91,
+ 0xa6, 0xfe, 0xa6, 0x09, 0x9f, 0x06, 0x10, 0x6d, 0x8f, 0x81, 0x0c, 0x64,
+ 0x40, 0x5e, 0x73, 0x00, 0x9a, 0xe0, 0x2e, 0x65, 0x98, 0x54, 0x10, 0x00,
+ 0x70, 0x98, 0xc8, 0xe1, 0xed, 0x34, 0x5f, 0xd8, 0x9c, 0xc7, 0x0d, 0xc0,
+ 0xd6, 0x23, 0x59, 0x45, 0xfc, 0xfe, 0x55, 0x7a, 0x86, 0xee, 0x94, 0x60,
+ 0x22, 0xf1, 0xae, 0xd1, 0xe6, 0x55, 0x46, 0xf6, 0x99, 0xc5, 0x1b, 0x08,
+ 0x74, 0x5f, 0xac, 0xb0, 0x64, 0x84, 0x8f, 0x89, 0x38, 0x1c, 0xa1, 0xa7,
+ 0x90, 0x21, 0x4f, 0x02, 0x6e, 0xbd, 0xe0, 0x61, 0x67, 0xd4, 0xf8, 0x42,
+ 0x87, 0x0f, 0x0a, 0xf7, 0xc9, 0x04, 0x6d, 0x2a, 0xa9, 0x2f, 0xef, 0x42,
+ 0xa5, 0xdf, 0xdd, 0xa3, 0x53, 0xdb, 0x98, 0x1e, 0x81, 0xf9, 0x9a, 0x72,
+ 0x7b, 0x5a, 0xde, 0x4f, 0x3e, 0x7f, 0xa2, 0x58, 0xa0, 0xe2, 0x17, 0xad,
+ 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x23, 0x30, 0x82,
+ 0x01, 0x1f, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0xf5, 0xcd, 0xd5, 0x3c, 0x08, 0x50, 0xf9, 0x6a, 0x4f, 0x3a, 0xb7,
+ 0x97, 0xda, 0x56, 0x83, 0xe6, 0x69, 0xd2, 0x68, 0xf7, 0x30, 0x45, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x04,
+ 0x55, 0x1d, 0x20, 0x00, 0x30, 0x32, 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x24, 0x68, 0x74, 0x74, 0x70,
+ 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61, 0x6c, 0x70, 0x68,
+ 0x61, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70,
+ 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0,
+ 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f,
+ 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97,
+ 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd,
+ 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x60, 0x40, 0x68,
+ 0x16, 0x47, 0xe7, 0x16, 0x8d, 0xdb, 0x5c, 0xa1, 0x56, 0x2a, 0xcb, 0xf4,
+ 0x5c, 0x9b, 0xb0, 0x1e, 0xa2, 0x4b, 0xf5, 0xcb, 0x02, 0x3f, 0xf8, 0x0b,
+ 0xa1, 0xf2, 0xa7, 0x42, 0xd4, 0xb7, 0x4c, 0xeb, 0xe3, 0x66, 0x80, 0xf3,
+ 0x25, 0x43, 0x78, 0x2e, 0x1b, 0x17, 0x56, 0x07, 0x52, 0x18, 0xcb, 0xd1,
+ 0xa8, 0xec, 0xe6, 0xfb, 0x73, 0x3e, 0xa4, 0x62, 0x8c, 0x80, 0xb4, 0xd2,
+ 0xc5, 0x12, 0x73, 0xa3, 0xd3, 0xfa, 0x02, 0x38, 0xbe, 0x63, 0x3d, 0x84,
+ 0xb8, 0x99, 0xc1, 0xf1, 0xba, 0xf7, 0x9f, 0xc3, 0x40, 0xd1, 0x58, 0x18,
+ 0x53, 0xc1, 0x62, 0xdd, 0xaf, 0x18, 0x42, 0x7f, 0x34, 0x4e, 0xc5, 0x43,
+ 0xd5, 0x71, 0xb0, 0x30, 0x00, 0xc7, 0xe3, 0x90, 0xae, 0x3f, 0x57, 0x86,
+ 0x97, 0xce, 0xea, 0x0c, 0x12, 0x8e, 0x22, 0x70, 0xe3, 0x66, 0xa7, 0x54,
+ 0x7f, 0x2e, 0x28, 0xcb, 0xd4, 0x54, 0xd0, 0xb3, 0x1e, 0x62, 0x67, 0x08,
+ 0xf9, 0x27, 0xe1, 0xcb, 0xe3, 0x66, 0xb8, 0x24, 0x1b, 0x89, 0x6a, 0x89,
+ 0x44, 0x65, 0xf2, 0xd9, 0x4c, 0xd2, 0x58, 0x1c, 0x8c, 0x4e, 0xc0, 0x95,
+ 0xa1, 0xd4, 0xef, 0x67, 0x2f, 0x38, 0x20, 0xe8, 0x2e, 0xff, 0x96, 0x51,
+ 0xf0, 0xba, 0xd8, 0x3d, 0x92, 0x70, 0x47, 0x65, 0x1c, 0x9e, 0x73, 0x72,
+ 0xb4, 0x60, 0x0c, 0x5c, 0xe2, 0xd1, 0x73, 0x76, 0xe0, 0xaf, 0x4e, 0xe2,
+ 0xe5, 0x37, 0xa5, 0x45, 0x2f, 0x8a, 0x23, 0x3e, 0x87, 0xc7, 0x30, 0xe6,
+ 0x31, 0x38, 0x7c, 0xf4, 0xdd, 0x52, 0xca, 0xf3, 0x53, 0x04, 0x25, 0x57,
+ 0x56, 0x66, 0x94, 0xe8, 0x0b, 0xee, 0xe6, 0x03, 0x14, 0x4e, 0xee, 0xfd,
+ 0x6d, 0x94, 0x64, 0x9e, 0x5e, 0xce, 0x79, 0xd4, 0xb2, 0xa6, 0xcf, 0x40,
+ 0xb1, 0x44, 0xa8, 0x3e, 0x87, 0x19, 0x5e, 0xe9, 0xf8, 0x21, 0x16, 0x59,
+ 0x53,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146031 (0x23a6f)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Nov 5 21:36:50 2013 GMT
+ Not After : May 20 21:36:50 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e3:be:7e:0a:86:a3:cf:6b:6d:3d:2b:a1:97:ad:
+ 49:24:4d:d7:77:b9:34:79:08:a5:9e:a2:9e:de:47:
+ 12:92:3d:7e:ea:19:86:b1:e8:4f:3d:5f:f7:d0:a7:
+ 77:9a:5b:1f:0a:03:b5:19:53:db:a5:21:94:69:63:
+ 9d:6a:4c:91:0c:10:47:be:11:fa:6c:86:25:b7:ab:
+ 04:68:42:38:09:65:f0:14:da:19:9e:fa:6b:0b:ab:
+ 62:ef:8d:a7:ef:63:70:23:a8:af:81:f3:d1:6e:88:
+ 67:53:ec:12:a4:29:75:8a:a7:f2:57:3d:a2:83:98:
+ 97:f2:0a:7d:d4:e7:43:6e:30:78:62:22:59:59:b8:
+ 71:27:45:aa:0f:66:c6:55:3f:fa:32:17:2b:31:8f:
+ 46:a0:fa:69:14:7c:9d:9f:5a:e2:eb:33:4e:10:a6:
+ b3:ed:77:63:d8:c3:9e:f4:dd:df:79:9a:7a:d4:ee:
+ de:dd:9a:cc:c3:b7:a9:5d:cc:11:3a:07:bb:6f:97:
+ a4:01:23:47:95:1f:a3:77:fa:58:92:c6:c7:d0:bd:
+ cf:93:18:42:b7:7e:f7:9e:65:ea:d5:3b:ca:ed:ac:
+ c5:70:a1:fe:d4:10:9a:f0:12:04:44:ac:1a:5b:78:
+ 50:45:57:4c:6f:bd:80:cb:81:5c:2d:b3:bc:76:a1:
+ 1e:65
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ D2:6F:F7:96:F4:85:3F:72:3C:30:7D:23:DA:85:78:9B:A3:7C:5A:7C
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g1.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-539
+ Signature Algorithm: sha256WithRSAEncryption
+ a0:d4:f7:2c:fb:74:0b:7f:64:f1:cd:43:6a:9f:62:53:1c:02:
+ 7c:98:90:a2:ee:4f:68:d4:20:1a:73:12:3e:77:b3:50:eb:72:
+ bc:ee:88:be:7f:17:ea:77:8f:83:61:95:4f:84:a1:cb:32:4f:
+ 6c:21:be:d2:69:96:7d:63:bd:dc:2b:a8:1f:d0:13:84:70:fe:
+ f6:35:95:89:f9:a6:77:b0:46:c8:bb:b7:13:f5:c9:60:69:d6:
+ 4c:fe:d2:8e:ef:d3:60:c1:80:80:e1:e7:fb:8b:6f:21:79:4a:
+ e0:dc:a9:1b:c1:b7:fb:c3:49:59:5c:b5:77:07:44:d4:97:fc:
+ 49:00:89:6f:06:4e:01:70:19:ac:2f:11:c0:e2:e6:0f:2f:86:
+ 4b:8d:7b:c3:b9:a7:2e:f4:f1:ac:16:3e:39:49:51:9e:17:4b:
+ 4f:10:3a:5b:a5:a8:92:6f:fd:fa:d6:0b:03:4d:47:56:57:19:
+ f3:cb:6b:f5:f3:d6:cf:b0:f5:f5:a3:11:d2:20:53:13:34:37:
+ 05:2c:43:5a:63:df:8d:40:d6:85:1e:51:e9:51:17:1e:03:56:
+ c9:f1:30:ad:e7:9b:11:a2:b9:d0:31:81:9b:68:b1:d9:e8:f3:
+ e6:94:7e:c7:ae:13:2f:87:ed:d0:25:b0:68:f9:de:08:5a:f3:
+ 29:cc:d4:92
+-----BEGIN CERTIFICATE-----
+MIIETzCCAzegAwIBAgIDAjpvMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTMxMTA1MjEzNjUwWhcNMjIwNTIwMjEzNjUwWjBEMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
+U1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjvn4K
+hqPPa209K6GXrUkkTdd3uTR5CKWeop7eRxKSPX7qGYax6E89X/fQp3eaWx8KA7UZ
+U9ulIZRpY51qTJEMEEe+EfpshiW3qwRoQjgJZfAU2hme+msLq2LvjafvY3AjqK+B
+89FuiGdT7BKkKXWKp/JXPaKDmJfyCn3U50NuMHhiIllZuHEnRaoPZsZVP/oyFysx
+j0ag+mkUfJ2fWuLrM04QprPtd2PYw5703d95mnrU7t7dmszDt6ldzBE6B7tvl6QB
+I0eVH6N3+liSxsfQvc+TGEK3fveeZerVO8rtrMVwof7UEJrwEgRErBpbeFBFV0xv
+vYDLgVwts7x2oR5lAgMBAAGjggFKMIIBRjAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjAdBgNVHQ4EFgQU0m/3lvSFP3I8MH0j2oV4m6N8WnwwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNgYDVR0fBC8wLTAroCmgJ4Yl
+aHR0cDovL2cxLnN5bWNiLmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAvBggrBgEFBQcB
+AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9nMi5zeW1jYi5jb20wTAYDVR0gBEUw
+QzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1
+c3QuY29tL3Jlc291cmNlcy9jcHMwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVN5
+bWFudGVjUEtJLTEtNTM5MA0GCSqGSIb3DQEBCwUAA4IBAQCg1Pcs+3QLf2TxzUNq
+n2JTHAJ8mJCi7k9o1CAacxI+d7NQ63K87oi+fxfqd4+DYZVPhKHLMk9sIb7SaZZ9
+Y73cK6gf0BOEcP72NZWJ+aZ3sEbIu7cT9clgadZM/tKO79NgwYCA4ef7i28heUrg
+3Kkbwbf7w0lZXLV3B0TUl/xJAIlvBk4BcBmsLxHA4uYPL4ZLjXvDuacu9PGsFj45
+SVGeF0tPEDpbpaiSb/361gsDTUdWVxnzy2v189bPsPX1oxHSIFMTNDcFLENaY9+N
+QNaFHlHpURceA1bJ8TCt55sRornQMYGbaLHZ6PPmlH7HrhMvh+3QJbBo+d4IWvMp
+zNSS
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert12[] = {
+ 0x30, 0x82, 0x04, 0x4f, 0x30, 0x82, 0x03, 0x37, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x6f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31,
+ 0x31, 0x30, 0x35, 0x32, 0x31, 0x33, 0x36, 0x35, 0x30, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x36, 0x35, 0x30,
+ 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe3, 0xbe, 0x7e, 0x0a,
+ 0x86, 0xa3, 0xcf, 0x6b, 0x6d, 0x3d, 0x2b, 0xa1, 0x97, 0xad, 0x49, 0x24,
+ 0x4d, 0xd7, 0x77, 0xb9, 0x34, 0x79, 0x08, 0xa5, 0x9e, 0xa2, 0x9e, 0xde,
+ 0x47, 0x12, 0x92, 0x3d, 0x7e, 0xea, 0x19, 0x86, 0xb1, 0xe8, 0x4f, 0x3d,
+ 0x5f, 0xf7, 0xd0, 0xa7, 0x77, 0x9a, 0x5b, 0x1f, 0x0a, 0x03, 0xb5, 0x19,
+ 0x53, 0xdb, 0xa5, 0x21, 0x94, 0x69, 0x63, 0x9d, 0x6a, 0x4c, 0x91, 0x0c,
+ 0x10, 0x47, 0xbe, 0x11, 0xfa, 0x6c, 0x86, 0x25, 0xb7, 0xab, 0x04, 0x68,
+ 0x42, 0x38, 0x09, 0x65, 0xf0, 0x14, 0xda, 0x19, 0x9e, 0xfa, 0x6b, 0x0b,
+ 0xab, 0x62, 0xef, 0x8d, 0xa7, 0xef, 0x63, 0x70, 0x23, 0xa8, 0xaf, 0x81,
+ 0xf3, 0xd1, 0x6e, 0x88, 0x67, 0x53, 0xec, 0x12, 0xa4, 0x29, 0x75, 0x8a,
+ 0xa7, 0xf2, 0x57, 0x3d, 0xa2, 0x83, 0x98, 0x97, 0xf2, 0x0a, 0x7d, 0xd4,
+ 0xe7, 0x43, 0x6e, 0x30, 0x78, 0x62, 0x22, 0x59, 0x59, 0xb8, 0x71, 0x27,
+ 0x45, 0xaa, 0x0f, 0x66, 0xc6, 0x55, 0x3f, 0xfa, 0x32, 0x17, 0x2b, 0x31,
+ 0x8f, 0x46, 0xa0, 0xfa, 0x69, 0x14, 0x7c, 0x9d, 0x9f, 0x5a, 0xe2, 0xeb,
+ 0x33, 0x4e, 0x10, 0xa6, 0xb3, 0xed, 0x77, 0x63, 0xd8, 0xc3, 0x9e, 0xf4,
+ 0xdd, 0xdf, 0x79, 0x9a, 0x7a, 0xd4, 0xee, 0xde, 0xdd, 0x9a, 0xcc, 0xc3,
+ 0xb7, 0xa9, 0x5d, 0xcc, 0x11, 0x3a, 0x07, 0xbb, 0x6f, 0x97, 0xa4, 0x01,
+ 0x23, 0x47, 0x95, 0x1f, 0xa3, 0x77, 0xfa, 0x58, 0x92, 0xc6, 0xc7, 0xd0,
+ 0xbd, 0xcf, 0x93, 0x18, 0x42, 0xb7, 0x7e, 0xf7, 0x9e, 0x65, 0xea, 0xd5,
+ 0x3b, 0xca, 0xed, 0xac, 0xc5, 0x70, 0xa1, 0xfe, 0xd4, 0x10, 0x9a, 0xf0,
+ 0x12, 0x04, 0x44, 0xac, 0x1a, 0x5b, 0x78, 0x50, 0x45, 0x57, 0x4c, 0x6f,
+ 0xbd, 0x80, 0xcb, 0x81, 0x5c, 0x2d, 0xb3, 0xbc, 0x76, 0xa1, 0x1e, 0x65,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x4a, 0x30, 0x82, 0x01,
+ 0x46, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64,
+ 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xd2, 0x6f, 0xf7,
+ 0x96, 0xf4, 0x85, 0x3f, 0x72, 0x3c, 0x30, 0x7d, 0x23, 0xda, 0x85, 0x78,
+ 0x9b, 0xa3, 0x7c, 0x5a, 0x7c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79,
+ 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73,
+ 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30,
+ 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45,
+ 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03,
+ 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31,
+ 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79,
+ 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d,
+ 0x35, 0x33, 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa0,
+ 0xd4, 0xf7, 0x2c, 0xfb, 0x74, 0x0b, 0x7f, 0x64, 0xf1, 0xcd, 0x43, 0x6a,
+ 0x9f, 0x62, 0x53, 0x1c, 0x02, 0x7c, 0x98, 0x90, 0xa2, 0xee, 0x4f, 0x68,
+ 0xd4, 0x20, 0x1a, 0x73, 0x12, 0x3e, 0x77, 0xb3, 0x50, 0xeb, 0x72, 0xbc,
+ 0xee, 0x88, 0xbe, 0x7f, 0x17, 0xea, 0x77, 0x8f, 0x83, 0x61, 0x95, 0x4f,
+ 0x84, 0xa1, 0xcb, 0x32, 0x4f, 0x6c, 0x21, 0xbe, 0xd2, 0x69, 0x96, 0x7d,
+ 0x63, 0xbd, 0xdc, 0x2b, 0xa8, 0x1f, 0xd0, 0x13, 0x84, 0x70, 0xfe, 0xf6,
+ 0x35, 0x95, 0x89, 0xf9, 0xa6, 0x77, 0xb0, 0x46, 0xc8, 0xbb, 0xb7, 0x13,
+ 0xf5, 0xc9, 0x60, 0x69, 0xd6, 0x4c, 0xfe, 0xd2, 0x8e, 0xef, 0xd3, 0x60,
+ 0xc1, 0x80, 0x80, 0xe1, 0xe7, 0xfb, 0x8b, 0x6f, 0x21, 0x79, 0x4a, 0xe0,
+ 0xdc, 0xa9, 0x1b, 0xc1, 0xb7, 0xfb, 0xc3, 0x49, 0x59, 0x5c, 0xb5, 0x77,
+ 0x07, 0x44, 0xd4, 0x97, 0xfc, 0x49, 0x00, 0x89, 0x6f, 0x06, 0x4e, 0x01,
+ 0x70, 0x19, 0xac, 0x2f, 0x11, 0xc0, 0xe2, 0xe6, 0x0f, 0x2f, 0x86, 0x4b,
+ 0x8d, 0x7b, 0xc3, 0xb9, 0xa7, 0x2e, 0xf4, 0xf1, 0xac, 0x16, 0x3e, 0x39,
+ 0x49, 0x51, 0x9e, 0x17, 0x4b, 0x4f, 0x10, 0x3a, 0x5b, 0xa5, 0xa8, 0x92,
+ 0x6f, 0xfd, 0xfa, 0xd6, 0x0b, 0x03, 0x4d, 0x47, 0x56, 0x57, 0x19, 0xf3,
+ 0xcb, 0x6b, 0xf5, 0xf3, 0xd6, 0xcf, 0xb0, 0xf5, 0xf5, 0xa3, 0x11, 0xd2,
+ 0x20, 0x53, 0x13, 0x34, 0x37, 0x05, 0x2c, 0x43, 0x5a, 0x63, 0xdf, 0x8d,
+ 0x40, 0xd6, 0x85, 0x1e, 0x51, 0xe9, 0x51, 0x17, 0x1e, 0x03, 0x56, 0xc9,
+ 0xf1, 0x30, 0xad, 0xe7, 0x9b, 0x11, 0xa2, 0xb9, 0xd0, 0x31, 0x81, 0x9b,
+ 0x68, 0xb1, 0xd9, 0xe8, 0xf3, 0xe6, 0x94, 0x7e, 0xc7, 0xae, 0x13, 0x2f,
+ 0x87, 0xed, 0xd0, 0x25, 0xb0, 0x68, 0xf9, 0xde, 0x08, 0x5a, 0xf3, 0x29,
+ 0xcc, 0xd4, 0x92,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146019 (0x23a63)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Aug 27 20:40:40 2012 GMT
+ Not After : May 20 20:40:40 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b9:27:f9:4f:d8:f6:b7:15:3f:8f:cd:ce:d6:8d:
+ 1c:6b:fd:7f:da:54:21:4e:03:d8:ca:d0:72:52:15:
+ b8:c9:82:5b:58:79:84:ff:24:72:6f:f2:69:7f:bc:
+ 96:d9:9a:7a:c3:3e:a9:cf:50:22:13:0e:86:19:db:
+ e8:49:ef:8b:e6:d6:47:f2:fd:73:45:08:ae:8f:ac:
+ 5e:b6:f8:9e:7c:f7:10:ff:92:43:66:ef:1c:d4:ee:
+ a1:46:88:11:89:49:79:7a:25:ce:4b:6a:f0:d7:1c:
+ 76:1a:29:3c:c9:e4:fd:1e:85:dc:e0:31:65:05:47:
+ 16:ac:0a:07:4b:2e:70:5e:6b:06:a7:6b:3a:6c:af:
+ 05:12:c4:b2:11:25:d6:3e:97:29:f0:83:6c:57:1c:
+ d8:a5:ef:cc:ec:fd:d6:12:f1:3f:db:40:b4:ae:0f:
+ 18:d3:c5:af:40:92:5d:07:5e:4e:fe:62:17:37:89:
+ e9:8b:74:26:a2:ed:b8:0a:e7:6c:15:5b:35:90:72:
+ dd:d8:4d:21:d4:40:23:5c:8f:ee:80:31:16:ab:68:
+ 55:f4:0e:3b:54:e9:04:4d:f0:cc:4e:81:5e:e9:6f:
+ 52:69:4e:be:a6:16:6d:42:f5:51:ff:e0:0b:56:3c:
+ 98:4f:73:8f:0e:6f:1a:23:f1:c9:c8:d9:df:bc:ec:
+ 52:d7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ 11:4A:D0:73:39:D5:5B:69:08:5C:BA:3D:BF:64:9A:A8:8B:1C:55:BC
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.geotrust.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-254
+ Signature Algorithm: sha1WithRSAEncryption
+ 3c:e5:3d:5a:1b:a2:37:2a:e3:46:cf:36:96:18:3c:7b:f1:84:
+ c5:57:86:77:40:9d:35:f0:12:f0:78:18:fb:22:a4:de:98:4b:
+ 78:81:e6:4d:86:e3:91:0f:42:e3:b9:dc:a0:d6:ff:a9:f8:b1:
+ 79:97:99:d1:c3:6c:42:a5:92:94:e0:5d:0c:33:18:25:c9:2b:
+ 95:53:e0:e5:a9:0c:7d:47:fe:7f:51:31:44:5e:f7:2a:1e:35:
+ a2:94:32:f7:c9:ee:c0:b6:c6:9a:ac:de:99:21:6a:23:a0:38:
+ 64:ee:a3:c4:88:73:32:3b:50:ce:bf:ad:d3:75:1e:a6:f4:e9:
+ f9:42:6b:60:b2:dd:45:fd:5d:57:08:ce:2d:50:e6:12:32:16:
+ 13:8a:f2:94:a2:9b:47:a8:86:7f:d9:98:e5:f7:e5:76:74:64:
+ d8:91:bc:84:16:28:d8:25:44:30:7e:82:d8:ac:b1:e4:c0:e4:
+ 15:6c:db:b6:24:27:02:2a:01:12:85:ba:31:88:58:47:74:e3:
+ b8:d2:64:a6:c3:32:59:2e:29:4b:45:f1:5b:89:49:2e:82:9a:
+ c6:18:15:44:d0:2e:64:01:15:68:38:f9:f6:f9:66:03:0c:55:
+ 1b:9d:bf:00:40:ae:f0:48:27:4c:e0:80:5e:2d:b9:2a:15:7a:
+ bc:66:f8:35
+-----BEGIN CERTIFICATE-----
+MIIEWTCCA0GgAwIBAgIDAjpjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTIwODI3MjA0MDQwWhcNMjIwNTIwMjA0MDQwWjBEMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
+U1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5J/lP
+2Pa3FT+Pzc7WjRxr/X/aVCFOA9jK0HJSFbjJgltYeYT/JHJv8ml/vJbZmnrDPqnP
+UCITDoYZ2+hJ74vm1kfy/XNFCK6PrF62+J589xD/kkNm7xzU7qFGiBGJSXl6Jc5L
+avDXHHYaKTzJ5P0ehdzgMWUFRxasCgdLLnBeawanazpsrwUSxLIRJdY+lynwg2xX
+HNil78zs/dYS8T/bQLSuDxjTxa9Akl0HXk7+Yhc3iemLdCai7bgK52wVWzWQct3Y
+TSHUQCNcj+6AMRaraFX0DjtU6QRN8MxOgV7pb1JpTr6mFm1C9VH/4AtWPJhPc48O
+bxoj8cnI2d+87FLXAgMBAAGjggFUMIIBUDAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjAdBgNVHQ4EFgQUEUrQcznVW2kIXLo9v2SaqIscVbwwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2gK4Yp
+aHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwNAYIKwYB
+BQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nZW90cnVzdC5jb20w
+TAYDVR0gBEUwQzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93
+d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwKgYDVR0RBCMwIaQfMB0xGzAZ
+BgNVBAMTElZlcmlTaWduTVBLSS0yLTI1NDANBgkqhkiG9w0BAQUFAAOCAQEAPOU9
+WhuiNyrjRs82lhg8e/GExVeGd0CdNfAS8HgY+yKk3phLeIHmTYbjkQ9C47ncoNb/
+qfixeZeZ0cNsQqWSlOBdDDMYJckrlVPg5akMfUf+f1ExRF73Kh41opQy98nuwLbG
+mqzemSFqI6A4ZO6jxIhzMjtQzr+t03UepvTp+UJrYLLdRf1dVwjOLVDmEjIWE4ry
+lKKbR6iGf9mY5ffldnRk2JG8hBYo2CVEMH6C2Kyx5MDkFWzbtiQnAioBEoW6MYhY
+R3TjuNJkpsMyWS4pS0XxW4lJLoKaxhgVRNAuZAEVaDj59vlmAwxVG52/AECu8Egn
+TOCAXi25KhV6vGb4NQ==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert13[] = {
+ 0x30, 0x82, 0x04, 0x59, 0x30, 0x82, 0x03, 0x41, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x63, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30,
+ 0x38, 0x32, 0x37, 0x32, 0x30, 0x34, 0x30, 0x34, 0x30, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x30, 0x34, 0x30, 0x34, 0x30,
+ 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x27, 0xf9, 0x4f,
+ 0xd8, 0xf6, 0xb7, 0x15, 0x3f, 0x8f, 0xcd, 0xce, 0xd6, 0x8d, 0x1c, 0x6b,
+ 0xfd, 0x7f, 0xda, 0x54, 0x21, 0x4e, 0x03, 0xd8, 0xca, 0xd0, 0x72, 0x52,
+ 0x15, 0xb8, 0xc9, 0x82, 0x5b, 0x58, 0x79, 0x84, 0xff, 0x24, 0x72, 0x6f,
+ 0xf2, 0x69, 0x7f, 0xbc, 0x96, 0xd9, 0x9a, 0x7a, 0xc3, 0x3e, 0xa9, 0xcf,
+ 0x50, 0x22, 0x13, 0x0e, 0x86, 0x19, 0xdb, 0xe8, 0x49, 0xef, 0x8b, 0xe6,
+ 0xd6, 0x47, 0xf2, 0xfd, 0x73, 0x45, 0x08, 0xae, 0x8f, 0xac, 0x5e, 0xb6,
+ 0xf8, 0x9e, 0x7c, 0xf7, 0x10, 0xff, 0x92, 0x43, 0x66, 0xef, 0x1c, 0xd4,
+ 0xee, 0xa1, 0x46, 0x88, 0x11, 0x89, 0x49, 0x79, 0x7a, 0x25, 0xce, 0x4b,
+ 0x6a, 0xf0, 0xd7, 0x1c, 0x76, 0x1a, 0x29, 0x3c, 0xc9, 0xe4, 0xfd, 0x1e,
+ 0x85, 0xdc, 0xe0, 0x31, 0x65, 0x05, 0x47, 0x16, 0xac, 0x0a, 0x07, 0x4b,
+ 0x2e, 0x70, 0x5e, 0x6b, 0x06, 0xa7, 0x6b, 0x3a, 0x6c, 0xaf, 0x05, 0x12,
+ 0xc4, 0xb2, 0x11, 0x25, 0xd6, 0x3e, 0x97, 0x29, 0xf0, 0x83, 0x6c, 0x57,
+ 0x1c, 0xd8, 0xa5, 0xef, 0xcc, 0xec, 0xfd, 0xd6, 0x12, 0xf1, 0x3f, 0xdb,
+ 0x40, 0xb4, 0xae, 0x0f, 0x18, 0xd3, 0xc5, 0xaf, 0x40, 0x92, 0x5d, 0x07,
+ 0x5e, 0x4e, 0xfe, 0x62, 0x17, 0x37, 0x89, 0xe9, 0x8b, 0x74, 0x26, 0xa2,
+ 0xed, 0xb8, 0x0a, 0xe7, 0x6c, 0x15, 0x5b, 0x35, 0x90, 0x72, 0xdd, 0xd8,
+ 0x4d, 0x21, 0xd4, 0x40, 0x23, 0x5c, 0x8f, 0xee, 0x80, 0x31, 0x16, 0xab,
+ 0x68, 0x55, 0xf4, 0x0e, 0x3b, 0x54, 0xe9, 0x04, 0x4d, 0xf0, 0xcc, 0x4e,
+ 0x81, 0x5e, 0xe9, 0x6f, 0x52, 0x69, 0x4e, 0xbe, 0xa6, 0x16, 0x6d, 0x42,
+ 0xf5, 0x51, 0xff, 0xe0, 0x0b, 0x56, 0x3c, 0x98, 0x4f, 0x73, 0x8f, 0x0e,
+ 0x6f, 0x1a, 0x23, 0xf1, 0xc9, 0xc8, 0xd9, 0xdf, 0xbc, 0xec, 0x52, 0xd7,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x54, 0x30, 0x82, 0x01,
+ 0x50, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64,
+ 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x11, 0x4a, 0xd0,
+ 0x73, 0x39, 0xd5, 0x5b, 0x69, 0x08, 0x5c, 0xba, 0x3d, 0xbf, 0x64, 0x9a,
+ 0xa8, 0x8b, 0x1c, 0x55, 0xbc, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b, 0x86, 0x29,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67,
+ 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67,
+ 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41,
+ 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36,
+ 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11,
+ 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53,
+ 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x32, 0x35,
+ 0x34, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0xe5, 0x3d,
+ 0x5a, 0x1b, 0xa2, 0x37, 0x2a, 0xe3, 0x46, 0xcf, 0x36, 0x96, 0x18, 0x3c,
+ 0x7b, 0xf1, 0x84, 0xc5, 0x57, 0x86, 0x77, 0x40, 0x9d, 0x35, 0xf0, 0x12,
+ 0xf0, 0x78, 0x18, 0xfb, 0x22, 0xa4, 0xde, 0x98, 0x4b, 0x78, 0x81, 0xe6,
+ 0x4d, 0x86, 0xe3, 0x91, 0x0f, 0x42, 0xe3, 0xb9, 0xdc, 0xa0, 0xd6, 0xff,
+ 0xa9, 0xf8, 0xb1, 0x79, 0x97, 0x99, 0xd1, 0xc3, 0x6c, 0x42, 0xa5, 0x92,
+ 0x94, 0xe0, 0x5d, 0x0c, 0x33, 0x18, 0x25, 0xc9, 0x2b, 0x95, 0x53, 0xe0,
+ 0xe5, 0xa9, 0x0c, 0x7d, 0x47, 0xfe, 0x7f, 0x51, 0x31, 0x44, 0x5e, 0xf7,
+ 0x2a, 0x1e, 0x35, 0xa2, 0x94, 0x32, 0xf7, 0xc9, 0xee, 0xc0, 0xb6, 0xc6,
+ 0x9a, 0xac, 0xde, 0x99, 0x21, 0x6a, 0x23, 0xa0, 0x38, 0x64, 0xee, 0xa3,
+ 0xc4, 0x88, 0x73, 0x32, 0x3b, 0x50, 0xce, 0xbf, 0xad, 0xd3, 0x75, 0x1e,
+ 0xa6, 0xf4, 0xe9, 0xf9, 0x42, 0x6b, 0x60, 0xb2, 0xdd, 0x45, 0xfd, 0x5d,
+ 0x57, 0x08, 0xce, 0x2d, 0x50, 0xe6, 0x12, 0x32, 0x16, 0x13, 0x8a, 0xf2,
+ 0x94, 0xa2, 0x9b, 0x47, 0xa8, 0x86, 0x7f, 0xd9, 0x98, 0xe5, 0xf7, 0xe5,
+ 0x76, 0x74, 0x64, 0xd8, 0x91, 0xbc, 0x84, 0x16, 0x28, 0xd8, 0x25, 0x44,
+ 0x30, 0x7e, 0x82, 0xd8, 0xac, 0xb1, 0xe4, 0xc0, 0xe4, 0x15, 0x6c, 0xdb,
+ 0xb6, 0x24, 0x27, 0x02, 0x2a, 0x01, 0x12, 0x85, 0xba, 0x31, 0x88, 0x58,
+ 0x47, 0x74, 0xe3, 0xb8, 0xd2, 0x64, 0xa6, 0xc3, 0x32, 0x59, 0x2e, 0x29,
+ 0x4b, 0x45, 0xf1, 0x5b, 0x89, 0x49, 0x2e, 0x82, 0x9a, 0xc6, 0x18, 0x15,
+ 0x44, 0xd0, 0x2e, 0x64, 0x01, 0x15, 0x68, 0x38, 0xf9, 0xf6, 0xf9, 0x66,
+ 0x03, 0x0c, 0x55, 0x1b, 0x9d, 0xbf, 0x00, 0x40, 0xae, 0xf0, 0x48, 0x27,
+ 0x4c, 0xe0, 0x80, 0x5e, 0x2d, 0xb9, 0x2a, 0x15, 0x7a, 0xbc, 0x66, 0xf8,
+ 0x35,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:44:4e:f0:3e:20
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Feb 20 10:00:00 2014 GMT
+ Not After : Feb 20 10:00:00 2024 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Domain Validation CA - SHA256 - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a9:dd:cc:0e:b3:e2:32:39:dd:49:22:a8:13:69:
+ 93:87:88:e1:0c:ee:71:7d:bd:90:87:96:5d:59:f2:
+ cc:b3:d2:58:57:57:f9:46:ef:6c:26:d8:36:42:8e:
+ 7e:30:b3:2f:9a:3e:53:7b:1f:6e:b6:a2:4c:45:1f:
+ 3c:d3:15:93:1c:89:ed:3c:f4:57:de:ca:bd:ec:06:
+ 9a:6a:2a:a0:19:52:7f:51:d1:74:39:08:9f:ab:eb:
+ d7:86:13:15:97:ae:36:c3:54:66:0e:5a:f2:a0:73:
+ 85:31:e3:b2:64:14:6a:ff:a5:a2:8e:24:bb:bd:85:
+ 52:15:a2:79:ee:f0:b5:ee:3d:b8:f4:7d:80:bc:d9:
+ 90:35:65:b8:17:a9:ad:b3:98:9f:a0:7e:7d:6e:fb:
+ 3f:ad:7c:c2:1b:59:36:96:da:37:32:4b:4b:5d:35:
+ 02:63:8e:db:a7:cf:62:ee:cc:2e:d4:8d:c9:bd:3c:
+ 6a:91:72:a2:22:a7:72:2d:20:d1:fa:ca:37:da:18:
+ 98:e6:16:24:71:25:4b:c4:e5:7b:89:52:09:02:fd:
+ 59:2b:04:6e:ca:07:81:d4:b3:da:da:db:e3:cc:80:
+ a8:56:07:06:7c:96:08:37:9d:db:38:b6:62:34:91:
+ 62:07:74:01:38:d8:72:30:e2:eb:90:71:26:62:c0:
+ 57:f3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ EA:4E:7C:D4:80:2D:E5:15:81:86:26:8C:82:6D:C0:98:A4:CF:97:0F
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha256WithRSAEncryption
+ d7:45:9e:a0:dc:e0:e3:61:5a:0b:7d:77:84:17:2d:65:5a:82:
+ 9a:8d:a3:27:2a:85:f7:c9:ef:e9:86:fd:d4:47:cd:01:52:96:
+ c5:43:bd:37:b1:e1:b8:f2:a9:d2:8a:11:84:71:91:15:89:dc:
+ 02:9d:0b:cb:6c:33:85:34:28:9e:20:b2:b1:97:dc:6d:0b:10:
+ c1:3c:cd:5f:ea:5d:d7:98:31:c5:34:99:5c:00:61:55:c4:1b:
+ 02:5b:c5:e3:89:c8:b4:b8:6f:1e:38:f2:56:26:e9:41:ef:3d:
+ cd:ac:99:4f:59:4a:57:2d:4b:7d:ae:c7:88:fb:d6:98:3b:f5:
+ e5:f0:e8:89:89:b9:8b:03:cb:5a:23:1f:a4:fd:b8:ea:fb:2e:
+ 9d:ae:6a:73:09:bc:fc:d5:a0:b5:44:82:ab:44:91:2e:50:2e:
+ 57:c1:43:d8:91:04:8b:e9:11:2e:5f:b4:3f:79:df:1e:fb:3f:
+ 30:00:8b:53:e3:b7:2c:1d:3b:4d:8b:dc:e4:64:1d:04:58:33:
+ af:1b:55:e7:ab:0c:bf:30:04:74:e4:f3:0e:2f:30:39:8d:4b:
+ 04:8c:1e:75:66:66:49:e0:be:40:34:c7:5c:5a:51:92:ba:12:
+ 3c:52:d5:04:82:55:2d:67:a5:df:b7:95:7c:ee:3f:c3:08:ba:
+ 04:be:c0:46
+-----BEGIN CERTIFICATE-----
+MIIEYzCCA0ugAwIBAgILBAAAAAABRE7wPiAwDQYJKoZIhvcNAQELBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw
+MDBaFw0yNDAyMjAxMDAwMDBaMGAxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMTYwNAYDVQQDEy1HbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0
+aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCp3cwOs+IyOd1JIqgTaZOHiOEM7nF9vZCHll1Z8syz0lhXV/lG72wm2DZC
+jn4wsy+aPlN7H262okxFHzzTFZMcie089Ffeyr3sBppqKqAZUn9R0XQ5CJ+r69eG
+ExWXrjbDVGYOWvKgc4Ux47JkFGr/paKOJLu9hVIVonnu8LXuPbj0fYC82ZA1ZbgX
+qa2zmJ+gfn1u+z+tfMIbWTaW2jcyS0tdNQJjjtunz2LuzC7Ujcm9PGqRcqIip3It
+INH6yjfaGJjmFiRxJUvE5XuJUgkC/VkrBG7KB4HUs9ra2+PMgKhWBwZ8lgg3nds4
+tmI0kWIHdAE42HIw4uuQcSZiwFfzAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMC
+AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU6k581IAt5RWBhiaMgm3A
+mKTPlw8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v
+d3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSG
+Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEE
+MTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290
+cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEL
+BQADggEBANdFnqDc4ONhWgt9d4QXLWVagpqNoycqhffJ7+mG/dRHzQFSlsVDvTex
+4bjyqdKKEYRxkRWJ3AKdC8tsM4U0KJ4gsrGX3G0LEME8zV/qXdeYMcU0mVwAYVXE
+GwJbxeOJyLS4bx448lYm6UHvPc2smU9ZSlctS32ux4j71pg79eXw6ImJuYsDy1oj
+H6T9uOr7Lp2uanMJvPzVoLVEgqtEkS5QLlfBQ9iRBIvpES5ftD953x77PzAAi1Pj
+tywdO02L3ORkHQRYM68bVeerDL8wBHTk8w4vMDmNSwSMHnVmZkngvkA0x1xaUZK6
+EjxS1QSCVS1npd+3lXzuP8MIugS+wEY=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert14[] = {
+ 0x30, 0x82, 0x04, 0x63, 0x30, 0x82, 0x03, 0x4b, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0,
+ 0x3e, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x60, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x44, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41,
+ 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xa9, 0xdd, 0xcc, 0x0e, 0xb3, 0xe2, 0x32,
+ 0x39, 0xdd, 0x49, 0x22, 0xa8, 0x13, 0x69, 0x93, 0x87, 0x88, 0xe1, 0x0c,
+ 0xee, 0x71, 0x7d, 0xbd, 0x90, 0x87, 0x96, 0x5d, 0x59, 0xf2, 0xcc, 0xb3,
+ 0xd2, 0x58, 0x57, 0x57, 0xf9, 0x46, 0xef, 0x6c, 0x26, 0xd8, 0x36, 0x42,
+ 0x8e, 0x7e, 0x30, 0xb3, 0x2f, 0x9a, 0x3e, 0x53, 0x7b, 0x1f, 0x6e, 0xb6,
+ 0xa2, 0x4c, 0x45, 0x1f, 0x3c, 0xd3, 0x15, 0x93, 0x1c, 0x89, 0xed, 0x3c,
+ 0xf4, 0x57, 0xde, 0xca, 0xbd, 0xec, 0x06, 0x9a, 0x6a, 0x2a, 0xa0, 0x19,
+ 0x52, 0x7f, 0x51, 0xd1, 0x74, 0x39, 0x08, 0x9f, 0xab, 0xeb, 0xd7, 0x86,
+ 0x13, 0x15, 0x97, 0xae, 0x36, 0xc3, 0x54, 0x66, 0x0e, 0x5a, 0xf2, 0xa0,
+ 0x73, 0x85, 0x31, 0xe3, 0xb2, 0x64, 0x14, 0x6a, 0xff, 0xa5, 0xa2, 0x8e,
+ 0x24, 0xbb, 0xbd, 0x85, 0x52, 0x15, 0xa2, 0x79, 0xee, 0xf0, 0xb5, 0xee,
+ 0x3d, 0xb8, 0xf4, 0x7d, 0x80, 0xbc, 0xd9, 0x90, 0x35, 0x65, 0xb8, 0x17,
+ 0xa9, 0xad, 0xb3, 0x98, 0x9f, 0xa0, 0x7e, 0x7d, 0x6e, 0xfb, 0x3f, 0xad,
+ 0x7c, 0xc2, 0x1b, 0x59, 0x36, 0x96, 0xda, 0x37, 0x32, 0x4b, 0x4b, 0x5d,
+ 0x35, 0x02, 0x63, 0x8e, 0xdb, 0xa7, 0xcf, 0x62, 0xee, 0xcc, 0x2e, 0xd4,
+ 0x8d, 0xc9, 0xbd, 0x3c, 0x6a, 0x91, 0x72, 0xa2, 0x22, 0xa7, 0x72, 0x2d,
+ 0x20, 0xd1, 0xfa, 0xca, 0x37, 0xda, 0x18, 0x98, 0xe6, 0x16, 0x24, 0x71,
+ 0x25, 0x4b, 0xc4, 0xe5, 0x7b, 0x89, 0x52, 0x09, 0x02, 0xfd, 0x59, 0x2b,
+ 0x04, 0x6e, 0xca, 0x07, 0x81, 0xd4, 0xb3, 0xda, 0xda, 0xdb, 0xe3, 0xcc,
+ 0x80, 0xa8, 0x56, 0x07, 0x06, 0x7c, 0x96, 0x08, 0x37, 0x9d, 0xdb, 0x38,
+ 0xb6, 0x62, 0x34, 0x91, 0x62, 0x07, 0x74, 0x01, 0x38, 0xd8, 0x72, 0x30,
+ 0xe2, 0xeb, 0x90, 0x71, 0x26, 0x62, 0xc0, 0x57, 0xf3, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25, 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xea, 0x4e, 0x7c,
+ 0xd4, 0x80, 0x2d, 0xe5, 0x15, 0x81, 0x86, 0x26, 0x8c, 0x82, 0x6d, 0xc0,
+ 0x98, 0xa4, 0xcf, 0x97, 0x0f, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
+ 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
+ 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74,
+ 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89,
+ 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xd7, 0x45, 0x9e, 0xa0, 0xdc,
+ 0xe0, 0xe3, 0x61, 0x5a, 0x0b, 0x7d, 0x77, 0x84, 0x17, 0x2d, 0x65, 0x5a,
+ 0x82, 0x9a, 0x8d, 0xa3, 0x27, 0x2a, 0x85, 0xf7, 0xc9, 0xef, 0xe9, 0x86,
+ 0xfd, 0xd4, 0x47, 0xcd, 0x01, 0x52, 0x96, 0xc5, 0x43, 0xbd, 0x37, 0xb1,
+ 0xe1, 0xb8, 0xf2, 0xa9, 0xd2, 0x8a, 0x11, 0x84, 0x71, 0x91, 0x15, 0x89,
+ 0xdc, 0x02, 0x9d, 0x0b, 0xcb, 0x6c, 0x33, 0x85, 0x34, 0x28, 0x9e, 0x20,
+ 0xb2, 0xb1, 0x97, 0xdc, 0x6d, 0x0b, 0x10, 0xc1, 0x3c, 0xcd, 0x5f, 0xea,
+ 0x5d, 0xd7, 0x98, 0x31, 0xc5, 0x34, 0x99, 0x5c, 0x00, 0x61, 0x55, 0xc4,
+ 0x1b, 0x02, 0x5b, 0xc5, 0xe3, 0x89, 0xc8, 0xb4, 0xb8, 0x6f, 0x1e, 0x38,
+ 0xf2, 0x56, 0x26, 0xe9, 0x41, 0xef, 0x3d, 0xcd, 0xac, 0x99, 0x4f, 0x59,
+ 0x4a, 0x57, 0x2d, 0x4b, 0x7d, 0xae, 0xc7, 0x88, 0xfb, 0xd6, 0x98, 0x3b,
+ 0xf5, 0xe5, 0xf0, 0xe8, 0x89, 0x89, 0xb9, 0x8b, 0x03, 0xcb, 0x5a, 0x23,
+ 0x1f, 0xa4, 0xfd, 0xb8, 0xea, 0xfb, 0x2e, 0x9d, 0xae, 0x6a, 0x73, 0x09,
+ 0xbc, 0xfc, 0xd5, 0xa0, 0xb5, 0x44, 0x82, 0xab, 0x44, 0x91, 0x2e, 0x50,
+ 0x2e, 0x57, 0xc1, 0x43, 0xd8, 0x91, 0x04, 0x8b, 0xe9, 0x11, 0x2e, 0x5f,
+ 0xb4, 0x3f, 0x79, 0xdf, 0x1e, 0xfb, 0x3f, 0x30, 0x00, 0x8b, 0x53, 0xe3,
+ 0xb7, 0x2c, 0x1d, 0x3b, 0x4d, 0x8b, 0xdc, 0xe4, 0x64, 0x1d, 0x04, 0x58,
+ 0x33, 0xaf, 0x1b, 0x55, 0xe7, 0xab, 0x0c, 0xbf, 0x30, 0x04, 0x74, 0xe4,
+ 0xf3, 0x0e, 0x2f, 0x30, 0x39, 0x8d, 0x4b, 0x04, 0x8c, 0x1e, 0x75, 0x66,
+ 0x66, 0x49, 0xe0, 0xbe, 0x40, 0x34, 0xc7, 0x5c, 0x5a, 0x51, 0x92, 0xba,
+ 0x12, 0x3c, 0x52, 0xd5, 0x04, 0x82, 0x55, 0x2d, 0x67, 0xa5, 0xdf, 0xb7,
+ 0x95, 0x7c, 0xee, 0x3f, 0xc3, 0x08, 0xba, 0x04, 0xbe, 0xc0, 0x46,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:44:4e:f0:42:47
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Feb 20 10:00:00 2014 GMT
+ Not After : Feb 20 10:00:00 2024 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Organization Validation CA - SHA256 - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c7:0e:6c:3f:23:93:7f:cc:70:a5:9d:20:c3:0e:
+ 53:3f:7e:c0:4e:c2:98:49:ca:47:d5:23:ef:03:34:
+ 85:74:c8:a3:02:2e:46:5c:0b:7d:c9:88:9d:4f:8b:
+ f0:f8:9c:6c:8c:55:35:db:bf:f2:b3:ea:fb:e3:56:
+ e7:4a:46:d9:13:22:ca:36:d5:9b:c1:a8:e3:96:43:
+ 93:f2:0c:bc:e6:f9:e6:e8:99:c8:63:48:78:7f:57:
+ 36:69:1a:19:1d:5a:d1:d4:7d:c2:9c:d4:7f:e1:80:
+ 12:ae:7a:ea:88:ea:57:d8:ca:0a:0a:3a:12:49:a2:
+ 62:19:7a:0d:24:f7:37:eb:b4:73:92:7b:05:23:9b:
+ 12:b5:ce:eb:29:df:a4:14:02:b9:01:a5:d4:a6:9c:
+ 43:64:88:de:f8:7e:fe:e3:f5:1e:e5:fe:dc:a3:a8:
+ e4:66:31:d9:4c:25:e9:18:b9:89:59:09:ae:e9:9d:
+ 1c:6d:37:0f:4a:1e:35:20:28:e2:af:d4:21:8b:01:
+ c4:45:ad:6e:2b:63:ab:92:6b:61:0a:4d:20:ed:73:
+ ba:7c:ce:fe:16:b5:db:9f:80:f0:d6:8b:6c:d9:08:
+ 79:4a:4f:78:65:da:92:bc:be:35:f9:b3:c4:f9:27:
+ 80:4e:ff:96:52:e6:02:20:e1:07:73:e9:5d:2b:bd:
+ b2:f1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 96:DE:61:F1:BD:1C:16:29:53:1C:C0:CC:7D:3B:83:00:40:E6:1A:7C
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 46:2a:ee:5e:bd:ae:01:60:37:31:11:86:71:74:b6:46:49:c8:
+ 10:16:fe:2f:62:23:17:ab:1f:87:f8:82:ed:ca:df:0e:2c:df:
+ 64:75:8e:e5:18:72:a7:8c:3a:8b:c9:ac:a5:77:50:f7:ef:9e:
+ a4:e0:a0:8f:14:57:a3:2a:5f:ec:7e:6d:10:e6:ba:8d:b0:08:
+ 87:76:0e:4c:b2:d9:51:bb:11:02:f2:5c:dd:1c:bd:f3:55:96:
+ 0f:d4:06:c0:fc:e2:23:8a:24:70:d3:bb:f0:79:1a:a7:61:70:
+ 83:8a:af:06:c5:20:d8:a1:63:d0:6c:ae:4f:32:d7:ae:7c:18:
+ 45:75:05:29:77:df:42:40:64:64:86:be:2a:76:09:31:6f:1d:
+ 24:f4:99:d0:85:fe:f2:21:08:f9:c6:f6:f1:d0:59:ed:d6:56:
+ 3c:08:28:03:67:ba:f0:f9:f1:90:16:47:ae:67:e6:bc:80:48:
+ e9:42:76:34:97:55:69:24:0e:83:d6:a0:2d:b4:f5:f3:79:8a:
+ 49:28:74:1a:41:a1:c2:d3:24:88:35:30:60:94:17:b4:e1:04:
+ 22:31:3d:3b:2f:17:06:b2:b8:9d:86:2b:5a:69:ef:83:f5:4b:
+ c4:aa:b4:2a:f8:7c:a1:b1:85:94:8c:f4:0c:87:0c:f4:ac:40:
+ f8:59:49:98
+-----BEGIN CERTIFICATE-----
+MIIEaTCCA1GgAwIBAgILBAAAAAABRE7wQkcwDQYJKoZIhvcNAQELBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw
+MDBaFw0yNDAyMjAxMDAwMDBaMGYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMTwwOgYDVQQDEzNHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBW
+YWxpZGF0aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDHDmw/I5N/zHClnSDDDlM/fsBOwphJykfVI+8DNIV0yKMCLkZc
+C33JiJ1Pi/D4nGyMVTXbv/Kz6vvjVudKRtkTIso21ZvBqOOWQ5PyDLzm+ebomchj
+SHh/VzZpGhkdWtHUfcKc1H/hgBKueuqI6lfYygoKOhJJomIZeg0k9zfrtHOSewUj
+mxK1zusp36QUArkBpdSmnENkiN74fv7j9R7l/tyjqORmMdlMJekYuYlZCa7pnRxt
+Nw9KHjUgKOKv1CGLAcRFrW4rY6uSa2EKTSDtc7p8zv4WtdufgPDWi2zZCHlKT3hl
+2pK8vjX5s8T5J4BO/5ZS5gIg4Qdz6V0rvbLxAgMBAAGjggElMIIBITAOBgNVHQ8B
+Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUlt5h8b0cFilT
+HMDMfTuDAEDmGnwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0
+dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCow
+KKAmoCSGImh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYB
+BQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNv
+bS9yb290cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZI
+hvcNAQELBQADggEBAEYq7l69rgFgNzERhnF0tkZJyBAW/i9iIxerH4f4gu3K3w4s
+32R1juUYcqeMOovJrKV3UPfvnqTgoI8UV6MqX+x+bRDmuo2wCId2Dkyy2VG7EQLy
+XN0cvfNVlg/UBsD84iOKJHDTu/B5GqdhcIOKrwbFINihY9Bsrk8y1658GEV1BSl3
+30JAZGSGvip2CTFvHST0mdCF/vIhCPnG9vHQWe3WVjwIKANnuvD58ZAWR65n5ryA
+SOlCdjSXVWkkDoPWoC209fN5ikkodBpBocLTJIg1MGCUF7ThBCIxPTsvFwayuJ2G
+K1pp74P1S8SqtCr4fKGxhZSM9AyHDPSsQPhZSZg=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert15[] = {
+ 0x30, 0x82, 0x04, 0x69, 0x30, 0x82, 0x03, 0x51, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0,
+ 0x42, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x3c, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x33, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x4f, 0x72,
+ 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20,
+ 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc7,
+ 0x0e, 0x6c, 0x3f, 0x23, 0x93, 0x7f, 0xcc, 0x70, 0xa5, 0x9d, 0x20, 0xc3,
+ 0x0e, 0x53, 0x3f, 0x7e, 0xc0, 0x4e, 0xc2, 0x98, 0x49, 0xca, 0x47, 0xd5,
+ 0x23, 0xef, 0x03, 0x34, 0x85, 0x74, 0xc8, 0xa3, 0x02, 0x2e, 0x46, 0x5c,
+ 0x0b, 0x7d, 0xc9, 0x88, 0x9d, 0x4f, 0x8b, 0xf0, 0xf8, 0x9c, 0x6c, 0x8c,
+ 0x55, 0x35, 0xdb, 0xbf, 0xf2, 0xb3, 0xea, 0xfb, 0xe3, 0x56, 0xe7, 0x4a,
+ 0x46, 0xd9, 0x13, 0x22, 0xca, 0x36, 0xd5, 0x9b, 0xc1, 0xa8, 0xe3, 0x96,
+ 0x43, 0x93, 0xf2, 0x0c, 0xbc, 0xe6, 0xf9, 0xe6, 0xe8, 0x99, 0xc8, 0x63,
+ 0x48, 0x78, 0x7f, 0x57, 0x36, 0x69, 0x1a, 0x19, 0x1d, 0x5a, 0xd1, 0xd4,
+ 0x7d, 0xc2, 0x9c, 0xd4, 0x7f, 0xe1, 0x80, 0x12, 0xae, 0x7a, 0xea, 0x88,
+ 0xea, 0x57, 0xd8, 0xca, 0x0a, 0x0a, 0x3a, 0x12, 0x49, 0xa2, 0x62, 0x19,
+ 0x7a, 0x0d, 0x24, 0xf7, 0x37, 0xeb, 0xb4, 0x73, 0x92, 0x7b, 0x05, 0x23,
+ 0x9b, 0x12, 0xb5, 0xce, 0xeb, 0x29, 0xdf, 0xa4, 0x14, 0x02, 0xb9, 0x01,
+ 0xa5, 0xd4, 0xa6, 0x9c, 0x43, 0x64, 0x88, 0xde, 0xf8, 0x7e, 0xfe, 0xe3,
+ 0xf5, 0x1e, 0xe5, 0xfe, 0xdc, 0xa3, 0xa8, 0xe4, 0x66, 0x31, 0xd9, 0x4c,
+ 0x25, 0xe9, 0x18, 0xb9, 0x89, 0x59, 0x09, 0xae, 0xe9, 0x9d, 0x1c, 0x6d,
+ 0x37, 0x0f, 0x4a, 0x1e, 0x35, 0x20, 0x28, 0xe2, 0xaf, 0xd4, 0x21, 0x8b,
+ 0x01, 0xc4, 0x45, 0xad, 0x6e, 0x2b, 0x63, 0xab, 0x92, 0x6b, 0x61, 0x0a,
+ 0x4d, 0x20, 0xed, 0x73, 0xba, 0x7c, 0xce, 0xfe, 0x16, 0xb5, 0xdb, 0x9f,
+ 0x80, 0xf0, 0xd6, 0x8b, 0x6c, 0xd9, 0x08, 0x79, 0x4a, 0x4f, 0x78, 0x65,
+ 0xda, 0x92, 0xbc, 0xbe, 0x35, 0xf9, 0xb3, 0xc4, 0xf9, 0x27, 0x80, 0x4e,
+ 0xff, 0x96, 0x52, 0xe6, 0x02, 0x20, 0xe1, 0x07, 0x73, 0xe9, 0x5d, 0x2b,
+ 0xbd, 0xb2, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25,
+ 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x96, 0xde, 0x61, 0xf1, 0xbd, 0x1c, 0x16, 0x29, 0x53,
+ 0x1c, 0xc0, 0xcc, 0x7d, 0x3b, 0x83, 0x00, 0x40, 0xe6, 0x1a, 0x7c, 0x30,
+ 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30,
+ 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66,
+ 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34,
+ 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x46, 0x2a, 0xee, 0x5e, 0xbd, 0xae, 0x01, 0x60, 0x37, 0x31, 0x11,
+ 0x86, 0x71, 0x74, 0xb6, 0x46, 0x49, 0xc8, 0x10, 0x16, 0xfe, 0x2f, 0x62,
+ 0x23, 0x17, 0xab, 0x1f, 0x87, 0xf8, 0x82, 0xed, 0xca, 0xdf, 0x0e, 0x2c,
+ 0xdf, 0x64, 0x75, 0x8e, 0xe5, 0x18, 0x72, 0xa7, 0x8c, 0x3a, 0x8b, 0xc9,
+ 0xac, 0xa5, 0x77, 0x50, 0xf7, 0xef, 0x9e, 0xa4, 0xe0, 0xa0, 0x8f, 0x14,
+ 0x57, 0xa3, 0x2a, 0x5f, 0xec, 0x7e, 0x6d, 0x10, 0xe6, 0xba, 0x8d, 0xb0,
+ 0x08, 0x87, 0x76, 0x0e, 0x4c, 0xb2, 0xd9, 0x51, 0xbb, 0x11, 0x02, 0xf2,
+ 0x5c, 0xdd, 0x1c, 0xbd, 0xf3, 0x55, 0x96, 0x0f, 0xd4, 0x06, 0xc0, 0xfc,
+ 0xe2, 0x23, 0x8a, 0x24, 0x70, 0xd3, 0xbb, 0xf0, 0x79, 0x1a, 0xa7, 0x61,
+ 0x70, 0x83, 0x8a, 0xaf, 0x06, 0xc5, 0x20, 0xd8, 0xa1, 0x63, 0xd0, 0x6c,
+ 0xae, 0x4f, 0x32, 0xd7, 0xae, 0x7c, 0x18, 0x45, 0x75, 0x05, 0x29, 0x77,
+ 0xdf, 0x42, 0x40, 0x64, 0x64, 0x86, 0xbe, 0x2a, 0x76, 0x09, 0x31, 0x6f,
+ 0x1d, 0x24, 0xf4, 0x99, 0xd0, 0x85, 0xfe, 0xf2, 0x21, 0x08, 0xf9, 0xc6,
+ 0xf6, 0xf1, 0xd0, 0x59, 0xed, 0xd6, 0x56, 0x3c, 0x08, 0x28, 0x03, 0x67,
+ 0xba, 0xf0, 0xf9, 0xf1, 0x90, 0x16, 0x47, 0xae, 0x67, 0xe6, 0xbc, 0x80,
+ 0x48, 0xe9, 0x42, 0x76, 0x34, 0x97, 0x55, 0x69, 0x24, 0x0e, 0x83, 0xd6,
+ 0xa0, 0x2d, 0xb4, 0xf5, 0xf3, 0x79, 0x8a, 0x49, 0x28, 0x74, 0x1a, 0x41,
+ 0xa1, 0xc2, 0xd3, 0x24, 0x88, 0x35, 0x30, 0x60, 0x94, 0x17, 0xb4, 0xe1,
+ 0x04, 0x22, 0x31, 0x3d, 0x3b, 0x2f, 0x17, 0x06, 0xb2, 0xb8, 0x9d, 0x86,
+ 0x2b, 0x5a, 0x69, 0xef, 0x83, 0xf5, 0x4b, 0xc4, 0xaa, 0xb4, 0x2a, 0xf8,
+ 0x7c, 0xa1, 0xb1, 0x85, 0x94, 0x8c, 0xf4, 0x0c, 0x87, 0x0c, 0xf4, 0xac,
+ 0x40, 0xf8, 0x59, 0x49, 0x98,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4d:5f:2c:34:08:b2:4c:20:cd:6d:50:7e:24:4d:c9:ec
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Feb 8 00:00:00 2010 GMT
+ Not After : Feb 7 23:59:59 2020 GMT
+ Subject: C=US, O=Thawte, Inc., CN=Thawte SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:99:e4:85:5b:76:49:7d:2f:05:d8:c5:ac:c8:c8:
+ a9:d3:dc:98:e6:d7:34:a6:2f:0c:f2:22:26:d8:a3:
+ c9:14:4c:8f:05:a4:45:e8:14:0c:58:90:05:1a:b7:
+ c5:c1:06:a5:80:af:bb:1d:49:6b:52:34:88:c3:59:
+ e7:ef:6b:c4:27:41:8c:2b:66:1d:d0:e0:a3:97:98:
+ 19:34:4b:41:d5:98:d5:c7:05:ad:a2:e4:d7:ed:0c:
+ ad:4f:c1:b5:b0:21:fd:3e:50:53:b2:c4:90:d0:d4:
+ 30:67:6c:9a:f1:0e:74:c4:c2:dc:8a:e8:97:ff:c9:
+ 92:ae:01:8a:56:0a:98:32:b0:00:23:ec:90:1a:60:
+ c3:ed:bb:3a:cb:0f:63:9f:0d:44:c9:52:e1:25:96:
+ bf:ed:50:95:89:7f:56:14:b1:b7:61:1d:1c:07:8c:
+ 3a:2c:f7:ff:80:de:39:45:d5:af:1a:d1:78:d8:c7:
+ 71:6a:a3:19:a7:32:50:21:e9:f2:0e:a1:c6:13:03:
+ 44:48:d1:66:a8:52:57:d7:11:b4:93:8b:e5:99:9f:
+ 5d:e7:78:51:e5:4d:f6:b7:59:b4:76:b5:09:37:4d:
+ 06:38:13:7a:1c:08:98:5c:c4:48:4a:cb:52:a0:a9:
+ f8:b1:9d:8e:7b:79:b0:20:2f:3c:96:a8:11:62:47:
+ bb:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.thawte.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePCA.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-9
+ X509v3 Subject Key Identifier:
+ A7:A2:83:BB:34:45:40:3D:FC:D5:30:4F:12:B9:3E:A1:01:9F:F6:DB
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 80:22:80:e0:6c:c8:95:16:d7:57:26:87:f3:72:34:db:c6:72:
+ 56:27:3e:d3:96:f6:2e:25:91:a5:3e:33:97:a7:4b:e5:2f:fb:
+ 25:7d:2f:07:61:fa:6f:83:74:4c:4c:53:72:20:a4:7a:cf:51:
+ 51:56:81:88:b0:6d:1f:36:2c:c8:2b:b1:88:99:c1:fe:44:ab:
+ 48:51:7c:d8:f2:44:64:2a:d8:71:a7:fb:1a:2f:f9:19:8d:34:
+ b2:23:bf:c4:4c:55:1d:8e:44:e8:aa:5d:9a:dd:9f:fd:03:c7:
+ ba:24:43:8d:2d:47:44:db:f6:d8:98:c8:b2:f9:da:ef:ed:29:
+ 5c:69:12:fa:d1:23:96:0f:bf:9c:0d:f2:79:45:53:37:9a:56:
+ 2f:e8:57:10:70:f6:ee:89:0c:49:89:9a:c1:23:f5:c2:2a:cc:
+ 41:cf:22:ab:65:6e:b7:94:82:6d:2f:40:5f:58:de:eb:95:2b:
+ a6:72:68:52:19:91:2a:ae:75:9d:4e:92:e6:ca:de:54:ea:18:
+ ab:25:3c:e6:64:a6:79:1f:26:7d:61:ed:7d:d2:e5:71:55:d8:
+ 93:17:7c:14:38:30:3c:df:86:e3:4c:ad:49:e3:97:59:ce:1b:
+ 9b:2b:ce:dc:65:d4:0b:28:6b:4e:84:46:51:44:f7:33:08:2d:
+ 58:97:21:ae
+-----BEGIN CERTIFICATE-----
+MIIEbDCCA1SgAwIBAgIQTV8sNAiyTCDNbVB+JE3J7DANBgkqhkiG9w0BAQUFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTAwMjA4MDAwMDAwWhcNMjAw
+MjA3MjM1OTU5WjA8MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMu
+MRYwFAYDVQQDEw1UaGF3dGUgU1NMIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAmeSFW3ZJfS8F2MWsyMip09yY5tc0pi8M8iIm2KPJFEyPBaRF6BQM
+WJAFGrfFwQalgK+7HUlrUjSIw1nn72vEJ0GMK2Yd0OCjl5gZNEtB1ZjVxwWtouTX
+7QytT8G1sCH9PlBTssSQ0NQwZ2ya8Q50xMLciuiX/8mSrgGKVgqYMrAAI+yQGmDD
+7bs6yw9jnw1EyVLhJZa/7VCViX9WFLG3YR0cB4w6LPf/gN45RdWvGtF42MdxaqMZ
+pzJQIenyDqHGEwNESNFmqFJX1xG0k4vlmZ9d53hR5U32t1m0drUJN00GOBN6HAiY
+XMRISstSoKn4sZ2Oe3mwIC88lqgRYke7EQIDAQABo4H7MIH4MDIGCCsGAQUFBwEB
+BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTASBgNVHRMB
+Af8ECDAGAQH/AgEAMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudGhhd3Rl
+LmNvbS9UaGF3dGVQQ0EuY3JsMA4GA1UdDwEB/wQEAwIBBjAoBgNVHREEITAfpB0w
+GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItOTAdBgNVHQ4EFgQUp6KDuzRFQD38
+1TBPErk+oQGf9tswHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutXSFAwDQYJ
+KoZIhvcNAQEFBQADggEBAIAigOBsyJUW11cmh/NyNNvGclYnPtOW9i4lkaU+M5en
+S+Uv+yV9Lwdh+m+DdExMU3IgpHrPUVFWgYiwbR82LMgrsYiZwf5Eq0hRfNjyRGQq
+2HGn+xov+RmNNLIjv8RMVR2OROiqXZrdn/0Dx7okQ40tR0Tb9tiYyLL52u/tKVxp
+EvrRI5YPv5wN8nlFUzeaVi/oVxBw9u6JDEmJmsEj9cIqzEHPIqtlbreUgm0vQF9Y
+3uuVK6ZyaFIZkSqudZ1OkubK3lTqGKslPOZkpnkfJn1h7X3S5XFV2JMXfBQ4MDzf
+huNMrUnjl1nOG5srztxl1Asoa06ERlFE9zMILViXIa4=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert16[] = {
+ 0x30, 0x82, 0x04, 0x6c, 0x30, 0x82, 0x03, 0x54, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x4d, 0x5f, 0x2c, 0x34, 0x08, 0xb2, 0x4c, 0x20, 0xcd,
+ 0x6d, 0x50, 0x7e, 0x24, 0x4d, 0xc9, 0xec, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30,
+ 0x32, 0x30, 0x37, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x3c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0d, 0x54,
+ 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x99, 0xe4, 0x85,
+ 0x5b, 0x76, 0x49, 0x7d, 0x2f, 0x05, 0xd8, 0xc5, 0xac, 0xc8, 0xc8, 0xa9,
+ 0xd3, 0xdc, 0x98, 0xe6, 0xd7, 0x34, 0xa6, 0x2f, 0x0c, 0xf2, 0x22, 0x26,
+ 0xd8, 0xa3, 0xc9, 0x14, 0x4c, 0x8f, 0x05, 0xa4, 0x45, 0xe8, 0x14, 0x0c,
+ 0x58, 0x90, 0x05, 0x1a, 0xb7, 0xc5, 0xc1, 0x06, 0xa5, 0x80, 0xaf, 0xbb,
+ 0x1d, 0x49, 0x6b, 0x52, 0x34, 0x88, 0xc3, 0x59, 0xe7, 0xef, 0x6b, 0xc4,
+ 0x27, 0x41, 0x8c, 0x2b, 0x66, 0x1d, 0xd0, 0xe0, 0xa3, 0x97, 0x98, 0x19,
+ 0x34, 0x4b, 0x41, 0xd5, 0x98, 0xd5, 0xc7, 0x05, 0xad, 0xa2, 0xe4, 0xd7,
+ 0xed, 0x0c, 0xad, 0x4f, 0xc1, 0xb5, 0xb0, 0x21, 0xfd, 0x3e, 0x50, 0x53,
+ 0xb2, 0xc4, 0x90, 0xd0, 0xd4, 0x30, 0x67, 0x6c, 0x9a, 0xf1, 0x0e, 0x74,
+ 0xc4, 0xc2, 0xdc, 0x8a, 0xe8, 0x97, 0xff, 0xc9, 0x92, 0xae, 0x01, 0x8a,
+ 0x56, 0x0a, 0x98, 0x32, 0xb0, 0x00, 0x23, 0xec, 0x90, 0x1a, 0x60, 0xc3,
+ 0xed, 0xbb, 0x3a, 0xcb, 0x0f, 0x63, 0x9f, 0x0d, 0x44, 0xc9, 0x52, 0xe1,
+ 0x25, 0x96, 0xbf, 0xed, 0x50, 0x95, 0x89, 0x7f, 0x56, 0x14, 0xb1, 0xb7,
+ 0x61, 0x1d, 0x1c, 0x07, 0x8c, 0x3a, 0x2c, 0xf7, 0xff, 0x80, 0xde, 0x39,
+ 0x45, 0xd5, 0xaf, 0x1a, 0xd1, 0x78, 0xd8, 0xc7, 0x71, 0x6a, 0xa3, 0x19,
+ 0xa7, 0x32, 0x50, 0x21, 0xe9, 0xf2, 0x0e, 0xa1, 0xc6, 0x13, 0x03, 0x44,
+ 0x48, 0xd1, 0x66, 0xa8, 0x52, 0x57, 0xd7, 0x11, 0xb4, 0x93, 0x8b, 0xe5,
+ 0x99, 0x9f, 0x5d, 0xe7, 0x78, 0x51, 0xe5, 0x4d, 0xf6, 0xb7, 0x59, 0xb4,
+ 0x76, 0xb5, 0x09, 0x37, 0x4d, 0x06, 0x38, 0x13, 0x7a, 0x1c, 0x08, 0x98,
+ 0x5c, 0xc4, 0x48, 0x4a, 0xcb, 0x52, 0xa0, 0xa9, 0xf8, 0xb1, 0x9d, 0x8e,
+ 0x7b, 0x79, 0xb0, 0x20, 0x2f, 0x3c, 0x96, 0xa8, 0x11, 0x62, 0x47, 0xbb,
+ 0x11, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xfb, 0x30, 0x81, 0xf8,
+ 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30,
+ 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50,
+ 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x28,
+ 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, 0x30,
+ 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49,
+ 0x2d, 0x32, 0x2d, 0x39, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0xa7, 0xa2, 0x83, 0xbb, 0x34, 0x45, 0x40, 0x3d, 0xfc,
+ 0xd5, 0x30, 0x4f, 0x12, 0xb9, 0x3e, 0xa1, 0x01, 0x9f, 0xf6, 0xdb, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a,
+ 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x80, 0x22, 0x80, 0xe0, 0x6c, 0xc8, 0x95, 0x16,
+ 0xd7, 0x57, 0x26, 0x87, 0xf3, 0x72, 0x34, 0xdb, 0xc6, 0x72, 0x56, 0x27,
+ 0x3e, 0xd3, 0x96, 0xf6, 0x2e, 0x25, 0x91, 0xa5, 0x3e, 0x33, 0x97, 0xa7,
+ 0x4b, 0xe5, 0x2f, 0xfb, 0x25, 0x7d, 0x2f, 0x07, 0x61, 0xfa, 0x6f, 0x83,
+ 0x74, 0x4c, 0x4c, 0x53, 0x72, 0x20, 0xa4, 0x7a, 0xcf, 0x51, 0x51, 0x56,
+ 0x81, 0x88, 0xb0, 0x6d, 0x1f, 0x36, 0x2c, 0xc8, 0x2b, 0xb1, 0x88, 0x99,
+ 0xc1, 0xfe, 0x44, 0xab, 0x48, 0x51, 0x7c, 0xd8, 0xf2, 0x44, 0x64, 0x2a,
+ 0xd8, 0x71, 0xa7, 0xfb, 0x1a, 0x2f, 0xf9, 0x19, 0x8d, 0x34, 0xb2, 0x23,
+ 0xbf, 0xc4, 0x4c, 0x55, 0x1d, 0x8e, 0x44, 0xe8, 0xaa, 0x5d, 0x9a, 0xdd,
+ 0x9f, 0xfd, 0x03, 0xc7, 0xba, 0x24, 0x43, 0x8d, 0x2d, 0x47, 0x44, 0xdb,
+ 0xf6, 0xd8, 0x98, 0xc8, 0xb2, 0xf9, 0xda, 0xef, 0xed, 0x29, 0x5c, 0x69,
+ 0x12, 0xfa, 0xd1, 0x23, 0x96, 0x0f, 0xbf, 0x9c, 0x0d, 0xf2, 0x79, 0x45,
+ 0x53, 0x37, 0x9a, 0x56, 0x2f, 0xe8, 0x57, 0x10, 0x70, 0xf6, 0xee, 0x89,
+ 0x0c, 0x49, 0x89, 0x9a, 0xc1, 0x23, 0xf5, 0xc2, 0x2a, 0xcc, 0x41, 0xcf,
+ 0x22, 0xab, 0x65, 0x6e, 0xb7, 0x94, 0x82, 0x6d, 0x2f, 0x40, 0x5f, 0x58,
+ 0xde, 0xeb, 0x95, 0x2b, 0xa6, 0x72, 0x68, 0x52, 0x19, 0x91, 0x2a, 0xae,
+ 0x75, 0x9d, 0x4e, 0x92, 0xe6, 0xca, 0xde, 0x54, 0xea, 0x18, 0xab, 0x25,
+ 0x3c, 0xe6, 0x64, 0xa6, 0x79, 0x1f, 0x26, 0x7d, 0x61, 0xed, 0x7d, 0xd2,
+ 0xe5, 0x71, 0x55, 0xd8, 0x93, 0x17, 0x7c, 0x14, 0x38, 0x30, 0x3c, 0xdf,
+ 0x86, 0xe3, 0x4c, 0xad, 0x49, 0xe3, 0x97, 0x59, 0xce, 0x1b, 0x9b, 0x2b,
+ 0xce, 0xdc, 0x65, 0xd4, 0x0b, 0x28, 0x6b, 0x4e, 0x84, 0x46, 0x51, 0x44,
+ 0xf7, 0x33, 0x08, 0x2d, 0x58, 0x97, 0x21, 0xae,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 6e:8a:90:eb:cf:f0:44:8a:72:0d:08:05:d0:82:a5:44
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust EV SSL CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d9:b4:05:f2:38:67:0f:09:e7:7c:f5:63:2a:e5:
+ b9:5e:a8:11:ae:75:71:d9:4c:84:67:ad:89:5d:fc:
+ 28:3d:2a:b0:a5:d5:d4:e6:30:0a:84:d4:e4:18:cb:
+ 85:37:c5:46:71:eb:1c:7b:69:db:65:69:8c:30:05:
+ 3e:07:e1:6f:3c:c1:0b:61:e6:38:44:fc:bc:8c:2f:
+ 4e:75:57:f5:96:99:7c:3e:87:1f:0f:90:4b:70:c3:
+ 3f:39:45:3b:3a:6b:cb:bb:7b:40:54:d1:8b:4b:a1:
+ 72:d2:04:e9:e0:72:1a:93:11:7a:2f:f1:ab:9d:9c:
+ 98:58:ae:2c:ea:77:5f:2f:2e:87:af:b8:6b:e3:e2:
+ e2:3f:d6:3d:e0:96:44:df:11:55:63:52:2f:f4:26:
+ 78:c4:0f:20:4d:0a:c0:68:70:15:86:38:ee:b7:76:
+ 88:ab:18:8f:4f:35:1e:d4:8c:c9:db:7e:3d:44:d4:
+ 36:8c:c1:37:b5:59:5b:87:f9:e9:f1:d4:c5:28:bd:
+ 1d:dc:cc:96:72:d1:7a:a1:a7:20:b5:b8:af:f8:6e:
+ a5:60:7b:2b:8d:1f:ee:f4:2b:d6:69:cd:af:ca:80:
+ 58:29:e8:4c:00:20:8a:49:0a:6e:8e:8c:a8:d1:00:
+ 12:84:b6:c5:e2:95:a2:c0:3b:a4:6b:f0:82:d0:96:
+ 5d:25
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://g2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.geotrust.com/resources/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g1.symcb.com/GeoTrustPCA.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-538
+ X509v3 Subject Key Identifier:
+ DE:CF:5C:50:B7:AE:02:1F:15:17:AA:16:E8:0D:B5:28:9D:6A:5A:F3
+ X509v3 Authority Key Identifier:
+ keyid:2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92
+
+ Signature Algorithm: sha256WithRSAEncryption
+ b4:8e:bd:07:b9:9a:85:ec:3b:67:bd:07:60:61:e6:84:d1:d4:
+ ef:eb:1b:ba:0b:82:4b:95:64:b6:66:53:23:bd:b7:84:dd:e4:
+ 7b:8d:09:da:cf:b2:f5:f1:c3:bf:87:84:be:4e:a6:a8:c2:e7:
+ 12:39:28:34:e0:a4:56:44:40:0c:9f:88:a3:15:d3:e8:d3:5e:
+ e3:1c:04:60:fb:69:36:4f:6a:7e:0c:2a:28:c1:f3:aa:58:0e:
+ 6c:ce:1d:07:c3:4a:c0:9c:8d:c3:74:b1:ae:82:f0:1a:e1:f9:
+ 4e:29:bd:46:de:b7:1d:f9:7d:db:d9:0f:84:cb:92:45:cc:1c:
+ b3:18:f6:a0:cf:71:6f:0c:2e:9b:d2:2d:b3:99:93:83:44:ac:
+ 15:aa:9b:2e:67:ec:4f:88:69:05:56:7b:8b:b2:43:a9:3a:6c:
+ 1c:13:33:25:1b:fd:a8:c8:57:02:fb:1c:e0:d1:bd:3b:56:44:
+ 65:c3:63:f5:1b:ef:ec:30:d9:e3:6e:2e:13:e9:39:08:2a:0c:
+ 72:f3:9a:cc:f6:27:29:84:d3:ef:4c:c7:84:11:65:1f:c6:e3:
+ 81:03:db:87:cc:78:f7:b5:9d:96:3e:6a:7f:bc:11:85:7a:75:
+ e6:41:7d:0d:cf:f9:e5:85:69:25:8f:c7:8d:07:2d:f8:69:0f:
+ cb:41:53:00
+-----BEGIN CERTIFICATE-----
+MIIEbjCCA1agAwIBAgIQboqQ68/wRIpyDQgF0IKlRDANBgkqhkiG9w0BAQsFADBY
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
+R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMzEw
+MzEwMDAwMDBaFw0yMzEwMzAyMzU5NTlaMEcxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMSAwHgYDVQQDExdHZW9UcnVzdCBFViBTU0wgQ0EgLSBH
+NDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANm0BfI4Zw8J53z1Yyrl
+uV6oEa51cdlMhGetiV38KD0qsKXV1OYwCoTU5BjLhTfFRnHrHHtp22VpjDAFPgfh
+bzzBC2HmOET8vIwvTnVX9ZaZfD6HHw+QS3DDPzlFOzpry7t7QFTRi0uhctIE6eBy
+GpMRei/xq52cmFiuLOp3Xy8uh6+4a+Pi4j/WPeCWRN8RVWNSL/QmeMQPIE0KwGhw
+FYY47rd2iKsYj081HtSMydt+PUTUNozBN7VZW4f56fHUxSi9HdzMlnLReqGnILW4
+r/hupWB7K40f7vQr1mnNr8qAWCnoTAAgikkKbo6MqNEAEoS2xeKVosA7pGvwgtCW
+XSUCAwEAAaOCAUMwggE/MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQD
+AgEGMC8GCCsGAQUFBwEBBCMwITAfBggrBgEFBQcwAYYTaHR0cDovL2cyLnN5bWNi
+LmNvbTBHBgNVHSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93
+d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwNAYDVR0fBC0wKzApoCegJYYj
+aHR0cDovL2cxLnN5bWNiLmNvbS9HZW9UcnVzdFBDQS5jcmwwKQYDVR0RBCIwIKQe
+MBwxGjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTM4MB0GA1UdDgQWBBTez1xQt64C
+HxUXqhboDbUonWpa8zAfBgNVHSMEGDAWgBQs1VBBlxWL8I82YVtK+2vZmckzkjAN
+BgkqhkiG9w0BAQsFAAOCAQEAtI69B7mahew7Z70HYGHmhNHU7+sbuguCS5VktmZT
+I723hN3ke40J2s+y9fHDv4eEvk6mqMLnEjkoNOCkVkRADJ+IoxXT6NNe4xwEYPtp
+Nk9qfgwqKMHzqlgObM4dB8NKwJyNw3SxroLwGuH5Tim9Rt63Hfl929kPhMuSRcwc
+sxj2oM9xbwwum9Its5mTg0SsFaqbLmfsT4hpBVZ7i7JDqTpsHBMzJRv9qMhXAvsc
+4NG9O1ZEZcNj9Rvv7DDZ424uE+k5CCoMcvOazPYnKYTT70zHhBFlH8bjgQPbh8x4
+97Wdlj5qf7wRhXp15kF9Dc/55YVpJY/HjQct+GkPy0FTAA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert17[] = {
+ 0x30, 0x82, 0x04, 0x6e, 0x30, 0x82, 0x03, 0x56, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x6e, 0x8a, 0x90, 0xeb, 0xcf, 0xf0, 0x44, 0x8a, 0x72,
+ 0x0d, 0x08, 0x05, 0xd0, 0x82, 0xa5, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x58,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69,
+ 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30,
+ 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32,
+ 0x33, 0x31, 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+ 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x17, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47,
+ 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+ 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd9, 0xb4,
+ 0x05, 0xf2, 0x38, 0x67, 0x0f, 0x09, 0xe7, 0x7c, 0xf5, 0x63, 0x2a, 0xe5,
+ 0xb9, 0x5e, 0xa8, 0x11, 0xae, 0x75, 0x71, 0xd9, 0x4c, 0x84, 0x67, 0xad,
+ 0x89, 0x5d, 0xfc, 0x28, 0x3d, 0x2a, 0xb0, 0xa5, 0xd5, 0xd4, 0xe6, 0x30,
+ 0x0a, 0x84, 0xd4, 0xe4, 0x18, 0xcb, 0x85, 0x37, 0xc5, 0x46, 0x71, 0xeb,
+ 0x1c, 0x7b, 0x69, 0xdb, 0x65, 0x69, 0x8c, 0x30, 0x05, 0x3e, 0x07, 0xe1,
+ 0x6f, 0x3c, 0xc1, 0x0b, 0x61, 0xe6, 0x38, 0x44, 0xfc, 0xbc, 0x8c, 0x2f,
+ 0x4e, 0x75, 0x57, 0xf5, 0x96, 0x99, 0x7c, 0x3e, 0x87, 0x1f, 0x0f, 0x90,
+ 0x4b, 0x70, 0xc3, 0x3f, 0x39, 0x45, 0x3b, 0x3a, 0x6b, 0xcb, 0xbb, 0x7b,
+ 0x40, 0x54, 0xd1, 0x8b, 0x4b, 0xa1, 0x72, 0xd2, 0x04, 0xe9, 0xe0, 0x72,
+ 0x1a, 0x93, 0x11, 0x7a, 0x2f, 0xf1, 0xab, 0x9d, 0x9c, 0x98, 0x58, 0xae,
+ 0x2c, 0xea, 0x77, 0x5f, 0x2f, 0x2e, 0x87, 0xaf, 0xb8, 0x6b, 0xe3, 0xe2,
+ 0xe2, 0x3f, 0xd6, 0x3d, 0xe0, 0x96, 0x44, 0xdf, 0x11, 0x55, 0x63, 0x52,
+ 0x2f, 0xf4, 0x26, 0x78, 0xc4, 0x0f, 0x20, 0x4d, 0x0a, 0xc0, 0x68, 0x70,
+ 0x15, 0x86, 0x38, 0xee, 0xb7, 0x76, 0x88, 0xab, 0x18, 0x8f, 0x4f, 0x35,
+ 0x1e, 0xd4, 0x8c, 0xc9, 0xdb, 0x7e, 0x3d, 0x44, 0xd4, 0x36, 0x8c, 0xc1,
+ 0x37, 0xb5, 0x59, 0x5b, 0x87, 0xf9, 0xe9, 0xf1, 0xd4, 0xc5, 0x28, 0xbd,
+ 0x1d, 0xdc, 0xcc, 0x96, 0x72, 0xd1, 0x7a, 0xa1, 0xa7, 0x20, 0xb5, 0xb8,
+ 0xaf, 0xf8, 0x6e, 0xa5, 0x60, 0x7b, 0x2b, 0x8d, 0x1f, 0xee, 0xf4, 0x2b,
+ 0xd6, 0x69, 0xcd, 0xaf, 0xca, 0x80, 0x58, 0x29, 0xe8, 0x4c, 0x00, 0x20,
+ 0x8a, 0x49, 0x0a, 0x6e, 0x8e, 0x8c, 0xa8, 0xd1, 0x00, 0x12, 0x84, 0xb6,
+ 0xc5, 0xe2, 0x95, 0xa2, 0xc0, 0x3b, 0xa4, 0x6b, 0xf0, 0x82, 0xd0, 0x96,
+ 0x5d, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x43, 0x30,
+ 0x82, 0x01, 0x3f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30,
+ 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79,
+ 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e,
+ 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49,
+ 0x2d, 0x31, 0x2d, 0x35, 0x33, 0x38, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xde, 0xcf, 0x5c, 0x50, 0xb7, 0xae, 0x02,
+ 0x1f, 0x15, 0x17, 0xaa, 0x16, 0xe8, 0x0d, 0xb5, 0x28, 0x9d, 0x6a, 0x5a,
+ 0xf3, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x2c, 0xd5, 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36,
+ 0x61, 0x5b, 0x4a, 0xfb, 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xb4, 0x8e, 0xbd, 0x07, 0xb9, 0x9a,
+ 0x85, 0xec, 0x3b, 0x67, 0xbd, 0x07, 0x60, 0x61, 0xe6, 0x84, 0xd1, 0xd4,
+ 0xef, 0xeb, 0x1b, 0xba, 0x0b, 0x82, 0x4b, 0x95, 0x64, 0xb6, 0x66, 0x53,
+ 0x23, 0xbd, 0xb7, 0x84, 0xdd, 0xe4, 0x7b, 0x8d, 0x09, 0xda, 0xcf, 0xb2,
+ 0xf5, 0xf1, 0xc3, 0xbf, 0x87, 0x84, 0xbe, 0x4e, 0xa6, 0xa8, 0xc2, 0xe7,
+ 0x12, 0x39, 0x28, 0x34, 0xe0, 0xa4, 0x56, 0x44, 0x40, 0x0c, 0x9f, 0x88,
+ 0xa3, 0x15, 0xd3, 0xe8, 0xd3, 0x5e, 0xe3, 0x1c, 0x04, 0x60, 0xfb, 0x69,
+ 0x36, 0x4f, 0x6a, 0x7e, 0x0c, 0x2a, 0x28, 0xc1, 0xf3, 0xaa, 0x58, 0x0e,
+ 0x6c, 0xce, 0x1d, 0x07, 0xc3, 0x4a, 0xc0, 0x9c, 0x8d, 0xc3, 0x74, 0xb1,
+ 0xae, 0x82, 0xf0, 0x1a, 0xe1, 0xf9, 0x4e, 0x29, 0xbd, 0x46, 0xde, 0xb7,
+ 0x1d, 0xf9, 0x7d, 0xdb, 0xd9, 0x0f, 0x84, 0xcb, 0x92, 0x45, 0xcc, 0x1c,
+ 0xb3, 0x18, 0xf6, 0xa0, 0xcf, 0x71, 0x6f, 0x0c, 0x2e, 0x9b, 0xd2, 0x2d,
+ 0xb3, 0x99, 0x93, 0x83, 0x44, 0xac, 0x15, 0xaa, 0x9b, 0x2e, 0x67, 0xec,
+ 0x4f, 0x88, 0x69, 0x05, 0x56, 0x7b, 0x8b, 0xb2, 0x43, 0xa9, 0x3a, 0x6c,
+ 0x1c, 0x13, 0x33, 0x25, 0x1b, 0xfd, 0xa8, 0xc8, 0x57, 0x02, 0xfb, 0x1c,
+ 0xe0, 0xd1, 0xbd, 0x3b, 0x56, 0x44, 0x65, 0xc3, 0x63, 0xf5, 0x1b, 0xef,
+ 0xec, 0x30, 0xd9, 0xe3, 0x6e, 0x2e, 0x13, 0xe9, 0x39, 0x08, 0x2a, 0x0c,
+ 0x72, 0xf3, 0x9a, 0xcc, 0xf6, 0x27, 0x29, 0x84, 0xd3, 0xef, 0x4c, 0xc7,
+ 0x84, 0x11, 0x65, 0x1f, 0xc6, 0xe3, 0x81, 0x03, 0xdb, 0x87, 0xcc, 0x78,
+ 0xf7, 0xb5, 0x9d, 0x96, 0x3e, 0x6a, 0x7f, 0xbc, 0x11, 0x85, 0x7a, 0x75,
+ 0xe6, 0x41, 0x7d, 0x0d, 0xcf, 0xf9, 0xe5, 0x85, 0x69, 0x25, 0x8f, 0xc7,
+ 0x8d, 0x07, 0x2d, 0xf8, 0x69, 0x0f, 0xcb, 0x41, 0x53, 0x00,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1828629 (0x1be715)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority
+ Validity
+ Not Before: Jan 1 07:00:00 2014 GMT
+ Not After : May 30 07:00:00 2031 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bf:71:62:08:f1:fa:59:34:f7:1b:c9:18:a3:f7:
+ 80:49:58:e9:22:83:13:a6:c5:20:43:01:3b:84:f1:
+ e6:85:49:9f:27:ea:f6:84:1b:4e:a0:b4:db:70:98:
+ c7:32:01:b1:05:3e:07:4e:ee:f4:fa:4f:2f:59:30:
+ 22:e7:ab:19:56:6b:e2:80:07:fc:f3:16:75:80:39:
+ 51:7b:e5:f9:35:b6:74:4e:a9:8d:82:13:e4:b6:3f:
+ a9:03:83:fa:a2:be:8a:15:6a:7f:de:0b:c3:b6:19:
+ 14:05:ca:ea:c3:a8:04:94:3b:46:7c:32:0d:f3:00:
+ 66:22:c8:8d:69:6d:36:8c:11:18:b7:d3:b2:1c:60:
+ b4:38:fa:02:8c:ce:d3:dd:46:07:de:0a:3e:eb:5d:
+ 7c:c8:7c:fb:b0:2b:53:a4:92:62:69:51:25:05:61:
+ 1a:44:81:8c:2c:a9:43:96:23:df:ac:3a:81:9a:0e:
+ 29:c5:1c:a9:e9:5d:1e:b6:9e:9e:30:0a:39:ce:f1:
+ 88:80:fb:4b:5d:cc:32:ec:85:62:43:25:34:02:56:
+ 27:01:91:b4:3b:70:2a:3f:6e:b1:e8:9c:88:01:7d:
+ 9f:d4:f9:db:53:6d:60:9d:bf:2c:e7:58:ab:b8:5f:
+ 46:fc:ce:c4:1b:03:3c:09:eb:49:31:5c:69:46:b3:
+ e0:47
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE
+ X509v3 Authority Key Identifier:
+ keyid:D2:C4:B0:D2:91:D4:4C:11:71:B3:61:CB:3D:A1:FE:DD:A8:6A:D4:E3
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.godaddy.com/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.godaddy.com/gdroot.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://certs.godaddy.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 59:0b:53:bd:92:86:11:a7:24:7b:ed:5b:31:cf:1d:1f:6c:70:
+ c5:b8:6e:be:4e:bb:f6:be:97:50:e1:30:7f:ba:28:5c:62:94:
+ c2:e3:7e:33:f7:fb:42:76:85:db:95:1c:8c:22:58:75:09:0c:
+ 88:65:67:39:0a:16:09:c5:a0:38:97:a4:c5:23:93:3f:b4:18:
+ a6:01:06:44:91:e3:a7:69:27:b4:5a:25:7f:3a:b7:32:cd:dd:
+ 84:ff:2a:38:29:33:a4:dd:67:b2:85:fe:a1:88:20:1c:50:89:
+ c8:dc:2a:f6:42:03:37:4c:e6:88:df:d5:af:24:f2:b1:c3:df:
+ cc:b5:ec:e0:99:5e:b7:49:54:20:3c:94:18:0c:c7:1c:52:18:
+ 49:a4:6d:e1:b3:58:0b:c9:d8:ec:d9:ae:1c:32:8e:28:70:0d:
+ e2:fe:a6:17:9e:84:0f:bd:57:70:b3:5a:e9:1f:a0:86:53:bb:
+ ef:7c:ff:69:0b:e0:48:c3:b7:93:0b:c8:0a:54:c4:ac:5d:14:
+ 67:37:6c:ca:a5:2f:31:08:37:aa:6e:6f:8c:bc:9b:e2:57:5d:
+ 24:81:af:97:97:9c:84:ad:6c:ac:37:4c:66:f3:61:91:11:20:
+ e4:be:30:9f:7a:a4:29:09:b0:e1:34:5f:64:77:18:40:51:df:
+ 8c:30:a6:af
+-----BEGIN CERTIFICATE-----
+MIIEfTCCA2WgAwIBAgIDG+cVMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVT
+MSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIEluYy4xMTAvBgNVBAsTKEdv
+IERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMTAx
+MDcwMDAwWhcNMzEwNTMwMDcwMDAwWjCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHku
+Y29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1
+dGhvcml0eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3Fi
+CPH6WTT3G8kYo/eASVjpIoMTpsUgQwE7hPHmhUmfJ+r2hBtOoLTbcJjHMgGxBT4H
+Tu70+k8vWTAi56sZVmvigAf88xZ1gDlRe+X5NbZ0TqmNghPktj+pA4P6or6KFWp/
+3gvDthkUBcrqw6gElDtGfDIN8wBmIsiNaW02jBEYt9OyHGC0OPoCjM7T3UYH3go+
+6118yHz7sCtTpJJiaVElBWEaRIGMLKlDliPfrDqBmg4pxRyp6V0etp6eMAo5zvGI
+gPtLXcwy7IViQyU0AlYnAZG0O3AqP26x6JyIAX2f1PnbU21gnb8s51iruF9G/M7E
+GwM8CetJMVxpRrPgRwIDAQABo4IBFzCCARMwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9BUFuIMGU2g/eMB8GA1Ud
+IwQYMBaAFNLEsNKR1EwRcbNhyz2h/t2oatTjMDQGCCsGAQUFBwEBBCgwJjAkBggr
+BgEFBQcwAYYYaHR0cDovL29jc3AuZ29kYWRkeS5jb20vMDIGA1UdHwQrMCkwJ6Al
+oCOGIWh0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Ryb290LmNybDBGBgNVHSAEPzA9
+MDsGBFUdIAAwMzAxBggrBgEFBQcCARYlaHR0cHM6Ly9jZXJ0cy5nb2RhZGR5LmNv
+bS9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAWQtTvZKGEacke+1bMc8d
+H2xwxbhuvk679r6XUOEwf7ooXGKUwuN+M/f7QnaF25UcjCJYdQkMiGVnOQoWCcWg
+OJekxSOTP7QYpgEGRJHjp2kntFolfzq3Ms3dhP8qOCkzpN1nsoX+oYggHFCJyNwq
+9kIDN0zmiN/VryTyscPfzLXs4Jlet0lUIDyUGAzHHFIYSaRt4bNYC8nY7NmuHDKO
+KHAN4v6mF56ED71XcLNa6R+ghlO773z/aQvgSMO3kwvIClTErF0UZzdsyqUvMQg3
+qm5vjLyb4lddJIGvl5echK1srDdMZvNhkREg5L4wn3qkKQmw4TRfZHcYQFHfjDCm
+rw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert18[] = {
+ 0x30, 0x82, 0x04, 0x7d, 0x30, 0x82, 0x03, 0x65, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x1b, 0xe7, 0x15, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x63, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x54,
+ 0x68, 0x65, 0x20, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20,
+ 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x47, 0x6f,
+ 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73,
+ 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x31, 0x30, 0x31,
+ 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30,
+ 0x35, 0x33, 0x30, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81,
+ 0x83, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74,
+ 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30,
+ 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44,
+ 0x61, 0x64, 0x64, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbf, 0x71, 0x62,
+ 0x08, 0xf1, 0xfa, 0x59, 0x34, 0xf7, 0x1b, 0xc9, 0x18, 0xa3, 0xf7, 0x80,
+ 0x49, 0x58, 0xe9, 0x22, 0x83, 0x13, 0xa6, 0xc5, 0x20, 0x43, 0x01, 0x3b,
+ 0x84, 0xf1, 0xe6, 0x85, 0x49, 0x9f, 0x27, 0xea, 0xf6, 0x84, 0x1b, 0x4e,
+ 0xa0, 0xb4, 0xdb, 0x70, 0x98, 0xc7, 0x32, 0x01, 0xb1, 0x05, 0x3e, 0x07,
+ 0x4e, 0xee, 0xf4, 0xfa, 0x4f, 0x2f, 0x59, 0x30, 0x22, 0xe7, 0xab, 0x19,
+ 0x56, 0x6b, 0xe2, 0x80, 0x07, 0xfc, 0xf3, 0x16, 0x75, 0x80, 0x39, 0x51,
+ 0x7b, 0xe5, 0xf9, 0x35, 0xb6, 0x74, 0x4e, 0xa9, 0x8d, 0x82, 0x13, 0xe4,
+ 0xb6, 0x3f, 0xa9, 0x03, 0x83, 0xfa, 0xa2, 0xbe, 0x8a, 0x15, 0x6a, 0x7f,
+ 0xde, 0x0b, 0xc3, 0xb6, 0x19, 0x14, 0x05, 0xca, 0xea, 0xc3, 0xa8, 0x04,
+ 0x94, 0x3b, 0x46, 0x7c, 0x32, 0x0d, 0xf3, 0x00, 0x66, 0x22, 0xc8, 0x8d,
+ 0x69, 0x6d, 0x36, 0x8c, 0x11, 0x18, 0xb7, 0xd3, 0xb2, 0x1c, 0x60, 0xb4,
+ 0x38, 0xfa, 0x02, 0x8c, 0xce, 0xd3, 0xdd, 0x46, 0x07, 0xde, 0x0a, 0x3e,
+ 0xeb, 0x5d, 0x7c, 0xc8, 0x7c, 0xfb, 0xb0, 0x2b, 0x53, 0xa4, 0x92, 0x62,
+ 0x69, 0x51, 0x25, 0x05, 0x61, 0x1a, 0x44, 0x81, 0x8c, 0x2c, 0xa9, 0x43,
+ 0x96, 0x23, 0xdf, 0xac, 0x3a, 0x81, 0x9a, 0x0e, 0x29, 0xc5, 0x1c, 0xa9,
+ 0xe9, 0x5d, 0x1e, 0xb6, 0x9e, 0x9e, 0x30, 0x0a, 0x39, 0xce, 0xf1, 0x88,
+ 0x80, 0xfb, 0x4b, 0x5d, 0xcc, 0x32, 0xec, 0x85, 0x62, 0x43, 0x25, 0x34,
+ 0x02, 0x56, 0x27, 0x01, 0x91, 0xb4, 0x3b, 0x70, 0x2a, 0x3f, 0x6e, 0xb1,
+ 0xe8, 0x9c, 0x88, 0x01, 0x7d, 0x9f, 0xd4, 0xf9, 0xdb, 0x53, 0x6d, 0x60,
+ 0x9d, 0xbf, 0x2c, 0xe7, 0x58, 0xab, 0xb8, 0x5f, 0x46, 0xfc, 0xce, 0xc4,
+ 0x1b, 0x03, 0x3c, 0x09, 0xeb, 0x49, 0x31, 0x5c, 0x69, 0x46, 0xb3, 0xe0,
+ 0x47, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x17, 0x30, 0x82,
+ 0x01, 0x13, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3a, 0x9a,
+ 0x85, 0x07, 0x10, 0x67, 0x28, 0xb6, 0xef, 0xf6, 0xbd, 0x05, 0x41, 0x6e,
+ 0x20, 0xc1, 0x94, 0xda, 0x0f, 0xde, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd2, 0xc4, 0xb0, 0xd2, 0x91,
+ 0xd4, 0x4c, 0x11, 0x71, 0xb3, 0x61, 0xcb, 0x3d, 0xa1, 0xfe, 0xdd, 0xa8,
+ 0x6a, 0xd4, 0xe3, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
+ 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x32, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25,
+ 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x67, 0x64, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d,
+ 0x30, 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74,
+ 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
+ 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x59, 0x0b, 0x53,
+ 0xbd, 0x92, 0x86, 0x11, 0xa7, 0x24, 0x7b, 0xed, 0x5b, 0x31, 0xcf, 0x1d,
+ 0x1f, 0x6c, 0x70, 0xc5, 0xb8, 0x6e, 0xbe, 0x4e, 0xbb, 0xf6, 0xbe, 0x97,
+ 0x50, 0xe1, 0x30, 0x7f, 0xba, 0x28, 0x5c, 0x62, 0x94, 0xc2, 0xe3, 0x7e,
+ 0x33, 0xf7, 0xfb, 0x42, 0x76, 0x85, 0xdb, 0x95, 0x1c, 0x8c, 0x22, 0x58,
+ 0x75, 0x09, 0x0c, 0x88, 0x65, 0x67, 0x39, 0x0a, 0x16, 0x09, 0xc5, 0xa0,
+ 0x38, 0x97, 0xa4, 0xc5, 0x23, 0x93, 0x3f, 0xb4, 0x18, 0xa6, 0x01, 0x06,
+ 0x44, 0x91, 0xe3, 0xa7, 0x69, 0x27, 0xb4, 0x5a, 0x25, 0x7f, 0x3a, 0xb7,
+ 0x32, 0xcd, 0xdd, 0x84, 0xff, 0x2a, 0x38, 0x29, 0x33, 0xa4, 0xdd, 0x67,
+ 0xb2, 0x85, 0xfe, 0xa1, 0x88, 0x20, 0x1c, 0x50, 0x89, 0xc8, 0xdc, 0x2a,
+ 0xf6, 0x42, 0x03, 0x37, 0x4c, 0xe6, 0x88, 0xdf, 0xd5, 0xaf, 0x24, 0xf2,
+ 0xb1, 0xc3, 0xdf, 0xcc, 0xb5, 0xec, 0xe0, 0x99, 0x5e, 0xb7, 0x49, 0x54,
+ 0x20, 0x3c, 0x94, 0x18, 0x0c, 0xc7, 0x1c, 0x52, 0x18, 0x49, 0xa4, 0x6d,
+ 0xe1, 0xb3, 0x58, 0x0b, 0xc9, 0xd8, 0xec, 0xd9, 0xae, 0x1c, 0x32, 0x8e,
+ 0x28, 0x70, 0x0d, 0xe2, 0xfe, 0xa6, 0x17, 0x9e, 0x84, 0x0f, 0xbd, 0x57,
+ 0x70, 0xb3, 0x5a, 0xe9, 0x1f, 0xa0, 0x86, 0x53, 0xbb, 0xef, 0x7c, 0xff,
+ 0x69, 0x0b, 0xe0, 0x48, 0xc3, 0xb7, 0x93, 0x0b, 0xc8, 0x0a, 0x54, 0xc4,
+ 0xac, 0x5d, 0x14, 0x67, 0x37, 0x6c, 0xca, 0xa5, 0x2f, 0x31, 0x08, 0x37,
+ 0xaa, 0x6e, 0x6f, 0x8c, 0xbc, 0x9b, 0xe2, 0x57, 0x5d, 0x24, 0x81, 0xaf,
+ 0x97, 0x97, 0x9c, 0x84, 0xad, 0x6c, 0xac, 0x37, 0x4c, 0x66, 0xf3, 0x61,
+ 0x91, 0x11, 0x20, 0xe4, 0xbe, 0x30, 0x9f, 0x7a, 0xa4, 0x29, 0x09, 0xb0,
+ 0xe1, 0x34, 0x5f, 0x64, 0x77, 0x18, 0x40, 0x51, 0xdf, 0x8c, 0x30, 0xa6,
+ 0xaf,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 06:9e:1d:b7:7f:cf:1d:fb:a9:7a:f5:e5:c9:a2:40:37
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA
+ Validity
+ Not Before: Mar 8 12:00:00 2013 GMT
+ Not After : Mar 8 12:00:00 2023 GMT
+ Subject: C=US, O=DigiCert Inc, CN=DigiCert Secure Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bb:57:e4:21:a9:d5:9b:60:37:7e:8e:a1:61:7f:
+ 81:e2:1a:c2:75:64:d9:91:50:0b:e4:36:44:24:6e:
+ 30:d2:9b:7a:27:fa:c2:6a:ae:6a:70:09:38:b9:20:
+ 0a:c8:65:10:4a:88:ac:31:f2:dc:92:f2:63:a1:5d:
+ 80:63:59:80:92:23:1c:e6:ef:76:4a:50:35:c9:d8:
+ 71:38:b9:ed:f0:e6:42:ae:d3:38:26:79:30:f9:22:
+ 94:c6:db:a6:3f:41:78:90:d8:de:5c:7e:69:7d:f8:
+ 90:15:3a:d0:a1:a0:be:fa:b2:b2:19:a1:d8:2b:d1:
+ ce:bf:6b:dd:49:ab:a3:92:fe:b5:ab:c8:c1:3e:ee:
+ 01:00:d8:a9:44:b8:42:73:88:c3:61:f5:ab:4a:83:
+ 28:0a:d2:d4:49:fa:6a:b1:cd:df:57:2c:94:e5:e2:
+ ca:83:5f:b7:ba:62:5c:2f:68:a5:f0:c0:b9:fd:2b:
+ d1:e9:1f:d8:1a:62:15:bd:ff:3d:a6:f7:cb:ef:e6:
+ db:65:2f:25:38:ec:fb:e6:20:66:58:96:34:19:d2:
+ 15:ce:21:d3:24:cc:d9:14:6f:d8:fe:55:c7:e7:6f:
+ b6:0f:1a:8c:49:be:29:f2:ba:5a:9a:81:26:37:24:
+ 6f:d7:48:12:6c:2e:59:f5:9c:18:bb:d9:f6:68:e2:
+ df:45
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertGlobalRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertGlobalRootCA.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.digicert.com/CPS
+
+ X509v3 Subject Key Identifier:
+ 90:71:DB:37:EB:73:C8:EF:DC:D5:1E:12:B6:34:BA:2B:5A:A0:A6:92
+ X509v3 Authority Key Identifier:
+ keyid:03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 30:ce:d1:95:51:00:ae:06:0b:a1:0e:02:c0:17:ac:b6:7f:8f:
+ 20:f6:40:75:74:1c:cc:78:b1:a4:4f:ea:f4:d0:c4:9d:a2:de:
+ 81:07:26:1f:40:88:51:f0:1f:cf:b7:4c:40:99:d0:f4:3c:71:
+ 98:73:88:97:2c:19:d7:6e:84:8f:a4:1f:9c:5a:20:e3:51:5c:
+ b0:c5:9e:99:6a:4f:c8:69:f7:10:ff:4e:ad:19:d9:c9:58:b3:
+ 33:ae:0c:d9:96:29:9e:71:b2:70:63:a3:b6:99:16:42:1d:65:
+ f3:f7:a0:1e:7d:c5:d4:65:14:b2:62:84:d4:6c:5c:08:0c:d8:
+ 6c:93:2b:b4:76:59:8a:d1:7f:ff:03:d8:c2:5d:b8:2f:22:d6:
+ 38:f0:f6:9c:6b:7d:46:eb:99:74:f7:eb:4a:0e:a9:a6:04:eb:
+ 7b:ce:f0:5c:6b:98:31:5a:98:40:eb:69:c4:05:f4:20:a8:ca:
+ 08:3a:65:6c:38:15:f5:5c:2c:b2:55:e4:2c:6b:41:f0:be:5c:
+ 46:ca:4a:29:a0:48:5e:20:d2:45:ff:05:de:34:af:70:4b:81:
+ 39:e2:ca:07:57:7c:b6:31:dc:21:29:e2:be:97:0e:77:90:14:
+ 51:40:e1:bf:e3:cc:1b:19:9c:25:ca:a7:06:b2:53:df:23:b2:
+ cf:12:19:a3
+-----BEGIN CERTIFICATE-----
+MIIEjzCCA3egAwIBAgIQBp4dt3/PHfupevXlyaJANzANBgkqhkiG9w0BAQUFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEgxCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxIjAgBgNVBAMTGURpZ2lDZXJ0IFNlY3Vy
+ZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V+Qh
+qdWbYDd+jqFhf4HiGsJ1ZNmRUAvkNkQkbjDSm3on+sJqrmpwCTi5IArIZRBKiKwx
+8tyS8mOhXYBjWYCSIxzm73ZKUDXJ2HE4ue3w5kKu0zgmeTD5IpTG26Y/QXiQ2N5c
+fml9+JAVOtChoL76srIZodgr0c6/a91Jq6OS/rWryME+7gEA2KlEuEJziMNh9atK
+gygK0tRJ+mqxzd9XLJTl4sqDX7e6YlwvaKXwwLn9K9HpH9gaYhW9/z2m98vv5ttl
+LyU47PvmIGZYljQZ0hXOIdMkzNkUb9j+Vcfnb7YPGoxJvinyulqagSY3JG/XSBJs
+Lln1nBi72fZo4t9FAgMBAAGjggFaMIIBVjASBgNVHRMBAf8ECDAGAQH/AgEAMA4G
+A1UdDwEB/wQEAwIBhjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6
+Ly9vY3NwLmRpZ2ljZXJ0LmNvbTB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3Js
+My5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMDegNaAzhjFo
+dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3Js
+MD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k
+aWdpY2VydC5jb20vQ1BTMB0GA1UdDgQWBBSQcds363PI79zVHhK2NLorWqCmkjAf
+BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTANBgkqhkiG9w0BAQUFAAOC
+AQEAMM7RlVEArgYLoQ4CwBestn+PIPZAdXQczHixpE/q9NDEnaLegQcmH0CIUfAf
+z7dMQJnQ9DxxmHOIlywZ126Ej6QfnFog41FcsMWemWpPyGn3EP9OrRnZyVizM64M
+2ZYpnnGycGOjtpkWQh1l8/egHn3F1GUUsmKE1GxcCAzYbJMrtHZZitF//wPYwl24
+LyLWOPD2nGt9RuuZdPfrSg6ppgTre87wXGuYMVqYQOtpxAX0IKjKCDplbDgV9Vws
+slXkLGtB8L5cRspKKaBIXiDSRf8F3jSvcEuBOeLKB1d8tjHcISnivpcOd5AUUUDh
+v+PMGxmcJcqnBrJT3yOyzxIZow==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert19[] = {
+ 0x30, 0x82, 0x04, 0x8f, 0x30, 0x82, 0x03, 0x77, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x06, 0x9e, 0x1d, 0xb7, 0x7f, 0xcf, 0x1d, 0xfb, 0xa9,
+ 0x7a, 0xf5, 0xe5, 0xc9, 0xa2, 0x40, 0x37, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x61,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
+ 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x33, 0x30, 0x38, 0x31,
+ 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x33,
+ 0x30, 0x38, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x48, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44,
+ 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31,
+ 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72,
+ 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbb, 0x57, 0xe4, 0x21,
+ 0xa9, 0xd5, 0x9b, 0x60, 0x37, 0x7e, 0x8e, 0xa1, 0x61, 0x7f, 0x81, 0xe2,
+ 0x1a, 0xc2, 0x75, 0x64, 0xd9, 0x91, 0x50, 0x0b, 0xe4, 0x36, 0x44, 0x24,
+ 0x6e, 0x30, 0xd2, 0x9b, 0x7a, 0x27, 0xfa, 0xc2, 0x6a, 0xae, 0x6a, 0x70,
+ 0x09, 0x38, 0xb9, 0x20, 0x0a, 0xc8, 0x65, 0x10, 0x4a, 0x88, 0xac, 0x31,
+ 0xf2, 0xdc, 0x92, 0xf2, 0x63, 0xa1, 0x5d, 0x80, 0x63, 0x59, 0x80, 0x92,
+ 0x23, 0x1c, 0xe6, 0xef, 0x76, 0x4a, 0x50, 0x35, 0xc9, 0xd8, 0x71, 0x38,
+ 0xb9, 0xed, 0xf0, 0xe6, 0x42, 0xae, 0xd3, 0x38, 0x26, 0x79, 0x30, 0xf9,
+ 0x22, 0x94, 0xc6, 0xdb, 0xa6, 0x3f, 0x41, 0x78, 0x90, 0xd8, 0xde, 0x5c,
+ 0x7e, 0x69, 0x7d, 0xf8, 0x90, 0x15, 0x3a, 0xd0, 0xa1, 0xa0, 0xbe, 0xfa,
+ 0xb2, 0xb2, 0x19, 0xa1, 0xd8, 0x2b, 0xd1, 0xce, 0xbf, 0x6b, 0xdd, 0x49,
+ 0xab, 0xa3, 0x92, 0xfe, 0xb5, 0xab, 0xc8, 0xc1, 0x3e, 0xee, 0x01, 0x00,
+ 0xd8, 0xa9, 0x44, 0xb8, 0x42, 0x73, 0x88, 0xc3, 0x61, 0xf5, 0xab, 0x4a,
+ 0x83, 0x28, 0x0a, 0xd2, 0xd4, 0x49, 0xfa, 0x6a, 0xb1, 0xcd, 0xdf, 0x57,
+ 0x2c, 0x94, 0xe5, 0xe2, 0xca, 0x83, 0x5f, 0xb7, 0xba, 0x62, 0x5c, 0x2f,
+ 0x68, 0xa5, 0xf0, 0xc0, 0xb9, 0xfd, 0x2b, 0xd1, 0xe9, 0x1f, 0xd8, 0x1a,
+ 0x62, 0x15, 0xbd, 0xff, 0x3d, 0xa6, 0xf7, 0xcb, 0xef, 0xe6, 0xdb, 0x65,
+ 0x2f, 0x25, 0x38, 0xec, 0xfb, 0xe6, 0x20, 0x66, 0x58, 0x96, 0x34, 0x19,
+ 0xd2, 0x15, 0xce, 0x21, 0xd3, 0x24, 0xcc, 0xd9, 0x14, 0x6f, 0xd8, 0xfe,
+ 0x55, 0xc7, 0xe7, 0x6f, 0xb6, 0x0f, 0x1a, 0x8c, 0x49, 0xbe, 0x29, 0xf2,
+ 0xba, 0x5a, 0x9a, 0x81, 0x26, 0x37, 0x24, 0x6f, 0xd7, 0x48, 0x12, 0x6c,
+ 0x2e, 0x59, 0xf5, 0x9c, 0x18, 0xbb, 0xd9, 0xf6, 0x68, 0xe2, 0xdf, 0x45,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5a, 0x30, 0x82, 0x01,
+ 0x56, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x86, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63,
+ 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x7b, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33,
+ 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64,
+ 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30,
+ 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64,
+ 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0x90, 0x71, 0xdb, 0x37, 0xeb, 0x73, 0xc8, 0xef, 0xdc, 0xd5,
+ 0x1e, 0x12, 0xb6, 0x34, 0xba, 0x2b, 0x5a, 0xa0, 0xa6, 0x92, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x03,
+ 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, 0x66, 0xf0, 0xa3, 0xe2, 0x1b,
+ 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x30, 0xce, 0xd1, 0x95, 0x51, 0x00, 0xae, 0x06, 0x0b,
+ 0xa1, 0x0e, 0x02, 0xc0, 0x17, 0xac, 0xb6, 0x7f, 0x8f, 0x20, 0xf6, 0x40,
+ 0x75, 0x74, 0x1c, 0xcc, 0x78, 0xb1, 0xa4, 0x4f, 0xea, 0xf4, 0xd0, 0xc4,
+ 0x9d, 0xa2, 0xde, 0x81, 0x07, 0x26, 0x1f, 0x40, 0x88, 0x51, 0xf0, 0x1f,
+ 0xcf, 0xb7, 0x4c, 0x40, 0x99, 0xd0, 0xf4, 0x3c, 0x71, 0x98, 0x73, 0x88,
+ 0x97, 0x2c, 0x19, 0xd7, 0x6e, 0x84, 0x8f, 0xa4, 0x1f, 0x9c, 0x5a, 0x20,
+ 0xe3, 0x51, 0x5c, 0xb0, 0xc5, 0x9e, 0x99, 0x6a, 0x4f, 0xc8, 0x69, 0xf7,
+ 0x10, 0xff, 0x4e, 0xad, 0x19, 0xd9, 0xc9, 0x58, 0xb3, 0x33, 0xae, 0x0c,
+ 0xd9, 0x96, 0x29, 0x9e, 0x71, 0xb2, 0x70, 0x63, 0xa3, 0xb6, 0x99, 0x16,
+ 0x42, 0x1d, 0x65, 0xf3, 0xf7, 0xa0, 0x1e, 0x7d, 0xc5, 0xd4, 0x65, 0x14,
+ 0xb2, 0x62, 0x84, 0xd4, 0x6c, 0x5c, 0x08, 0x0c, 0xd8, 0x6c, 0x93, 0x2b,
+ 0xb4, 0x76, 0x59, 0x8a, 0xd1, 0x7f, 0xff, 0x03, 0xd8, 0xc2, 0x5d, 0xb8,
+ 0x2f, 0x22, 0xd6, 0x38, 0xf0, 0xf6, 0x9c, 0x6b, 0x7d, 0x46, 0xeb, 0x99,
+ 0x74, 0xf7, 0xeb, 0x4a, 0x0e, 0xa9, 0xa6, 0x04, 0xeb, 0x7b, 0xce, 0xf0,
+ 0x5c, 0x6b, 0x98, 0x31, 0x5a, 0x98, 0x40, 0xeb, 0x69, 0xc4, 0x05, 0xf4,
+ 0x20, 0xa8, 0xca, 0x08, 0x3a, 0x65, 0x6c, 0x38, 0x15, 0xf5, 0x5c, 0x2c,
+ 0xb2, 0x55, 0xe4, 0x2c, 0x6b, 0x41, 0xf0, 0xbe, 0x5c, 0x46, 0xca, 0x4a,
+ 0x29, 0xa0, 0x48, 0x5e, 0x20, 0xd2, 0x45, 0xff, 0x05, 0xde, 0x34, 0xaf,
+ 0x70, 0x4b, 0x81, 0x39, 0xe2, 0xca, 0x07, 0x57, 0x7c, 0xb6, 0x31, 0xdc,
+ 0x21, 0x29, 0xe2, 0xbe, 0x97, 0x0e, 0x77, 0x90, 0x14, 0x51, 0x40, 0xe1,
+ 0xbf, 0xe3, 0xcc, 0x1b, 0x19, 0x9c, 0x25, 0xca, 0xa7, 0x06, 0xb2, 0x53,
+ 0xdf, 0x23, 0xb2, 0xcf, 0x12, 0x19, 0xa3,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 1b:09:3b:78:60:96:da:37:bb:a4:51:94:46:c8:96:78
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ a3:cd:7d:1e:f7:c7:75:8d:48:e7:56:34:4c:00:90:75:a9:51:
+ a5:56:c1:6d:bc:fe:f5:53:22:e9:98:a2:ac:9a:7e:70:1e:b3:
+ 8e:3b:45:e3:86:95:31:da:6d:4c:fb:34:50:80:96:cd:24:f2:
+ 40:df:04:3f:e2:65:ce:34:22:61:15:ea:66:70:64:d2:f1:6e:
+ f3:ca:18:59:6a:41:46:7e:82:de:19:b0:70:31:56:69:0d:0c:
+ e6:1d:9d:71:58:dc:cc:de:62:f5:e1:7a:10:02:d8:7a:dc:3b:
+ fa:57:bd:c9:e9:8f:46:21:39:9f:51:65:4c:8e:3a:be:28:41:
+ 70:1d
+-----BEGIN CERTIFICATE-----
+MIIEkDCCA/mgAwIBAgIQGwk7eGCW2je7pFGURsiWeDANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggFbMIIBVzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
+BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
+aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
+KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
+j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
+L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEAo819HvfHdY1I51Y0
+TACQdalRpVbBbbz+9VMi6ZiirJp+cB6zjjtF44aVMdptTPs0UICWzSTyQN8EP+Jl
+zjQiYRXqZnBk0vFu88oYWWpBRn6C3hmwcDFWaQ0M5h2dcVjczN5i9eF6EALYetw7
++le9yemPRiE5n1FlTI46vihBcB0=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert20[] = {
+ 0x30, 0x82, 0x04, 0x90, 0x30, 0x82, 0x03, 0xf9, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x1b, 0x09, 0x3b, 0x78, 0x60, 0x96, 0xda, 0x37, 0xbb,
+ 0xa4, 0x51, 0x94, 0x46, 0xc8, 0x96, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5b, 0x30, 0x82, 0x01, 0x57, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f,
+ 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09,
+ 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30,
+ 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
+ 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80,
+ 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0xa3, 0xcd, 0x7d, 0x1e, 0xf7, 0xc7, 0x75, 0x8d, 0x48, 0xe7, 0x56, 0x34,
+ 0x4c, 0x00, 0x90, 0x75, 0xa9, 0x51, 0xa5, 0x56, 0xc1, 0x6d, 0xbc, 0xfe,
+ 0xf5, 0x53, 0x22, 0xe9, 0x98, 0xa2, 0xac, 0x9a, 0x7e, 0x70, 0x1e, 0xb3,
+ 0x8e, 0x3b, 0x45, 0xe3, 0x86, 0x95, 0x31, 0xda, 0x6d, 0x4c, 0xfb, 0x34,
+ 0x50, 0x80, 0x96, 0xcd, 0x24, 0xf2, 0x40, 0xdf, 0x04, 0x3f, 0xe2, 0x65,
+ 0xce, 0x34, 0x22, 0x61, 0x15, 0xea, 0x66, 0x70, 0x64, 0xd2, 0xf1, 0x6e,
+ 0xf3, 0xca, 0x18, 0x59, 0x6a, 0x41, 0x46, 0x7e, 0x82, 0xde, 0x19, 0xb0,
+ 0x70, 0x31, 0x56, 0x69, 0x0d, 0x0c, 0xe6, 0x1d, 0x9d, 0x71, 0x58, 0xdc,
+ 0xcc, 0xde, 0x62, 0xf5, 0xe1, 0x7a, 0x10, 0x02, 0xd8, 0x7a, 0xdc, 0x3b,
+ 0xfa, 0x57, 0xbd, 0xc9, 0xe9, 0x8f, 0x46, 0x21, 0x39, 0x9f, 0x51, 0x65,
+ 0x4c, 0x8e, 0x3a, 0xbe, 0x28, 0x41, 0x70, 0x1d,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 01:fd:a3:eb:6e:ca:75:c8:88:43:8b:72:4b:cf:bc:91
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA
+ Validity
+ Not Before: Mar 8 12:00:00 2013 GMT
+ Not After : Mar 8 12:00:00 2023 GMT
+ Subject: C=US, O=DigiCert Inc, CN=DigiCert SHA2 Secure Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:dc:ae:58:90:4d:c1:c4:30:15:90:35:5b:6e:3c:
+ 82:15:f5:2c:5c:bd:e3:db:ff:71:43:fa:64:25:80:
+ d4:ee:18:a2:4d:f0:66:d0:0a:73:6e:11:98:36:17:
+ 64:af:37:9d:fd:fa:41:84:af:c7:af:8c:fe:1a:73:
+ 4d:cf:33:97:90:a2:96:87:53:83:2b:b9:a6:75:48:
+ 2d:1d:56:37:7b:da:31:32:1a:d7:ac:ab:06:f4:aa:
+ 5d:4b:b7:47:46:dd:2a:93:c3:90:2e:79:80:80:ef:
+ 13:04:6a:14:3b:b5:9b:92:be:c2:07:65:4e:fc:da:
+ fc:ff:7a:ae:dc:5c:7e:55:31:0c:e8:39:07:a4:d7:
+ be:2f:d3:0b:6a:d2:b1:df:5f:fe:57:74:53:3b:35:
+ 80:dd:ae:8e:44:98:b3:9f:0e:d3:da:e0:d7:f4:6b:
+ 29:ab:44:a7:4b:58:84:6d:92:4b:81:c3:da:73:8b:
+ 12:97:48:90:04:45:75:1a:dd:37:31:97:92:e8:cd:
+ 54:0d:3b:e4:c1:3f:39:5e:2e:b8:f3:5c:7e:10:8e:
+ 86:41:00:8d:45:66:47:b0:a1:65:ce:a0:aa:29:09:
+ 4e:f3:97:eb:e8:2e:ab:0f:72:a7:30:0e:fa:c7:f4:
+ fd:14:77:c3:a4:5b:28:57:c2:b3:f9:82:fd:b7:45:
+ 58:9b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertGlobalRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertGlobalRootCA.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.digicert.com/CPS
+
+ X509v3 Subject Key Identifier:
+ 0F:80:61:1C:82:31:61:D5:2F:28:E7:8D:46:38:B4:2C:E1:C6:D9:E2
+ X509v3 Authority Key Identifier:
+ keyid:03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 23:3e:df:4b:d2:31:42:a5:b6:7e:42:5c:1a:44:cc:69:d1:68:
+ b4:5d:4b:e0:04:21:6c:4b:e2:6d:cc:b1:e0:97:8f:a6:53:09:
+ cd:aa:2a:65:e5:39:4f:1e:83:a5:6e:5c:98:a2:24:26:e6:fb:
+ a1:ed:93:c7:2e:02:c6:4d:4a:bf:b0:42:df:78:da:b3:a8:f9:
+ 6d:ff:21:85:53:36:60:4c:76:ce:ec:38:dc:d6:51:80:f0:c5:
+ d6:e5:d4:4d:27:64:ab:9b:c7:3e:71:fb:48:97:b8:33:6d:c9:
+ 13:07:ee:96:a2:1b:18:15:f6:5c:4c:40:ed:b3:c2:ec:ff:71:
+ c1:e3:47:ff:d4:b9:00:b4:37:42:da:20:c9:ea:6e:8a:ee:14:
+ 06:ae:7d:a2:59:98:88:a8:1b:6f:2d:f4:f2:c9:14:5f:26:cf:
+ 2c:8d:7e:ed:37:c0:a9:d5:39:b9:82:bf:19:0c:ea:34:af:00:
+ 21:68:f8:ad:73:e2:c9:32:da:38:25:0b:55:d3:9a:1d:f0:68:
+ 86:ed:2e:41:34:ef:7c:a5:50:1d:bf:3a:f9:d3:c1:08:0c:e6:
+ ed:1e:8a:58:25:e4:b8:77:ad:2d:6e:f5:52:dd:b4:74:8f:ab:
+ 49:2e:9d:3b:93:34:28:1f:78:ce:94:ea:c7:bd:d3:c9:6d:1c:
+ de:5c:32:f3
+-----BEGIN CERTIFICATE-----
+MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg
+U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83
+nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd
+KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f
+/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX
+kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0
+/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY
+aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6
+Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1
+oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD
+QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
+d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh
+xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB
+CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl
+5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA
+8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC
+2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit
+c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0
+j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert21[] = {
+ 0x30, 0x82, 0x04, 0x94, 0x30, 0x82, 0x03, 0x7c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x01, 0xfd, 0xa3, 0xeb, 0x6e, 0xca, 0x75, 0xc8, 0x88,
+ 0x43, 0x8b, 0x72, 0x4b, 0xcf, 0xbc, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x61,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
+ 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x33, 0x30, 0x38, 0x31,
+ 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x33,
+ 0x30, 0x38, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4d, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44,
+ 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31,
+ 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1e, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41, 0x32, 0x20,
+ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65,
+ 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xdc, 0xae, 0x58, 0x90, 0x4d, 0xc1, 0xc4, 0x30, 0x15, 0x90, 0x35,
+ 0x5b, 0x6e, 0x3c, 0x82, 0x15, 0xf5, 0x2c, 0x5c, 0xbd, 0xe3, 0xdb, 0xff,
+ 0x71, 0x43, 0xfa, 0x64, 0x25, 0x80, 0xd4, 0xee, 0x18, 0xa2, 0x4d, 0xf0,
+ 0x66, 0xd0, 0x0a, 0x73, 0x6e, 0x11, 0x98, 0x36, 0x17, 0x64, 0xaf, 0x37,
+ 0x9d, 0xfd, 0xfa, 0x41, 0x84, 0xaf, 0xc7, 0xaf, 0x8c, 0xfe, 0x1a, 0x73,
+ 0x4d, 0xcf, 0x33, 0x97, 0x90, 0xa2, 0x96, 0x87, 0x53, 0x83, 0x2b, 0xb9,
+ 0xa6, 0x75, 0x48, 0x2d, 0x1d, 0x56, 0x37, 0x7b, 0xda, 0x31, 0x32, 0x1a,
+ 0xd7, 0xac, 0xab, 0x06, 0xf4, 0xaa, 0x5d, 0x4b, 0xb7, 0x47, 0x46, 0xdd,
+ 0x2a, 0x93, 0xc3, 0x90, 0x2e, 0x79, 0x80, 0x80, 0xef, 0x13, 0x04, 0x6a,
+ 0x14, 0x3b, 0xb5, 0x9b, 0x92, 0xbe, 0xc2, 0x07, 0x65, 0x4e, 0xfc, 0xda,
+ 0xfc, 0xff, 0x7a, 0xae, 0xdc, 0x5c, 0x7e, 0x55, 0x31, 0x0c, 0xe8, 0x39,
+ 0x07, 0xa4, 0xd7, 0xbe, 0x2f, 0xd3, 0x0b, 0x6a, 0xd2, 0xb1, 0xdf, 0x5f,
+ 0xfe, 0x57, 0x74, 0x53, 0x3b, 0x35, 0x80, 0xdd, 0xae, 0x8e, 0x44, 0x98,
+ 0xb3, 0x9f, 0x0e, 0xd3, 0xda, 0xe0, 0xd7, 0xf4, 0x6b, 0x29, 0xab, 0x44,
+ 0xa7, 0x4b, 0x58, 0x84, 0x6d, 0x92, 0x4b, 0x81, 0xc3, 0xda, 0x73, 0x8b,
+ 0x12, 0x97, 0x48, 0x90, 0x04, 0x45, 0x75, 0x1a, 0xdd, 0x37, 0x31, 0x97,
+ 0x92, 0xe8, 0xcd, 0x54, 0x0d, 0x3b, 0xe4, 0xc1, 0x3f, 0x39, 0x5e, 0x2e,
+ 0xb8, 0xf3, 0x5c, 0x7e, 0x10, 0x8e, 0x86, 0x41, 0x00, 0x8d, 0x45, 0x66,
+ 0x47, 0xb0, 0xa1, 0x65, 0xce, 0xa0, 0xaa, 0x29, 0x09, 0x4e, 0xf3, 0x97,
+ 0xeb, 0xe8, 0x2e, 0xab, 0x0f, 0x72, 0xa7, 0x30, 0x0e, 0xfa, 0xc7, 0xf4,
+ 0xfd, 0x14, 0x77, 0xc3, 0xa4, 0x5b, 0x28, 0x57, 0xc2, 0xb3, 0xf9, 0x82,
+ 0xfd, 0xb7, 0x45, 0x58, 0x9b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x5a, 0x30, 0x82, 0x01, 0x56, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+ 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72, 0x30,
+ 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63,
+ 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69,
+ 0x43, 0x65, 0x72, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f,
+ 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x37, 0xa0, 0x35,
+ 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72,
+ 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0f, 0x80, 0x61, 0x1c, 0x82,
+ 0x31, 0x61, 0xd5, 0x2f, 0x28, 0xe7, 0x8d, 0x46, 0x38, 0xb4, 0x2c, 0xe1,
+ 0xc6, 0xd9, 0xe2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb,
+ 0x66, 0xf0, 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x23, 0x3e, 0xdf, 0x4b,
+ 0xd2, 0x31, 0x42, 0xa5, 0xb6, 0x7e, 0x42, 0x5c, 0x1a, 0x44, 0xcc, 0x69,
+ 0xd1, 0x68, 0xb4, 0x5d, 0x4b, 0xe0, 0x04, 0x21, 0x6c, 0x4b, 0xe2, 0x6d,
+ 0xcc, 0xb1, 0xe0, 0x97, 0x8f, 0xa6, 0x53, 0x09, 0xcd, 0xaa, 0x2a, 0x65,
+ 0xe5, 0x39, 0x4f, 0x1e, 0x83, 0xa5, 0x6e, 0x5c, 0x98, 0xa2, 0x24, 0x26,
+ 0xe6, 0xfb, 0xa1, 0xed, 0x93, 0xc7, 0x2e, 0x02, 0xc6, 0x4d, 0x4a, 0xbf,
+ 0xb0, 0x42, 0xdf, 0x78, 0xda, 0xb3, 0xa8, 0xf9, 0x6d, 0xff, 0x21, 0x85,
+ 0x53, 0x36, 0x60, 0x4c, 0x76, 0xce, 0xec, 0x38, 0xdc, 0xd6, 0x51, 0x80,
+ 0xf0, 0xc5, 0xd6, 0xe5, 0xd4, 0x4d, 0x27, 0x64, 0xab, 0x9b, 0xc7, 0x3e,
+ 0x71, 0xfb, 0x48, 0x97, 0xb8, 0x33, 0x6d, 0xc9, 0x13, 0x07, 0xee, 0x96,
+ 0xa2, 0x1b, 0x18, 0x15, 0xf6, 0x5c, 0x4c, 0x40, 0xed, 0xb3, 0xc2, 0xec,
+ 0xff, 0x71, 0xc1, 0xe3, 0x47, 0xff, 0xd4, 0xb9, 0x00, 0xb4, 0x37, 0x42,
+ 0xda, 0x20, 0xc9, 0xea, 0x6e, 0x8a, 0xee, 0x14, 0x06, 0xae, 0x7d, 0xa2,
+ 0x59, 0x98, 0x88, 0xa8, 0x1b, 0x6f, 0x2d, 0xf4, 0xf2, 0xc9, 0x14, 0x5f,
+ 0x26, 0xcf, 0x2c, 0x8d, 0x7e, 0xed, 0x37, 0xc0, 0xa9, 0xd5, 0x39, 0xb9,
+ 0x82, 0xbf, 0x19, 0x0c, 0xea, 0x34, 0xaf, 0x00, 0x21, 0x68, 0xf8, 0xad,
+ 0x73, 0xe2, 0xc9, 0x32, 0xda, 0x38, 0x25, 0x0b, 0x55, 0xd3, 0x9a, 0x1d,
+ 0xf0, 0x68, 0x86, 0xed, 0x2e, 0x41, 0x34, 0xef, 0x7c, 0xa5, 0x50, 0x1d,
+ 0xbf, 0x3a, 0xf9, 0xd3, 0xc1, 0x08, 0x0c, 0xe6, 0xed, 0x1e, 0x8a, 0x58,
+ 0x25, 0xe4, 0xb8, 0x77, 0xad, 0x2d, 0x6e, 0xf5, 0x52, 0xdd, 0xb4, 0x74,
+ 0x8f, 0xab, 0x49, 0x2e, 0x9d, 0x3b, 0x93, 0x34, 0x28, 0x1f, 0x78, 0xce,
+ 0x94, 0xea, 0xc7, 0xbd, 0xd3, 0xc9, 0x6d, 0x1c, 0xde, 0x5c, 0x32, 0xf3,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0b:1d:b1:a9:19:f2:4c:3c:4e:fc:b5:7a:6a:4e:6c:bf
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority
+ Validity
+ Not Before: Aug 23 00:00:00 2012 GMT
+ Not After : Aug 22 23:59:59 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Extended Validation SSL CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:9e:c6:21:cd:2e:3d:d0:bb:2a:4d:a4:7b:1f:a8:
+ 1a:c2:03:a6:ff:43:62:5b:bf:91:d1:66:52:a9:81:
+ 90:68:31:86:16:bb:1d:85:58:a9:7e:91:6a:1e:4c:
+ 31:ca:21:c4:be:70:1b:9f:8c:e4:05:2d:9c:ed:11:
+ 79:ad:8f:9c:25:86:4c:ba:f2:e5:62:79:8e:22:5f:
+ 85:7c:22:35:38:23:8d:80:3c:ac:cc:2d:fc:58:f2:
+ 35:bf:66:5b:eb:c1:24:f8:70:80:74:32:f9:46:de:
+ 32:19:80:8c:b7:e7:1a:a1:aa:64:98:8d:ca:ce:0e:
+ dc:6b:f7:e2:90:0a:6c:1c:a5:f4:90:32:52:e5:f1:
+ 00:42:31:91:48:42:89:a8:5d:7f:63:8d:31:b2:d6:
+ 48:5c:45:45:22:c9:c5:59:12:ab:41:94:ea:fe:9c:
+ 46:4d:9a:bc:9c:e0:e2:c6:46:b3:e6:7f:dc:f5:0f:
+ a3:13:45:86:6d:79:78:fc:e1:50:cf:09:86:e5:9f:
+ bf:cb:3a:d4:e0:b1:d4:ff:a8:3f:7d:62:1f:c0:6d:
+ 78:48:c3:d7:a3:a5:23:61:c5:3e:35:4d:b2:e5:f8:
+ fd:94:4b:bc:73:53:af:e3:9a:69:55:be:cb:67:ab:
+ e1:be:ef:1b:c2:4d:ac:cb:29:5c:bc:ed:b8:62:9d:
+ 10:e9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://EVSecure-ocsp.geotrust.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://EVSecure-crl.geotrust.com/GeoTrustPCA.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-253
+ X509v3 Subject Key Identifier:
+ 6F:26:56:D9:5C:E7:F7:C9:04:20:F8:1E:BA:7C:91:27:2F:8C:FA:07
+ X509v3 Authority Key Identifier:
+ keyid:2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 92:77:e9:57:c9:eb:c4:45:6f:c9:4c:6e:7d:00:12:71:a5:e3:
+ 39:fe:13:84:49:6c:e7:49:71:f5:2c:c7:c0:36:c2:08:58:f3:
+ 83:75:c5:72:d8:8d:78:f4:65:ea:8c:d5:e3:a5:0e:a9:ad:eb:
+ e3:a1:23:ae:93:b7:d8:75:75:4a:59:cb:f2:9e:db:40:bf:4e:
+ 89:fe:95:42:29:34:7b:f4:dd:6a:0d:74:5f:c7:11:13:2e:dd:
+ 11:6e:c6:e3:5b:b3:cf:a6:8d:e5:f7:67:7b:ba:b3:b3:69:70:
+ 14:b0:c2:99:b4:d2:76:5b:38:17:39:45:1b:82:f1:53:b8:3d:
+ 55:39:0b:7f:ff:98:ad:6e:96:9a:b6:6a:4c:7a:5e:bd:b1:86:
+ 12:9d:7c:2c:62:bb:09:93:5f:3f:d8:b5:8a:c3:49:28:0f:0b:
+ f9:39:22:1a:fe:5d:d3:e8:18:5f:9d:5f:b4:c0:20:c6:a9:49:
+ 0d:55:73:6a:09:7a:ff:a2:99:bf:d8:bb:91:dc:30:39:ae:28:
+ 4b:f6:c5:77:24:e8:d6:c6:a7:a0:4e:f2:a6:99:75:cd:dd:57:
+ dd:0a:47:92:cb:bb:b7:48:fa:21:f0:69:21:ff:e5:0c:aa:0c:
+ b1:ea:dd:05:1c:19:8e:d1:2a:79:68:02:5e:cc:38:e6:29:c4:
+ 77:f5:19:1c
+-----BEGIN CERTIFICATE-----
+MIIEmjCCA4KgAwIBAgIQCx2xqRnyTDxO/LV6ak5svzANBgkqhkiG9w0BAQUFADBY
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
+R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMjA4
+MjMwMDAwMDBaFw0yMjA4MjIyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBFeHRlbmRlZCBWYWxp
+ZGF0aW9uIFNTTCBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAnsYhzS490LsqTaR7H6gawgOm/0NiW7+R0WZSqYGQaDGGFrsdhVipfpFqHkwx
+yiHEvnAbn4zkBS2c7RF5rY+cJYZMuvLlYnmOIl+FfCI1OCONgDyszC38WPI1v2Zb
+68Ek+HCAdDL5Rt4yGYCMt+caoapkmI3Kzg7ca/fikApsHKX0kDJS5fEAQjGRSEKJ
+qF1/Y40xstZIXEVFIsnFWRKrQZTq/pxGTZq8nODixkaz5n/c9Q+jE0WGbXl4/OFQ
+zwmG5Z+/yzrU4LHU/6g/fWIfwG14SMPXo6UjYcU+NU2y5fj9lEu8c1Ov45ppVb7L
+Z6vhvu8bwk2syylcvO24Yp0Q6QIDAQABo4IBXjCCAVowPQYIKwYBBQUHAQEEMTAv
+MC0GCCsGAQUFBzABhiFodHRwOi8vRVZTZWN1cmUtb2NzcC5nZW90cnVzdC5jb20w
+EgYDVR0TAQH/BAgwBgEB/wIBADBGBgNVHSAEPzA9MDsGBFUdIAAwMzAxBggrBgEF
+BQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczBBBgNV
+HR8EOjA4MDagNKAyhjBodHRwOi8vRVZTZWN1cmUtY3JsLmdlb3RydXN0LmNvbS9H
+ZW9UcnVzdFBDQS5jcmwwDgYDVR0PAQH/BAQDAgEGMCoGA1UdEQQjMCGkHzAdMRsw
+GQYDVQQDExJWZXJpU2lnbk1QS0ktMi0yNTMwHQYDVR0OBBYEFG8mVtlc5/fJBCD4
+Hrp8kScvjPoHMB8GA1UdIwQYMBaAFCzVUEGXFYvwjzZhW0r7a9mZyTOSMA0GCSqG
+SIb3DQEBBQUAA4IBAQCSd+lXyevERW/JTG59ABJxpeM5/hOESWznSXH1LMfANsII
+WPODdcVy2I149GXqjNXjpQ6prevjoSOuk7fYdXVKWcvynttAv06J/pVCKTR79N1q
+DXRfxxETLt0RbsbjW7PPpo3l92d7urOzaXAUsMKZtNJ2WzgXOUUbgvFTuD1VOQt/
+/5itbpaatmpMel69sYYSnXwsYrsJk18/2LWKw0koDwv5OSIa/l3T6BhfnV+0wCDG
+qUkNVXNqCXr/opm/2LuR3DA5rihL9sV3JOjWxqegTvKmmXXN3VfdCkeSy7u3SPoh
+8Gkh/+UMqgyx6t0FHBmO0Sp5aAJezDjmKcR39Rkc
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert22[] = {
+ 0x30, 0x82, 0x04, 0x9a, 0x30, 0x82, 0x03, 0x82, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x0b, 0x1d, 0xb1, 0xa9, 0x19, 0xf2, 0x4c, 0x3c, 0x4e,
+ 0xfc, 0xb5, 0x7a, 0x6a, 0x4e, 0x6c, 0xbf, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x58,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69,
+ 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30, 0x38,
+ 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32,
+ 0x32, 0x30, 0x38, 0x32, 0x32, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+ 0x30, 0x58, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x28, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69,
+ 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43,
+ 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0x9e, 0xc6, 0x21, 0xcd, 0x2e, 0x3d, 0xd0, 0xbb, 0x2a,
+ 0x4d, 0xa4, 0x7b, 0x1f, 0xa8, 0x1a, 0xc2, 0x03, 0xa6, 0xff, 0x43, 0x62,
+ 0x5b, 0xbf, 0x91, 0xd1, 0x66, 0x52, 0xa9, 0x81, 0x90, 0x68, 0x31, 0x86,
+ 0x16, 0xbb, 0x1d, 0x85, 0x58, 0xa9, 0x7e, 0x91, 0x6a, 0x1e, 0x4c, 0x31,
+ 0xca, 0x21, 0xc4, 0xbe, 0x70, 0x1b, 0x9f, 0x8c, 0xe4, 0x05, 0x2d, 0x9c,
+ 0xed, 0x11, 0x79, 0xad, 0x8f, 0x9c, 0x25, 0x86, 0x4c, 0xba, 0xf2, 0xe5,
+ 0x62, 0x79, 0x8e, 0x22, 0x5f, 0x85, 0x7c, 0x22, 0x35, 0x38, 0x23, 0x8d,
+ 0x80, 0x3c, 0xac, 0xcc, 0x2d, 0xfc, 0x58, 0xf2, 0x35, 0xbf, 0x66, 0x5b,
+ 0xeb, 0xc1, 0x24, 0xf8, 0x70, 0x80, 0x74, 0x32, 0xf9, 0x46, 0xde, 0x32,
+ 0x19, 0x80, 0x8c, 0xb7, 0xe7, 0x1a, 0xa1, 0xaa, 0x64, 0x98, 0x8d, 0xca,
+ 0xce, 0x0e, 0xdc, 0x6b, 0xf7, 0xe2, 0x90, 0x0a, 0x6c, 0x1c, 0xa5, 0xf4,
+ 0x90, 0x32, 0x52, 0xe5, 0xf1, 0x00, 0x42, 0x31, 0x91, 0x48, 0x42, 0x89,
+ 0xa8, 0x5d, 0x7f, 0x63, 0x8d, 0x31, 0xb2, 0xd6, 0x48, 0x5c, 0x45, 0x45,
+ 0x22, 0xc9, 0xc5, 0x59, 0x12, 0xab, 0x41, 0x94, 0xea, 0xfe, 0x9c, 0x46,
+ 0x4d, 0x9a, 0xbc, 0x9c, 0xe0, 0xe2, 0xc6, 0x46, 0xb3, 0xe6, 0x7f, 0xdc,
+ 0xf5, 0x0f, 0xa3, 0x13, 0x45, 0x86, 0x6d, 0x79, 0x78, 0xfc, 0xe1, 0x50,
+ 0xcf, 0x09, 0x86, 0xe5, 0x9f, 0xbf, 0xcb, 0x3a, 0xd4, 0xe0, 0xb1, 0xd4,
+ 0xff, 0xa8, 0x3f, 0x7d, 0x62, 0x1f, 0xc0, 0x6d, 0x78, 0x48, 0xc3, 0xd7,
+ 0xa3, 0xa5, 0x23, 0x61, 0xc5, 0x3e, 0x35, 0x4d, 0xb2, 0xe5, 0xf8, 0xfd,
+ 0x94, 0x4b, 0xbc, 0x73, 0x53, 0xaf, 0xe3, 0x9a, 0x69, 0x55, 0xbe, 0xcb,
+ 0x67, 0xab, 0xe1, 0xbe, 0xef, 0x1b, 0xc2, 0x4d, 0xac, 0xcb, 0x29, 0x5c,
+ 0xbc, 0xed, 0xb8, 0x62, 0x9d, 0x10, 0xe9, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x5e, 0x30, 0x82, 0x01, 0x5a, 0x30, 0x3d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f,
+ 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67,
+ 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x46, 0x06, 0x03, 0x55,
+ 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x04, 0x55, 0x1d,
+ 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x41, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x3a, 0x30, 0x38, 0x30, 0x36, 0xa0, 0x34, 0xa0, 0x32,
+ 0x86, 0x30, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65,
+ 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d,
+ 0x11, 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30,
+ 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x32,
+ 0x35, 0x33, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x6f, 0x26, 0x56, 0xd9, 0x5c, 0xe7, 0xf7, 0xc9, 0x04, 0x20, 0xf8,
+ 0x1e, 0xba, 0x7c, 0x91, 0x27, 0x2f, 0x8c, 0xfa, 0x07, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x2c, 0xd5,
+ 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b, 0x4a, 0xfb,
+ 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x92, 0x77, 0xe9, 0x57, 0xc9, 0xeb, 0xc4, 0x45, 0x6f, 0xc9,
+ 0x4c, 0x6e, 0x7d, 0x00, 0x12, 0x71, 0xa5, 0xe3, 0x39, 0xfe, 0x13, 0x84,
+ 0x49, 0x6c, 0xe7, 0x49, 0x71, 0xf5, 0x2c, 0xc7, 0xc0, 0x36, 0xc2, 0x08,
+ 0x58, 0xf3, 0x83, 0x75, 0xc5, 0x72, 0xd8, 0x8d, 0x78, 0xf4, 0x65, 0xea,
+ 0x8c, 0xd5, 0xe3, 0xa5, 0x0e, 0xa9, 0xad, 0xeb, 0xe3, 0xa1, 0x23, 0xae,
+ 0x93, 0xb7, 0xd8, 0x75, 0x75, 0x4a, 0x59, 0xcb, 0xf2, 0x9e, 0xdb, 0x40,
+ 0xbf, 0x4e, 0x89, 0xfe, 0x95, 0x42, 0x29, 0x34, 0x7b, 0xf4, 0xdd, 0x6a,
+ 0x0d, 0x74, 0x5f, 0xc7, 0x11, 0x13, 0x2e, 0xdd, 0x11, 0x6e, 0xc6, 0xe3,
+ 0x5b, 0xb3, 0xcf, 0xa6, 0x8d, 0xe5, 0xf7, 0x67, 0x7b, 0xba, 0xb3, 0xb3,
+ 0x69, 0x70, 0x14, 0xb0, 0xc2, 0x99, 0xb4, 0xd2, 0x76, 0x5b, 0x38, 0x17,
+ 0x39, 0x45, 0x1b, 0x82, 0xf1, 0x53, 0xb8, 0x3d, 0x55, 0x39, 0x0b, 0x7f,
+ 0xff, 0x98, 0xad, 0x6e, 0x96, 0x9a, 0xb6, 0x6a, 0x4c, 0x7a, 0x5e, 0xbd,
+ 0xb1, 0x86, 0x12, 0x9d, 0x7c, 0x2c, 0x62, 0xbb, 0x09, 0x93, 0x5f, 0x3f,
+ 0xd8, 0xb5, 0x8a, 0xc3, 0x49, 0x28, 0x0f, 0x0b, 0xf9, 0x39, 0x22, 0x1a,
+ 0xfe, 0x5d, 0xd3, 0xe8, 0x18, 0x5f, 0x9d, 0x5f, 0xb4, 0xc0, 0x20, 0xc6,
+ 0xa9, 0x49, 0x0d, 0x55, 0x73, 0x6a, 0x09, 0x7a, 0xff, 0xa2, 0x99, 0xbf,
+ 0xd8, 0xbb, 0x91, 0xdc, 0x30, 0x39, 0xae, 0x28, 0x4b, 0xf6, 0xc5, 0x77,
+ 0x24, 0xe8, 0xd6, 0xc6, 0xa7, 0xa0, 0x4e, 0xf2, 0xa6, 0x99, 0x75, 0xcd,
+ 0xdd, 0x57, 0xdd, 0x0a, 0x47, 0x92, 0xcb, 0xbb, 0xb7, 0x48, 0xfa, 0x21,
+ 0xf0, 0x69, 0x21, 0xff, 0xe5, 0x0c, 0xaa, 0x0c, 0xb1, 0xea, 0xdd, 0x05,
+ 0x1c, 0x19, 0x8e, 0xd1, 0x2a, 0x79, 0x68, 0x02, 0x5e, 0xcc, 0x38, 0xe6,
+ 0x29, 0xc4, 0x77, 0xf5, 0x19, 0x1c,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 3740804 (0x391484)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority
+ Validity
+ Not Before: Jan 1 07:00:00 2014 GMT
+ Not After : May 30 07:00:00 2031 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:ed:c1:03:fc:f6:8f:fc:02:b1:6f:5b:9f:48:
+ d9:9d:79:e2:a2:b7:03:61:56:18:c3:47:b6:d7:ca:
+ 3d:35:2e:89:43:f7:a1:69:9b:de:8a:1a:fd:13:20:
+ 9c:b4:49:77:32:29:56:fd:b9:ec:8c:dd:22:fa:72:
+ dc:27:61:97:ee:f6:5a:84:ec:6e:19:b9:89:2c:dc:
+ 84:5b:d5:74:fb:6b:5f:c5:89:a5:10:52:89:46:55:
+ f4:b8:75:1c:e6:7f:e4:54:ae:4b:f8:55:72:57:02:
+ 19:f8:17:71:59:eb:1e:28:07:74:c5:9d:48:be:6c:
+ b4:f4:a4:b0:f3:64:37:79:92:c0:ec:46:5e:7f:e1:
+ 6d:53:4c:62:af:cd:1f:0b:63:bb:3a:9d:fb:fc:79:
+ 00:98:61:74:cf:26:82:40:63:f3:b2:72:6a:19:0d:
+ 99:ca:d4:0e:75:cc:37:fb:8b:89:c1:59:f1:62:7f:
+ 5f:b3:5f:65:30:f8:a7:b7:4d:76:5a:1e:76:5e:34:
+ c0:e8:96:56:99:8a:b3:f0:7f:a4:cd:bd:dc:32:31:
+ 7c:91:cf:e0:5f:11:f8:6b:aa:49:5c:d1:99:94:d1:
+ a2:e3:63:5b:09:76:b5:56:62:e1:4b:74:1d:96:d4:
+ 26:d4:08:04:59:d0:98:0e:0e:e6:de:fc:c3:ec:1f:
+ 90:f1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27
+ X509v3 Authority Key Identifier:
+ keyid:BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.starfieldtech.com/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.starfieldtech.com/sfroot.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://certs.starfieldtech.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 85:63:c1:d9:dd:b9:ff:a9:bd:a6:19:dc:bf:13:3a:11:38:22:
+ 54:b1:ac:05:10:fb:7c:b3:96:3f:31:8b:66:ff:88:f3:e1:bf:
+ fb:c7:1f:00:ff:46:6a:8b:61:32:c9:01:51:76:fb:9a:c6:fa:
+ 20:51:c8:46:c4:98:d7:79:a3:e3:04:72:3f:8b:4d:34:53:67:
+ ec:33:2c:7b:e8:94:01:28:7c:3a:34:5b:02:77:16:8d:40:25:
+ 33:b0:bc:6c:97:d7:05:7a:ff:8c:85:ce:6f:a0:53:00:17:6e:
+ 1e:6c:bd:22:d7:0a:88:37:f6:7d:eb:99:41:ef:27:cb:8c:60:
+ 6b:4c:01:7e:65:50:0b:4f:b8:95:9a:9a:6e:34:fd:73:3a:33:
+ f1:91:d5:f3:4e:2d:74:e8:ef:d3:90:35:f1:06:68:64:d4:d0:
+ 13:fd:52:d3:c6:6d:c1:3a:8a:31:dd:05:26:35:4a:8c:65:b8:
+ 52:6b:81:ec:d2:9c:b5:34:10:97:9c:3e:c6:2f:ed:8e:42:42:
+ 24:2e:e9:73:9a:25:f9:11:f1:f2:23:69:cb:e5:94:69:a0:d2:
+ dc:b0:fc:44:89:ac:17:a8:cc:d5:37:77:16:c5:80:b9:0c:8f:
+ 57:02:55:99:85:7b:49:f0:2e:5b:a0:c2:57:53:5d:a2:e8:a6:
+ 37:c3:01:fa
+-----BEGIN CERTIFICATE-----
+MIIEoDCCA4igAwIBAgIDORSEMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAlVT
+MSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQL
+EylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x
+NDAxMDEwNzAwMDBaFw0zMTA1MzAwNzAwMDBaMIGPMQswCQYDVQQGEwJVUzEQMA4G
+A1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UEChMcU3Rh
+cmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UEAxMpU3RhcmZpZWxkIFJv
+b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC97cED/PaP/AKxb1ufSNmdeeKitwNhVhjDR7bXyj01LolD
+96Fpm96KGv0TIJy0SXcyKVb9ueyM3SL6ctwnYZfu9lqE7G4ZuYks3IRb1XT7a1/F
+iaUQUolGVfS4dRzmf+RUrkv4VXJXAhn4F3FZ6x4oB3TFnUi+bLT0pLDzZDd5ksDs
+Rl5/4W1TTGKvzR8LY7s6nfv8eQCYYXTPJoJAY/OycmoZDZnK1A51zDf7i4nBWfFi
+f1+zX2Uw+Ke3TXZaHnZeNMDollaZirPwf6TNvdwyMXyRz+BfEfhrqklc0ZmU0aLj
+Y1sJdrVWYuFLdB2W1CbUCARZ0JgODube/MPsH5DxAgMBAAGjggEpMIIBJTAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfAwyH6fZMH/E
+fWijYqihzqsHWycwHwYDVR0jBBgwFoAUv1+30c7dH4b0W1Ws3NcQwg6piOcwOgYI
+KwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0
+ZWNoLmNvbS8wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL2NybC5zdGFyZmllbGR0
+ZWNoLmNvbS9zZnJvb3QuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF
+BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQCFY8HZ3bn/qb2mGdy/EzoROCJUsawFEPt8s5Y/
+MYtm/4jz4b/7xx8A/0Zqi2EyyQFRdvuaxvogUchGxJjXeaPjBHI/i000U2fsMyx7
+6JQBKHw6NFsCdxaNQCUzsLxsl9cFev+Mhc5voFMAF24ebL0i1wqIN/Z965lB7yfL
+jGBrTAF+ZVALT7iVmppuNP1zOjPxkdXzTi106O/TkDXxBmhk1NAT/VLTxm3BOoox
+3QUmNUqMZbhSa4Hs0py1NBCXnD7GL+2OQkIkLulzmiX5EfHyI2nL5ZRpoNLcsPxE
+iawXqMzVN3cWxYC5DI9XAlWZhXtJ8C5boMJXU12i6KY3wwH6
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert23[] = {
+ 0x30, 0x82, 0x04, 0xa0, 0x30, 0x82, 0x03, 0x88, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x39, 0x14, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x68, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63,
+ 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20,
+ 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x34, 0x30, 0x31, 0x30, 0x31, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x33, 0x30, 0x30, 0x37, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x30, 0x81, 0x8f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e,
+ 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a,
+ 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25,
+ 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61,
+ 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e,
+ 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x29,
+ 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xbd, 0xed, 0xc1, 0x03, 0xfc, 0xf6, 0x8f, 0xfc, 0x02, 0xb1,
+ 0x6f, 0x5b, 0x9f, 0x48, 0xd9, 0x9d, 0x79, 0xe2, 0xa2, 0xb7, 0x03, 0x61,
+ 0x56, 0x18, 0xc3, 0x47, 0xb6, 0xd7, 0xca, 0x3d, 0x35, 0x2e, 0x89, 0x43,
+ 0xf7, 0xa1, 0x69, 0x9b, 0xde, 0x8a, 0x1a, 0xfd, 0x13, 0x20, 0x9c, 0xb4,
+ 0x49, 0x77, 0x32, 0x29, 0x56, 0xfd, 0xb9, 0xec, 0x8c, 0xdd, 0x22, 0xfa,
+ 0x72, 0xdc, 0x27, 0x61, 0x97, 0xee, 0xf6, 0x5a, 0x84, 0xec, 0x6e, 0x19,
+ 0xb9, 0x89, 0x2c, 0xdc, 0x84, 0x5b, 0xd5, 0x74, 0xfb, 0x6b, 0x5f, 0xc5,
+ 0x89, 0xa5, 0x10, 0x52, 0x89, 0x46, 0x55, 0xf4, 0xb8, 0x75, 0x1c, 0xe6,
+ 0x7f, 0xe4, 0x54, 0xae, 0x4b, 0xf8, 0x55, 0x72, 0x57, 0x02, 0x19, 0xf8,
+ 0x17, 0x71, 0x59, 0xeb, 0x1e, 0x28, 0x07, 0x74, 0xc5, 0x9d, 0x48, 0xbe,
+ 0x6c, 0xb4, 0xf4, 0xa4, 0xb0, 0xf3, 0x64, 0x37, 0x79, 0x92, 0xc0, 0xec,
+ 0x46, 0x5e, 0x7f, 0xe1, 0x6d, 0x53, 0x4c, 0x62, 0xaf, 0xcd, 0x1f, 0x0b,
+ 0x63, 0xbb, 0x3a, 0x9d, 0xfb, 0xfc, 0x79, 0x00, 0x98, 0x61, 0x74, 0xcf,
+ 0x26, 0x82, 0x40, 0x63, 0xf3, 0xb2, 0x72, 0x6a, 0x19, 0x0d, 0x99, 0xca,
+ 0xd4, 0x0e, 0x75, 0xcc, 0x37, 0xfb, 0x8b, 0x89, 0xc1, 0x59, 0xf1, 0x62,
+ 0x7f, 0x5f, 0xb3, 0x5f, 0x65, 0x30, 0xf8, 0xa7, 0xb7, 0x4d, 0x76, 0x5a,
+ 0x1e, 0x76, 0x5e, 0x34, 0xc0, 0xe8, 0x96, 0x56, 0x99, 0x8a, 0xb3, 0xf0,
+ 0x7f, 0xa4, 0xcd, 0xbd, 0xdc, 0x32, 0x31, 0x7c, 0x91, 0xcf, 0xe0, 0x5f,
+ 0x11, 0xf8, 0x6b, 0xaa, 0x49, 0x5c, 0xd1, 0x99, 0x94, 0xd1, 0xa2, 0xe3,
+ 0x63, 0x5b, 0x09, 0x76, 0xb5, 0x56, 0x62, 0xe1, 0x4b, 0x74, 0x1d, 0x96,
+ 0xd4, 0x26, 0xd4, 0x08, 0x04, 0x59, 0xd0, 0x98, 0x0e, 0x0e, 0xe6, 0xde,
+ 0xfc, 0xc3, 0xec, 0x1f, 0x90, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x29, 0x30, 0x82, 0x01, 0x25, 0x30, 0x0f, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x7c, 0x0c, 0x32, 0x1f, 0xa7, 0xd9, 0x30, 0x7f, 0xc4,
+ 0x7d, 0x68, 0xa3, 0x62, 0xa8, 0xa1, 0xce, 0xab, 0x07, 0x5b, 0x27, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0xbf, 0x5f, 0xb7, 0xd1, 0xce, 0xdd, 0x1f, 0x86, 0xf4, 0x5b, 0x55, 0xac,
+ 0xdc, 0xd7, 0x10, 0xc2, 0x0e, 0xa9, 0x88, 0xe7, 0x30, 0x3a, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2e, 0x30, 0x2c,
+ 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74,
+ 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x38, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0xa0, 0x2b, 0xa0,
+ 0x29, 0x86, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74,
+ 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x72, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x85, 0x63, 0xc1, 0xd9,
+ 0xdd, 0xb9, 0xff, 0xa9, 0xbd, 0xa6, 0x19, 0xdc, 0xbf, 0x13, 0x3a, 0x11,
+ 0x38, 0x22, 0x54, 0xb1, 0xac, 0x05, 0x10, 0xfb, 0x7c, 0xb3, 0x96, 0x3f,
+ 0x31, 0x8b, 0x66, 0xff, 0x88, 0xf3, 0xe1, 0xbf, 0xfb, 0xc7, 0x1f, 0x00,
+ 0xff, 0x46, 0x6a, 0x8b, 0x61, 0x32, 0xc9, 0x01, 0x51, 0x76, 0xfb, 0x9a,
+ 0xc6, 0xfa, 0x20, 0x51, 0xc8, 0x46, 0xc4, 0x98, 0xd7, 0x79, 0xa3, 0xe3,
+ 0x04, 0x72, 0x3f, 0x8b, 0x4d, 0x34, 0x53, 0x67, 0xec, 0x33, 0x2c, 0x7b,
+ 0xe8, 0x94, 0x01, 0x28, 0x7c, 0x3a, 0x34, 0x5b, 0x02, 0x77, 0x16, 0x8d,
+ 0x40, 0x25, 0x33, 0xb0, 0xbc, 0x6c, 0x97, 0xd7, 0x05, 0x7a, 0xff, 0x8c,
+ 0x85, 0xce, 0x6f, 0xa0, 0x53, 0x00, 0x17, 0x6e, 0x1e, 0x6c, 0xbd, 0x22,
+ 0xd7, 0x0a, 0x88, 0x37, 0xf6, 0x7d, 0xeb, 0x99, 0x41, 0xef, 0x27, 0xcb,
+ 0x8c, 0x60, 0x6b, 0x4c, 0x01, 0x7e, 0x65, 0x50, 0x0b, 0x4f, 0xb8, 0x95,
+ 0x9a, 0x9a, 0x6e, 0x34, 0xfd, 0x73, 0x3a, 0x33, 0xf1, 0x91, 0xd5, 0xf3,
+ 0x4e, 0x2d, 0x74, 0xe8, 0xef, 0xd3, 0x90, 0x35, 0xf1, 0x06, 0x68, 0x64,
+ 0xd4, 0xd0, 0x13, 0xfd, 0x52, 0xd3, 0xc6, 0x6d, 0xc1, 0x3a, 0x8a, 0x31,
+ 0xdd, 0x05, 0x26, 0x35, 0x4a, 0x8c, 0x65, 0xb8, 0x52, 0x6b, 0x81, 0xec,
+ 0xd2, 0x9c, 0xb5, 0x34, 0x10, 0x97, 0x9c, 0x3e, 0xc6, 0x2f, 0xed, 0x8e,
+ 0x42, 0x42, 0x24, 0x2e, 0xe9, 0x73, 0x9a, 0x25, 0xf9, 0x11, 0xf1, 0xf2,
+ 0x23, 0x69, 0xcb, 0xe5, 0x94, 0x69, 0xa0, 0xd2, 0xdc, 0xb0, 0xfc, 0x44,
+ 0x89, 0xac, 0x17, 0xa8, 0xcc, 0xd5, 0x37, 0x77, 0x16, 0xc5, 0x80, 0xb9,
+ 0x0c, 0x8f, 0x57, 0x02, 0x55, 0x99, 0x85, 0x7b, 0x49, 0xf0, 0x2e, 0x5b,
+ 0xa0, 0xc2, 0x57, 0x53, 0x5d, 0xa2, 0xe8, 0xa6, 0x37, 0xc3, 0x01, 0xfa,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 28:1c:89:29:66:14:43:80:42:63:55:3a:32:40:ae:b3
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., OU=(c) 2008 GeoTrust Inc. - For authorized use only, CN=GeoTrust Primary Certification Authority - G3
+ Validity
+ Not Before: Jun 30 00:00:00 2015 GMT
+ Not After : Jun 29 23:59:59 2025 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c0:9e:3a:0f:9a:b2:ba:d3:d2:dc:15:ec:d0:30:
+ 54:59:30:4d:40:51:ae:42:71:71:d2:8d:53:73:81:
+ fe:b8:e0:c4:96:c5:8e:7e:c2:f1:b7:63:4a:cf:a7:
+ 1e:3f:a8:e7:ce:53:a0:fa:2d:f7:d6:e6:ce:70:11:
+ a6:ee:e1:03:52:d2:68:de:3d:08:0d:87:fd:1c:d7:
+ 0b:97:62:6d:82:30:76:1b:47:3a:c4:f7:ce:ed:1d:
+ 7c:8c:b7:17:8e:53:80:1e:1d:0f:5d:8c:f9:90:e4:
+ 04:1e:02:7e:cb:b0:49:ef:da:52:25:fb:fb:67:ed:
+ dd:84:74:59:84:0e:f3:de:70:66:8d:e4:52:38:f7:
+ 53:5a:37:13:67:0b:3e:bb:a8:58:b7:2e:ed:ff:b7:
+ 5e:11:73:b9:77:45:52:67:46:ae:c4:dc:24:81:89:
+ 76:0a:ca:a1:6c:66:73:04:82:aa:f5:70:6c:5f:1b:
+ 9a:00:79:46:d6:7f:7a:26:17:30:cf:39:4b:2c:74:
+ d9:89:44:76:10:d0:ed:f7:8b:bb:89:05:75:4d:0b:
+ 0d:b3:da:e9:bf:f1:6a:7d:2a:11:db:1e:9f:8c:e3:
+ c4:06:69:e1:1d:88:45:39:d1:6e:55:d8:aa:b7:9b:
+ 6f:ea:f4:de:ac:17:11:92:5d:40:9b:83:7b:9a:e2:
+ f7:a9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.23.140.1.2.1
+ CPS: https://www.geotrust.com/resources/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/GeoTrustPCA-G3.crl
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ F3:B5:56:0C:C4:09:B0:B4:CF:1F:AA:F9:DD:23:56:F0:77:E8:A1:F9
+ X509v3 Authority Key Identifier:
+ keyid:C4:79:CA:8E:A1:4E:03:1D:1C:DC:6B:DB:31:5B:94:3E:3F:30:7F:2D
+
+ Signature Algorithm: sha256WithRSAEncryption
+ c3:7e:d8:83:4b:04:4c:55:29:2a:4f:14:9d:9a:6e:de:90:70:
+ c1:a4:26:4c:88:8e:78:48:ef:bd:9c:b0:a0:f5:f0:66:fc:fe:
+ 59:26:e1:79:ef:c8:b7:60:64:a8:8b:47:ea:2f:e0:83:99:da:
+ 41:19:d7:c5:be:05:fa:f2:90:11:f0:0a:ff:6c:dc:05:b4:d8:
+ 06:6f:a4:6f:8d:be:20:2b:54:db:f9:a2:45:83:9a:1e:a5:21:
+ 89:35:1d:7c:20:5c:17:fd:04:2e:45:d8:b2:c6:f8:42:99:fc:
+ 54:08:4e:4b:80:5f:39:37:ba:95:4e:a6:37:0a:9e:93:5e:87:
+ 5b:e9:90:d6:a8:b6:65:08:8d:61:49:eb:83:20:a9:5d:1b:16:
+ 60:62:6b:2f:54:fb:5a:02:0d:7a:27:e2:4b:e1:05:14:c2:e4:
+ e9:f9:70:c0:d9:f7:34:65:0e:a2:91:4b:ac:28:f2:b7:08:0f:
+ 98:ca:d7:3e:70:b6:c8:0b:f1:8b:9c:51:f8:c6:10:6c:d2:53:
+ 4f:62:8c:11:00:3e:88:df:bf:e6:d2:cc:70:bd:ed:25:9c:fb:
+ dd:24:0a:bd:59:91:4a:42:03:38:12:71:32:88:76:a0:8e:7c:
+ bb:32:ef:88:2a:1b:d4:6a:6f:50:b9:52:67:8b:ab:30:fa:1f:
+ fd:e3:24:9a
+-----BEGIN CERTIFICATE-----
+MIIEpjCCA46gAwIBAgIQKByJKWYUQ4BCY1U6MkCuszANBgkqhkiG9w0BAQsFADCB
+mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
+MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEczMB4XDTE1MDYzMDAwMDAwMFoXDTI1MDYyOTIzNTk1OVowRzELMAkG
+A1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xIDAeBgNVBAMTF1JhcGlk
+U1NMIFNIQTI1NiBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAwJ46D5qyutPS3BXs0DBUWTBNQFGuQnFx0o1Tc4H+uODElsWOfsLxt2NKz6ce
+P6jnzlOg+i331ubOcBGm7uEDUtJo3j0IDYf9HNcLl2JtgjB2G0c6xPfO7R18jLcX
+jlOAHh0PXYz5kOQEHgJ+y7BJ79pSJfv7Z+3dhHRZhA7z3nBmjeRSOPdTWjcTZws+
+u6hYty7t/7deEXO5d0VSZ0auxNwkgYl2CsqhbGZzBIKq9XBsXxuaAHlG1n96Jhcw
+zzlLLHTZiUR2ENDt94u7iQV1TQsNs9rpv/FqfSoR2x6fjOPEBmnhHYhFOdFuVdiq
+t5tv6vTerBcRkl1Am4N7muL3qQIDAQABo4IBOjCCATYwLgYIKwYBBQUHAQEEIjAg
+MB4GCCsGAQUFBzABhhJodHRwOi8vZy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB
+/wIBADBJBgNVHSAEQjBAMD4GBmeBDAECATA0MDIGCCsGAQUFBwIBFiZodHRwczov
+L3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczA2BgNVHR8ELzAtMCugKaAn
+hiVodHRwOi8vZy5zeW1jYi5jb20vR2VvVHJ1c3RQQ0EtRzMuY3JsMB0GA1UdJQQW
+MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
+FPO1VgzECbC0zx+q+d0jVvB36KH5MB8GA1UdIwQYMBaAFMR5yo6hTgMdHNxr2zFb
+lD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQDDftiDSwRMVSkqTxSdmm7ekHDBpCZM
+iI54SO+9nLCg9fBm/P5ZJuF578i3YGSoi0fqL+CDmdpBGdfFvgX68pAR8Ar/bNwF
+tNgGb6Rvjb4gK1Tb+aJFg5oepSGJNR18IFwX/QQuRdiyxvhCmfxUCE5LgF85N7qV
+TqY3Cp6TXodb6ZDWqLZlCI1hSeuDIKldGxZgYmsvVPtaAg16J+JL4QUUwuTp+XDA
+2fc0ZQ6ikUusKPK3CA+Yytc+cLbIC/GLnFH4xhBs0lNPYowRAD6I37/m0sxwve0l
+nPvdJAq9WZFKQgM4EnEyiHagjny7Mu+IKhvUam9QuVJni6sw+h/94ySa
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert24[] = {
+ 0x30, 0x82, 0x04, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x28, 0x1c, 0x89, 0x29, 0x66, 0x14, 0x43, 0x80, 0x42,
+ 0x63, 0x55, 0x3a, 0x32, 0x40, 0xae, 0xb3, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x47, 0x65,
+ 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20,
+ 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c,
+ 0x79, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69,
+ 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x35, 0x30, 0x36, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x36, 0x32, 0x39, 0x32, 0x33,
+ 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64,
+ 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43,
+ 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xc0, 0x9e, 0x3a, 0x0f, 0x9a, 0xb2, 0xba, 0xd3, 0xd2,
+ 0xdc, 0x15, 0xec, 0xd0, 0x30, 0x54, 0x59, 0x30, 0x4d, 0x40, 0x51, 0xae,
+ 0x42, 0x71, 0x71, 0xd2, 0x8d, 0x53, 0x73, 0x81, 0xfe, 0xb8, 0xe0, 0xc4,
+ 0x96, 0xc5, 0x8e, 0x7e, 0xc2, 0xf1, 0xb7, 0x63, 0x4a, 0xcf, 0xa7, 0x1e,
+ 0x3f, 0xa8, 0xe7, 0xce, 0x53, 0xa0, 0xfa, 0x2d, 0xf7, 0xd6, 0xe6, 0xce,
+ 0x70, 0x11, 0xa6, 0xee, 0xe1, 0x03, 0x52, 0xd2, 0x68, 0xde, 0x3d, 0x08,
+ 0x0d, 0x87, 0xfd, 0x1c, 0xd7, 0x0b, 0x97, 0x62, 0x6d, 0x82, 0x30, 0x76,
+ 0x1b, 0x47, 0x3a, 0xc4, 0xf7, 0xce, 0xed, 0x1d, 0x7c, 0x8c, 0xb7, 0x17,
+ 0x8e, 0x53, 0x80, 0x1e, 0x1d, 0x0f, 0x5d, 0x8c, 0xf9, 0x90, 0xe4, 0x04,
+ 0x1e, 0x02, 0x7e, 0xcb, 0xb0, 0x49, 0xef, 0xda, 0x52, 0x25, 0xfb, 0xfb,
+ 0x67, 0xed, 0xdd, 0x84, 0x74, 0x59, 0x84, 0x0e, 0xf3, 0xde, 0x70, 0x66,
+ 0x8d, 0xe4, 0x52, 0x38, 0xf7, 0x53, 0x5a, 0x37, 0x13, 0x67, 0x0b, 0x3e,
+ 0xbb, 0xa8, 0x58, 0xb7, 0x2e, 0xed, 0xff, 0xb7, 0x5e, 0x11, 0x73, 0xb9,
+ 0x77, 0x45, 0x52, 0x67, 0x46, 0xae, 0xc4, 0xdc, 0x24, 0x81, 0x89, 0x76,
+ 0x0a, 0xca, 0xa1, 0x6c, 0x66, 0x73, 0x04, 0x82, 0xaa, 0xf5, 0x70, 0x6c,
+ 0x5f, 0x1b, 0x9a, 0x00, 0x79, 0x46, 0xd6, 0x7f, 0x7a, 0x26, 0x17, 0x30,
+ 0xcf, 0x39, 0x4b, 0x2c, 0x74, 0xd9, 0x89, 0x44, 0x76, 0x10, 0xd0, 0xed,
+ 0xf7, 0x8b, 0xbb, 0x89, 0x05, 0x75, 0x4d, 0x0b, 0x0d, 0xb3, 0xda, 0xe9,
+ 0xbf, 0xf1, 0x6a, 0x7d, 0x2a, 0x11, 0xdb, 0x1e, 0x9f, 0x8c, 0xe3, 0xc4,
+ 0x06, 0x69, 0xe1, 0x1d, 0x88, 0x45, 0x39, 0xd1, 0x6e, 0x55, 0xd8, 0xaa,
+ 0xb7, 0x9b, 0x6f, 0xea, 0xf4, 0xde, 0xac, 0x17, 0x11, 0x92, 0x5d, 0x40,
+ 0x9b, 0x83, 0x7b, 0x9a, 0xe2, 0xf7, 0xa9, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x3a, 0x30, 0x82, 0x01, 0x36, 0x30, 0x2e, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20,
+ 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73,
+ 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x49, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x42, 0x30, 0x40, 0x30, 0x3e, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02,
+ 0x01, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x36, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27,
+ 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73,
+ 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2d, 0x47, 0x33, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16,
+ 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0xf3, 0xb5, 0x56, 0x0c, 0xc4, 0x09, 0xb0, 0xb4, 0xcf, 0x1f, 0xaa,
+ 0xf9, 0xdd, 0x23, 0x56, 0xf0, 0x77, 0xe8, 0xa1, 0xf9, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc4, 0x79,
+ 0xca, 0x8e, 0xa1, 0x4e, 0x03, 0x1d, 0x1c, 0xdc, 0x6b, 0xdb, 0x31, 0x5b,
+ 0x94, 0x3e, 0x3f, 0x30, 0x7f, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0xc3, 0x7e, 0xd8, 0x83, 0x4b, 0x04, 0x4c, 0x55, 0x29, 0x2a,
+ 0x4f, 0x14, 0x9d, 0x9a, 0x6e, 0xde, 0x90, 0x70, 0xc1, 0xa4, 0x26, 0x4c,
+ 0x88, 0x8e, 0x78, 0x48, 0xef, 0xbd, 0x9c, 0xb0, 0xa0, 0xf5, 0xf0, 0x66,
+ 0xfc, 0xfe, 0x59, 0x26, 0xe1, 0x79, 0xef, 0xc8, 0xb7, 0x60, 0x64, 0xa8,
+ 0x8b, 0x47, 0xea, 0x2f, 0xe0, 0x83, 0x99, 0xda, 0x41, 0x19, 0xd7, 0xc5,
+ 0xbe, 0x05, 0xfa, 0xf2, 0x90, 0x11, 0xf0, 0x0a, 0xff, 0x6c, 0xdc, 0x05,
+ 0xb4, 0xd8, 0x06, 0x6f, 0xa4, 0x6f, 0x8d, 0xbe, 0x20, 0x2b, 0x54, 0xdb,
+ 0xf9, 0xa2, 0x45, 0x83, 0x9a, 0x1e, 0xa5, 0x21, 0x89, 0x35, 0x1d, 0x7c,
+ 0x20, 0x5c, 0x17, 0xfd, 0x04, 0x2e, 0x45, 0xd8, 0xb2, 0xc6, 0xf8, 0x42,
+ 0x99, 0xfc, 0x54, 0x08, 0x4e, 0x4b, 0x80, 0x5f, 0x39, 0x37, 0xba, 0x95,
+ 0x4e, 0xa6, 0x37, 0x0a, 0x9e, 0x93, 0x5e, 0x87, 0x5b, 0xe9, 0x90, 0xd6,
+ 0xa8, 0xb6, 0x65, 0x08, 0x8d, 0x61, 0x49, 0xeb, 0x83, 0x20, 0xa9, 0x5d,
+ 0x1b, 0x16, 0x60, 0x62, 0x6b, 0x2f, 0x54, 0xfb, 0x5a, 0x02, 0x0d, 0x7a,
+ 0x27, 0xe2, 0x4b, 0xe1, 0x05, 0x14, 0xc2, 0xe4, 0xe9, 0xf9, 0x70, 0xc0,
+ 0xd9, 0xf7, 0x34, 0x65, 0x0e, 0xa2, 0x91, 0x4b, 0xac, 0x28, 0xf2, 0xb7,
+ 0x08, 0x0f, 0x98, 0xca, 0xd7, 0x3e, 0x70, 0xb6, 0xc8, 0x0b, 0xf1, 0x8b,
+ 0x9c, 0x51, 0xf8, 0xc6, 0x10, 0x6c, 0xd2, 0x53, 0x4f, 0x62, 0x8c, 0x11,
+ 0x00, 0x3e, 0x88, 0xdf, 0xbf, 0xe6, 0xd2, 0xcc, 0x70, 0xbd, 0xed, 0x25,
+ 0x9c, 0xfb, 0xdd, 0x24, 0x0a, 0xbd, 0x59, 0x91, 0x4a, 0x42, 0x03, 0x38,
+ 0x12, 0x71, 0x32, 0x88, 0x76, 0xa0, 0x8e, 0x7c, 0xbb, 0x32, 0xef, 0x88,
+ 0x2a, 0x1b, 0xd4, 0x6a, 0x6f, 0x50, 0xb9, 0x52, 0x67, 0x8b, 0xab, 0x30,
+ 0xfa, 0x1f, 0xfd, 0xe3, 0x24, 0x9a,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5d:72:fb:33:76:20:f6:4c:72:80:db:e9:12:81:ff:6a
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=thawte, Inc., CN=thawte EV SSL CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c4:dd:da:94:1e:32:b2:2e:a0:83:c0:a6:7d:5f:
+ 65:2d:fd:27:b8:73:0e:f8:0b:a9:d4:56:26:69:98:
+ 67:35:39:64:58:ce:82:6f:98:94:d1:8f:e0:90:d6:
+ ed:55:4b:98:4b:d7:10:59:34:02:1b:e7:51:31:51:
+ c4:38:c2:bc:db:03:5c:ca:e1:7c:dc:4f:59:97:ea:
+ 07:7f:0f:85:3e:92:ea:aa:a7:d9:be:01:41:e4:62:
+ 56:47:36:bd:57:91:e6:21:d3:f8:41:0b:d8:ba:e8:
+ ed:81:ad:70:c0:8b:6e:f3:89:6e:27:9e:a6:a6:73:
+ 59:bb:71:00:d4:4f:4b:48:e9:d5:c9:27:36:9c:7c:
+ 1c:02:aa:ac:bd:3b:d1:53:83:6a:1f:e6:08:47:33:
+ a7:b1:9f:02:be:9b:47:ed:33:04:dc:1c:80:27:d1:
+ 4a:33:a0:8c:eb:01:47:a1:32:90:64:7b:c4:e0:84:
+ c9:32:e9:dd:34:1f:8a:68:67:f3:ad:10:63:eb:ee:
+ 8a:9a:b1:2a:1b:26:74:a1:2a:b0:8f:fe:52:98:46:
+ 97:cf:a3:56:1c:6f:6e:99:97:8d:26:0e:a9:ec:c2:
+ 53:70:fc:7a:a5:19:49:bd:b5:17:82:55:de:97:e0:
+ 5d:62:84:81:f0:70:a8:34:53:4f:14:fd:3d:5d:3d:
+ 6f:b9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://t2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.thawte.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://t1.symcb.com/ThawtePCA.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-536
+ X509v3 Subject Key Identifier:
+ F0:70:51:DA:D3:2A:91:4F:52:77:D7:86:77:74:0F:CE:71:1A:6C:22
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha256WithRSAEncryption
+ a1:2e:94:3e:9b:16:f4:58:1a:6f:c1:fa:c1:7e:43:93:b2:c3:
+ f7:89:eb:13:62:5d:dd:cc:61:13:2b:1d:4e:88:79:11:62:14:
+ 37:30:46:ff:89:62:10:85:2a:87:1e:f8:e2:af:fe:93:02:93:
+ ca:f2:e9:46:03:6b:a1:1a:ac:d5:f0:80:1b:98:6f:b8:3a:50:
+ f8:54:71:06:03:e7:84:cc:8e:61:d2:5f:4d:0c:97:02:65:b5:
+ 8c:26:bc:05:98:f4:dc:c6:af:e4:57:7f:e3:dc:a1:d7:27:47:
+ 2a:e0:2c:3f:09:74:dc:5a:e5:b5:7c:fa:82:9a:15:fa:74:2b:
+ 84:2e:6b:ac:ef:35:a6:30:fa:47:4a:aa:36:44:f6:5a:91:07:
+ d3:e4:4e:97:3f:a6:53:d8:29:33:32:6f:8b:3d:b5:a5:0d:e5:
+ e4:8a:e8:f5:c0:fa:af:d8:37:28:27:c3:ed:34:31:d9:7c:a6:
+ af:4d:12:4f:d0:2b:92:9c:69:95:f2:28:a6:fe:a8:c6:e0:2c:
+ 4d:36:eb:11:34:d6:e1:81:99:9d:41:f2:e7:c5:57:05:0e:19:
+ ca:af:42:39:1f:a7:27:5e:e0:0a:17:b8:ae:47:ab:92:f1:8a:
+ 04:df:30:e0:bb:4f:8a:f9:1b:88:4f:03:b4:25:7a:78:de:2e:
+ 7d:29:d1:31
+-----BEGIN CERTIFICATE-----
+MIIErzCCA5egAwIBAgIQXXL7M3Yg9kxygNvpEoH/ajANBgkqhkiG9w0BAQsFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMDMxMDAwMDAwWhcNMjMx
+MDMwMjM1OTU5WjBEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu
+MR4wHAYDVQQDExV0aGF3dGUgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDE3dqUHjKyLqCDwKZ9X2Ut/Se4cw74C6nUViZpmGc1
+OWRYzoJvmJTRj+CQ1u1VS5hL1xBZNAIb51ExUcQ4wrzbA1zK4XzcT1mX6gd/D4U+
+kuqqp9m+AUHkYlZHNr1XkeYh0/hBC9i66O2BrXDAi27ziW4nnqamc1m7cQDUT0tI
+6dXJJzacfBwCqqy9O9FTg2of5ghHM6exnwK+m0ftMwTcHIAn0UozoIzrAUehMpBk
+e8TghMky6d00H4poZ/OtEGPr7oqasSobJnShKrCP/lKYRpfPo1Ycb26Zl40mDqns
+wlNw/HqlGUm9tReCVd6X4F1ihIHwcKg0U08U/T1dPW+5AgMBAAGjggE1MIIBMTAS
+BgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAvBggrBgEFBQcBAQQj
+MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly90Mi5zeW1jYi5jb20wOwYDVR0gBDQwMjAw
+BgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3Bz
+MDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6Ly90MS5zeW1jYi5jb20vVGhhd3RlUENB
+LmNybDApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01MzYw
+HQYDVR0OBBYEFPBwUdrTKpFPUnfXhnd0D85xGmwiMB8GA1UdIwQYMBaAFHtbRc+v
+zst6/TGSGmq280brV0hQMA0GCSqGSIb3DQEBCwUAA4IBAQChLpQ+mxb0WBpvwfrB
+fkOTssP3iesTYl3dzGETKx1OiHkRYhQ3MEb/iWIQhSqHHvjir/6TApPK8ulGA2uh
+GqzV8IAbmG+4OlD4VHEGA+eEzI5h0l9NDJcCZbWMJrwFmPTcxq/kV3/j3KHXJ0cq
+4Cw/CXTcWuW1fPqCmhX6dCuELmus7zWmMPpHSqo2RPZakQfT5E6XP6ZT2CkzMm+L
+PbWlDeXkiuj1wPqv2DcoJ8PtNDHZfKavTRJP0CuSnGmV8iim/qjG4CxNNusRNNbh
+gZmdQfLnxVcFDhnKr0I5H6cnXuAKF7iuR6uS8YoE3zDgu0+K+RuITwO0JXp43i59
+KdEx
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert25[] = {
+ 0x30, 0x82, 0x04, 0xaf, 0x30, 0x82, 0x03, 0x97, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x5d, 0x72, 0xfb, 0x33, 0x76, 0x20, 0xf6, 0x4c, 0x72,
+ 0x80, 0xdb, 0xe9, 0x12, 0x81, 0xff, 0x6a, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31,
+ 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x44,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x74,
+ 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x45, 0x56, 0x20, 0x53, 0x53, 0x4c,
+ 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xc4, 0xdd, 0xda, 0x94, 0x1e, 0x32, 0xb2,
+ 0x2e, 0xa0, 0x83, 0xc0, 0xa6, 0x7d, 0x5f, 0x65, 0x2d, 0xfd, 0x27, 0xb8,
+ 0x73, 0x0e, 0xf8, 0x0b, 0xa9, 0xd4, 0x56, 0x26, 0x69, 0x98, 0x67, 0x35,
+ 0x39, 0x64, 0x58, 0xce, 0x82, 0x6f, 0x98, 0x94, 0xd1, 0x8f, 0xe0, 0x90,
+ 0xd6, 0xed, 0x55, 0x4b, 0x98, 0x4b, 0xd7, 0x10, 0x59, 0x34, 0x02, 0x1b,
+ 0xe7, 0x51, 0x31, 0x51, 0xc4, 0x38, 0xc2, 0xbc, 0xdb, 0x03, 0x5c, 0xca,
+ 0xe1, 0x7c, 0xdc, 0x4f, 0x59, 0x97, 0xea, 0x07, 0x7f, 0x0f, 0x85, 0x3e,
+ 0x92, 0xea, 0xaa, 0xa7, 0xd9, 0xbe, 0x01, 0x41, 0xe4, 0x62, 0x56, 0x47,
+ 0x36, 0xbd, 0x57, 0x91, 0xe6, 0x21, 0xd3, 0xf8, 0x41, 0x0b, 0xd8, 0xba,
+ 0xe8, 0xed, 0x81, 0xad, 0x70, 0xc0, 0x8b, 0x6e, 0xf3, 0x89, 0x6e, 0x27,
+ 0x9e, 0xa6, 0xa6, 0x73, 0x59, 0xbb, 0x71, 0x00, 0xd4, 0x4f, 0x4b, 0x48,
+ 0xe9, 0xd5, 0xc9, 0x27, 0x36, 0x9c, 0x7c, 0x1c, 0x02, 0xaa, 0xac, 0xbd,
+ 0x3b, 0xd1, 0x53, 0x83, 0x6a, 0x1f, 0xe6, 0x08, 0x47, 0x33, 0xa7, 0xb1,
+ 0x9f, 0x02, 0xbe, 0x9b, 0x47, 0xed, 0x33, 0x04, 0xdc, 0x1c, 0x80, 0x27,
+ 0xd1, 0x4a, 0x33, 0xa0, 0x8c, 0xeb, 0x01, 0x47, 0xa1, 0x32, 0x90, 0x64,
+ 0x7b, 0xc4, 0xe0, 0x84, 0xc9, 0x32, 0xe9, 0xdd, 0x34, 0x1f, 0x8a, 0x68,
+ 0x67, 0xf3, 0xad, 0x10, 0x63, 0xeb, 0xee, 0x8a, 0x9a, 0xb1, 0x2a, 0x1b,
+ 0x26, 0x74, 0xa1, 0x2a, 0xb0, 0x8f, 0xfe, 0x52, 0x98, 0x46, 0x97, 0xcf,
+ 0xa3, 0x56, 0x1c, 0x6f, 0x6e, 0x99, 0x97, 0x8d, 0x26, 0x0e, 0xa9, 0xec,
+ 0xc2, 0x53, 0x70, 0xfc, 0x7a, 0xa5, 0x19, 0x49, 0xbd, 0xb5, 0x17, 0x82,
+ 0x55, 0xde, 0x97, 0xe0, 0x5d, 0x62, 0x84, 0x81, 0xf0, 0x70, 0xa8, 0x34,
+ 0x53, 0x4f, 0x14, 0xfd, 0x3d, 0x5d, 0x3d, 0x6f, 0xb9, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x35, 0x30, 0x82, 0x01, 0x31, 0x30, 0x12,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
+ 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23,
+ 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74,
+ 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68,
+ 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73,
+ 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30,
+ 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x74, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04,
+ 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74,
+ 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35, 0x33, 0x36, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf0, 0x70,
+ 0x51, 0xda, 0xd3, 0x2a, 0x91, 0x4f, 0x52, 0x77, 0xd7, 0x86, 0x77, 0x74,
+ 0x0f, 0xce, 0x71, 0x1a, 0x6c, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf,
+ 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb,
+ 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa1,
+ 0x2e, 0x94, 0x3e, 0x9b, 0x16, 0xf4, 0x58, 0x1a, 0x6f, 0xc1, 0xfa, 0xc1,
+ 0x7e, 0x43, 0x93, 0xb2, 0xc3, 0xf7, 0x89, 0xeb, 0x13, 0x62, 0x5d, 0xdd,
+ 0xcc, 0x61, 0x13, 0x2b, 0x1d, 0x4e, 0x88, 0x79, 0x11, 0x62, 0x14, 0x37,
+ 0x30, 0x46, 0xff, 0x89, 0x62, 0x10, 0x85, 0x2a, 0x87, 0x1e, 0xf8, 0xe2,
+ 0xaf, 0xfe, 0x93, 0x02, 0x93, 0xca, 0xf2, 0xe9, 0x46, 0x03, 0x6b, 0xa1,
+ 0x1a, 0xac, 0xd5, 0xf0, 0x80, 0x1b, 0x98, 0x6f, 0xb8, 0x3a, 0x50, 0xf8,
+ 0x54, 0x71, 0x06, 0x03, 0xe7, 0x84, 0xcc, 0x8e, 0x61, 0xd2, 0x5f, 0x4d,
+ 0x0c, 0x97, 0x02, 0x65, 0xb5, 0x8c, 0x26, 0xbc, 0x05, 0x98, 0xf4, 0xdc,
+ 0xc6, 0xaf, 0xe4, 0x57, 0x7f, 0xe3, 0xdc, 0xa1, 0xd7, 0x27, 0x47, 0x2a,
+ 0xe0, 0x2c, 0x3f, 0x09, 0x74, 0xdc, 0x5a, 0xe5, 0xb5, 0x7c, 0xfa, 0x82,
+ 0x9a, 0x15, 0xfa, 0x74, 0x2b, 0x84, 0x2e, 0x6b, 0xac, 0xef, 0x35, 0xa6,
+ 0x30, 0xfa, 0x47, 0x4a, 0xaa, 0x36, 0x44, 0xf6, 0x5a, 0x91, 0x07, 0xd3,
+ 0xe4, 0x4e, 0x97, 0x3f, 0xa6, 0x53, 0xd8, 0x29, 0x33, 0x32, 0x6f, 0x8b,
+ 0x3d, 0xb5, 0xa5, 0x0d, 0xe5, 0xe4, 0x8a, 0xe8, 0xf5, 0xc0, 0xfa, 0xaf,
+ 0xd8, 0x37, 0x28, 0x27, 0xc3, 0xed, 0x34, 0x31, 0xd9, 0x7c, 0xa6, 0xaf,
+ 0x4d, 0x12, 0x4f, 0xd0, 0x2b, 0x92, 0x9c, 0x69, 0x95, 0xf2, 0x28, 0xa6,
+ 0xfe, 0xa8, 0xc6, 0xe0, 0x2c, 0x4d, 0x36, 0xeb, 0x11, 0x34, 0xd6, 0xe1,
+ 0x81, 0x99, 0x9d, 0x41, 0xf2, 0xe7, 0xc5, 0x57, 0x05, 0x0e, 0x19, 0xca,
+ 0xaf, 0x42, 0x39, 0x1f, 0xa7, 0x27, 0x5e, 0xe0, 0x0a, 0x17, 0xb8, 0xae,
+ 0x47, 0xab, 0x92, 0xf1, 0x8a, 0x04, 0xdf, 0x30, 0xe0, 0xbb, 0x4f, 0x8a,
+ 0xf9, 0x1b, 0x88, 0x4f, 0x03, 0xb4, 0x25, 0x7a, 0x78, 0xde, 0x2e, 0x7d,
+ 0x29, 0xd1, 0x31,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:e1:e7:a4:dc:5c:f2:f3:6d:c0:2b:42:b8:5d:15:9f
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Oct 22 12:00:00 2013 GMT
+ Not After : Oct 22 12:00:00 2028 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b6:e0:2f:c2:24:06:c8:6d:04:5f:d7:ef:0a:64:
+ 06:b2:7d:22:26:65:16:ae:42:40:9b:ce:dc:9f:9f:
+ 76:07:3e:c3:30:55:87:19:b9:4f:94:0e:5a:94:1f:
+ 55:56:b4:c2:02:2a:af:d0:98:ee:0b:40:d7:c4:d0:
+ 3b:72:c8:14:9e:ef:90:b1:11:a9:ae:d2:c8:b8:43:
+ 3a:d9:0b:0b:d5:d5:95:f5:40:af:c8:1d:ed:4d:9c:
+ 5f:57:b7:86:50:68:99:f5:8a:da:d2:c7:05:1f:a8:
+ 97:c9:dc:a4:b1:82:84:2d:c6:ad:a5:9c:c7:19:82:
+ a6:85:0f:5e:44:58:2a:37:8f:fd:35:f1:0b:08:27:
+ 32:5a:f5:bb:8b:9e:a4:bd:51:d0:27:e2:dd:3b:42:
+ 33:a3:05:28:c4:bb:28:cc:9a:ac:2b:23:0d:78:c6:
+ 7b:e6:5e:71:b7:4a:3e:08:fb:81:b7:16:16:a1:9d:
+ 23:12:4d:e5:d7:92:08:ac:75:a4:9c:ba:cd:17:b2:
+ 1e:44:35:65:7f:53:25:39:d1:1c:0a:9a:63:1b:19:
+ 92:74:68:0a:37:c2:c2:52:48:cb:39:5a:a2:b6:e1:
+ 5d:c1:dd:a0:20:b8:21:a2:93:26:6f:14:4a:21:41:
+ c7:ed:6d:9b:f2:48:2f:f3:03:f5:a2:68:92:53:2f:
+ 5e:e3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.digicert.com/CPS
+
+ X509v3 Subject Key Identifier:
+ 51:68:FF:90:AF:02:07:75:3C:CC:D9:65:64:62:A2:12:B8:59:72:3B
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 18:8a:95:89:03:e6:6d:df:5c:fc:1d:68:ea:4a:8f:83:d6:51:
+ 2f:8d:6b:44:16:9e:ac:63:f5:d2:6e:6c:84:99:8b:aa:81:71:
+ 84:5b:ed:34:4e:b0:b7:79:92:29:cc:2d:80:6a:f0:8e:20:e1:
+ 79:a4:fe:03:47:13:ea:f5:86:ca:59:71:7d:f4:04:96:6b:d3:
+ 59:58:3d:fe:d3:31:25:5c:18:38:84:a3:e6:9f:82:fd:8c:5b:
+ 98:31:4e:cd:78:9e:1a:fd:85:cb:49:aa:f2:27:8b:99:72:fc:
+ 3e:aa:d5:41:0b:da:d5:36:a1:bf:1c:6e:47:49:7f:5e:d9:48:
+ 7c:03:d9:fd:8b:49:a0:98:26:42:40:eb:d6:92:11:a4:64:0a:
+ 57:54:c4:f5:1d:d6:02:5e:6b:ac:ee:c4:80:9a:12:72:fa:56:
+ 93:d7:ff:bf:30:85:06:30:bf:0b:7f:4e:ff:57:05:9d:24:ed:
+ 85:c3:2b:fb:a6:75:a8:ac:2d:16:ef:7d:79:27:b2:eb:c2:9d:
+ 0b:07:ea:aa:85:d3:01:a3:20:28:41:59:43:28:d2:81:e3:aa:
+ f6:ec:7b:3b:77:b6:40:62:80:05:41:45:01:ef:17:06:3e:de:
+ c0:33:9b:67:d3:61:2e:72:87:e4:69:fc:12:00:57:40:1e:70:
+ f5:1e:c9:b4
+-----BEGIN CERTIFICATE-----
+MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy
+YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2
+4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC
+Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1
+itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn
+4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X
+sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft
+bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA
+MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
+NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
+dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t
+L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG
+BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ
+UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D
+aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd
+aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH
+E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly
+/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu
+xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF
+0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae
+cPUeybQ=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert26[] = {
+ 0x30, 0x82, 0x04, 0xb1, 0x30, 0x82, 0x03, 0x99, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x04, 0xe1, 0xe7, 0xa4, 0xdc, 0x5c, 0xf2, 0xf3, 0x6d,
+ 0xc0, 0x2b, 0x42, 0xb8, 0x5d, 0x15, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x32, 0x32, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x30, 0x32,
+ 0x32, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x70, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41,
+ 0x32, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72,
+ 0x61, 0x6e, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6,
+ 0xe0, 0x2f, 0xc2, 0x24, 0x06, 0xc8, 0x6d, 0x04, 0x5f, 0xd7, 0xef, 0x0a,
+ 0x64, 0x06, 0xb2, 0x7d, 0x22, 0x26, 0x65, 0x16, 0xae, 0x42, 0x40, 0x9b,
+ 0xce, 0xdc, 0x9f, 0x9f, 0x76, 0x07, 0x3e, 0xc3, 0x30, 0x55, 0x87, 0x19,
+ 0xb9, 0x4f, 0x94, 0x0e, 0x5a, 0x94, 0x1f, 0x55, 0x56, 0xb4, 0xc2, 0x02,
+ 0x2a, 0xaf, 0xd0, 0x98, 0xee, 0x0b, 0x40, 0xd7, 0xc4, 0xd0, 0x3b, 0x72,
+ 0xc8, 0x14, 0x9e, 0xef, 0x90, 0xb1, 0x11, 0xa9, 0xae, 0xd2, 0xc8, 0xb8,
+ 0x43, 0x3a, 0xd9, 0x0b, 0x0b, 0xd5, 0xd5, 0x95, 0xf5, 0x40, 0xaf, 0xc8,
+ 0x1d, 0xed, 0x4d, 0x9c, 0x5f, 0x57, 0xb7, 0x86, 0x50, 0x68, 0x99, 0xf5,
+ 0x8a, 0xda, 0xd2, 0xc7, 0x05, 0x1f, 0xa8, 0x97, 0xc9, 0xdc, 0xa4, 0xb1,
+ 0x82, 0x84, 0x2d, 0xc6, 0xad, 0xa5, 0x9c, 0xc7, 0x19, 0x82, 0xa6, 0x85,
+ 0x0f, 0x5e, 0x44, 0x58, 0x2a, 0x37, 0x8f, 0xfd, 0x35, 0xf1, 0x0b, 0x08,
+ 0x27, 0x32, 0x5a, 0xf5, 0xbb, 0x8b, 0x9e, 0xa4, 0xbd, 0x51, 0xd0, 0x27,
+ 0xe2, 0xdd, 0x3b, 0x42, 0x33, 0xa3, 0x05, 0x28, 0xc4, 0xbb, 0x28, 0xcc,
+ 0x9a, 0xac, 0x2b, 0x23, 0x0d, 0x78, 0xc6, 0x7b, 0xe6, 0x5e, 0x71, 0xb7,
+ 0x4a, 0x3e, 0x08, 0xfb, 0x81, 0xb7, 0x16, 0x16, 0xa1, 0x9d, 0x23, 0x12,
+ 0x4d, 0xe5, 0xd7, 0x92, 0x08, 0xac, 0x75, 0xa4, 0x9c, 0xba, 0xcd, 0x17,
+ 0xb2, 0x1e, 0x44, 0x35, 0x65, 0x7f, 0x53, 0x25, 0x39, 0xd1, 0x1c, 0x0a,
+ 0x9a, 0x63, 0x1b, 0x19, 0x92, 0x74, 0x68, 0x0a, 0x37, 0xc2, 0xc2, 0x52,
+ 0x48, 0xcb, 0x39, 0x5a, 0xa2, 0xb6, 0xe1, 0x5d, 0xc1, 0xdd, 0xa0, 0x20,
+ 0xb8, 0x21, 0xa2, 0x93, 0x26, 0x6f, 0x14, 0x4a, 0x21, 0x41, 0xc7, 0xed,
+ 0x6d, 0x9b, 0xf2, 0x48, 0x2f, 0xf3, 0x03, 0xf5, 0xa2, 0x68, 0x92, 0x53,
+ 0x2f, 0x5e, 0xe3, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x49,
+ 0x30, 0x82, 0x01, 0x45, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
+ 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4b, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e,
+ 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67,
+ 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56,
+ 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67,
+ 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50,
+ 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x51, 0x68, 0xff, 0x90, 0xaf, 0x02, 0x07, 0x75, 0x3c, 0xcc, 0xd9, 0x65,
+ 0x64, 0x62, 0xa2, 0x12, 0xb8, 0x59, 0x72, 0x3b, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, 0xc3,
+ 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02,
+ 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x18, 0x8a, 0x95, 0x89, 0x03, 0xe6, 0x6d, 0xdf, 0x5c, 0xfc, 0x1d,
+ 0x68, 0xea, 0x4a, 0x8f, 0x83, 0xd6, 0x51, 0x2f, 0x8d, 0x6b, 0x44, 0x16,
+ 0x9e, 0xac, 0x63, 0xf5, 0xd2, 0x6e, 0x6c, 0x84, 0x99, 0x8b, 0xaa, 0x81,
+ 0x71, 0x84, 0x5b, 0xed, 0x34, 0x4e, 0xb0, 0xb7, 0x79, 0x92, 0x29, 0xcc,
+ 0x2d, 0x80, 0x6a, 0xf0, 0x8e, 0x20, 0xe1, 0x79, 0xa4, 0xfe, 0x03, 0x47,
+ 0x13, 0xea, 0xf5, 0x86, 0xca, 0x59, 0x71, 0x7d, 0xf4, 0x04, 0x96, 0x6b,
+ 0xd3, 0x59, 0x58, 0x3d, 0xfe, 0xd3, 0x31, 0x25, 0x5c, 0x18, 0x38, 0x84,
+ 0xa3, 0xe6, 0x9f, 0x82, 0xfd, 0x8c, 0x5b, 0x98, 0x31, 0x4e, 0xcd, 0x78,
+ 0x9e, 0x1a, 0xfd, 0x85, 0xcb, 0x49, 0xaa, 0xf2, 0x27, 0x8b, 0x99, 0x72,
+ 0xfc, 0x3e, 0xaa, 0xd5, 0x41, 0x0b, 0xda, 0xd5, 0x36, 0xa1, 0xbf, 0x1c,
+ 0x6e, 0x47, 0x49, 0x7f, 0x5e, 0xd9, 0x48, 0x7c, 0x03, 0xd9, 0xfd, 0x8b,
+ 0x49, 0xa0, 0x98, 0x26, 0x42, 0x40, 0xeb, 0xd6, 0x92, 0x11, 0xa4, 0x64,
+ 0x0a, 0x57, 0x54, 0xc4, 0xf5, 0x1d, 0xd6, 0x02, 0x5e, 0x6b, 0xac, 0xee,
+ 0xc4, 0x80, 0x9a, 0x12, 0x72, 0xfa, 0x56, 0x93, 0xd7, 0xff, 0xbf, 0x30,
+ 0x85, 0x06, 0x30, 0xbf, 0x0b, 0x7f, 0x4e, 0xff, 0x57, 0x05, 0x9d, 0x24,
+ 0xed, 0x85, 0xc3, 0x2b, 0xfb, 0xa6, 0x75, 0xa8, 0xac, 0x2d, 0x16, 0xef,
+ 0x7d, 0x79, 0x27, 0xb2, 0xeb, 0xc2, 0x9d, 0x0b, 0x07, 0xea, 0xaa, 0x85,
+ 0xd3, 0x01, 0xa3, 0x20, 0x28, 0x41, 0x59, 0x43, 0x28, 0xd2, 0x81, 0xe3,
+ 0xaa, 0xf6, 0xec, 0x7b, 0x3b, 0x77, 0xb6, 0x40, 0x62, 0x80, 0x05, 0x41,
+ 0x45, 0x01, 0xef, 0x17, 0x06, 0x3e, 0xde, 0xc0, 0x33, 0x9b, 0x67, 0xd3,
+ 0x61, 0x2e, 0x72, 0x87, 0xe4, 0x69, 0xfc, 0x12, 0x00, 0x57, 0x40, 0x1e,
+ 0x70, 0xf5, 0x1e, 0xc9, 0xb4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 16:87:d6:88:6d:e2:30:06:85:23:3d:bf:11:bf:65:97
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=thawte, Inc., CN=thawte SSL CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b2:fc:06:fb:04:93:d2:ea:59:20:3b:44:85:97:
+ 52:39:e7:10:f0:7a:e0:b0:94:40:da:46:f8:0c:28:
+ bb:b9:ce:60:38:3f:d2:d8:11:42:1b:91:ad:49:ee:
+ 8f:c7:de:6c:de:37:6f:fd:8b:20:3c:6d:e7:74:d3:
+ dc:d5:24:88:41:80:89:ee:36:be:c4:d5:be:8d:53:
+ 13:aa:e4:a5:b8:93:0a:be:ec:da:cd:3c:d4:32:56:
+ ef:d0:4e:a0:b8:97:bb:39:50:1e:6e:65:c3:fd:b2:
+ ce:e0:59:a9:48:09:c6:fe:be:ae:fc:3e:3b:81:20:
+ 97:8b:8f:46:df:60:64:07:75:bb:1b:86:38:9f:47:
+ 7b:34:ce:a1:d1:97:ad:76:d8:9f:b7:26:db:79:80:
+ 36:48:f2:c5:37:f8:d9:32:ae:7c:a4:53:81:c7:99:
+ a1:54:38:2f:4f:75:a0:bb:5a:a5:bb:cd:ac:02:5b:
+ 19:02:d5:13:18:a7:ce:ac:74:55:12:05:8b:9b:a2:
+ 95:46:64:72:38:cd:5a:1b:3a:16:a7:be:71:99:8c:
+ 54:03:b8:96:6c:01:d3:3e:06:98:3f:21:81:3b:02:
+ 7e:00:47:53:01:1e:0e:46:43:fb:4b:2d:dc:0b:1a:
+ e8:2f:98:f8:7e:d1:99:ab:13:6c:a4:17:de:6f:f6:
+ 15:f5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://t1.symcb.com/ThawtePCA.crl
+
+ Authority Information Access:
+ OCSP - URI:http://t2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: https://www.thawte.com/cps
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-537
+ X509v3 Subject Key Identifier:
+ C2:4F:48:57:FC:D1:4F:9A:C0:5D:38:7D:0E:05:DB:D9:2E:B5:52:60
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 8d:06:de:43:c9:76:02:ca:d9:23:97:5e:f3:63:d7:7d:44:c2:
+ 0f:6b:0a:f5:07:e5:8b:b8:fa:e0:a3:fa:6b:80:92:b5:03:2c:
+ c5:37:e0:c2:e5:95:b5:92:70:18:28:42:94:ee:4b:77:6a:01:
+ 0f:8b:23:ec:56:4d:f4:00:69:e5:84:c8:e2:ea:de:5b:3e:f6:
+ 3c:07:3a:94:ca:6c:27:b1:cc:83:1a:60:71:27:d2:bf:02:f5:
+ 1e:44:d3:48:d5:a6:d3:76:21:00:9c:fa:98:64:eb:17:36:3f:
+ eb:1b:3c:3e:a6:b1:d9:58:06:0e:72:d9:68:be:f1:a7:20:d7:
+ 52:e4:a4:77:1f:71:70:9d:55:35:85:37:e1:1d:4d:94:c2:70:
+ 7f:95:40:6e:4b:7d:b2:b4:29:2a:03:79:c8:b9:4c:67:61:04:
+ a0:8b:27:ff:59:00:eb:55:7f:c6:b7:33:35:2d:5e:4e:ac:b8:
+ ea:12:c5:e8:f7:b9:ab:be:74:92:2c:b7:d9:4d:ca:84:2f:1c:
+ c2:f0:72:7c:b2:31:6e:cf:80:e5:88:07:36:51:7b:ba:61:af:
+ 6d:8d:23:5b:34:a3:95:bc:a2:31:7f:f2:f5:e7:b7:e8:ef:c4:
+ b5:27:32:e9:f7:9e:69:c7:2b:e8:be:bb:0c:aa:e7:ea:60:12:
+ ea:26:8a:78
+-----BEGIN CERTIFICATE-----
+MIIEsjCCA5qgAwIBAgIQFofWiG3iMAaFIz2/Eb9llzANBgkqhkiG9w0BAQsFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMDMxMDAwMDAwWhcNMjMx
+MDMwMjM1OTU5WjBBMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu
+MRswGQYDVQQDExJ0aGF3dGUgU1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCy/Ab7BJPS6lkgO0SFl1I55xDweuCwlEDaRvgMKLu5zmA4
+P9LYEUIbka1J7o/H3mzeN2/9iyA8bed009zVJIhBgInuNr7E1b6NUxOq5KW4kwq+
+7NrNPNQyVu/QTqC4l7s5UB5uZcP9ss7gWalICcb+vq78PjuBIJeLj0bfYGQHdbsb
+hjifR3s0zqHRl6122J+3Jtt5gDZI8sU3+NkyrnykU4HHmaFUOC9PdaC7WqW7zawC
+WxkC1RMYp86sdFUSBYubopVGZHI4zVobOhanvnGZjFQDuJZsAdM+Bpg/IYE7An4A
+R1MBHg5GQ/tLLdwLGugvmPh+0ZmrE2ykF95v9hX1AgMBAAGjggE7MIIBNzASBgNV
+HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAyBgNVHR8EKzApMCegJaAj
+hiFodHRwOi8vdDEuc3ltY2IuY29tL1RoYXd0ZVBDQS5jcmwwLwYIKwYBBQUHAQEE
+IzAhMB8GCCsGAQUFBzABhhNodHRwOi8vdDIuc3ltY2IuY29tMEEGA1UdIAQ6MDgw
+NgYKYIZIAYb4RQEHNjAoMCYGCCsGAQUFBwIBFhpodHRwczovL3d3dy50aGF3dGUu
+Y29tL2NwczApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01
+MzcwHQYDVR0OBBYEFMJPSFf80U+awF04fQ4F29kutVJgMB8GA1UdIwQYMBaAFHtb
+Rc+vzst6/TGSGmq280brV0hQMA0GCSqGSIb3DQEBCwUAA4IBAQCNBt5DyXYCytkj
+l17zY9d9RMIPawr1B+WLuPrgo/prgJK1AyzFN+DC5ZW1knAYKEKU7kt3agEPiyPs
+Vk30AGnlhMji6t5bPvY8BzqUymwnscyDGmBxJ9K/AvUeRNNI1abTdiEAnPqYZOsX
+Nj/rGzw+prHZWAYOctlovvGnINdS5KR3H3FwnVU1hTfhHU2UwnB/lUBuS32ytCkq
+A3nIuUxnYQSgiyf/WQDrVX/GtzM1LV5OrLjqEsXo97mrvnSSLLfZTcqELxzC8HJ8
+sjFuz4DliAc2UXu6Ya9tjSNbNKOVvKIxf/L157fo78S1JzLp955pxyvovrsMqufq
+YBLqJop4
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert27[] = {
+ 0x30, 0x82, 0x04, 0xb2, 0x30, 0x82, 0x03, 0x9a, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x16, 0x87, 0xd6, 0x88, 0x6d, 0xe2, 0x30, 0x06, 0x85,
+ 0x23, 0x3d, 0xbf, 0x11, 0xbf, 0x65, 0x97, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31,
+ 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x41,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x74,
+ 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xb2, 0xfc, 0x06, 0xfb, 0x04, 0x93, 0xd2, 0xea, 0x59, 0x20,
+ 0x3b, 0x44, 0x85, 0x97, 0x52, 0x39, 0xe7, 0x10, 0xf0, 0x7a, 0xe0, 0xb0,
+ 0x94, 0x40, 0xda, 0x46, 0xf8, 0x0c, 0x28, 0xbb, 0xb9, 0xce, 0x60, 0x38,
+ 0x3f, 0xd2, 0xd8, 0x11, 0x42, 0x1b, 0x91, 0xad, 0x49, 0xee, 0x8f, 0xc7,
+ 0xde, 0x6c, 0xde, 0x37, 0x6f, 0xfd, 0x8b, 0x20, 0x3c, 0x6d, 0xe7, 0x74,
+ 0xd3, 0xdc, 0xd5, 0x24, 0x88, 0x41, 0x80, 0x89, 0xee, 0x36, 0xbe, 0xc4,
+ 0xd5, 0xbe, 0x8d, 0x53, 0x13, 0xaa, 0xe4, 0xa5, 0xb8, 0x93, 0x0a, 0xbe,
+ 0xec, 0xda, 0xcd, 0x3c, 0xd4, 0x32, 0x56, 0xef, 0xd0, 0x4e, 0xa0, 0xb8,
+ 0x97, 0xbb, 0x39, 0x50, 0x1e, 0x6e, 0x65, 0xc3, 0xfd, 0xb2, 0xce, 0xe0,
+ 0x59, 0xa9, 0x48, 0x09, 0xc6, 0xfe, 0xbe, 0xae, 0xfc, 0x3e, 0x3b, 0x81,
+ 0x20, 0x97, 0x8b, 0x8f, 0x46, 0xdf, 0x60, 0x64, 0x07, 0x75, 0xbb, 0x1b,
+ 0x86, 0x38, 0x9f, 0x47, 0x7b, 0x34, 0xce, 0xa1, 0xd1, 0x97, 0xad, 0x76,
+ 0xd8, 0x9f, 0xb7, 0x26, 0xdb, 0x79, 0x80, 0x36, 0x48, 0xf2, 0xc5, 0x37,
+ 0xf8, 0xd9, 0x32, 0xae, 0x7c, 0xa4, 0x53, 0x81, 0xc7, 0x99, 0xa1, 0x54,
+ 0x38, 0x2f, 0x4f, 0x75, 0xa0, 0xbb, 0x5a, 0xa5, 0xbb, 0xcd, 0xac, 0x02,
+ 0x5b, 0x19, 0x02, 0xd5, 0x13, 0x18, 0xa7, 0xce, 0xac, 0x74, 0x55, 0x12,
+ 0x05, 0x8b, 0x9b, 0xa2, 0x95, 0x46, 0x64, 0x72, 0x38, 0xcd, 0x5a, 0x1b,
+ 0x3a, 0x16, 0xa7, 0xbe, 0x71, 0x99, 0x8c, 0x54, 0x03, 0xb8, 0x96, 0x6c,
+ 0x01, 0xd3, 0x3e, 0x06, 0x98, 0x3f, 0x21, 0x81, 0x3b, 0x02, 0x7e, 0x00,
+ 0x47, 0x53, 0x01, 0x1e, 0x0e, 0x46, 0x43, 0xfb, 0x4b, 0x2d, 0xdc, 0x0b,
+ 0x1a, 0xe8, 0x2f, 0x98, 0xf8, 0x7e, 0xd1, 0x99, 0xab, 0x13, 0x6c, 0xa4,
+ 0x17, 0xde, 0x6f, 0xf6, 0x15, 0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x3b, 0x30, 0x82, 0x01, 0x37, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x32, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23,
+ 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, 0x31, 0x2e,
+ 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68,
+ 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x74, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, 0x38, 0x30,
+ 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07,
+ 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, 0x55,
+ 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a,
+ 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d,
+ 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35,
+ 0x33, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0xc2, 0x4f, 0x48, 0x57, 0xfc, 0xd1, 0x4f, 0x9a, 0xc0, 0x5d, 0x38,
+ 0x7d, 0x0e, 0x05, 0xdb, 0xd9, 0x2e, 0xb5, 0x52, 0x60, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b,
+ 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6,
+ 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x8d, 0x06, 0xde, 0x43, 0xc9, 0x76, 0x02, 0xca, 0xd9, 0x23,
+ 0x97, 0x5e, 0xf3, 0x63, 0xd7, 0x7d, 0x44, 0xc2, 0x0f, 0x6b, 0x0a, 0xf5,
+ 0x07, 0xe5, 0x8b, 0xb8, 0xfa, 0xe0, 0xa3, 0xfa, 0x6b, 0x80, 0x92, 0xb5,
+ 0x03, 0x2c, 0xc5, 0x37, 0xe0, 0xc2, 0xe5, 0x95, 0xb5, 0x92, 0x70, 0x18,
+ 0x28, 0x42, 0x94, 0xee, 0x4b, 0x77, 0x6a, 0x01, 0x0f, 0x8b, 0x23, 0xec,
+ 0x56, 0x4d, 0xf4, 0x00, 0x69, 0xe5, 0x84, 0xc8, 0xe2, 0xea, 0xde, 0x5b,
+ 0x3e, 0xf6, 0x3c, 0x07, 0x3a, 0x94, 0xca, 0x6c, 0x27, 0xb1, 0xcc, 0x83,
+ 0x1a, 0x60, 0x71, 0x27, 0xd2, 0xbf, 0x02, 0xf5, 0x1e, 0x44, 0xd3, 0x48,
+ 0xd5, 0xa6, 0xd3, 0x76, 0x21, 0x00, 0x9c, 0xfa, 0x98, 0x64, 0xeb, 0x17,
+ 0x36, 0x3f, 0xeb, 0x1b, 0x3c, 0x3e, 0xa6, 0xb1, 0xd9, 0x58, 0x06, 0x0e,
+ 0x72, 0xd9, 0x68, 0xbe, 0xf1, 0xa7, 0x20, 0xd7, 0x52, 0xe4, 0xa4, 0x77,
+ 0x1f, 0x71, 0x70, 0x9d, 0x55, 0x35, 0x85, 0x37, 0xe1, 0x1d, 0x4d, 0x94,
+ 0xc2, 0x70, 0x7f, 0x95, 0x40, 0x6e, 0x4b, 0x7d, 0xb2, 0xb4, 0x29, 0x2a,
+ 0x03, 0x79, 0xc8, 0xb9, 0x4c, 0x67, 0x61, 0x04, 0xa0, 0x8b, 0x27, 0xff,
+ 0x59, 0x00, 0xeb, 0x55, 0x7f, 0xc6, 0xb7, 0x33, 0x35, 0x2d, 0x5e, 0x4e,
+ 0xac, 0xb8, 0xea, 0x12, 0xc5, 0xe8, 0xf7, 0xb9, 0xab, 0xbe, 0x74, 0x92,
+ 0x2c, 0xb7, 0xd9, 0x4d, 0xca, 0x84, 0x2f, 0x1c, 0xc2, 0xf0, 0x72, 0x7c,
+ 0xb2, 0x31, 0x6e, 0xcf, 0x80, 0xe5, 0x88, 0x07, 0x36, 0x51, 0x7b, 0xba,
+ 0x61, 0xaf, 0x6d, 0x8d, 0x23, 0x5b, 0x34, 0xa3, 0x95, 0xbc, 0xa2, 0x31,
+ 0x7f, 0xf2, 0xf5, 0xe7, 0xb7, 0xe8, 0xef, 0xc4, 0xb5, 0x27, 0x32, 0xe9,
+ 0xf7, 0x9e, 0x69, 0xc7, 0x2b, 0xe8, 0xbe, 0xbb, 0x0c, 0xaa, 0xe7, 0xea,
+ 0x60, 0x12, 0xea, 0x26, 0x8a, 0x78,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0c:79:a9:44:b0:8c:11:95:20:92:61:5f:e2:6b:1d:83
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Oct 22 12:00:00 2013 GMT
+ Not After : Oct 22 12:00:00 2028 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 Extended Validation Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d7:53:a4:04:51:f8:99:a6:16:48:4b:67:27:aa:
+ 93:49:d0:39:ed:0c:b0:b0:00:87:f1:67:28:86:85:
+ 8c:8e:63:da:bc:b1:40:38:e2:d3:f5:ec:a5:05:18:
+ b8:3d:3e:c5:99:17:32:ec:18:8c:fa:f1:0c:a6:64:
+ 21:85:cb:07:10:34:b0:52:88:2b:1f:68:9b:d2:b1:
+ 8f:12:b0:b3:d2:e7:88:1f:1f:ef:38:77:54:53:5f:
+ 80:79:3f:2e:1a:aa:a8:1e:4b:2b:0d:ab:b7:63:b9:
+ 35:b7:7d:14:bc:59:4b:df:51:4a:d2:a1:e2:0c:e2:
+ 90:82:87:6a:ae:ea:d7:64:d6:98:55:e8:fd:af:1a:
+ 50:6c:54:bc:11:f2:fd:4a:f2:9d:bb:7f:0e:f4:d5:
+ be:8e:16:89:12:55:d8:c0:71:34:ee:f6:dc:2d:ec:
+ c4:87:25:86:8d:d8:21:e4:b0:4d:0c:89:dc:39:26:
+ 17:dd:f6:d7:94:85:d8:04:21:70:9d:6f:6f:ff:5c:
+ ba:19:e1:45:cb:56:57:28:7e:1c:0d:41:57:aa:b7:
+ b8:27:bb:b1:e4:fa:2a:ef:21:23:75:1a:ad:2d:9b:
+ 86:35:8c:9c:77:b5:73:ad:d8:94:2d:e4:f3:0c:9d:
+ ee:c1:4e:62:7e:17:c0:71:9e:2c:de:f1:f9:10:28:
+ 19:33
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.digicert.com/CPS
+
+ X509v3 Subject Key Identifier:
+ 3D:D3:50:A5:D6:A0:AD:EE:F3:4A:60:0A:65:D3:21:D4:F8:F8:D6:0F
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 9d:b6:d0:90:86:e1:86:02:ed:c5:a0:f0:34:1c:74:c1:8d:76:
+ cc:86:0a:a8:f0:4a:8a:42:d6:3f:c8:a9:4d:ad:7c:08:ad:e6:
+ b6:50:b8:a2:1a:4d:88:07:b1:29:21:dc:e7:da:c6:3c:21:e0:
+ e3:11:49:70:ac:7a:1d:01:a4:ca:11:3a:57:ab:7d:57:2a:40:
+ 74:fd:d3:1d:85:18:50:df:57:47:75:a1:7d:55:20:2e:47:37:
+ 50:72:8c:7f:82:1b:d2:62:8f:2d:03:5a:da:c3:c8:a1:ce:2c:
+ 52:a2:00:63:eb:73:ba:71:c8:49:27:23:97:64:85:9e:38:0e:
+ ad:63:68:3c:ba:52:81:58:79:a3:2c:0c:df:de:6d:eb:31:f2:
+ ba:a0:7c:6c:f1:2c:d4:e1:bd:77:84:37:03:ce:32:b5:c8:9a:
+ 81:1a:4a:92:4e:3b:46:9a:85:fe:83:a2:f9:9e:8c:a3:cc:0d:
+ 5e:b3:3d:cf:04:78:8f:14:14:7b:32:9c:c7:00:a6:5c:c4:b5:
+ a1:55:8d:5a:56:68:a4:22:70:aa:3c:81:71:d9:9d:a8:45:3b:
+ f4:e5:f6:a2:51:dd:c7:7b:62:e8:6f:0c:74:eb:b8:da:f8:bf:
+ 87:0d:79:50:91:90:9b:18:3b:91:59:27:f1:35:28:13:ab:26:
+ 7e:d5:f7:7a
+-----BEGIN CERTIFICATE-----
+MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW
+YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY
+uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/
+LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy
+/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh
+cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k
+8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB
+Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
+BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
+Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy
+dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2
+MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j
+b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW
+gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh
+hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg
+4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa
+2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs
+1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1
+oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn
+8TUoE6smftX3eg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert28[] = {
+ 0x30, 0x82, 0x04, 0xb6, 0x30, 0x82, 0x03, 0x9e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x0c, 0x79, 0xa9, 0x44, 0xb0, 0x8c, 0x11, 0x95, 0x20,
+ 0x92, 0x61, 0x5f, 0xe2, 0x6b, 0x1d, 0x83, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x32, 0x32, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x30, 0x32,
+ 0x32, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x75, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41,
+ 0x32, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xd7, 0x53, 0xa4, 0x04, 0x51, 0xf8, 0x99, 0xa6,
+ 0x16, 0x48, 0x4b, 0x67, 0x27, 0xaa, 0x93, 0x49, 0xd0, 0x39, 0xed, 0x0c,
+ 0xb0, 0xb0, 0x00, 0x87, 0xf1, 0x67, 0x28, 0x86, 0x85, 0x8c, 0x8e, 0x63,
+ 0xda, 0xbc, 0xb1, 0x40, 0x38, 0xe2, 0xd3, 0xf5, 0xec, 0xa5, 0x05, 0x18,
+ 0xb8, 0x3d, 0x3e, 0xc5, 0x99, 0x17, 0x32, 0xec, 0x18, 0x8c, 0xfa, 0xf1,
+ 0x0c, 0xa6, 0x64, 0x21, 0x85, 0xcb, 0x07, 0x10, 0x34, 0xb0, 0x52, 0x88,
+ 0x2b, 0x1f, 0x68, 0x9b, 0xd2, 0xb1, 0x8f, 0x12, 0xb0, 0xb3, 0xd2, 0xe7,
+ 0x88, 0x1f, 0x1f, 0xef, 0x38, 0x77, 0x54, 0x53, 0x5f, 0x80, 0x79, 0x3f,
+ 0x2e, 0x1a, 0xaa, 0xa8, 0x1e, 0x4b, 0x2b, 0x0d, 0xab, 0xb7, 0x63, 0xb9,
+ 0x35, 0xb7, 0x7d, 0x14, 0xbc, 0x59, 0x4b, 0xdf, 0x51, 0x4a, 0xd2, 0xa1,
+ 0xe2, 0x0c, 0xe2, 0x90, 0x82, 0x87, 0x6a, 0xae, 0xea, 0xd7, 0x64, 0xd6,
+ 0x98, 0x55, 0xe8, 0xfd, 0xaf, 0x1a, 0x50, 0x6c, 0x54, 0xbc, 0x11, 0xf2,
+ 0xfd, 0x4a, 0xf2, 0x9d, 0xbb, 0x7f, 0x0e, 0xf4, 0xd5, 0xbe, 0x8e, 0x16,
+ 0x89, 0x12, 0x55, 0xd8, 0xc0, 0x71, 0x34, 0xee, 0xf6, 0xdc, 0x2d, 0xec,
+ 0xc4, 0x87, 0x25, 0x86, 0x8d, 0xd8, 0x21, 0xe4, 0xb0, 0x4d, 0x0c, 0x89,
+ 0xdc, 0x39, 0x26, 0x17, 0xdd, 0xf6, 0xd7, 0x94, 0x85, 0xd8, 0x04, 0x21,
+ 0x70, 0x9d, 0x6f, 0x6f, 0xff, 0x5c, 0xba, 0x19, 0xe1, 0x45, 0xcb, 0x56,
+ 0x57, 0x28, 0x7e, 0x1c, 0x0d, 0x41, 0x57, 0xaa, 0xb7, 0xb8, 0x27, 0xbb,
+ 0xb1, 0xe4, 0xfa, 0x2a, 0xef, 0x21, 0x23, 0x75, 0x1a, 0xad, 0x2d, 0x9b,
+ 0x86, 0x35, 0x8c, 0x9c, 0x77, 0xb5, 0x73, 0xad, 0xd8, 0x94, 0x2d, 0xe4,
+ 0xf3, 0x0c, 0x9d, 0xee, 0xc1, 0x4e, 0x62, 0x7e, 0x17, 0xc0, 0x71, 0x9e,
+ 0x2c, 0xde, 0xf1, 0xf9, 0x10, 0x28, 0x19, 0x33, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x49, 0x30, 0x82, 0x01, 0x45, 0x30, 0x12, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01,
+ 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x02, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69,
+ 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4b,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0xa0,
+ 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65,
+ 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61,
+ 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36,
+ 0x30, 0x34, 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a,
+ 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01,
+ 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3d, 0xd3, 0x50, 0xa5, 0xd6, 0xa0, 0xad,
+ 0xee, 0xf3, 0x4a, 0x60, 0x0a, 0x65, 0xd3, 0x21, 0xd4, 0xf8, 0xf8, 0xd6,
+ 0x0f, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4,
+ 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9d, 0xb6, 0xd0, 0x90, 0x86, 0xe1,
+ 0x86, 0x02, 0xed, 0xc5, 0xa0, 0xf0, 0x34, 0x1c, 0x74, 0xc1, 0x8d, 0x76,
+ 0xcc, 0x86, 0x0a, 0xa8, 0xf0, 0x4a, 0x8a, 0x42, 0xd6, 0x3f, 0xc8, 0xa9,
+ 0x4d, 0xad, 0x7c, 0x08, 0xad, 0xe6, 0xb6, 0x50, 0xb8, 0xa2, 0x1a, 0x4d,
+ 0x88, 0x07, 0xb1, 0x29, 0x21, 0xdc, 0xe7, 0xda, 0xc6, 0x3c, 0x21, 0xe0,
+ 0xe3, 0x11, 0x49, 0x70, 0xac, 0x7a, 0x1d, 0x01, 0xa4, 0xca, 0x11, 0x3a,
+ 0x57, 0xab, 0x7d, 0x57, 0x2a, 0x40, 0x74, 0xfd, 0xd3, 0x1d, 0x85, 0x18,
+ 0x50, 0xdf, 0x57, 0x47, 0x75, 0xa1, 0x7d, 0x55, 0x20, 0x2e, 0x47, 0x37,
+ 0x50, 0x72, 0x8c, 0x7f, 0x82, 0x1b, 0xd2, 0x62, 0x8f, 0x2d, 0x03, 0x5a,
+ 0xda, 0xc3, 0xc8, 0xa1, 0xce, 0x2c, 0x52, 0xa2, 0x00, 0x63, 0xeb, 0x73,
+ 0xba, 0x71, 0xc8, 0x49, 0x27, 0x23, 0x97, 0x64, 0x85, 0x9e, 0x38, 0x0e,
+ 0xad, 0x63, 0x68, 0x3c, 0xba, 0x52, 0x81, 0x58, 0x79, 0xa3, 0x2c, 0x0c,
+ 0xdf, 0xde, 0x6d, 0xeb, 0x31, 0xf2, 0xba, 0xa0, 0x7c, 0x6c, 0xf1, 0x2c,
+ 0xd4, 0xe1, 0xbd, 0x77, 0x84, 0x37, 0x03, 0xce, 0x32, 0xb5, 0xc8, 0x9a,
+ 0x81, 0x1a, 0x4a, 0x92, 0x4e, 0x3b, 0x46, 0x9a, 0x85, 0xfe, 0x83, 0xa2,
+ 0xf9, 0x9e, 0x8c, 0xa3, 0xcc, 0x0d, 0x5e, 0xb3, 0x3d, 0xcf, 0x04, 0x78,
+ 0x8f, 0x14, 0x14, 0x7b, 0x32, 0x9c, 0xc7, 0x00, 0xa6, 0x5c, 0xc4, 0xb5,
+ 0xa1, 0x55, 0x8d, 0x5a, 0x56, 0x68, 0xa4, 0x22, 0x70, 0xaa, 0x3c, 0x81,
+ 0x71, 0xd9, 0x9d, 0xa8, 0x45, 0x3b, 0xf4, 0xe5, 0xf6, 0xa2, 0x51, 0xdd,
+ 0xc7, 0x7b, 0x62, 0xe8, 0x6f, 0x0c, 0x74, 0xeb, 0xb8, 0xda, 0xf8, 0xbf,
+ 0x87, 0x0d, 0x79, 0x50, 0x91, 0x90, 0x9b, 0x18, 0x3b, 0x91, 0x59, 0x27,
+ 0xf1, 0x35, 0x28, 0x13, 0xab, 0x26, 0x7e, 0xd5, 0xf7, 0x7a,
+};
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2b.inc b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2b.inc
new file mode 100644
index 00000000000..17c64889652
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_2b.inc
@@ -0,0 +1,5739 @@
+/* This file contains common certificates. It's designed to be #included in
+ * another file, in a namespace. */
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 36:34:9e:18:c9:9c:26:69:b6:56:2e:6c:e5:ad:71:32
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2008 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA - G3
+ Validity
+ Not Before: May 23 00:00:00 2013 GMT
+ Not After : May 22 23:59:59 2023 GMT
+ Subject: C=US, O=thawte, Inc., CN=thawte SHA256 SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:63:2b:d4:ba:5d:38:ae:b0:cf:b9:4c:38:df:
+ 20:7d:f1:2b:47:71:1d:8b:68:f3:56:f9:9c:da:aa:
+ e5:84:26:de:a5:71:30:bc:f3:31:23:9d:e8:3b:80:
+ c8:66:57:75:b6:57:0e:db:93:f5:26:8e:70:ba:64:
+ 52:66:8a:2a:88:5c:44:18:4d:a8:a2:7c:bd:56:61:
+ 32:90:12:f9:35:87:48:60:b0:6e:90:67:44:01:8d:
+ e7:c9:0d:63:68:72:72:ab:63:3c:86:b8:1f:7d:ad:
+ 88:25:a7:6a:88:29:fb:59:c6:78:71:5f:2c:ba:89:
+ e6:d3:80:fd:57:ec:b9:51:5f:43:33:2e:7e:25:3b:
+ a4:04:d1:60:8c:b3:44:33:93:0c:ad:2a:b6:44:a2:
+ 19:3b:af:c4:90:6f:7b:05:87:86:9b:2c:6a:9d:2b:
+ 6c:77:c9:00:9f:c9:cf:ac:ed:3e:1b:f7:c3:f3:d9:
+ f8:6c:d4:a0:57:c4:fb:28:32:aa:33:f0:e6:ba:98:
+ df:e5:c2:4e:9c:74:bf:8a:48:c2:f2:1b:f0:77:40:
+ 41:07:04:b2:3a:d5:4c:c4:29:a9:11:40:3f:02:46:
+ f0:91:d5:d2:81:83:86:13:b3:31:ed:46:ab:a8:87:
+ 76:a9:99:7d:bc:cd:31:50:f4:a5:b5:dc:a5:32:b3:
+ 8b:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.thawte.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: https://www.thawte.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePCA-G3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-415
+ X509v3 Subject Key Identifier:
+ 2B:9A:35:AE:01:18:38:30:E1:70:7A:05:E0:11:76:A3:CE:BD:90:14
+ X509v3 Authority Key Identifier:
+ keyid:AD:6C:AA:94:60:9C:ED:E4:FF:FA:3E:0A:74:2B:63:03:F7:B6:59:BF
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 74:a6:56:e8:af:93:96:19:fb:26:f9:0d:b0:44:a5:cd:e9:7a:
+ 48:03:74:01:6c:13:71:b7:e0:82:90:99:62:23:e3:d6:99:af:
+ f0:c7:1e:9e:a8:18:21:db:b4:94:3f:34:56:1b:99:55:2f:8e:
+ f0:45:33:32:b7:72:c1:13:5b:34:d3:f5:60:e5:2e:18:d1:5c:
+ c5:6a:c1:aa:87:50:0c:1c:9d:64:2b:ff:1b:dc:d5:2e:61:0b:
+ e7:b9:b6:91:53:86:d9:03:2a:d1:3d:7b:4a:da:2b:07:be:29:
+ f2:60:42:a9:91:1a:0e:2e:3c:d1:7d:a5:13:14:02:fa:ee:8b:
+ 8d:b6:c8:b8:3e:56:81:57:21:24:3f:65:c3:b4:c9:ce:5c:8d:
+ 46:ac:53:f3:f9:55:74:c8:2b:fd:d2:78:70:f5:f8:11:e5:f4:
+ a7:ad:20:f5:9d:f1:ec:70:f6:13:ac:e6:8c:8d:db:3f:c6:f2:
+ 79:0e:ab:52:f2:cc:1b:79:27:cf:16:b3:d6:f3:c6:36:80:43:
+ ec:c5:94:f0:dd:90:8d:f8:c6:52:46:56:eb:74:47:be:a6:f3:
+ 19:ae:71:4c:c0:e1:e7:d4:cf:ed:d4:06:28:2a:11:3c:ba:d9:
+ 41:6e:00:e7:81:37:93:e4:da:62:c6:1d:67:6f:63:b4:14:86:
+ d9:a6:62:f0
+-----BEGIN CERTIFICATE-----
+MIIEwjCCA6qgAwIBAgIQNjSeGMmcJmm2Vi5s5a1xMjANBgkqhkiG9w0BAQsFADCB
+rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
+BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0xMzA1MjMwMDAwMDBa
+Fw0yMzA1MjIyMzU5NTlaMEMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUs
+IEluYy4xHTAbBgNVBAMTFHRoYXd0ZSBTSEEyNTYgU1NMIENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2Mr1LpdOK6wz7lMON8gffErR3Edi2jzVvmc
+2qrlhCbepXEwvPMxI53oO4DIZld1tlcO25P1Jo5wumRSZooqiFxEGE2oony9VmEy
+kBL5NYdIYLBukGdEAY3nyQ1jaHJyq2M8hrgffa2IJadqiCn7WcZ4cV8suonm04D9
+V+y5UV9DMy5+JTukBNFgjLNEM5MMrSq2RKIZO6/EkG97BYeGmyxqnStsd8kAn8nP
+rO0+G/fD89n4bNSgV8T7KDKqM/Dmupjf5cJOnHS/ikjC8hvwd0BBBwSyOtVMxCmp
+EUA/AkbwkdXSgYOGE7Mx7UarqId2qZl9vM0xUPSltdylMrOLiwIDAQABo4IBRDCC
+AUAwMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3
+dGUuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwQQYDVR0gBDowODA2BgpghkgBhvhF
+AQc2MCgwJgYIKwYBBQUHAgEWGmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3BzMDcG
+A1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVQQ0Et
+RzMuY3JsMA4GA1UdDwEB/wQEAwIBBjAqBgNVHREEIzAhpB8wHTEbMBkGA1UEAxMS
+VmVyaVNpZ25NUEtJLTItNDE1MB0GA1UdDgQWBBQrmjWuARg4MOFwegXgEXajzr2Q
+FDAfBgNVHSMEGDAWgBStbKqUYJzt5P/6Pgp0K2MD97ZZvzANBgkqhkiG9w0BAQsF
+AAOCAQEAdKZW6K+Tlhn7JvkNsESlzel6SAN0AWwTcbfggpCZYiPj1pmv8McenqgY
+Idu0lD80VhuZVS+O8EUzMrdywRNbNNP1YOUuGNFcxWrBqodQDBydZCv/G9zVLmEL
+57m2kVOG2QMq0T17StorB74p8mBCqZEaDi480X2lExQC+u6LjbbIuD5WgVchJD9l
+w7TJzlyNRqxT8/lVdMgr/dJ4cPX4EeX0p60g9Z3x7HD2E6zmjI3bP8byeQ6rUvLM
+G3knzxaz1vPGNoBD7MWU8N2QjfjGUkZW63RHvqbzGa5xTMDh59TP7dQGKCoRPLrZ
+QW4A54E3k+TaYsYdZ29jtBSG2aZi8A==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert29[] = {
+ 0x30, 0x82, 0x04, 0xc2, 0x30, 0x82, 0x03, 0xaa, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x36, 0x34, 0x9e, 0x18, 0xc9, 0x9c, 0x26, 0x69, 0xb6,
+ 0x56, 0x2e, 0x6c, 0xe5, 0xad, 0x71, 0x32, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x38, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x1b, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x35, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x17, 0x0d, 0x32, 0x33, 0x30, 0x35, 0x32, 0x32, 0x32, 0x33, 0x35, 0x39,
+ 0x35, 0x39, 0x5a, 0x30, 0x43, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x14, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53,
+ 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x63, 0x2b,
+ 0xd4, 0xba, 0x5d, 0x38, 0xae, 0xb0, 0xcf, 0xb9, 0x4c, 0x38, 0xdf, 0x20,
+ 0x7d, 0xf1, 0x2b, 0x47, 0x71, 0x1d, 0x8b, 0x68, 0xf3, 0x56, 0xf9, 0x9c,
+ 0xda, 0xaa, 0xe5, 0x84, 0x26, 0xde, 0xa5, 0x71, 0x30, 0xbc, 0xf3, 0x31,
+ 0x23, 0x9d, 0xe8, 0x3b, 0x80, 0xc8, 0x66, 0x57, 0x75, 0xb6, 0x57, 0x0e,
+ 0xdb, 0x93, 0xf5, 0x26, 0x8e, 0x70, 0xba, 0x64, 0x52, 0x66, 0x8a, 0x2a,
+ 0x88, 0x5c, 0x44, 0x18, 0x4d, 0xa8, 0xa2, 0x7c, 0xbd, 0x56, 0x61, 0x32,
+ 0x90, 0x12, 0xf9, 0x35, 0x87, 0x48, 0x60, 0xb0, 0x6e, 0x90, 0x67, 0x44,
+ 0x01, 0x8d, 0xe7, 0xc9, 0x0d, 0x63, 0x68, 0x72, 0x72, 0xab, 0x63, 0x3c,
+ 0x86, 0xb8, 0x1f, 0x7d, 0xad, 0x88, 0x25, 0xa7, 0x6a, 0x88, 0x29, 0xfb,
+ 0x59, 0xc6, 0x78, 0x71, 0x5f, 0x2c, 0xba, 0x89, 0xe6, 0xd3, 0x80, 0xfd,
+ 0x57, 0xec, 0xb9, 0x51, 0x5f, 0x43, 0x33, 0x2e, 0x7e, 0x25, 0x3b, 0xa4,
+ 0x04, 0xd1, 0x60, 0x8c, 0xb3, 0x44, 0x33, 0x93, 0x0c, 0xad, 0x2a, 0xb6,
+ 0x44, 0xa2, 0x19, 0x3b, 0xaf, 0xc4, 0x90, 0x6f, 0x7b, 0x05, 0x87, 0x86,
+ 0x9b, 0x2c, 0x6a, 0x9d, 0x2b, 0x6c, 0x77, 0xc9, 0x00, 0x9f, 0xc9, 0xcf,
+ 0xac, 0xed, 0x3e, 0x1b, 0xf7, 0xc3, 0xf3, 0xd9, 0xf8, 0x6c, 0xd4, 0xa0,
+ 0x57, 0xc4, 0xfb, 0x28, 0x32, 0xaa, 0x33, 0xf0, 0xe6, 0xba, 0x98, 0xdf,
+ 0xe5, 0xc2, 0x4e, 0x9c, 0x74, 0xbf, 0x8a, 0x48, 0xc2, 0xf2, 0x1b, 0xf0,
+ 0x77, 0x40, 0x41, 0x07, 0x04, 0xb2, 0x3a, 0xd5, 0x4c, 0xc4, 0x29, 0xa9,
+ 0x11, 0x40, 0x3f, 0x02, 0x46, 0xf0, 0x91, 0xd5, 0xd2, 0x81, 0x83, 0x86,
+ 0x13, 0xb3, 0x31, 0xed, 0x46, 0xab, 0xa8, 0x87, 0x76, 0xa9, 0x99, 0x7d,
+ 0xbc, 0xcd, 0x31, 0x50, 0xf4, 0xa5, 0xb5, 0xdc, 0xa5, 0x32, 0xb3, 0x8b,
+ 0x8b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x44, 0x30, 0x82,
+ 0x01, 0x40, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30,
+ 0x38, 0x30, 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45,
+ 0x01, 0x07, 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74,
+ 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x37, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30, 0x30, 0x2e, 0x30, 0x2c, 0xa0, 0x2a,
+ 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2d,
+ 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2a,
+ 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30,
+ 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49,
+ 0x2d, 0x32, 0x2d, 0x34, 0x31, 0x35, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2b, 0x9a, 0x35, 0xae, 0x01, 0x18, 0x38,
+ 0x30, 0xe1, 0x70, 0x7a, 0x05, 0xe0, 0x11, 0x76, 0xa3, 0xce, 0xbd, 0x90,
+ 0x14, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xad, 0x6c, 0xaa, 0x94, 0x60, 0x9c, 0xed, 0xe4, 0xff, 0xfa,
+ 0x3e, 0x0a, 0x74, 0x2b, 0x63, 0x03, 0xf7, 0xb6, 0x59, 0xbf, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x74, 0xa6, 0x56, 0xe8, 0xaf, 0x93,
+ 0x96, 0x19, 0xfb, 0x26, 0xf9, 0x0d, 0xb0, 0x44, 0xa5, 0xcd, 0xe9, 0x7a,
+ 0x48, 0x03, 0x74, 0x01, 0x6c, 0x13, 0x71, 0xb7, 0xe0, 0x82, 0x90, 0x99,
+ 0x62, 0x23, 0xe3, 0xd6, 0x99, 0xaf, 0xf0, 0xc7, 0x1e, 0x9e, 0xa8, 0x18,
+ 0x21, 0xdb, 0xb4, 0x94, 0x3f, 0x34, 0x56, 0x1b, 0x99, 0x55, 0x2f, 0x8e,
+ 0xf0, 0x45, 0x33, 0x32, 0xb7, 0x72, 0xc1, 0x13, 0x5b, 0x34, 0xd3, 0xf5,
+ 0x60, 0xe5, 0x2e, 0x18, 0xd1, 0x5c, 0xc5, 0x6a, 0xc1, 0xaa, 0x87, 0x50,
+ 0x0c, 0x1c, 0x9d, 0x64, 0x2b, 0xff, 0x1b, 0xdc, 0xd5, 0x2e, 0x61, 0x0b,
+ 0xe7, 0xb9, 0xb6, 0x91, 0x53, 0x86, 0xd9, 0x03, 0x2a, 0xd1, 0x3d, 0x7b,
+ 0x4a, 0xda, 0x2b, 0x07, 0xbe, 0x29, 0xf2, 0x60, 0x42, 0xa9, 0x91, 0x1a,
+ 0x0e, 0x2e, 0x3c, 0xd1, 0x7d, 0xa5, 0x13, 0x14, 0x02, 0xfa, 0xee, 0x8b,
+ 0x8d, 0xb6, 0xc8, 0xb8, 0x3e, 0x56, 0x81, 0x57, 0x21, 0x24, 0x3f, 0x65,
+ 0xc3, 0xb4, 0xc9, 0xce, 0x5c, 0x8d, 0x46, 0xac, 0x53, 0xf3, 0xf9, 0x55,
+ 0x74, 0xc8, 0x2b, 0xfd, 0xd2, 0x78, 0x70, 0xf5, 0xf8, 0x11, 0xe5, 0xf4,
+ 0xa7, 0xad, 0x20, 0xf5, 0x9d, 0xf1, 0xec, 0x70, 0xf6, 0x13, 0xac, 0xe6,
+ 0x8c, 0x8d, 0xdb, 0x3f, 0xc6, 0xf2, 0x79, 0x0e, 0xab, 0x52, 0xf2, 0xcc,
+ 0x1b, 0x79, 0x27, 0xcf, 0x16, 0xb3, 0xd6, 0xf3, 0xc6, 0x36, 0x80, 0x43,
+ 0xec, 0xc5, 0x94, 0xf0, 0xdd, 0x90, 0x8d, 0xf8, 0xc6, 0x52, 0x46, 0x56,
+ 0xeb, 0x74, 0x47, 0xbe, 0xa6, 0xf3, 0x19, 0xae, 0x71, 0x4c, 0xc0, 0xe1,
+ 0xe7, 0xd4, 0xcf, 0xed, 0xd4, 0x06, 0x28, 0x2a, 0x11, 0x3c, 0xba, 0xd9,
+ 0x41, 0x6e, 0x00, 0xe7, 0x81, 0x37, 0x93, 0xe4, 0xda, 0x62, 0xc6, 0x1d,
+ 0x67, 0x6f, 0x63, 0xb4, 0x14, 0x86, 0xd9, 0xa6, 0x62, 0xf0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 35:97:31:87:f3:87:3a:07:32:7e:ce:58:0c:9b:7e:da
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ X509v3 Extended Key Usage:
+ Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1, TLS Web Server Authentication, TLS Web Client Authentication
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 0f:25:ae:48:ed:1b:33:85:4c:0c:b5:c2:d7:fe:4d:d6:83:28:
+ 4c:41:65:60:00:0b:77:48:71:82:fe:7f:db:5a:0e:20:cc:d2:
+ ea:47:bc:64:42:61:44:34:74:30:81:81:26:8a:4a:f7:44:5d:
+ 7e:34:80:a8:b8:83:e2:09:d7:6d:23:dd:89:ed:28:08:bd:63:
+ 5a:11:57:08:c4:9e:da:e2:68:28:af:dd:50:3c:ec:82:21:d8:
+ 00:c2:55:44:50:70:41:ad:83:17:79:ba:08:f3:2b:de:ed:34:
+ 1d:44:9e:d2:04:93:f4:cb:05:17:2d:09:2d:2d:63:ef:f6:26:
+ 0b:7b
+-----BEGIN CERTIFICATE-----
+MIIExjCCBC+gAwIBAgIQNZcxh/OHOgcyfs5YDJt+2jANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggGRMIIBjTAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
+BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
+aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwNAYD
+VR0lBC0wKwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBBggrBgEFBQcDAQYIKwYBBQUH
+AwIwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUr
+DgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNp
+Z24uY29tL3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhho
+dHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEADyWuSO0b
+M4VMDLXC1/5N1oMoTEFlYAALd0hxgv5/21oOIMzS6ke8ZEJhRDR0MIGBJopK90Rd
+fjSAqLiD4gnXbSPdie0oCL1jWhFXCMSe2uJoKK/dUDzsgiHYAMJVRFBwQa2DF3m6
+CPMr3u00HUSe0gST9MsFFy0JLS1j7/YmC3s=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert30[] = {
+ 0x30, 0x82, 0x04, 0xc6, 0x30, 0x82, 0x04, 0x2f, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x35, 0x97, 0x31, 0x87, 0xf3, 0x87, 0x3a, 0x07, 0x32,
+ 0x7e, 0xce, 0x58, 0x0c, 0x9b, 0x7e, 0xda, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x91, 0x30, 0x82, 0x01, 0x8d, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x34, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01,
+ 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59,
+ 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f,
+ 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b,
+ 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac,
+ 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b,
+ 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67,
+ 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x0f, 0x25, 0xae, 0x48, 0xed, 0x1b,
+ 0x33, 0x85, 0x4c, 0x0c, 0xb5, 0xc2, 0xd7, 0xfe, 0x4d, 0xd6, 0x83, 0x28,
+ 0x4c, 0x41, 0x65, 0x60, 0x00, 0x0b, 0x77, 0x48, 0x71, 0x82, 0xfe, 0x7f,
+ 0xdb, 0x5a, 0x0e, 0x20, 0xcc, 0xd2, 0xea, 0x47, 0xbc, 0x64, 0x42, 0x61,
+ 0x44, 0x34, 0x74, 0x30, 0x81, 0x81, 0x26, 0x8a, 0x4a, 0xf7, 0x44, 0x5d,
+ 0x7e, 0x34, 0x80, 0xa8, 0xb8, 0x83, 0xe2, 0x09, 0xd7, 0x6d, 0x23, 0xdd,
+ 0x89, 0xed, 0x28, 0x08, 0xbd, 0x63, 0x5a, 0x11, 0x57, 0x08, 0xc4, 0x9e,
+ 0xda, 0xe2, 0x68, 0x28, 0xaf, 0xdd, 0x50, 0x3c, 0xec, 0x82, 0x21, 0xd8,
+ 0x00, 0xc2, 0x55, 0x44, 0x50, 0x70, 0x41, 0xad, 0x83, 0x17, 0x79, 0xba,
+ 0x08, 0xf3, 0x2b, 0xde, 0xed, 0x34, 0x1d, 0x44, 0x9e, 0xd2, 0x04, 0x93,
+ 0xf4, 0xcb, 0x05, 0x17, 0x2d, 0x09, 0x2d, 0x2d, 0x63, 0xef, 0xf6, 0x26,
+ 0x0b, 0x7b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 7 (0x7)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2
+ Validity
+ Not Before: May 3 07:00:00 2011 GMT
+ Not After : May 3 07:00:00 2031 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., OU=http://certs.godaddy.com/repository/, CN=Go Daddy Secure Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b9:e0:cb:10:d4:af:76:bd:d4:93:62:eb:30:64:
+ b8:81:08:6c:c3:04:d9:62:17:8e:2f:ff:3e:65:cf:
+ 8f:ce:62:e6:3c:52:1c:da:16:45:4b:55:ab:78:6b:
+ 63:83:62:90:ce:0f:69:6c:99:c8:1a:14:8b:4c:cc:
+ 45:33:ea:88:dc:9e:a3:af:2b:fe:80:61:9d:79:57:
+ c4:cf:2e:f4:3f:30:3c:5d:47:fc:9a:16:bc:c3:37:
+ 96:41:51:8e:11:4b:54:f8:28:be:d0:8c:be:f0:30:
+ 38:1e:f3:b0:26:f8:66:47:63:6d:de:71:26:47:8f:
+ 38:47:53:d1:46:1d:b4:e3:dc:00:ea:45:ac:bd:bc:
+ 71:d9:aa:6f:00:db:db:cd:30:3a:79:4f:5f:4c:47:
+ f8:1d:ef:5b:c2:c4:9d:60:3b:b1:b2:43:91:d8:a4:
+ 33:4e:ea:b3:d6:27:4f:ad:25:8a:a5:c6:f4:d5:d0:
+ a6:ae:74:05:64:57:88:b5:44:55:d4:2d:2a:3a:3e:
+ f8:b8:bd:e9:32:0a:02:94:64:c4:16:3a:50:f1:4a:
+ ae:e7:79:33:af:0c:20:07:7f:e8:df:04:39:c2:69:
+ 02:6c:63:52:fa:77:c1:1b:c8:74:87:c8:b9:93:18:
+ 50:54:35:4b:69:4e:bc:3b:d3:49:2e:1f:dc:c1:d2:
+ 52:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 40:C2:BD:27:8E:CC:34:83:30:A2:33:D7:FB:6C:B3:F0:B4:2C:80:CE
+ X509v3 Authority Key Identifier:
+ keyid:3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.godaddy.com/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.godaddy.com/gdroot-g2.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://certs.godaddy.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 08:7e:6c:93:10:c8:38:b8:96:a9:90:4b:ff:a1:5f:4f:04:ef:
+ 6c:3e:9c:88:06:c9:50:8f:a6:73:f7:57:31:1b:be:bc:e4:2f:
+ db:f8:ba:d3:5b:e0:b4:e7:e6:79:62:0e:0c:a2:d7:6a:63:73:
+ 31:b5:f5:a8:48:a4:3b:08:2d:a2:5d:90:d7:b4:7c:25:4f:11:
+ 56:30:c4:b6:44:9d:7b:2c:9d:e5:5e:e6:ef:0c:61:aa:bf:e4:
+ 2a:1b:ee:84:9e:b8:83:7d:c1:43:ce:44:a7:13:70:0d:91:1f:
+ f4:c8:13:ad:83:60:d9:d8:72:a8:73:24:1e:b5:ac:22:0e:ca:
+ 17:89:62:58:44:1b:ab:89:25:01:00:0f:cd:c4:1b:62:db:51:
+ b4:d3:0f:51:2a:9b:f4:bc:73:fc:76:ce:36:a4:cd:d9:d8:2c:
+ ea:ae:9b:f5:2a:b2:90:d1:4d:75:18:8a:3f:8a:41:90:23:7d:
+ 5b:4b:fe:a4:03:58:9b:46:b2:c3:60:60:83:f8:7d:50:41:ce:
+ c2:a1:90:c3:bb:ef:02:2f:d2:15:54:ee:44:15:d9:0a:ae:a7:
+ 8a:33:ed:b1:2d:76:36:26:dc:04:eb:9f:f7:61:1f:15:dc:87:
+ 6f:ee:46:96:28:ad:a1:26:7d:0a:09:a7:2e:04:a3:8d:bc:f8:
+ bc:04:30:01
+-----BEGIN CERTIFICATE-----
+MIIE0DCCA7igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
+EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
+ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3
+MDAwMFowgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
+EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UE
+CxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQD
+EypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzD
+BNliF44v/z5lz4/OYuY8UhzaFkVLVat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOv
+K/6AYZ15V8TPLvQ/MDxdR/yaFrzDN5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23e
+cSZHjzhHU9FGHbTj3ADqRay9vHHZqm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HY
+pDNO6rPWJ0+tJYqlxvTV0KaudAVkV4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7n
+eTOvDCAHf+jfBDnCaQJsY1L6d8EbyHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMB
+AAGjggEaMIIBFjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
+HQ4EFgQUQMK9J47MNIMwojPX+2yz8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv
+9r0FQW4gwZTaD94wNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC5nb2RhZGR5LmNvbS8wNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5n
+b2RhZGR5LmNvbS9nZHJvb3QtZzIuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEG
+CCsGAQUFBwIBFiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQAIfmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz
+91cxG7685C/b+LrTW+C05+Z5Yg4MotdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2
+RJ17LJ3lXubvDGGqv+QqG+6EnriDfcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawi
+DsoXiWJYRBuriSUBAA/NxBti21G00w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11
+GIo/ikGQI31bS/6kA1ibRrLDYGCD+H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2x
+LXY2JtwE65/3YR8V3Idv7kaWKK2hJn0KCacuBKONvPi8BDAB
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert31[] = {
+ 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x03, 0xb8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x83, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72,
+ 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61,
+ 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64,
+ 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, 0xb4, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a,
+ 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65,
+ 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47,
+ 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65,
+ 0x72, 0x74, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x2f, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x2a, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0xe0, 0xcb, 0x10, 0xd4, 0xaf, 0x76,
+ 0xbd, 0xd4, 0x93, 0x62, 0xeb, 0x30, 0x64, 0xb8, 0x81, 0x08, 0x6c, 0xc3,
+ 0x04, 0xd9, 0x62, 0x17, 0x8e, 0x2f, 0xff, 0x3e, 0x65, 0xcf, 0x8f, 0xce,
+ 0x62, 0xe6, 0x3c, 0x52, 0x1c, 0xda, 0x16, 0x45, 0x4b, 0x55, 0xab, 0x78,
+ 0x6b, 0x63, 0x83, 0x62, 0x90, 0xce, 0x0f, 0x69, 0x6c, 0x99, 0xc8, 0x1a,
+ 0x14, 0x8b, 0x4c, 0xcc, 0x45, 0x33, 0xea, 0x88, 0xdc, 0x9e, 0xa3, 0xaf,
+ 0x2b, 0xfe, 0x80, 0x61, 0x9d, 0x79, 0x57, 0xc4, 0xcf, 0x2e, 0xf4, 0x3f,
+ 0x30, 0x3c, 0x5d, 0x47, 0xfc, 0x9a, 0x16, 0xbc, 0xc3, 0x37, 0x96, 0x41,
+ 0x51, 0x8e, 0x11, 0x4b, 0x54, 0xf8, 0x28, 0xbe, 0xd0, 0x8c, 0xbe, 0xf0,
+ 0x30, 0x38, 0x1e, 0xf3, 0xb0, 0x26, 0xf8, 0x66, 0x47, 0x63, 0x6d, 0xde,
+ 0x71, 0x26, 0x47, 0x8f, 0x38, 0x47, 0x53, 0xd1, 0x46, 0x1d, 0xb4, 0xe3,
+ 0xdc, 0x00, 0xea, 0x45, 0xac, 0xbd, 0xbc, 0x71, 0xd9, 0xaa, 0x6f, 0x00,
+ 0xdb, 0xdb, 0xcd, 0x30, 0x3a, 0x79, 0x4f, 0x5f, 0x4c, 0x47, 0xf8, 0x1d,
+ 0xef, 0x5b, 0xc2, 0xc4, 0x9d, 0x60, 0x3b, 0xb1, 0xb2, 0x43, 0x91, 0xd8,
+ 0xa4, 0x33, 0x4e, 0xea, 0xb3, 0xd6, 0x27, 0x4f, 0xad, 0x25, 0x8a, 0xa5,
+ 0xc6, 0xf4, 0xd5, 0xd0, 0xa6, 0xae, 0x74, 0x05, 0x64, 0x57, 0x88, 0xb5,
+ 0x44, 0x55, 0xd4, 0x2d, 0x2a, 0x3a, 0x3e, 0xf8, 0xb8, 0xbd, 0xe9, 0x32,
+ 0x0a, 0x02, 0x94, 0x64, 0xc4, 0x16, 0x3a, 0x50, 0xf1, 0x4a, 0xae, 0xe7,
+ 0x79, 0x33, 0xaf, 0x0c, 0x20, 0x07, 0x7f, 0xe8, 0xdf, 0x04, 0x39, 0xc2,
+ 0x69, 0x02, 0x6c, 0x63, 0x52, 0xfa, 0x77, 0xc1, 0x1b, 0xc8, 0x74, 0x87,
+ 0xc8, 0xb9, 0x93, 0x18, 0x50, 0x54, 0x35, 0x4b, 0x69, 0x4e, 0xbc, 0x3b,
+ 0xd3, 0x49, 0x2e, 0x1f, 0xdc, 0xc1, 0xd2, 0x52, 0xfb, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1a, 0x30, 0x82, 0x01, 0x16, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x40, 0xc2, 0xbd, 0x27, 0x8e, 0xcc,
+ 0x34, 0x83, 0x30, 0xa2, 0x33, 0xd7, 0xfb, 0x6c, 0xb3, 0xf0, 0xb4, 0x2c,
+ 0x80, 0xce, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0x3a, 0x9a, 0x85, 0x07, 0x10, 0x67, 0x28, 0xb6, 0xef,
+ 0xf6, 0xbd, 0x05, 0x41, 0x6e, 0x20, 0xc1, 0x94, 0xda, 0x0f, 0xde, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67,
+ 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67,
+ 0x64, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30,
+ 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73,
+ 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x08, 0x7e, 0x6c, 0x93,
+ 0x10, 0xc8, 0x38, 0xb8, 0x96, 0xa9, 0x90, 0x4b, 0xff, 0xa1, 0x5f, 0x4f,
+ 0x04, 0xef, 0x6c, 0x3e, 0x9c, 0x88, 0x06, 0xc9, 0x50, 0x8f, 0xa6, 0x73,
+ 0xf7, 0x57, 0x31, 0x1b, 0xbe, 0xbc, 0xe4, 0x2f, 0xdb, 0xf8, 0xba, 0xd3,
+ 0x5b, 0xe0, 0xb4, 0xe7, 0xe6, 0x79, 0x62, 0x0e, 0x0c, 0xa2, 0xd7, 0x6a,
+ 0x63, 0x73, 0x31, 0xb5, 0xf5, 0xa8, 0x48, 0xa4, 0x3b, 0x08, 0x2d, 0xa2,
+ 0x5d, 0x90, 0xd7, 0xb4, 0x7c, 0x25, 0x4f, 0x11, 0x56, 0x30, 0xc4, 0xb6,
+ 0x44, 0x9d, 0x7b, 0x2c, 0x9d, 0xe5, 0x5e, 0xe6, 0xef, 0x0c, 0x61, 0xaa,
+ 0xbf, 0xe4, 0x2a, 0x1b, 0xee, 0x84, 0x9e, 0xb8, 0x83, 0x7d, 0xc1, 0x43,
+ 0xce, 0x44, 0xa7, 0x13, 0x70, 0x0d, 0x91, 0x1f, 0xf4, 0xc8, 0x13, 0xad,
+ 0x83, 0x60, 0xd9, 0xd8, 0x72, 0xa8, 0x73, 0x24, 0x1e, 0xb5, 0xac, 0x22,
+ 0x0e, 0xca, 0x17, 0x89, 0x62, 0x58, 0x44, 0x1b, 0xab, 0x89, 0x25, 0x01,
+ 0x00, 0x0f, 0xcd, 0xc4, 0x1b, 0x62, 0xdb, 0x51, 0xb4, 0xd3, 0x0f, 0x51,
+ 0x2a, 0x9b, 0xf4, 0xbc, 0x73, 0xfc, 0x76, 0xce, 0x36, 0xa4, 0xcd, 0xd9,
+ 0xd8, 0x2c, 0xea, 0xae, 0x9b, 0xf5, 0x2a, 0xb2, 0x90, 0xd1, 0x4d, 0x75,
+ 0x18, 0x8a, 0x3f, 0x8a, 0x41, 0x90, 0x23, 0x7d, 0x5b, 0x4b, 0xfe, 0xa4,
+ 0x03, 0x58, 0x9b, 0x46, 0xb2, 0xc3, 0x60, 0x60, 0x83, 0xf8, 0x7d, 0x50,
+ 0x41, 0xce, 0xc2, 0xa1, 0x90, 0xc3, 0xbb, 0xef, 0x02, 0x2f, 0xd2, 0x15,
+ 0x54, 0xee, 0x44, 0x15, 0xd9, 0x0a, 0xae, 0xa7, 0x8a, 0x33, 0xed, 0xb1,
+ 0x2d, 0x76, 0x36, 0x26, 0xdc, 0x04, 0xeb, 0x9f, 0xf7, 0x61, 0x1f, 0x15,
+ 0xdc, 0x87, 0x6f, 0xee, 0x46, 0x96, 0x28, 0xad, 0xa1, 0x26, 0x7d, 0x0a,
+ 0x09, 0xa7, 0x2e, 0x04, 0xa3, 0x8d, 0xbc, 0xf8, 0xbc, 0x04, 0x30, 0x01,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0a:48:9e:88:53:7e:8a:a6:45:4d:6e:2c:4b:2a:eb:20
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2008 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA - G3
+ Validity
+ Not Before: Apr 9 00:00:00 2013 GMT
+ Not After : Apr 8 23:59:59 2023 GMT
+ Subject: C=US, O=thawte, Inc., CN=thawte Extended Validation SHA256 SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:f2:c4:bc:74:e8:25:f6:00:62:28:e3:4c:e8:b8:
+ df:13:9f:8b:07:37:ef:62:4a:f1:57:09:f6:82:e8:
+ 75:f0:0a:a9:27:cf:93:3b:ec:36:89:a5:6e:1d:d6:
+ 54:f3:b8:04:97:72:b4:69:25:cc:d1:42:0e:5b:d5:
+ 1c:7f:a2:60:6e:b1:52:1a:db:93:2f:bb:0b:0d:64:
+ 53:16:cb:1c:09:24:95:29:22:b4:8a:18:00:89:fe:
+ f7:1f:72:c8:e8:5c:2f:1a:1b:a2:18:b8:ef:18:5c:
+ cb:b5:db:3a:4e:db:0f:ae:df:c4:79:e3:1e:aa:5c:
+ a3:a4:e5:ac:61:9b:37:85:8f:48:75:1b:b9:d5:68:
+ 96:e9:27:79:70:57:23:1a:bb:6c:93:90:c7:45:d7:
+ 17:d2:37:2a:76:b3:cd:82:a9:4f:c0:03:7b:e1:3d:
+ 7a:7e:5b:b8:85:f2:f5:15:fb:70:a9:bd:f5:50:65:
+ 16:9d:e3:b6:6b:61:6e:a1:7a:9e:e8:0d:1c:f7:2a:
+ 8e:69:7e:43:30:8e:78:ce:ee:65:1e:3b:9b:87:1e:
+ 49:1c:f8:32:46:5d:28:46:79:2a:4e:27:5d:17:58:
+ a8:37:fe:a8:13:a9:69:15:df:36:22:89:75:ba:ca:
+ 01:40:2e:ed:9d:d7:0c:aa:31:ce:27:ae:57:d5:d2:
+ 51:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://ocsp.thawte.com
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.thawte.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePCA-G3.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-374
+ X509v3 Subject Key Identifier:
+ 3B:24:C8:31:A0:B7:5A:D0:6A:B8:D2:CA:07:74:CC:1E:24:D4:C4:DC
+ X509v3 Authority Key Identifier:
+ keyid:AD:6C:AA:94:60:9C:ED:E4:FF:FA:3E:0A:74:2B:63:03:F7:B6:59:BF
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 68:98:26:aa:d4:33:c9:ba:75:70:d4:9f:49:ad:d6:c1:54:dc:
+ ee:aa:56:1f:78:a7:f0:a1:a4:ee:0b:f9:12:af:df:a6:b8:ee:
+ c3:cb:35:13:6a:59:2a:f8:c9:e9:4c:2f:bc:b1:bc:2b:c2:02:
+ 30:e1:c3:be:c2:f0:81:8c:99:77:89:58:00:a3:cc:7f:a3:02:
+ 4c:53:b2:6e:36:4f:fe:df:87:76:b3:3f:ec:5a:62:50:b6:00:
+ 45:58:f2:87:ac:77:e6:d0:20:50:63:c5:e4:b2:70:15:18:90:
+ 05:7b:7b:af:2b:46:be:6b:4e:1f:53:fc:84:27:ae:83:d2:8d:
+ 47:53:a7:0e:1f:63:b5:ba:db:16:d8:6a:09:25:55:7d:8f:3d:
+ 4a:c1:83:f9:b3:b9:a7:04:5a:c8:f3:11:04:91:53:30:d9:52:
+ 87:cb:39:00:9c:ec:53:c3:02:09:7e:a7:36:8e:72:21:2f:23:
+ bb:4c:c6:47:a5:a1:ee:67:c4:2f:5c:3a:47:38:61:e2:c3:1e:
+ 37:92:9e:c8:2f:6b:fa:ef:d2:c3:cd:29:8d:98:f8:52:17:ed:
+ b5:53:3c:df:af:c9:1b:62:ad:df:02:ee:5d:34:f6:41:4b:cb:
+ c3:55:af:b1:cb:da:9c:73:d5:02:a8:2d:a7:ac:fc:e1:e5:07:
+ d0:51:e8:35
+-----BEGIN CERTIFICATE-----
+MIIE0DCCA7igAwIBAgIQCkieiFN+iqZFTW4sSyrrIDANBgkqhkiG9w0BAQsFADCB
+rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
+BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0xMzA0MDkwMDAwMDBa
+Fw0yMzA0MDgyMzU5NTlaMFcxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUs
+IEluYy4xMTAvBgNVBAMTKHRoYXd0ZSBFeHRlbmRlZCBWYWxpZGF0aW9uIFNIQTI1
+NiBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDyxLx06CX2
+AGIo40zouN8Tn4sHN+9iSvFXCfaC6HXwCqknz5M77DaJpW4d1lTzuASXcrRpJczR
+Qg5b1Rx/omBusVIa25MvuwsNZFMWyxwJJJUpIrSKGACJ/vcfcsjoXC8aG6IYuO8Y
+XMu12zpO2w+u38R54x6qXKOk5axhmzeFj0h1G7nVaJbpJ3lwVyMau2yTkMdF1xfS
+Nyp2s82CqU/AA3vhPXp+W7iF8vUV+3CpvfVQZRad47ZrYW6hep7oDRz3Ko5pfkMw
+jnjO7mUeO5uHHkkc+DJGXShGeSpOJ10XWKg3/qgTqWkV3zYiiXW6ygFALu2d1wyq
+Mc4nrlfV0lH7AgMBAAGjggE+MIIBOjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1Ud
+DwEB/wQEAwIBBjAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9v
+Y3NwLnRoYXd0ZS5jb20wOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEW
+Gmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3BzMDcGA1UdHwQwMC4wLKAqoCiGJmh0
+dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVQQ0EtRzMuY3JsMCoGA1UdEQQjMCGk
+HzAdMRswGQYDVQQDExJWZXJpU2lnbk1QS0ktMi0zNzQwHQYDVR0OBBYEFDskyDGg
+t1rQarjSygd0zB4k1MTcMB8GA1UdIwQYMBaAFK1sqpRgnO3k//o+CnQrYwP3tlm/
+MA0GCSqGSIb3DQEBCwUAA4IBAQBomCaq1DPJunVw1J9JrdbBVNzuqlYfeKfwoaTu
+C/kSr9+muO7DyzUTalkq+MnpTC+8sbwrwgIw4cO+wvCBjJl3iVgAo8x/owJMU7Ju
+Nk/+34d2sz/sWmJQtgBFWPKHrHfm0CBQY8XksnAVGJAFe3uvK0a+a04fU/yEJ66D
+0o1HU6cOH2O1utsW2GoJJVV9jz1KwYP5s7mnBFrI8xEEkVMw2VKHyzkAnOxTwwIJ
+fqc2jnIhLyO7TMZHpaHuZ8QvXDpHOGHiwx43kp7IL2v679LDzSmNmPhSF+21Uzzf
+r8kbYq3fAu5dNPZBS8vDVa+xy9qcc9UCqC2nrPzh5QfQUeg1
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert32[] = {
+ 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x03, 0xb8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x0a, 0x48, 0x9e, 0x88, 0x53, 0x7e, 0x8a, 0xa6, 0x45,
+ 0x4d, 0x6e, 0x2c, 0x4b, 0x2a, 0xeb, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x38, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x1b, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x34, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x17, 0x0d, 0x32, 0x33, 0x30, 0x34, 0x30, 0x38, 0x32, 0x33, 0x35, 0x39,
+ 0x35, 0x39, 0x5a, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x28, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69,
+ 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35,
+ 0x36, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xf2, 0xc4, 0xbc, 0x74, 0xe8, 0x25, 0xf6,
+ 0x00, 0x62, 0x28, 0xe3, 0x4c, 0xe8, 0xb8, 0xdf, 0x13, 0x9f, 0x8b, 0x07,
+ 0x37, 0xef, 0x62, 0x4a, 0xf1, 0x57, 0x09, 0xf6, 0x82, 0xe8, 0x75, 0xf0,
+ 0x0a, 0xa9, 0x27, 0xcf, 0x93, 0x3b, 0xec, 0x36, 0x89, 0xa5, 0x6e, 0x1d,
+ 0xd6, 0x54, 0xf3, 0xb8, 0x04, 0x97, 0x72, 0xb4, 0x69, 0x25, 0xcc, 0xd1,
+ 0x42, 0x0e, 0x5b, 0xd5, 0x1c, 0x7f, 0xa2, 0x60, 0x6e, 0xb1, 0x52, 0x1a,
+ 0xdb, 0x93, 0x2f, 0xbb, 0x0b, 0x0d, 0x64, 0x53, 0x16, 0xcb, 0x1c, 0x09,
+ 0x24, 0x95, 0x29, 0x22, 0xb4, 0x8a, 0x18, 0x00, 0x89, 0xfe, 0xf7, 0x1f,
+ 0x72, 0xc8, 0xe8, 0x5c, 0x2f, 0x1a, 0x1b, 0xa2, 0x18, 0xb8, 0xef, 0x18,
+ 0x5c, 0xcb, 0xb5, 0xdb, 0x3a, 0x4e, 0xdb, 0x0f, 0xae, 0xdf, 0xc4, 0x79,
+ 0xe3, 0x1e, 0xaa, 0x5c, 0xa3, 0xa4, 0xe5, 0xac, 0x61, 0x9b, 0x37, 0x85,
+ 0x8f, 0x48, 0x75, 0x1b, 0xb9, 0xd5, 0x68, 0x96, 0xe9, 0x27, 0x79, 0x70,
+ 0x57, 0x23, 0x1a, 0xbb, 0x6c, 0x93, 0x90, 0xc7, 0x45, 0xd7, 0x17, 0xd2,
+ 0x37, 0x2a, 0x76, 0xb3, 0xcd, 0x82, 0xa9, 0x4f, 0xc0, 0x03, 0x7b, 0xe1,
+ 0x3d, 0x7a, 0x7e, 0x5b, 0xb8, 0x85, 0xf2, 0xf5, 0x15, 0xfb, 0x70, 0xa9,
+ 0xbd, 0xf5, 0x50, 0x65, 0x16, 0x9d, 0xe3, 0xb6, 0x6b, 0x61, 0x6e, 0xa1,
+ 0x7a, 0x9e, 0xe8, 0x0d, 0x1c, 0xf7, 0x2a, 0x8e, 0x69, 0x7e, 0x43, 0x30,
+ 0x8e, 0x78, 0xce, 0xee, 0x65, 0x1e, 0x3b, 0x9b, 0x87, 0x1e, 0x49, 0x1c,
+ 0xf8, 0x32, 0x46, 0x5d, 0x28, 0x46, 0x79, 0x2a, 0x4e, 0x27, 0x5d, 0x17,
+ 0x58, 0xa8, 0x37, 0xfe, 0xa8, 0x13, 0xa9, 0x69, 0x15, 0xdf, 0x36, 0x22,
+ 0x89, 0x75, 0xba, 0xca, 0x01, 0x40, 0x2e, 0xed, 0x9d, 0xd7, 0x0c, 0xaa,
+ 0x31, 0xce, 0x27, 0xae, 0x57, 0xd5, 0xd2, 0x51, 0xfb, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x3e, 0x30, 0x82, 0x01, 0x3a, 0x30, 0x12,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
+ 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x32,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x26,
+ 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f,
+ 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30,
+ 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30,
+ 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x70, 0x73, 0x30, 0x37, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30,
+ 0x30, 0x2e, 0x30, 0x2c, 0xa0, 0x2a, 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61,
+ 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x50, 0x43, 0x41, 0x2d, 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, 0x30, 0x21, 0xa4,
+ 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50,
+ 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x33, 0x37, 0x34, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3b, 0x24, 0xc8, 0x31, 0xa0,
+ 0xb7, 0x5a, 0xd0, 0x6a, 0xb8, 0xd2, 0xca, 0x07, 0x74, 0xcc, 0x1e, 0x24,
+ 0xd4, 0xc4, 0xdc, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0xad, 0x6c, 0xaa, 0x94, 0x60, 0x9c, 0xed, 0xe4,
+ 0xff, 0xfa, 0x3e, 0x0a, 0x74, 0x2b, 0x63, 0x03, 0xf7, 0xb6, 0x59, 0xbf,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x68, 0x98, 0x26, 0xaa,
+ 0xd4, 0x33, 0xc9, 0xba, 0x75, 0x70, 0xd4, 0x9f, 0x49, 0xad, 0xd6, 0xc1,
+ 0x54, 0xdc, 0xee, 0xaa, 0x56, 0x1f, 0x78, 0xa7, 0xf0, 0xa1, 0xa4, 0xee,
+ 0x0b, 0xf9, 0x12, 0xaf, 0xdf, 0xa6, 0xb8, 0xee, 0xc3, 0xcb, 0x35, 0x13,
+ 0x6a, 0x59, 0x2a, 0xf8, 0xc9, 0xe9, 0x4c, 0x2f, 0xbc, 0xb1, 0xbc, 0x2b,
+ 0xc2, 0x02, 0x30, 0xe1, 0xc3, 0xbe, 0xc2, 0xf0, 0x81, 0x8c, 0x99, 0x77,
+ 0x89, 0x58, 0x00, 0xa3, 0xcc, 0x7f, 0xa3, 0x02, 0x4c, 0x53, 0xb2, 0x6e,
+ 0x36, 0x4f, 0xfe, 0xdf, 0x87, 0x76, 0xb3, 0x3f, 0xec, 0x5a, 0x62, 0x50,
+ 0xb6, 0x00, 0x45, 0x58, 0xf2, 0x87, 0xac, 0x77, 0xe6, 0xd0, 0x20, 0x50,
+ 0x63, 0xc5, 0xe4, 0xb2, 0x70, 0x15, 0x18, 0x90, 0x05, 0x7b, 0x7b, 0xaf,
+ 0x2b, 0x46, 0xbe, 0x6b, 0x4e, 0x1f, 0x53, 0xfc, 0x84, 0x27, 0xae, 0x83,
+ 0xd2, 0x8d, 0x47, 0x53, 0xa7, 0x0e, 0x1f, 0x63, 0xb5, 0xba, 0xdb, 0x16,
+ 0xd8, 0x6a, 0x09, 0x25, 0x55, 0x7d, 0x8f, 0x3d, 0x4a, 0xc1, 0x83, 0xf9,
+ 0xb3, 0xb9, 0xa7, 0x04, 0x5a, 0xc8, 0xf3, 0x11, 0x04, 0x91, 0x53, 0x30,
+ 0xd9, 0x52, 0x87, 0xcb, 0x39, 0x00, 0x9c, 0xec, 0x53, 0xc3, 0x02, 0x09,
+ 0x7e, 0xa7, 0x36, 0x8e, 0x72, 0x21, 0x2f, 0x23, 0xbb, 0x4c, 0xc6, 0x47,
+ 0xa5, 0xa1, 0xee, 0x67, 0xc4, 0x2f, 0x5c, 0x3a, 0x47, 0x38, 0x61, 0xe2,
+ 0xc3, 0x1e, 0x37, 0x92, 0x9e, 0xc8, 0x2f, 0x6b, 0xfa, 0xef, 0xd2, 0xc3,
+ 0xcd, 0x29, 0x8d, 0x98, 0xf8, 0x52, 0x17, 0xed, 0xb5, 0x53, 0x3c, 0xdf,
+ 0xaf, 0xc9, 0x1b, 0x62, 0xad, 0xdf, 0x02, 0xee, 0x5d, 0x34, 0xf6, 0x41,
+ 0x4b, 0xcb, 0xc3, 0x55, 0xaf, 0xb1, 0xcb, 0xda, 0x9c, 0x73, 0xd5, 0x02,
+ 0xa8, 0x2d, 0xa7, 0xac, 0xfc, 0xe1, 0xe5, 0x07, 0xd0, 0x51, 0xe8, 0x35,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 25:0c:e8:e0:30:61:2e:9f:2b:89:f7:05:4d:7c:f8:fd
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ Signature Algorithm: sha1WithRSAEncryption
+ 13:02:dd:f8:e8:86:00:f2:5a:f8:f8:20:0c:59:88:62:07:ce:
+ ce:f7:4e:f9:bb:59:a1:98:e5:e1:38:dd:4e:bc:66:18:d3:ad:
+ eb:18:f2:0d:c9:6d:3e:4a:94:20:c3:3c:ba:bd:65:54:c6:af:
+ 44:b3:10:ad:2c:6b:3e:ab:d7:07:b6:b8:81:63:c5:f9:5e:2e:
+ e5:2a:67:ce:cd:33:0c:2a:d7:89:56:03:23:1f:b3:be:e8:3a:
+ 08:59:b4:ec:45:35:f7:8a:5b:ff:66:cf:50:af:c6:6d:57:8d:
+ 19:78:b7:b9:a2:d1:57:ea:1f:9a:4b:af:ba:c9:8e:12:7e:c6:
+ bd:ff
+-----BEGIN CERTIFICATE-----
+MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggGbMIIBlzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
+BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
+aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
+KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
+j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
+L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC52ZXJpc2lnbi5jb20wPgYDVR0lBDcwNQYIKwYBBQUHAwEGCCsGAQUFBwMC
+BggrBgEFBQcDAwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUA
+A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K
+lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ
+tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert33[] = {
+ 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x04, 0x39, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x25, 0x0c, 0xe8, 0xe0, 0x30, 0x61, 0x2e, 0x9f, 0x2b,
+ 0x89, 0xf7, 0x05, 0x4d, 0x7c, 0xf8, 0xfd, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x9b, 0x30, 0x82, 0x01, 0x97, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f,
+ 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09,
+ 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30,
+ 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
+ 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80,
+ 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d, 0x25,
+ 0x04, 0x37, 0x30, 0x35, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60,
+ 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x81, 0x81, 0x00, 0x13, 0x02, 0xdd, 0xf8, 0xe8, 0x86, 0x00, 0xf2,
+ 0x5a, 0xf8, 0xf8, 0x20, 0x0c, 0x59, 0x88, 0x62, 0x07, 0xce, 0xce, 0xf7,
+ 0x4e, 0xf9, 0xbb, 0x59, 0xa1, 0x98, 0xe5, 0xe1, 0x38, 0xdd, 0x4e, 0xbc,
+ 0x66, 0x18, 0xd3, 0xad, 0xeb, 0x18, 0xf2, 0x0d, 0xc9, 0x6d, 0x3e, 0x4a,
+ 0x94, 0x20, 0xc3, 0x3c, 0xba, 0xbd, 0x65, 0x54, 0xc6, 0xaf, 0x44, 0xb3,
+ 0x10, 0xad, 0x2c, 0x6b, 0x3e, 0xab, 0xd7, 0x07, 0xb6, 0xb8, 0x81, 0x63,
+ 0xc5, 0xf9, 0x5e, 0x2e, 0xe5, 0x2a, 0x67, 0xce, 0xcd, 0x33, 0x0c, 0x2a,
+ 0xd7, 0x89, 0x56, 0x03, 0x23, 0x1f, 0xb3, 0xbe, 0xe8, 0x3a, 0x08, 0x59,
+ 0xb4, 0xec, 0x45, 0x35, 0xf7, 0x8a, 0x5b, 0xff, 0x66, 0xcf, 0x50, 0xaf,
+ 0xc6, 0x6d, 0x57, 0x8d, 0x19, 0x78, 0xb7, 0xb9, 0xa2, 0xd1, 0x57, 0xea,
+ 0x1f, 0x9a, 0x4b, 0xaf, 0xba, 0xc9, 0x8e, 0x12, 0x7e, 0xc6, 0xbd, 0xff,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 2c:69:e1:2f:6a:67:0b:d9:9d:d2:0f:91:9e:f0:9e:51
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Jun 10 00:00:00 2014 GMT
+ Not After : Jun 9 23:59:59 2024 GMT
+ Subject: C=US, O=thawte, Inc., OU=Domain Validated SSL, CN=thawte DV SSL CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ea:94:07:85:c8:41:2c:f6:83:12:6c:92:5f:ab:
+ 1f:00:d4:96:6f:74:cd:2e:11:e9:6c:0f:39:01:b9:
+ 48:90:40:39:4d:c4:a2:c8:79:6a:a5:9a:bd:91:44:
+ 65:77:54:ad:ff:25:5f:ee:42:fb:b3:02:0f:ea:5d:
+ 7a:dd:1a:54:9e:d7:73:42:9b:cc:79:5f:c5:4d:f4:
+ b7:0b:18:39:20:7a:dd:50:01:5d:34:45:5f:4c:11:
+ 0e:f5:87:26:26:b4:b0:f3:7e:71:a0:31:71:50:89:
+ 68:5a:63:8a:14:62:e5:8c:3a:16:55:0d:3e:eb:aa:
+ 80:1d:71:7a:e3:87:07:ab:bd:a2:74:cd:da:08:01:
+ 9d:1b:cc:27:88:8c:47:d4:69:25:42:d6:bb:50:6d:
+ 85:50:d0:48:82:0d:08:9f:e9:23:e3:42:c6:3c:98:
+ b8:bb:6e:c5:70:13:df:19:1d:01:fd:d2:b5:4e:e6:
+ 62:f4:07:fa:6b:7d:11:77:c4:62:4f:40:4e:a5:78:
+ 97:ab:2c:4d:0c:a7:7c:c3:c4:50:32:9f:d0:70:9b:
+ 0f:ff:ff:75:59:34:85:ad:49:d5:35:ee:4f:5b:d4:
+ d4:36:95:a0:7e:e8:c5:a1:1c:bd:13:4e:7d:ee:63:
+ 6a:96:19:99:c8:a7:2a:00:e6:51:8d:46:eb:30:58:
+ e8:2d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: https://www.thawte.com/cps
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://t.symcd.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://t.symcb.com/ThawtePCA.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-698
+ X509v3 Subject Key Identifier:
+ 9F:B8:C1:A9:6C:F2:F5:C0:22:2A:94:ED:5C:99:AC:D4:EC:D7:C6:07
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 53:54:f2:47:a8:02:d7:ef:aa:35:78:be:4a:08:0d:90:18:4b:
+ 6d:9e:2a:53:2b:e9:54:17:77:74:29:7e:d0:37:07:05:b8:e4:
+ fa:b8:b4:63:98:44:dc:c6:4f:81:06:8c:3a:be:c7:30:57:c6:
+ 70:fc:d6:93:19:9f:c3:55:d7:3e:1f:72:8a:9d:30:5a:35:97:
+ 32:cb:63:e4:c6:72:df:fb:68:ca:69:2f:db:cd:50:38:3e:2b:
+ bb:ab:3b:82:c7:fd:4b:9b:bd:7c:41:98:ef:01:53:d8:35:8f:
+ 25:c9:03:06:e6:9c:57:c1:51:0f:9e:f6:7d:93:4d:f8:76:c8:
+ 3a:6b:f4:c4:8f:33:32:7f:9d:21:84:34:d9:a7:f9:92:fa:41:
+ 91:61:84:05:9d:a3:79:46:ce:67:e7:81:f2:5e:ac:4c:bc:a8:
+ ab:6a:6d:15:e2:9c:4e:5a:d9:63:80:bc:f7:42:eb:9a:44:c6:
+ 8c:6b:06:36:b4:8b:32:89:de:c2:f1:a8:26:aa:a9:ac:ff:ea:
+ 71:a6:e7:8c:41:fa:17:35:bb:b3:87:31:a9:93:c2:c8:58:e1:
+ 0a:4e:95:83:9c:b9:ed:3b:a5:ef:08:e0:74:f9:c3:1b:e6:07:
+ a3:ee:07:d7:42:22:79:21:a0:a1:d4:1d:26:d3:d0:d6:a6:5d:
+ 2b:41:c0:79
+-----BEGIN CERTIFICATE-----
+MIIE0jCCA7qgAwIBAgIQLGnhL2pnC9md0g+RnvCeUTANBgkqhkiG9w0BAQsFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTQwNjEwMDAwMDAwWhcNMjQw
+NjA5MjM1OTU5WjBjMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu
+MR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEeMBwGA1UEAxMVdGhhd3Rl
+IERWIFNTTCBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+6pQHhchBLPaDEmySX6sfANSWb3TNLhHpbA85AblIkEA5TcSiyHlqpZq9kURld1St
+/yVf7kL7swIP6l163RpUntdzQpvMeV/FTfS3Cxg5IHrdUAFdNEVfTBEO9YcmJrSw
+835xoDFxUIloWmOKFGLljDoWVQ0+66qAHXF644cHq72idM3aCAGdG8wniIxH1Gkl
+Qta7UG2FUNBIgg0In+kj40LGPJi4u27FcBPfGR0B/dK1TuZi9Af6a30Rd8RiT0BO
+pXiXqyxNDKd8w8RQMp/QcJsP//91WTSFrUnVNe5PW9TUNpWgfujFoRy9E0597mNq
+lhmZyKcqAOZRjUbrMFjoLQIDAQABo4IBOTCCATUwEgYDVR0TAQH/BAgwBgEB/wIB
+ADBBBgNVHSAEOjA4MDYGCmCGSAGG+EUBBzYwKDAmBggrBgEFBQcCARYaaHR0cHM6
+Ly93d3cudGhhd3RlLmNvbS9jcHMwDgYDVR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEB
+BCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL3Quc3ltY2QuY29tMDEGA1UdHwQqMCgw
+JqAkoCKGIGh0dHA6Ly90LnN5bWNiLmNvbS9UaGF3dGVQQ0EuY3JsMCkGA1UdEQQi
+MCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTY5ODAdBgNVHQ4EFgQUn7jB
+qWzy9cAiKpTtXJms1OzXxgcwHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutX
+SFAwDQYJKoZIhvcNAQELBQADggEBAFNU8keoAtfvqjV4vkoIDZAYS22eKlMr6VQX
+d3QpftA3BwW45Pq4tGOYRNzGT4EGjDq+xzBXxnD81pMZn8NV1z4fcoqdMFo1lzLL
+Y+TGct/7aMppL9vNUDg+K7urO4LH/UubvXxBmO8BU9g1jyXJAwbmnFfBUQ+e9n2T
+Tfh2yDpr9MSPMzJ/nSGENNmn+ZL6QZFhhAWdo3lGzmfngfJerEy8qKtqbRXinE5a
+2WOAvPdC65pExoxrBja0izKJ3sLxqCaqqaz/6nGm54xB+hc1u7OHMamTwshY4QpO
+lYOcue07pe8I4HT5wxvmB6PuB9dCInkhoKHUHSbT0NamXStBwHk=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert34[] = {
+ 0x30, 0x82, 0x04, 0xd2, 0x30, 0x82, 0x03, 0xba, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x2c, 0x69, 0xe1, 0x2f, 0x6a, 0x67, 0x0b, 0xd9, 0x9d,
+ 0xd2, 0x0f, 0x91, 0x9e, 0xf0, 0x9e, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x36, 0x31, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30,
+ 0x36, 0x30, 0x39, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x63,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x14, 0x44,
+ 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61,
+ 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x20, 0x44, 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d,
+ 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xea, 0x94, 0x07, 0x85, 0xc8, 0x41, 0x2c, 0xf6, 0x83, 0x12, 0x6c, 0x92,
+ 0x5f, 0xab, 0x1f, 0x00, 0xd4, 0x96, 0x6f, 0x74, 0xcd, 0x2e, 0x11, 0xe9,
+ 0x6c, 0x0f, 0x39, 0x01, 0xb9, 0x48, 0x90, 0x40, 0x39, 0x4d, 0xc4, 0xa2,
+ 0xc8, 0x79, 0x6a, 0xa5, 0x9a, 0xbd, 0x91, 0x44, 0x65, 0x77, 0x54, 0xad,
+ 0xff, 0x25, 0x5f, 0xee, 0x42, 0xfb, 0xb3, 0x02, 0x0f, 0xea, 0x5d, 0x7a,
+ 0xdd, 0x1a, 0x54, 0x9e, 0xd7, 0x73, 0x42, 0x9b, 0xcc, 0x79, 0x5f, 0xc5,
+ 0x4d, 0xf4, 0xb7, 0x0b, 0x18, 0x39, 0x20, 0x7a, 0xdd, 0x50, 0x01, 0x5d,
+ 0x34, 0x45, 0x5f, 0x4c, 0x11, 0x0e, 0xf5, 0x87, 0x26, 0x26, 0xb4, 0xb0,
+ 0xf3, 0x7e, 0x71, 0xa0, 0x31, 0x71, 0x50, 0x89, 0x68, 0x5a, 0x63, 0x8a,
+ 0x14, 0x62, 0xe5, 0x8c, 0x3a, 0x16, 0x55, 0x0d, 0x3e, 0xeb, 0xaa, 0x80,
+ 0x1d, 0x71, 0x7a, 0xe3, 0x87, 0x07, 0xab, 0xbd, 0xa2, 0x74, 0xcd, 0xda,
+ 0x08, 0x01, 0x9d, 0x1b, 0xcc, 0x27, 0x88, 0x8c, 0x47, 0xd4, 0x69, 0x25,
+ 0x42, 0xd6, 0xbb, 0x50, 0x6d, 0x85, 0x50, 0xd0, 0x48, 0x82, 0x0d, 0x08,
+ 0x9f, 0xe9, 0x23, 0xe3, 0x42, 0xc6, 0x3c, 0x98, 0xb8, 0xbb, 0x6e, 0xc5,
+ 0x70, 0x13, 0xdf, 0x19, 0x1d, 0x01, 0xfd, 0xd2, 0xb5, 0x4e, 0xe6, 0x62,
+ 0xf4, 0x07, 0xfa, 0x6b, 0x7d, 0x11, 0x77, 0xc4, 0x62, 0x4f, 0x40, 0x4e,
+ 0xa5, 0x78, 0x97, 0xab, 0x2c, 0x4d, 0x0c, 0xa7, 0x7c, 0xc3, 0xc4, 0x50,
+ 0x32, 0x9f, 0xd0, 0x70, 0x9b, 0x0f, 0xff, 0xff, 0x75, 0x59, 0x34, 0x85,
+ 0xad, 0x49, 0xd5, 0x35, 0xee, 0x4f, 0x5b, 0xd4, 0xd4, 0x36, 0x95, 0xa0,
+ 0x7e, 0xe8, 0xc5, 0xa1, 0x1c, 0xbd, 0x13, 0x4e, 0x7d, 0xee, 0x63, 0x6a,
+ 0x96, 0x19, 0x99, 0xc8, 0xa7, 0x2a, 0x00, 0xe6, 0x51, 0x8d, 0x46, 0xeb,
+ 0x30, 0x58, 0xe8, 0x2d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0x39, 0x30, 0x82, 0x01, 0x35, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, 0x38,
+ 0x30, 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01,
+ 0x07, 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x74, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, 0x30, 0x28, 0x30,
+ 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x74, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22,
+ 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65,
+ 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x36, 0x39, 0x38, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9f, 0xb8, 0xc1,
+ 0xa9, 0x6c, 0xf2, 0xf5, 0xc0, 0x22, 0x2a, 0x94, 0xed, 0x5c, 0x99, 0xac,
+ 0xd4, 0xec, 0xd7, 0xc6, 0x07, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce,
+ 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57,
+ 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x53, 0x54,
+ 0xf2, 0x47, 0xa8, 0x02, 0xd7, 0xef, 0xaa, 0x35, 0x78, 0xbe, 0x4a, 0x08,
+ 0x0d, 0x90, 0x18, 0x4b, 0x6d, 0x9e, 0x2a, 0x53, 0x2b, 0xe9, 0x54, 0x17,
+ 0x77, 0x74, 0x29, 0x7e, 0xd0, 0x37, 0x07, 0x05, 0xb8, 0xe4, 0xfa, 0xb8,
+ 0xb4, 0x63, 0x98, 0x44, 0xdc, 0xc6, 0x4f, 0x81, 0x06, 0x8c, 0x3a, 0xbe,
+ 0xc7, 0x30, 0x57, 0xc6, 0x70, 0xfc, 0xd6, 0x93, 0x19, 0x9f, 0xc3, 0x55,
+ 0xd7, 0x3e, 0x1f, 0x72, 0x8a, 0x9d, 0x30, 0x5a, 0x35, 0x97, 0x32, 0xcb,
+ 0x63, 0xe4, 0xc6, 0x72, 0xdf, 0xfb, 0x68, 0xca, 0x69, 0x2f, 0xdb, 0xcd,
+ 0x50, 0x38, 0x3e, 0x2b, 0xbb, 0xab, 0x3b, 0x82, 0xc7, 0xfd, 0x4b, 0x9b,
+ 0xbd, 0x7c, 0x41, 0x98, 0xef, 0x01, 0x53, 0xd8, 0x35, 0x8f, 0x25, 0xc9,
+ 0x03, 0x06, 0xe6, 0x9c, 0x57, 0xc1, 0x51, 0x0f, 0x9e, 0xf6, 0x7d, 0x93,
+ 0x4d, 0xf8, 0x76, 0xc8, 0x3a, 0x6b, 0xf4, 0xc4, 0x8f, 0x33, 0x32, 0x7f,
+ 0x9d, 0x21, 0x84, 0x34, 0xd9, 0xa7, 0xf9, 0x92, 0xfa, 0x41, 0x91, 0x61,
+ 0x84, 0x05, 0x9d, 0xa3, 0x79, 0x46, 0xce, 0x67, 0xe7, 0x81, 0xf2, 0x5e,
+ 0xac, 0x4c, 0xbc, 0xa8, 0xab, 0x6a, 0x6d, 0x15, 0xe2, 0x9c, 0x4e, 0x5a,
+ 0xd9, 0x63, 0x80, 0xbc, 0xf7, 0x42, 0xeb, 0x9a, 0x44, 0xc6, 0x8c, 0x6b,
+ 0x06, 0x36, 0xb4, 0x8b, 0x32, 0x89, 0xde, 0xc2, 0xf1, 0xa8, 0x26, 0xaa,
+ 0xa9, 0xac, 0xff, 0xea, 0x71, 0xa6, 0xe7, 0x8c, 0x41, 0xfa, 0x17, 0x35,
+ 0xbb, 0xb3, 0x87, 0x31, 0xa9, 0x93, 0xc2, 0xc8, 0x58, 0xe1, 0x0a, 0x4e,
+ 0x95, 0x83, 0x9c, 0xb9, 0xed, 0x3b, 0xa5, 0xef, 0x08, 0xe0, 0x74, 0xf9,
+ 0xc3, 0x1b, 0xe6, 0x07, 0xa3, 0xee, 0x07, 0xd7, 0x42, 0x22, 0x79, 0x21,
+ 0xa0, 0xa1, 0xd4, 0x1d, 0x26, 0xd3, 0xd0, 0xd6, 0xa6, 0x5d, 0x2b, 0x41,
+ 0xc0, 0x79,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4f:e3:e2:65:21:07:ab:20:37:41:6e:48:70:ce:d2:c2
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: May 25 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, O=Trusted Secure Certificate Authority, CN=Trusted Secure Certificate Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:80:0b:42:c6:06:6c:cf:22:b3:1a:9e:11:2e:42:
+ 6e:39:bf:e8:12:af:3c:42:21:12:95:40:5d:32:b1:
+ 6d:1c:21:d1:34:e5:4f:a8:d1:43:a2:26:4e:30:7d:
+ 73:44:2c:73:aa:c5:4d:66:01:19:d2:ea:50:59:65:
+ d0:68:9d:05:a0:7c:a1:79:53:d0:21:90:59:0e:37:
+ db:1e:dc:92:a7:8b:0d:c4:f5:f8:e6:ff:b5:35:1a:
+ da:a8:b6:9b:20:85:65:c4:a2:4d:df:f3:94:4d:63:
+ 7e:ee:89:07:af:fe:e1:ba:00:15:2d:c6:77:8e:a3:
+ fe:ad:cf:26:54:5a:df:fc:d2:de:c2:ad:f6:b2:23:
+ fd:a8:83:e5:65:bd:27:f7:27:1a:18:59:6a:9e:14:
+ f6:b4:86:ff:1c:58:14:43:73:96:24:bf:10:43:d5:
+ 5c:89:f0:ce:f7:e1:96:16:5e:18:4a:27:28:90:80:
+ 18:fc:32:fe:f4:c7:b8:d6:82:3d:35:af:bb:4a:1c:
+ 5b:05:78:f6:fd:55:3e:82:74:b2:73:b8:89:4e:f7:
+ 1b:85:9a:d8:ca:b1:5a:b1:00:20:41:14:30:2b:14:
+ 24:ed:37:0e:32:3e:23:88:39:7e:b9:d9:38:03:e2:
+ 4c:d9:0d:43:41:33:10:eb:30:72:53:88:f7:52:9b:
+ 4f:81
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ CC:03:5B:96:5A:9E:16:CC:26:1E:BD:A3:70:FB:E3:CB:79:19:FC:4D
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6449.1.2.2.8
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 7b:f0:fc:a1:28:47:bc:2b:b4:04:73:3f:4b:dd:1e:d1:b9:cd:
+ 1c:ed:7d:e5:e8:cb:51:f4:92:bf:dd:9c:0d:5c:6e:1d:95:ed:
+ 5b:70:50:89:d4:67:9a:15:54:d1:90:0a:fa:09:68:06:18:bb:
+ d7:27:e4:93:ff:43:48:81:3b:c8:59:49:35:ea:ac:b6:ae:46:
+ b5:d4:f3:b8:c3:c6:e4:91:bf:c9:34:fd:7e:d0:59:6e:61:a1:
+ 1f:48:63:54:b2:7d:46:bf:c8:fa:c3:bf:48:58:98:f6:69:84:
+ a7:16:69:08:27:a4:22:cb:a2:2c:c8:df:6e:a9:ee:f8:41:df:
+ 1b:a8:b7:f3:e3:ae:ce:a3:fe:d9:27:60:50:3f:04:7d:7a:44:
+ ea:76:42:5c:d3:55:46:ef:27:c5:6a:4a:80:e7:35:a0:91:c6:
+ 1b:a6:86:9c:5a:3b:04:83:54:34:d7:d1:88:a6:36:e9:7f:40:
+ 27:da:56:0a:50:21:9d:29:8b:a0:84:ec:fe:71:23:53:04:18:
+ 19:70:67:86:44:95:72:40:55:f6:dd:a3:b4:3d:2d:09:60:a5:
+ e7:5f:fc:ac:3b:ec:0c:91:9f:f8:ee:6a:ba:b2:3c:fd:95:7d:
+ 9a:07:f4:b0:65:43:a2:f6:df:7d:b8:21:49:84:04:ee:bd:ce:
+ 53:8f:0f:29
+-----BEGIN CERTIFICATE-----
+MIIE5DCCA8ygAwIBAgIQT+PiZSEHqyA3QW5IcM7SwjANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEwMDUyNTAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+azELMAkGA1UEBhMCVVMxLTArBgNVBAoTJFRydXN0ZWQgU2VjdXJlIENlcnRpZmlj
+YXRlIEF1dGhvcml0eTEtMCsGA1UEAxMkVHJ1c3RlZCBTZWN1cmUgQ2VydGlmaWNh
+dGUgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgAtC
+xgZszyKzGp4RLkJuOb/oEq88QiESlUBdMrFtHCHRNOVPqNFDoiZOMH1zRCxzqsVN
+ZgEZ0upQWWXQaJ0FoHyheVPQIZBZDjfbHtySp4sNxPX45v+1NRraqLabIIVlxKJN
+3/OUTWN+7okHr/7hugAVLcZ3jqP+rc8mVFrf/NLewq32siP9qIPlZb0n9ycaGFlq
+nhT2tIb/HFgUQ3OWJL8QQ9VcifDO9+GWFl4YSicokIAY/DL+9Me41oI9Na+7Shxb
+BXj2/VU+gnSyc7iJTvcbhZrYyrFasQAgQRQwKxQk7TcOMj4jiDl+udk4A+JM2Q1D
+QTMQ6zByU4j3UptPgQIDAQABo4IBfjCCAXowHwYDVR0jBBgwFoAUrb2YejS0Jvf6
+xCZU7wO94CTLVBowHQYDVR0OBBYEFMwDW5ZanhbMJh69o3D748t5GfxNMA4GA1Ud
+DwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMBgGA1UdIAQRMA8wDQYLKwYB
+BAGyMQECAggwRAYDVR0fBD0wOzA5oDegNYYzaHR0cDovL2NybC51c2VydHJ1c3Qu
+Y29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMIGzBggrBgEFBQcBAQSBpjCB
+ozA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0
+RXh0ZXJuYWxDQVJvb3QucDdjMDkGCCsGAQUFBzAChi1odHRwOi8vY3J0LnVzZXJ0
+cnVzdC5jb20vQWRkVHJ1c3RVVE5TR0NDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6
+Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEFBQADggEBAHvw/KEoR7wr
+tARzP0vdHtG5zRztfeXoy1H0kr/dnA1cbh2V7VtwUInUZ5oVVNGQCvoJaAYYu9cn
+5JP/Q0iBO8hZSTXqrLauRrXU87jDxuSRv8k0/X7QWW5hoR9IY1SyfUa/yPrDv0hY
+mPZphKcWaQgnpCLLoizI326p7vhB3xuot/Pjrs6j/tknYFA/BH16ROp2QlzTVUbv
+J8VqSoDnNaCRxhumhpxaOwSDVDTX0YimNul/QCfaVgpQIZ0pi6CE7P5xI1MEGBlw
+Z4ZElXJAVfbdo7Q9LQlgpedf/Kw77AyRn/juarqyPP2VfZoH9LBlQ6L23324IUmE
+BO69zlOPDyk=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert35[] = {
+ 0x30, 0x82, 0x04, 0xe4, 0x30, 0x82, 0x03, 0xcc, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x4f, 0xe3, 0xe2, 0x65, 0x21, 0x07, 0xab, 0x20, 0x37,
+ 0x41, 0x6e, 0x48, 0x70, 0xce, 0xd2, 0xc2, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x35, 0x32,
+ 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x6b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x24, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x53, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x80, 0x0b, 0x42,
+ 0xc6, 0x06, 0x6c, 0xcf, 0x22, 0xb3, 0x1a, 0x9e, 0x11, 0x2e, 0x42, 0x6e,
+ 0x39, 0xbf, 0xe8, 0x12, 0xaf, 0x3c, 0x42, 0x21, 0x12, 0x95, 0x40, 0x5d,
+ 0x32, 0xb1, 0x6d, 0x1c, 0x21, 0xd1, 0x34, 0xe5, 0x4f, 0xa8, 0xd1, 0x43,
+ 0xa2, 0x26, 0x4e, 0x30, 0x7d, 0x73, 0x44, 0x2c, 0x73, 0xaa, 0xc5, 0x4d,
+ 0x66, 0x01, 0x19, 0xd2, 0xea, 0x50, 0x59, 0x65, 0xd0, 0x68, 0x9d, 0x05,
+ 0xa0, 0x7c, 0xa1, 0x79, 0x53, 0xd0, 0x21, 0x90, 0x59, 0x0e, 0x37, 0xdb,
+ 0x1e, 0xdc, 0x92, 0xa7, 0x8b, 0x0d, 0xc4, 0xf5, 0xf8, 0xe6, 0xff, 0xb5,
+ 0x35, 0x1a, 0xda, 0xa8, 0xb6, 0x9b, 0x20, 0x85, 0x65, 0xc4, 0xa2, 0x4d,
+ 0xdf, 0xf3, 0x94, 0x4d, 0x63, 0x7e, 0xee, 0x89, 0x07, 0xaf, 0xfe, 0xe1,
+ 0xba, 0x00, 0x15, 0x2d, 0xc6, 0x77, 0x8e, 0xa3, 0xfe, 0xad, 0xcf, 0x26,
+ 0x54, 0x5a, 0xdf, 0xfc, 0xd2, 0xde, 0xc2, 0xad, 0xf6, 0xb2, 0x23, 0xfd,
+ 0xa8, 0x83, 0xe5, 0x65, 0xbd, 0x27, 0xf7, 0x27, 0x1a, 0x18, 0x59, 0x6a,
+ 0x9e, 0x14, 0xf6, 0xb4, 0x86, 0xff, 0x1c, 0x58, 0x14, 0x43, 0x73, 0x96,
+ 0x24, 0xbf, 0x10, 0x43, 0xd5, 0x5c, 0x89, 0xf0, 0xce, 0xf7, 0xe1, 0x96,
+ 0x16, 0x5e, 0x18, 0x4a, 0x27, 0x28, 0x90, 0x80, 0x18, 0xfc, 0x32, 0xfe,
+ 0xf4, 0xc7, 0xb8, 0xd6, 0x82, 0x3d, 0x35, 0xaf, 0xbb, 0x4a, 0x1c, 0x5b,
+ 0x05, 0x78, 0xf6, 0xfd, 0x55, 0x3e, 0x82, 0x74, 0xb2, 0x73, 0xb8, 0x89,
+ 0x4e, 0xf7, 0x1b, 0x85, 0x9a, 0xd8, 0xca, 0xb1, 0x5a, 0xb1, 0x00, 0x20,
+ 0x41, 0x14, 0x30, 0x2b, 0x14, 0x24, 0xed, 0x37, 0x0e, 0x32, 0x3e, 0x23,
+ 0x88, 0x39, 0x7e, 0xb9, 0xd9, 0x38, 0x03, 0xe2, 0x4c, 0xd9, 0x0d, 0x43,
+ 0x41, 0x33, 0x10, 0xeb, 0x30, 0x72, 0x53, 0x88, 0xf7, 0x52, 0x9b, 0x4f,
+ 0x81, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x7e, 0x30, 0x82,
+ 0x01, 0x7a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa,
+ 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xcc, 0x03,
+ 0x5b, 0x96, 0x5a, 0x9e, 0x16, 0xcc, 0x26, 0x1e, 0xbd, 0xa3, 0x70, 0xfb,
+ 0xe3, 0xcb, 0x79, 0x19, 0xfc, 0x4d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
+ 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x18, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x11, 0x30, 0x0f, 0x30, 0x0d, 0x06, 0x0b, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, 0x02, 0x08, 0x30, 0x44, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0,
+ 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81,
+ 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x7b, 0xf0, 0xfc, 0xa1, 0x28, 0x47, 0xbc, 0x2b,
+ 0xb4, 0x04, 0x73, 0x3f, 0x4b, 0xdd, 0x1e, 0xd1, 0xb9, 0xcd, 0x1c, 0xed,
+ 0x7d, 0xe5, 0xe8, 0xcb, 0x51, 0xf4, 0x92, 0xbf, 0xdd, 0x9c, 0x0d, 0x5c,
+ 0x6e, 0x1d, 0x95, 0xed, 0x5b, 0x70, 0x50, 0x89, 0xd4, 0x67, 0x9a, 0x15,
+ 0x54, 0xd1, 0x90, 0x0a, 0xfa, 0x09, 0x68, 0x06, 0x18, 0xbb, 0xd7, 0x27,
+ 0xe4, 0x93, 0xff, 0x43, 0x48, 0x81, 0x3b, 0xc8, 0x59, 0x49, 0x35, 0xea,
+ 0xac, 0xb6, 0xae, 0x46, 0xb5, 0xd4, 0xf3, 0xb8, 0xc3, 0xc6, 0xe4, 0x91,
+ 0xbf, 0xc9, 0x34, 0xfd, 0x7e, 0xd0, 0x59, 0x6e, 0x61, 0xa1, 0x1f, 0x48,
+ 0x63, 0x54, 0xb2, 0x7d, 0x46, 0xbf, 0xc8, 0xfa, 0xc3, 0xbf, 0x48, 0x58,
+ 0x98, 0xf6, 0x69, 0x84, 0xa7, 0x16, 0x69, 0x08, 0x27, 0xa4, 0x22, 0xcb,
+ 0xa2, 0x2c, 0xc8, 0xdf, 0x6e, 0xa9, 0xee, 0xf8, 0x41, 0xdf, 0x1b, 0xa8,
+ 0xb7, 0xf3, 0xe3, 0xae, 0xce, 0xa3, 0xfe, 0xd9, 0x27, 0x60, 0x50, 0x3f,
+ 0x04, 0x7d, 0x7a, 0x44, 0xea, 0x76, 0x42, 0x5c, 0xd3, 0x55, 0x46, 0xef,
+ 0x27, 0xc5, 0x6a, 0x4a, 0x80, 0xe7, 0x35, 0xa0, 0x91, 0xc6, 0x1b, 0xa6,
+ 0x86, 0x9c, 0x5a, 0x3b, 0x04, 0x83, 0x54, 0x34, 0xd7, 0xd1, 0x88, 0xa6,
+ 0x36, 0xe9, 0x7f, 0x40, 0x27, 0xda, 0x56, 0x0a, 0x50, 0x21, 0x9d, 0x29,
+ 0x8b, 0xa0, 0x84, 0xec, 0xfe, 0x71, 0x23, 0x53, 0x04, 0x18, 0x19, 0x70,
+ 0x67, 0x86, 0x44, 0x95, 0x72, 0x40, 0x55, 0xf6, 0xdd, 0xa3, 0xb4, 0x3d,
+ 0x2d, 0x09, 0x60, 0xa5, 0xe7, 0x5f, 0xfc, 0xac, 0x3b, 0xec, 0x0c, 0x91,
+ 0x9f, 0xf8, 0xee, 0x6a, 0xba, 0xb2, 0x3c, 0xfd, 0x95, 0x7d, 0x9a, 0x07,
+ 0xf4, 0xb0, 0x65, 0x43, 0xa2, 0xf6, 0xdf, 0x7d, 0xb8, 0x21, 0x49, 0x84,
+ 0x04, 0xee, 0xbd, 0xce, 0x53, 0x8f, 0x0f, 0x29,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 946072060 (0x3863e9fc)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048)
+ Validity
+ Not Before: Dec 10 20:43:54 2009 GMT
+ Not After : Dec 10 21:13:54 2019 GMT
+ Subject: C=US, O=Entrust, Inc., OU=www.entrust.net/rpa is incorporated by reference, OU=(c) 2009 Entrust, Inc., CN=Entrust Certification Authority - L1C
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:97:a3:2d:3c:9e:de:05:da:13:c2:11:8d:9d:8e:
+ e3:7f:c7:4b:7e:5a:9f:b3:ff:62:ab:73:c8:28:6b:
+ ba:10:64:82:87:13:cd:57:18:ff:28:ce:c0:e6:0e:
+ 06:91:50:29:83:d1:f2:c3:2a:db:d8:db:4e:04:cc:
+ 00:eb:8b:b6:96:dc:bc:aa:fa:52:77:04:c1:db:19:
+ e4:ae:9c:fd:3c:8b:03:ef:4d:bc:1a:03:65:f9:c1:
+ b1:3f:72:86:f2:38:aa:19:ae:10:88:78:28:da:75:
+ c3:3d:02:82:02:9c:b9:c1:65:77:76:24:4c:98:f7:
+ 6d:31:38:fb:db:fe:db:37:02:76:a1:18:97:a6:cc:
+ de:20:09:49:36:24:69:42:f6:e4:37:62:f1:59:6d:
+ a9:3c:ed:34:9c:a3:8e:db:dc:3a:d7:f7:0a:6f:ef:
+ 2e:d8:d5:93:5a:7a:ed:08:49:68:e2:41:e3:5a:90:
+ c1:86:55:fc:51:43:9d:e0:b2:c4:67:b4:cb:32:31:
+ 25:f0:54:9f:4b:d1:6f:db:d4:dd:fc:af:5e:6c:78:
+ 90:95:de:ca:3a:48:b9:79:3c:9b:19:d6:75:05:a0:
+ f9:88:d7:c1:e8:a5:09:e4:1a:15:dc:87:23:aa:b2:
+ 75:8c:63:25:87:d8:f8:3d:a6:c2:cc:66:ff:a5:66:
+ 68:55
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/2048ca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/rpa
+
+ X509v3 Subject Key Identifier:
+ 1E:F1:AB:89:06:F8:49:0F:01:33:77:EE:14:7A:EE:19:7C:93:28:4D
+ X509v3 Authority Key Identifier:
+ keyid:55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 07:f6:5f:82:84:7f:80:40:c7:90:34:46:42:24:03:ce:2f:ab:
+ ba:83:9e:25:73:0d:ed:ac:05:69:c6:87:ed:a3:5c:f2:57:c1:
+ b1:49:76:9a:4d:f2:3f:dd:e4:0e:fe:0b:3e:b9:98:d9:32:95:
+ 1d:32:f4:01:ee:9c:c8:c8:e5:3f:e0:53:76:62:fc:dd:ab:6d:
+ 3d:94:90:f2:c0:b3:3c:98:27:36:5e:28:97:22:fc:1b:40:d3:
+ 2b:0d:ad:b5:57:6d:df:0f:e3:4b:ef:73:02:10:65:fa:1b:d0:
+ ac:31:d5:e3:0f:e8:ba:32:30:83:ee:4a:d0:bf:df:22:90:7a:
+ be:ec:3a:1b:c4:49:04:1d:f1:ae:80:77:3c:42:08:db:a7:3b:
+ 28:a6:80:01:03:e6:39:a3:eb:df:80:59:1b:f3:2c:be:dc:72:
+ 44:79:a0:6c:07:a5:6d:4d:44:8e:42:68:ca:94:7c:2e:36:ba:
+ 85:9e:cd:aa:c4:5e:3c:54:be:fe:2f:ea:69:9d:1c:1e:29:9b:
+ 96:d8:c8:fe:51:90:f1:24:a6:90:06:b3:f0:29:a2:ff:78:2e:
+ 77:5c:45:21:d9:44:00:31:f3:be:32:4f:f5:0a:32:0d:fc:fc:
+ ba:16:76:56:b2:d6:48:92:f2:8b:a6:3e:b7:ac:5c:69:ea:0b:
+ 3f:66:45:b9
+-----BEGIN CERTIFICATE-----
+MIIE8jCCA9qgAwIBAgIEOGPp/DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
+RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
+IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw0wOTEyMTAyMDQzNTRaFw0xOTEy
+MTAyMTEzNTRaMIGxMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5j
+LjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L3JwYSBpcyBpbmNvcnBvcmF0ZWQg
+YnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwOSBFbnRydXN0LCBJbmMuMS4w
+LAYDVQQDEyVFbnRydXN0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gTDFDMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl6MtPJ7eBdoTwhGNnY7jf8dL
+flqfs/9iq3PIKGu6EGSChxPNVxj/KM7A5g4GkVApg9Hywyrb2NtOBMwA64u2lty8
+qvpSdwTB2xnkrpz9PIsD7028GgNl+cGxP3KG8jiqGa4QiHgo2nXDPQKCApy5wWV3
+diRMmPdtMTj72/7bNwJ2oRiXpszeIAlJNiRpQvbkN2LxWW2pPO00nKOO29w61/cK
+b+8u2NWTWnrtCElo4kHjWpDBhlX8UUOd4LLEZ7TLMjEl8FSfS9Fv29Td/K9ebHiQ
+ld7KOki5eTybGdZ1BaD5iNfB6KUJ5BoV3IcjqrJ1jGMlh9j4PabCzGb/pWZoVQID
+AQABo4IBCzCCAQcwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wMwYI
+KwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5l
+dDAyBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLmVudHJ1c3QubmV0LzIwNDhj
+YS5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly93
+d3cuZW50cnVzdC5uZXQvcnBhMB0GA1UdDgQWBBQe8auJBvhJDwEzd+4Ueu4ZfJMo
+TTAfBgNVHSMEGDAWgBRV5IHREYC+2Im5CKMx+aEkCRa5cDANBgkqhkiG9w0BAQUF
+AAOCAQEAB/ZfgoR/gEDHkDRGQiQDzi+ruoOeJXMN7awFacaH7aNc8lfBsUl2mk3y
+P93kDv4LPrmY2TKVHTL0Ae6cyMjlP+BTdmL83attPZSQ8sCzPJgnNl4olyL8G0DT
+Kw2ttVdt3w/jS+9zAhBl+hvQrDHV4w/oujIwg+5K0L/fIpB6vuw6G8RJBB3xroB3
+PEII26c7KKaAAQPmOaPr34BZG/MsvtxyRHmgbAelbU1EjkJoypR8Lja6hZ7NqsRe
+PFS+/i/qaZ0cHimbltjI/lGQ8SSmkAaz8Cmi/3gud1xFIdlEADHzvjJP9QoyDfz8
+uhZ2VrLWSJLyi6Y+t6xcaeoLP2ZFuQ==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert36[] = {
+ 0x30, 0x82, 0x04, 0xf2, 0x30, 0x82, 0x03, 0xda, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x38, 0x63, 0xe9, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x31,
+ 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, 0x77, 0x77,
+ 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x2f, 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x20, 0x69,
+ 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65,
+ 0x66, 0x2e, 0x20, 0x28, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c,
+ 0x69, 0x61, 0x62, 0x2e, 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39,
+ 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x33, 0x30, 0x31,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, 0x34, 0x38,
+ 0x29, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x31, 0x32, 0x31, 0x30, 0x32,
+ 0x30, 0x34, 0x33, 0x35, 0x34, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32,
+ 0x31, 0x30, 0x32, 0x31, 0x31, 0x33, 0x35, 0x34, 0x5a, 0x30, 0x81, 0xb1,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30,
+ 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x20, 0x69, 0x73, 0x20, 0x69,
+ 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20,
+ 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2e, 0x30,
+ 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x4c, 0x31, 0x43, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde,
+ 0x05, 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3, 0x7f, 0xc7, 0x4b,
+ 0x7e, 0x5a, 0x9f, 0xb3, 0xff, 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba,
+ 0x10, 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff, 0x28, 0xce, 0xc0,
+ 0xe6, 0x0e, 0x06, 0x91, 0x50, 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb,
+ 0xd8, 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6, 0x96, 0xdc, 0xbc,
+ 0xaa, 0xfa, 0x52, 0x77, 0x04, 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd,
+ 0x3c, 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65, 0xf9, 0xc1, 0xb1,
+ 0x3f, 0x72, 0x86, 0xf2, 0x38, 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28,
+ 0xda, 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9, 0xc1, 0x65, 0x77,
+ 0x76, 0x24, 0x4c, 0x98, 0xf7, 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb,
+ 0x37, 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde, 0x20, 0x09, 0x49,
+ 0x36, 0x24, 0x69, 0x42, 0xf6, 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9,
+ 0x3c, 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a, 0xd7, 0xf7, 0x0a,
+ 0x6f, 0xef, 0x2e, 0xd8, 0xd5, 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68,
+ 0xe2, 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc, 0x51, 0x43, 0x9d,
+ 0xe0, 0xb2, 0xc4, 0x67, 0xb4, 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f,
+ 0x4b, 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e, 0x6c, 0x78, 0x90,
+ 0x95, 0xde, 0xca, 0x3a, 0x48, 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75,
+ 0x05, 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09, 0xe4, 0x1a, 0x15,
+ 0xdc, 0x87, 0x23, 0xaa, 0xb2, 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8,
+ 0x3d, 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68, 0x55, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x0b, 0x30, 0x82, 0x01, 0x07, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x33, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25,
+ 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29,
+ 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x32, 0x30, 0x34, 0x38, 0x63,
+ 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x1e, 0xf1, 0xab, 0x89, 0x06, 0xf8, 0x49,
+ 0x0f, 0x01, 0x33, 0x77, 0xee, 0x14, 0x7a, 0xee, 0x19, 0x7c, 0x93, 0x28,
+ 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x55, 0xe4, 0x81, 0xd1, 0x11, 0x80, 0xbe, 0xd8, 0x89, 0xb9,
+ 0x08, 0xa3, 0x31, 0xf9, 0xa1, 0x24, 0x09, 0x16, 0xb9, 0x70, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x07, 0xf6, 0x5f, 0x82, 0x84, 0x7f,
+ 0x80, 0x40, 0xc7, 0x90, 0x34, 0x46, 0x42, 0x24, 0x03, 0xce, 0x2f, 0xab,
+ 0xba, 0x83, 0x9e, 0x25, 0x73, 0x0d, 0xed, 0xac, 0x05, 0x69, 0xc6, 0x87,
+ 0xed, 0xa3, 0x5c, 0xf2, 0x57, 0xc1, 0xb1, 0x49, 0x76, 0x9a, 0x4d, 0xf2,
+ 0x3f, 0xdd, 0xe4, 0x0e, 0xfe, 0x0b, 0x3e, 0xb9, 0x98, 0xd9, 0x32, 0x95,
+ 0x1d, 0x32, 0xf4, 0x01, 0xee, 0x9c, 0xc8, 0xc8, 0xe5, 0x3f, 0xe0, 0x53,
+ 0x76, 0x62, 0xfc, 0xdd, 0xab, 0x6d, 0x3d, 0x94, 0x90, 0xf2, 0xc0, 0xb3,
+ 0x3c, 0x98, 0x27, 0x36, 0x5e, 0x28, 0x97, 0x22, 0xfc, 0x1b, 0x40, 0xd3,
+ 0x2b, 0x0d, 0xad, 0xb5, 0x57, 0x6d, 0xdf, 0x0f, 0xe3, 0x4b, 0xef, 0x73,
+ 0x02, 0x10, 0x65, 0xfa, 0x1b, 0xd0, 0xac, 0x31, 0xd5, 0xe3, 0x0f, 0xe8,
+ 0xba, 0x32, 0x30, 0x83, 0xee, 0x4a, 0xd0, 0xbf, 0xdf, 0x22, 0x90, 0x7a,
+ 0xbe, 0xec, 0x3a, 0x1b, 0xc4, 0x49, 0x04, 0x1d, 0xf1, 0xae, 0x80, 0x77,
+ 0x3c, 0x42, 0x08, 0xdb, 0xa7, 0x3b, 0x28, 0xa6, 0x80, 0x01, 0x03, 0xe6,
+ 0x39, 0xa3, 0xeb, 0xdf, 0x80, 0x59, 0x1b, 0xf3, 0x2c, 0xbe, 0xdc, 0x72,
+ 0x44, 0x79, 0xa0, 0x6c, 0x07, 0xa5, 0x6d, 0x4d, 0x44, 0x8e, 0x42, 0x68,
+ 0xca, 0x94, 0x7c, 0x2e, 0x36, 0xba, 0x85, 0x9e, 0xcd, 0xaa, 0xc4, 0x5e,
+ 0x3c, 0x54, 0xbe, 0xfe, 0x2f, 0xea, 0x69, 0x9d, 0x1c, 0x1e, 0x29, 0x9b,
+ 0x96, 0xd8, 0xc8, 0xfe, 0x51, 0x90, 0xf1, 0x24, 0xa6, 0x90, 0x06, 0xb3,
+ 0xf0, 0x29, 0xa2, 0xff, 0x78, 0x2e, 0x77, 0x5c, 0x45, 0x21, 0xd9, 0x44,
+ 0x00, 0x31, 0xf3, 0xbe, 0x32, 0x4f, 0xf5, 0x0a, 0x32, 0x0d, 0xfc, 0xfc,
+ 0xba, 0x16, 0x76, 0x56, 0xb2, 0xd6, 0x48, 0x92, 0xf2, 0x8b, 0xa6, 0x3e,
+ 0xb7, 0xac, 0x5c, 0x69, 0xea, 0x0b, 0x3f, 0x66, 0x45, 0xb9,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 16:90:c3:29:b6:78:06:07:51:1f:05:b0:34:48:46:cb
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Apr 16 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO High-Assurance Secure Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e7:87:da:c0:77:e4:bb:3a:fa:6a:24:c8:80:41:
+ ac:d2:16:13:15:3d:fa:f7:f8:2a:76:dc:a8:2d:39:
+ 08:ce:48:4a:be:0f:7d:f0:de:ba:bb:47:d5:bd:2d:
+ d7:1b:ab:0f:20:81:23:08:72:b1:c0:11:95:0d:e6:
+ ea:a9:87:ff:c7:6e:1e:4f:66:32:ba:53:bc:05:aa:
+ 1c:2c:0c:ef:4d:37:47:6b:10:0c:db:c5:a0:98:7e:
+ 58:db:37:d6:ae:e9:06:bd:d7:a8:65:f3:37:b9:c7:
+ 6d:ce:77:c7:26:e0:d7:74:1f:a6:98:16:bb:0c:6b:
+ c8:be:77:d0:ef:58:a7:29:a0:b9:b8:69:05:36:cb:
+ b2:da:58:a3:0b:75:ad:3d:8b:22:82:20:3e:70:86:
+ 99:1c:b9:4f:cf:77:a4:07:1a:23:63:d1:38:56:84:
+ ec:bf:8f:c5:4e:f4:18:96:9b:1a:e8:93:ec:8d:af:
+ 15:9c:24:f0:5a:3b:e8:0f:b9:a8:5a:01:d3:b2:1c:
+ 60:c9:9c:52:04:dd:92:a7:fe:0c:ac:e2:45:8d:03:
+ 61:bc:79:e0:77:2e:87:41:3c:58:5f:cb:f5:c5:77:
+ f2:58:c8:4d:28:d0:9a:fa:f3:73:09:24:68:74:bc:
+ 20:4c:d8:2c:b0:aa:e8:d9:4e:6d:f2:8c:24:d3:93:
+ 5d:91
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 3F:D5:B5:D0:D6:44:79:50:4A:17:A3:9B:8C:4A:DC:B8:B0:22:64:6B
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 13:85:1f:52:80:18:c9:53:f7:fe:2e:1a:af:cc:d9:0b:3c:c2:
+ d3:85:81:10:f0:28:8d:b9:40:7e:2c:9e:8f:d6:36:86:0a:4c:
+ 14:2d:d6:97:43:92:41:19:37:4b:96:9e:eb:a9:30:79:12:95:
+ b3:02:36:57:ed:2b:b9:1d:98:1a:a3:18:0a:3f:9b:39:8b:cd:
+ a1:49:29:4c:2f:f9:d0:95:8c:c8:4d:95:ba:a8:43:cf:33:aa:
+ 25:2a:5a:0e:aa:27:c9:4e:6b:b1:e6:73:1f:b3:74:04:c3:f3:
+ 4c:e2:a8:eb:67:b7:5d:b8:08:05:1a:56:9a:54:29:85:f5:29:
+ 4e:80:3b:95:d0:7b:53:96:11:56:c1:02:d3:ea:b2:7f:ca:8f:
+ 9c:70:4a:14:8d:5a:b9:16:60:75:d6:cd:27:1e:16:cd:5b:33:
+ 8e:79:40:cf:28:48:e7:dc:71:16:4e:74:91:75:b9:2a:8c:f1:
+ 70:ac:26:dd:04:b9:40:c2:85:de:1c:93:40:d0:cc:6e:c3:9b:
+ aa:ef:60:65:df:60:22:f0:5a:a5:7a:a2:2f:e4:70:73:ee:3c:
+ d4:26:2b:68:07:c1:20:7a:e8:98:5a:3e:7b:9f:02:8b:62:c0:
+ 85:81:80:60:35:7e:a5:1d:0c:d2:9c:df:62:45:0d:db:fc:37:
+ fb:f5:25:22
+-----BEGIN CERTIFICATE-----
+MIIE/DCCA+SgAwIBAgIQFpDDKbZ4BgdRHwWwNEhGyzANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEwMDQxNjAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+gYkxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMS8wLQYD
+VQQDEyZDT01PRE8gSGlnaC1Bc3N1cmFuY2UgU2VjdXJlIFNlcnZlciBDQTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOeH2sB35Ls6+mokyIBBrNIWExU9
++vf4KnbcqC05CM5ISr4PffDeurtH1b0t1xurDyCBIwhyscARlQ3m6qmH/8duHk9m
+MrpTvAWqHCwM7003R2sQDNvFoJh+WNs31q7pBr3XqGXzN7nHbc53xybg13QfppgW
+uwxryL530O9YpymgubhpBTbLstpYowt1rT2LIoIgPnCGmRy5T893pAcaI2PROFaE
+7L+PxU70GJabGuiT7I2vFZwk8Fo76A+5qFoB07IcYMmcUgTdkqf+DKziRY0DYbx5
+4Hcuh0E8WF/L9cV38ljITSjQmvrzcwkkaHS8IEzYLLCq6NlObfKMJNOTXZECAwEA
+AaOCAXcwggFzMB8GA1UdIwQYMBaAFK29mHo0tCb3+sQmVO8DveAky1QaMB0GA1Ud
+DgQWBBQ/1bXQ1kR5UEoXo5uMSty4sCJkazAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T
+AQH/BAgwBgEB/wIBADARBgNVHSAECjAIMAYGBFUdIAAwRAYDVR0fBD0wOzA5oDeg
+NYYzaHR0cDovL2NybC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJv
+b3QuY3JsMIGzBggrBgEFBQcBAQSBpjCBozA/BggrBgEFBQcwAoYzaHR0cDovL2Ny
+dC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QucDdjMDkGCCsG
+AQUFBzAChi1odHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RVVE5TR0ND
+QS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJ
+KoZIhvcNAQEFBQADggEBABOFH1KAGMlT9/4uGq/M2Qs8wtOFgRDwKI25QH4sno/W
+NoYKTBQt1pdDkkEZN0uWnuupMHkSlbMCNlftK7kdmBqjGAo/mzmLzaFJKUwv+dCV
+jMhNlbqoQ88zqiUqWg6qJ8lOa7Hmcx+zdATD80ziqOtnt124CAUaVppUKYX1KU6A
+O5XQe1OWEVbBAtPqsn/Kj5xwShSNWrkWYHXWzSceFs1bM455QM8oSOfccRZOdJF1
+uSqM8XCsJt0EuUDChd4ck0DQzG7Dm6rvYGXfYCLwWqV6oi/kcHPuPNQmK2gHwSB6
+6JhaPnufAotiwIWBgGA1fqUdDNKc32JFDdv8N/v1JSI=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert37[] = {
+ 0x30, 0x82, 0x04, 0xfc, 0x30, 0x82, 0x03, 0xe4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x16, 0x90, 0xc3, 0x29, 0xb6, 0x78, 0x06, 0x07, 0x51,
+ 0x1f, 0x05, 0xb0, 0x34, 0x48, 0x46, 0xcb, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x34, 0x31,
+ 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x81, 0x89, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61,
+ 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f,
+ 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x26, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20,
+ 0x48, 0x69, 0x67, 0x68, 0x2d, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e,
+ 0x63, 0x65, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xe7, 0x87, 0xda, 0xc0, 0x77, 0xe4, 0xbb, 0x3a,
+ 0xfa, 0x6a, 0x24, 0xc8, 0x80, 0x41, 0xac, 0xd2, 0x16, 0x13, 0x15, 0x3d,
+ 0xfa, 0xf7, 0xf8, 0x2a, 0x76, 0xdc, 0xa8, 0x2d, 0x39, 0x08, 0xce, 0x48,
+ 0x4a, 0xbe, 0x0f, 0x7d, 0xf0, 0xde, 0xba, 0xbb, 0x47, 0xd5, 0xbd, 0x2d,
+ 0xd7, 0x1b, 0xab, 0x0f, 0x20, 0x81, 0x23, 0x08, 0x72, 0xb1, 0xc0, 0x11,
+ 0x95, 0x0d, 0xe6, 0xea, 0xa9, 0x87, 0xff, 0xc7, 0x6e, 0x1e, 0x4f, 0x66,
+ 0x32, 0xba, 0x53, 0xbc, 0x05, 0xaa, 0x1c, 0x2c, 0x0c, 0xef, 0x4d, 0x37,
+ 0x47, 0x6b, 0x10, 0x0c, 0xdb, 0xc5, 0xa0, 0x98, 0x7e, 0x58, 0xdb, 0x37,
+ 0xd6, 0xae, 0xe9, 0x06, 0xbd, 0xd7, 0xa8, 0x65, 0xf3, 0x37, 0xb9, 0xc7,
+ 0x6d, 0xce, 0x77, 0xc7, 0x26, 0xe0, 0xd7, 0x74, 0x1f, 0xa6, 0x98, 0x16,
+ 0xbb, 0x0c, 0x6b, 0xc8, 0xbe, 0x77, 0xd0, 0xef, 0x58, 0xa7, 0x29, 0xa0,
+ 0xb9, 0xb8, 0x69, 0x05, 0x36, 0xcb, 0xb2, 0xda, 0x58, 0xa3, 0x0b, 0x75,
+ 0xad, 0x3d, 0x8b, 0x22, 0x82, 0x20, 0x3e, 0x70, 0x86, 0x99, 0x1c, 0xb9,
+ 0x4f, 0xcf, 0x77, 0xa4, 0x07, 0x1a, 0x23, 0x63, 0xd1, 0x38, 0x56, 0x84,
+ 0xec, 0xbf, 0x8f, 0xc5, 0x4e, 0xf4, 0x18, 0x96, 0x9b, 0x1a, 0xe8, 0x93,
+ 0xec, 0x8d, 0xaf, 0x15, 0x9c, 0x24, 0xf0, 0x5a, 0x3b, 0xe8, 0x0f, 0xb9,
+ 0xa8, 0x5a, 0x01, 0xd3, 0xb2, 0x1c, 0x60, 0xc9, 0x9c, 0x52, 0x04, 0xdd,
+ 0x92, 0xa7, 0xfe, 0x0c, 0xac, 0xe2, 0x45, 0x8d, 0x03, 0x61, 0xbc, 0x79,
+ 0xe0, 0x77, 0x2e, 0x87, 0x41, 0x3c, 0x58, 0x5f, 0xcb, 0xf5, 0xc5, 0x77,
+ 0xf2, 0x58, 0xc8, 0x4d, 0x28, 0xd0, 0x9a, 0xfa, 0xf3, 0x73, 0x09, 0x24,
+ 0x68, 0x74, 0xbc, 0x20, 0x4c, 0xd8, 0x2c, 0xb0, 0xaa, 0xe8, 0xd9, 0x4e,
+ 0x6d, 0xf2, 0x8c, 0x24, 0xd3, 0x93, 0x5d, 0x91, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x77, 0x30, 0x82, 0x01, 0x73, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd,
+ 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03,
+ 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3f, 0xd5, 0xb5, 0xd0, 0xd6, 0x44, 0x79,
+ 0x50, 0x4a, 0x17, 0xa3, 0x9b, 0x8c, 0x4a, 0xdc, 0xb8, 0xb0, 0x22, 0x64,
+ 0x6b, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08,
+ 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x44, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0,
+ 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81,
+ 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x13, 0x85, 0x1f, 0x52, 0x80, 0x18, 0xc9, 0x53,
+ 0xf7, 0xfe, 0x2e, 0x1a, 0xaf, 0xcc, 0xd9, 0x0b, 0x3c, 0xc2, 0xd3, 0x85,
+ 0x81, 0x10, 0xf0, 0x28, 0x8d, 0xb9, 0x40, 0x7e, 0x2c, 0x9e, 0x8f, 0xd6,
+ 0x36, 0x86, 0x0a, 0x4c, 0x14, 0x2d, 0xd6, 0x97, 0x43, 0x92, 0x41, 0x19,
+ 0x37, 0x4b, 0x96, 0x9e, 0xeb, 0xa9, 0x30, 0x79, 0x12, 0x95, 0xb3, 0x02,
+ 0x36, 0x57, 0xed, 0x2b, 0xb9, 0x1d, 0x98, 0x1a, 0xa3, 0x18, 0x0a, 0x3f,
+ 0x9b, 0x39, 0x8b, 0xcd, 0xa1, 0x49, 0x29, 0x4c, 0x2f, 0xf9, 0xd0, 0x95,
+ 0x8c, 0xc8, 0x4d, 0x95, 0xba, 0xa8, 0x43, 0xcf, 0x33, 0xaa, 0x25, 0x2a,
+ 0x5a, 0x0e, 0xaa, 0x27, 0xc9, 0x4e, 0x6b, 0xb1, 0xe6, 0x73, 0x1f, 0xb3,
+ 0x74, 0x04, 0xc3, 0xf3, 0x4c, 0xe2, 0xa8, 0xeb, 0x67, 0xb7, 0x5d, 0xb8,
+ 0x08, 0x05, 0x1a, 0x56, 0x9a, 0x54, 0x29, 0x85, 0xf5, 0x29, 0x4e, 0x80,
+ 0x3b, 0x95, 0xd0, 0x7b, 0x53, 0x96, 0x11, 0x56, 0xc1, 0x02, 0xd3, 0xea,
+ 0xb2, 0x7f, 0xca, 0x8f, 0x9c, 0x70, 0x4a, 0x14, 0x8d, 0x5a, 0xb9, 0x16,
+ 0x60, 0x75, 0xd6, 0xcd, 0x27, 0x1e, 0x16, 0xcd, 0x5b, 0x33, 0x8e, 0x79,
+ 0x40, 0xcf, 0x28, 0x48, 0xe7, 0xdc, 0x71, 0x16, 0x4e, 0x74, 0x91, 0x75,
+ 0xb9, 0x2a, 0x8c, 0xf1, 0x70, 0xac, 0x26, 0xdd, 0x04, 0xb9, 0x40, 0xc2,
+ 0x85, 0xde, 0x1c, 0x93, 0x40, 0xd0, 0xcc, 0x6e, 0xc3, 0x9b, 0xaa, 0xef,
+ 0x60, 0x65, 0xdf, 0x60, 0x22, 0xf0, 0x5a, 0xa5, 0x7a, 0xa2, 0x2f, 0xe4,
+ 0x70, 0x73, 0xee, 0x3c, 0xd4, 0x26, 0x2b, 0x68, 0x07, 0xc1, 0x20, 0x7a,
+ 0xe8, 0x98, 0x5a, 0x3e, 0x7b, 0x9f, 0x02, 0x8b, 0x62, 0xc0, 0x85, 0x81,
+ 0x80, 0x60, 0x35, 0x7e, 0xa5, 0x1d, 0x0c, 0xd2, 0x9c, 0xdf, 0x62, 0x45,
+ 0x0d, 0xdb, 0xfc, 0x37, 0xfb, 0xf5, 0x25, 0x22,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1372799044 (0x51d34044)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority
+ Validity
+ Not Before: Sep 22 17:14:57 2014 GMT
+ Not After : Sep 23 01:31:53 2024 GMT
+ Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ba:84:b6:72:db:9e:0c:6b:e2:99:e9:30:01:a7:
+ 76:ea:32:b8:95:41:1a:c9:da:61:4e:58:72:cf:fe:
+ f6:82:79:bf:73:61:06:0a:a5:27:d8:b3:5f:d3:45:
+ 4e:1c:72:d6:4e:32:f2:72:8a:0f:f7:83:19:d0:6a:
+ 80:80:00:45:1e:b0:c7:e7:9a:bf:12:57:27:1c:a3:
+ 68:2f:0a:87:bd:6a:6b:0e:5e:65:f3:1c:77:d5:d4:
+ 85:8d:70:21:b4:b3:32:e7:8b:a2:d5:86:39:02:b1:
+ b8:d2:47:ce:e4:c9:49:c4:3b:a7:de:fb:54:7d:57:
+ be:f0:e8:6e:c2:79:b2:3a:0b:55:e2:50:98:16:32:
+ 13:5c:2f:78:56:c1:c2:94:b3:f2:5a:e4:27:9a:9f:
+ 24:d7:c6:ec:d0:9b:25:82:e3:cc:c2:c4:45:c5:8c:
+ 97:7a:06:6b:2a:11:9f:a9:0a:6e:48:3b:6f:db:d4:
+ 11:19:42:f7:8f:07:bf:f5:53:5f:9c:3e:f4:17:2c:
+ e6:69:ac:4e:32:4c:62:77:ea:b7:e8:e5:bb:34:bc:
+ 19:8b:ae:9c:51:e7:b7:7e:b5:53:b1:33:22:e5:6d:
+ cf:70:3c:1a:fa:e2:9b:67:b6:83:f4:8d:a5:af:62:
+ 4c:4d:e0:58:ac:64:34:12:03:f8:b6:8d:94:63:24:
+ a4:71
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/rootca1.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/CPS
+
+ X509v3 Subject Key Identifier:
+ 6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB
+ X509v3 Authority Key Identifier:
+ keyid:68:90:E4:67:A4:A6:53:80:C7:86:66:A4:F1:F7:4B:43:FB:84:BD:6D
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 69:33:83:fc:28:7a:6f:7d:ef:9d:55:eb:c5:3e:7a:9d:75:b3:
+ cc:c3:38:36:d9:34:a2:28:68:18:ea:1e:69:d3:bd:e7:d0:77:
+ da:b8:00:83:4e:4a:cf:6f:d1:f1:c1:22:3f:74:e4:f7:98:49:
+ 9e:9b:b6:9e:e1:db:98:77:2d:56:34:b1:a8:3c:d9:fd:c0:cd:
+ c7:bf:05:03:d4:02:c5:f1:e5:c6:da:08:a5:13:c7:62:23:11:
+ d1:61:30:1d:60:84:45:ef:79:a8:c6:26:93:a4:b7:cd:34:b8:
+ 69:c5:13:f6:91:b3:c9:45:73:76:b6:92:f6:76:0a:5b:e1:03:
+ 47:b7:e9:29:4c:91:32:23:37:4a:9c:35:d8:78:fd:1d:1f:e4:
+ 83:89:24:80:ad:b7:f9:cf:e4:5d:a5:d4:71:c4:85:5b:70:1f:
+ db:3f:1c:01:eb:1a:45:26:31:14:cc:65:bf:67:de:ca:cc:33:
+ 65:e5:41:91:d7:37:be:41:1a:96:9d:e6:8a:97:9d:a7:ce:ac:
+ 4e:9a:3d:bd:01:a0:6a:d9:4f:22:00:8b:44:d5:69:62:7b:2e:
+ eb:cc:ba:e7:92:7d:69:67:3d:fc:b8:7c:de:41:87:d0:69:ea:
+ ba:0a:18:7a:1a:95:43:b3:79:71:28:76:6d:a1:fb:57:4a:ec:
+ 4d:c8:0e:10
+-----BEGIN CERTIFICATE-----
+MIIE/zCCA+egAwIBAgIEUdNARDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
+Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
+KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDkyMjE3MTQ1N1oXDTI0MDkyMzAx
+MzE1M1owgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgw
+JgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQL
+EzAoYykgMjAwOSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9u
+bHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoS2ctueDGvi
+mekwAad26jK4lUEaydphTlhyz/72gnm/c2EGCqUn2LNf00VOHHLWTjLycooP94MZ
+0GqAgABFHrDH55q/ElcnHKNoLwqHvWprDl5l8xx31dSFjXAhtLMy54ui1YY5ArG4
+0kfO5MlJxDun3vtUfVe+8OhuwnmyOgtV4lCYFjITXC94VsHClLPyWuQnmp8k18bs
+0JslguPMwsRFxYyXegZrKhGfqQpuSDtv29QRGUL3jwe/9VNfnD70FyzmaaxOMkxi
+d+q36OW7NLwZi66cUee3frVTsTMi5W3PcDwa+uKbZ7aD9I2lr2JMTeBYrGQ0EgP4
+to2UYySkcQIDAQABo4IBDzCCAQswDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
+MAYBAf8CAQEwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz
+cC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmVudHJ1
+c3QubmV0L3Jvb3RjYTEuY3JsMDsGA1UdIAQ0MDIwMAYEVR0gADAoMCYGCCsGAQUF
+BwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NQUzAdBgNVHQ4EFgQUanImetAe
+733nO2lR1GyNn5ASZqswHwYDVR0jBBgwFoAUaJDkZ6SmU4DHhmak8fdLQ/uEvW0w
+DQYJKoZIhvcNAQELBQADggEBAGkzg/woem99751V68U+ep11s8zDODbZNKIoaBjq
+HmnTvefQd9q4AINOSs9v0fHBIj905PeYSZ6btp7h25h3LVY0sag82f3Azce/BQPU
+AsXx5cbaCKUTx2IjEdFhMB1ghEXveajGJpOkt800uGnFE/aRs8lFc3a2kvZ2Clvh
+A0e36SlMkTIjN0qcNdh4/R0f5IOJJICtt/nP5F2l1HHEhVtwH9s/HAHrGkUmMRTM
+Zb9n3srMM2XlQZHXN75BGpad5oqXnafOrE6aPb0BoGrZTyIAi0TVaWJ7LuvMuueS
+fWlnPfy4fN5Bh9Bp6roKGHoalUOzeXEodm2h+1dK7E3IDhA=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert38[] = {
+ 0x30, 0x82, 0x04, 0xff, 0x30, 0x82, 0x03, 0xe7, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x51, 0xd3, 0x40, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xb0, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x30, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x73, 0x20,
+ 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+ 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16,
+ 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d,
+ 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x34, 0x30, 0x39, 0x32, 0x32, 0x31, 0x37, 0x31, 0x34, 0x35,
+ 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x39, 0x32, 0x33, 0x30, 0x31,
+ 0x33, 0x31, 0x35, 0x33, 0x5a, 0x30, 0x81, 0xbe, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30,
+ 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x28, 0x30,
+ 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, 0x65, 0x20,
+ 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x74, 0x65,
+ 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, 0x45,
+ 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e,
+ 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f,
+ 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xba, 0x84, 0xb6, 0x72, 0xdb, 0x9e, 0x0c, 0x6b, 0xe2,
+ 0x99, 0xe9, 0x30, 0x01, 0xa7, 0x76, 0xea, 0x32, 0xb8, 0x95, 0x41, 0x1a,
+ 0xc9, 0xda, 0x61, 0x4e, 0x58, 0x72, 0xcf, 0xfe, 0xf6, 0x82, 0x79, 0xbf,
+ 0x73, 0x61, 0x06, 0x0a, 0xa5, 0x27, 0xd8, 0xb3, 0x5f, 0xd3, 0x45, 0x4e,
+ 0x1c, 0x72, 0xd6, 0x4e, 0x32, 0xf2, 0x72, 0x8a, 0x0f, 0xf7, 0x83, 0x19,
+ 0xd0, 0x6a, 0x80, 0x80, 0x00, 0x45, 0x1e, 0xb0, 0xc7, 0xe7, 0x9a, 0xbf,
+ 0x12, 0x57, 0x27, 0x1c, 0xa3, 0x68, 0x2f, 0x0a, 0x87, 0xbd, 0x6a, 0x6b,
+ 0x0e, 0x5e, 0x65, 0xf3, 0x1c, 0x77, 0xd5, 0xd4, 0x85, 0x8d, 0x70, 0x21,
+ 0xb4, 0xb3, 0x32, 0xe7, 0x8b, 0xa2, 0xd5, 0x86, 0x39, 0x02, 0xb1, 0xb8,
+ 0xd2, 0x47, 0xce, 0xe4, 0xc9, 0x49, 0xc4, 0x3b, 0xa7, 0xde, 0xfb, 0x54,
+ 0x7d, 0x57, 0xbe, 0xf0, 0xe8, 0x6e, 0xc2, 0x79, 0xb2, 0x3a, 0x0b, 0x55,
+ 0xe2, 0x50, 0x98, 0x16, 0x32, 0x13, 0x5c, 0x2f, 0x78, 0x56, 0xc1, 0xc2,
+ 0x94, 0xb3, 0xf2, 0x5a, 0xe4, 0x27, 0x9a, 0x9f, 0x24, 0xd7, 0xc6, 0xec,
+ 0xd0, 0x9b, 0x25, 0x82, 0xe3, 0xcc, 0xc2, 0xc4, 0x45, 0xc5, 0x8c, 0x97,
+ 0x7a, 0x06, 0x6b, 0x2a, 0x11, 0x9f, 0xa9, 0x0a, 0x6e, 0x48, 0x3b, 0x6f,
+ 0xdb, 0xd4, 0x11, 0x19, 0x42, 0xf7, 0x8f, 0x07, 0xbf, 0xf5, 0x53, 0x5f,
+ 0x9c, 0x3e, 0xf4, 0x17, 0x2c, 0xe6, 0x69, 0xac, 0x4e, 0x32, 0x4c, 0x62,
+ 0x77, 0xea, 0xb7, 0xe8, 0xe5, 0xbb, 0x34, 0xbc, 0x19, 0x8b, 0xae, 0x9c,
+ 0x51, 0xe7, 0xb7, 0x7e, 0xb5, 0x53, 0xb1, 0x33, 0x22, 0xe5, 0x6d, 0xcf,
+ 0x70, 0x3c, 0x1a, 0xfa, 0xe2, 0x9b, 0x67, 0xb6, 0x83, 0xf4, 0x8d, 0xa5,
+ 0xaf, 0x62, 0x4c, 0x4d, 0xe0, 0x58, 0xac, 0x64, 0x34, 0x12, 0x03, 0xf8,
+ 0xb6, 0x8d, 0x94, 0x63, 0x24, 0xa4, 0x71, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x0f, 0x30, 0x82, 0x01, 0x0b, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x33, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25,
+ 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a,
+ 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x63,
+ 0x61, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0, 0x1e,
+ 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90, 0x12,
+ 0x66, 0xab, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0x68, 0x90, 0xe4, 0x67, 0xa4, 0xa6, 0x53, 0x80, 0xc7,
+ 0x86, 0x66, 0xa4, 0xf1, 0xf7, 0x4b, 0x43, 0xfb, 0x84, 0xbd, 0x6d, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x69, 0x33, 0x83, 0xfc, 0x28,
+ 0x7a, 0x6f, 0x7d, 0xef, 0x9d, 0x55, 0xeb, 0xc5, 0x3e, 0x7a, 0x9d, 0x75,
+ 0xb3, 0xcc, 0xc3, 0x38, 0x36, 0xd9, 0x34, 0xa2, 0x28, 0x68, 0x18, 0xea,
+ 0x1e, 0x69, 0xd3, 0xbd, 0xe7, 0xd0, 0x77, 0xda, 0xb8, 0x00, 0x83, 0x4e,
+ 0x4a, 0xcf, 0x6f, 0xd1, 0xf1, 0xc1, 0x22, 0x3f, 0x74, 0xe4, 0xf7, 0x98,
+ 0x49, 0x9e, 0x9b, 0xb6, 0x9e, 0xe1, 0xdb, 0x98, 0x77, 0x2d, 0x56, 0x34,
+ 0xb1, 0xa8, 0x3c, 0xd9, 0xfd, 0xc0, 0xcd, 0xc7, 0xbf, 0x05, 0x03, 0xd4,
+ 0x02, 0xc5, 0xf1, 0xe5, 0xc6, 0xda, 0x08, 0xa5, 0x13, 0xc7, 0x62, 0x23,
+ 0x11, 0xd1, 0x61, 0x30, 0x1d, 0x60, 0x84, 0x45, 0xef, 0x79, 0xa8, 0xc6,
+ 0x26, 0x93, 0xa4, 0xb7, 0xcd, 0x34, 0xb8, 0x69, 0xc5, 0x13, 0xf6, 0x91,
+ 0xb3, 0xc9, 0x45, 0x73, 0x76, 0xb6, 0x92, 0xf6, 0x76, 0x0a, 0x5b, 0xe1,
+ 0x03, 0x47, 0xb7, 0xe9, 0x29, 0x4c, 0x91, 0x32, 0x23, 0x37, 0x4a, 0x9c,
+ 0x35, 0xd8, 0x78, 0xfd, 0x1d, 0x1f, 0xe4, 0x83, 0x89, 0x24, 0x80, 0xad,
+ 0xb7, 0xf9, 0xcf, 0xe4, 0x5d, 0xa5, 0xd4, 0x71, 0xc4, 0x85, 0x5b, 0x70,
+ 0x1f, 0xdb, 0x3f, 0x1c, 0x01, 0xeb, 0x1a, 0x45, 0x26, 0x31, 0x14, 0xcc,
+ 0x65, 0xbf, 0x67, 0xde, 0xca, 0xcc, 0x33, 0x65, 0xe5, 0x41, 0x91, 0xd7,
+ 0x37, 0xbe, 0x41, 0x1a, 0x96, 0x9d, 0xe6, 0x8a, 0x97, 0x9d, 0xa7, 0xce,
+ 0xac, 0x4e, 0x9a, 0x3d, 0xbd, 0x01, 0xa0, 0x6a, 0xd9, 0x4f, 0x22, 0x00,
+ 0x8b, 0x44, 0xd5, 0x69, 0x62, 0x7b, 0x2e, 0xeb, 0xcc, 0xba, 0xe7, 0x92,
+ 0x7d, 0x69, 0x67, 0x3d, 0xfc, 0xb8, 0x7c, 0xde, 0x41, 0x87, 0xd0, 0x69,
+ 0xea, 0xba, 0x0a, 0x18, 0x7a, 0x1a, 0x95, 0x43, 0xb3, 0x79, 0x71, 0x28,
+ 0x76, 0x6d, 0xa1, 0xfb, 0x57, 0x4a, 0xec, 0x4d, 0xc8, 0x0e, 0x10,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 7 (0x7)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2
+ Validity
+ Not Before: May 3 07:00:00 2011 GMT
+ Not After : May 3 07:00:00 2031 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., OU=http://certs.starfieldtech.com/repository/, CN=Starfield Secure Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e5:90:66:4b:ec:f9:46:71:a9:20:83:be:e9:6c:
+ bf:4a:c9:48:69:81:75:4e:6d:24:f6:cb:17:13:f8:
+ b0:71:59:84:7a:6b:2b:85:a4:34:b5:16:e5:cb:cc:
+ e9:41:70:2c:a4:2e:d6:fa:32:7d:e1:a8:de:94:10:
+ ac:31:c1:c0:d8:6a:ff:59:27:ab:76:d6:fc:0b:74:
+ 6b:b8:a7:ae:3f:c4:54:f4:b4:31:44:dd:93:56:8c:
+ a4:4c:5e:9b:89:cb:24:83:9b:e2:57:7d:b7:d8:12:
+ 1f:c9:85:6d:f4:d1:80:f1:50:9b:87:ae:d4:0b:10:
+ 05:fb:27:ba:28:6d:17:e9:0e:d6:4d:b9:39:55:06:
+ ff:0a:24:05:7e:2f:c6:1d:72:6c:d4:8b:29:8c:57:
+ 7d:da:d9:eb:66:1a:d3:4f:a7:df:7f:52:c4:30:c5:
+ a5:c9:0e:02:c5:53:bf:77:38:68:06:24:c3:66:c8:
+ 37:7e:30:1e:45:71:23:35:ff:90:d8:2a:9d:8d:e7:
+ b0:92:4d:3c:7f:2a:0a:93:dc:cd:16:46:65:f7:60:
+ 84:8b:76:4b:91:27:73:14:92:e0:ea:ee:8f:16:ea:
+ 8d:0e:3e:76:17:bf:7d:89:80:80:44:43:e7:2d:e0:
+ 43:09:75:da:36:e8:ad:db:89:3a:f5:5d:12:8e:23:
+ 04:83
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 25:45:81:68:50:26:38:3D:3B:2D:2C:BE:CD:6A:D9:B6:3D:B3:66:63
+ X509v3 Authority Key Identifier:
+ keyid:7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.starfieldtech.com/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.starfieldtech.com/sfroot-g2.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://certs.starfieldtech.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 56:65:ca:fe:f3:3f:0a:a8:93:8b:18:c7:de:43:69:13:34:20:
+ be:4e:5f:78:a8:6b:9c:db:6a:4d:41:db:c1:13:ec:dc:31:00:
+ 22:5e:f7:00:9e:0c:e0:34:65:34:f9:b1:3a:4e:48:c8:12:81:
+ 88:5c:5b:3e:08:53:7a:f7:1a:64:df:b8:50:61:cc:53:51:40:
+ 29:4b:c2:f4:ae:3a:5f:e4:ca:ad:26:cc:4e:61:43:e5:fd:57:
+ a6:37:70:ce:43:2b:b0:94:c3:92:e9:e1:5f:aa:10:49:b7:69:
+ e4:e0:d0:1f:64:a4:2b:cd:1f:6f:a0:f8:84:24:18:ce:79:3d:
+ a9:91:bf:54:18:13:89:99:54:11:0d:55:c5:26:0b:79:4f:5a:
+ 1c:6e:f9:63:db:14:80:a4:07:ab:fa:b2:a5:b9:88:dd:91:fe:
+ 65:3b:a4:a3:79:be:89:4d:e1:d0:b0:f4:c8:17:0c:0a:96:14:
+ 7c:09:b7:6c:e1:c2:d8:55:d4:18:a0:aa:41:69:70:24:a3:b9:
+ ef:e9:5a:dc:3e:eb:94:4a:f0:b7:de:5f:0e:76:fa:fb:fb:69:
+ 03:45:40:50:ee:72:0c:a4:12:86:81:cd:13:d1:4e:c4:3c:ca:
+ 4e:0d:d2:26:f1:00:b7:b4:a6:a2:e1:6e:7a:81:fd:30:ac:7a:
+ 1f:c7:59:7b
+-----BEGIN CERTIFICATE-----
+MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
+ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw
+MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
+aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk
+dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg
+Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF
+pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE
+3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV
+Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+
+MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX
+v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB
+Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+
+zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB
+BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo
+LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo
+LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF
+BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN
+QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0
+rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO
+eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ
+sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ
+7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert39[] = {
+ 0x30, 0x82, 0x05, 0x00, 0x30, 0x82, 0x03, 0xe8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x8f, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72,
+ 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61,
+ 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54,
+ 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c,
+ 0x64, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, 0xc6, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a,
+ 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65,
+ 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63,
+ 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72,
+ 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64,
+ 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70,
+ 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x31, 0x34, 0x30, 0x32,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b, 0x53, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20,
+ 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5,
+ 0x90, 0x66, 0x4b, 0xec, 0xf9, 0x46, 0x71, 0xa9, 0x20, 0x83, 0xbe, 0xe9,
+ 0x6c, 0xbf, 0x4a, 0xc9, 0x48, 0x69, 0x81, 0x75, 0x4e, 0x6d, 0x24, 0xf6,
+ 0xcb, 0x17, 0x13, 0xf8, 0xb0, 0x71, 0x59, 0x84, 0x7a, 0x6b, 0x2b, 0x85,
+ 0xa4, 0x34, 0xb5, 0x16, 0xe5, 0xcb, 0xcc, 0xe9, 0x41, 0x70, 0x2c, 0xa4,
+ 0x2e, 0xd6, 0xfa, 0x32, 0x7d, 0xe1, 0xa8, 0xde, 0x94, 0x10, 0xac, 0x31,
+ 0xc1, 0xc0, 0xd8, 0x6a, 0xff, 0x59, 0x27, 0xab, 0x76, 0xd6, 0xfc, 0x0b,
+ 0x74, 0x6b, 0xb8, 0xa7, 0xae, 0x3f, 0xc4, 0x54, 0xf4, 0xb4, 0x31, 0x44,
+ 0xdd, 0x93, 0x56, 0x8c, 0xa4, 0x4c, 0x5e, 0x9b, 0x89, 0xcb, 0x24, 0x83,
+ 0x9b, 0xe2, 0x57, 0x7d, 0xb7, 0xd8, 0x12, 0x1f, 0xc9, 0x85, 0x6d, 0xf4,
+ 0xd1, 0x80, 0xf1, 0x50, 0x9b, 0x87, 0xae, 0xd4, 0x0b, 0x10, 0x05, 0xfb,
+ 0x27, 0xba, 0x28, 0x6d, 0x17, 0xe9, 0x0e, 0xd6, 0x4d, 0xb9, 0x39, 0x55,
+ 0x06, 0xff, 0x0a, 0x24, 0x05, 0x7e, 0x2f, 0xc6, 0x1d, 0x72, 0x6c, 0xd4,
+ 0x8b, 0x29, 0x8c, 0x57, 0x7d, 0xda, 0xd9, 0xeb, 0x66, 0x1a, 0xd3, 0x4f,
+ 0xa7, 0xdf, 0x7f, 0x52, 0xc4, 0x30, 0xc5, 0xa5, 0xc9, 0x0e, 0x02, 0xc5,
+ 0x53, 0xbf, 0x77, 0x38, 0x68, 0x06, 0x24, 0xc3, 0x66, 0xc8, 0x37, 0x7e,
+ 0x30, 0x1e, 0x45, 0x71, 0x23, 0x35, 0xff, 0x90, 0xd8, 0x2a, 0x9d, 0x8d,
+ 0xe7, 0xb0, 0x92, 0x4d, 0x3c, 0x7f, 0x2a, 0x0a, 0x93, 0xdc, 0xcd, 0x16,
+ 0x46, 0x65, 0xf7, 0x60, 0x84, 0x8b, 0x76, 0x4b, 0x91, 0x27, 0x73, 0x14,
+ 0x92, 0xe0, 0xea, 0xee, 0x8f, 0x16, 0xea, 0x8d, 0x0e, 0x3e, 0x76, 0x17,
+ 0xbf, 0x7d, 0x89, 0x80, 0x80, 0x44, 0x43, 0xe7, 0x2d, 0xe0, 0x43, 0x09,
+ 0x75, 0xda, 0x36, 0xe8, 0xad, 0xdb, 0x89, 0x3a, 0xf5, 0x5d, 0x12, 0x8e,
+ 0x23, 0x04, 0x83, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x2c,
+ 0x30, 0x82, 0x01, 0x28, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x25, 0x45, 0x81, 0x68, 0x50, 0x26, 0x38, 0x3d, 0x3b, 0x2d, 0x2c, 0xbe,
+ 0xcd, 0x6a, 0xd9, 0xb6, 0x3d, 0xb3, 0x66, 0x63, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7c, 0x0c, 0x32,
+ 0x1f, 0xa7, 0xd9, 0x30, 0x7f, 0xc4, 0x7d, 0x68, 0xa3, 0x62, 0xa8, 0xa1,
+ 0xce, 0xab, 0x07, 0x5b, 0x27, 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1e, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0xa0, 0x2e, 0xa0, 0x2c, 0x86, 0x2a,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x72, 0x6f, 0x6f, 0x74, 0x2d,
+ 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x56, 0x65, 0xca, 0xfe,
+ 0xf3, 0x3f, 0x0a, 0xa8, 0x93, 0x8b, 0x18, 0xc7, 0xde, 0x43, 0x69, 0x13,
+ 0x34, 0x20, 0xbe, 0x4e, 0x5f, 0x78, 0xa8, 0x6b, 0x9c, 0xdb, 0x6a, 0x4d,
+ 0x41, 0xdb, 0xc1, 0x13, 0xec, 0xdc, 0x31, 0x00, 0x22, 0x5e, 0xf7, 0x00,
+ 0x9e, 0x0c, 0xe0, 0x34, 0x65, 0x34, 0xf9, 0xb1, 0x3a, 0x4e, 0x48, 0xc8,
+ 0x12, 0x81, 0x88, 0x5c, 0x5b, 0x3e, 0x08, 0x53, 0x7a, 0xf7, 0x1a, 0x64,
+ 0xdf, 0xb8, 0x50, 0x61, 0xcc, 0x53, 0x51, 0x40, 0x29, 0x4b, 0xc2, 0xf4,
+ 0xae, 0x3a, 0x5f, 0xe4, 0xca, 0xad, 0x26, 0xcc, 0x4e, 0x61, 0x43, 0xe5,
+ 0xfd, 0x57, 0xa6, 0x37, 0x70, 0xce, 0x43, 0x2b, 0xb0, 0x94, 0xc3, 0x92,
+ 0xe9, 0xe1, 0x5f, 0xaa, 0x10, 0x49, 0xb7, 0x69, 0xe4, 0xe0, 0xd0, 0x1f,
+ 0x64, 0xa4, 0x2b, 0xcd, 0x1f, 0x6f, 0xa0, 0xf8, 0x84, 0x24, 0x18, 0xce,
+ 0x79, 0x3d, 0xa9, 0x91, 0xbf, 0x54, 0x18, 0x13, 0x89, 0x99, 0x54, 0x11,
+ 0x0d, 0x55, 0xc5, 0x26, 0x0b, 0x79, 0x4f, 0x5a, 0x1c, 0x6e, 0xf9, 0x63,
+ 0xdb, 0x14, 0x80, 0xa4, 0x07, 0xab, 0xfa, 0xb2, 0xa5, 0xb9, 0x88, 0xdd,
+ 0x91, 0xfe, 0x65, 0x3b, 0xa4, 0xa3, 0x79, 0xbe, 0x89, 0x4d, 0xe1, 0xd0,
+ 0xb0, 0xf4, 0xc8, 0x17, 0x0c, 0x0a, 0x96, 0x14, 0x7c, 0x09, 0xb7, 0x6c,
+ 0xe1, 0xc2, 0xd8, 0x55, 0xd4, 0x18, 0xa0, 0xaa, 0x41, 0x69, 0x70, 0x24,
+ 0xa3, 0xb9, 0xef, 0xe9, 0x5a, 0xdc, 0x3e, 0xeb, 0x94, 0x4a, 0xf0, 0xb7,
+ 0xde, 0x5f, 0x0e, 0x76, 0xfa, 0xfb, 0xfb, 0x69, 0x03, 0x45, 0x40, 0x50,
+ 0xee, 0x72, 0x0c, 0xa4, 0x12, 0x86, 0x81, 0xcd, 0x13, 0xd1, 0x4e, 0xc4,
+ 0x3c, 0xca, 0x4e, 0x0d, 0xd2, 0x26, 0xf1, 0x00, 0xb7, 0xb4, 0xa6, 0xa2,
+ 0xe1, 0x6e, 0x7a, 0x81, 0xfd, 0x30, 0xac, 0x7a, 0x1f, 0xc7, 0x59, 0x7b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1372807406 (0x51d360ee)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2
+ Validity
+ Not Before: Oct 22 17:05:14 2014 GMT
+ Not After : Oct 23 07:33:22 2024 GMT
+ Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Certification Authority - L1K
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:da:3f:96:d0:4d:b9:2f:44:e7:db:39:5e:9b:50:
+ ee:5c:a5:61:da:41:67:53:09:aa:00:9a:8e:57:7f:
+ 29:6b:db:c7:e1:21:24:aa:3a:d0:8d:47:23:d2:ed:
+ 72:16:f0:91:21:d2:5d:b7:b8:4b:a8:83:8f:b7:91:
+ 32:68:cf:ce:25:93:2c:b2:7d:97:c8:fe:c1:b4:17:
+ ba:09:9e:03:90:93:7b:7c:49:83:22:68:8a:9b:de:
+ 47:c3:31:98:7a:2e:7d:40:0b:d2:ef:3e:d3:b2:8c:
+ aa:8f:48:a9:ff:00:e8:29:58:06:f7:b6:93:5a:94:
+ 73:26:26:ad:58:0e:e5:42:b8:d5:ea:73:79:64:68:
+ 53:25:b8:84:cf:94:7a:ae:06:45:0c:a3:6b:4d:d0:
+ c6:be:ea:18:a4:36:f0:92:b2:ba:1c:88:8f:3a:52:
+ 7f:f7:5e:6d:83:1c:9d:f0:1f:e5:c3:d6:dd:a5:78:
+ 92:3d:b0:6d:2c:ea:c9:cf:94:41:19:71:44:68:ba:
+ 47:3c:04:e9:5d:ba:3e:f0:35:f7:15:b6:9e:f2:2e:
+ 15:1e:3f:47:c8:c8:38:a7:73:45:5d:4d:b0:3b:b1:
+ 8e:17:29:37:ea:dd:05:01:22:bb:94:36:2a:8d:5b:
+ 35:fe:53:19:2f:08:46:c1:2a:b3:1a:62:1d:4e:2b:
+ d9:1b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints:
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/g2ca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/rpa
+
+ X509v3 Subject Key Identifier:
+ 82:A2:70:74:DD:BC:53:3F:CF:7B:D4:F7:CD:7F:A7:60:C6:0A:4C:BF
+ X509v3 Authority Key Identifier:
+ keyid:6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 3f:1c:1a:5b:ff:40:22:1d:8f:35:0c:2d:aa:99:27:ab:c0:11:
+ 32:70:d7:36:28:69:a5:8d:b1:27:99:42:be:c4:93:eb:48:57:
+ 43:71:23:c4:e5:4e:ad:ae:43:6f:92:76:c5:19:ef:ca:bc:6f:
+ 42:4c:16:9a:86:a9:04:38:c7:65:f0:f5:0c:e0:4a:df:a2:fa:
+ ce:1a:11:a8:9c:69:2f:1b:df:ea:e2:32:f3:ce:4c:bc:46:0c:
+ c0:89:80:d1:87:6b:a2:cf:6b:d4:7f:fd:f5:60:52:67:57:a0:
+ 6d:d1:64:41:14:6d:34:62:ed:06:6c:24:f2:06:bc:28:02:af:
+ 03:2d:c2:33:05:fb:cb:aa:16:e8:65:10:43:f5:69:5c:e3:81:
+ 58:99:cd:6b:d3:b8:c7:7b:19:55:c9:40:ce:79:55:b8:73:89:
+ e9:5c:40:66:43:12:7f:07:b8:65:56:d5:8d:c3:a7:f5:b1:b6:
+ 65:9e:c0:83:36:7f:16:45:3c:74:4b:93:8a:3c:f1:2b:f5:35:
+ 70:73:7b:e7:82:04:b1:18:98:0e:d4:9c:6f:1a:fc:fc:a7:33:
+ a5:bb:bb:18:f3:6b:7a:5d:32:87:f7:6d:25:e4:e2:76:86:21:
+ 1e:11:46:cd:76:0e:6f:4f:a4:21:71:0a:84:a7:2d:36:a9:48:
+ 22:51:7e:82
+-----BEGIN CERTIFICATE-----
+MIIFAzCCA+ugAwIBAgIEUdNg7jANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50
+cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs
+IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz
+dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMTQxMDIyMTcw
+NTE0WhcNMjQxMDIzMDczMzIyWjCBujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu
+dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt
+dGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0
+aG9yaXplZCB1c2Ugb25seTEuMCwGA1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eSAtIEwxSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANo/ltBNuS9E59s5XptQ7lylYdpBZ1MJqgCajld/KWvbx+EhJKo60I1HI9Ltchbw
+kSHSXbe4S6iDj7eRMmjPziWTLLJ9l8j+wbQXugmeA5CTe3xJgyJoipveR8MxmHou
+fUAL0u8+07KMqo9Iqf8A6ClYBve2k1qUcyYmrVgO5UK41epzeWRoUyW4hM+Ueq4G
+RQyja03Qxr7qGKQ28JKyuhyIjzpSf/debYMcnfAf5cPW3aV4kj2wbSzqyc+UQRlx
+RGi6RzwE6V26PvA19xW2nvIuFR4/R8jIOKdzRV1NsDuxjhcpN+rdBQEiu5Q2Ko1b
+Nf5TGS8IRsEqsxpiHU4r2RsCAwEAAaOCAQkwggEFMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMECDAGAQH/AgEAMDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcwAYYXaHR0
+cDovL29jc3AuZW50cnVzdC5uZXQwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL2Ny
+bC5lbnRydXN0Lm5ldC9nMmNhLmNybDA7BgNVHSAENDAyMDAGBFUdIAAwKDAmBggr
+BgEFBQcCARYaaHR0cDovL3d3dy5lbnRydXN0Lm5ldC9ycGEwHQYDVR0OBBYEFIKi
+cHTdvFM/z3vU981/p2DGCky/MB8GA1UdIwQYMBaAFGpyJnrQHu995ztpUdRsjZ+Q
+EmarMA0GCSqGSIb3DQEBCwUAA4IBAQA/HBpb/0AiHY81DC2qmSerwBEycNc2KGml
+jbEnmUK+xJPrSFdDcSPE5U6trkNvknbFGe/KvG9CTBaahqkEOMdl8PUM4ErfovrO
+GhGonGkvG9/q4jLzzky8RgzAiYDRh2uiz2vUf/31YFJnV6Bt0WRBFG00Yu0GbCTy
+BrwoAq8DLcIzBfvLqhboZRBD9Wlc44FYmc1r07jHexlVyUDOeVW4c4npXEBmQxJ/
+B7hlVtWNw6f1sbZlnsCDNn8WRTx0S5OKPPEr9TVwc3vnggSxGJgO1JxvGvz8pzOl
+u7sY82t6XTKH920l5OJ2hiEeEUbNdg5vT6QhcQqEpy02qUgiUX6C
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert40[] = {
+ 0x30, 0x82, 0x05, 0x03, 0x30, 0x82, 0x03, 0xeb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x51, 0xd3, 0x60, 0xee, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xbe, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1f, 0x53, 0x65, 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67,
+ 0x61, 0x6c, 0x2d, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32,
+ 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75,
+ 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x32, 0x32, 0x31, 0x37, 0x30,
+ 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x31, 0x30, 0x32, 0x33,
+ 0x30, 0x37, 0x33, 0x33, 0x32, 0x32, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65,
+ 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x32,
+ 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20,
+ 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d,
+ 0x20, 0x4c, 0x31, 0x4b, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xda, 0x3f, 0x96, 0xd0, 0x4d, 0xb9, 0x2f, 0x44, 0xe7, 0xdb, 0x39,
+ 0x5e, 0x9b, 0x50, 0xee, 0x5c, 0xa5, 0x61, 0xda, 0x41, 0x67, 0x53, 0x09,
+ 0xaa, 0x00, 0x9a, 0x8e, 0x57, 0x7f, 0x29, 0x6b, 0xdb, 0xc7, 0xe1, 0x21,
+ 0x24, 0xaa, 0x3a, 0xd0, 0x8d, 0x47, 0x23, 0xd2, 0xed, 0x72, 0x16, 0xf0,
+ 0x91, 0x21, 0xd2, 0x5d, 0xb7, 0xb8, 0x4b, 0xa8, 0x83, 0x8f, 0xb7, 0x91,
+ 0x32, 0x68, 0xcf, 0xce, 0x25, 0x93, 0x2c, 0xb2, 0x7d, 0x97, 0xc8, 0xfe,
+ 0xc1, 0xb4, 0x17, 0xba, 0x09, 0x9e, 0x03, 0x90, 0x93, 0x7b, 0x7c, 0x49,
+ 0x83, 0x22, 0x68, 0x8a, 0x9b, 0xde, 0x47, 0xc3, 0x31, 0x98, 0x7a, 0x2e,
+ 0x7d, 0x40, 0x0b, 0xd2, 0xef, 0x3e, 0xd3, 0xb2, 0x8c, 0xaa, 0x8f, 0x48,
+ 0xa9, 0xff, 0x00, 0xe8, 0x29, 0x58, 0x06, 0xf7, 0xb6, 0x93, 0x5a, 0x94,
+ 0x73, 0x26, 0x26, 0xad, 0x58, 0x0e, 0xe5, 0x42, 0xb8, 0xd5, 0xea, 0x73,
+ 0x79, 0x64, 0x68, 0x53, 0x25, 0xb8, 0x84, 0xcf, 0x94, 0x7a, 0xae, 0x06,
+ 0x45, 0x0c, 0xa3, 0x6b, 0x4d, 0xd0, 0xc6, 0xbe, 0xea, 0x18, 0xa4, 0x36,
+ 0xf0, 0x92, 0xb2, 0xba, 0x1c, 0x88, 0x8f, 0x3a, 0x52, 0x7f, 0xf7, 0x5e,
+ 0x6d, 0x83, 0x1c, 0x9d, 0xf0, 0x1f, 0xe5, 0xc3, 0xd6, 0xdd, 0xa5, 0x78,
+ 0x92, 0x3d, 0xb0, 0x6d, 0x2c, 0xea, 0xc9, 0xcf, 0x94, 0x41, 0x19, 0x71,
+ 0x44, 0x68, 0xba, 0x47, 0x3c, 0x04, 0xe9, 0x5d, 0xba, 0x3e, 0xf0, 0x35,
+ 0xf7, 0x15, 0xb6, 0x9e, 0xf2, 0x2e, 0x15, 0x1e, 0x3f, 0x47, 0xc8, 0xc8,
+ 0x38, 0xa7, 0x73, 0x45, 0x5d, 0x4d, 0xb0, 0x3b, 0xb1, 0x8e, 0x17, 0x29,
+ 0x37, 0xea, 0xdd, 0x05, 0x01, 0x22, 0xbb, 0x94, 0x36, 0x2a, 0x8d, 0x5b,
+ 0x35, 0xfe, 0x53, 0x19, 0x2f, 0x08, 0x46, 0xc1, 0x2a, 0xb3, 0x1a, 0x62,
+ 0x1d, 0x4e, 0x2b, 0xd9, 0x1b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x09, 0x30, 0x82, 0x01, 0x05, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x30, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0,
+ 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x2f, 0x67, 0x32, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x82, 0xa2,
+ 0x70, 0x74, 0xdd, 0xbc, 0x53, 0x3f, 0xcf, 0x7b, 0xd4, 0xf7, 0xcd, 0x7f,
+ 0xa7, 0x60, 0xc6, 0x0a, 0x4c, 0xbf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0,
+ 0x1e, 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90,
+ 0x12, 0x66, 0xab, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3f,
+ 0x1c, 0x1a, 0x5b, 0xff, 0x40, 0x22, 0x1d, 0x8f, 0x35, 0x0c, 0x2d, 0xaa,
+ 0x99, 0x27, 0xab, 0xc0, 0x11, 0x32, 0x70, 0xd7, 0x36, 0x28, 0x69, 0xa5,
+ 0x8d, 0xb1, 0x27, 0x99, 0x42, 0xbe, 0xc4, 0x93, 0xeb, 0x48, 0x57, 0x43,
+ 0x71, 0x23, 0xc4, 0xe5, 0x4e, 0xad, 0xae, 0x43, 0x6f, 0x92, 0x76, 0xc5,
+ 0x19, 0xef, 0xca, 0xbc, 0x6f, 0x42, 0x4c, 0x16, 0x9a, 0x86, 0xa9, 0x04,
+ 0x38, 0xc7, 0x65, 0xf0, 0xf5, 0x0c, 0xe0, 0x4a, 0xdf, 0xa2, 0xfa, 0xce,
+ 0x1a, 0x11, 0xa8, 0x9c, 0x69, 0x2f, 0x1b, 0xdf, 0xea, 0xe2, 0x32, 0xf3,
+ 0xce, 0x4c, 0xbc, 0x46, 0x0c, 0xc0, 0x89, 0x80, 0xd1, 0x87, 0x6b, 0xa2,
+ 0xcf, 0x6b, 0xd4, 0x7f, 0xfd, 0xf5, 0x60, 0x52, 0x67, 0x57, 0xa0, 0x6d,
+ 0xd1, 0x64, 0x41, 0x14, 0x6d, 0x34, 0x62, 0xed, 0x06, 0x6c, 0x24, 0xf2,
+ 0x06, 0xbc, 0x28, 0x02, 0xaf, 0x03, 0x2d, 0xc2, 0x33, 0x05, 0xfb, 0xcb,
+ 0xaa, 0x16, 0xe8, 0x65, 0x10, 0x43, 0xf5, 0x69, 0x5c, 0xe3, 0x81, 0x58,
+ 0x99, 0xcd, 0x6b, 0xd3, 0xb8, 0xc7, 0x7b, 0x19, 0x55, 0xc9, 0x40, 0xce,
+ 0x79, 0x55, 0xb8, 0x73, 0x89, 0xe9, 0x5c, 0x40, 0x66, 0x43, 0x12, 0x7f,
+ 0x07, 0xb8, 0x65, 0x56, 0xd5, 0x8d, 0xc3, 0xa7, 0xf5, 0xb1, 0xb6, 0x65,
+ 0x9e, 0xc0, 0x83, 0x36, 0x7f, 0x16, 0x45, 0x3c, 0x74, 0x4b, 0x93, 0x8a,
+ 0x3c, 0xf1, 0x2b, 0xf5, 0x35, 0x70, 0x73, 0x7b, 0xe7, 0x82, 0x04, 0xb1,
+ 0x18, 0x98, 0x0e, 0xd4, 0x9c, 0x6f, 0x1a, 0xfc, 0xfc, 0xa7, 0x33, 0xa5,
+ 0xbb, 0xbb, 0x18, 0xf3, 0x6b, 0x7a, 0x5d, 0x32, 0x87, 0xf7, 0x6d, 0x25,
+ 0xe4, 0xe2, 0x76, 0x86, 0x21, 0x1e, 0x11, 0x46, 0xcd, 0x76, 0x0e, 0x6f,
+ 0x4f, 0xa4, 0x21, 0x71, 0x0a, 0x84, 0xa7, 0x2d, 0x36, 0xa9, 0x48, 0x22,
+ 0x51, 0x7e, 0x82,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120038507 (0x727a46b)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Apr 2 14:36:10 2014 GMT
+ Not After : Apr 2 14:35:52 2021 GMT
+ Subject: C=NL, L=Amsterdam, O=Verizon Enterprise Solutions, OU=Cybertrust, CN=Verizon Akamai SureServer CA G14-SHA2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:dd:6e:9e:02:69:02:b5:a3:99:2e:08:64:32:6a:
+ 59:f3:c6:9e:a6:20:07:d2:48:d1:a8:93:c7:ea:47:
+ 8f:83:39:40:d7:20:5d:8d:9a:ba:ab:d8:70:ec:9d:
+ 88:d1:bd:62:f6:db:ec:9d:5e:35:01:76:03:23:e5:
+ 6f:d2:af:46:35:59:5a:5c:d1:a8:23:c1:eb:e9:20:
+ d4:49:d6:3f:00:d8:a8:22:de:43:79:81:ac:e9:a4:
+ 92:f5:77:70:05:1e:5c:b6:a0:f7:90:a4:cd:ab:28:
+ 2c:90:c2:e7:0f:c3:af:1c:47:59:d5:84:2e:df:26:
+ 07:45:23:5a:c6:e8:90:c8:85:4b:8c:16:1e:60:f9:
+ 01:13:f1:14:1f:e6:e8:14:ed:c5:d2:6f:63:28:6e:
+ 72:8c:49:ae:08:72:c7:93:95:b4:0b:0c:ae:8f:9a:
+ 67:84:f5:57:1b:db:81:d7:17:9d:41:11:43:19:bd:
+ 6d:4a:85:ed:8f:70:25:ab:66:ab:f6:fa:6d:1c:3c:
+ ab:ed:17:bd:56:84:e1:db:75:33:b2:28:4b:99:8e:
+ f9:4b:82:33:50:9f:92:53:ed:fa:ad:0f:95:9c:a3:
+ f2:cb:60:f0:77:1d:c9:01:8b:5f:2d:86:be:bf:36:
+ b8:24:96:13:7c:c1:86:5a:6c:c1:48:2a:7f:3e:93:
+ 60:c5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:2
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.50
+ CPS: https://secure.omniroot.com/repository
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.omniroot.com/baltimoreroot
+ CA Issuers - URI:https://cacert.omniroot.com/baltimoreroot.crt
+ CA Issuers - URI:https://cacert.omniroot.com/baltimoreroot.der
+
+ X509v3 Key Usage: critical
+ Digital Signature, Non Repudiation, Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ F8:BD:FA:AF:73:77:C6:C7:1B:F9:4B:4D:11:A7:D1:33:AF:AF:72:11
+ Signature Algorithm: sha256WithRSAEncryption
+ 80:d9:7a:ed:72:05:37:8f:61:aa:73:7c:9a:6a:fc:fe:01:e2:
+ 19:81:70:07:25:32:b0:f0:6f:3b:c7:6a:28:3d:e4:51:87:e6:
+ 7e:82:ec:ae:48:a7:b1:77:38:c2:d6:56:af:8f:f2:01:fc:65:
+ 65:10:09:f7:74:29:b5:0e:92:ee:90:98:d1:88:a2:65:b7:cd:
+ 9c:0e:a7:86:98:28:bc:ae:15:83:b6:1a:d7:1d:ec:19:da:7a:
+ 8e:40:f9:99:15:d5:7d:a5:ba:ab:fd:26:98:6e:9c:41:3b:b6:
+ 81:18:ec:70:48:d7:6e:7f:a6:e1:77:25:d6:dd:62:e8:52:f3:
+ 8c:16:39:67:e2:22:0d:77:2e:fb:11:6c:e4:dd:38:b4:27:5f:
+ 03:a8:3d:44:e2:f2:84:4b:84:fd:56:a6:9e:4d:7b:a2:16:4f:
+ 07:f5:34:24:72:a5:a2:fa:16:66:2a:a4:4a:0e:c8:0d:27:44:
+ 9c:77:d4:12:10:87:d2:00:2c:7a:bb:8e:88:22:91:15:be:a2:
+ 59:ca:34:e0:1c:61:94:86:20:33:cd:e7:4c:5d:3b:92:3e:cb:
+ d6:2d:ea:54:fa:fb:af:54:f5:a8:c5:0b:ca:8b:87:00:e6:9f:
+ e6:95:bf:b7:c4:a3:59:f5:16:6c:5f:3e:69:55:80:39:f6:75:
+ 50:14:3e:32
+-----BEGIN CERTIFICATE-----
+MIIFHzCCBAegAwIBAgIEByekazANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDQwMjE0MzYxMFoX
+DTIxMDQwMjE0MzU1MlowgY0xCzAJBgNVBAYTAk5MMRIwEAYDVQQHEwlBbXN0ZXJk
+YW0xJTAjBgNVBAoTHFZlcml6b24gRW50ZXJwcmlzZSBTb2x1dGlvbnMxEzARBgNV
+BAsTCkN5YmVydHJ1c3QxLjAsBgNVBAMTJVZlcml6b24gQWthbWFpIFN1cmVTZXJ2
+ZXIgQ0EgRzE0LVNIQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDd
+bp4CaQK1o5kuCGQyalnzxp6mIAfSSNGok8fqR4+DOUDXIF2Nmrqr2HDsnYjRvWL2
+2+ydXjUBdgMj5W/Sr0Y1WVpc0agjwevpINRJ1j8A2Kgi3kN5gazppJL1d3AFHly2
+oPeQpM2rKCyQwucPw68cR1nVhC7fJgdFI1rG6JDIhUuMFh5g+QET8RQf5ugU7cXS
+b2MobnKMSa4IcseTlbQLDK6PmmeE9Vcb24HXF51BEUMZvW1Khe2PcCWrZqv2+m0c
+PKvtF71WhOHbdTOyKEuZjvlLgjNQn5JT7fqtD5Wco/LLYPB3HckBi18thr6/Nrgk
+lhN8wYZabMFIKn8+k2DFAgMBAAGjggG3MIIBszASBgNVHRMBAf8ECDAGAQH/AgEC
+MEwGA1UdIARFMEMwQQYJKwYBBAGxPgEyMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v
+c2VjdXJlLm9tbmlyb290LmNvbS9yZXBvc2l0b3J5MIG6BggrBgEFBQcBAQSBrTCB
+qjAyBggrBgEFBQcwAYYmaHR0cDovL29jc3Aub21uaXJvb3QuY29tL2JhbHRpbW9y
+ZXJvb3QwOQYIKwYBBQUHMAKGLWh0dHBzOi8vY2FjZXJ0Lm9tbmlyb290LmNvbS9i
+YWx0aW1vcmVyb290LmNydDA5BggrBgEFBQcwAoYtaHR0cHM6Ly9jYWNlcnQub21u
+aXJvb3QuY29tL2JhbHRpbW9yZXJvb3QuZGVyMA4GA1UdDwEB/wQEAwIBxjAfBgNV
+HSMEGDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DBCBgNVHR8EOzA5MDegNaAzhjFo
+dHRwOi8vY2RwMS5wdWJsaWMtdHJ1c3QuY29tL0NSTC9PbW5pcm9vdDIwMjUuY3Js
+MB0GA1UdDgQWBBT4vfqvc3fGxxv5S00Rp9Ezr69yETANBgkqhkiG9w0BAQsFAAOC
+AQEAgNl67XIFN49hqnN8mmr8/gHiGYFwByUysPBvO8dqKD3kUYfmfoLsrkinsXc4
+wtZWr4/yAfxlZRAJ93QptQ6S7pCY0YiiZbfNnA6nhpgovK4Vg7Ya1x3sGdp6jkD5
+mRXVfaW6q/0mmG6cQTu2gRjscEjXbn+m4Xcl1t1i6FLzjBY5Z+IiDXcu+xFs5N04
+tCdfA6g9ROLyhEuE/Vamnk17ohZPB/U0JHKlovoWZiqkSg7IDSdEnHfUEhCH0gAs
+eruOiCKRFb6iWco04BxhlIYgM83nTF07kj7L1i3qVPr7r1T1qMULyouHAOaf5pW/
+t8SjWfUWbF8+aVWAOfZ1UBQ+Mg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert41[] = {
+ 0x30, 0x82, 0x05, 0x1f, 0x30, 0x82, 0x04, 0x07, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0xa4, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34,
+ 0x30, 0x34, 0x30, 0x32, 0x31, 0x34, 0x33, 0x36, 0x31, 0x30, 0x5a, 0x17,
+ 0x0d, 0x32, 0x31, 0x30, 0x34, 0x30, 0x32, 0x31, 0x34, 0x33, 0x35, 0x35,
+ 0x32, 0x5a, 0x30, 0x81, 0x8d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x13, 0x09, 0x41, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x64,
+ 0x61, 0x6d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x1c, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x45, 0x6e, 0x74,
+ 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x20, 0x53, 0x6f, 0x6c, 0x75,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x25, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x41, 0x6b, 0x61,
+ 0x6d, 0x61, 0x69, 0x20, 0x53, 0x75, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x47, 0x31, 0x34, 0x2d, 0x53, 0x48,
+ 0x41, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdd,
+ 0x6e, 0x9e, 0x02, 0x69, 0x02, 0xb5, 0xa3, 0x99, 0x2e, 0x08, 0x64, 0x32,
+ 0x6a, 0x59, 0xf3, 0xc6, 0x9e, 0xa6, 0x20, 0x07, 0xd2, 0x48, 0xd1, 0xa8,
+ 0x93, 0xc7, 0xea, 0x47, 0x8f, 0x83, 0x39, 0x40, 0xd7, 0x20, 0x5d, 0x8d,
+ 0x9a, 0xba, 0xab, 0xd8, 0x70, 0xec, 0x9d, 0x88, 0xd1, 0xbd, 0x62, 0xf6,
+ 0xdb, 0xec, 0x9d, 0x5e, 0x35, 0x01, 0x76, 0x03, 0x23, 0xe5, 0x6f, 0xd2,
+ 0xaf, 0x46, 0x35, 0x59, 0x5a, 0x5c, 0xd1, 0xa8, 0x23, 0xc1, 0xeb, 0xe9,
+ 0x20, 0xd4, 0x49, 0xd6, 0x3f, 0x00, 0xd8, 0xa8, 0x22, 0xde, 0x43, 0x79,
+ 0x81, 0xac, 0xe9, 0xa4, 0x92, 0xf5, 0x77, 0x70, 0x05, 0x1e, 0x5c, 0xb6,
+ 0xa0, 0xf7, 0x90, 0xa4, 0xcd, 0xab, 0x28, 0x2c, 0x90, 0xc2, 0xe7, 0x0f,
+ 0xc3, 0xaf, 0x1c, 0x47, 0x59, 0xd5, 0x84, 0x2e, 0xdf, 0x26, 0x07, 0x45,
+ 0x23, 0x5a, 0xc6, 0xe8, 0x90, 0xc8, 0x85, 0x4b, 0x8c, 0x16, 0x1e, 0x60,
+ 0xf9, 0x01, 0x13, 0xf1, 0x14, 0x1f, 0xe6, 0xe8, 0x14, 0xed, 0xc5, 0xd2,
+ 0x6f, 0x63, 0x28, 0x6e, 0x72, 0x8c, 0x49, 0xae, 0x08, 0x72, 0xc7, 0x93,
+ 0x95, 0xb4, 0x0b, 0x0c, 0xae, 0x8f, 0x9a, 0x67, 0x84, 0xf5, 0x57, 0x1b,
+ 0xdb, 0x81, 0xd7, 0x17, 0x9d, 0x41, 0x11, 0x43, 0x19, 0xbd, 0x6d, 0x4a,
+ 0x85, 0xed, 0x8f, 0x70, 0x25, 0xab, 0x66, 0xab, 0xf6, 0xfa, 0x6d, 0x1c,
+ 0x3c, 0xab, 0xed, 0x17, 0xbd, 0x56, 0x84, 0xe1, 0xdb, 0x75, 0x33, 0xb2,
+ 0x28, 0x4b, 0x99, 0x8e, 0xf9, 0x4b, 0x82, 0x33, 0x50, 0x9f, 0x92, 0x53,
+ 0xed, 0xfa, 0xad, 0x0f, 0x95, 0x9c, 0xa3, 0xf2, 0xcb, 0x60, 0xf0, 0x77,
+ 0x1d, 0xc9, 0x01, 0x8b, 0x5f, 0x2d, 0x86, 0xbe, 0xbf, 0x36, 0xb8, 0x24,
+ 0x96, 0x13, 0x7c, 0xc1, 0x86, 0x5a, 0x6c, 0xc1, 0x48, 0x2a, 0x7f, 0x3e,
+ 0x93, 0x60, 0xc5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xb7,
+ 0x30, 0x82, 0x01, 0xb3, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x02,
+ 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30,
+ 0x41, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x32,
+ 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x81, 0xba, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xad, 0x30, 0x81,
+ 0xaa, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72,
+ 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62,
+ 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x72, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x6d, 0x6e,
+ 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61,
+ 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x2e,
+ 0x64, 0x65, 0x72, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0xc6, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30,
+ 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a,
+ 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69,
+ 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf8,
+ 0xbd, 0xfa, 0xaf, 0x73, 0x77, 0xc6, 0xc7, 0x1b, 0xf9, 0x4b, 0x4d, 0x11,
+ 0xa7, 0xd1, 0x33, 0xaf, 0xaf, 0x72, 0x11, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x80, 0xd9, 0x7a, 0xed, 0x72, 0x05, 0x37, 0x8f, 0x61,
+ 0xaa, 0x73, 0x7c, 0x9a, 0x6a, 0xfc, 0xfe, 0x01, 0xe2, 0x19, 0x81, 0x70,
+ 0x07, 0x25, 0x32, 0xb0, 0xf0, 0x6f, 0x3b, 0xc7, 0x6a, 0x28, 0x3d, 0xe4,
+ 0x51, 0x87, 0xe6, 0x7e, 0x82, 0xec, 0xae, 0x48, 0xa7, 0xb1, 0x77, 0x38,
+ 0xc2, 0xd6, 0x56, 0xaf, 0x8f, 0xf2, 0x01, 0xfc, 0x65, 0x65, 0x10, 0x09,
+ 0xf7, 0x74, 0x29, 0xb5, 0x0e, 0x92, 0xee, 0x90, 0x98, 0xd1, 0x88, 0xa2,
+ 0x65, 0xb7, 0xcd, 0x9c, 0x0e, 0xa7, 0x86, 0x98, 0x28, 0xbc, 0xae, 0x15,
+ 0x83, 0xb6, 0x1a, 0xd7, 0x1d, 0xec, 0x19, 0xda, 0x7a, 0x8e, 0x40, 0xf9,
+ 0x99, 0x15, 0xd5, 0x7d, 0xa5, 0xba, 0xab, 0xfd, 0x26, 0x98, 0x6e, 0x9c,
+ 0x41, 0x3b, 0xb6, 0x81, 0x18, 0xec, 0x70, 0x48, 0xd7, 0x6e, 0x7f, 0xa6,
+ 0xe1, 0x77, 0x25, 0xd6, 0xdd, 0x62, 0xe8, 0x52, 0xf3, 0x8c, 0x16, 0x39,
+ 0x67, 0xe2, 0x22, 0x0d, 0x77, 0x2e, 0xfb, 0x11, 0x6c, 0xe4, 0xdd, 0x38,
+ 0xb4, 0x27, 0x5f, 0x03, 0xa8, 0x3d, 0x44, 0xe2, 0xf2, 0x84, 0x4b, 0x84,
+ 0xfd, 0x56, 0xa6, 0x9e, 0x4d, 0x7b, 0xa2, 0x16, 0x4f, 0x07, 0xf5, 0x34,
+ 0x24, 0x72, 0xa5, 0xa2, 0xfa, 0x16, 0x66, 0x2a, 0xa4, 0x4a, 0x0e, 0xc8,
+ 0x0d, 0x27, 0x44, 0x9c, 0x77, 0xd4, 0x12, 0x10, 0x87, 0xd2, 0x00, 0x2c,
+ 0x7a, 0xbb, 0x8e, 0x88, 0x22, 0x91, 0x15, 0xbe, 0xa2, 0x59, 0xca, 0x34,
+ 0xe0, 0x1c, 0x61, 0x94, 0x86, 0x20, 0x33, 0xcd, 0xe7, 0x4c, 0x5d, 0x3b,
+ 0x92, 0x3e, 0xcb, 0xd6, 0x2d, 0xea, 0x54, 0xfa, 0xfb, 0xaf, 0x54, 0xf5,
+ 0xa8, 0xc5, 0x0b, 0xca, 0x8b, 0x87, 0x00, 0xe6, 0x9f, 0xe6, 0x95, 0xbf,
+ 0xb7, 0xc4, 0xa3, 0x59, 0xf5, 0x16, 0x6c, 0x5f, 0x3e, 0x69, 0x55, 0x80,
+ 0x39, 0xf6, 0x75, 0x50, 0x14, 0x3e, 0x32,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 7e:e1:4a:6f:6f:ef:f2:d3:7f:3f:ad:65:4d:3a:da:b4
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 EV SSL CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d8:a1:65:74:23:e8:2b:64:e2:32:d7:33:37:3d:
+ 8e:f5:34:16:48:dd:4f:7f:87:1c:f8:44:23:13:8e:
+ fb:11:d8:44:5a:18:71:8e:60:16:26:92:9b:fd:17:
+ 0b:e1:71:70:42:fe:bf:fa:1c:c0:aa:a3:a7:b5:71:
+ e8:ff:18:83:f6:df:10:0a:13:62:c8:3d:9c:a7:de:
+ 2e:3f:0c:d9:1d:e7:2e:fb:2a:ce:c8:9a:7f:87:bf:
+ d8:4c:04:15:32:c9:d1:cc:95:71:a0:4e:28:4f:84:
+ d9:35:fb:e3:86:6f:94:53:e6:72:8a:63:67:2e:be:
+ 69:f6:f7:6e:8e:9c:60:04:eb:29:fa:c4:47:42:d2:
+ 78:98:e3:ec:0b:a5:92:dc:b7:9a:bd:80:64:2b:38:
+ 7c:38:09:5b:66:f6:2d:95:7a:86:b2:34:2e:85:9e:
+ 90:0e:5f:b7:5d:a4:51:72:46:70:13:bf:67:f2:b6:
+ a7:4d:14:1e:6c:b9:53:ee:23:1a:4e:8d:48:55:43:
+ 41:b1:89:75:6a:40:28:c5:7d:dd:d2:6e:d2:02:19:
+ 2f:7b:24:94:4b:eb:f1:1a:a9:9b:e3:23:9a:ea:fa:
+ 33:ab:0a:2c:b7:f4:60:08:dd:9f:1c:cd:dd:2d:01:
+ 66:80:af:b3:2f:29:1d:23:b8:8a:e1:a1:70:07:0c:
+ 34:0f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://s2.symcb.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.symauth.com/cps
+ User Notice:
+ Explicit Text: http://www.symauth.com/rpa
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://s1.symcb.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-533
+ X509v3 Subject Key Identifier:
+ 01:59:AB:E7:DD:3A:0B:59:A6:64:63:D6:CF:20:07:57:D5:91:E7:6A
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 42:01:55:7b:d0:16:1a:5d:58:e8:bb:9b:a8:4d:d7:f3:d7:eb:
+ 13:94:86:d6:7f:21:0b:47:bc:57:9b:92:5d:4f:05:9f:38:a4:
+ 10:7c:cf:83:be:06:43:46:8d:08:bc:6a:d7:10:a6:fa:ab:af:
+ 2f:61:a8:63:f2:65:df:7f:4c:88:12:88:4f:b3:69:d9:ff:27:
+ c0:0a:97:91:8f:56:fb:89:c4:a8:bb:92:2d:1b:73:b0:c6:ab:
+ 36:f4:96:6c:20:08:ef:0a:1e:66:24:45:4f:67:00:40:c8:07:
+ 54:74:33:3b:a6:ad:bb:23:9f:66:ed:a2:44:70:34:fb:0e:ea:
+ 01:fd:cf:78:74:df:a7:ad:55:b7:5f:4d:f6:d6:3f:e0:86:ce:
+ 24:c7:42:a9:13:14:44:35:4b:b6:df:c9:60:ac:0c:7f:d9:93:
+ 21:4b:ee:9c:e4:49:02:98:d3:60:7b:5c:bc:d5:30:2f:07:ce:
+ 44:42:c4:0b:99:fe:e6:9f:fc:b0:78:86:51:6d:d1:2c:9d:c6:
+ 96:fb:85:82:bb:04:2f:f7:62:80:ef:62:da:7f:f6:0e:ac:90:
+ b8:56:bd:79:3f:f2:80:6e:a3:d9:b9:0f:5d:3a:07:1d:91:93:
+ 86:4b:29:4c:e1:dc:b5:e1:e0:33:9d:b3:cb:36:91:4b:fe:a1:
+ b4:ee:f0:f9
+-----BEGIN CERTIFICATE-----
+MIIFKzCCBBOgAwIBAgIQfuFKb2/v8tN/P61lTTratDANBgkqhkiG9w0BAQsFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB3MQsw
+CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
+BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIENs
+YXNzIDMgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDYoWV0I+grZOIy1zM3PY71NBZI3U9/hxz4RCMTjvsR2ERaGHGOYBYmkpv9
+FwvhcXBC/r/6HMCqo6e1cej/GIP23xAKE2LIPZyn3i4/DNkd5y77Ks7Imn+Hv9hM
+BBUyydHMlXGgTihPhNk1++OGb5RT5nKKY2cuvmn2926OnGAE6yn6xEdC0niY4+wL
+pZLct5q9gGQrOHw4CVtm9i2VeoayNC6FnpAOX7ddpFFyRnATv2fytqdNFB5suVPu
+IxpOjUhVQ0GxiXVqQCjFfd3SbtICGS97JJRL6/EaqZvjI5rq+jOrCiy39GAI3Z8c
+zd0tAWaAr7MvKR0juIrhoXAHDDQPAgMBAAGjggFdMIIBWTAvBggrBgEFBQcBAQQj
+MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wEgYDVR0TAQH/BAgw
+BgEB/wIBADBlBgNVHSAEXjBcMFoGBFUdIAAwUjAmBggrBgEFBQcCARYaaHR0cDov
+L3d3dy5zeW1hdXRoLmNvbS9jcHMwKAYIKwYBBQUHAgIwHBoaaHR0cDovL3d3dy5z
+eW1hdXRoLmNvbS9ycGEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3MxLnN5bWNi
+LmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwx
+GjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTMzMB0GA1UdDgQWBBQBWavn3ToLWaZk
+Y9bPIAdX1ZHnajAfBgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkq
+hkiG9w0BAQsFAAOCAQEAQgFVe9AWGl1Y6LubqE3X89frE5SG1n8hC0e8V5uSXU8F
+nzikEHzPg74GQ0aNCLxq1xCm+quvL2GoY/Jl339MiBKIT7Np2f8nwAqXkY9W+4nE
+qLuSLRtzsMarNvSWbCAI7woeZiRFT2cAQMgHVHQzO6atuyOfZu2iRHA0+w7qAf3P
+eHTfp61Vt19N9tY/4IbOJMdCqRMURDVLtt/JYKwMf9mTIUvunORJApjTYHtcvNUw
+LwfORELEC5n+5p/8sHiGUW3RLJ3GlvuFgrsEL/digO9i2n/2DqyQuFa9eT/ygG6j
+2bkPXToHHZGThkspTOHcteHgM52zyzaRS/6htO7w+Q==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert42[] = {
+ 0x30, 0x82, 0x05, 0x2b, 0x30, 0x82, 0x04, 0x13, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x7e, 0xe1, 0x4a, 0x6f, 0x6f, 0xef, 0xf2, 0xd3, 0x7f,
+ 0x3f, 0xad, 0x65, 0x4d, 0x3a, 0xda, 0xb4, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, 0x30, 0x33, 0x30,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x77, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d,
+ 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d,
+ 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63,
+ 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f,
+ 0x72, 0x6b, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x1f, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x56, 0x20, 0x53, 0x53, 0x4c,
+ 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xd8, 0xa1, 0x65, 0x74, 0x23, 0xe8, 0x2b,
+ 0x64, 0xe2, 0x32, 0xd7, 0x33, 0x37, 0x3d, 0x8e, 0xf5, 0x34, 0x16, 0x48,
+ 0xdd, 0x4f, 0x7f, 0x87, 0x1c, 0xf8, 0x44, 0x23, 0x13, 0x8e, 0xfb, 0x11,
+ 0xd8, 0x44, 0x5a, 0x18, 0x71, 0x8e, 0x60, 0x16, 0x26, 0x92, 0x9b, 0xfd,
+ 0x17, 0x0b, 0xe1, 0x71, 0x70, 0x42, 0xfe, 0xbf, 0xfa, 0x1c, 0xc0, 0xaa,
+ 0xa3, 0xa7, 0xb5, 0x71, 0xe8, 0xff, 0x18, 0x83, 0xf6, 0xdf, 0x10, 0x0a,
+ 0x13, 0x62, 0xc8, 0x3d, 0x9c, 0xa7, 0xde, 0x2e, 0x3f, 0x0c, 0xd9, 0x1d,
+ 0xe7, 0x2e, 0xfb, 0x2a, 0xce, 0xc8, 0x9a, 0x7f, 0x87, 0xbf, 0xd8, 0x4c,
+ 0x04, 0x15, 0x32, 0xc9, 0xd1, 0xcc, 0x95, 0x71, 0xa0, 0x4e, 0x28, 0x4f,
+ 0x84, 0xd9, 0x35, 0xfb, 0xe3, 0x86, 0x6f, 0x94, 0x53, 0xe6, 0x72, 0x8a,
+ 0x63, 0x67, 0x2e, 0xbe, 0x69, 0xf6, 0xf7, 0x6e, 0x8e, 0x9c, 0x60, 0x04,
+ 0xeb, 0x29, 0xfa, 0xc4, 0x47, 0x42, 0xd2, 0x78, 0x98, 0xe3, 0xec, 0x0b,
+ 0xa5, 0x92, 0xdc, 0xb7, 0x9a, 0xbd, 0x80, 0x64, 0x2b, 0x38, 0x7c, 0x38,
+ 0x09, 0x5b, 0x66, 0xf6, 0x2d, 0x95, 0x7a, 0x86, 0xb2, 0x34, 0x2e, 0x85,
+ 0x9e, 0x90, 0x0e, 0x5f, 0xb7, 0x5d, 0xa4, 0x51, 0x72, 0x46, 0x70, 0x13,
+ 0xbf, 0x67, 0xf2, 0xb6, 0xa7, 0x4d, 0x14, 0x1e, 0x6c, 0xb9, 0x53, 0xee,
+ 0x23, 0x1a, 0x4e, 0x8d, 0x48, 0x55, 0x43, 0x41, 0xb1, 0x89, 0x75, 0x6a,
+ 0x40, 0x28, 0xc5, 0x7d, 0xdd, 0xd2, 0x6e, 0xd2, 0x02, 0x19, 0x2f, 0x7b,
+ 0x24, 0x94, 0x4b, 0xeb, 0xf1, 0x1a, 0xa9, 0x9b, 0xe3, 0x23, 0x9a, 0xea,
+ 0xfa, 0x33, 0xab, 0x0a, 0x2c, 0xb7, 0xf4, 0x60, 0x08, 0xdd, 0x9f, 0x1c,
+ 0xcd, 0xdd, 0x2d, 0x01, 0x66, 0x80, 0xaf, 0xb3, 0x2f, 0x29, 0x1d, 0x23,
+ 0xb8, 0x8a, 0xe1, 0xa1, 0x70, 0x07, 0x0c, 0x34, 0x0f, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5d, 0x30, 0x82, 0x01, 0x59, 0x30, 0x2f,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23,
+ 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
+ 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x65, 0x06, 0x03, 0x55,
+ 0x1d, 0x20, 0x04, 0x5e, 0x30, 0x5c, 0x30, 0x5a, 0x06, 0x04, 0x55, 0x1d,
+ 0x20, 0x00, 0x30, 0x52, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73,
+ 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72,
+ 0x70, 0x61, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30,
+ 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x29, 0x06, 0x03,
+ 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31,
+ 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79,
+ 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d,
+ 0x35, 0x33, 0x33, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0x01, 0x59, 0xab, 0xe7, 0xdd, 0x3a, 0x0b, 0x59, 0xa6, 0x64,
+ 0x63, 0xd6, 0xcf, 0x20, 0x07, 0x57, 0xd5, 0x91, 0xe7, 0x6a, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f,
+ 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43,
+ 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x42, 0x01, 0x55, 0x7b, 0xd0, 0x16, 0x1a, 0x5d, 0x58,
+ 0xe8, 0xbb, 0x9b, 0xa8, 0x4d, 0xd7, 0xf3, 0xd7, 0xeb, 0x13, 0x94, 0x86,
+ 0xd6, 0x7f, 0x21, 0x0b, 0x47, 0xbc, 0x57, 0x9b, 0x92, 0x5d, 0x4f, 0x05,
+ 0x9f, 0x38, 0xa4, 0x10, 0x7c, 0xcf, 0x83, 0xbe, 0x06, 0x43, 0x46, 0x8d,
+ 0x08, 0xbc, 0x6a, 0xd7, 0x10, 0xa6, 0xfa, 0xab, 0xaf, 0x2f, 0x61, 0xa8,
+ 0x63, 0xf2, 0x65, 0xdf, 0x7f, 0x4c, 0x88, 0x12, 0x88, 0x4f, 0xb3, 0x69,
+ 0xd9, 0xff, 0x27, 0xc0, 0x0a, 0x97, 0x91, 0x8f, 0x56, 0xfb, 0x89, 0xc4,
+ 0xa8, 0xbb, 0x92, 0x2d, 0x1b, 0x73, 0xb0, 0xc6, 0xab, 0x36, 0xf4, 0x96,
+ 0x6c, 0x20, 0x08, 0xef, 0x0a, 0x1e, 0x66, 0x24, 0x45, 0x4f, 0x67, 0x00,
+ 0x40, 0xc8, 0x07, 0x54, 0x74, 0x33, 0x3b, 0xa6, 0xad, 0xbb, 0x23, 0x9f,
+ 0x66, 0xed, 0xa2, 0x44, 0x70, 0x34, 0xfb, 0x0e, 0xea, 0x01, 0xfd, 0xcf,
+ 0x78, 0x74, 0xdf, 0xa7, 0xad, 0x55, 0xb7, 0x5f, 0x4d, 0xf6, 0xd6, 0x3f,
+ 0xe0, 0x86, 0xce, 0x24, 0xc7, 0x42, 0xa9, 0x13, 0x14, 0x44, 0x35, 0x4b,
+ 0xb6, 0xdf, 0xc9, 0x60, 0xac, 0x0c, 0x7f, 0xd9, 0x93, 0x21, 0x4b, 0xee,
+ 0x9c, 0xe4, 0x49, 0x02, 0x98, 0xd3, 0x60, 0x7b, 0x5c, 0xbc, 0xd5, 0x30,
+ 0x2f, 0x07, 0xce, 0x44, 0x42, 0xc4, 0x0b, 0x99, 0xfe, 0xe6, 0x9f, 0xfc,
+ 0xb0, 0x78, 0x86, 0x51, 0x6d, 0xd1, 0x2c, 0x9d, 0xc6, 0x96, 0xfb, 0x85,
+ 0x82, 0xbb, 0x04, 0x2f, 0xf7, 0x62, 0x80, 0xef, 0x62, 0xda, 0x7f, 0xf6,
+ 0x0e, 0xac, 0x90, 0xb8, 0x56, 0xbd, 0x79, 0x3f, 0xf2, 0x80, 0x6e, 0xa3,
+ 0xd9, 0xb9, 0x0f, 0x5d, 0x3a, 0x07, 0x1d, 0x91, 0x93, 0x86, 0x4b, 0x29,
+ 0x4c, 0xe1, 0xdc, 0xb5, 0xe1, 0xe0, 0x33, 0x9d, 0xb3, 0xcb, 0x36, 0x91,
+ 0x4b, 0xfe, 0xa1, 0xb4, 0xee, 0xf0, 0xf9,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 51:3f:b9:74:38:70:b7:34:40:41:8d:30:93:06:99:ff
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 Secure Server CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b2:d8:05:ca:1c:74:2d:b5:17:56:39:c5:4a:52:
+ 09:96:e8:4b:d8:0c:f1:68:9f:9a:42:28:62:c3:a5:
+ 30:53:7e:55:11:82:5b:03:7a:0d:2f:e1:79:04:c9:
+ b4:96:77:19:81:01:94:59:f9:bc:f7:7a:99:27:82:
+ 2d:b7:83:dd:5a:27:7f:b2:03:7a:9c:53:25:e9:48:
+ 1f:46:4f:c8:9d:29:f8:be:79:56:f6:f7:fd:d9:3a:
+ 68:da:8b:4b:82:33:41:12:c3:c8:3c:cc:d6:96:7a:
+ 84:21:1a:22:04:03:27:17:8b:1c:68:61:93:0f:0e:
+ 51:80:33:1d:b4:b5:ce:eb:7e:d0:62:ac:ee:b3:7b:
+ 01:74:ef:69:35:eb:ca:d5:3d:a9:ee:97:98:ca:8d:
+ aa:44:0e:25:99:4a:15:96:a4:ce:6d:02:54:1f:2a:
+ 6a:26:e2:06:3a:63:48:ac:b4:4c:d1:75:93:50:ff:
+ 13:2f:d6:da:e1:c6:18:f5:9f:c9:25:5d:f3:00:3a:
+ de:26:4d:b4:29:09:cd:0f:3d:23:6f:16:4a:81:16:
+ fb:f2:83:10:c3:b8:d6:d8:55:32:3d:f1:bd:0f:bd:
+ 8c:52:95:4a:16:97:7a:52:21:63:75:2f:16:f9:c4:
+ 66:be:f5:b5:09:d8:ff:27:00:cd:44:7c:6f:4b:3f:
+ b0:f7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://s1.symcb.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://s2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.symauth.com/cps
+ User Notice:
+ Explicit Text: http://www.symauth.com/rpa
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-534
+ X509v3 Subject Key Identifier:
+ 5F:60:CF:61:90:55:DF:84:43:14:8A:60:2A:B2:F5:7A:F4:43:18:EF
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 5e:94:56:49:dd:8e:2d:65:f5:c1:36:51:b6:03:e3:da:9e:73:
+ 19:f2:1f:59:ab:58:7e:6c:26:05:2c:fa:81:d7:5c:23:17:22:
+ 2c:37:93:f7:86:ec:85:e6:b0:a3:fd:1f:e2:32:a8:45:6f:e1:
+ d9:fb:b9:af:d2:70:a0:32:42:65:bf:84:fe:16:2a:8f:3f:c5:
+ a6:d6:a3:93:7d:43:e9:74:21:91:35:28:f4:63:e9:2e:ed:f7:
+ f5:5c:7f:4b:9a:b5:20:e9:0a:bd:e0:45:10:0c:14:94:9a:5d:
+ a5:e3:4b:91:e8:24:9b:46:40:65:f4:22:72:cd:99:f8:88:11:
+ f5:f3:7f:e6:33:82:e6:a8:c5:7e:fe:d0:08:e2:25:58:08:71:
+ 68:e6:cd:a2:e6:14:de:4e:52:24:2d:fd:e5:79:13:53:e7:5e:
+ 2f:2d:4d:1b:6d:40:15:52:2b:f7:87:89:78:12:81:6e:d9:4d:
+ aa:2d:78:d4:c2:2c:3d:08:5f:87:91:9e:1f:0e:b0:de:30:52:
+ 64:86:89:aa:9d:66:9c:0e:76:0c:80:f2:74:d8:2a:f8:b8:3a:
+ ce:d7:d6:0f:11:be:6b:ab:14:f5:bd:41:a0:22:63:89:f1:ba:
+ 0f:6f:29:63:66:2d:3f:ac:8c:72:c5:fb:c7:e4:d4:0f:f2:3b:
+ 4f:8c:29:c7
+-----BEGIN CERTIFICATE-----
+MIIFODCCBCCgAwIBAgIQUT+5dDhwtzRAQY0wkwaZ/zANBgkqhkiG9w0BAQsFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB+MQsw
+CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
+BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxLzAtBgNVBAMTJlN5bWFudGVjIENs
+YXNzIDMgU2VjdXJlIFNlcnZlciBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAstgFyhx0LbUXVjnFSlIJluhL2AzxaJ+aQihiw6UwU35VEYJb
+A3oNL+F5BMm0lncZgQGUWfm893qZJ4Itt4PdWid/sgN6nFMl6UgfRk/InSn4vnlW
+9vf92Tpo2otLgjNBEsPIPMzWlnqEIRoiBAMnF4scaGGTDw5RgDMdtLXO637QYqzu
+s3sBdO9pNevK1T2p7peYyo2qRA4lmUoVlqTObQJUHypqJuIGOmNIrLRM0XWTUP8T
+L9ba4cYY9Z/JJV3zADreJk20KQnNDz0jbxZKgRb78oMQw7jW2FUyPfG9D72MUpVK
+Fpd6UiFjdS8W+cRmvvW1Cdj/JwDNRHxvSz+w9wIDAQABo4IBYzCCAV8wEgYDVR0T
+AQH/BAgwBgEB/wIBADAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vczEuc3ltY2Iu
+Y29tL3BjYTMtZzUuY3JsMA4GA1UdDwEB/wQEAwIBBjAvBggrBgEFBQcBAQQjMCEw
+HwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wawYDVR0gBGQwYjBgBgpg
+hkgBhvhFAQc2MFIwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20v
+Y3BzMCgGCCsGAQUFBwICMBwaGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20vcnBhMCkG
+A1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTUzNDAdBgNVHQ4E
+FgQUX2DPYZBV34RDFIpgKrL1evRDGO8wHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnz
+Qzn6Aq8zMTMwDQYJKoZIhvcNAQELBQADggEBAF6UVkndji1l9cE2UbYD49qecxny
+H1mrWH5sJgUs+oHXXCMXIiw3k/eG7IXmsKP9H+IyqEVv4dn7ua/ScKAyQmW/hP4W
+Ko8/xabWo5N9Q+l0IZE1KPRj6S7t9/Vcf0uatSDpCr3gRRAMFJSaXaXjS5HoJJtG
+QGX0InLNmfiIEfXzf+YzguaoxX7+0AjiJVgIcWjmzaLmFN5OUiQt/eV5E1PnXi8t
+TRttQBVSK/eHiXgSgW7ZTaoteNTCLD0IX4eRnh8OsN4wUmSGiaqdZpwOdgyA8nTY
+Kvi4Os7X1g8RvmurFPW9QaAiY4nxug9vKWNmLT+sjHLF+8fk1A/yO0+MKcc=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert43[] = {
+ 0x30, 0x82, 0x05, 0x38, 0x30, 0x82, 0x04, 0x20, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x51, 0x3f, 0xb9, 0x74, 0x38, 0x70, 0xb7, 0x34, 0x40,
+ 0x41, 0x8d, 0x30, 0x93, 0x06, 0x99, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, 0x30, 0x33, 0x30,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x7e, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d,
+ 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d,
+ 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63,
+ 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f,
+ 0x72, 0x6b, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x26, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65,
+ 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x2d,
+ 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xb2, 0xd8, 0x05, 0xca, 0x1c, 0x74, 0x2d, 0xb5, 0x17, 0x56, 0x39, 0xc5,
+ 0x4a, 0x52, 0x09, 0x96, 0xe8, 0x4b, 0xd8, 0x0c, 0xf1, 0x68, 0x9f, 0x9a,
+ 0x42, 0x28, 0x62, 0xc3, 0xa5, 0x30, 0x53, 0x7e, 0x55, 0x11, 0x82, 0x5b,
+ 0x03, 0x7a, 0x0d, 0x2f, 0xe1, 0x79, 0x04, 0xc9, 0xb4, 0x96, 0x77, 0x19,
+ 0x81, 0x01, 0x94, 0x59, 0xf9, 0xbc, 0xf7, 0x7a, 0x99, 0x27, 0x82, 0x2d,
+ 0xb7, 0x83, 0xdd, 0x5a, 0x27, 0x7f, 0xb2, 0x03, 0x7a, 0x9c, 0x53, 0x25,
+ 0xe9, 0x48, 0x1f, 0x46, 0x4f, 0xc8, 0x9d, 0x29, 0xf8, 0xbe, 0x79, 0x56,
+ 0xf6, 0xf7, 0xfd, 0xd9, 0x3a, 0x68, 0xda, 0x8b, 0x4b, 0x82, 0x33, 0x41,
+ 0x12, 0xc3, 0xc8, 0x3c, 0xcc, 0xd6, 0x96, 0x7a, 0x84, 0x21, 0x1a, 0x22,
+ 0x04, 0x03, 0x27, 0x17, 0x8b, 0x1c, 0x68, 0x61, 0x93, 0x0f, 0x0e, 0x51,
+ 0x80, 0x33, 0x1d, 0xb4, 0xb5, 0xce, 0xeb, 0x7e, 0xd0, 0x62, 0xac, 0xee,
+ 0xb3, 0x7b, 0x01, 0x74, 0xef, 0x69, 0x35, 0xeb, 0xca, 0xd5, 0x3d, 0xa9,
+ 0xee, 0x97, 0x98, 0xca, 0x8d, 0xaa, 0x44, 0x0e, 0x25, 0x99, 0x4a, 0x15,
+ 0x96, 0xa4, 0xce, 0x6d, 0x02, 0x54, 0x1f, 0x2a, 0x6a, 0x26, 0xe2, 0x06,
+ 0x3a, 0x63, 0x48, 0xac, 0xb4, 0x4c, 0xd1, 0x75, 0x93, 0x50, 0xff, 0x13,
+ 0x2f, 0xd6, 0xda, 0xe1, 0xc6, 0x18, 0xf5, 0x9f, 0xc9, 0x25, 0x5d, 0xf3,
+ 0x00, 0x3a, 0xde, 0x26, 0x4d, 0xb4, 0x29, 0x09, 0xcd, 0x0f, 0x3d, 0x23,
+ 0x6f, 0x16, 0x4a, 0x81, 0x16, 0xfb, 0xf2, 0x83, 0x10, 0xc3, 0xb8, 0xd6,
+ 0xd8, 0x55, 0x32, 0x3d, 0xf1, 0xbd, 0x0f, 0xbd, 0x8c, 0x52, 0x95, 0x4a,
+ 0x16, 0x97, 0x7a, 0x52, 0x21, 0x63, 0x75, 0x2f, 0x16, 0xf9, 0xc4, 0x66,
+ 0xbe, 0xf5, 0xb5, 0x09, 0xd8, 0xff, 0x27, 0x00, 0xcd, 0x44, 0x7c, 0x6f,
+ 0x4b, 0x3f, 0xb0, 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0x63, 0x30, 0x82, 0x01, 0x5f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27,
+ 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x73, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23, 0x30, 0x21, 0x30,
+ 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86,
+ 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x32, 0x2e, 0x73,
+ 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x6b, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x64, 0x30, 0x62, 0x30, 0x60, 0x06, 0x0a, 0x60,
+ 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x52, 0x30,
+ 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74,
+ 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x29, 0x06,
+ 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c,
+ 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53,
+ 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31,
+ 0x2d, 0x35, 0x33, 0x34, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x5f, 0x60, 0xcf, 0x61, 0x90, 0x55, 0xdf, 0x84, 0x43,
+ 0x14, 0x8a, 0x60, 0x2a, 0xb2, 0xf5, 0x7a, 0xf4, 0x43, 0x18, 0xef, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x5e, 0x94, 0x56, 0x49, 0xdd, 0x8e, 0x2d, 0x65,
+ 0xf5, 0xc1, 0x36, 0x51, 0xb6, 0x03, 0xe3, 0xda, 0x9e, 0x73, 0x19, 0xf2,
+ 0x1f, 0x59, 0xab, 0x58, 0x7e, 0x6c, 0x26, 0x05, 0x2c, 0xfa, 0x81, 0xd7,
+ 0x5c, 0x23, 0x17, 0x22, 0x2c, 0x37, 0x93, 0xf7, 0x86, 0xec, 0x85, 0xe6,
+ 0xb0, 0xa3, 0xfd, 0x1f, 0xe2, 0x32, 0xa8, 0x45, 0x6f, 0xe1, 0xd9, 0xfb,
+ 0xb9, 0xaf, 0xd2, 0x70, 0xa0, 0x32, 0x42, 0x65, 0xbf, 0x84, 0xfe, 0x16,
+ 0x2a, 0x8f, 0x3f, 0xc5, 0xa6, 0xd6, 0xa3, 0x93, 0x7d, 0x43, 0xe9, 0x74,
+ 0x21, 0x91, 0x35, 0x28, 0xf4, 0x63, 0xe9, 0x2e, 0xed, 0xf7, 0xf5, 0x5c,
+ 0x7f, 0x4b, 0x9a, 0xb5, 0x20, 0xe9, 0x0a, 0xbd, 0xe0, 0x45, 0x10, 0x0c,
+ 0x14, 0x94, 0x9a, 0x5d, 0xa5, 0xe3, 0x4b, 0x91, 0xe8, 0x24, 0x9b, 0x46,
+ 0x40, 0x65, 0xf4, 0x22, 0x72, 0xcd, 0x99, 0xf8, 0x88, 0x11, 0xf5, 0xf3,
+ 0x7f, 0xe6, 0x33, 0x82, 0xe6, 0xa8, 0xc5, 0x7e, 0xfe, 0xd0, 0x08, 0xe2,
+ 0x25, 0x58, 0x08, 0x71, 0x68, 0xe6, 0xcd, 0xa2, 0xe6, 0x14, 0xde, 0x4e,
+ 0x52, 0x24, 0x2d, 0xfd, 0xe5, 0x79, 0x13, 0x53, 0xe7, 0x5e, 0x2f, 0x2d,
+ 0x4d, 0x1b, 0x6d, 0x40, 0x15, 0x52, 0x2b, 0xf7, 0x87, 0x89, 0x78, 0x12,
+ 0x81, 0x6e, 0xd9, 0x4d, 0xaa, 0x2d, 0x78, 0xd4, 0xc2, 0x2c, 0x3d, 0x08,
+ 0x5f, 0x87, 0x91, 0x9e, 0x1f, 0x0e, 0xb0, 0xde, 0x30, 0x52, 0x64, 0x86,
+ 0x89, 0xaa, 0x9d, 0x66, 0x9c, 0x0e, 0x76, 0x0c, 0x80, 0xf2, 0x74, 0xd8,
+ 0x2a, 0xf8, 0xb8, 0x3a, 0xce, 0xd7, 0xd6, 0x0f, 0x11, 0xbe, 0x6b, 0xab,
+ 0x14, 0xf5, 0xbd, 0x41, 0xa0, 0x22, 0x63, 0x89, 0xf1, 0xba, 0x0f, 0x6f,
+ 0x29, 0x63, 0x66, 0x2d, 0x3f, 0xac, 0x8c, 0x72, 0xc5, 0xfb, 0xc7, 0xe4,
+ 0xd4, 0x0f, 0xf2, 0x3b, 0x4f, 0x8c, 0x29, 0xc7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120036009 (0x7279aa9)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Dec 19 20:07:32 2013 GMT
+ Not After : Dec 19 20:06:55 2017 GMT
+ Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT SSL SHA2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:d1:e8:37:a7:76:8a:70:4b:19:f0:20:37:09:24:
+ 37:7f:ea:fb:78:e6:05:ba:6a:ad:4e:27:0d:fc:72:
+ 6a:d9:6c:21:c4:64:11:95:73:10:0a:5c:25:7b:88:
+ 6c:94:04:fd:c7:db:ae:7b:dc:4a:08:b3:3e:16:f1:
+ d0:ad:db:30:6d:d7:1a:1e:52:b5:3d:f0:47:19:03:
+ e2:7d:a6:bd:57:13:3f:54:ea:3a:a3:b1:77:fc:42:
+ f0:63:49:6a:91:80:2e:30:49:c0:8a:eb:2b:af:fe:
+ 3a:eb:07:5d:06:f7:e9:fd:84:0e:91:bd:09:20:29:
+ e8:6e:5d:09:ce:15:d3:e7:ef:db:50:eb:44:ef:18:
+ 57:ab:04:1d:bc:31:f9:f7:7b:2a:13:cf:d1:3d:51:
+ af:1b:c5:b5:7b:e7:b0:fc:53:bb:9a:e7:63:de:41:
+ 33:b6:47:24:69:5d:b8:46:a7:ff:ad:ab:df:4f:7a:
+ 78:25:27:21:26:34:ca:02:6e:37:51:f0:ed:58:1a:
+ 60:94:f6:c4:93:d8:dd:30:24:25:d7:1c:eb:19:94:
+ 35:5d:93:b2:ae:aa:29:83:73:c4:74:59:05:52:67:
+ 9d:da:67:51:39:05:3a:36:ea:f2:1e:76:2b:14:ae:
+ ec:3d:f9:14:99:8b:07:6e:bc:e7:0c:56:de:ac:be:
+ ae:db:75:32:90:9e:63:bd:74:bf:e0:0a:ca:f8:34:
+ 96:67:84:cd:d1:42:38:78:c7:99:b6:0c:ce:b6:0f:
+ e9:1b:cb:f4:59:be:11:0e:cb:2c:32:c8:fa:83:29:
+ 64:79:3c:8b:4b:f0:32:74:6c:f3:93:b8:96:6b:5d:
+ 57:5a:68:c1:cc:0c:79:8a:19:de:f5:49:02:5e:08:
+ 80:01:89:0c:32:cd:d2:d6:96:d5:4b:a0:f3:ec:bf:
+ ab:f4:7d:b3:a1:b9:7c:da:4e:d7:e5:b7:ac:b9:f2:
+ 25:5f:01:cb:8c:96:a8:28:ae:c1:33:5a:f6:3f:08:
+ 90:dc:eb:ff:39:d8:26:c8:12:9d:1c:9a:aa:a9:c0:
+ 16:8e:86:ed:67:52:96:00:7f:0d:92:3d:3d:d9:70:
+ 36:e5:ea:42:6f:1f:ae:95:e5:5b:5d:f8:d0:3a:c7:
+ d4:de:77:86:d0:fc:9e:4e:e2:e2:b8:a9:68:37:09:
+ c4:39:e3:85:b8:89:f3:1f:6e:b7:6d:1f:4a:2f:18:
+ 09:6f:de:4a:01:8f:14:c9:b7:a6:ee:a7:63:9f:33:
+ a4:54:7c:42:83:68:b8:a5:df:bf:ec:b9:1a:5d:13:
+ 3b:d9:ad:68:fd:20:0a:55:91:21:64:f9:d7:13:01:
+ a0:08:5d:59:89:1b:44:af:a4:ac:c7:05:10:fa:41:
+ 4a:a8:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 51:AF:24:26:9C:F4:68:22:57:80:26:2B:3B:46:62:15:7B:1E:CC:A5
+ Signature Algorithm: sha256WithRSAEncryption
+ 76:85:c5:23:31:1f:b4:73:ea:a0:bc:a5:ed:df:45:43:6a:7f:
+ 69:20:1b:80:b2:fb:1c:dd:aa:7f:88:d3:31:41:36:f7:fb:fb:
+ 6b:ad:98:8c:78:1f:9d:11:67:3a:cd:4b:ec:a8:bc:9d:15:19:
+ c4:3b:0b:a7:93:ce:e8:fc:9d:5b:e8:1f:cb:56:ae:76:43:2b:
+ c7:13:51:77:41:a8:66:4c:5f:a7:d1:d7:aa:75:c5:1b:29:4c:
+ c9:f4:6d:a1:5e:a1:85:93:16:c2:cb:3b:ab:14:7d:44:fd:da:
+ 25:29:86:2a:fe:63:20:ca:d2:0b:c2:34:15:bb:af:5b:7f:8a:
+ e0:aa:ed:45:a6:ea:79:db:d8:35:66:54:43:de:37:33:d1:e4:
+ e0:cd:57:ca:71:b0:7d:e9:16:77:64:e8:59:97:b9:d5:2e:d1:
+ b4:91:da:77:71:f3:4a:0f:48:d2:34:99:60:95:37:ac:1f:01:
+ cd:10:9d:e8:2a:a5:20:c7:50:9b:b3:6c:49:78:2b:58:92:64:
+ 89:b8:95:36:a8:34:aa:f0:41:d2:95:5a:24:54:97:4d:6e:05:
+ c4:95:ad:c4:7a:a3:39:fb:79:06:8a:9b:a6:4f:d9:22:fa:44:
+ 4e:36:f3:c9:0f:a6:39:e7:80:b2:5e:bf:bd:39:d1:46:e5:55:
+ 47:db:bc:6e
+-----BEGIN CERTIFICATE-----
+MIIFhjCCBG6gAwIBAgIEByeaqTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEzMTIxOTIwMDczMloX
+DTE3MTIxOTIwMDY1NVowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
+YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0
+IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3
+p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu
+e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA
+iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R
+PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg
+lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU
+mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R
+DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV
+S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq
+qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE
+OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o
+/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCASAwggEcMBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwUwYDVR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEF
+BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku
+Y2ZtMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH
+AwIwHwYDVR0jBBgwFoAU5Z1ZMIJHWMys+ghUNoZ7OrUETfAwQgYDVR0fBDswOTA3
+oDWgM4YxaHR0cDovL2NkcDEucHVibGljLXRydXN0LmNvbS9DUkwvT21uaXJvb3Qy
+MDI1LmNybDAdBgNVHQ4EFgQUUa8kJpz0aCJXgCYrO0ZiFXsezKUwDQYJKoZIhvcN
+AQELBQADggEBAHaFxSMxH7Rz6qC8pe3fRUNqf2kgG4Cy+xzdqn+I0zFBNvf7+2ut
+mIx4H50RZzrNS+yovJ0VGcQ7C6eTzuj8nVvoH8tWrnZDK8cTUXdBqGZMX6fR16p1
+xRspTMn0baFeoYWTFsLLO6sUfUT92iUphir+YyDK0gvCNBW7r1t/iuCq7UWm6nnb
+2DVmVEPeNzPR5ODNV8pxsH3pFndk6FmXudUu0bSR2ndx80oPSNI0mWCVN6wfAc0Q
+negqpSDHUJuzbEl4K1iSZIm4lTaoNKrwQdKVWiRUl01uBcSVrcR6ozn7eQaKm6ZP
+2SL6RE4288kPpjnngLJev7050UblVUfbvG4=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert44[] = {
+ 0x30, 0x82, 0x05, 0x86, 0x30, 0x82, 0x04, 0x6e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x9a, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33,
+ 0x31, 0x32, 0x31, 0x39, 0x32, 0x30, 0x30, 0x37, 0x33, 0x32, 0x5a, 0x17,
+ 0x0d, 0x31, 0x37, 0x31, 0x32, 0x31, 0x39, 0x32, 0x30, 0x30, 0x36, 0x35,
+ 0x35, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67,
+ 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30,
+ 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72,
+ 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
+ 0x74, 0x20, 0x49, 0x54, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x49, 0x54, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32,
+ 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
+ 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xd1, 0xe8, 0x37,
+ 0xa7, 0x76, 0x8a, 0x70, 0x4b, 0x19, 0xf0, 0x20, 0x37, 0x09, 0x24, 0x37,
+ 0x7f, 0xea, 0xfb, 0x78, 0xe6, 0x05, 0xba, 0x6a, 0xad, 0x4e, 0x27, 0x0d,
+ 0xfc, 0x72, 0x6a, 0xd9, 0x6c, 0x21, 0xc4, 0x64, 0x11, 0x95, 0x73, 0x10,
+ 0x0a, 0x5c, 0x25, 0x7b, 0x88, 0x6c, 0x94, 0x04, 0xfd, 0xc7, 0xdb, 0xae,
+ 0x7b, 0xdc, 0x4a, 0x08, 0xb3, 0x3e, 0x16, 0xf1, 0xd0, 0xad, 0xdb, 0x30,
+ 0x6d, 0xd7, 0x1a, 0x1e, 0x52, 0xb5, 0x3d, 0xf0, 0x47, 0x19, 0x03, 0xe2,
+ 0x7d, 0xa6, 0xbd, 0x57, 0x13, 0x3f, 0x54, 0xea, 0x3a, 0xa3, 0xb1, 0x77,
+ 0xfc, 0x42, 0xf0, 0x63, 0x49, 0x6a, 0x91, 0x80, 0x2e, 0x30, 0x49, 0xc0,
+ 0x8a, 0xeb, 0x2b, 0xaf, 0xfe, 0x3a, 0xeb, 0x07, 0x5d, 0x06, 0xf7, 0xe9,
+ 0xfd, 0x84, 0x0e, 0x91, 0xbd, 0x09, 0x20, 0x29, 0xe8, 0x6e, 0x5d, 0x09,
+ 0xce, 0x15, 0xd3, 0xe7, 0xef, 0xdb, 0x50, 0xeb, 0x44, 0xef, 0x18, 0x57,
+ 0xab, 0x04, 0x1d, 0xbc, 0x31, 0xf9, 0xf7, 0x7b, 0x2a, 0x13, 0xcf, 0xd1,
+ 0x3d, 0x51, 0xaf, 0x1b, 0xc5, 0xb5, 0x7b, 0xe7, 0xb0, 0xfc, 0x53, 0xbb,
+ 0x9a, 0xe7, 0x63, 0xde, 0x41, 0x33, 0xb6, 0x47, 0x24, 0x69, 0x5d, 0xb8,
+ 0x46, 0xa7, 0xff, 0xad, 0xab, 0xdf, 0x4f, 0x7a, 0x78, 0x25, 0x27, 0x21,
+ 0x26, 0x34, 0xca, 0x02, 0x6e, 0x37, 0x51, 0xf0, 0xed, 0x58, 0x1a, 0x60,
+ 0x94, 0xf6, 0xc4, 0x93, 0xd8, 0xdd, 0x30, 0x24, 0x25, 0xd7, 0x1c, 0xeb,
+ 0x19, 0x94, 0x35, 0x5d, 0x93, 0xb2, 0xae, 0xaa, 0x29, 0x83, 0x73, 0xc4,
+ 0x74, 0x59, 0x05, 0x52, 0x67, 0x9d, 0xda, 0x67, 0x51, 0x39, 0x05, 0x3a,
+ 0x36, 0xea, 0xf2, 0x1e, 0x76, 0x2b, 0x14, 0xae, 0xec, 0x3d, 0xf9, 0x14,
+ 0x99, 0x8b, 0x07, 0x6e, 0xbc, 0xe7, 0x0c, 0x56, 0xde, 0xac, 0xbe, 0xae,
+ 0xdb, 0x75, 0x32, 0x90, 0x9e, 0x63, 0xbd, 0x74, 0xbf, 0xe0, 0x0a, 0xca,
+ 0xf8, 0x34, 0x96, 0x67, 0x84, 0xcd, 0xd1, 0x42, 0x38, 0x78, 0xc7, 0x99,
+ 0xb6, 0x0c, 0xce, 0xb6, 0x0f, 0xe9, 0x1b, 0xcb, 0xf4, 0x59, 0xbe, 0x11,
+ 0x0e, 0xcb, 0x2c, 0x32, 0xc8, 0xfa, 0x83, 0x29, 0x64, 0x79, 0x3c, 0x8b,
+ 0x4b, 0xf0, 0x32, 0x74, 0x6c, 0xf3, 0x93, 0xb8, 0x96, 0x6b, 0x5d, 0x57,
+ 0x5a, 0x68, 0xc1, 0xcc, 0x0c, 0x79, 0x8a, 0x19, 0xde, 0xf5, 0x49, 0x02,
+ 0x5e, 0x08, 0x80, 0x01, 0x89, 0x0c, 0x32, 0xcd, 0xd2, 0xd6, 0x96, 0xd5,
+ 0x4b, 0xa0, 0xf3, 0xec, 0xbf, 0xab, 0xf4, 0x7d, 0xb3, 0xa1, 0xb9, 0x7c,
+ 0xda, 0x4e, 0xd7, 0xe5, 0xb7, 0xac, 0xb9, 0xf2, 0x25, 0x5f, 0x01, 0xcb,
+ 0x8c, 0x96, 0xa8, 0x28, 0xae, 0xc1, 0x33, 0x5a, 0xf6, 0x3f, 0x08, 0x90,
+ 0xdc, 0xeb, 0xff, 0x39, 0xd8, 0x26, 0xc8, 0x12, 0x9d, 0x1c, 0x9a, 0xaa,
+ 0xa9, 0xc0, 0x16, 0x8e, 0x86, 0xed, 0x67, 0x52, 0x96, 0x00, 0x7f, 0x0d,
+ 0x92, 0x3d, 0x3d, 0xd9, 0x70, 0x36, 0xe5, 0xea, 0x42, 0x6f, 0x1f, 0xae,
+ 0x95, 0xe5, 0x5b, 0x5d, 0xf8, 0xd0, 0x3a, 0xc7, 0xd4, 0xde, 0x77, 0x86,
+ 0xd0, 0xfc, 0x9e, 0x4e, 0xe2, 0xe2, 0xb8, 0xa9, 0x68, 0x37, 0x09, 0xc4,
+ 0x39, 0xe3, 0x85, 0xb8, 0x89, 0xf3, 0x1f, 0x6e, 0xb7, 0x6d, 0x1f, 0x4a,
+ 0x2f, 0x18, 0x09, 0x6f, 0xde, 0x4a, 0x01, 0x8f, 0x14, 0xc9, 0xb7, 0xa6,
+ 0xee, 0xa7, 0x63, 0x9f, 0x33, 0xa4, 0x54, 0x7c, 0x42, 0x83, 0x68, 0xb8,
+ 0xa5, 0xdf, 0xbf, 0xec, 0xb9, 0x1a, 0x5d, 0x13, 0x3b, 0xd9, 0xad, 0x68,
+ 0xfd, 0x20, 0x0a, 0x55, 0x91, 0x21, 0x64, 0xf9, 0xd7, 0x13, 0x01, 0xa0,
+ 0x08, 0x5d, 0x59, 0x89, 0x1b, 0x44, 0xaf, 0xa4, 0xac, 0xc7, 0x05, 0x10,
+ 0xfa, 0x41, 0x4a, 0xa8, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x20, 0x30, 0x82, 0x01, 0x1c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30,
+ 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e,
+ 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
+ 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac,
+ 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30,
+ 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37,
+ 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
+ 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43,
+ 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32,
+ 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x51, 0xaf, 0x24, 0x26, 0x9c, 0xf4,
+ 0x68, 0x22, 0x57, 0x80, 0x26, 0x2b, 0x3b, 0x46, 0x62, 0x15, 0x7b, 0x1e,
+ 0xcc, 0xa5, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x76, 0x85,
+ 0xc5, 0x23, 0x31, 0x1f, 0xb4, 0x73, 0xea, 0xa0, 0xbc, 0xa5, 0xed, 0xdf,
+ 0x45, 0x43, 0x6a, 0x7f, 0x69, 0x20, 0x1b, 0x80, 0xb2, 0xfb, 0x1c, 0xdd,
+ 0xaa, 0x7f, 0x88, 0xd3, 0x31, 0x41, 0x36, 0xf7, 0xfb, 0xfb, 0x6b, 0xad,
+ 0x98, 0x8c, 0x78, 0x1f, 0x9d, 0x11, 0x67, 0x3a, 0xcd, 0x4b, 0xec, 0xa8,
+ 0xbc, 0x9d, 0x15, 0x19, 0xc4, 0x3b, 0x0b, 0xa7, 0x93, 0xce, 0xe8, 0xfc,
+ 0x9d, 0x5b, 0xe8, 0x1f, 0xcb, 0x56, 0xae, 0x76, 0x43, 0x2b, 0xc7, 0x13,
+ 0x51, 0x77, 0x41, 0xa8, 0x66, 0x4c, 0x5f, 0xa7, 0xd1, 0xd7, 0xaa, 0x75,
+ 0xc5, 0x1b, 0x29, 0x4c, 0xc9, 0xf4, 0x6d, 0xa1, 0x5e, 0xa1, 0x85, 0x93,
+ 0x16, 0xc2, 0xcb, 0x3b, 0xab, 0x14, 0x7d, 0x44, 0xfd, 0xda, 0x25, 0x29,
+ 0x86, 0x2a, 0xfe, 0x63, 0x20, 0xca, 0xd2, 0x0b, 0xc2, 0x34, 0x15, 0xbb,
+ 0xaf, 0x5b, 0x7f, 0x8a, 0xe0, 0xaa, 0xed, 0x45, 0xa6, 0xea, 0x79, 0xdb,
+ 0xd8, 0x35, 0x66, 0x54, 0x43, 0xde, 0x37, 0x33, 0xd1, 0xe4, 0xe0, 0xcd,
+ 0x57, 0xca, 0x71, 0xb0, 0x7d, 0xe9, 0x16, 0x77, 0x64, 0xe8, 0x59, 0x97,
+ 0xb9, 0xd5, 0x2e, 0xd1, 0xb4, 0x91, 0xda, 0x77, 0x71, 0xf3, 0x4a, 0x0f,
+ 0x48, 0xd2, 0x34, 0x99, 0x60, 0x95, 0x37, 0xac, 0x1f, 0x01, 0xcd, 0x10,
+ 0x9d, 0xe8, 0x2a, 0xa5, 0x20, 0xc7, 0x50, 0x9b, 0xb3, 0x6c, 0x49, 0x78,
+ 0x2b, 0x58, 0x92, 0x64, 0x89, 0xb8, 0x95, 0x36, 0xa8, 0x34, 0xaa, 0xf0,
+ 0x41, 0xd2, 0x95, 0x5a, 0x24, 0x54, 0x97, 0x4d, 0x6e, 0x05, 0xc4, 0x95,
+ 0xad, 0xc4, 0x7a, 0xa3, 0x39, 0xfb, 0x79, 0x06, 0x8a, 0x9b, 0xa6, 0x4f,
+ 0xd9, 0x22, 0xfa, 0x44, 0x4e, 0x36, 0xf3, 0xc9, 0x0f, 0xa6, 0x39, 0xe7,
+ 0x80, 0xb2, 0x5e, 0xbf, 0xbd, 0x39, 0xd1, 0x46, 0xe5, 0x55, 0x47, 0xdb,
+ 0xbc, 0x6e,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 67:3f:33:4f:21:53:36:52:c3:5e:15:d2:fd:b3:02:0f
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=CN, O=WoSign CA Limited, CN=Certification Authority of WoSign
+ Validity
+ Not Before: Aug 8 01:00:05 2009 GMT
+ Not After : Aug 8 01:00:05 2024 GMT
+ Subject: C=CN, O=WoSign CA Limited, CN=WoSign Class 3 OV Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bc:89:be:61:51:53:c8:2b:96:75:b3:5a:d3:0e:
+ 34:fe:4a:c2:9f:a3:18:83:a2:ac:e3:2e:5e:93:79:
+ 0b:13:49:5e:93:b2:8f:84:10:ed:91:8f:82:ba:ad:
+ 67:df:33:1b:ae:84:f2:55:b0:5b:f4:b3:9e:bc:e6:
+ 04:0f:1d:ef:04:5a:a8:0b:ec:12:6d:56:19:64:70:
+ 49:0f:57:92:f3:5f:21:a6:4d:b4:d2:96:2b:3c:32:
+ b3:ef:8f:59:0b:14:ba:6e:a2:9e:71:db:f2:88:3f:
+ 28:3b:ec:ce:be:47:ac:45:c7:8a:9e:fa:61:93:c5:
+ 49:17:b6:46:b6:f7:99:16:8c:1c:6e:31:ae:69:ce:
+ ed:c6:24:92:70:a1:cb:96:c3:6c:16:d0:ee:cc:4f:
+ 86:33:b3:41:e6:3d:3d:db:0e:8c:33:74:bb:c3:fc:
+ 0b:a7:fc:d1:71:e2:c1:0c:d4:f7:ba:3e:80:90:d4:
+ 48:eb:a2:83:70:d8:db:30:07:29:89:f9:81:21:2c:
+ ff:eb:47:f6:7a:6d:43:96:67:17:3e:f3:e2:73:51:
+ c7:76:1e:e9:1c:a0:ec:11:1a:b1:cf:1e:2d:9c:55:
+ ee:3b:c6:2d:ae:dc:66:65:91:a2:66:9c:ac:82:f1:
+ a4:17:b5:d7:43:83:c3:88:a0:64:de:ca:72:45:dc:
+ 38:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication, TLS Web Server Authentication
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crls1.wosign.com/ca1.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp1.wosign.com/ca1
+ CA Issuers - URI:http://aia1.wosign.com/ca1-class3-server.cer
+
+ X509v3 Subject Key Identifier:
+ 62:2E:81:D9:E3:42:79:14:A3:CD:D9:54:8A:6E:F8:DE:95:AA:8F:98
+ X509v3 Authority Key Identifier:
+ keyid:E1:66:CF:0E:D1:F1:B3:4B:B7:06:20:14:FE:87:12:D5:F6:FE:FB:3E
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.36305.1.3.2
+ CPS: http://www.wosign.com/policy/
+
+ Signature Algorithm: sha1WithRSAEncryption
+ ab:70:aa:64:c4:0b:34:91:b9:63:20:5e:b0:9c:21:ff:25:79:
+ 6c:57:4e:56:44:58:83:b9:00:ce:2d:65:a8:6d:95:38:ea:82:
+ 2d:55:18:60:12:7e:1a:1d:6b:62:34:2c:d9:cd:17:00:43:84:
+ 3e:ad:bc:ff:26:85:1f:4a:a7:46:13:b0:7d:3b:0b:d9:4b:9d:
+ b0:cf:8d:f4:05:cb:12:29:fe:e1:97:c7:b7:c7:aa:53:7e:39:
+ 2d:9d:f6:d4:5e:b7:8c:15:6a:81:d2:37:1a:43:0e:cb:e6:30:
+ 21:43:83:69:0f:ef:6b:cd:10:f9:84:60:cf:89:e9:88:10:01:
+ af:09:f3:48:bb:07:09:75:01:84:fa:b1:1e:51:19:8f:c6:c9:
+ 85:65:16:5f:e0:56:7e:b7:bf:40:c2:d4:d0:05:1f:93:63:c9:
+ 24:08:3b:91:b2:35:e1:a4:8f:35:db:24:58:75:39:e4:dd:10:
+ 1a:b0:df:13:12:73:9e:6d:e7:67:3c:db:1c:1c:dd:10:dd:cc:
+ f4:07:09:b9:2e:e5:75:6d:97:b7:60:5b:89:70:81:d2:26:d8:
+ c6:09:2b:b2:05:7f:c4:b8:14:41:1e:07:f0:48:41:63:cb:0c:
+ aa:45:7e:84:f9:33:b3:58:87:bc:b1:d6:c2:65:c7:57:c6:95:
+ e8:85:90:b0:62:50:f5:ee:12:f1:d8:7e:73:cb:c0:c3:a0:25:
+ 17:23:37:91:ba:63:bd:84:af:f3:89:e0:51:c2:73:35:6d:63:
+ 86:21:f2:73:bd:c2:47:e0:4d:7e:46:37:4b:d0:f7:61:2a:c7:
+ 94:50:25:36:e8:ae:da:2e:1f:b8:08:b2:55:7c:6b:66:43:8f:
+ 02:1d:dd:a7:eb:98:00:a7:25:74:f5:93:1b:6d:26:bb:1d:e5:
+ b7:fc:21:25:26:d1:77:1b:a8:6e:aa:c3:4b:64:51:7f:91:0e:
+ 41:5c:19:83:a1:a8:1f:94:99:43:0f:99:db:18:dc:21:6f:76:
+ d1:9e:ea:a3:76:e0:f0:09:bc:b9:b4:f7:43:6c:1f:d3:2a:86:
+ 6a:2f:e0:6c:f1:83:39:d7:70:db:a2:91:ab:54:be:f4:47:88:
+ 8c:f0:10:d2:e4:ad:eb:7e:b1:ba:08:4b:67:04:a3:f2:e9:90:
+ 2b:81:e3:74:76:3d:00:9d:d2:bb:fc:a5:a0:15:1c:28:df:10:
+ 4f:47:d7:33:46:9d:b2:57:d2:c6:1f:fb:e4:59:4a:2b:28:a9:
+ 13:dd:b9:e9:93:b4:88:ee:e2:5b:a0:07:25:fe:8a:2e:78:e4:
+ b4:e1:d5:1d:f6:1a:3a:e3:1c:01:2a:1e:a1:86:54:9e:49:dc:
+ c9:59:e3:0d:6d:5a:13:36
+-----BEGIN CERTIFICATE-----
+MIIFozCCA4ugAwIBAgIQZz8zTyFTNlLDXhXS/bMCDzANBgkqhkiG9w0BAQUFADBV
+MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV
+BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw
+MTAwMDVaFw0yNDA4MDgwMTAwMDVaME8xCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX
+b1NpZ24gQ0EgTGltaXRlZDEkMCIGA1UEAxMbV29TaWduIENsYXNzIDMgT1YgU2Vy
+dmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvIm+YVFTyCuW
+dbNa0w40/krCn6MYg6Ks4y5ek3kLE0lek7KPhBDtkY+Cuq1n3zMbroTyVbBb9LOe
+vOYEDx3vBFqoC+wSbVYZZHBJD1eS818hpk200pYrPDKz749ZCxS6bqKecdvyiD8o
+O+zOvkesRceKnvphk8VJF7ZGtveZFowcbjGuac7txiSScKHLlsNsFtDuzE+GM7NB
+5j092w6MM3S7w/wLp/zRceLBDNT3uj6AkNRI66KDcNjbMAcpifmBISz/60f2em1D
+lmcXPvPic1HHdh7pHKDsERqxzx4tnFXuO8YtrtxmZZGiZpysgvGkF7XXQ4PDiKBk
+3spyRdw4+wIDAQABo4IBczCCAW8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdJQQWMBQG
+CCsGAQUFBwMCBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMDAGA1UdHwQp
+MCcwJaAjoCGGH2h0dHA6Ly9jcmxzMS53b3NpZ24uY29tL2NhMS5jcmwwcQYIKwYB
+BQUHAQEEZTBjMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcDEud29zaWduLmNvbS9j
+YTEwOAYIKwYBBQUHMAKGLGh0dHA6Ly9haWExLndvc2lnbi5jb20vY2ExLWNsYXNz
+My1zZXJ2ZXIuY2VyMB0GA1UdDgQWBBRiLoHZ40J5FKPN2VSKbvjelaqPmDAfBgNV
+HSMEGDAWgBThZs8O0fGzS7cGIBT+hxLV9v77PjBFBgNVHSAEPjA8MDoGCysGAQQB
+gptRAQMCMCswKQYIKwYBBQUHAgEWHWh0dHA6Ly93d3cud29zaWduLmNvbS9wb2xp
+Y3kvMA0GCSqGSIb3DQEBBQUAA4ICAQCrcKpkxAs0kbljIF6wnCH/JXlsV05WRFiD
+uQDOLWWobZU46oItVRhgEn4aHWtiNCzZzRcAQ4Q+rbz/JoUfSqdGE7B9OwvZS52w
+z430BcsSKf7hl8e3x6pTfjktnfbUXreMFWqB0jcaQw7L5jAhQ4NpD+9rzRD5hGDP
+iemIEAGvCfNIuwcJdQGE+rEeURmPxsmFZRZf4FZ+t79AwtTQBR+TY8kkCDuRsjXh
+pI812yRYdTnk3RAasN8TEnOebednPNscHN0Q3cz0Bwm5LuV1bZe3YFuJcIHSJtjG
+CSuyBX/EuBRBHgfwSEFjywyqRX6E+TOzWIe8sdbCZcdXxpXohZCwYlD17hLx2H5z
+y8DDoCUXIzeRumO9hK/zieBRwnM1bWOGIfJzvcJH4E1+RjdL0PdhKseUUCU26K7a
+Lh+4CLJVfGtmQ48CHd2n65gApyV09ZMbbSa7HeW3/CElJtF3G6huqsNLZFF/kQ5B
+XBmDoagflJlDD5nbGNwhb3bRnuqjduDwCby5tPdDbB/TKoZqL+Bs8YM513DbopGr
+VL70R4iM8BDS5K3rfrG6CEtnBKPy6ZArgeN0dj0AndK7/KWgFRwo3xBPR9czRp2y
+V9LGH/vkWUorKKkT3bnpk7SI7uJboAcl/ooueOS04dUd9ho64xwBKh6hhlSeSdzJ
+WeMNbVoTNg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert45[] = {
+ 0x30, 0x82, 0x05, 0xa3, 0x30, 0x82, 0x03, 0x8b, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x67, 0x3f, 0x33, 0x4f, 0x21, 0x53, 0x36, 0x52, 0xc3,
+ 0x5e, 0x15, 0xd2, 0xfd, 0xb3, 0x02, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x55,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43,
+ 0x4e, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x57, 0x6f, 0x53, 0x69, 0x67,
+ 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x38, 0x30, 0x38, 0x30,
+ 0x31, 0x30, 0x30, 0x30, 0x35, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x38,
+ 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, 0x35, 0x5a, 0x30, 0x4f, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4e,
+ 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x57,
+ 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x1b, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x4f, 0x56, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xbc, 0x89, 0xbe, 0x61, 0x51, 0x53, 0xc8, 0x2b, 0x96,
+ 0x75, 0xb3, 0x5a, 0xd3, 0x0e, 0x34, 0xfe, 0x4a, 0xc2, 0x9f, 0xa3, 0x18,
+ 0x83, 0xa2, 0xac, 0xe3, 0x2e, 0x5e, 0x93, 0x79, 0x0b, 0x13, 0x49, 0x5e,
+ 0x93, 0xb2, 0x8f, 0x84, 0x10, 0xed, 0x91, 0x8f, 0x82, 0xba, 0xad, 0x67,
+ 0xdf, 0x33, 0x1b, 0xae, 0x84, 0xf2, 0x55, 0xb0, 0x5b, 0xf4, 0xb3, 0x9e,
+ 0xbc, 0xe6, 0x04, 0x0f, 0x1d, 0xef, 0x04, 0x5a, 0xa8, 0x0b, 0xec, 0x12,
+ 0x6d, 0x56, 0x19, 0x64, 0x70, 0x49, 0x0f, 0x57, 0x92, 0xf3, 0x5f, 0x21,
+ 0xa6, 0x4d, 0xb4, 0xd2, 0x96, 0x2b, 0x3c, 0x32, 0xb3, 0xef, 0x8f, 0x59,
+ 0x0b, 0x14, 0xba, 0x6e, 0xa2, 0x9e, 0x71, 0xdb, 0xf2, 0x88, 0x3f, 0x28,
+ 0x3b, 0xec, 0xce, 0xbe, 0x47, 0xac, 0x45, 0xc7, 0x8a, 0x9e, 0xfa, 0x61,
+ 0x93, 0xc5, 0x49, 0x17, 0xb6, 0x46, 0xb6, 0xf7, 0x99, 0x16, 0x8c, 0x1c,
+ 0x6e, 0x31, 0xae, 0x69, 0xce, 0xed, 0xc6, 0x24, 0x92, 0x70, 0xa1, 0xcb,
+ 0x96, 0xc3, 0x6c, 0x16, 0xd0, 0xee, 0xcc, 0x4f, 0x86, 0x33, 0xb3, 0x41,
+ 0xe6, 0x3d, 0x3d, 0xdb, 0x0e, 0x8c, 0x33, 0x74, 0xbb, 0xc3, 0xfc, 0x0b,
+ 0xa7, 0xfc, 0xd1, 0x71, 0xe2, 0xc1, 0x0c, 0xd4, 0xf7, 0xba, 0x3e, 0x80,
+ 0x90, 0xd4, 0x48, 0xeb, 0xa2, 0x83, 0x70, 0xd8, 0xdb, 0x30, 0x07, 0x29,
+ 0x89, 0xf9, 0x81, 0x21, 0x2c, 0xff, 0xeb, 0x47, 0xf6, 0x7a, 0x6d, 0x43,
+ 0x96, 0x67, 0x17, 0x3e, 0xf3, 0xe2, 0x73, 0x51, 0xc7, 0x76, 0x1e, 0xe9,
+ 0x1c, 0xa0, 0xec, 0x11, 0x1a, 0xb1, 0xcf, 0x1e, 0x2d, 0x9c, 0x55, 0xee,
+ 0x3b, 0xc6, 0x2d, 0xae, 0xdc, 0x66, 0x65, 0x91, 0xa2, 0x66, 0x9c, 0xac,
+ 0x82, 0xf1, 0xa4, 0x17, 0xb5, 0xd7, 0x43, 0x83, 0xc3, 0x88, 0xa0, 0x64,
+ 0xde, 0xca, 0x72, 0x45, 0xdc, 0x38, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x73, 0x30, 0x82, 0x01, 0x6f, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29,
+ 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x31, 0x2e, 0x77,
+ 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61,
+ 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x71, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x65, 0x30, 0x63, 0x30, 0x27, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x31, 0x2e,
+ 0x77, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
+ 0x61, 0x31, 0x30, 0x38, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61,
+ 0x69, 0x61, 0x31, 0x2e, 0x77, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x31, 0x2d, 0x63, 0x6c, 0x61, 0x73, 0x73,
+ 0x33, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x65, 0x72,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x62,
+ 0x2e, 0x81, 0xd9, 0xe3, 0x42, 0x79, 0x14, 0xa3, 0xcd, 0xd9, 0x54, 0x8a,
+ 0x6e, 0xf8, 0xde, 0x95, 0xaa, 0x8f, 0x98, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe1, 0x66, 0xcf, 0x0e,
+ 0xd1, 0xf1, 0xb3, 0x4b, 0xb7, 0x06, 0x20, 0x14, 0xfe, 0x87, 0x12, 0xd5,
+ 0xf6, 0xfe, 0xfb, 0x3e, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01,
+ 0x82, 0x9b, 0x51, 0x01, 0x03, 0x02, 0x30, 0x2b, 0x30, 0x29, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1d, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x6f, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x69,
+ 0x63, 0x79, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0xab,
+ 0x70, 0xaa, 0x64, 0xc4, 0x0b, 0x34, 0x91, 0xb9, 0x63, 0x20, 0x5e, 0xb0,
+ 0x9c, 0x21, 0xff, 0x25, 0x79, 0x6c, 0x57, 0x4e, 0x56, 0x44, 0x58, 0x83,
+ 0xb9, 0x00, 0xce, 0x2d, 0x65, 0xa8, 0x6d, 0x95, 0x38, 0xea, 0x82, 0x2d,
+ 0x55, 0x18, 0x60, 0x12, 0x7e, 0x1a, 0x1d, 0x6b, 0x62, 0x34, 0x2c, 0xd9,
+ 0xcd, 0x17, 0x00, 0x43, 0x84, 0x3e, 0xad, 0xbc, 0xff, 0x26, 0x85, 0x1f,
+ 0x4a, 0xa7, 0x46, 0x13, 0xb0, 0x7d, 0x3b, 0x0b, 0xd9, 0x4b, 0x9d, 0xb0,
+ 0xcf, 0x8d, 0xf4, 0x05, 0xcb, 0x12, 0x29, 0xfe, 0xe1, 0x97, 0xc7, 0xb7,
+ 0xc7, 0xaa, 0x53, 0x7e, 0x39, 0x2d, 0x9d, 0xf6, 0xd4, 0x5e, 0xb7, 0x8c,
+ 0x15, 0x6a, 0x81, 0xd2, 0x37, 0x1a, 0x43, 0x0e, 0xcb, 0xe6, 0x30, 0x21,
+ 0x43, 0x83, 0x69, 0x0f, 0xef, 0x6b, 0xcd, 0x10, 0xf9, 0x84, 0x60, 0xcf,
+ 0x89, 0xe9, 0x88, 0x10, 0x01, 0xaf, 0x09, 0xf3, 0x48, 0xbb, 0x07, 0x09,
+ 0x75, 0x01, 0x84, 0xfa, 0xb1, 0x1e, 0x51, 0x19, 0x8f, 0xc6, 0xc9, 0x85,
+ 0x65, 0x16, 0x5f, 0xe0, 0x56, 0x7e, 0xb7, 0xbf, 0x40, 0xc2, 0xd4, 0xd0,
+ 0x05, 0x1f, 0x93, 0x63, 0xc9, 0x24, 0x08, 0x3b, 0x91, 0xb2, 0x35, 0xe1,
+ 0xa4, 0x8f, 0x35, 0xdb, 0x24, 0x58, 0x75, 0x39, 0xe4, 0xdd, 0x10, 0x1a,
+ 0xb0, 0xdf, 0x13, 0x12, 0x73, 0x9e, 0x6d, 0xe7, 0x67, 0x3c, 0xdb, 0x1c,
+ 0x1c, 0xdd, 0x10, 0xdd, 0xcc, 0xf4, 0x07, 0x09, 0xb9, 0x2e, 0xe5, 0x75,
+ 0x6d, 0x97, 0xb7, 0x60, 0x5b, 0x89, 0x70, 0x81, 0xd2, 0x26, 0xd8, 0xc6,
+ 0x09, 0x2b, 0xb2, 0x05, 0x7f, 0xc4, 0xb8, 0x14, 0x41, 0x1e, 0x07, 0xf0,
+ 0x48, 0x41, 0x63, 0xcb, 0x0c, 0xaa, 0x45, 0x7e, 0x84, 0xf9, 0x33, 0xb3,
+ 0x58, 0x87, 0xbc, 0xb1, 0xd6, 0xc2, 0x65, 0xc7, 0x57, 0xc6, 0x95, 0xe8,
+ 0x85, 0x90, 0xb0, 0x62, 0x50, 0xf5, 0xee, 0x12, 0xf1, 0xd8, 0x7e, 0x73,
+ 0xcb, 0xc0, 0xc3, 0xa0, 0x25, 0x17, 0x23, 0x37, 0x91, 0xba, 0x63, 0xbd,
+ 0x84, 0xaf, 0xf3, 0x89, 0xe0, 0x51, 0xc2, 0x73, 0x35, 0x6d, 0x63, 0x86,
+ 0x21, 0xf2, 0x73, 0xbd, 0xc2, 0x47, 0xe0, 0x4d, 0x7e, 0x46, 0x37, 0x4b,
+ 0xd0, 0xf7, 0x61, 0x2a, 0xc7, 0x94, 0x50, 0x25, 0x36, 0xe8, 0xae, 0xda,
+ 0x2e, 0x1f, 0xb8, 0x08, 0xb2, 0x55, 0x7c, 0x6b, 0x66, 0x43, 0x8f, 0x02,
+ 0x1d, 0xdd, 0xa7, 0xeb, 0x98, 0x00, 0xa7, 0x25, 0x74, 0xf5, 0x93, 0x1b,
+ 0x6d, 0x26, 0xbb, 0x1d, 0xe5, 0xb7, 0xfc, 0x21, 0x25, 0x26, 0xd1, 0x77,
+ 0x1b, 0xa8, 0x6e, 0xaa, 0xc3, 0x4b, 0x64, 0x51, 0x7f, 0x91, 0x0e, 0x41,
+ 0x5c, 0x19, 0x83, 0xa1, 0xa8, 0x1f, 0x94, 0x99, 0x43, 0x0f, 0x99, 0xdb,
+ 0x18, 0xdc, 0x21, 0x6f, 0x76, 0xd1, 0x9e, 0xea, 0xa3, 0x76, 0xe0, 0xf0,
+ 0x09, 0xbc, 0xb9, 0xb4, 0xf7, 0x43, 0x6c, 0x1f, 0xd3, 0x2a, 0x86, 0x6a,
+ 0x2f, 0xe0, 0x6c, 0xf1, 0x83, 0x39, 0xd7, 0x70, 0xdb, 0xa2, 0x91, 0xab,
+ 0x54, 0xbe, 0xf4, 0x47, 0x88, 0x8c, 0xf0, 0x10, 0xd2, 0xe4, 0xad, 0xeb,
+ 0x7e, 0xb1, 0xba, 0x08, 0x4b, 0x67, 0x04, 0xa3, 0xf2, 0xe9, 0x90, 0x2b,
+ 0x81, 0xe3, 0x74, 0x76, 0x3d, 0x00, 0x9d, 0xd2, 0xbb, 0xfc, 0xa5, 0xa0,
+ 0x15, 0x1c, 0x28, 0xdf, 0x10, 0x4f, 0x47, 0xd7, 0x33, 0x46, 0x9d, 0xb2,
+ 0x57, 0xd2, 0xc6, 0x1f, 0xfb, 0xe4, 0x59, 0x4a, 0x2b, 0x28, 0xa9, 0x13,
+ 0xdd, 0xb9, 0xe9, 0x93, 0xb4, 0x88, 0xee, 0xe2, 0x5b, 0xa0, 0x07, 0x25,
+ 0xfe, 0x8a, 0x2e, 0x78, 0xe4, 0xb4, 0xe1, 0xd5, 0x1d, 0xf6, 0x1a, 0x3a,
+ 0xe3, 0x1c, 0x01, 0x2a, 0x1e, 0xa1, 0x86, 0x54, 0x9e, 0x49, 0xdc, 0xc9,
+ 0x59, 0xe3, 0x0d, 0x6d, 0x5a, 0x13, 0x36,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120040007 (0x727aa47)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: May 7 17:04:09 2014 GMT
+ Not After : May 7 17:03:30 2018 GMT
+ Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT SSL SHA2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:d1:e8:37:a7:76:8a:70:4b:19:f0:20:37:09:24:
+ 37:7f:ea:fb:78:e6:05:ba:6a:ad:4e:27:0d:fc:72:
+ 6a:d9:6c:21:c4:64:11:95:73:10:0a:5c:25:7b:88:
+ 6c:94:04:fd:c7:db:ae:7b:dc:4a:08:b3:3e:16:f1:
+ d0:ad:db:30:6d:d7:1a:1e:52:b5:3d:f0:47:19:03:
+ e2:7d:a6:bd:57:13:3f:54:ea:3a:a3:b1:77:fc:42:
+ f0:63:49:6a:91:80:2e:30:49:c0:8a:eb:2b:af:fe:
+ 3a:eb:07:5d:06:f7:e9:fd:84:0e:91:bd:09:20:29:
+ e8:6e:5d:09:ce:15:d3:e7:ef:db:50:eb:44:ef:18:
+ 57:ab:04:1d:bc:31:f9:f7:7b:2a:13:cf:d1:3d:51:
+ af:1b:c5:b5:7b:e7:b0:fc:53:bb:9a:e7:63:de:41:
+ 33:b6:47:24:69:5d:b8:46:a7:ff:ad:ab:df:4f:7a:
+ 78:25:27:21:26:34:ca:02:6e:37:51:f0:ed:58:1a:
+ 60:94:f6:c4:93:d8:dd:30:24:25:d7:1c:eb:19:94:
+ 35:5d:93:b2:ae:aa:29:83:73:c4:74:59:05:52:67:
+ 9d:da:67:51:39:05:3a:36:ea:f2:1e:76:2b:14:ae:
+ ec:3d:f9:14:99:8b:07:6e:bc:e7:0c:56:de:ac:be:
+ ae:db:75:32:90:9e:63:bd:74:bf:e0:0a:ca:f8:34:
+ 96:67:84:cd:d1:42:38:78:c7:99:b6:0c:ce:b6:0f:
+ e9:1b:cb:f4:59:be:11:0e:cb:2c:32:c8:fa:83:29:
+ 64:79:3c:8b:4b:f0:32:74:6c:f3:93:b8:96:6b:5d:
+ 57:5a:68:c1:cc:0c:79:8a:19:de:f5:49:02:5e:08:
+ 80:01:89:0c:32:cd:d2:d6:96:d5:4b:a0:f3:ec:bf:
+ ab:f4:7d:b3:a1:b9:7c:da:4e:d7:e5:b7:ac:b9:f2:
+ 25:5f:01:cb:8c:96:a8:28:ae:c1:33:5a:f6:3f:08:
+ 90:dc:eb:ff:39:d8:26:c8:12:9d:1c:9a:aa:a9:c0:
+ 16:8e:86:ed:67:52:96:00:7f:0d:92:3d:3d:d9:70:
+ 36:e5:ea:42:6f:1f:ae:95:e5:5b:5d:f8:d0:3a:c7:
+ d4:de:77:86:d0:fc:9e:4e:e2:e2:b8:a9:68:37:09:
+ c4:39:e3:85:b8:89:f3:1f:6e:b7:6d:1f:4a:2f:18:
+ 09:6f:de:4a:01:8f:14:c9:b7:a6:ee:a7:63:9f:33:
+ a4:54:7c:42:83:68:b8:a5:df:bf:ec:b9:1a:5d:13:
+ 3b:d9:ad:68:fd:20:0a:55:91:21:64:f9:d7:13:01:
+ a0:08:5d:59:89:1b:44:af:a4:ac:c7:05:10:fa:41:
+ 4a:a8:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+ Policy: 1.3.6.1.4.1.311.42.1
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.omniroot.com/baltimoreroot
+
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, OCSP Signing
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 51:AF:24:26:9C:F4:68:22:57:80:26:2B:3B:46:62:15:7B:1E:CC:A5
+ Signature Algorithm: sha256WithRSAEncryption
+ 69:62:f6:84:91:00:c4:6f:82:7b:24:e1:42:a2:a5:8b:82:5c:
+ a7:c5:44:cb:e7:52:76:63:d3:76:9e:78:e2:69:35:b1:38:ba:
+ b0:96:c6:1f:ac:7b:c6:b2:65:77:8b:7d:8d:ae:64:b9:a5:8c:
+ 17:ca:58:65:c3:ad:82:f5:c5:a2:f5:01:13:93:c6:7e:44:e5:
+ c4:61:fa:03:b6:56:c1:72:e1:c8:28:c5:69:21:8f:ac:6e:fd:
+ 7f:43:83:36:b8:c0:d6:a0:28:fe:1a:45:be:fd:93:8c:8d:a4:
+ 64:79:1f:14:db:a1:9f:21:dc:c0:4e:7b:17:22:17:b1:b6:3c:
+ d3:9b:e2:0a:a3:7e:99:b0:c1:ac:d8:f4:86:df:3c:da:7d:14:
+ 9c:40:c1:7c:d2:18:6f:f1:4f:26:45:09:95:94:5c:da:d0:98:
+ f8:f4:4c:82:96:10:de:ac:30:cb:2b:ae:f9:92:ea:bf:79:03:
+ fc:1e:3f:ac:09:a4:3f:65:fd:91:4f:96:24:a7:ce:b4:4e:6a:
+ 96:29:17:ae:c0:a8:df:17:22:f4:17:e3:dc:1c:39:06:56:10:
+ ea:ea:b5:74:17:3c:4e:dd:7e:91:0a:a8:0b:78:07:a7:31:44:
+ 08:31:ab:18:84:0f:12:9c:e7:de:84:2c:e9:6d:93:45:bf:a8:
+ c1:3f:34:dc
+-----BEGIN CERTIFICATE-----
+MIIF4TCCBMmgAwIBAgIEByeqRzANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDUwNzE3MDQwOVoX
+DTE4MDUwNzE3MDMzMFowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
+YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0
+IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3
+p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu
+e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA
+iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R
+PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg
+lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU
+mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R
+DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV
+S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq
+qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE
+OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o
+/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCAXswggF3MBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwYAYDVR0gBFkwVzBIBgkrBgEEAbE+AQAwOzA5BggrBgEF
+BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku
+Y2ZtMAsGCSsGAQQBgjcqATBCBggrBgEFBQcBAQQ2MDQwMgYIKwYBBQUHMAGGJmh0
+dHA6Ly9vY3NwLm9tbmlyb290LmNvbS9iYWx0aW1vcmVyb290MA4GA1UdDwEB/wQE
+AwIBhjAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMJMB8G
+A1UdIwQYMBaAFOWdWTCCR1jMrPoIVDaGezq1BE3wMEIGA1UdHwQ7MDkwN6A1oDOG
+MWh0dHA6Ly9jZHAxLnB1YmxpYy10cnVzdC5jb20vQ1JML09tbmlyb290MjAyNS5j
+cmwwHQYDVR0OBBYEFFGvJCac9GgiV4AmKztGYhV7HsylMA0GCSqGSIb3DQEBCwUA
+A4IBAQBpYvaEkQDEb4J7JOFCoqWLglynxUTL51J2Y9N2nnjiaTWxOLqwlsYfrHvG
+smV3i32NrmS5pYwXylhlw62C9cWi9QETk8Z+ROXEYfoDtlbBcuHIKMVpIY+sbv1/
+Q4M2uMDWoCj+GkW+/ZOMjaRkeR8U26GfIdzATnsXIhextjzTm+IKo36ZsMGs2PSG
+3zzafRScQMF80hhv8U8mRQmVlFza0Jj49EyClhDerDDLK675kuq/eQP8Hj+sCaQ/
+Zf2RT5Ykp860TmqWKReuwKjfFyL0F+PcHDkGVhDq6rV0FzxO3X6RCqgLeAenMUQI
+MasYhA8SnOfehCzpbZNFv6jBPzTc
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert46[] = {
+ 0x30, 0x82, 0x05, 0xe1, 0x30, 0x82, 0x04, 0xc9, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0xaa, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34,
+ 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x34, 0x30, 0x39, 0x5a, 0x17,
+ 0x0d, 0x31, 0x38, 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x33, 0x33,
+ 0x30, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67,
+ 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30,
+ 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72,
+ 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
+ 0x74, 0x20, 0x49, 0x54, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x49, 0x54, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32,
+ 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
+ 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xd1, 0xe8, 0x37,
+ 0xa7, 0x76, 0x8a, 0x70, 0x4b, 0x19, 0xf0, 0x20, 0x37, 0x09, 0x24, 0x37,
+ 0x7f, 0xea, 0xfb, 0x78, 0xe6, 0x05, 0xba, 0x6a, 0xad, 0x4e, 0x27, 0x0d,
+ 0xfc, 0x72, 0x6a, 0xd9, 0x6c, 0x21, 0xc4, 0x64, 0x11, 0x95, 0x73, 0x10,
+ 0x0a, 0x5c, 0x25, 0x7b, 0x88, 0x6c, 0x94, 0x04, 0xfd, 0xc7, 0xdb, 0xae,
+ 0x7b, 0xdc, 0x4a, 0x08, 0xb3, 0x3e, 0x16, 0xf1, 0xd0, 0xad, 0xdb, 0x30,
+ 0x6d, 0xd7, 0x1a, 0x1e, 0x52, 0xb5, 0x3d, 0xf0, 0x47, 0x19, 0x03, 0xe2,
+ 0x7d, 0xa6, 0xbd, 0x57, 0x13, 0x3f, 0x54, 0xea, 0x3a, 0xa3, 0xb1, 0x77,
+ 0xfc, 0x42, 0xf0, 0x63, 0x49, 0x6a, 0x91, 0x80, 0x2e, 0x30, 0x49, 0xc0,
+ 0x8a, 0xeb, 0x2b, 0xaf, 0xfe, 0x3a, 0xeb, 0x07, 0x5d, 0x06, 0xf7, 0xe9,
+ 0xfd, 0x84, 0x0e, 0x91, 0xbd, 0x09, 0x20, 0x29, 0xe8, 0x6e, 0x5d, 0x09,
+ 0xce, 0x15, 0xd3, 0xe7, 0xef, 0xdb, 0x50, 0xeb, 0x44, 0xef, 0x18, 0x57,
+ 0xab, 0x04, 0x1d, 0xbc, 0x31, 0xf9, 0xf7, 0x7b, 0x2a, 0x13, 0xcf, 0xd1,
+ 0x3d, 0x51, 0xaf, 0x1b, 0xc5, 0xb5, 0x7b, 0xe7, 0xb0, 0xfc, 0x53, 0xbb,
+ 0x9a, 0xe7, 0x63, 0xde, 0x41, 0x33, 0xb6, 0x47, 0x24, 0x69, 0x5d, 0xb8,
+ 0x46, 0xa7, 0xff, 0xad, 0xab, 0xdf, 0x4f, 0x7a, 0x78, 0x25, 0x27, 0x21,
+ 0x26, 0x34, 0xca, 0x02, 0x6e, 0x37, 0x51, 0xf0, 0xed, 0x58, 0x1a, 0x60,
+ 0x94, 0xf6, 0xc4, 0x93, 0xd8, 0xdd, 0x30, 0x24, 0x25, 0xd7, 0x1c, 0xeb,
+ 0x19, 0x94, 0x35, 0x5d, 0x93, 0xb2, 0xae, 0xaa, 0x29, 0x83, 0x73, 0xc4,
+ 0x74, 0x59, 0x05, 0x52, 0x67, 0x9d, 0xda, 0x67, 0x51, 0x39, 0x05, 0x3a,
+ 0x36, 0xea, 0xf2, 0x1e, 0x76, 0x2b, 0x14, 0xae, 0xec, 0x3d, 0xf9, 0x14,
+ 0x99, 0x8b, 0x07, 0x6e, 0xbc, 0xe7, 0x0c, 0x56, 0xde, 0xac, 0xbe, 0xae,
+ 0xdb, 0x75, 0x32, 0x90, 0x9e, 0x63, 0xbd, 0x74, 0xbf, 0xe0, 0x0a, 0xca,
+ 0xf8, 0x34, 0x96, 0x67, 0x84, 0xcd, 0xd1, 0x42, 0x38, 0x78, 0xc7, 0x99,
+ 0xb6, 0x0c, 0xce, 0xb6, 0x0f, 0xe9, 0x1b, 0xcb, 0xf4, 0x59, 0xbe, 0x11,
+ 0x0e, 0xcb, 0x2c, 0x32, 0xc8, 0xfa, 0x83, 0x29, 0x64, 0x79, 0x3c, 0x8b,
+ 0x4b, 0xf0, 0x32, 0x74, 0x6c, 0xf3, 0x93, 0xb8, 0x96, 0x6b, 0x5d, 0x57,
+ 0x5a, 0x68, 0xc1, 0xcc, 0x0c, 0x79, 0x8a, 0x19, 0xde, 0xf5, 0x49, 0x02,
+ 0x5e, 0x08, 0x80, 0x01, 0x89, 0x0c, 0x32, 0xcd, 0xd2, 0xd6, 0x96, 0xd5,
+ 0x4b, 0xa0, 0xf3, 0xec, 0xbf, 0xab, 0xf4, 0x7d, 0xb3, 0xa1, 0xb9, 0x7c,
+ 0xda, 0x4e, 0xd7, 0xe5, 0xb7, 0xac, 0xb9, 0xf2, 0x25, 0x5f, 0x01, 0xcb,
+ 0x8c, 0x96, 0xa8, 0x28, 0xae, 0xc1, 0x33, 0x5a, 0xf6, 0x3f, 0x08, 0x90,
+ 0xdc, 0xeb, 0xff, 0x39, 0xd8, 0x26, 0xc8, 0x12, 0x9d, 0x1c, 0x9a, 0xaa,
+ 0xa9, 0xc0, 0x16, 0x8e, 0x86, 0xed, 0x67, 0x52, 0x96, 0x00, 0x7f, 0x0d,
+ 0x92, 0x3d, 0x3d, 0xd9, 0x70, 0x36, 0xe5, 0xea, 0x42, 0x6f, 0x1f, 0xae,
+ 0x95, 0xe5, 0x5b, 0x5d, 0xf8, 0xd0, 0x3a, 0xc7, 0xd4, 0xde, 0x77, 0x86,
+ 0xd0, 0xfc, 0x9e, 0x4e, 0xe2, 0xe2, 0xb8, 0xa9, 0x68, 0x37, 0x09, 0xc4,
+ 0x39, 0xe3, 0x85, 0xb8, 0x89, 0xf3, 0x1f, 0x6e, 0xb7, 0x6d, 0x1f, 0x4a,
+ 0x2f, 0x18, 0x09, 0x6f, 0xde, 0x4a, 0x01, 0x8f, 0x14, 0xc9, 0xb7, 0xa6,
+ 0xee, 0xa7, 0x63, 0x9f, 0x33, 0xa4, 0x54, 0x7c, 0x42, 0x83, 0x68, 0xb8,
+ 0xa5, 0xdf, 0xbf, 0xec, 0xb9, 0x1a, 0x5d, 0x13, 0x3b, 0xd9, 0xad, 0x68,
+ 0xfd, 0x20, 0x0a, 0x55, 0x91, 0x21, 0x64, 0xf9, 0xd7, 0x13, 0x01, 0xa0,
+ 0x08, 0x5d, 0x59, 0x89, 0x1b, 0x44, 0xaf, 0xa4, 0xac, 0xc7, 0x05, 0x10,
+ 0xfa, 0x41, 0x4a, 0xa8, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x7b, 0x30, 0x82, 0x01, 0x77, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x60, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x59, 0x30,
+ 0x57, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e,
+ 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
+ 0x63, 0x66, 0x6d, 0x30, 0x0b, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01,
+ 0x82, 0x37, 0x2a, 0x01, 0x30, 0x42, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x26, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x6f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62,
+ 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x86, 0x30, 0x27, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
+ 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d,
+ 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86,
+ 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86,
+ 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31,
+ 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x51, 0xaf, 0x24, 0x26, 0x9c, 0xf4, 0x68, 0x22, 0x57, 0x80, 0x26,
+ 0x2b, 0x3b, 0x46, 0x62, 0x15, 0x7b, 0x1e, 0xcc, 0xa5, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x69, 0x62, 0xf6, 0x84, 0x91, 0x00, 0xc4,
+ 0x6f, 0x82, 0x7b, 0x24, 0xe1, 0x42, 0xa2, 0xa5, 0x8b, 0x82, 0x5c, 0xa7,
+ 0xc5, 0x44, 0xcb, 0xe7, 0x52, 0x76, 0x63, 0xd3, 0x76, 0x9e, 0x78, 0xe2,
+ 0x69, 0x35, 0xb1, 0x38, 0xba, 0xb0, 0x96, 0xc6, 0x1f, 0xac, 0x7b, 0xc6,
+ 0xb2, 0x65, 0x77, 0x8b, 0x7d, 0x8d, 0xae, 0x64, 0xb9, 0xa5, 0x8c, 0x17,
+ 0xca, 0x58, 0x65, 0xc3, 0xad, 0x82, 0xf5, 0xc5, 0xa2, 0xf5, 0x01, 0x13,
+ 0x93, 0xc6, 0x7e, 0x44, 0xe5, 0xc4, 0x61, 0xfa, 0x03, 0xb6, 0x56, 0xc1,
+ 0x72, 0xe1, 0xc8, 0x28, 0xc5, 0x69, 0x21, 0x8f, 0xac, 0x6e, 0xfd, 0x7f,
+ 0x43, 0x83, 0x36, 0xb8, 0xc0, 0xd6, 0xa0, 0x28, 0xfe, 0x1a, 0x45, 0xbe,
+ 0xfd, 0x93, 0x8c, 0x8d, 0xa4, 0x64, 0x79, 0x1f, 0x14, 0xdb, 0xa1, 0x9f,
+ 0x21, 0xdc, 0xc0, 0x4e, 0x7b, 0x17, 0x22, 0x17, 0xb1, 0xb6, 0x3c, 0xd3,
+ 0x9b, 0xe2, 0x0a, 0xa3, 0x7e, 0x99, 0xb0, 0xc1, 0xac, 0xd8, 0xf4, 0x86,
+ 0xdf, 0x3c, 0xda, 0x7d, 0x14, 0x9c, 0x40, 0xc1, 0x7c, 0xd2, 0x18, 0x6f,
+ 0xf1, 0x4f, 0x26, 0x45, 0x09, 0x95, 0x94, 0x5c, 0xda, 0xd0, 0x98, 0xf8,
+ 0xf4, 0x4c, 0x82, 0x96, 0x10, 0xde, 0xac, 0x30, 0xcb, 0x2b, 0xae, 0xf9,
+ 0x92, 0xea, 0xbf, 0x79, 0x03, 0xfc, 0x1e, 0x3f, 0xac, 0x09, 0xa4, 0x3f,
+ 0x65, 0xfd, 0x91, 0x4f, 0x96, 0x24, 0xa7, 0xce, 0xb4, 0x4e, 0x6a, 0x96,
+ 0x29, 0x17, 0xae, 0xc0, 0xa8, 0xdf, 0x17, 0x22, 0xf4, 0x17, 0xe3, 0xdc,
+ 0x1c, 0x39, 0x06, 0x56, 0x10, 0xea, 0xea, 0xb5, 0x74, 0x17, 0x3c, 0x4e,
+ 0xdd, 0x7e, 0x91, 0x0a, 0xa8, 0x0b, 0x78, 0x07, 0xa7, 0x31, 0x44, 0x08,
+ 0x31, 0xab, 0x18, 0x84, 0x0f, 0x12, 0x9c, 0xe7, 0xde, 0x84, 0x2c, 0xe9,
+ 0x6d, 0x93, 0x45, 0xbf, 0xa8, 0xc1, 0x3f, 0x34, 0xdc,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 6e:cc:7a:a5:a7:03:20:09:b8:ce:bc:f4:e9:52:d4:91
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Feb 8 00:00:00 2010 GMT
+ Not After : Feb 7 23:59:59 2020 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)10, CN=VeriSign Class 3 Secure Server CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b1:87:84:1f:c2:0c:45:f5:bc:ab:25:97:a7:ad:
+ a2:3e:9c:ba:f6:c1:39:b8:8b:ca:c2:ac:56:c6:e5:
+ bb:65:8e:44:4f:4d:ce:6f:ed:09:4a:d4:af:4e:10:
+ 9c:68:8b:2e:95:7b:89:9b:13:ca:e2:34:34:c1:f3:
+ 5b:f3:49:7b:62:83:48:81:74:d1:88:78:6c:02:53:
+ f9:bc:7f:43:26:57:58:33:83:3b:33:0a:17:b0:d0:
+ 4e:91:24:ad:86:7d:64:12:dc:74:4a:34:a1:1d:0a:
+ ea:96:1d:0b:15:fc:a3:4b:3b:ce:63:88:d0:f8:2d:
+ 0c:94:86:10:ca:b6:9a:3d:ca:eb:37:9c:00:48:35:
+ 86:29:50:78:e8:45:63:cd:19:41:4f:f5:95:ec:7b:
+ 98:d4:c4:71:b3:50:be:28:b3:8f:a0:b9:53:9c:f5:
+ ca:2c:23:a9:fd:14:06:e8:18:b4:9a:e8:3c:6e:81:
+ fd:e4:cd:35:36:b3:51:d3:69:ec:12:ba:56:6e:6f:
+ 9b:57:c5:8b:14:e7:0e:c7:9c:ed:4a:54:6a:c9:4d:
+ c5:bf:11:b1:ae:1c:67:81:cb:44:55:33:99:7f:24:
+ 9b:3f:53:45:7f:86:1a:f3:3c:fa:6d:7f:81:f5:b8:
+ 4a:d3:f5:85:37:1c:b5:a6:d0:09:e4:18:7b:38:4e:
+ fa:0f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.23.3
+ CPS: https://www.verisign.com/cps
+ User Notice:
+ Explicit Text: https://www.verisign.com/rpa
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-6
+ X509v3 Subject Key Identifier:
+ 0D:44:5C:16:53:44:C1:82:7E:1D:20:AB:25:F4:01:63:D8:BE:79:A5
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 0c:83:24:ef:dd:c3:0c:d9:58:9c:fe:36:b6:eb:8a:80:4b:d1:
+ a3:f7:9d:f3:cc:53:ef:82:9e:a3:a1:e6:97:c1:58:9d:75:6c:
+ e0:1d:1b:4c:fa:d1:c1:2d:05:c0:ea:6e:b2:22:70:55:d9:20:
+ 33:40:33:07:c2:65:83:fa:8f:43:37:9b:ea:0e:9a:6c:70:ee:
+ f6:9c:80:3b:d9:37:f4:7a:6d:ec:d0:18:7d:49:4a:ca:99:c7:
+ 19:28:a2:be:d8:77:24:f7:85:26:86:6d:87:05:40:41:67:d1:
+ 27:3a:ed:dc:48:1d:22:cd:0b:0b:8b:bc:f4:b1:7b:fd:b4:99:
+ a8:e9:76:2a:e1:1a:2d:87:6e:74:d3:88:dd:1e:22:c6:df:16:
+ b6:2b:82:14:0a:94:5c:f2:50:ec:af:ce:ff:62:37:0d:ad:65:
+ d3:06:41:53:ed:02:14:c8:b5:58:28:a1:ac:e0:5b:ec:b3:7f:
+ 95:4a:fb:03:c8:ad:26:db:e6:66:78:12:4a:d9:9f:42:fb:e1:
+ 98:e6:42:83:9b:8f:8f:67:24:e8:61:19:b5:dd:cd:b5:0b:26:
+ 05:8e:c3:6e:c4:c8:75:b8:46:cf:e2:18:06:5e:a9:ae:a8:81:
+ 9a:47:16:de:0c:28:6c:25:27:b9:de:b7:84:58:c6:1f:38:1e:
+ a4:c4:cb:66
+-----BEGIN CERTIFICATE-----
+MIIF7DCCBNSgAwIBAgIQbsx6pacDIAm4zrz06VLUkTANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBtTEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVy
+aVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG
+5btljkRPTc5v7QlK1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8
+f0MmV1gzgzszChew0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDK
+tpo9yus3nABINYYpUHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAbo
+GLSa6Dxugf3kzTU2s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RV
+M5l/JJs/U0V/hhrzPPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggHfMIIB
+2zA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlz
+aWduLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4
+RQEHFwMwVjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2Nw
+czAqBggrBgEFBQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMDQG
+A1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMtZzUu
+Y3JsMA4GA1UdDwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglp
+bWFnZS9naWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNo
+dHRwOi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjAoBgNVHREEITAfpB0w
+GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+
+HSCrJfQBY9i+eaUwHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwDQYJ
+KoZIhvcNAQEFBQADggEBAAyDJO/dwwzZWJz+NrbrioBL0aP3nfPMU++CnqOh5pfB
+WJ11bOAdG0z60cEtBcDqbrIicFXZIDNAMwfCZYP6j0M3m+oOmmxw7vacgDvZN/R6
+bezQGH1JSsqZxxkoor7YdyT3hSaGbYcFQEFn0Sc67dxIHSLNCwuLvPSxe/20majp
+dirhGi2HbnTTiN0eIsbfFrYrghQKlFzyUOyvzv9iNw2tZdMGQVPtAhTItVgooazg
+W+yzf5VK+wPIrSbb5mZ4EkrZn0L74ZjmQoObj49nJOhhGbXdzbULJgWOw27EyHW4
+Rs/iGAZeqa6ogZpHFt4MKGwlJ7net4RYxh84HqTEy2Y=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert47[] = {
+ 0x30, 0x82, 0x05, 0xec, 0x30, 0x82, 0x04, 0xd4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x6e, 0xcc, 0x7a, 0xa5, 0xa7, 0x03, 0x20, 0x09, 0xb8,
+ 0xce, 0xbc, 0xf4, 0xe9, 0x52, 0xd4, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x32, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xb5, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30,
+ 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x31, 0x30, 0x31, 0x2f,
+ 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb1, 0x87, 0x84, 0x1f,
+ 0xc2, 0x0c, 0x45, 0xf5, 0xbc, 0xab, 0x25, 0x97, 0xa7, 0xad, 0xa2, 0x3e,
+ 0x9c, 0xba, 0xf6, 0xc1, 0x39, 0xb8, 0x8b, 0xca, 0xc2, 0xac, 0x56, 0xc6,
+ 0xe5, 0xbb, 0x65, 0x8e, 0x44, 0x4f, 0x4d, 0xce, 0x6f, 0xed, 0x09, 0x4a,
+ 0xd4, 0xaf, 0x4e, 0x10, 0x9c, 0x68, 0x8b, 0x2e, 0x95, 0x7b, 0x89, 0x9b,
+ 0x13, 0xca, 0xe2, 0x34, 0x34, 0xc1, 0xf3, 0x5b, 0xf3, 0x49, 0x7b, 0x62,
+ 0x83, 0x48, 0x81, 0x74, 0xd1, 0x88, 0x78, 0x6c, 0x02, 0x53, 0xf9, 0xbc,
+ 0x7f, 0x43, 0x26, 0x57, 0x58, 0x33, 0x83, 0x3b, 0x33, 0x0a, 0x17, 0xb0,
+ 0xd0, 0x4e, 0x91, 0x24, 0xad, 0x86, 0x7d, 0x64, 0x12, 0xdc, 0x74, 0x4a,
+ 0x34, 0xa1, 0x1d, 0x0a, 0xea, 0x96, 0x1d, 0x0b, 0x15, 0xfc, 0xa3, 0x4b,
+ 0x3b, 0xce, 0x63, 0x88, 0xd0, 0xf8, 0x2d, 0x0c, 0x94, 0x86, 0x10, 0xca,
+ 0xb6, 0x9a, 0x3d, 0xca, 0xeb, 0x37, 0x9c, 0x00, 0x48, 0x35, 0x86, 0x29,
+ 0x50, 0x78, 0xe8, 0x45, 0x63, 0xcd, 0x19, 0x41, 0x4f, 0xf5, 0x95, 0xec,
+ 0x7b, 0x98, 0xd4, 0xc4, 0x71, 0xb3, 0x50, 0xbe, 0x28, 0xb3, 0x8f, 0xa0,
+ 0xb9, 0x53, 0x9c, 0xf5, 0xca, 0x2c, 0x23, 0xa9, 0xfd, 0x14, 0x06, 0xe8,
+ 0x18, 0xb4, 0x9a, 0xe8, 0x3c, 0x6e, 0x81, 0xfd, 0xe4, 0xcd, 0x35, 0x36,
+ 0xb3, 0x51, 0xd3, 0x69, 0xec, 0x12, 0xba, 0x56, 0x6e, 0x6f, 0x9b, 0x57,
+ 0xc5, 0x8b, 0x14, 0xe7, 0x0e, 0xc7, 0x9c, 0xed, 0x4a, 0x54, 0x6a, 0xc9,
+ 0x4d, 0xc5, 0xbf, 0x11, 0xb1, 0xae, 0x1c, 0x67, 0x81, 0xcb, 0x44, 0x55,
+ 0x33, 0x99, 0x7f, 0x24, 0x9b, 0x3f, 0x53, 0x45, 0x7f, 0x86, 0x1a, 0xf3,
+ 0x3c, 0xfa, 0x6d, 0x7f, 0x81, 0xf5, 0xb8, 0x4a, 0xd3, 0xf5, 0x85, 0x37,
+ 0x1c, 0xb5, 0xa6, 0xd0, 0x09, 0xe4, 0x18, 0x7b, 0x38, 0x4e, 0xfa, 0x0f,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xdf, 0x30, 0x82, 0x01,
+ 0xdb, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x69,
+ 0x30, 0x67, 0x30, 0x65, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8,
+ 0x45, 0x01, 0x07, 0x17, 0x03, 0x30, 0x56, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x02, 0x30, 0x1e, 0x1a, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x34, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27,
+ 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1,
+ 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69,
+ 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f,
+ 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f,
+ 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a,
+ 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x28,
+ 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, 0x30,
+ 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49,
+ 0x2d, 0x32, 0x2d, 0x36, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x0d, 0x44, 0x5c, 0x16, 0x53, 0x44, 0xc1, 0x82, 0x7e,
+ 0x1d, 0x20, 0xab, 0x25, 0xf4, 0x01, 0x63, 0xd8, 0xbe, 0x79, 0xa5, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x0c, 0x83, 0x24, 0xef, 0xdd, 0xc3, 0x0c, 0xd9,
+ 0x58, 0x9c, 0xfe, 0x36, 0xb6, 0xeb, 0x8a, 0x80, 0x4b, 0xd1, 0xa3, 0xf7,
+ 0x9d, 0xf3, 0xcc, 0x53, 0xef, 0x82, 0x9e, 0xa3, 0xa1, 0xe6, 0x97, 0xc1,
+ 0x58, 0x9d, 0x75, 0x6c, 0xe0, 0x1d, 0x1b, 0x4c, 0xfa, 0xd1, 0xc1, 0x2d,
+ 0x05, 0xc0, 0xea, 0x6e, 0xb2, 0x22, 0x70, 0x55, 0xd9, 0x20, 0x33, 0x40,
+ 0x33, 0x07, 0xc2, 0x65, 0x83, 0xfa, 0x8f, 0x43, 0x37, 0x9b, 0xea, 0x0e,
+ 0x9a, 0x6c, 0x70, 0xee, 0xf6, 0x9c, 0x80, 0x3b, 0xd9, 0x37, 0xf4, 0x7a,
+ 0x6d, 0xec, 0xd0, 0x18, 0x7d, 0x49, 0x4a, 0xca, 0x99, 0xc7, 0x19, 0x28,
+ 0xa2, 0xbe, 0xd8, 0x77, 0x24, 0xf7, 0x85, 0x26, 0x86, 0x6d, 0x87, 0x05,
+ 0x40, 0x41, 0x67, 0xd1, 0x27, 0x3a, 0xed, 0xdc, 0x48, 0x1d, 0x22, 0xcd,
+ 0x0b, 0x0b, 0x8b, 0xbc, 0xf4, 0xb1, 0x7b, 0xfd, 0xb4, 0x99, 0xa8, 0xe9,
+ 0x76, 0x2a, 0xe1, 0x1a, 0x2d, 0x87, 0x6e, 0x74, 0xd3, 0x88, 0xdd, 0x1e,
+ 0x22, 0xc6, 0xdf, 0x16, 0xb6, 0x2b, 0x82, 0x14, 0x0a, 0x94, 0x5c, 0xf2,
+ 0x50, 0xec, 0xaf, 0xce, 0xff, 0x62, 0x37, 0x0d, 0xad, 0x65, 0xd3, 0x06,
+ 0x41, 0x53, 0xed, 0x02, 0x14, 0xc8, 0xb5, 0x58, 0x28, 0xa1, 0xac, 0xe0,
+ 0x5b, 0xec, 0xb3, 0x7f, 0x95, 0x4a, 0xfb, 0x03, 0xc8, 0xad, 0x26, 0xdb,
+ 0xe6, 0x66, 0x78, 0x12, 0x4a, 0xd9, 0x9f, 0x42, 0xfb, 0xe1, 0x98, 0xe6,
+ 0x42, 0x83, 0x9b, 0x8f, 0x8f, 0x67, 0x24, 0xe8, 0x61, 0x19, 0xb5, 0xdd,
+ 0xcd, 0xb5, 0x0b, 0x26, 0x05, 0x8e, 0xc3, 0x6e, 0xc4, 0xc8, 0x75, 0xb8,
+ 0x46, 0xcf, 0xe2, 0x18, 0x06, 0x5e, 0xa9, 0xae, 0xa8, 0x81, 0x9a, 0x47,
+ 0x16, 0xde, 0x0c, 0x28, 0x6c, 0x25, 0x27, 0xb9, 0xde, 0xb7, 0x84, 0x58,
+ 0xc6, 0x1f, 0x38, 0x1e, 0xa4, 0xc4, 0xcb, 0x66,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 2c:48:dd:93:0d:f5:59:8e:f9:3c:99:54:7a:60:ed:43
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2016 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)06, CN=VeriSign Class 3 Extended Validation SSL SGC CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:56:88:ba:88:34:64:64:cf:cd:ca:b0:ee:e7:
+ 19:73:c5:72:d9:bb:45:bc:b5:a8:ff:83:be:1c:03:
+ db:ed:89:b7:2e:10:1a:25:bc:55:ca:41:a1:9f:0b:
+ cf:19:5e:70:b9:5e:39:4b:9e:31:1c:5f:87:ae:2a:
+ aa:a8:2b:a2:1b:3b:10:23:5f:13:b1:dd:08:8c:4e:
+ 14:da:83:81:e3:b5:8c:e3:68:ed:24:67:ce:56:b6:
+ ac:9b:73:96:44:db:8a:8c:b3:d6:f0:71:93:8e:db:
+ 71:54:4a:eb:73:59:6a:8f:70:51:2c:03:9f:97:d1:
+ cc:11:7a:bc:62:0d:95:2a:c9:1c:75:57:e9:f5:c7:
+ ea:ba:84:35:cb:c7:85:5a:7e:e4:4d:e1:11:97:7d:
+ 0e:20:34:45:db:f1:a2:09:eb:eb:3d:9e:b8:96:43:
+ 5e:34:4b:08:25:1e:43:1a:a2:d9:b7:8a:01:34:3d:
+ c3:f8:e5:af:4f:8c:ff:cd:65:f0:23:4e:c5:97:b3:
+ 5c:da:90:1c:82:85:0d:06:0d:c1:22:b6:7b:28:a4:
+ 03:c3:4c:53:d1:58:bc:72:bc:08:39:fc:a0:76:a8:
+ a8:e9:4b:6e:88:3d:e3:b3:31:25:8c:73:29:48:0e:
+ 32:79:06:ed:3d:43:f4:f6:e4:e9:fc:7d:be:8e:08:
+ d5:1f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 4E:43:C8:1D:76:EF:37:53:7A:4F:F2:58:6F:94:F3:38:E2:D5:BD:DF
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://EVSecure-crl.verisign.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Netscape Cert Type:
+ SSL CA, S/MIME CA
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Subject Alternative Name:
+ DirName:/CN=Class3CA2048-1-48
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Authority Information Access:
+ OCSP - URI:http://EVSecure-ocsp.verisign.com
+
+ X509v3 Extended Key Usage:
+ Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1, TLS Web Server Authentication, TLS Web Client Authentication
+ Signature Algorithm: sha1WithRSAEncryption
+ 27:74:a6:34:ea:1d:9d:e1:53:d6:1c:9d:0c:a7:5b:4c:a9:67:
+ f2:f0:32:b7:01:0f:fb:42:18:38:de:e4:ee:49:c8:13:c9:0b:
+ ec:04:c3:40:71:18:72:76:43:02:23:5d:ab:7b:c8:48:14:1a:
+ c8:7b:1d:fc:f6:0a:9f:36:a1:d2:09:73:71:66:96:75:51:34:
+ bf:99:30:51:67:9d:54:b7:26:45:ac:73:08:23:86:26:99:71:
+ f4:8e:d7:ea:39:9b:06:09:23:bf:62:dd:a8:c4:b6:7d:a4:89:
+ 07:3e:f3:6d:ae:40:59:50:79:97:37:3d:32:78:7d:b2:63:4b:
+ f9:ea:08:69:0e:13:ed:e8:cf:bb:ac:05:86:ca:22:cf:88:62:
+ 5d:3c:22:49:d8:63:d5:24:a6:bd:ef:5c:e3:cc:20:3b:22:ea:
+ fc:44:c6:a8:e5:1f:e1:86:cd:0c:4d:8f:93:53:d9:7f:ee:a1:
+ 08:a7:b3:30:96:49:70:6e:a3:6c:3d:d0:63:ef:25:66:63:cc:
+ aa:b7:18:17:4e:ea:70:76:f6:ba:42:a6:80:37:09:4e:9f:66:
+ 88:2e:6b:33:66:c8:c0:71:a4:41:eb:5a:e3:fc:14:2e:4b:88:
+ fd:ae:6e:5b:65:e9:27:e4:bf:e4:b0:23:c1:b2:7d:5b:62:25:
+ d7:3e:10:d4
+-----BEGIN CERTIFICATE-----
+MIIGHjCCBQagAwIBAgIQLEjdkw31WY75PJlUemDtQzANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMTYxMTA3MjM1OTU5WjCBvjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE4MDYGA1UEAxMvVmVy
+aVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBTR0MgQ0EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9Voi6iDRkZM/NyrDu5xlzxXLZ
+u0W8taj/g74cA9vtibcuEBolvFXKQaGfC88ZXnC5XjlLnjEcX4euKqqoK6IbOxAj
+XxOx3QiMThTag4HjtYzjaO0kZ85Wtqybc5ZE24qMs9bwcZOO23FUSutzWWqPcFEs
+A5+X0cwRerxiDZUqyRx1V+n1x+q6hDXLx4VafuRN4RGXfQ4gNEXb8aIJ6+s9nriW
+Q140SwglHkMaotm3igE0PcP45a9PjP/NZfAjTsWXs1zakByChQ0GDcEitnsopAPD
+TFPRWLxyvAg5/KB2qKjpS26IPeOzMSWMcylIDjJ5Bu09Q/T25On8fb6OCNUfAgMB
+AAGjggIIMIICBDAdBgNVHQ4EFgQUTkPIHXbvN1N6T/JYb5TzOOLVvd8wEgYDVR0T
+AQH/BAgwBgEB/wIBADA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYc
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczA9BgNVHR8ENjA0MDKgMKAuhixo
+dHRwOi8vRVZTZWN1cmUtY3JsLnZlcmlzaWduLmNvbS9wY2EzLWc1LmNybDAOBgNV
+HQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgEGMG0GCCsGAQUFBwEMBGEwX6Fd
+oFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrU
+SBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMCkG
+A1UdEQQiMCCkHjAcMRowGAYDVQQDExFDbGFzczNDQTIwNDgtMS00ODAfBgNVHSME
+GDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzA9BggrBgEFBQcBAQQxMC8wLQYIKwYB
+BQUHMAGGIWh0dHA6Ly9FVlNlY3VyZS1vY3NwLnZlcmlzaWduLmNvbTA0BgNVHSUE
+LTArBglghkgBhvhCBAEGCmCGSAGG+EUBCAEGCCsGAQUFBwMBBggrBgEFBQcDAjAN
+BgkqhkiG9w0BAQUFAAOCAQEAJ3SmNOodneFT1hydDKdbTKln8vAytwEP+0IYON7k
+7knIE8kL7ATDQHEYcnZDAiNdq3vISBQayHsd/PYKnzah0glzcWaWdVE0v5kwUWed
+VLcmRaxzCCOGJplx9I7X6jmbBgkjv2LdqMS2faSJBz7zba5AWVB5lzc9Mnh9smNL
++eoIaQ4T7ejPu6wFhsoiz4hiXTwiSdhj1SSmve9c48wgOyLq/ETGqOUf4YbNDE2P
+k1PZf+6hCKezMJZJcG6jbD3QY+8lZmPMqrcYF07qcHb2ukKmgDcJTp9miC5rM2bI
+wHGkQeta4/wULkuI/a5uW2XpJ+S/5LAjwbJ9W2Il1z4Q1A==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert48[] = {
+ 0x30, 0x82, 0x06, 0x1e, 0x30, 0x82, 0x05, 0x06, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x2c, 0x48, 0xdd, 0x93, 0x0d, 0xf5, 0x59, 0x8e, 0xf9,
+ 0x3c, 0x99, 0x54, 0x7a, 0x60, 0xed, 0x43, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xbe, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30,
+ 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x36, 0x31, 0x38,
+ 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x53, 0x47, 0x43, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0x56, 0x88, 0xba, 0x88, 0x34, 0x64,
+ 0x64, 0xcf, 0xcd, 0xca, 0xb0, 0xee, 0xe7, 0x19, 0x73, 0xc5, 0x72, 0xd9,
+ 0xbb, 0x45, 0xbc, 0xb5, 0xa8, 0xff, 0x83, 0xbe, 0x1c, 0x03, 0xdb, 0xed,
+ 0x89, 0xb7, 0x2e, 0x10, 0x1a, 0x25, 0xbc, 0x55, 0xca, 0x41, 0xa1, 0x9f,
+ 0x0b, 0xcf, 0x19, 0x5e, 0x70, 0xb9, 0x5e, 0x39, 0x4b, 0x9e, 0x31, 0x1c,
+ 0x5f, 0x87, 0xae, 0x2a, 0xaa, 0xa8, 0x2b, 0xa2, 0x1b, 0x3b, 0x10, 0x23,
+ 0x5f, 0x13, 0xb1, 0xdd, 0x08, 0x8c, 0x4e, 0x14, 0xda, 0x83, 0x81, 0xe3,
+ 0xb5, 0x8c, 0xe3, 0x68, 0xed, 0x24, 0x67, 0xce, 0x56, 0xb6, 0xac, 0x9b,
+ 0x73, 0x96, 0x44, 0xdb, 0x8a, 0x8c, 0xb3, 0xd6, 0xf0, 0x71, 0x93, 0x8e,
+ 0xdb, 0x71, 0x54, 0x4a, 0xeb, 0x73, 0x59, 0x6a, 0x8f, 0x70, 0x51, 0x2c,
+ 0x03, 0x9f, 0x97, 0xd1, 0xcc, 0x11, 0x7a, 0xbc, 0x62, 0x0d, 0x95, 0x2a,
+ 0xc9, 0x1c, 0x75, 0x57, 0xe9, 0xf5, 0xc7, 0xea, 0xba, 0x84, 0x35, 0xcb,
+ 0xc7, 0x85, 0x5a, 0x7e, 0xe4, 0x4d, 0xe1, 0x11, 0x97, 0x7d, 0x0e, 0x20,
+ 0x34, 0x45, 0xdb, 0xf1, 0xa2, 0x09, 0xeb, 0xeb, 0x3d, 0x9e, 0xb8, 0x96,
+ 0x43, 0x5e, 0x34, 0x4b, 0x08, 0x25, 0x1e, 0x43, 0x1a, 0xa2, 0xd9, 0xb7,
+ 0x8a, 0x01, 0x34, 0x3d, 0xc3, 0xf8, 0xe5, 0xaf, 0x4f, 0x8c, 0xff, 0xcd,
+ 0x65, 0xf0, 0x23, 0x4e, 0xc5, 0x97, 0xb3, 0x5c, 0xda, 0x90, 0x1c, 0x82,
+ 0x85, 0x0d, 0x06, 0x0d, 0xc1, 0x22, 0xb6, 0x7b, 0x28, 0xa4, 0x03, 0xc3,
+ 0x4c, 0x53, 0xd1, 0x58, 0xbc, 0x72, 0xbc, 0x08, 0x39, 0xfc, 0xa0, 0x76,
+ 0xa8, 0xa8, 0xe9, 0x4b, 0x6e, 0x88, 0x3d, 0xe3, 0xb3, 0x31, 0x25, 0x8c,
+ 0x73, 0x29, 0x48, 0x0e, 0x32, 0x79, 0x06, 0xed, 0x3d, 0x43, 0xf4, 0xf6,
+ 0xe4, 0xe9, 0xfc, 0x7d, 0xbe, 0x8e, 0x08, 0xd5, 0x1f, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x02, 0x08, 0x30, 0x82, 0x02, 0x04, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x4e, 0x43, 0xc8,
+ 0x1d, 0x76, 0xef, 0x37, 0x53, 0x7a, 0x4f, 0xf2, 0x58, 0x6f, 0x94, 0xf3,
+ 0x38, 0xe2, 0xd5, 0xbd, 0xdf, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34,
+ 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x63, 0x70, 0x73, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x36, 0x30, 0x34, 0x30, 0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33,
+ 0x2d, 0x67, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x11, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d,
+ 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d,
+ 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30,
+ 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5,
+ 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4,
+ 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76,
+ 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x29, 0x06,
+ 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c,
+ 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x43,
+ 0x6c, 0x61, 0x73, 0x73, 0x33, 0x43, 0x41, 0x32, 0x30, 0x34, 0x38, 0x2d,
+ 0x31, 0x2d, 0x34, 0x38, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec,
+ 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31,
+ 0x33, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x6f,
+ 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x2d, 0x30, 0x2b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42,
+ 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01,
+ 0x08, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x27, 0x74, 0xa6, 0x34, 0xea, 0x1d,
+ 0x9d, 0xe1, 0x53, 0xd6, 0x1c, 0x9d, 0x0c, 0xa7, 0x5b, 0x4c, 0xa9, 0x67,
+ 0xf2, 0xf0, 0x32, 0xb7, 0x01, 0x0f, 0xfb, 0x42, 0x18, 0x38, 0xde, 0xe4,
+ 0xee, 0x49, 0xc8, 0x13, 0xc9, 0x0b, 0xec, 0x04, 0xc3, 0x40, 0x71, 0x18,
+ 0x72, 0x76, 0x43, 0x02, 0x23, 0x5d, 0xab, 0x7b, 0xc8, 0x48, 0x14, 0x1a,
+ 0xc8, 0x7b, 0x1d, 0xfc, 0xf6, 0x0a, 0x9f, 0x36, 0xa1, 0xd2, 0x09, 0x73,
+ 0x71, 0x66, 0x96, 0x75, 0x51, 0x34, 0xbf, 0x99, 0x30, 0x51, 0x67, 0x9d,
+ 0x54, 0xb7, 0x26, 0x45, 0xac, 0x73, 0x08, 0x23, 0x86, 0x26, 0x99, 0x71,
+ 0xf4, 0x8e, 0xd7, 0xea, 0x39, 0x9b, 0x06, 0x09, 0x23, 0xbf, 0x62, 0xdd,
+ 0xa8, 0xc4, 0xb6, 0x7d, 0xa4, 0x89, 0x07, 0x3e, 0xf3, 0x6d, 0xae, 0x40,
+ 0x59, 0x50, 0x79, 0x97, 0x37, 0x3d, 0x32, 0x78, 0x7d, 0xb2, 0x63, 0x4b,
+ 0xf9, 0xea, 0x08, 0x69, 0x0e, 0x13, 0xed, 0xe8, 0xcf, 0xbb, 0xac, 0x05,
+ 0x86, 0xca, 0x22, 0xcf, 0x88, 0x62, 0x5d, 0x3c, 0x22, 0x49, 0xd8, 0x63,
+ 0xd5, 0x24, 0xa6, 0xbd, 0xef, 0x5c, 0xe3, 0xcc, 0x20, 0x3b, 0x22, 0xea,
+ 0xfc, 0x44, 0xc6, 0xa8, 0xe5, 0x1f, 0xe1, 0x86, 0xcd, 0x0c, 0x4d, 0x8f,
+ 0x93, 0x53, 0xd9, 0x7f, 0xee, 0xa1, 0x08, 0xa7, 0xb3, 0x30, 0x96, 0x49,
+ 0x70, 0x6e, 0xa3, 0x6c, 0x3d, 0xd0, 0x63, 0xef, 0x25, 0x66, 0x63, 0xcc,
+ 0xaa, 0xb7, 0x18, 0x17, 0x4e, 0xea, 0x70, 0x76, 0xf6, 0xba, 0x42, 0xa6,
+ 0x80, 0x37, 0x09, 0x4e, 0x9f, 0x66, 0x88, 0x2e, 0x6b, 0x33, 0x66, 0xc8,
+ 0xc0, 0x71, 0xa4, 0x41, 0xeb, 0x5a, 0xe3, 0xfc, 0x14, 0x2e, 0x4b, 0x88,
+ 0xfd, 0xae, 0x6e, 0x5b, 0x65, 0xe9, 0x27, 0xe4, 0xbf, 0xe4, 0xb0, 0x23,
+ 0xc1, 0xb2, 0x7d, 0x5b, 0x62, 0x25, 0xd7, 0x3e, 0x10, 0xd4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 64:1b:e8:20:ce:02:08:13:f3:2d:4d:2d:95:d6:7e:67
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Feb 8 00:00:00 2010 GMT
+ Not After : Feb 7 23:59:59 2020 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)10, CN=VeriSign Class 3 International Server CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:99:d6:9c:62:f0:15:f4:81:9a:41:08:59:8f:13:
+ 9d:17:c9:9f:51:dc:da:b1:52:ef:ff:e3:41:dd:e0:
+ df:c4:28:c6:e3:ad:79:1f:27:10:98:b8:bb:20:97:
+ c1:28:44:41:0f:ea:a9:a8:52:cf:4d:4e:1b:8b:bb:
+ b5:c4:76:d9:cc:56:06:ee:b3:55:20:2a:de:15:8d:
+ 71:cb:54:c8:6f:17:cd:89:00:e4:dc:ff:e1:c0:1f:
+ 68:71:e9:c7:29:2e:7e:bc:3b:fc:e5:bb:ab:26:54:
+ 8b:66:90:cd:f6:92:b9:31:24:80:bc:9e:6c:d5:fc:
+ 7e:d2:e1:4b:8c:dc:42:fa:44:4b:5f:f8:18:b5:2e:
+ 30:f4:3d:12:98:d3:62:05:73:54:a6:9c:a2:1d:be:
+ 52:83:3a:07:46:c4:3b:02:56:21:bf:f2:51:4f:d0:
+ a6:99:39:e9:ae:a5:3f:89:9b:9c:7d:fe:4d:60:07:
+ 25:20:f7:bb:d7:69:83:2b:82:93:43:37:d9:83:41:
+ 1b:6b:0b:ab:4a:66:84:4f:4a:8e:de:7e:34:99:8e:
+ 68:d6:ca:39:06:9b:4c:b3:9a:48:4d:13:46:b4:58:
+ 21:04:c4:fb:a0:4d:ac:2e:4b:62:12:e3:fb:4d:f6:
+ c9:51:00:01:1f:fc:1e:6a:81:2a:38:e0:b9:4f:d6:
+ 2d:45
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.23.3
+ CPS: https://www.verisign.com/cps
+ User Notice:
+ Explicit Text: https://www.verisign.com/rpa
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3-g5.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-7
+ X509v3 Subject Key Identifier:
+ D7:9B:7C:D8:22:A0:15:F7:DD:AD:5F:CE:29:9B:58:C3:BC:46:00:B5
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 71:b5:7d:73:52:4a:dd:d7:4d:34:2b:2e:af:94:46:a5:49:50:
+ 02:4f:f8:2f:17:70:f2:13:dc:1f:21:86:aa:c2:4f:7c:37:3c:
+ d4:46:78:ae:5d:78:6f:d1:ba:5a:bc:10:ab:58:36:c5:8c:62:
+ 15:45:60:17:21:e2:d5:42:a8:77:a1:55:d8:43:04:51:f6:6e:
+ ba:48:e6:5d:4c:b7:44:d3:3e:a4:d5:d6:33:9a:9f:0d:e6:d7:
+ 4e:96:44:95:5a:6c:d6:a3:16:53:0e:98:43:ce:a4:b8:c3:66:
+ 7a:05:5c:62:10:e8:1b:12:db:7d:2e:76:50:ff:df:d7:6b:1b:
+ cc:8a:cc:71:fa:b3:40:56:7c:33:7a:77:94:5b:f5:0b:53:fb:
+ 0e:5f:bc:68:fb:af:2a:ee:30:37:79:16:93:25:7f:4d:10:ff:
+ 57:fb:bf:6e:3b:33:21:de:79:dc:86:17:59:2d:43:64:b7:a6:
+ 66:87:ea:bc:96:46:19:1a:86:8b:6f:d7:b7:49:00:5b:db:a3:
+ bf:29:9a:ee:f7:d3:33:ae:a3:f4:9e:4c:ca:5e:69:d4:1b:ad:
+ b7:90:77:6a:d8:59:6f:79:ab:01:fa:55:f0:8a:21:66:e5:65:
+ 6e:fd:7c:d3:df:1e:eb:7e:3f:06:90:fb:19:0b:d3:06:02:1b:
+ 78:43:99:a8
+-----BEGIN CERTIFICATE-----
+MIIGKTCCBRGgAwIBAgIQZBvoIM4CCBPzLU0tldZ+ZzANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBvDEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDE2MDQGA1UEAxMtVmVy
+aVNpZ24gQ2xhc3MgMyBJbnRlcm5hdGlvbmFsIFNlcnZlciBDQSAtIEczMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmdacYvAV9IGaQQhZjxOdF8mfUdza
+sVLv/+NB3eDfxCjG4615HycQmLi7IJfBKERBD+qpqFLPTU4bi7u1xHbZzFYG7rNV
+ICreFY1xy1TIbxfNiQDk3P/hwB9ocenHKS5+vDv85burJlSLZpDN9pK5MSSAvJ5s
+1fx+0uFLjNxC+kRLX/gYtS4w9D0SmNNiBXNUppyiHb5SgzoHRsQ7AlYhv/JRT9Cm
+mTnprqU/iZucff5NYAclIPe712mDK4KTQzfZg0EbawurSmaET0qO3n40mY5o1so5
+BptMs5pITRNGtFghBMT7oE2sLktiEuP7TfbJUQABH/weaoEqOOC5T9YtRQIDAQAB
+o4ICFTCCAhEwEgYDVR0TAQH/BAgwBgEB/wIBADBwBgNVHSAEaTBnMGUGC2CGSAGG
++EUBBxcDMFYwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9j
+cHMwKgYIKwYBBQUHAgIwHhocaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYTAO
+BgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
+Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDov
+L2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwNAYDVR0lBC0wKwYIKwYBBQUH
+AwEGCCsGAQUFBwMCBglghkgBhvhCBAEGCmCGSAGG+EUBCAEwNAYIKwYBBQUHAQEE
+KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wNAYDVR0f
+BC0wKzApoCegJYYjaHR0cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy1nNS5jcmww
+KAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFZlcmlTaWduTVBLSS0yLTcwHQYDVR0O
+BBYEFNebfNgioBX33a1fzimbWMO8RgC1MB8GA1UdIwQYMBaAFH/TZafC3ey78DAJ
+80M5+gKvMzEzMA0GCSqGSIb3DQEBBQUAA4IBAQBxtX1zUkrd1000Ky6vlEalSVAC
+T/gvF3DyE9wfIYaqwk98NzzURniuXXhv0bpavBCrWDbFjGIVRWAXIeLVQqh3oVXY
+QwRR9m66SOZdTLdE0z6k1dYzmp8N5tdOlkSVWmzWoxZTDphDzqS4w2Z6BVxiEOgb
+Ett9LnZQ/9/XaxvMisxx+rNAVnwzeneUW/ULU/sOX7xo+68q7jA3eRaTJX9NEP9X
++79uOzMh3nnchhdZLUNkt6Zmh+q8lkYZGoaLb9e3SQBb26O/KZru99MzrqP0nkzK
+XmnUG623kHdq2FlveasB+lXwiiFm5WVu/XzT3x7rfj8GkPsZC9MGAht4Q5mo
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert49[] = {
+ 0x30, 0x82, 0x06, 0x29, 0x30, 0x82, 0x05, 0x11, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x64, 0x1b, 0xe8, 0x20, 0xce, 0x02, 0x08, 0x13, 0xf3,
+ 0x2d, 0x4d, 0x2d, 0x95, 0xd6, 0x7e, 0x67, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x32, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xbc, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30,
+ 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x31, 0x30, 0x31, 0x36,
+ 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43,
+ 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0x99, 0xd6, 0x9c, 0x62, 0xf0, 0x15, 0xf4, 0x81, 0x9a,
+ 0x41, 0x08, 0x59, 0x8f, 0x13, 0x9d, 0x17, 0xc9, 0x9f, 0x51, 0xdc, 0xda,
+ 0xb1, 0x52, 0xef, 0xff, 0xe3, 0x41, 0xdd, 0xe0, 0xdf, 0xc4, 0x28, 0xc6,
+ 0xe3, 0xad, 0x79, 0x1f, 0x27, 0x10, 0x98, 0xb8, 0xbb, 0x20, 0x97, 0xc1,
+ 0x28, 0x44, 0x41, 0x0f, 0xea, 0xa9, 0xa8, 0x52, 0xcf, 0x4d, 0x4e, 0x1b,
+ 0x8b, 0xbb, 0xb5, 0xc4, 0x76, 0xd9, 0xcc, 0x56, 0x06, 0xee, 0xb3, 0x55,
+ 0x20, 0x2a, 0xde, 0x15, 0x8d, 0x71, 0xcb, 0x54, 0xc8, 0x6f, 0x17, 0xcd,
+ 0x89, 0x00, 0xe4, 0xdc, 0xff, 0xe1, 0xc0, 0x1f, 0x68, 0x71, 0xe9, 0xc7,
+ 0x29, 0x2e, 0x7e, 0xbc, 0x3b, 0xfc, 0xe5, 0xbb, 0xab, 0x26, 0x54, 0x8b,
+ 0x66, 0x90, 0xcd, 0xf6, 0x92, 0xb9, 0x31, 0x24, 0x80, 0xbc, 0x9e, 0x6c,
+ 0xd5, 0xfc, 0x7e, 0xd2, 0xe1, 0x4b, 0x8c, 0xdc, 0x42, 0xfa, 0x44, 0x4b,
+ 0x5f, 0xf8, 0x18, 0xb5, 0x2e, 0x30, 0xf4, 0x3d, 0x12, 0x98, 0xd3, 0x62,
+ 0x05, 0x73, 0x54, 0xa6, 0x9c, 0xa2, 0x1d, 0xbe, 0x52, 0x83, 0x3a, 0x07,
+ 0x46, 0xc4, 0x3b, 0x02, 0x56, 0x21, 0xbf, 0xf2, 0x51, 0x4f, 0xd0, 0xa6,
+ 0x99, 0x39, 0xe9, 0xae, 0xa5, 0x3f, 0x89, 0x9b, 0x9c, 0x7d, 0xfe, 0x4d,
+ 0x60, 0x07, 0x25, 0x20, 0xf7, 0xbb, 0xd7, 0x69, 0x83, 0x2b, 0x82, 0x93,
+ 0x43, 0x37, 0xd9, 0x83, 0x41, 0x1b, 0x6b, 0x0b, 0xab, 0x4a, 0x66, 0x84,
+ 0x4f, 0x4a, 0x8e, 0xde, 0x7e, 0x34, 0x99, 0x8e, 0x68, 0xd6, 0xca, 0x39,
+ 0x06, 0x9b, 0x4c, 0xb3, 0x9a, 0x48, 0x4d, 0x13, 0x46, 0xb4, 0x58, 0x21,
+ 0x04, 0xc4, 0xfb, 0xa0, 0x4d, 0xac, 0x2e, 0x4b, 0x62, 0x12, 0xe3, 0xfb,
+ 0x4d, 0xf6, 0xc9, 0x51, 0x00, 0x01, 0x1f, 0xfc, 0x1e, 0x6a, 0x81, 0x2a,
+ 0x38, 0xe0, 0xb9, 0x4f, 0xd6, 0x2d, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x02, 0x15, 0x30, 0x82, 0x02, 0x11, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x69, 0x30, 0x67, 0x30, 0x65, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x45, 0x01, 0x07, 0x17, 0x03, 0x30, 0x56, 0x30, 0x28, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
+ 0x70, 0x73, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x02, 0x30, 0x1e, 0x1a, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59,
+ 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f,
+ 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b,
+ 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac,
+ 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b,
+ 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67,
+ 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x25,
+ 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06,
+ 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x28, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d,
+ 0x30, 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x10, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b,
+ 0x49, 0x2d, 0x32, 0x2d, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xd7, 0x9b, 0x7c, 0xd8, 0x22, 0xa0, 0x15, 0xf7,
+ 0xdd, 0xad, 0x5f, 0xce, 0x29, 0x9b, 0x58, 0xc3, 0xbc, 0x46, 0x00, 0xb5,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09,
+ 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x71, 0xb5, 0x7d, 0x73, 0x52, 0x4a, 0xdd,
+ 0xd7, 0x4d, 0x34, 0x2b, 0x2e, 0xaf, 0x94, 0x46, 0xa5, 0x49, 0x50, 0x02,
+ 0x4f, 0xf8, 0x2f, 0x17, 0x70, 0xf2, 0x13, 0xdc, 0x1f, 0x21, 0x86, 0xaa,
+ 0xc2, 0x4f, 0x7c, 0x37, 0x3c, 0xd4, 0x46, 0x78, 0xae, 0x5d, 0x78, 0x6f,
+ 0xd1, 0xba, 0x5a, 0xbc, 0x10, 0xab, 0x58, 0x36, 0xc5, 0x8c, 0x62, 0x15,
+ 0x45, 0x60, 0x17, 0x21, 0xe2, 0xd5, 0x42, 0xa8, 0x77, 0xa1, 0x55, 0xd8,
+ 0x43, 0x04, 0x51, 0xf6, 0x6e, 0xba, 0x48, 0xe6, 0x5d, 0x4c, 0xb7, 0x44,
+ 0xd3, 0x3e, 0xa4, 0xd5, 0xd6, 0x33, 0x9a, 0x9f, 0x0d, 0xe6, 0xd7, 0x4e,
+ 0x96, 0x44, 0x95, 0x5a, 0x6c, 0xd6, 0xa3, 0x16, 0x53, 0x0e, 0x98, 0x43,
+ 0xce, 0xa4, 0xb8, 0xc3, 0x66, 0x7a, 0x05, 0x5c, 0x62, 0x10, 0xe8, 0x1b,
+ 0x12, 0xdb, 0x7d, 0x2e, 0x76, 0x50, 0xff, 0xdf, 0xd7, 0x6b, 0x1b, 0xcc,
+ 0x8a, 0xcc, 0x71, 0xfa, 0xb3, 0x40, 0x56, 0x7c, 0x33, 0x7a, 0x77, 0x94,
+ 0x5b, 0xf5, 0x0b, 0x53, 0xfb, 0x0e, 0x5f, 0xbc, 0x68, 0xfb, 0xaf, 0x2a,
+ 0xee, 0x30, 0x37, 0x79, 0x16, 0x93, 0x25, 0x7f, 0x4d, 0x10, 0xff, 0x57,
+ 0xfb, 0xbf, 0x6e, 0x3b, 0x33, 0x21, 0xde, 0x79, 0xdc, 0x86, 0x17, 0x59,
+ 0x2d, 0x43, 0x64, 0xb7, 0xa6, 0x66, 0x87, 0xea, 0xbc, 0x96, 0x46, 0x19,
+ 0x1a, 0x86, 0x8b, 0x6f, 0xd7, 0xb7, 0x49, 0x00, 0x5b, 0xdb, 0xa3, 0xbf,
+ 0x29, 0x9a, 0xee, 0xf7, 0xd3, 0x33, 0xae, 0xa3, 0xf4, 0x9e, 0x4c, 0xca,
+ 0x5e, 0x69, 0xd4, 0x1b, 0xad, 0xb7, 0x90, 0x77, 0x6a, 0xd8, 0x59, 0x6f,
+ 0x79, 0xab, 0x01, 0xfa, 0x55, 0xf0, 0x8a, 0x21, 0x66, 0xe5, 0x65, 0x6e,
+ 0xfd, 0x7c, 0xd3, 0xdf, 0x1e, 0xeb, 0x7e, 0x3f, 0x06, 0x90, 0xfb, 0x19,
+ 0x0b, 0xd3, 0x06, 0x02, 0x1b, 0x78, 0x43, 0x99, 0xa8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 26 (0x1a)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+ Validity
+ Not Before: Oct 24 20:57:09 2007 GMT
+ Not After : Oct 24 20:57:09 2017 GMT
+ Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Class 2 Primary Intermediate Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e2:4f:39:2f:a1:8c:9a:85:ad:08:0e:08:3e:57:
+ f2:88:01:21:1b:94:a9:6c:e2:b8:db:aa:19:18:46:
+ 3a:52:a1:f5:0f:f4:6e:8c:ea:96:8c:96:87:79:13:
+ 40:51:2f:22:f2:0c:8b:87:0f:65:df:71:74:34:43:
+ 55:b1:35:09:9b:d9:bc:1f:fa:eb:42:d0:97:40:72:
+ b7:43:96:3d:ba:96:9d:5d:50:02:1c:9b:91:8d:9c:
+ c0:ac:d7:bb:2f:17:d7:cb:3e:82:9d:73:eb:07:42:
+ 92:b2:cd:64:b3:74:55:1b:b4:4b:86:21:2c:f7:78:
+ 87:32:e0:16:e4:da:bd:4c:95:ea:a4:0a:7e:b6:0a:
+ 0d:2e:8a:cf:55:ab:c3:e5:dd:41:8a:4e:e6:6f:65:
+ 6c:b2:40:cf:17:5d:b9:c3:6a:0b:27:11:84:77:61:
+ f6:c2:7c:ed:c0:8d:78:14:18:99:81:99:75:63:b7:
+ e8:53:d3:ba:61:e9:0e:fa:a2:30:f3:46:a2:b9:c9:
+ 1f:6c:80:5a:40:ac:27:ed:48:47:33:b0:54:c6:46:
+ 1a:f3:35:61:c1:02:29:90:54:7e:64:4d:c4:30:52:
+ 02:82:d7:df:ce:21:6e:18:91:d7:b8:ab:8c:27:17:
+ b5:f0:a3:01:2f:8e:d2:2e:87:3a:3d:b4:29:67:8a:
+ c4:03
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 11:DB:23:45:FD:54:CC:6A:71:6F:84:8A:03:D7:BE:F7:01:2F:26:86
+ X509v3 Authority Key Identifier:
+ keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.startssl.com/ca
+ CA Issuers - URI:http://www.startssl.com/sfsca.crt
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.startssl.com/sfsca.crl
+
+ Full Name:
+ URI:http://crl.startssl.com/sfsca.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.23223.1.2.1
+ CPS: http://www.startssl.com/policy.pdf
+ CPS: http://www.startssl.com/intermediate.pdf
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 9d:07:e1:ee:90:76:31:67:16:45:70:8c:cb:84:8b:4b:57:68:
+ 44:a5:89:c1:f2:7e:cb:28:8b:f5:e7:70:77:d5:b6:f4:0b:21:
+ 60:a5:a1:74:73:24:22:80:d6:d8:ba:8d:a2:62:5d:09:35:42:
+ 29:fb:39:63:45:0b:a4:b0:38:1a:68:f4:95:13:cc:e0:43:94:
+ ec:eb:39:1a:ec:57:29:d9:99:6d:f5:84:cd:8e:73:ae:c9:dc:
+ 6a:fa:9e:9d:16:64:93:08:c7:1c:c2:89:54:9e:77:80:90:f6:
+ b9:29:76:eb:13:67:48:59:f8:2e:3a:31:b8:c9:d3:88:e5:5f:
+ 4e:d2:19:3d:43:8e:d7:92:ff:cf:38:b6:e1:5b:8a:53:1d:ce:
+ ac:b4:76:2f:d8:f7:40:63:d5:ee:69:f3:45:7d:a0:62:c1:61:
+ c3:75:ed:b2:7b:4d:ac:21:27:30:4e:59:46:6a:93:17:ca:c8:
+ 39:2d:01:73:65:5b:e9:41:9b:11:17:9c:c8:c8:4a:ef:a1:76:
+ 60:2d:ae:93:ff:0c:d5:33:13:9f:4f:13:ce:dd:86:f1:fc:f8:
+ 35:54:15:a8:5b:e7:85:7e:fa:37:09:ff:8b:b8:31:49:9e:0d:
+ 6e:de:b4:d2:12:2d:b8:ed:c8:c3:f1:b6:42:a0:4c:97:79:df:
+ fe:c3:a3:9f:a1:f4:6d:2c:84:77:a4:a2:05:e1:17:ff:31:dd:
+ 9a:f3:b8:7a:c3:52:c2:11:11:b7:50:31:8a:7f:cc:e7:5a:89:
+ cc:f7:86:9a:61:92:4f:2f:94:b6:98:c7:78:e0:62:4b:43:7d:
+ 3c:de:d6:9a:b4:10:a1:40:9c:4b:2a:dc:b8:d0:d4:9e:fd:f1:
+ 84:78:1b:0e:57:8f:69:54:42:68:7b:ea:a0:ef:75:0f:07:a2:
+ 8c:73:99:ab:55:f5:07:09:d2:af:38:03:6a:90:03:0c:2f:8f:
+ e2:e8:43:c2:31:e9:6f:ad:87:e5:8d:bd:4e:2c:89:4b:51:e6:
+ 9c:4c:54:76:c0:12:81:53:9b:ec:a0:fc:2c:9c:da:18:95:6e:
+ 1e:38:26:42:27:78:60:08:df:7f:6d:32:e8:d8:c0:6f:1f:eb:
+ 26:75:9f:93:fc:7b:1b:fe:35:90:dc:53:a3:07:a6:3f:83:55:
+ 0a:2b:4e:62:82:25:ce:66:30:5d:2c:e0:f9:19:1b:75:b9:9d:
+ 98:56:a6:83:27:7a:d1:8f:8d:59:93:fc:3f:73:d7:2e:b4:2c:
+ 95:d8:8b:f7:c9:7e:c7:fc:9d:ac:72:04:1f:d2:cc:17:f4:ed:
+ 34:60:9b:9e:4a:97:04:fe:dd:72:0e:57:54:51:06:70:4d:ef:
+ aa:1c:a4:82:e0:33:c7:f4
+-----BEGIN CERTIFICATE-----
+MIIGNDCCBBygAwIBAgIBGjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NzA5WhcNMTcxMDI0MjA1NzA5WjCB
+jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
+IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
+YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4k85L6GMmoWtCA4IPlfyiAEh
+G5SpbOK426oZGEY6UqH1D/RujOqWjJaHeRNAUS8i8gyLhw9l33F0NENVsTUJm9m8
+H/rrQtCXQHK3Q5Y9upadXVACHJuRjZzArNe7LxfXyz6CnXPrB0KSss1ks3RVG7RL
+hiEs93iHMuAW5Nq9TJXqpAp+tgoNLorPVavD5d1Bik7mb2VsskDPF125w2oLJxGE
+d2H2wnztwI14FBiZgZl1Y7foU9O6YekO+qIw80aiuckfbIBaQKwn7UhHM7BUxkYa
+8zVhwQIpkFR+ZE3EMFICgtffziFuGJHXuKuMJxe18KMBL47SLoc6PbQpZ4rEAwID
+AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFBHbI0X9VMxqcW+EigPXvvcBLyaGMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
+L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
+YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
+dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
+c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
+BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
+BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
+LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAnQfh7pB2MWcWRXCMy4SLS1doRKWJwfJ+
+yyiL9edwd9W29AshYKWhdHMkIoDW2LqNomJdCTVCKfs5Y0ULpLA4Gmj0lRPM4EOU
+7Os5GuxXKdmZbfWEzY5zrsncavqenRZkkwjHHMKJVJ53gJD2uSl26xNnSFn4Ljox
+uMnTiOVfTtIZPUOO15L/zzi24VuKUx3OrLR2L9j3QGPV7mnzRX2gYsFhw3XtsntN
+rCEnME5ZRmqTF8rIOS0Bc2Vb6UGbERecyMhK76F2YC2uk/8M1TMTn08Tzt2G8fz4
+NVQVqFvnhX76Nwn/i7gxSZ4Nbt600hItuO3Iw/G2QqBMl3nf/sOjn6H0bSyEd6Si
+BeEX/zHdmvO4esNSwhERt1Axin/M51qJzPeGmmGSTy+UtpjHeOBiS0N9PN7WmrQQ
+oUCcSyrcuNDUnv3xhHgbDlePaVRCaHvqoO91DweijHOZq1X1BwnSrzgDapADDC+P
+4uhDwjHpb62H5Y29TiyJS1HmnExUdsASgVOb7KD8LJzaGJVuHjgmQid4YAjff20y
+6NjAbx/rJnWfk/x7G/41kNxTowemP4NVCitOYoIlzmYwXSzg+RkbdbmdmFamgyd6
+0Y+NWZP8P3PXLrQsldiL98l+x/ydrHIEH9LMF/TtNGCbnkqXBP7dcg5XVFEGcE3v
+qhykguAzx/Q=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert50[] = {
+ 0x30, 0x82, 0x06, 0x34, 0x30, 0x82, 0x04, 0x1c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x01, 0x1a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x7d, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16,
+ 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61,
+ 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b,
+ 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
+ 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43,
+ 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x31, 0x30, 0x32, 0x34,
+ 0x32, 0x30, 0x35, 0x37, 0x30, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x31,
+ 0x30, 0x32, 0x34, 0x32, 0x30, 0x35, 0x37, 0x30, 0x39, 0x5a, 0x30, 0x81,
+ 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74,
+ 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31,
+ 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73,
+ 0x20, 0x32, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49,
+ 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe2, 0x4f, 0x39, 0x2f, 0xa1, 0x8c,
+ 0x9a, 0x85, 0xad, 0x08, 0x0e, 0x08, 0x3e, 0x57, 0xf2, 0x88, 0x01, 0x21,
+ 0x1b, 0x94, 0xa9, 0x6c, 0xe2, 0xb8, 0xdb, 0xaa, 0x19, 0x18, 0x46, 0x3a,
+ 0x52, 0xa1, 0xf5, 0x0f, 0xf4, 0x6e, 0x8c, 0xea, 0x96, 0x8c, 0x96, 0x87,
+ 0x79, 0x13, 0x40, 0x51, 0x2f, 0x22, 0xf2, 0x0c, 0x8b, 0x87, 0x0f, 0x65,
+ 0xdf, 0x71, 0x74, 0x34, 0x43, 0x55, 0xb1, 0x35, 0x09, 0x9b, 0xd9, 0xbc,
+ 0x1f, 0xfa, 0xeb, 0x42, 0xd0, 0x97, 0x40, 0x72, 0xb7, 0x43, 0x96, 0x3d,
+ 0xba, 0x96, 0x9d, 0x5d, 0x50, 0x02, 0x1c, 0x9b, 0x91, 0x8d, 0x9c, 0xc0,
+ 0xac, 0xd7, 0xbb, 0x2f, 0x17, 0xd7, 0xcb, 0x3e, 0x82, 0x9d, 0x73, 0xeb,
+ 0x07, 0x42, 0x92, 0xb2, 0xcd, 0x64, 0xb3, 0x74, 0x55, 0x1b, 0xb4, 0x4b,
+ 0x86, 0x21, 0x2c, 0xf7, 0x78, 0x87, 0x32, 0xe0, 0x16, 0xe4, 0xda, 0xbd,
+ 0x4c, 0x95, 0xea, 0xa4, 0x0a, 0x7e, 0xb6, 0x0a, 0x0d, 0x2e, 0x8a, 0xcf,
+ 0x55, 0xab, 0xc3, 0xe5, 0xdd, 0x41, 0x8a, 0x4e, 0xe6, 0x6f, 0x65, 0x6c,
+ 0xb2, 0x40, 0xcf, 0x17, 0x5d, 0xb9, 0xc3, 0x6a, 0x0b, 0x27, 0x11, 0x84,
+ 0x77, 0x61, 0xf6, 0xc2, 0x7c, 0xed, 0xc0, 0x8d, 0x78, 0x14, 0x18, 0x99,
+ 0x81, 0x99, 0x75, 0x63, 0xb7, 0xe8, 0x53, 0xd3, 0xba, 0x61, 0xe9, 0x0e,
+ 0xfa, 0xa2, 0x30, 0xf3, 0x46, 0xa2, 0xb9, 0xc9, 0x1f, 0x6c, 0x80, 0x5a,
+ 0x40, 0xac, 0x27, 0xed, 0x48, 0x47, 0x33, 0xb0, 0x54, 0xc6, 0x46, 0x1a,
+ 0xf3, 0x35, 0x61, 0xc1, 0x02, 0x29, 0x90, 0x54, 0x7e, 0x64, 0x4d, 0xc4,
+ 0x30, 0x52, 0x02, 0x82, 0xd7, 0xdf, 0xce, 0x21, 0x6e, 0x18, 0x91, 0xd7,
+ 0xb8, 0xab, 0x8c, 0x27, 0x17, 0xb5, 0xf0, 0xa3, 0x01, 0x2f, 0x8e, 0xd2,
+ 0x2e, 0x87, 0x3a, 0x3d, 0xb4, 0x29, 0x67, 0x8a, 0xc4, 0x03, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xad, 0x30, 0x82, 0x01, 0xa9, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30,
+ 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x11, 0xdb, 0x23, 0x45, 0xfd,
+ 0x54, 0xcc, 0x6a, 0x71, 0x6f, 0x84, 0x8a, 0x03, 0xd7, 0xbe, 0xf7, 0x01,
+ 0x2f, 0x26, 0x86, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef, 0x1a, 0xa4, 0x40, 0x5b, 0xa5,
+ 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68, 0x43, 0xd0, 0x41, 0xae, 0xf2,
+ 0x30, 0x66, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x5a, 0x30, 0x58, 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73,
+ 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x30, 0x2d, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x21, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73,
+ 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x5b, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x54, 0x30, 0x52, 0x30, 0x27, 0xa0, 0x25, 0xa0,
+ 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63,
+ 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0x80, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x79, 0x30, 0x77, 0x30, 0x75, 0x06, 0x0b, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x81, 0xb5, 0x37, 0x01, 0x02, 0x01, 0x30, 0x66, 0x30, 0x2e,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x22,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70, 0x64, 0x66, 0x30, 0x34,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x28,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65,
+ 0x2e, 0x70, 0x64, 0x66, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00,
+ 0x9d, 0x07, 0xe1, 0xee, 0x90, 0x76, 0x31, 0x67, 0x16, 0x45, 0x70, 0x8c,
+ 0xcb, 0x84, 0x8b, 0x4b, 0x57, 0x68, 0x44, 0xa5, 0x89, 0xc1, 0xf2, 0x7e,
+ 0xcb, 0x28, 0x8b, 0xf5, 0xe7, 0x70, 0x77, 0xd5, 0xb6, 0xf4, 0x0b, 0x21,
+ 0x60, 0xa5, 0xa1, 0x74, 0x73, 0x24, 0x22, 0x80, 0xd6, 0xd8, 0xba, 0x8d,
+ 0xa2, 0x62, 0x5d, 0x09, 0x35, 0x42, 0x29, 0xfb, 0x39, 0x63, 0x45, 0x0b,
+ 0xa4, 0xb0, 0x38, 0x1a, 0x68, 0xf4, 0x95, 0x13, 0xcc, 0xe0, 0x43, 0x94,
+ 0xec, 0xeb, 0x39, 0x1a, 0xec, 0x57, 0x29, 0xd9, 0x99, 0x6d, 0xf5, 0x84,
+ 0xcd, 0x8e, 0x73, 0xae, 0xc9, 0xdc, 0x6a, 0xfa, 0x9e, 0x9d, 0x16, 0x64,
+ 0x93, 0x08, 0xc7, 0x1c, 0xc2, 0x89, 0x54, 0x9e, 0x77, 0x80, 0x90, 0xf6,
+ 0xb9, 0x29, 0x76, 0xeb, 0x13, 0x67, 0x48, 0x59, 0xf8, 0x2e, 0x3a, 0x31,
+ 0xb8, 0xc9, 0xd3, 0x88, 0xe5, 0x5f, 0x4e, 0xd2, 0x19, 0x3d, 0x43, 0x8e,
+ 0xd7, 0x92, 0xff, 0xcf, 0x38, 0xb6, 0xe1, 0x5b, 0x8a, 0x53, 0x1d, 0xce,
+ 0xac, 0xb4, 0x76, 0x2f, 0xd8, 0xf7, 0x40, 0x63, 0xd5, 0xee, 0x69, 0xf3,
+ 0x45, 0x7d, 0xa0, 0x62, 0xc1, 0x61, 0xc3, 0x75, 0xed, 0xb2, 0x7b, 0x4d,
+ 0xac, 0x21, 0x27, 0x30, 0x4e, 0x59, 0x46, 0x6a, 0x93, 0x17, 0xca, 0xc8,
+ 0x39, 0x2d, 0x01, 0x73, 0x65, 0x5b, 0xe9, 0x41, 0x9b, 0x11, 0x17, 0x9c,
+ 0xc8, 0xc8, 0x4a, 0xef, 0xa1, 0x76, 0x60, 0x2d, 0xae, 0x93, 0xff, 0x0c,
+ 0xd5, 0x33, 0x13, 0x9f, 0x4f, 0x13, 0xce, 0xdd, 0x86, 0xf1, 0xfc, 0xf8,
+ 0x35, 0x54, 0x15, 0xa8, 0x5b, 0xe7, 0x85, 0x7e, 0xfa, 0x37, 0x09, 0xff,
+ 0x8b, 0xb8, 0x31, 0x49, 0x9e, 0x0d, 0x6e, 0xde, 0xb4, 0xd2, 0x12, 0x2d,
+ 0xb8, 0xed, 0xc8, 0xc3, 0xf1, 0xb6, 0x42, 0xa0, 0x4c, 0x97, 0x79, 0xdf,
+ 0xfe, 0xc3, 0xa3, 0x9f, 0xa1, 0xf4, 0x6d, 0x2c, 0x84, 0x77, 0xa4, 0xa2,
+ 0x05, 0xe1, 0x17, 0xff, 0x31, 0xdd, 0x9a, 0xf3, 0xb8, 0x7a, 0xc3, 0x52,
+ 0xc2, 0x11, 0x11, 0xb7, 0x50, 0x31, 0x8a, 0x7f, 0xcc, 0xe7, 0x5a, 0x89,
+ 0xcc, 0xf7, 0x86, 0x9a, 0x61, 0x92, 0x4f, 0x2f, 0x94, 0xb6, 0x98, 0xc7,
+ 0x78, 0xe0, 0x62, 0x4b, 0x43, 0x7d, 0x3c, 0xde, 0xd6, 0x9a, 0xb4, 0x10,
+ 0xa1, 0x40, 0x9c, 0x4b, 0x2a, 0xdc, 0xb8, 0xd0, 0xd4, 0x9e, 0xfd, 0xf1,
+ 0x84, 0x78, 0x1b, 0x0e, 0x57, 0x8f, 0x69, 0x54, 0x42, 0x68, 0x7b, 0xea,
+ 0xa0, 0xef, 0x75, 0x0f, 0x07, 0xa2, 0x8c, 0x73, 0x99, 0xab, 0x55, 0xf5,
+ 0x07, 0x09, 0xd2, 0xaf, 0x38, 0x03, 0x6a, 0x90, 0x03, 0x0c, 0x2f, 0x8f,
+ 0xe2, 0xe8, 0x43, 0xc2, 0x31, 0xe9, 0x6f, 0xad, 0x87, 0xe5, 0x8d, 0xbd,
+ 0x4e, 0x2c, 0x89, 0x4b, 0x51, 0xe6, 0x9c, 0x4c, 0x54, 0x76, 0xc0, 0x12,
+ 0x81, 0x53, 0x9b, 0xec, 0xa0, 0xfc, 0x2c, 0x9c, 0xda, 0x18, 0x95, 0x6e,
+ 0x1e, 0x38, 0x26, 0x42, 0x27, 0x78, 0x60, 0x08, 0xdf, 0x7f, 0x6d, 0x32,
+ 0xe8, 0xd8, 0xc0, 0x6f, 0x1f, 0xeb, 0x26, 0x75, 0x9f, 0x93, 0xfc, 0x7b,
+ 0x1b, 0xfe, 0x35, 0x90, 0xdc, 0x53, 0xa3, 0x07, 0xa6, 0x3f, 0x83, 0x55,
+ 0x0a, 0x2b, 0x4e, 0x62, 0x82, 0x25, 0xce, 0x66, 0x30, 0x5d, 0x2c, 0xe0,
+ 0xf9, 0x19, 0x1b, 0x75, 0xb9, 0x9d, 0x98, 0x56, 0xa6, 0x83, 0x27, 0x7a,
+ 0xd1, 0x8f, 0x8d, 0x59, 0x93, 0xfc, 0x3f, 0x73, 0xd7, 0x2e, 0xb4, 0x2c,
+ 0x95, 0xd8, 0x8b, 0xf7, 0xc9, 0x7e, 0xc7, 0xfc, 0x9d, 0xac, 0x72, 0x04,
+ 0x1f, 0xd2, 0xcc, 0x17, 0xf4, 0xed, 0x34, 0x60, 0x9b, 0x9e, 0x4a, 0x97,
+ 0x04, 0xfe, 0xdd, 0x72, 0x0e, 0x57, 0x54, 0x51, 0x06, 0x70, 0x4d, 0xef,
+ 0xaa, 0x1c, 0xa4, 0x82, 0xe0, 0x33, 0xc7, 0xf4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Apr 2 12:00:00 2008 GMT
+ Not After : Apr 3 00:00:00 2022 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e:
+ fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9:
+ 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29:
+ 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f:
+ 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f:
+ a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf:
+ 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42:
+ 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12:
+ a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a:
+ 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03:
+ af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a:
+ aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd:
+ 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f:
+ e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed:
+ cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28:
+ 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c:
+ 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e:
+ 5e:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.114412.1.3.0.2
+ CPS: http://www.digicert.com/ssl-cps-repository.htm
+ User Notice:
+ Explicit Text:
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ X509v3 Subject Key Identifier:
+ 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7
+ Signature Algorithm: sha1WithRSAEncryption
+ 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43:
+ ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7:
+ fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3:
+ 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a:
+ 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4:
+ 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a:
+ 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d:
+ 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d:
+ f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e:
+ 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd:
+ 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d:
+ 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4:
+ 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d:
+ c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3:
+ 5a:76:f7:61
+-----BEGIN CERTIFICATE-----
+MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR
+CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv
+KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5
+BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf
+1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs
+zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d
+32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w
+ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3
+LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH
+AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy
+AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj
+AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg
+AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ
+AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt
+AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj
+AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl
+AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm
+MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB
+hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln
+aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl
+cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME
+GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB
+INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a
+vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j
+CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X
+dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE
+JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY
+Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert51[] = {
+ 0x30, 0x82, 0x06, 0x58, 0x30, 0x82, 0x05, 0x40, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x0a, 0x5f, 0x11, 0x4d, 0x03, 0x5b, 0x17, 0x91, 0x17,
+ 0xd2, 0xef, 0xd4, 0x03, 0x8c, 0x3f, 0x3b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x34, 0x30, 0x32, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x30,
+ 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x66, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67,
+ 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20,
+ 0x43, 0x41, 0x2d, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xbf, 0x61, 0x0a, 0x29, 0x10, 0x1f, 0x5e, 0xfe, 0x34, 0x37, 0x51,
+ 0x08, 0xf8, 0x1e, 0xfb, 0x22, 0xed, 0x61, 0xbe, 0x0b, 0x0d, 0x70, 0x4c,
+ 0x50, 0x63, 0x26, 0x75, 0x15, 0xb9, 0x41, 0x88, 0x97, 0xb6, 0xf0, 0xa0,
+ 0x15, 0xbb, 0x08, 0x60, 0xe0, 0x42, 0xe8, 0x05, 0x29, 0x10, 0x87, 0x36,
+ 0x8a, 0x28, 0x65, 0xa8, 0xef, 0x31, 0x07, 0x74, 0x6d, 0x36, 0x97, 0x2f,
+ 0x28, 0x46, 0x66, 0x04, 0xc7, 0x2a, 0x79, 0x26, 0x7a, 0x99, 0xd5, 0x8e,
+ 0xc3, 0x6d, 0x4f, 0xa0, 0x5e, 0xad, 0xbc, 0x3d, 0x91, 0xc2, 0x59, 0x7b,
+ 0x5e, 0x36, 0x6c, 0xc0, 0x53, 0xcf, 0x00, 0x08, 0x32, 0x3e, 0x10, 0x64,
+ 0x58, 0x10, 0x13, 0x69, 0xc7, 0x0c, 0xee, 0x9c, 0x42, 0x51, 0x00, 0xf9,
+ 0x05, 0x44, 0xee, 0x24, 0xce, 0x7a, 0x1f, 0xed, 0x8c, 0x11, 0xbd, 0x12,
+ 0xa8, 0xf3, 0x15, 0xf4, 0x1c, 0x7a, 0x31, 0x69, 0x01, 0x1b, 0xa7, 0xe6,
+ 0x5d, 0xc0, 0x9a, 0x6c, 0x7e, 0x09, 0x9e, 0xe7, 0x52, 0x44, 0x4a, 0x10,
+ 0x3a, 0x23, 0xe4, 0x9b, 0xb6, 0x03, 0xaf, 0xa8, 0x9c, 0xb4, 0x5b, 0x9f,
+ 0xd4, 0x4b, 0xad, 0x92, 0x8c, 0xce, 0xb5, 0x11, 0x2a, 0xaa, 0x37, 0x18,
+ 0x8d, 0xb4, 0xc2, 0xb8, 0xd8, 0x5c, 0x06, 0x8c, 0xf8, 0xff, 0x23, 0xbd,
+ 0x35, 0x5e, 0xd4, 0x7c, 0x3e, 0x7e, 0x83, 0x0e, 0x91, 0x96, 0x05, 0x98,
+ 0xc3, 0xb2, 0x1f, 0xe3, 0xc8, 0x65, 0xeb, 0xa9, 0x7b, 0x5d, 0xa0, 0x2c,
+ 0xcc, 0xfc, 0x3c, 0xd9, 0x6d, 0xed, 0xcc, 0xfa, 0x4b, 0x43, 0x8c, 0xc9,
+ 0xd4, 0xb8, 0xa5, 0x61, 0x1c, 0xb2, 0x40, 0xb6, 0x28, 0x12, 0xdf, 0xb9,
+ 0xf8, 0x5f, 0xfe, 0xd3, 0xb2, 0xc9, 0xef, 0x3d, 0xb4, 0x1e, 0x4b, 0x7c,
+ 0x1c, 0x4c, 0x99, 0x36, 0x9e, 0x3d, 0xeb, 0xec, 0xa7, 0x68, 0x5e, 0x1d,
+ 0xdf, 0x67, 0x6e, 0x5e, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x02, 0xfa, 0x30, 0x82, 0x02, 0xf6, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x82,
+ 0x01, 0xc6, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x82, 0x01, 0xbd, 0x30,
+ 0x82, 0x01, 0xb9, 0x30, 0x82, 0x01, 0xb5, 0x06, 0x0b, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xfd, 0x6c, 0x01, 0x03, 0x00, 0x02, 0x30, 0x82, 0x01, 0xa4,
+ 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01,
+ 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d, 0x72, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68, 0x74, 0x6d,
+ 0x30, 0x82, 0x01, 0x64, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x02, 0x30, 0x82, 0x01, 0x56, 0x1e, 0x82, 0x01, 0x52, 0x00, 0x41,
+ 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65,
+ 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68,
+ 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72,
+ 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e,
+ 0x00, 0x73, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x75, 0x00, 0x74,
+ 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63, 0x00, 0x63,
+ 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x63,
+ 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x44, 0x00, 0x69, 0x00, 0x67,
+ 0x00, 0x69, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x20,
+ 0x00, 0x43, 0x00, 0x50, 0x00, 0x2f, 0x00, 0x43, 0x00, 0x50, 0x00, 0x53,
+ 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65, 0x00, 0x6c,
+ 0x00, 0x79, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20, 0x00, 0x50,
+ 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, 0x00, 0x41,
+ 0x00, 0x67, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65,
+ 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x77, 0x00, 0x68, 0x00, 0x69,
+ 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x6d,
+ 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x61,
+ 0x00, 0x62, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79,
+ 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x61,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x63,
+ 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x68, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x62,
+ 0x00, 0x79, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65, 0x00, 0x2e,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x34, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26,
+ 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x81, 0x8f, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81,
+ 0x87, 0x30, 0x81, 0x84, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e,
+ 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67,
+ 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56,
+ 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x40,
+ 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65,
+ 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43,
+ 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72,
+ 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf,
+ 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b,
+ 0xc3, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x50, 0xea, 0x73, 0x89, 0xdb, 0x29, 0xfb, 0x10, 0x8f, 0x9e, 0xe5, 0x01,
+ 0x20, 0xd4, 0xde, 0x79, 0x99, 0x48, 0x83, 0xf7, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x1e, 0xe2, 0xa5, 0x48, 0x9e, 0x6c, 0xdb, 0x53,
+ 0x38, 0x0f, 0xef, 0xa6, 0x1a, 0x2a, 0xac, 0xe2, 0x03, 0x43, 0xed, 0x9a,
+ 0xbc, 0x3e, 0x8e, 0x75, 0x1b, 0xf0, 0xfd, 0x2e, 0x22, 0x59, 0xac, 0x13,
+ 0xc0, 0x61, 0xe2, 0xe7, 0xfa, 0xe9, 0x99, 0xcd, 0x87, 0x09, 0x75, 0x54,
+ 0x28, 0xbf, 0x46, 0x60, 0xdc, 0xbe, 0x51, 0x2c, 0x92, 0xf3, 0x1b, 0x91,
+ 0x7c, 0x31, 0x08, 0x70, 0xe2, 0x37, 0xb9, 0xc1, 0x5b, 0xa8, 0xbd, 0xa3,
+ 0x0b, 0x00, 0xfb, 0x1a, 0x15, 0xfd, 0x03, 0xad, 0x58, 0x6a, 0xc5, 0xc7,
+ 0x24, 0x99, 0x48, 0x47, 0x46, 0x31, 0x1e, 0x92, 0xef, 0xb4, 0x5f, 0x4e,
+ 0x34, 0xc7, 0x90, 0xbf, 0x31, 0xc1, 0xf8, 0xb1, 0x84, 0x86, 0xd0, 0x9c,
+ 0x01, 0xaa, 0xdf, 0x8a, 0x56, 0x06, 0xce, 0x3a, 0xe9, 0x0e, 0xae, 0x97,
+ 0x74, 0x5d, 0xd7, 0x71, 0x9a, 0x42, 0x74, 0x5f, 0xde, 0x8d, 0x43, 0x7c,
+ 0xde, 0xe9, 0x55, 0xed, 0x69, 0x00, 0xcb, 0x05, 0xe0, 0x7a, 0x61, 0x61,
+ 0x33, 0xd1, 0x19, 0x4d, 0xf9, 0x08, 0xee, 0xa0, 0x39, 0xc5, 0x25, 0x35,
+ 0xb7, 0x2b, 0xc4, 0x0f, 0xb2, 0xdd, 0xf1, 0xa5, 0xb7, 0x0e, 0x24, 0xc4,
+ 0x26, 0x28, 0x8d, 0x79, 0x77, 0xf5, 0x2f, 0xf0, 0x57, 0xba, 0x7c, 0x07,
+ 0xd4, 0xe1, 0xfc, 0xcd, 0x5a, 0x30, 0x57, 0x7e, 0x86, 0x10, 0x47, 0xdd,
+ 0x31, 0x1f, 0xd7, 0xfc, 0xa2, 0xc2, 0xbf, 0x30, 0x7c, 0x5d, 0x24, 0xaa,
+ 0xe8, 0xf9, 0xae, 0x5f, 0x6a, 0x74, 0xc2, 0xce, 0x6b, 0xb3, 0x46, 0xd8,
+ 0x21, 0xbe, 0x29, 0xd4, 0x8e, 0x5e, 0x15, 0xd6, 0x42, 0x4a, 0xe7, 0x32,
+ 0x6f, 0xa4, 0xb1, 0x6b, 0x51, 0x83, 0x58, 0xbe, 0x3f, 0x6d, 0xc7, 0xfb,
+ 0xda, 0x03, 0x21, 0xcb, 0x6a, 0x16, 0x19, 0x4e, 0x0a, 0xf0, 0xad, 0x84,
+ 0xca, 0x5d, 0x94, 0xb3, 0x5a, 0x76, 0xf7, 0x61,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 7250751724796726 (0x19c28530e93b36)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+ Validity
+ Not Before: Sep 17 22:46:36 2006 GMT
+ Not After : Dec 31 23:59:59 2019 GMT
+ Subject: C=CN, O=WoSign CA Limited, CN=Certification Authority of WoSign
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:bd:ca:8d:ac:b8:91:15:56:97:7b:6b:5c:7a:c2:
+ de:6b:d9:a1:b0:c3:10:23:fa:a7:a1:b2:cc:31:fa:
+ 3e:d9:a6:29:6f:16:3d:e0:6b:f8:b8:40:5f:db:39:
+ a8:00:7a:8b:a0:4d:54:7d:c2:22:78:fc:8e:09:b8:
+ a8:85:d7:cc:95:97:4b:74:d8:9e:7e:f0:00:e4:0e:
+ 89:ae:49:28:44:1a:10:99:32:0f:25:88:53:a4:0d:
+ b3:0f:12:08:16:0b:03:71:27:1c:7f:e1:db:d2:fd:
+ 67:68:c4:05:5d:0a:0e:5d:70:d7:d8:97:a0:bc:53:
+ 41:9a:91:8d:f4:9e:36:66:7a:7e:56:c1:90:5f:e6:
+ b1:68:20:36:a4:8c:24:2c:2c:47:0b:59:76:66:30:
+ b5:be:de:ed:8f:f8:9d:d3:bb:01:30:e6:f2:f3:0e:
+ e0:2c:92:80:f3:85:f9:28:8a:b4:54:2e:9a:ed:f7:
+ 76:fc:15:68:16:eb:4a:6c:eb:2e:12:8f:d4:cf:fe:
+ 0c:c7:5c:1d:0b:7e:05:32:be:5e:b0:09:2a:42:d5:
+ c9:4e:90:b3:59:0d:bb:7a:7e:cd:d5:08:5a:b4:7f:
+ d8:1c:69:11:f9:27:0f:7b:06:af:54:83:18:7b:e1:
+ dd:54:7a:51:68:6e:77:fc:c6:bf:52:4a:66:46:a1:
+ b2:67:1a:bb:a3:4f:77:a0:be:5d:ff:fc:56:0b:43:
+ 72:77:90:ca:9e:f9:f2:39:f5:0d:a9:f4:ea:d7:e7:
+ b3:10:2f:30:42:37:21:cc:30:70:c9:86:98:0f:cc:
+ 58:4d:83:bb:7d:e5:1a:a5:37:8d:b6:ac:32:97:00:
+ 3a:63:71:24:1e:9e:37:c4:ff:74:d4:37:c0:e2:fe:
+ 88:46:60:11:dd:08:3f:50:36:ab:b8:7a:a4:95:62:
+ 6a:6e:b0:ca:6a:21:5a:69:f3:f3:fb:1d:70:39:95:
+ f3:a7:6e:a6:81:89:a1:88:c5:3b:71:ca:a3:52:ee:
+ 83:bb:fd:a0:77:f4:e4:6f:e7:42:db:6d:4a:99:8a:
+ 34:48:bc:17:dc:e4:80:08:22:b6:f2:31:c0:3f:04:
+ 3e:eb:9f:20:79:d6:b8:06:64:64:02:31:d7:a9:cd:
+ 52:fb:84:45:69:09:00:2a:dc:55:8b:c4:06:46:4b:
+ c0:4a:1d:09:5b:39:28:fd:a9:ab:ce:00:f9:2e:48:
+ 4b:26:e6:30:4c:a5:58:ca:b4:44:82:4f:e7:91:1e:
+ 33:c3:b0:93:ff:11:fc:81:d2:ca:1f:71:29:dd:76:
+ 4f:92:25:af:1d:81:b7:0f:2f:8c:c3:06:cc:2f:27:
+ a3:4a:e4:0e:99:ba:7c:1e:45:1f:7f:aa:19:45:96:
+ fd:fc:3d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:2
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ E1:66:CF:0E:D1:F1:B3:4B:B7:06:20:14:FE:87:12:D5:F6:FE:FB:3E
+ X509v3 Authority Key Identifier:
+ keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.startssl.com/ca
+ CA Issuers - URI:http://aia.startssl.com/certs/ca.crt
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.startssl.com/sfsca.crl
+
+ Signature Algorithm: sha256WithRSAEncryption
+ b6:6d:f8:70:fb:e2:0d:4c:98:b3:07:49:15:f5:04:c4:6c:ca:
+ ca:f5:68:a0:08:fe:12:6d:9c:04:06:c9:ad:9a:91:52:3e:78:
+ c4:5c:ee:9f:54:1d:ee:e3:f1:5e:30:c9:49:e1:39:e0:a6:9d:
+ 36:6c:57:fa:e6:34:4f:55:e8:87:a8:2c:dd:05:f1:58:12:91:
+ e8:ca:ce:28:78:8f:df:07:85:01:a5:dc:45:96:05:d4:80:b2:
+ 2b:05:9a:cb:9a:a5:8b:e0:3a:67:e6:73:47:be:4a:fd:27:b1:
+ 88:ef:e6:ca:cf:8d:0e:26:9f:fa:5f:57:78:ad:6d:fe:ae:9b:
+ 35:08:b1:c3:ba:c1:00:4a:4b:7d:14:bd:f7:f1:d3:55:18:ac:
+ d0:33:70:88:6d:c4:09:71:14:a6:2b:4f:88:81:e7:0b:00:37:
+ a9:15:7d:7e:d7:01:96:3f:2f:af:7b:62:ae:0a:4a:bf:4b:39:
+ 2e:35:10:8b:fe:04:39:e4:3c:3a:0c:09:56:40:3a:b5:f4:c2:
+ 68:0c:b5:f9:52:cd:ee:9d:f8:98:fc:78:e7:58:47:8f:1c:73:
+ 58:69:33:ab:ff:dd:df:8e:24:01:77:98:19:3a:b0:66:79:bc:
+ e1:08:a3:0e:4f:c1:04:b3:f3:01:c8:eb:d3:59:1c:35:d2:93:
+ 1e:70:65:82:7f:db:cf:fb:c8:99:12:60:c3:44:6f:3a:80:4b:
+ d7:be:21:aa:14:7a:64:cb:dd:37:43:45:5b:32:2e:45:f0:d9:
+ 59:1f:6b:18:f0:7c:e9:55:36:19:61:5f:b5:7d:f1:8d:bd:88:
+ e4:75:4b:98:dd:27:b0:e4:84:44:2a:61:84:57:05:82:11:1f:
+ aa:35:58:f3:20:0e:af:59:ef:fa:55:72:72:0d:26:d0:9b:53:
+ 49:ac:ce:37:2e:65:61:ff:f6:ec:1b:ea:f6:f1:a6:d3:d1:b5:
+ 7b:be:35:f4:22:c1:bc:8d:01:bd:68:5e:83:0d:2f:ec:d6:da:
+ 63:0c:27:d1:54:3e:e4:a8:d3:ce:4b:32:b8:91:94:ff:fb:5b:
+ 49:2d:75:18:a8:ba:71:9a:3b:ae:d9:c0:a9:4f:87:91:ed:8b:
+ 7b:6b:20:98:89:39:83:4f:80:c4:69:cc:17:c9:c8:4e:be:e4:
+ a9:a5:81:76:70:06:04:32:cd:83:65:f4:bc:7d:3e:13:bc:d2:
+ e8:6f:63:aa:b5:3b:da:8d:86:32:82:78:9d:d9:cc:ff:bf:57:
+ 64:74:ed:28:3d:44:62:15:61:4b:f7:94:b0:0d:2a:67:1c:f0:
+ cb:9b:a5:92:bf:f8:41:5a:c1:3d:60:ed:9f:bb:b8:6d:9b:ce:
+ a9:6a:16:3f:7e:ea:06:f1
+-----BEGIN CERTIFICATE-----
+MIIGXDCCBESgAwIBAgIHGcKFMOk7NjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQG
+EwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERp
+Z2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MjI0NjM2WhcNMTkxMjMxMjM1
+OTU5WjBVMQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQx
+KjAoBgNVBAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL3Kjay4kRVWl3trXHrC3mvZobDD
+ECP6p6GyzDH6PtmmKW8WPeBr+LhAX9s5qAB6i6BNVH3CInj8jgm4qIXXzJWXS3TY
+nn7wAOQOia5JKEQaEJkyDyWIU6QNsw8SCBYLA3EnHH/h29L9Z2jEBV0KDl1w19iX
+oLxTQZqRjfSeNmZ6flbBkF/msWggNqSMJCwsRwtZdmYwtb7e7Y/4ndO7ATDm8vMO
+4CySgPOF+SiKtFQumu33dvwVaBbrSmzrLhKP1M/+DMdcHQt+BTK+XrAJKkLVyU6Q
+s1kNu3p+zdUIWrR/2BxpEfknD3sGr1SDGHvh3VR6UWhud/zGv1JKZkahsmcau6NP
+d6C+Xf/8VgtDcneQyp758jn1Dan06tfnsxAvMEI3IcwwcMmGmA/MWE2Du33lGqU3
+jbasMpcAOmNxJB6eN8T/dNQ3wOL+iEZgEd0IP1A2q7h6pJViam6wymohWmnz8/sd
+cDmV86dupoGJoYjFO3HKo1Lug7v9oHf05G/nQtttSpmKNEi8F9zkgAgitvIxwD8E
+PuufIHnWuAZkZAIx16nNUvuERWkJACrcVYvEBkZLwEodCVs5KP2pq84A+S5ISybm
+MEylWMq0RIJP55EeM8Owk/8R/IHSyh9xKd12T5Ilrx2Btw8vjMMGzC8no0rkDpm6
+fB5FH3+qGUWW/fw9AgMBAAGjggEHMIIBAzASBgNVHRMBAf8ECDAGAQH/AgECMA4G
+A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU4WbPDtHxs0u3BiAU/ocS1fb++z4wHwYD
+VR0jBBgwFoAUTgvvGqRAW6UXaYcwyjRoQ9BBrvIwaQYIKwYBBQUHAQEEXTBbMCcG
+CCsGAQUFBzABhhtodHRwOi8vb2NzcC5zdGFydHNzbC5jb20vY2EwMAYIKwYBBQUH
+MAKGJGh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2NlcnRzL2NhLmNydDAyBgNVHR8E
+KzApMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5jcmwwDQYJ
+KoZIhvcNAQELBQADggIBALZt+HD74g1MmLMHSRX1BMRsysr1aKAI/hJtnAQGya2a
+kVI+eMRc7p9UHe7j8V4wyUnhOeCmnTZsV/rmNE9V6IeoLN0F8VgSkejKzih4j98H
+hQGl3EWWBdSAsisFmsuapYvgOmfmc0e+Sv0nsYjv5srPjQ4mn/pfV3itbf6umzUI
+scO6wQBKS30Uvffx01UYrNAzcIhtxAlxFKYrT4iB5wsAN6kVfX7XAZY/L697Yq4K
+Sr9LOS41EIv+BDnkPDoMCVZAOrX0wmgMtflSze6d+Jj8eOdYR48cc1hpM6v/3d+O
+JAF3mBk6sGZ5vOEIow5PwQSz8wHI69NZHDXSkx5wZYJ/28/7yJkSYMNEbzqAS9e+
+IaoUemTL3TdDRVsyLkXw2VkfaxjwfOlVNhlhX7V98Y29iOR1S5jdJ7DkhEQqYYRX
+BYIRH6o1WPMgDq9Z7/pVcnINJtCbU0mszjcuZWH/9uwb6vbxptPRtXu+NfQiwbyN
+Ab1oXoMNL+zW2mMMJ9FUPuSo085LMriRlP/7W0ktdRiounGaO67ZwKlPh5Hti3tr
+IJiJOYNPgMRpzBfJyE6+5KmlgXZwBgQyzYNl9Lx9PhO80uhvY6q1O9qNhjKCeJ3Z
+zP+/V2R07Sg9RGIVYUv3lLANKmcc8MubpZK/+EFawT1g7Z+7uG2bzqlqFj9+6gbx
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert52[] = {
+ 0x30, 0x82, 0x06, 0x5c, 0x30, 0x82, 0x04, 0x44, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x07, 0x19, 0xc2, 0x85, 0x30, 0xe9, 0x3b, 0x36, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20,
+ 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e,
+ 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x36, 0x30, 0x39, 0x31, 0x37, 0x32, 0x32, 0x34, 0x36, 0x33, 0x36,
+ 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35,
+ 0x39, 0x35, 0x39, 0x5a, 0x30, 0x55, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4e, 0x31, 0x1a, 0x30, 0x18, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e,
+ 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31,
+ 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x30, 0x82, 0x02, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02,
+ 0x82, 0x02, 0x01, 0x00, 0xbd, 0xca, 0x8d, 0xac, 0xb8, 0x91, 0x15, 0x56,
+ 0x97, 0x7b, 0x6b, 0x5c, 0x7a, 0xc2, 0xde, 0x6b, 0xd9, 0xa1, 0xb0, 0xc3,
+ 0x10, 0x23, 0xfa, 0xa7, 0xa1, 0xb2, 0xcc, 0x31, 0xfa, 0x3e, 0xd9, 0xa6,
+ 0x29, 0x6f, 0x16, 0x3d, 0xe0, 0x6b, 0xf8, 0xb8, 0x40, 0x5f, 0xdb, 0x39,
+ 0xa8, 0x00, 0x7a, 0x8b, 0xa0, 0x4d, 0x54, 0x7d, 0xc2, 0x22, 0x78, 0xfc,
+ 0x8e, 0x09, 0xb8, 0xa8, 0x85, 0xd7, 0xcc, 0x95, 0x97, 0x4b, 0x74, 0xd8,
+ 0x9e, 0x7e, 0xf0, 0x00, 0xe4, 0x0e, 0x89, 0xae, 0x49, 0x28, 0x44, 0x1a,
+ 0x10, 0x99, 0x32, 0x0f, 0x25, 0x88, 0x53, 0xa4, 0x0d, 0xb3, 0x0f, 0x12,
+ 0x08, 0x16, 0x0b, 0x03, 0x71, 0x27, 0x1c, 0x7f, 0xe1, 0xdb, 0xd2, 0xfd,
+ 0x67, 0x68, 0xc4, 0x05, 0x5d, 0x0a, 0x0e, 0x5d, 0x70, 0xd7, 0xd8, 0x97,
+ 0xa0, 0xbc, 0x53, 0x41, 0x9a, 0x91, 0x8d, 0xf4, 0x9e, 0x36, 0x66, 0x7a,
+ 0x7e, 0x56, 0xc1, 0x90, 0x5f, 0xe6, 0xb1, 0x68, 0x20, 0x36, 0xa4, 0x8c,
+ 0x24, 0x2c, 0x2c, 0x47, 0x0b, 0x59, 0x76, 0x66, 0x30, 0xb5, 0xbe, 0xde,
+ 0xed, 0x8f, 0xf8, 0x9d, 0xd3, 0xbb, 0x01, 0x30, 0xe6, 0xf2, 0xf3, 0x0e,
+ 0xe0, 0x2c, 0x92, 0x80, 0xf3, 0x85, 0xf9, 0x28, 0x8a, 0xb4, 0x54, 0x2e,
+ 0x9a, 0xed, 0xf7, 0x76, 0xfc, 0x15, 0x68, 0x16, 0xeb, 0x4a, 0x6c, 0xeb,
+ 0x2e, 0x12, 0x8f, 0xd4, 0xcf, 0xfe, 0x0c, 0xc7, 0x5c, 0x1d, 0x0b, 0x7e,
+ 0x05, 0x32, 0xbe, 0x5e, 0xb0, 0x09, 0x2a, 0x42, 0xd5, 0xc9, 0x4e, 0x90,
+ 0xb3, 0x59, 0x0d, 0xbb, 0x7a, 0x7e, 0xcd, 0xd5, 0x08, 0x5a, 0xb4, 0x7f,
+ 0xd8, 0x1c, 0x69, 0x11, 0xf9, 0x27, 0x0f, 0x7b, 0x06, 0xaf, 0x54, 0x83,
+ 0x18, 0x7b, 0xe1, 0xdd, 0x54, 0x7a, 0x51, 0x68, 0x6e, 0x77, 0xfc, 0xc6,
+ 0xbf, 0x52, 0x4a, 0x66, 0x46, 0xa1, 0xb2, 0x67, 0x1a, 0xbb, 0xa3, 0x4f,
+ 0x77, 0xa0, 0xbe, 0x5d, 0xff, 0xfc, 0x56, 0x0b, 0x43, 0x72, 0x77, 0x90,
+ 0xca, 0x9e, 0xf9, 0xf2, 0x39, 0xf5, 0x0d, 0xa9, 0xf4, 0xea, 0xd7, 0xe7,
+ 0xb3, 0x10, 0x2f, 0x30, 0x42, 0x37, 0x21, 0xcc, 0x30, 0x70, 0xc9, 0x86,
+ 0x98, 0x0f, 0xcc, 0x58, 0x4d, 0x83, 0xbb, 0x7d, 0xe5, 0x1a, 0xa5, 0x37,
+ 0x8d, 0xb6, 0xac, 0x32, 0x97, 0x00, 0x3a, 0x63, 0x71, 0x24, 0x1e, 0x9e,
+ 0x37, 0xc4, 0xff, 0x74, 0xd4, 0x37, 0xc0, 0xe2, 0xfe, 0x88, 0x46, 0x60,
+ 0x11, 0xdd, 0x08, 0x3f, 0x50, 0x36, 0xab, 0xb8, 0x7a, 0xa4, 0x95, 0x62,
+ 0x6a, 0x6e, 0xb0, 0xca, 0x6a, 0x21, 0x5a, 0x69, 0xf3, 0xf3, 0xfb, 0x1d,
+ 0x70, 0x39, 0x95, 0xf3, 0xa7, 0x6e, 0xa6, 0x81, 0x89, 0xa1, 0x88, 0xc5,
+ 0x3b, 0x71, 0xca, 0xa3, 0x52, 0xee, 0x83, 0xbb, 0xfd, 0xa0, 0x77, 0xf4,
+ 0xe4, 0x6f, 0xe7, 0x42, 0xdb, 0x6d, 0x4a, 0x99, 0x8a, 0x34, 0x48, 0xbc,
+ 0x17, 0xdc, 0xe4, 0x80, 0x08, 0x22, 0xb6, 0xf2, 0x31, 0xc0, 0x3f, 0x04,
+ 0x3e, 0xeb, 0x9f, 0x20, 0x79, 0xd6, 0xb8, 0x06, 0x64, 0x64, 0x02, 0x31,
+ 0xd7, 0xa9, 0xcd, 0x52, 0xfb, 0x84, 0x45, 0x69, 0x09, 0x00, 0x2a, 0xdc,
+ 0x55, 0x8b, 0xc4, 0x06, 0x46, 0x4b, 0xc0, 0x4a, 0x1d, 0x09, 0x5b, 0x39,
+ 0x28, 0xfd, 0xa9, 0xab, 0xce, 0x00, 0xf9, 0x2e, 0x48, 0x4b, 0x26, 0xe6,
+ 0x30, 0x4c, 0xa5, 0x58, 0xca, 0xb4, 0x44, 0x82, 0x4f, 0xe7, 0x91, 0x1e,
+ 0x33, 0xc3, 0xb0, 0x93, 0xff, 0x11, 0xfc, 0x81, 0xd2, 0xca, 0x1f, 0x71,
+ 0x29, 0xdd, 0x76, 0x4f, 0x92, 0x25, 0xaf, 0x1d, 0x81, 0xb7, 0x0f, 0x2f,
+ 0x8c, 0xc3, 0x06, 0xcc, 0x2f, 0x27, 0xa3, 0x4a, 0xe4, 0x0e, 0x99, 0xba,
+ 0x7c, 0x1e, 0x45, 0x1f, 0x7f, 0xaa, 0x19, 0x45, 0x96, 0xfd, 0xfc, 0x3d,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x07, 0x30, 0x82, 0x01,
+ 0x03, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x02, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0xe1, 0x66, 0xcf, 0x0e, 0xd1, 0xf1, 0xb3, 0x4b, 0xb7, 0x06, 0x20, 0x14,
+ 0xfe, 0x87, 0x12, 0xd5, 0xf6, 0xfe, 0xfb, 0x3e, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef,
+ 0x1a, 0xa4, 0x40, 0x5b, 0xa5, 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68,
+ 0x43, 0xd0, 0x41, 0xae, 0xf2, 0x30, 0x69, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x5d, 0x30, 0x5b, 0x30, 0x27, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x61, 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61,
+ 0x69, 0x61, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x63, 0x61,
+ 0x2e, 0x63, 0x72, 0x74, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73,
+ 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
+ 0x82, 0x02, 0x01, 0x00, 0xb6, 0x6d, 0xf8, 0x70, 0xfb, 0xe2, 0x0d, 0x4c,
+ 0x98, 0xb3, 0x07, 0x49, 0x15, 0xf5, 0x04, 0xc4, 0x6c, 0xca, 0xca, 0xf5,
+ 0x68, 0xa0, 0x08, 0xfe, 0x12, 0x6d, 0x9c, 0x04, 0x06, 0xc9, 0xad, 0x9a,
+ 0x91, 0x52, 0x3e, 0x78, 0xc4, 0x5c, 0xee, 0x9f, 0x54, 0x1d, 0xee, 0xe3,
+ 0xf1, 0x5e, 0x30, 0xc9, 0x49, 0xe1, 0x39, 0xe0, 0xa6, 0x9d, 0x36, 0x6c,
+ 0x57, 0xfa, 0xe6, 0x34, 0x4f, 0x55, 0xe8, 0x87, 0xa8, 0x2c, 0xdd, 0x05,
+ 0xf1, 0x58, 0x12, 0x91, 0xe8, 0xca, 0xce, 0x28, 0x78, 0x8f, 0xdf, 0x07,
+ 0x85, 0x01, 0xa5, 0xdc, 0x45, 0x96, 0x05, 0xd4, 0x80, 0xb2, 0x2b, 0x05,
+ 0x9a, 0xcb, 0x9a, 0xa5, 0x8b, 0xe0, 0x3a, 0x67, 0xe6, 0x73, 0x47, 0xbe,
+ 0x4a, 0xfd, 0x27, 0xb1, 0x88, 0xef, 0xe6, 0xca, 0xcf, 0x8d, 0x0e, 0x26,
+ 0x9f, 0xfa, 0x5f, 0x57, 0x78, 0xad, 0x6d, 0xfe, 0xae, 0x9b, 0x35, 0x08,
+ 0xb1, 0xc3, 0xba, 0xc1, 0x00, 0x4a, 0x4b, 0x7d, 0x14, 0xbd, 0xf7, 0xf1,
+ 0xd3, 0x55, 0x18, 0xac, 0xd0, 0x33, 0x70, 0x88, 0x6d, 0xc4, 0x09, 0x71,
+ 0x14, 0xa6, 0x2b, 0x4f, 0x88, 0x81, 0xe7, 0x0b, 0x00, 0x37, 0xa9, 0x15,
+ 0x7d, 0x7e, 0xd7, 0x01, 0x96, 0x3f, 0x2f, 0xaf, 0x7b, 0x62, 0xae, 0x0a,
+ 0x4a, 0xbf, 0x4b, 0x39, 0x2e, 0x35, 0x10, 0x8b, 0xfe, 0x04, 0x39, 0xe4,
+ 0x3c, 0x3a, 0x0c, 0x09, 0x56, 0x40, 0x3a, 0xb5, 0xf4, 0xc2, 0x68, 0x0c,
+ 0xb5, 0xf9, 0x52, 0xcd, 0xee, 0x9d, 0xf8, 0x98, 0xfc, 0x78, 0xe7, 0x58,
+ 0x47, 0x8f, 0x1c, 0x73, 0x58, 0x69, 0x33, 0xab, 0xff, 0xdd, 0xdf, 0x8e,
+ 0x24, 0x01, 0x77, 0x98, 0x19, 0x3a, 0xb0, 0x66, 0x79, 0xbc, 0xe1, 0x08,
+ 0xa3, 0x0e, 0x4f, 0xc1, 0x04, 0xb3, 0xf3, 0x01, 0xc8, 0xeb, 0xd3, 0x59,
+ 0x1c, 0x35, 0xd2, 0x93, 0x1e, 0x70, 0x65, 0x82, 0x7f, 0xdb, 0xcf, 0xfb,
+ 0xc8, 0x99, 0x12, 0x60, 0xc3, 0x44, 0x6f, 0x3a, 0x80, 0x4b, 0xd7, 0xbe,
+ 0x21, 0xaa, 0x14, 0x7a, 0x64, 0xcb, 0xdd, 0x37, 0x43, 0x45, 0x5b, 0x32,
+ 0x2e, 0x45, 0xf0, 0xd9, 0x59, 0x1f, 0x6b, 0x18, 0xf0, 0x7c, 0xe9, 0x55,
+ 0x36, 0x19, 0x61, 0x5f, 0xb5, 0x7d, 0xf1, 0x8d, 0xbd, 0x88, 0xe4, 0x75,
+ 0x4b, 0x98, 0xdd, 0x27, 0xb0, 0xe4, 0x84, 0x44, 0x2a, 0x61, 0x84, 0x57,
+ 0x05, 0x82, 0x11, 0x1f, 0xaa, 0x35, 0x58, 0xf3, 0x20, 0x0e, 0xaf, 0x59,
+ 0xef, 0xfa, 0x55, 0x72, 0x72, 0x0d, 0x26, 0xd0, 0x9b, 0x53, 0x49, 0xac,
+ 0xce, 0x37, 0x2e, 0x65, 0x61, 0xff, 0xf6, 0xec, 0x1b, 0xea, 0xf6, 0xf1,
+ 0xa6, 0xd3, 0xd1, 0xb5, 0x7b, 0xbe, 0x35, 0xf4, 0x22, 0xc1, 0xbc, 0x8d,
+ 0x01, 0xbd, 0x68, 0x5e, 0x83, 0x0d, 0x2f, 0xec, 0xd6, 0xda, 0x63, 0x0c,
+ 0x27, 0xd1, 0x54, 0x3e, 0xe4, 0xa8, 0xd3, 0xce, 0x4b, 0x32, 0xb8, 0x91,
+ 0x94, 0xff, 0xfb, 0x5b, 0x49, 0x2d, 0x75, 0x18, 0xa8, 0xba, 0x71, 0x9a,
+ 0x3b, 0xae, 0xd9, 0xc0, 0xa9, 0x4f, 0x87, 0x91, 0xed, 0x8b, 0x7b, 0x6b,
+ 0x20, 0x98, 0x89, 0x39, 0x83, 0x4f, 0x80, 0xc4, 0x69, 0xcc, 0x17, 0xc9,
+ 0xc8, 0x4e, 0xbe, 0xe4, 0xa9, 0xa5, 0x81, 0x76, 0x70, 0x06, 0x04, 0x32,
+ 0xcd, 0x83, 0x65, 0xf4, 0xbc, 0x7d, 0x3e, 0x13, 0xbc, 0xd2, 0xe8, 0x6f,
+ 0x63, 0xaa, 0xb5, 0x3b, 0xda, 0x8d, 0x86, 0x32, 0x82, 0x78, 0x9d, 0xd9,
+ 0xcc, 0xff, 0xbf, 0x57, 0x64, 0x74, 0xed, 0x28, 0x3d, 0x44, 0x62, 0x15,
+ 0x61, 0x4b, 0xf7, 0x94, 0xb0, 0x0d, 0x2a, 0x67, 0x1c, 0xf0, 0xcb, 0x9b,
+ 0xa5, 0x92, 0xbf, 0xf8, 0x41, 0x5a, 0xc1, 0x3d, 0x60, 0xed, 0x9f, 0xbb,
+ 0xb8, 0x6d, 0x9b, 0xce, 0xa9, 0x6a, 0x16, 0x3f, 0x7e, 0xea, 0x06, 0xf1,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 03:37:b9:28:34:7c:60:a6:ae:c5:ad:b1:21:7f:38:60
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Nov 9 12:00:00 2007 GMT
+ Not After : Nov 10 00:00:00 2021 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31:
+ 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af:
+ 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01:
+ 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10:
+ 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a:
+ 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a:
+ db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0:
+ 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db:
+ 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58:
+ 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3:
+ ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47:
+ f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8:
+ 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11:
+ 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df:
+ ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32:
+ b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88:
+ 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0:
+ 3e:a7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.114412.2.1
+ CPS: http://www.digicert.com/ssl-cps-repository.htm
+ User Notice:
+ Explicit Text:
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+ CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Subject Key Identifier:
+ 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 4c:7a:17:87:28:5d:17:bc:b2:32:73:bf:cd:2e:f5:58:31:1d:
+ f0:b1:71:54:9c:d6:9b:67:93:db:2f:03:3e:16:6f:1e:03:c9:
+ 53:84:a3:56:60:1e:78:94:1b:a2:a8:6f:a3:a4:8b:52:91:d7:
+ dd:5c:95:bb:ef:b5:16:49:e9:a5:42:4f:34:f2:47:ff:ae:81:
+ 7f:13:54:b7:20:c4:70:15:cb:81:0a:81:cb:74:57:dc:9c:df:
+ 24:a4:29:0c:18:f0:1c:e4:ae:07:33:ec:f1:49:3e:55:cf:6e:
+ 4f:0d:54:7b:d3:c9:e8:15:48:d4:c5:bb:dc:35:1c:77:45:07:
+ 48:45:85:bd:d7:7e:53:b8:c0:16:d9:95:cd:8b:8d:7d:c9:60:
+ 4f:d1:a2:9b:e3:d0:30:d6:b4:73:36:e6:d2:f9:03:b2:e3:a4:
+ f5:e5:b8:3e:04:49:00:ba:2e:a6:4a:72:83:72:9d:f7:0b:8c:
+ a9:89:e7:b3:d7:64:1f:d6:e3:60:cb:03:c4:dc:88:e9:9d:25:
+ 01:00:71:cb:03:b4:29:60:25:8f:f9:46:d1:7b:71:ae:cd:53:
+ 12:5b:84:8e:c2:0f:c7:ed:93:19:d9:c9:fa:8f:58:34:76:32:
+ 2f:ae:e1:50:14:61:d4:a8:58:a3:c8:30:13:23:ef:c6:25:8c:
+ 36:8f:1c:80
+-----BEGIN CERTIFICATE-----
+MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/
+PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC
+7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw
+PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6
+4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo
+LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U
+pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy
+BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH
+AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH
+AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o
+dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0
+AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1
+AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp
+AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl
+AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo
+AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg
+AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg
+AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB
+gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
+dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy
+dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw
+gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB
+c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
+LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE
+FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI
+Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU
+nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/
+roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU
+xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+
+BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu
+zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert53[] = {
+ 0x30, 0x82, 0x06, 0xe6, 0x30, 0x82, 0x05, 0xce, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x03, 0x37, 0xb9, 0x28, 0x34, 0x7c, 0x60, 0xa6, 0xae,
+ 0xc5, 0xad, 0xb1, 0x21, 0x7f, 0x38, 0x60, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x31, 0x31, 0x30, 0x39, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x69, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1f,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67,
+ 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20,
+ 0x45, 0x56, 0x20, 0x43, 0x41, 0x2d, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xf3, 0x96, 0x62, 0xd8, 0x75, 0x6e, 0x19, 0xff,
+ 0x3f, 0x34, 0x7c, 0x49, 0x4f, 0x31, 0x7e, 0x0d, 0x04, 0x4e, 0x99, 0x81,
+ 0xe2, 0xb3, 0x85, 0x55, 0x91, 0x30, 0xb1, 0xc0, 0xaf, 0x70, 0xbb, 0x2c,
+ 0xa8, 0xe7, 0x18, 0xaa, 0x3f, 0x78, 0xf7, 0x90, 0x68, 0x52, 0x86, 0x01,
+ 0x88, 0x97, 0xe2, 0x3b, 0x06, 0x65, 0x90, 0xaa, 0xbd, 0x65, 0x76, 0xc2,
+ 0xec, 0xbe, 0x10, 0x5b, 0x37, 0x78, 0x83, 0x60, 0x75, 0x45, 0xc6, 0xbd,
+ 0x74, 0xaa, 0xb6, 0x9f, 0xa4, 0x3a, 0x01, 0x50, 0x17, 0xc4, 0x39, 0x69,
+ 0xb9, 0xf1, 0x4f, 0xef, 0x82, 0xc1, 0xca, 0xf3, 0x4a, 0xdb, 0xcc, 0x9e,
+ 0x50, 0x4f, 0x4d, 0x40, 0xa3, 0x3a, 0x90, 0xe7, 0x86, 0x66, 0xbc, 0xf0,
+ 0x3e, 0x76, 0x28, 0x4c, 0xd1, 0x75, 0x80, 0x9e, 0x6a, 0x35, 0x14, 0x35,
+ 0x03, 0x9e, 0xdb, 0x0c, 0x8c, 0xc2, 0x28, 0xad, 0x50, 0xb2, 0xce, 0xf6,
+ 0x91, 0xa3, 0xc3, 0xa5, 0x0a, 0x58, 0x49, 0xf6, 0x75, 0x44, 0x6c, 0xba,
+ 0xf9, 0xce, 0xe9, 0xab, 0x3a, 0x02, 0xe0, 0x4d, 0xf3, 0xac, 0xe2, 0x7a,
+ 0xe0, 0x60, 0x22, 0x05, 0x3c, 0x82, 0xd3, 0x52, 0xe2, 0xf3, 0x9c, 0x47,
+ 0xf8, 0x3b, 0xd8, 0xb2, 0x4b, 0x93, 0x56, 0x4a, 0xbf, 0x70, 0xab, 0x3e,
+ 0xe9, 0x68, 0xc8, 0x1d, 0x8f, 0x58, 0x1d, 0x2a, 0x4d, 0x5e, 0x27, 0x3d,
+ 0xad, 0x0a, 0x59, 0x2f, 0x5a, 0x11, 0x20, 0x40, 0xd9, 0x68, 0x04, 0x68,
+ 0x2d, 0xf4, 0xc0, 0x84, 0x0b, 0x0a, 0x1b, 0x78, 0xdf, 0xed, 0x1a, 0x58,
+ 0xdc, 0xfb, 0x41, 0x5a, 0x6d, 0x6b, 0xf2, 0xed, 0x1c, 0xee, 0x5c, 0x32,
+ 0xb6, 0x5c, 0xec, 0xd7, 0xa6, 0x03, 0x32, 0xa6, 0xe8, 0xde, 0xb7, 0x28,
+ 0x27, 0x59, 0x88, 0x80, 0xff, 0x7b, 0xad, 0x89, 0x58, 0xd5, 0x1e, 0x14,
+ 0xa4, 0xf2, 0xb0, 0x70, 0xd4, 0xa0, 0x3e, 0xa7, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x03, 0x85, 0x30, 0x82, 0x03, 0x81, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x86, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x34, 0x30, 0x32,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x04, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x08, 0x30, 0x82, 0x01, 0xc4, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x82, 0x01, 0xbb, 0x30, 0x82, 0x01, 0xb7, 0x30, 0x82, 0x01, 0xb3, 0x06,
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6c, 0x02, 0x01, 0x30, 0x82,
+ 0x01, 0xa4, 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d,
+ 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68,
+ 0x74, 0x6d, 0x30, 0x82, 0x01, 0x64, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x02, 0x30, 0x82, 0x01, 0x56, 0x1e, 0x82, 0x01, 0x52,
+ 0x00, 0x41, 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73,
+ 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63,
+ 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f,
+ 0x00, 0x6e, 0x00, 0x73, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x75,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63,
+ 0x00, 0x63, 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e,
+ 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20,
+ 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x44, 0x00, 0x69,
+ 0x00, 0x67, 0x00, 0x69, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74,
+ 0x00, 0x20, 0x00, 0x45, 0x00, 0x56, 0x00, 0x20, 0x00, 0x43, 0x00, 0x50,
+ 0x00, 0x53, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20,
+ 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65,
+ 0x00, 0x6c, 0x00, 0x79, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20,
+ 0x00, 0x50, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20,
+ 0x00, 0x41, 0x00, 0x67, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6d,
+ 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x77, 0x00, 0x68,
+ 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69,
+ 0x00, 0x6d, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69,
+ 0x00, 0x61, 0x00, 0x62, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74,
+ 0x00, 0x79, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20,
+ 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e,
+ 0x00, 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72,
+ 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x68,
+ 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20,
+ 0x00, 0x62, 0x00, 0x79, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66,
+ 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65,
+ 0x00, 0x2e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x81,
+ 0x83, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x77, 0x30, 0x75, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x41, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65,
+ 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x41, 0x43, 0x65, 0x72,
+ 0x74, 0x73, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48,
+ 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65,
+ 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74,
+ 0x30, 0x81, 0x8f, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81, 0x87, 0x30,
+ 0x81, 0x84, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69,
+ 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44,
+ 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41,
+ 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f,
+ 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x40, 0xa0, 0x3e,
+ 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72,
+ 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e,
+ 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x4c, 0x58, 0xcb, 0x25, 0xf0, 0x41, 0x4f, 0x52, 0xf4, 0x28, 0xc8,
+ 0x81, 0x43, 0x9b, 0xa6, 0xa8, 0xa0, 0xe6, 0x92, 0xe5, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e,
+ 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08,
+ 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x4c, 0x7a, 0x17, 0x87, 0x28, 0x5d, 0x17, 0xbc, 0xb2, 0x32,
+ 0x73, 0xbf, 0xcd, 0x2e, 0xf5, 0x58, 0x31, 0x1d, 0xf0, 0xb1, 0x71, 0x54,
+ 0x9c, 0xd6, 0x9b, 0x67, 0x93, 0xdb, 0x2f, 0x03, 0x3e, 0x16, 0x6f, 0x1e,
+ 0x03, 0xc9, 0x53, 0x84, 0xa3, 0x56, 0x60, 0x1e, 0x78, 0x94, 0x1b, 0xa2,
+ 0xa8, 0x6f, 0xa3, 0xa4, 0x8b, 0x52, 0x91, 0xd7, 0xdd, 0x5c, 0x95, 0xbb,
+ 0xef, 0xb5, 0x16, 0x49, 0xe9, 0xa5, 0x42, 0x4f, 0x34, 0xf2, 0x47, 0xff,
+ 0xae, 0x81, 0x7f, 0x13, 0x54, 0xb7, 0x20, 0xc4, 0x70, 0x15, 0xcb, 0x81,
+ 0x0a, 0x81, 0xcb, 0x74, 0x57, 0xdc, 0x9c, 0xdf, 0x24, 0xa4, 0x29, 0x0c,
+ 0x18, 0xf0, 0x1c, 0xe4, 0xae, 0x07, 0x33, 0xec, 0xf1, 0x49, 0x3e, 0x55,
+ 0xcf, 0x6e, 0x4f, 0x0d, 0x54, 0x7b, 0xd3, 0xc9, 0xe8, 0x15, 0x48, 0xd4,
+ 0xc5, 0xbb, 0xdc, 0x35, 0x1c, 0x77, 0x45, 0x07, 0x48, 0x45, 0x85, 0xbd,
+ 0xd7, 0x7e, 0x53, 0xb8, 0xc0, 0x16, 0xd9, 0x95, 0xcd, 0x8b, 0x8d, 0x7d,
+ 0xc9, 0x60, 0x4f, 0xd1, 0xa2, 0x9b, 0xe3, 0xd0, 0x30, 0xd6, 0xb4, 0x73,
+ 0x36, 0xe6, 0xd2, 0xf9, 0x03, 0xb2, 0xe3, 0xa4, 0xf5, 0xe5, 0xb8, 0x3e,
+ 0x04, 0x49, 0x00, 0xba, 0x2e, 0xa6, 0x4a, 0x72, 0x83, 0x72, 0x9d, 0xf7,
+ 0x0b, 0x8c, 0xa9, 0x89, 0xe7, 0xb3, 0xd7, 0x64, 0x1f, 0xd6, 0xe3, 0x60,
+ 0xcb, 0x03, 0xc4, 0xdc, 0x88, 0xe9, 0x9d, 0x25, 0x01, 0x00, 0x71, 0xcb,
+ 0x03, 0xb4, 0x29, 0x60, 0x25, 0x8f, 0xf9, 0x46, 0xd1, 0x7b, 0x71, 0xae,
+ 0xcd, 0x53, 0x12, 0x5b, 0x84, 0x8e, 0xc2, 0x0f, 0xc7, 0xed, 0x93, 0x19,
+ 0xd9, 0xc9, 0xfa, 0x8f, 0x58, 0x34, 0x76, 0x32, 0x2f, 0xae, 0xe1, 0x50,
+ 0x14, 0x61, 0xd4, 0xa8, 0x58, 0xa3, 0xc8, 0x30, 0x13, 0x23, 0xef, 0xc6,
+ 0x25, 0x8c, 0x36, 0x8f, 0x1c, 0x80,
+};
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3.c b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3.c
new file mode 100644
index 00000000000..876d00c837a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3.c
@@ -0,0 +1,118 @@
+/* This file contains common certificates. It's designed to be #included in
+ * another file, in a namespace. */
+
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_3a.inc"
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set_3b.inc"
+
+static const size_t kNumCerts = 52;
+static const unsigned char* const kCerts[] = {
+ kDERCert0,
+ kDERCert1,
+ kDERCert2,
+ kDERCert3,
+ kDERCert4,
+ kDERCert5,
+ kDERCert6,
+ kDERCert7,
+ kDERCert8,
+ kDERCert9,
+ kDERCert10,
+ kDERCert11,
+ kDERCert12,
+ kDERCert13,
+ kDERCert14,
+ kDERCert15,
+ kDERCert16,
+ kDERCert17,
+ kDERCert18,
+ kDERCert19,
+ kDERCert20,
+ kDERCert21,
+ kDERCert22,
+ kDERCert23,
+ kDERCert24,
+ kDERCert25,
+ kDERCert26,
+ kDERCert27,
+ kDERCert28,
+ kDERCert29,
+ kDERCert30,
+ kDERCert31,
+ kDERCert32,
+ kDERCert33,
+ kDERCert34,
+ kDERCert35,
+ kDERCert36,
+ kDERCert37,
+ kDERCert38,
+ kDERCert39,
+ kDERCert40,
+ kDERCert41,
+ kDERCert42,
+ kDERCert43,
+ kDERCert44,
+ kDERCert45,
+ kDERCert46,
+ kDERCert47,
+ kDERCert48,
+ kDERCert49,
+ kDERCert50,
+ kDERCert51,
+};
+
+static const size_t kLens[] = {
+ 897,
+ 911,
+ 1012,
+ 1049,
+ 1065,
+ 1096,
+ 1097,
+ 1101,
+ 1105,
+ 1105,
+ 1107,
+ 1117,
+ 1127,
+ 1133,
+ 1136,
+ 1138,
+ 1139,
+ 1145,
+ 1149,
+ 1153,
+ 1167,
+ 1172,
+ 1174,
+ 1174,
+ 1176,
+ 1188,
+ 1194,
+ 1196,
+ 1203,
+ 1205,
+ 1206,
+ 1208,
+ 1209,
+ 1210,
+ 1222,
+ 1227,
+ 1236,
+ 1236,
+ 1238,
+ 1283,
+ 1284,
+ 1287,
+ 1298,
+ 1315,
+ 1327,
+ 1340,
+ 1357,
+ 1418,
+ 1447,
+ 1509,
+ 1513,
+ 1632,
+};
+
+static const uint64_t kHash = UINT64_C(0x918215a28680ed7e);
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3a.inc b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3a.inc
new file mode 100644
index 00000000000..d5bff198414
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3a.inc
@@ -0,0 +1,5033 @@
+/* This file contains common certificates. It's designed to be #included in
+ * another file, in a namespace. */
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1227750 (0x12bbe6)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+ Validity
+ Not Before: May 21 04:00:00 2002 GMT
+ Not After : Aug 21 04:00:00 2018 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df:
+ 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8:
+ 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29:
+ bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4:
+ 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3:
+ ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92:
+ 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d:
+ 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14:
+ 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd:
+ d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6:
+ d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5:
+ 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39:
+ 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05:
+ 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2:
+ fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32:
+ eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07:
+ 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b:
+ e4:f9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
+
+ X509v3 Subject Key Identifier:
+ C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/secureca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.geotrust.com/resources/repository
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 76:e1:12:6e:4e:4b:16:12:86:30:06:b2:81:08:cf:f0:08:c7:
+ c7:71:7e:66:ee:c2:ed:d4:3b:1f:ff:f0:f0:c8:4e:d6:43:38:
+ b0:b9:30:7d:18:d0:55:83:a2:6a:cb:36:11:9c:e8:48:66:a3:
+ 6d:7f:b8:13:d4:47:fe:8b:5a:5c:73:fc:ae:d9:1b:32:19:38:
+ ab:97:34:14:aa:96:d2:eb:a3:1c:14:08:49:b6:bb:e5:91:ef:
+ 83:36:eb:1d:56:6f:ca:da:bc:73:63:90:e4:7f:7b:3e:22:cb:
+ 3d:07:ed:5f:38:74:9c:e3:03:50:4e:a1:af:98:ee:61:f2:84:
+ 3f:12
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
+WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
+AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
+OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
+T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
+JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
+Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
+PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
+aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
+TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
+LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
+BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
+dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
+AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
+NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
+b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert0[] = {
+ 0x30, 0x82, 0x03, 0x7d, 0x30, 0x82, 0x02, 0xe6, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x12, 0xbb, 0xe6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x32, 0x30,
+ 0x35, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d,
+ 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x30, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x12, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0xcc, 0x18, 0x63, 0x30, 0xfd,
+ 0xf4, 0x17, 0x23, 0x1a, 0x56, 0x7e, 0x5b, 0xdf, 0x3c, 0x6c, 0x38, 0xe4,
+ 0x71, 0xb7, 0x78, 0x91, 0xd4, 0xbc, 0xa1, 0xd8, 0x4c, 0xf8, 0xa8, 0x43,
+ 0xb6, 0x03, 0xe9, 0x4d, 0x21, 0x07, 0x08, 0x88, 0xda, 0x58, 0x2f, 0x66,
+ 0x39, 0x29, 0xbd, 0x05, 0x78, 0x8b, 0x9d, 0x38, 0xe8, 0x05, 0xb7, 0x6a,
+ 0x7e, 0x71, 0xa4, 0xe6, 0xc4, 0x60, 0xa6, 0xb0, 0xef, 0x80, 0xe4, 0x89,
+ 0x28, 0x0f, 0x9e, 0x25, 0xd6, 0xed, 0x83, 0xf3, 0xad, 0xa6, 0x91, 0xc7,
+ 0x98, 0xc9, 0x42, 0x18, 0x35, 0x14, 0x9d, 0xad, 0x98, 0x46, 0x92, 0x2e,
+ 0x4f, 0xca, 0xf1, 0x87, 0x43, 0xc1, 0x16, 0x95, 0x57, 0x2d, 0x50, 0xef,
+ 0x89, 0x2d, 0x80, 0x7a, 0x57, 0xad, 0xf2, 0xee, 0x5f, 0x6b, 0xd2, 0x00,
+ 0x8d, 0xb9, 0x14, 0xf8, 0x14, 0x15, 0x35, 0xd9, 0xc0, 0x46, 0xa3, 0x7b,
+ 0x72, 0xc8, 0x91, 0xbf, 0xc9, 0x55, 0x2b, 0xcd, 0xd0, 0x97, 0x3e, 0x9c,
+ 0x26, 0x64, 0xcc, 0xdf, 0xce, 0x83, 0x19, 0x71, 0xca, 0x4e, 0xe6, 0xd4,
+ 0xd5, 0x7b, 0xa9, 0x19, 0xcd, 0x55, 0xde, 0xc8, 0xec, 0xd2, 0x5e, 0x38,
+ 0x53, 0xe5, 0x5c, 0x4f, 0x8c, 0x2d, 0xfe, 0x50, 0x23, 0x36, 0xfc, 0x66,
+ 0xe6, 0xcb, 0x8e, 0xa4, 0x39, 0x19, 0x00, 0xb7, 0x95, 0x02, 0x39, 0x91,
+ 0x0b, 0x0e, 0xfe, 0x38, 0x2e, 0xd1, 0x1d, 0x05, 0x9a, 0xf6, 0x4d, 0x3e,
+ 0x6f, 0x0f, 0x07, 0x1d, 0xaf, 0x2c, 0x1e, 0x8f, 0x60, 0x39, 0xe2, 0xfa,
+ 0x36, 0x53, 0x13, 0x39, 0xd4, 0x5e, 0x26, 0x2b, 0xdb, 0x3d, 0xa8, 0x14,
+ 0xbd, 0x32, 0xeb, 0x18, 0x03, 0x28, 0x52, 0x04, 0x71, 0xe5, 0xab, 0x33,
+ 0x3d, 0xe1, 0x38, 0xbb, 0x07, 0x36, 0x84, 0x62, 0x9c, 0x79, 0xea, 0x16,
+ 0x30, 0xf4, 0x5f, 0xc0, 0x2b, 0xe8, 0x71, 0x6b, 0xe4, 0xf9, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xf0, 0x30, 0x81, 0xed, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6,
+ 0x68, 0xf9, 0x2b, 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10,
+ 0x4f, 0x33, 0x98, 0x90, 0x9f, 0xd4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0,
+ 0x2d, 0xa0, 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4e,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81,
+ 0x00, 0x76, 0xe1, 0x12, 0x6e, 0x4e, 0x4b, 0x16, 0x12, 0x86, 0x30, 0x06,
+ 0xb2, 0x81, 0x08, 0xcf, 0xf0, 0x08, 0xc7, 0xc7, 0x71, 0x7e, 0x66, 0xee,
+ 0xc2, 0xed, 0xd4, 0x3b, 0x1f, 0xff, 0xf0, 0xf0, 0xc8, 0x4e, 0xd6, 0x43,
+ 0x38, 0xb0, 0xb9, 0x30, 0x7d, 0x18, 0xd0, 0x55, 0x83, 0xa2, 0x6a, 0xcb,
+ 0x36, 0x11, 0x9c, 0xe8, 0x48, 0x66, 0xa3, 0x6d, 0x7f, 0xb8, 0x13, 0xd4,
+ 0x47, 0xfe, 0x8b, 0x5a, 0x5c, 0x73, 0xfc, 0xae, 0xd9, 0x1b, 0x32, 0x19,
+ 0x38, 0xab, 0x97, 0x34, 0x14, 0xaa, 0x96, 0xd2, 0xeb, 0xa3, 0x1c, 0x14,
+ 0x08, 0x49, 0xb6, 0xbb, 0xe5, 0x91, 0xef, 0x83, 0x36, 0xeb, 0x1d, 0x56,
+ 0x6f, 0xca, 0xda, 0xbc, 0x73, 0x63, 0x90, 0xe4, 0x7f, 0x7b, 0x3e, 0x22,
+ 0xcb, 0x3d, 0x07, 0xed, 0x5f, 0x38, 0x74, 0x9c, 0xe3, 0x03, 0x50, 0x4e,
+ 0xa1, 0xaf, 0x98, 0xee, 0x61, 0xf2, 0x84, 0x3f, 0x12,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 880226 (0xd6e62)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+ Validity
+ Not Before: Nov 27 00:00:00 2006 GMT
+ Not After : Aug 21 16:15:00 2018 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:be:b8:15:7b:ff:d4:7c:7d:67:ad:83:64:7b:c8:
+ 42:53:2d:df:f6:84:08:20:61:d6:01:59:6a:9c:44:
+ 11:af:ef:76:fd:95:7e:ce:61:30:bb:7a:83:5f:02:
+ bd:01:66:ca:ee:15:8d:6f:a1:30:9c:bd:a1:85:9e:
+ 94:3a:f3:56:88:00:31:cf:d8:ee:6a:96:02:d9:ed:
+ 03:8c:fb:75:6d:e7:ea:b8:55:16:05:16:9a:f4:e0:
+ 5e:b1:88:c0:64:85:5c:15:4d:88:c7:b7:ba:e0:75:
+ e9:ad:05:3d:9d:c7:89:48:e0:bb:28:c8:03:e1:30:
+ 93:64:5e:52:c0:59:70:22:35:57:88:8a:f1:95:0a:
+ 83:d7:bc:31:73:01:34:ed:ef:46:71:e0:6b:02:a8:
+ 35:72:6b:97:9b:66:e0:cb:1c:79:5f:d8:1a:04:68:
+ 1e:47:02:e6:9d:60:e2:36:97:01:df:ce:35:92:df:
+ be:67:c7:6d:77:59:3b:8f:9d:d6:90:15:94:bc:42:
+ 34:10:c1:39:f9:b1:27:3e:7e:d6:8a:75:c5:b2:af:
+ 96:d3:a2:de:9b:e4:98:be:7d:e1:e9:81:ad:b6:6f:
+ fc:d7:0e:da:e0:34:b0:0d:1a:77:e7:e3:08:98:ef:
+ 58:fa:9c:84:b7:36:af:c2:df:ac:d2:f4:10:06:70:
+ 71:35
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92
+ X509v3 Authority Key Identifier:
+ keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/secureca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha1WithRSAEncryption
+ af:f3:0e:d6:72:ab:c7:a9:97:ca:2a:6b:84:39:de:79:a9:f0:
+ 81:e5:08:67:ab:d7:2f:20:02:01:71:0c:04:22:c9:1e:88:95:
+ 03:c9:49:3a:af:67:08:49:b0:d5:08:f5:20:3d:80:91:a0:c5:
+ 87:a3:fb:c9:a3:17:91:f9:a8:2f:ae:e9:0f:df:96:72:0f:75:
+ 17:80:5d:78:01:4d:9f:1f:6d:7b:d8:f5:42:38:23:1a:99:93:
+ f4:83:be:3b:35:74:e7:37:13:35:7a:ac:b4:b6:90:82:6c:27:
+ a4:e0:ec:9e:35:bd:bf:e5:29:a1:47:9f:5b:32:fc:e9:99:7d:
+ 2b:39
+-----BEGIN CERTIFICATE-----
+MIIDizCCAvSgAwIBAgIDDW5iMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMTI3MDAwMDAwWhcNMTgwODIxMTYxNTAw
+WjBYMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UE
+AxMoR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64FXv/1Hx9Z62DZHvIQlMt3/aE
+CCBh1gFZapxEEa/vdv2Vfs5hMLt6g18CvQFmyu4VjW+hMJy9oYWelDrzVogAMc/Y
+7mqWAtntA4z7dW3n6rhVFgUWmvTgXrGIwGSFXBVNiMe3uuB16a0FPZ3HiUjguyjI
+A+Ewk2ReUsBZcCI1V4iK8ZUKg9e8MXMBNO3vRnHgawKoNXJrl5tm4MsceV/YGgRo
+HkcC5p1g4jaXAd/ONZLfvmfHbXdZO4+d1pAVlLxCNBDBOfmxJz5+1op1xbKvltOi
+3pvkmL594emBrbZv/NcO2uA0sA0ad+fjCJjvWPqchLc2r8LfrNL0EAZwcTUCAwEA
+AaOB6DCB5TAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFCzVUEGXFYvwjzZhW0r7
+a9mZyTOSMB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMA8GA1UdEwEB
+/wQFMAMBAf8wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5j
+b20vY3Jscy9zZWN1cmVjYS5jcmwwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYB
+BQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwDQYJ
+KoZIhvcNAQEFBQADgYEAr/MO1nKrx6mXyiprhDneeanwgeUIZ6vXLyACAXEMBCLJ
+HoiVA8lJOq9nCEmw1Qj1ID2AkaDFh6P7yaMXkfmoL67pD9+Wcg91F4BdeAFNnx9t
+e9j1QjgjGpmT9IO+OzV05zcTNXqstLaQgmwnpODsnjW9v+UpoUefWzL86Zl9Kzk=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert1[] = {
+ 0x30, 0x82, 0x03, 0x8b, 0x30, 0x82, 0x02, 0xf4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x0d, 0x6e, 0x62, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31,
+ 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d,
+ 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x31, 0x36, 0x31, 0x35, 0x30, 0x30,
+ 0x5a, 0x30, 0x58, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x28, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xbe, 0xb8, 0x15, 0x7b, 0xff, 0xd4, 0x7c, 0x7d,
+ 0x67, 0xad, 0x83, 0x64, 0x7b, 0xc8, 0x42, 0x53, 0x2d, 0xdf, 0xf6, 0x84,
+ 0x08, 0x20, 0x61, 0xd6, 0x01, 0x59, 0x6a, 0x9c, 0x44, 0x11, 0xaf, 0xef,
+ 0x76, 0xfd, 0x95, 0x7e, 0xce, 0x61, 0x30, 0xbb, 0x7a, 0x83, 0x5f, 0x02,
+ 0xbd, 0x01, 0x66, 0xca, 0xee, 0x15, 0x8d, 0x6f, 0xa1, 0x30, 0x9c, 0xbd,
+ 0xa1, 0x85, 0x9e, 0x94, 0x3a, 0xf3, 0x56, 0x88, 0x00, 0x31, 0xcf, 0xd8,
+ 0xee, 0x6a, 0x96, 0x02, 0xd9, 0xed, 0x03, 0x8c, 0xfb, 0x75, 0x6d, 0xe7,
+ 0xea, 0xb8, 0x55, 0x16, 0x05, 0x16, 0x9a, 0xf4, 0xe0, 0x5e, 0xb1, 0x88,
+ 0xc0, 0x64, 0x85, 0x5c, 0x15, 0x4d, 0x88, 0xc7, 0xb7, 0xba, 0xe0, 0x75,
+ 0xe9, 0xad, 0x05, 0x3d, 0x9d, 0xc7, 0x89, 0x48, 0xe0, 0xbb, 0x28, 0xc8,
+ 0x03, 0xe1, 0x30, 0x93, 0x64, 0x5e, 0x52, 0xc0, 0x59, 0x70, 0x22, 0x35,
+ 0x57, 0x88, 0x8a, 0xf1, 0x95, 0x0a, 0x83, 0xd7, 0xbc, 0x31, 0x73, 0x01,
+ 0x34, 0xed, 0xef, 0x46, 0x71, 0xe0, 0x6b, 0x02, 0xa8, 0x35, 0x72, 0x6b,
+ 0x97, 0x9b, 0x66, 0xe0, 0xcb, 0x1c, 0x79, 0x5f, 0xd8, 0x1a, 0x04, 0x68,
+ 0x1e, 0x47, 0x02, 0xe6, 0x9d, 0x60, 0xe2, 0x36, 0x97, 0x01, 0xdf, 0xce,
+ 0x35, 0x92, 0xdf, 0xbe, 0x67, 0xc7, 0x6d, 0x77, 0x59, 0x3b, 0x8f, 0x9d,
+ 0xd6, 0x90, 0x15, 0x94, 0xbc, 0x42, 0x34, 0x10, 0xc1, 0x39, 0xf9, 0xb1,
+ 0x27, 0x3e, 0x7e, 0xd6, 0x8a, 0x75, 0xc5, 0xb2, 0xaf, 0x96, 0xd3, 0xa2,
+ 0xde, 0x9b, 0xe4, 0x98, 0xbe, 0x7d, 0xe1, 0xe9, 0x81, 0xad, 0xb6, 0x6f,
+ 0xfc, 0xd7, 0x0e, 0xda, 0xe0, 0x34, 0xb0, 0x0d, 0x1a, 0x77, 0xe7, 0xe3,
+ 0x08, 0x98, 0xef, 0x58, 0xfa, 0x9c, 0x84, 0xb7, 0x36, 0xaf, 0xc2, 0xdf,
+ 0xac, 0xd2, 0xf4, 0x10, 0x06, 0x70, 0x71, 0x35, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x81, 0xe8, 0x30, 0x81, 0xe5, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2c, 0xd5,
+ 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b, 0x4a, 0xfb,
+ 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b,
+ 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98,
+ 0x90, 0x9f, 0xd4, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3a, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0,
+ 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x81, 0x81, 0x00, 0xaf, 0xf3, 0x0e, 0xd6, 0x72, 0xab, 0xc7, 0xa9, 0x97,
+ 0xca, 0x2a, 0x6b, 0x84, 0x39, 0xde, 0x79, 0xa9, 0xf0, 0x81, 0xe5, 0x08,
+ 0x67, 0xab, 0xd7, 0x2f, 0x20, 0x02, 0x01, 0x71, 0x0c, 0x04, 0x22, 0xc9,
+ 0x1e, 0x88, 0x95, 0x03, 0xc9, 0x49, 0x3a, 0xaf, 0x67, 0x08, 0x49, 0xb0,
+ 0xd5, 0x08, 0xf5, 0x20, 0x3d, 0x80, 0x91, 0xa0, 0xc5, 0x87, 0xa3, 0xfb,
+ 0xc9, 0xa3, 0x17, 0x91, 0xf9, 0xa8, 0x2f, 0xae, 0xe9, 0x0f, 0xdf, 0x96,
+ 0x72, 0x0f, 0x75, 0x17, 0x80, 0x5d, 0x78, 0x01, 0x4d, 0x9f, 0x1f, 0x6d,
+ 0x7b, 0xd8, 0xf5, 0x42, 0x38, 0x23, 0x1a, 0x99, 0x93, 0xf4, 0x83, 0xbe,
+ 0x3b, 0x35, 0x74, 0xe7, 0x37, 0x13, 0x35, 0x7a, 0xac, 0xb4, 0xb6, 0x90,
+ 0x82, 0x6c, 0x27, 0xa4, 0xe0, 0xec, 0x9e, 0x35, 0xbd, 0xbf, 0xe5, 0x29,
+ 0xa1, 0x47, 0x9f, 0x5b, 0x32, 0xfc, 0xe9, 0x99, 0x7d, 0x2b, 0x39,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146066 (0x23a92)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Apr 1 00:00:00 2015 GMT
+ Not After : Dec 31 23:59:59 2017 GMT
+ Subject: C=US, O=Google Inc, CN=Google Internet Authority G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:9c:2a:04:77:5c:d8:50:91:3a:06:a3:82:e0:d8:
+ 50:48:bc:89:3f:f1:19:70:1a:88:46:7e:e0:8f:c5:
+ f1:89:ce:21:ee:5a:fe:61:0d:b7:32:44:89:a0:74:
+ 0b:53:4f:55:a4:ce:82:62:95:ee:eb:59:5f:c6:e1:
+ 05:80:12:c4:5e:94:3f:bc:5b:48:38:f4:53:f7:24:
+ e6:fb:91:e9:15:c4:cf:f4:53:0d:f4:4a:fc:9f:54:
+ de:7d:be:a0:6b:6f:87:c0:d0:50:1f:28:30:03:40:
+ da:08:73:51:6c:7f:ff:3a:3c:a7:37:06:8e:bd:4b:
+ 11:04:eb:7d:24:de:e6:f9:fc:31:71:fb:94:d5:60:
+ f3:2e:4a:af:42:d2:cb:ea:c4:6a:1a:b2:cc:53:dd:
+ 15:4b:8b:1f:c8:19:61:1f:cd:9d:a8:3e:63:2b:84:
+ 35:69:65:84:c8:19:c5:46:22:f8:53:95:be:e3:80:
+ 4a:10:c6:2a:ec:ba:97:20:11:c7:39:99:10:04:a0:
+ f0:61:7a:95:25:8c:4e:52:75:e2:b6:ed:08:ca:14:
+ fc:ce:22:6a:b3:4e:cf:46:03:97:97:03:7e:c0:b1:
+ de:7b:af:45:33:cf:ba:3e:71:b7:de:f4:25:25:c2:
+ 0d:35:89:9d:9d:fb:0e:11:79:89:1e:37:c5:af:8e:
+ 72:69
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ 4A:DD:06:16:1B:BC:F6:68:B5:76:F5:81:B6:BB:62:1A:BA:5A:81:2F
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/crls/gtglobal.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.11129.2.5.1
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 08:4e:04:a7:80:7f:10:16:43:5e:02:ad:d7:42:80:f4:b0:8e:
+ d2:ae:b3:eb:11:7d:90:84:18:7d:e7:90:15:fb:49:7f:a8:99:
+ 05:91:bb:7a:c9:d6:3c:37:18:09:9a:b6:c7:92:20:07:35:33:
+ 09:e4:28:63:72:0d:b4:e0:32:9c:87:98:c4:1b:76:89:67:c1:
+ 50:58:b0:13:aa:13:1a:1b:32:a5:be:ea:11:95:4c:48:63:49:
+ e9:99:5d:20:37:cc:fe:2a:69:51:16:95:4b:a9:de:49:82:c0:
+ 10:70:f4:2c:f3:ec:bc:24:24:d0:4e:ac:a5:d9:5e:1e:6d:92:
+ c1:a7:ac:48:35:81:f9:e5:e4:9c:65:69:cd:87:a4:41:50:3f:
+ 2e:57:a5:91:51:12:58:0e:8c:09:a1:ac:7a:a4:12:a5:27:f3:
+ 9a:10:97:7d:55:03:06:f7:66:58:5f:5f:64:e1:ab:5d:6d:a5:
+ 39:48:75:98:4c:29:5a:3a:8d:d3:2b:ca:9c:55:04:bf:f4:e6:
+ 14:d5:80:ac:26:ed:17:89:a6:93:6c:5c:a4:cc:b8:f0:66:8e:
+ 64:e3:7d:9a:e2:00:b3:49:c7:e4:0a:aa:dd:5b:83:c7:70:90:
+ 46:4e:be:d0:db:59:96:6c:2e:f5:16:36:de:71:cc:01:c2:12:
+ c1:21:c6:16
+-----BEGIN CERTIFICATE-----
+MIID8DCCAtigAwIBAgIDAjqSMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTUwNDAxMDAwMDAwWhcNMTcxMjMxMjM1OTU5WjBJMQswCQYDVQQG
+EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
+bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
+VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
+h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
+ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
+EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
+DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7
+qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYD
+VR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDov
+L2cuc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCig
+JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMBcGA1UdIAQQ
+MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQsFAAOCAQEACE4Ep4B/EBZDXgKt
+10KA9LCO0q6z6xF9kIQYfeeQFftJf6iZBZG7esnWPDcYCZq2x5IgBzUzCeQoY3IN
+tOAynIeYxBt2iWfBUFiwE6oTGhsypb7qEZVMSGNJ6ZldIDfM/ippURaVS6neSYLA
+EHD0LPPsvCQk0E6spdleHm2SwaesSDWB+eXknGVpzYekQVA/LlelkVESWA6MCaGs
+eqQSpSfzmhCXfVUDBvdmWF9fZOGrXW2lOUh1mEwpWjqN0yvKnFUEv/TmFNWArCbt
+F4mmk2xcpMy48GaOZON9muIAs0nH5Aqq3VuDx3CQRk6+0NtZlmwu9RY23nHMAcIS
+wSHGFg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert2[] = {
+ 0x30, 0x82, 0x03, 0xf0, 0x30, 0x82, 0x02, 0xd8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30,
+ 0x34, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d,
+ 0x31, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39,
+ 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e,
+ 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c,
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3,
+ 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a,
+ 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a,
+ 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f,
+ 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1,
+ 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4,
+ 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53,
+ 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f,
+ 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73,
+ 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b,
+ 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb,
+ 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4,
+ 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19,
+ 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65,
+ 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80,
+ 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99,
+ 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75,
+ 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e,
+ 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf,
+ 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2,
+ 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37,
+ 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81,
+ 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x35, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0,
+ 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e,
+ 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72,
+ 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10,
+ 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
+ 0x79, 0x02, 0x05, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x08, 0x4e, 0x04, 0xa7, 0x80, 0x7f, 0x10, 0x16, 0x43, 0x5e, 0x02, 0xad,
+ 0xd7, 0x42, 0x80, 0xf4, 0xb0, 0x8e, 0xd2, 0xae, 0xb3, 0xeb, 0x11, 0x7d,
+ 0x90, 0x84, 0x18, 0x7d, 0xe7, 0x90, 0x15, 0xfb, 0x49, 0x7f, 0xa8, 0x99,
+ 0x05, 0x91, 0xbb, 0x7a, 0xc9, 0xd6, 0x3c, 0x37, 0x18, 0x09, 0x9a, 0xb6,
+ 0xc7, 0x92, 0x20, 0x07, 0x35, 0x33, 0x09, 0xe4, 0x28, 0x63, 0x72, 0x0d,
+ 0xb4, 0xe0, 0x32, 0x9c, 0x87, 0x98, 0xc4, 0x1b, 0x76, 0x89, 0x67, 0xc1,
+ 0x50, 0x58, 0xb0, 0x13, 0xaa, 0x13, 0x1a, 0x1b, 0x32, 0xa5, 0xbe, 0xea,
+ 0x11, 0x95, 0x4c, 0x48, 0x63, 0x49, 0xe9, 0x99, 0x5d, 0x20, 0x37, 0xcc,
+ 0xfe, 0x2a, 0x69, 0x51, 0x16, 0x95, 0x4b, 0xa9, 0xde, 0x49, 0x82, 0xc0,
+ 0x10, 0x70, 0xf4, 0x2c, 0xf3, 0xec, 0xbc, 0x24, 0x24, 0xd0, 0x4e, 0xac,
+ 0xa5, 0xd9, 0x5e, 0x1e, 0x6d, 0x92, 0xc1, 0xa7, 0xac, 0x48, 0x35, 0x81,
+ 0xf9, 0xe5, 0xe4, 0x9c, 0x65, 0x69, 0xcd, 0x87, 0xa4, 0x41, 0x50, 0x3f,
+ 0x2e, 0x57, 0xa5, 0x91, 0x51, 0x12, 0x58, 0x0e, 0x8c, 0x09, 0xa1, 0xac,
+ 0x7a, 0xa4, 0x12, 0xa5, 0x27, 0xf3, 0x9a, 0x10, 0x97, 0x7d, 0x55, 0x03,
+ 0x06, 0xf7, 0x66, 0x58, 0x5f, 0x5f, 0x64, 0xe1, 0xab, 0x5d, 0x6d, 0xa5,
+ 0x39, 0x48, 0x75, 0x98, 0x4c, 0x29, 0x5a, 0x3a, 0x8d, 0xd3, 0x2b, 0xca,
+ 0x9c, 0x55, 0x04, 0xbf, 0xf4, 0xe6, 0x14, 0xd5, 0x80, 0xac, 0x26, 0xed,
+ 0x17, 0x89, 0xa6, 0x93, 0x6c, 0x5c, 0xa4, 0xcc, 0xb8, 0xf0, 0x66, 0x8e,
+ 0x64, 0xe3, 0x7d, 0x9a, 0xe2, 0x00, 0xb3, 0x49, 0xc7, 0xe4, 0x0a, 0xaa,
+ 0xdd, 0x5b, 0x83, 0xc7, 0x70, 0x90, 0x46, 0x4e, 0xbe, 0xd0, 0xdb, 0x59,
+ 0x96, 0x6c, 0x2e, 0xf5, 0x16, 0x36, 0xde, 0x71, 0xcc, 0x01, 0xc2, 0x12,
+ 0xc1, 0x21, 0xc6, 0x16,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120033005 (0x7278eed)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Apr 18 16:36:18 2012 GMT
+ Not After : Aug 13 16:35:17 2018 GMT
+ Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79:
+ d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a:
+ 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2:
+ 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01:
+ 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7:
+ 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6:
+ 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c:
+ a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70:
+ 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77:
+ d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae:
+ 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18:
+ 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85:
+ ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9:
+ 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5:
+ c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a:
+ ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0:
+ 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27:
+ 1a:39
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:3
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://cybertrust.omniroot.com/repository
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 93:1d:fe:8b:ae:46:ec:cb:a9:0f:ab:e5:ef:ca:b2:68:16:68:
+ d8:8f:fa:13:a9:af:b3:cb:2d:e7:4b:6e:8e:69:2a:c2:2b:10:
+ 0a:8d:f6:ae:73:b6:b9:fb:14:fd:5f:6d:b8:50:b6:c4:8a:d6:
+ 40:7e:d7:c3:cb:73:dc:c9:5d:5b:af:b0:41:b5:37:eb:ea:dc:
+ 20:91:c4:34:6a:f4:a1:f3:96:9d:37:86:97:e1:71:a4:dd:7d:
+ fa:44:84:94:ae:d7:09:04:22:76:0f:64:51:35:a9:24:0f:f9:
+ 0b:db:32:da:c2:fe:c1:b9:2a:5c:7a:27:13:ca:b1:48:3a:71:
+ d0:43
+-----BEGIN CERTIFICATE-----
+MIIEFTCCA36gAwIBAgIEByeO7TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTEyMDQxODE2MzYxOFoXDTE4MDgxMzE2MzUxN1owWjELMAkG
+A1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVz
+dDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uO
+KymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnn
+c+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP
+wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPg
+kAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFc
+B5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaOCAUcw
+ggFDMBIGA1UdEwEB/wQIMAYBAf8CAQMwSgYDVR0gBEMwQTA/BgRVHSAAMDcwNQYI
+KwYBBQUHAgEWKWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9yZXBvc2l0
+b3J5MA4GA1UdDwEB/wQEAwIBBjCBiQYDVR0jBIGBMH+heaR3MHUxCzAJBgNVBAYT
+AlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJl
+clRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg
+R2xvYmFsIFJvb3SCAgGlMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVi
+bGljLXRydXN0LmNvbS9jZ2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwDQYJKoZIhvcN
+AQEFBQADgYEAkx3+i65G7MupD6vl78qyaBZo2I/6E6mvs8st50tujmkqwisQCo32
+rnO2ufsU/V9tuFC2xIrWQH7Xw8tz3MldW6+wQbU36+rcIJHENGr0ofOWnTeGl+Fx
+pN19+kSElK7XCQQidg9kUTWpJA/5C9sy2sL+wbkqXHonE8qxSDpx0EM=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert3[] = {
+ 0x30, 0x82, 0x04, 0x15, 0x30, 0x82, 0x03, 0x7e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x8e, 0xed, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x32, 0x30, 0x34, 0x31, 0x38, 0x31, 0x36, 0x33, 0x36, 0x31,
+ 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x33, 0x31, 0x36,
+ 0x33, 0x35, 0x31, 0x37, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69,
+ 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79,
+ 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f,
+ 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+ 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x04,
+ 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a, 0xb5, 0x79,
+ 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3, 0x5b, 0x8e,
+ 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09, 0x05, 0x6d,
+ 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88, 0xda, 0x12,
+ 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52, 0x7b, 0x88,
+ 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a, 0x09, 0xe7,
+ 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d, 0x2d, 0xe5,
+ 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea, 0xf5, 0xab,
+ 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f, 0x0c, 0xd5,
+ 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70, 0xf0, 0x8f,
+ 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33, 0x7a, 0x77,
+ 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13, 0xd2, 0xc0,
+ 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc, 0xb4, 0xdd,
+ 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5, 0x63, 0xe0,
+ 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea, 0xeb, 0xd4,
+ 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69, 0xbc, 0xf9,
+ 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9, 0x90, 0x2c,
+ 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98, 0x21, 0x5c,
+ 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86, 0x3a, 0x6b,
+ 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78, 0x8d, 0x76,
+ 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90, 0xdc, 0x27,
+ 0x1a, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x47, 0x30,
+ 0x82, 0x01, 0x43, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x03, 0x30,
+ 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x43, 0x30, 0x41, 0x30, 0x3f,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x37, 0x30, 0x35, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x29, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x81, 0x89, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77,
+ 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23,
+ 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45,
+ 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82,
+ 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3e,
+ 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86, 0x34, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e, 0x2f, 0x43, 0x52,
+ 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64, 0x70, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x93, 0x1d, 0xfe,
+ 0x8b, 0xae, 0x46, 0xec, 0xcb, 0xa9, 0x0f, 0xab, 0xe5, 0xef, 0xca, 0xb2,
+ 0x68, 0x16, 0x68, 0xd8, 0x8f, 0xfa, 0x13, 0xa9, 0xaf, 0xb3, 0xcb, 0x2d,
+ 0xe7, 0x4b, 0x6e, 0x8e, 0x69, 0x2a, 0xc2, 0x2b, 0x10, 0x0a, 0x8d, 0xf6,
+ 0xae, 0x73, 0xb6, 0xb9, 0xfb, 0x14, 0xfd, 0x5f, 0x6d, 0xb8, 0x50, 0xb6,
+ 0xc4, 0x8a, 0xd6, 0x40, 0x7e, 0xd7, 0xc3, 0xcb, 0x73, 0xdc, 0xc9, 0x5d,
+ 0x5b, 0xaf, 0xb0, 0x41, 0xb5, 0x37, 0xeb, 0xea, 0xdc, 0x20, 0x91, 0xc4,
+ 0x34, 0x6a, 0xf4, 0xa1, 0xf3, 0x96, 0x9d, 0x37, 0x86, 0x97, 0xe1, 0x71,
+ 0xa4, 0xdd, 0x7d, 0xfa, 0x44, 0x84, 0x94, 0xae, 0xd7, 0x09, 0x04, 0x22,
+ 0x76, 0x0f, 0x64, 0x51, 0x35, 0xa9, 0x24, 0x0f, 0xf9, 0x0b, 0xdb, 0x32,
+ 0xda, 0xc2, 0xfe, 0xc1, 0xb9, 0x2a, 0x5c, 0x7a, 0x27, 0x13, 0xca, 0xb1,
+ 0x48, 0x3a, 0x71, 0xd0, 0x43,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146039 (0x23a77)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Aug 29 21:39:32 2014 GMT
+ Not After : May 20 21:39:32 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:54:9b:d9:58:5d:1e:2c:56:c6:d5:e8:7f:f4:
+ 7d:16:03:ff:d0:8b:5a:e4:8e:a7:dd:54:2e:d4:04:
+ c0:5d:98:9c:8d:90:0f:bc:10:65:5f:da:9a:d6:44:
+ 7c:c0:9f:b5:e9:4a:8c:0b:06:43:04:bb:f4:96:e2:
+ 26:f6:61:01:91:66:31:22:c3:34:34:5f:3f:3f:91:
+ 2f:44:5f:dc:c7:14:b6:03:9f:86:4b:0e:a3:ff:a0:
+ 80:02:83:c3:d3:1f:69:52:d6:9d:64:0f:c9:83:e7:
+ 1b:c4:70:ac:94:e7:c3:a4:6a:2c:bd:b8:9e:69:d8:
+ be:0a:8f:16:63:5a:68:71:80:7b:30:de:15:04:bf:
+ cc:d3:bf:3e:48:05:55:7a:b3:d7:10:0c:03:fc:9b:
+ fd:08:a7:8c:8c:db:a7:8e:f1:1e:63:dc:b3:01:2f:
+ 7f:af:57:c3:3c:48:a7:83:68:21:a7:2f:e7:a7:3f:
+ f0:b5:0c:fc:f5:84:d1:53:bc:0e:72:4f:60:0c:42:
+ b8:98:ad:19:88:57:d7:04:ec:87:bf:7e:87:4e:a3:
+ 21:f9:53:fd:36:98:48:8d:d6:f8:bb:48:f2:29:c8:
+ 64:d1:cc:54:48:53:8b:af:b7:65:1e:bf:29:33:29:
+ d9:29:60:48:f8:ff:91:bc:57:58:e5:35:2e:bb:69:
+ b6:59
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ C3:9C:F3:FC:D3:46:08:34:BB:CE:46:7F:A0:7C:5B:F3:E2:08:CB:59
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha256WithRSAEncryption
+ a3:58:1e:c6:43:32:ac:ac:2f:93:78:b7:ea:ae:54:40:47:2d:
+ 7e:78:8d:50:f6:f8:66:ac:d6:4f:73:d6:44:ef:af:0b:cc:5b:
+ c1:f4:4f:9a:8f:49:7e:60:af:c2:27:c7:16:f1:fb:93:81:90:
+ a9:7c:ef:6f:7e:6e:45:94:16:84:bd:ec:49:f1:c4:0e:f4:af:
+ 04:59:83:87:0f:2c:3b:97:c3:5a:12:9b:7b:04:35:7b:a3:95:
+ 33:08:7b:93:71:22:42:b3:a9:d9:6f:4f:81:92:fc:07:b6:79:
+ bc:84:4a:9d:77:09:f1:c5:89:f2:f0:b4:9c:54:aa:12:7b:0d:
+ ba:4f:ef:93:19:ec:ef:7d:4e:61:a3:8e:76:9c:59:cf:8c:94:
+ b1:84:97:f7:1a:b9:07:b8:b2:c6:4f:13:79:db:bf:4f:51:1b:
+ 7f:69:0d:51:2a:c1:d6:15:ff:37:51:34:65:51:f4:1e:be:38:
+ 6a:ec:0e:ab:bf:3d:7b:39:05:7b:f4:f3:fb:1a:a1:d0:c8:7e:
+ 4e:64:8d:cd:8c:61:55:90:fe:3a:ca:5d:25:0f:f8:1d:a3:4a:
+ 74:56:4f:1a:55:40:70:75:25:a6:33:2e:ba:4b:a5:5d:53:9a:
+ 0d:30:e1:8d:5f:61:2c:af:cc:ef:b0:99:a1:80:ff:0b:f2:62:
+ 4c:70:26:98
+-----BEGIN CERTIFICATE-----
+MIIEJTCCAw2gAwIBAgIDAjp3MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTQwODI5MjEzOTMyWhcNMjIwNTIwMjEzOTMyWjBHMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXUmFwaWRTU0wg
+U0hBMjU2IENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv
+VJvZWF0eLFbG1eh/9H0WA//Qi1rkjqfdVC7UBMBdmJyNkA+8EGVf2prWRHzAn7Xp
+SowLBkMEu/SW4ib2YQGRZjEiwzQ0Xz8/kS9EX9zHFLYDn4ZLDqP/oIACg8PTH2lS
+1p1kD8mD5xvEcKyU58Okaiy9uJ5p2L4KjxZjWmhxgHsw3hUEv8zTvz5IBVV6s9cQ
+DAP8m/0Ip4yM26eO8R5j3LMBL3+vV8M8SKeDaCGnL+enP/C1DPz1hNFTvA5yT2AM
+QriYrRmIV9cE7Ie/fodOoyH5U/02mEiN1vi7SPIpyGTRzFRIU4uvt2UevykzKdkp
+YEj4/5G8V1jlNS67abZZAgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7
+qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUw5zz/NNGCDS7zkZ/oHxb8+IIy1kwEgYD
+VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCig
+JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUF
+BwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARF
+MEMwQQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3Ry
+dXN0LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQCjWB7GQzKs
+rC+TeLfqrlRARy1+eI1Q9vhmrNZPc9ZE768LzFvB9E+aj0l+YK/CJ8cW8fuTgZCp
+fO9vfm5FlBaEvexJ8cQO9K8EWYOHDyw7l8NaEpt7BDV7o5UzCHuTcSJCs6nZb0+B
+kvwHtnm8hEqddwnxxYny8LScVKoSew26T++TGezvfU5ho452nFnPjJSxhJf3GrkH
+uLLGTxN5279PURt/aQ1RKsHWFf83UTRlUfQevjhq7A6rvz17OQV79PP7GqHQyH5O
+ZI3NjGFVkP46yl0lD/gdo0p0Vk8aVUBwdSWmMy66S6VdU5oNMOGNX2Esr8zvsJmh
+gP8L8mJMcCaY
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert4[] = {
+ 0x30, 0x82, 0x04, 0x25, 0x30, 0x82, 0x03, 0x0d, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x77, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30,
+ 0x38, 0x32, 0x39, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x39, 0x33, 0x32,
+ 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20,
+ 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20,
+ 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf,
+ 0x54, 0x9b, 0xd9, 0x58, 0x5d, 0x1e, 0x2c, 0x56, 0xc6, 0xd5, 0xe8, 0x7f,
+ 0xf4, 0x7d, 0x16, 0x03, 0xff, 0xd0, 0x8b, 0x5a, 0xe4, 0x8e, 0xa7, 0xdd,
+ 0x54, 0x2e, 0xd4, 0x04, 0xc0, 0x5d, 0x98, 0x9c, 0x8d, 0x90, 0x0f, 0xbc,
+ 0x10, 0x65, 0x5f, 0xda, 0x9a, 0xd6, 0x44, 0x7c, 0xc0, 0x9f, 0xb5, 0xe9,
+ 0x4a, 0x8c, 0x0b, 0x06, 0x43, 0x04, 0xbb, 0xf4, 0x96, 0xe2, 0x26, 0xf6,
+ 0x61, 0x01, 0x91, 0x66, 0x31, 0x22, 0xc3, 0x34, 0x34, 0x5f, 0x3f, 0x3f,
+ 0x91, 0x2f, 0x44, 0x5f, 0xdc, 0xc7, 0x14, 0xb6, 0x03, 0x9f, 0x86, 0x4b,
+ 0x0e, 0xa3, 0xff, 0xa0, 0x80, 0x02, 0x83, 0xc3, 0xd3, 0x1f, 0x69, 0x52,
+ 0xd6, 0x9d, 0x64, 0x0f, 0xc9, 0x83, 0xe7, 0x1b, 0xc4, 0x70, 0xac, 0x94,
+ 0xe7, 0xc3, 0xa4, 0x6a, 0x2c, 0xbd, 0xb8, 0x9e, 0x69, 0xd8, 0xbe, 0x0a,
+ 0x8f, 0x16, 0x63, 0x5a, 0x68, 0x71, 0x80, 0x7b, 0x30, 0xde, 0x15, 0x04,
+ 0xbf, 0xcc, 0xd3, 0xbf, 0x3e, 0x48, 0x05, 0x55, 0x7a, 0xb3, 0xd7, 0x10,
+ 0x0c, 0x03, 0xfc, 0x9b, 0xfd, 0x08, 0xa7, 0x8c, 0x8c, 0xdb, 0xa7, 0x8e,
+ 0xf1, 0x1e, 0x63, 0xdc, 0xb3, 0x01, 0x2f, 0x7f, 0xaf, 0x57, 0xc3, 0x3c,
+ 0x48, 0xa7, 0x83, 0x68, 0x21, 0xa7, 0x2f, 0xe7, 0xa7, 0x3f, 0xf0, 0xb5,
+ 0x0c, 0xfc, 0xf5, 0x84, 0xd1, 0x53, 0xbc, 0x0e, 0x72, 0x4f, 0x60, 0x0c,
+ 0x42, 0xb8, 0x98, 0xad, 0x19, 0x88, 0x57, 0xd7, 0x04, 0xec, 0x87, 0xbf,
+ 0x7e, 0x87, 0x4e, 0xa3, 0x21, 0xf9, 0x53, 0xfd, 0x36, 0x98, 0x48, 0x8d,
+ 0xd6, 0xf8, 0xbb, 0x48, 0xf2, 0x29, 0xc8, 0x64, 0xd1, 0xcc, 0x54, 0x48,
+ 0x53, 0x8b, 0xaf, 0xb7, 0x65, 0x1e, 0xbf, 0x29, 0x33, 0x29, 0xd9, 0x29,
+ 0x60, 0x48, 0xf8, 0xff, 0x91, 0xbc, 0x57, 0x58, 0xe5, 0x35, 0x2e, 0xbb,
+ 0x69, 0xb6, 0x59, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d,
+ 0x30, 0x82, 0x01, 0x19, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0xc3, 0x9c, 0xf3, 0xfc, 0xd3, 0x46, 0x08, 0x34, 0xbb, 0xce, 0x46, 0x7f,
+ 0xa0, 0x7c, 0x5b, 0xf3, 0xe2, 0x08, 0xcb, 0x59, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0,
+ 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e,
+ 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72,
+ 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45,
+ 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8,
+ 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x58, 0x1e, 0xc6, 0x43, 0x32, 0xac,
+ 0xac, 0x2f, 0x93, 0x78, 0xb7, 0xea, 0xae, 0x54, 0x40, 0x47, 0x2d, 0x7e,
+ 0x78, 0x8d, 0x50, 0xf6, 0xf8, 0x66, 0xac, 0xd6, 0x4f, 0x73, 0xd6, 0x44,
+ 0xef, 0xaf, 0x0b, 0xcc, 0x5b, 0xc1, 0xf4, 0x4f, 0x9a, 0x8f, 0x49, 0x7e,
+ 0x60, 0xaf, 0xc2, 0x27, 0xc7, 0x16, 0xf1, 0xfb, 0x93, 0x81, 0x90, 0xa9,
+ 0x7c, 0xef, 0x6f, 0x7e, 0x6e, 0x45, 0x94, 0x16, 0x84, 0xbd, 0xec, 0x49,
+ 0xf1, 0xc4, 0x0e, 0xf4, 0xaf, 0x04, 0x59, 0x83, 0x87, 0x0f, 0x2c, 0x3b,
+ 0x97, 0xc3, 0x5a, 0x12, 0x9b, 0x7b, 0x04, 0x35, 0x7b, 0xa3, 0x95, 0x33,
+ 0x08, 0x7b, 0x93, 0x71, 0x22, 0x42, 0xb3, 0xa9, 0xd9, 0x6f, 0x4f, 0x81,
+ 0x92, 0xfc, 0x07, 0xb6, 0x79, 0xbc, 0x84, 0x4a, 0x9d, 0x77, 0x09, 0xf1,
+ 0xc5, 0x89, 0xf2, 0xf0, 0xb4, 0x9c, 0x54, 0xaa, 0x12, 0x7b, 0x0d, 0xba,
+ 0x4f, 0xef, 0x93, 0x19, 0xec, 0xef, 0x7d, 0x4e, 0x61, 0xa3, 0x8e, 0x76,
+ 0x9c, 0x59, 0xcf, 0x8c, 0x94, 0xb1, 0x84, 0x97, 0xf7, 0x1a, 0xb9, 0x07,
+ 0xb8, 0xb2, 0xc6, 0x4f, 0x13, 0x79, 0xdb, 0xbf, 0x4f, 0x51, 0x1b, 0x7f,
+ 0x69, 0x0d, 0x51, 0x2a, 0xc1, 0xd6, 0x15, 0xff, 0x37, 0x51, 0x34, 0x65,
+ 0x51, 0xf4, 0x1e, 0xbe, 0x38, 0x6a, 0xec, 0x0e, 0xab, 0xbf, 0x3d, 0x7b,
+ 0x39, 0x05, 0x7b, 0xf4, 0xf3, 0xfb, 0x1a, 0xa1, 0xd0, 0xc8, 0x7e, 0x4e,
+ 0x64, 0x8d, 0xcd, 0x8c, 0x61, 0x55, 0x90, 0xfe, 0x3a, 0xca, 0x5d, 0x25,
+ 0x0f, 0xf8, 0x1d, 0xa3, 0x4a, 0x74, 0x56, 0x4f, 0x1a, 0x55, 0x40, 0x70,
+ 0x75, 0x25, 0xa6, 0x33, 0x2e, 0xba, 0x4b, 0xa5, 0x5d, 0x53, 0x9a, 0x0d,
+ 0x30, 0xe1, 0x8d, 0x5f, 0x61, 0x2c, 0xaf, 0xcc, 0xef, 0xb0, 0x99, 0xa1,
+ 0x80, 0xff, 0x0b, 0xf2, 0x62, 0x4c, 0x70, 0x26, 0x98,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146040 (0x23a78)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Aug 29 22:24:58 2014 GMT
+ Not After : May 20 22:24:58 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., OU=Domain Validated SSL, CN=GeoTrust DV SSL CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:df:41:94:7a:da:f7:e4:31:43:b6:ea:01:1b:5c:
+ ce:63:ea:fa:6d:a3:d9:6a:ee:2d:9a:75:f9:d5:9c:
+ 5b:bd:34:df:d8:1c:c9:6d:d8:04:88:da:6e:b5:b7:
+ b5:f0:30:ae:40:d6:5d:fa:c4:53:c1:d4:22:9d:04:
+ 4e:11:a6:95:d5:45:7c:41:05:58:e0:4c:dd:f9:ee:
+ 55:bd:5f:46:dc:ad:13:08:9d:2c:e4:f7:82:e6:07:
+ 2b:9e:0e:8c:34:a1:ce:c4:a1:e0:81:70:86:00:06:
+ 3f:2d:ea:7c:9b:28:ae:1b:28:8b:39:09:d3:e7:f0:
+ 45:a4:b1:ba:11:67:90:55:7b:8f:de:ed:38:5c:a1:
+ e1:e3:83:c4:c3:72:91:4f:98:ee:1c:c2:80:aa:64:
+ a5:3e:83:62:1c:cc:e0:9e:f8:5a:c0:13:12:7d:a2:
+ a7:8b:a3:e7:9f:2a:d7:9b:ca:cb:ed:97:01:9c:28:
+ 84:51:04:50:41:bc:b4:fc:78:e9:1b:cf:14:ea:1f:
+ 0f:fc:2e:01:32:8d:b6:35:cb:0a:18:3b:ec:5a:3e:
+ 3c:1b:d3:99:43:1e:2f:f7:bd:f3:5b:12:b9:07:5e:
+ ed:3e:d1:a9:87:cc:77:72:27:d4:d9:75:a2:63:4b:
+ 93:36:bd:e5:5c:d7:bf:5f:79:0d:b3:32:a7:0b:b2:
+ 63:23
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ 0B:50:EC:77:EF:2A:9B:FF:EC:03:A1:0A:FF:AD:C6:E4:2A:18:C7:3E
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 33:24:d5:90:aa:29:0c:35:b9:2f:c3:c7:42:93:c0:c6:10:4b:
+ 03:08:76:84:10:a2:e0:e7:53:12:27:f2:0a:da:7f:3a:dc:fd:
+ 5c:79:5a:8f:17:74:43:53:b1:d5:d1:5d:59:b9:a6:84:64:ca:
+ f1:3a:0a:59:96:10:bf:a9:81:57:8b:5c:87:dc:7f:e3:e4:bb:
+ 05:7a:a0:32:09:13:4e:10:81:28:1f:9c:03:62:bc:f4:01:b5:
+ 29:83:46:07:b9:e7:b8:5d:c8:e9:d1:dd:ad:3b:f8:34:db:c1:
+ d1:95:a9:91:18:ed:3c:2c:37:11:4d:cc:fe:53:3e:50:43:f9:
+ c3:56:41:ac:53:9b:6c:05:b2:9a:e2:e0:59:57:30:32:b6:26:
+ 4e:13:25:cd:fa:48:70:0f:75:55:60:11:f5:3b:d5:5e:5a:3c:
+ 8b:5b:0f:0f:62:42:48:61:85:8b:10:f4:c1:88:bf:7f:5f:8a:
+ c2:d7:cd:2b:94:5c:1f:34:4a:08:af:eb:ae:89:a8:48:75:55:
+ 95:1d:bb:c0:9a:01:b9:f4:03:22:3e:d4:e6:52:30:0d:67:b9:
+ c0:91:fd:2d:4c:30:8e:bd:8c:a5:04:91:bb:a4:ab:7f:0f:d8:
+ 6f:f0:66:00:c9:a3:5c:f5:b0:8f:83:e6:9c:5a:e6:b6:b9:c5:
+ bc:be:e4:02
+-----BEGIN CERTIFICATE-----
+MIIERDCCAyygAwIBAgIDAjp4MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTQwODI5MjIyNDU4WhcNMjIwNTIwMjIyNDU4WjBmMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UECxMURG9tYWluIFZh
+bGlkYXRlZCBTU0wxIDAeBgNVBAMTF0dlb1RydXN0IERWIFNTTCBDQSAtIEc0MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA30GUetr35DFDtuoBG1zOY+r6
+baPZau4tmnX51ZxbvTTf2BzJbdgEiNputbe18DCuQNZd+sRTwdQinQROEaaV1UV8
+QQVY4Ezd+e5VvV9G3K0TCJ0s5PeC5gcrng6MNKHOxKHggXCGAAY/Lep8myiuGyiL
+OQnT5/BFpLG6EWeQVXuP3u04XKHh44PEw3KRT5juHMKAqmSlPoNiHMzgnvhawBMS
+faKni6PnnyrXm8rL7ZcBnCiEUQRQQby0/HjpG88U6h8P/C4BMo22NcsKGDvsWj48
+G9OZQx4v973zWxK5B17tPtGph8x3cifU2XWiY0uTNr3lXNe/X3kNszKnC7JjIwID
+AQABo4IBHTCCARkwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4wHQYD
+VR0OBBYEFAtQ7HfvKpv/7AOhCv+txuQqGMc+MBIGA1UdEwEB/wQIMAYBAf8CAQAw
+DgYDVR0PAQH/BAQDAgEGMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9nLnN5bWNi
+LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAuBggrBgEFBQcBAQQiMCAwHgYIKwYBBQUH
+MAGGEmh0dHA6Ly9nLnN5bWNkLmNvbTBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYw
+MzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2Vz
+L2NwczANBgkqhkiG9w0BAQsFAAOCAQEAMyTVkKopDDW5L8PHQpPAxhBLAwh2hBCi
+4OdTEifyCtp/Otz9XHlajxd0Q1Ox1dFdWbmmhGTK8ToKWZYQv6mBV4tch9x/4+S7
+BXqgMgkTThCBKB+cA2K89AG1KYNGB7nnuF3I6dHdrTv4NNvB0ZWpkRjtPCw3EU3M
+/lM+UEP5w1ZBrFObbAWymuLgWVcwMrYmThMlzfpIcA91VWAR9TvVXlo8i1sPD2JC
+SGGFixD0wYi/f1+KwtfNK5RcHzRKCK/rromoSHVVlR27wJoBufQDIj7U5lIwDWe5
+wJH9LUwwjr2MpQSRu6Srfw/Yb/BmAMmjXPWwj4PmnFrmtrnFvL7kAg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert5[] = {
+ 0x30, 0x82, 0x04, 0x44, 0x30, 0x82, 0x03, 0x2c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30,
+ 0x38, 0x32, 0x39, 0x32, 0x32, 0x32, 0x34, 0x35, 0x38, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x32, 0x32, 0x34, 0x35, 0x38,
+ 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x14, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61,
+ 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31,
+ 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x47, 0x65,
+ 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x44, 0x56, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdf, 0x41, 0x94, 0x7a, 0xda, 0xf7,
+ 0xe4, 0x31, 0x43, 0xb6, 0xea, 0x01, 0x1b, 0x5c, 0xce, 0x63, 0xea, 0xfa,
+ 0x6d, 0xa3, 0xd9, 0x6a, 0xee, 0x2d, 0x9a, 0x75, 0xf9, 0xd5, 0x9c, 0x5b,
+ 0xbd, 0x34, 0xdf, 0xd8, 0x1c, 0xc9, 0x6d, 0xd8, 0x04, 0x88, 0xda, 0x6e,
+ 0xb5, 0xb7, 0xb5, 0xf0, 0x30, 0xae, 0x40, 0xd6, 0x5d, 0xfa, 0xc4, 0x53,
+ 0xc1, 0xd4, 0x22, 0x9d, 0x04, 0x4e, 0x11, 0xa6, 0x95, 0xd5, 0x45, 0x7c,
+ 0x41, 0x05, 0x58, 0xe0, 0x4c, 0xdd, 0xf9, 0xee, 0x55, 0xbd, 0x5f, 0x46,
+ 0xdc, 0xad, 0x13, 0x08, 0x9d, 0x2c, 0xe4, 0xf7, 0x82, 0xe6, 0x07, 0x2b,
+ 0x9e, 0x0e, 0x8c, 0x34, 0xa1, 0xce, 0xc4, 0xa1, 0xe0, 0x81, 0x70, 0x86,
+ 0x00, 0x06, 0x3f, 0x2d, 0xea, 0x7c, 0x9b, 0x28, 0xae, 0x1b, 0x28, 0x8b,
+ 0x39, 0x09, 0xd3, 0xe7, 0xf0, 0x45, 0xa4, 0xb1, 0xba, 0x11, 0x67, 0x90,
+ 0x55, 0x7b, 0x8f, 0xde, 0xed, 0x38, 0x5c, 0xa1, 0xe1, 0xe3, 0x83, 0xc4,
+ 0xc3, 0x72, 0x91, 0x4f, 0x98, 0xee, 0x1c, 0xc2, 0x80, 0xaa, 0x64, 0xa5,
+ 0x3e, 0x83, 0x62, 0x1c, 0xcc, 0xe0, 0x9e, 0xf8, 0x5a, 0xc0, 0x13, 0x12,
+ 0x7d, 0xa2, 0xa7, 0x8b, 0xa3, 0xe7, 0x9f, 0x2a, 0xd7, 0x9b, 0xca, 0xcb,
+ 0xed, 0x97, 0x01, 0x9c, 0x28, 0x84, 0x51, 0x04, 0x50, 0x41, 0xbc, 0xb4,
+ 0xfc, 0x78, 0xe9, 0x1b, 0xcf, 0x14, 0xea, 0x1f, 0x0f, 0xfc, 0x2e, 0x01,
+ 0x32, 0x8d, 0xb6, 0x35, 0xcb, 0x0a, 0x18, 0x3b, 0xec, 0x5a, 0x3e, 0x3c,
+ 0x1b, 0xd3, 0x99, 0x43, 0x1e, 0x2f, 0xf7, 0xbd, 0xf3, 0x5b, 0x12, 0xb9,
+ 0x07, 0x5e, 0xed, 0x3e, 0xd1, 0xa9, 0x87, 0xcc, 0x77, 0x72, 0x27, 0xd4,
+ 0xd9, 0x75, 0xa2, 0x63, 0x4b, 0x93, 0x36, 0xbd, 0xe5, 0x5c, 0xd7, 0xbf,
+ 0x5f, 0x79, 0x0d, 0xb3, 0x32, 0xa7, 0x0b, 0xb2, 0x63, 0x23, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, 0x19, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11,
+ 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0b, 0x50, 0xec, 0x77, 0xef,
+ 0x2a, 0x9b, 0xff, 0xec, 0x03, 0xa1, 0x0a, 0xff, 0xad, 0xc6, 0xe4, 0x2a,
+ 0x18, 0xc7, 0x3e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e,
+ 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74,
+ 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x2e,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22,
+ 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67,
+ 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4c,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06,
+ 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30,
+ 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x33, 0x24, 0xd5, 0x90, 0xaa, 0x29, 0x0c, 0x35, 0xb9, 0x2f, 0xc3, 0xc7,
+ 0x42, 0x93, 0xc0, 0xc6, 0x10, 0x4b, 0x03, 0x08, 0x76, 0x84, 0x10, 0xa2,
+ 0xe0, 0xe7, 0x53, 0x12, 0x27, 0xf2, 0x0a, 0xda, 0x7f, 0x3a, 0xdc, 0xfd,
+ 0x5c, 0x79, 0x5a, 0x8f, 0x17, 0x74, 0x43, 0x53, 0xb1, 0xd5, 0xd1, 0x5d,
+ 0x59, 0xb9, 0xa6, 0x84, 0x64, 0xca, 0xf1, 0x3a, 0x0a, 0x59, 0x96, 0x10,
+ 0xbf, 0xa9, 0x81, 0x57, 0x8b, 0x5c, 0x87, 0xdc, 0x7f, 0xe3, 0xe4, 0xbb,
+ 0x05, 0x7a, 0xa0, 0x32, 0x09, 0x13, 0x4e, 0x10, 0x81, 0x28, 0x1f, 0x9c,
+ 0x03, 0x62, 0xbc, 0xf4, 0x01, 0xb5, 0x29, 0x83, 0x46, 0x07, 0xb9, 0xe7,
+ 0xb8, 0x5d, 0xc8, 0xe9, 0xd1, 0xdd, 0xad, 0x3b, 0xf8, 0x34, 0xdb, 0xc1,
+ 0xd1, 0x95, 0xa9, 0x91, 0x18, 0xed, 0x3c, 0x2c, 0x37, 0x11, 0x4d, 0xcc,
+ 0xfe, 0x53, 0x3e, 0x50, 0x43, 0xf9, 0xc3, 0x56, 0x41, 0xac, 0x53, 0x9b,
+ 0x6c, 0x05, 0xb2, 0x9a, 0xe2, 0xe0, 0x59, 0x57, 0x30, 0x32, 0xb6, 0x26,
+ 0x4e, 0x13, 0x25, 0xcd, 0xfa, 0x48, 0x70, 0x0f, 0x75, 0x55, 0x60, 0x11,
+ 0xf5, 0x3b, 0xd5, 0x5e, 0x5a, 0x3c, 0x8b, 0x5b, 0x0f, 0x0f, 0x62, 0x42,
+ 0x48, 0x61, 0x85, 0x8b, 0x10, 0xf4, 0xc1, 0x88, 0xbf, 0x7f, 0x5f, 0x8a,
+ 0xc2, 0xd7, 0xcd, 0x2b, 0x94, 0x5c, 0x1f, 0x34, 0x4a, 0x08, 0xaf, 0xeb,
+ 0xae, 0x89, 0xa8, 0x48, 0x75, 0x55, 0x95, 0x1d, 0xbb, 0xc0, 0x9a, 0x01,
+ 0xb9, 0xf4, 0x03, 0x22, 0x3e, 0xd4, 0xe6, 0x52, 0x30, 0x0d, 0x67, 0xb9,
+ 0xc0, 0x91, 0xfd, 0x2d, 0x4c, 0x30, 0x8e, 0xbd, 0x8c, 0xa5, 0x04, 0x91,
+ 0xbb, 0xa4, 0xab, 0x7f, 0x0f, 0xd8, 0x6f, 0xf0, 0x66, 0x00, 0xc9, 0xa3,
+ 0x5c, 0xf5, 0xb0, 0x8f, 0x83, 0xe6, 0x9c, 0x5a, 0xe6, 0xb6, 0xb9, 0xc5,
+ 0xbc, 0xbe, 0xe4, 0x02,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 33:65:50:08:79:ad:73:e2:30:b9:e0:1d:0d:7f:ac:91
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
+ Validity
+ Not Before: Nov 17 00:00:00 2006 GMT
+ Not After : Dec 30 23:59:59 2020 GMT
+ Subject: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ac:a0:f0:fb:80:59:d4:9c:c7:a4:cf:9d:a1:59:
+ 73:09:10:45:0c:0d:2c:6e:68:f1:6c:5b:48:68:49:
+ 59:37:fc:0b:33:19:c2:77:7f:cc:10:2d:95:34:1c:
+ e6:eb:4d:09:a7:1c:d2:b8:c9:97:36:02:b7:89:d4:
+ 24:5f:06:c0:cc:44:94:94:8d:02:62:6f:eb:5a:dd:
+ 11:8d:28:9a:5c:84:90:10:7a:0d:bd:74:66:2f:6a:
+ 38:a0:e2:d5:54:44:eb:1d:07:9f:07:ba:6f:ee:e9:
+ fd:4e:0b:29:f5:3e:84:a0:01:f1:9c:ab:f8:1c:7e:
+ 89:a4:e8:a1:d8:71:65:0d:a3:51:7b:ee:bc:d2:22:
+ 60:0d:b9:5b:9d:df:ba:fc:51:5b:0b:af:98:b2:e9:
+ 2e:e9:04:e8:62:87:de:2b:c8:d7:4e:c1:4c:64:1e:
+ dd:cf:87:58:ba:4a:4f:ca:68:07:1d:1c:9d:4a:c6:
+ d5:2f:91:cc:7c:71:72:1c:c5:c0:67:eb:32:fd:c9:
+ 92:5c:94:da:85:c0:9b:bf:53:7d:2b:09:f4:8c:9d:
+ 91:1f:97:6a:52:cb:de:09:36:a4:77:d8:7b:87:50:
+ 44:d5:3e:6e:29:69:fb:39:49:26:1e:09:a5:80:7b:
+ 40:2d:eb:e8:27:85:c9:fe:61:fd:7e:e6:7c:97:1d:
+ d5:9d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.thawte.com/cps
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePremiumServerCA.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 84:a8:4c:c9:3e:2a:bc:9a:e2:cc:8f:0b:b2:25:77:c4:61:89:
+ 89:63:5a:d4:a3:15:40:d4:fb:5e:3f:b4:43:ea:63:17:2b:6b:
+ 99:74:9e:09:a8:dd:d4:56:15:2e:7a:79:31:5f:63:96:53:1b:
+ 34:d9:15:ea:4f:6d:70:ca:be:f6:82:a9:ed:da:85:77:cc:76:
+ 1c:6a:81:0a:21:d8:41:99:7f:5e:2e:82:c1:e8:aa:f7:93:81:
+ 05:aa:92:b4:1f:b7:9a:c0:07:17:f5:cb:c6:b4:4c:0e:d7:56:
+ dc:71:20:74:38:d6:74:c6:d6:8f:6b:af:8b:8d:a0:6c:29:0b:
+ 61:e0
+-----BEGIN CERTIFICATE-----
+MIIERTCCA66gAwIBAgIQM2VQCHmtc+IwueAdDX+skTANBgkqhkiG9w0BAQUFADCB
+zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
+Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
+d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTA2MTExNzAwMDAwMFoXDTIwMTIzMDIzNTk1OVow
+gakxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xKDAmBgNVBAsT
+H0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xODA2BgNVBAsTLyhjKSAy
+MDA2IHRoYXd0ZSwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYD
+VQQDExZ0aGF3dGUgUHJpbWFyeSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEArKDw+4BZ1JzHpM+doVlzCRBFDA0sbmjxbFtIaElZN/wLMxnC
+d3/MEC2VNBzm600JpxzSuMmXNgK3idQkXwbAzESUlI0CYm/rWt0RjSiaXISQEHoN
+vXRmL2o4oOLVVETrHQefB7pv7un9Tgsp9T6EoAHxnKv4HH6JpOih2HFlDaNRe+68
+0iJgDblbnd+6/FFbC6+Ysuku6QToYofeK8jXTsFMZB7dz4dYukpPymgHHRydSsbV
+L5HMfHFyHMXAZ+sy/cmSXJTahcCbv1N9Kwn0jJ2RH5dqUsveCTakd9h7h1BE1T5u
+KWn7OUkmHgmlgHtALevoJ4XJ/mH9fuZ8lx3VnQIDAQABo4HCMIG/MA8GA1UdEwEB
+/wQFMAMBAf8wOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBz
+Oi8vd3d3LnRoYXd0ZS5jb20vY3BzMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
+e1tFz6/Oy3r9MZIaarbzRutXSFAwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL2Ny
+bC50aGF3dGUuY29tL1RoYXd0ZVByZW1pdW1TZXJ2ZXJDQS5jcmwwDQYJKoZIhvcN
+AQEFBQADgYEAhKhMyT4qvJrizI8LsiV3xGGJiWNa1KMVQNT7Xj+0Q+pjFytrmXSe
+Cajd1FYVLnp5MV9jllMbNNkV6k9tcMq+9oKp7dqFd8x2HGqBCiHYQZl/Xi6Cweiq
+95OBBaqStB+3msAHF/XLxrRMDtdW3HEgdDjWdMbWj2uvi42gbCkLYeA=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert6[] = {
+ 0x30, 0x82, 0x04, 0x45, 0x30, 0x82, 0x03, 0xae, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x33, 0x65, 0x50, 0x08, 0x79, 0xad, 0x73, 0xe2, 0x30,
+ 0xb9, 0xe0, 0x1d, 0x0d, 0x7f, 0xac, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xce, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x5a, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0c, 0x57, 0x65, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x43, 0x61, 0x70,
+ 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09,
+ 0x43, 0x61, 0x70, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31, 0x1d, 0x30,
+ 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e,
+ 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61,
+ 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30,
+ 0x26, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x19, 0x70, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31,
+ 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x31, 0x32, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30,
+ 0x81, 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20,
+ 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32,
+ 0x30, 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73,
+ 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20,
+ 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74,
+ 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xac, 0xa0, 0xf0, 0xfb, 0x80, 0x59, 0xd4, 0x9c, 0xc7, 0xa4, 0xcf, 0x9d,
+ 0xa1, 0x59, 0x73, 0x09, 0x10, 0x45, 0x0c, 0x0d, 0x2c, 0x6e, 0x68, 0xf1,
+ 0x6c, 0x5b, 0x48, 0x68, 0x49, 0x59, 0x37, 0xfc, 0x0b, 0x33, 0x19, 0xc2,
+ 0x77, 0x7f, 0xcc, 0x10, 0x2d, 0x95, 0x34, 0x1c, 0xe6, 0xeb, 0x4d, 0x09,
+ 0xa7, 0x1c, 0xd2, 0xb8, 0xc9, 0x97, 0x36, 0x02, 0xb7, 0x89, 0xd4, 0x24,
+ 0x5f, 0x06, 0xc0, 0xcc, 0x44, 0x94, 0x94, 0x8d, 0x02, 0x62, 0x6f, 0xeb,
+ 0x5a, 0xdd, 0x11, 0x8d, 0x28, 0x9a, 0x5c, 0x84, 0x90, 0x10, 0x7a, 0x0d,
+ 0xbd, 0x74, 0x66, 0x2f, 0x6a, 0x38, 0xa0, 0xe2, 0xd5, 0x54, 0x44, 0xeb,
+ 0x1d, 0x07, 0x9f, 0x07, 0xba, 0x6f, 0xee, 0xe9, 0xfd, 0x4e, 0x0b, 0x29,
+ 0xf5, 0x3e, 0x84, 0xa0, 0x01, 0xf1, 0x9c, 0xab, 0xf8, 0x1c, 0x7e, 0x89,
+ 0xa4, 0xe8, 0xa1, 0xd8, 0x71, 0x65, 0x0d, 0xa3, 0x51, 0x7b, 0xee, 0xbc,
+ 0xd2, 0x22, 0x60, 0x0d, 0xb9, 0x5b, 0x9d, 0xdf, 0xba, 0xfc, 0x51, 0x5b,
+ 0x0b, 0xaf, 0x98, 0xb2, 0xe9, 0x2e, 0xe9, 0x04, 0xe8, 0x62, 0x87, 0xde,
+ 0x2b, 0xc8, 0xd7, 0x4e, 0xc1, 0x4c, 0x64, 0x1e, 0xdd, 0xcf, 0x87, 0x58,
+ 0xba, 0x4a, 0x4f, 0xca, 0x68, 0x07, 0x1d, 0x1c, 0x9d, 0x4a, 0xc6, 0xd5,
+ 0x2f, 0x91, 0xcc, 0x7c, 0x71, 0x72, 0x1c, 0xc5, 0xc0, 0x67, 0xeb, 0x32,
+ 0xfd, 0xc9, 0x92, 0x5c, 0x94, 0xda, 0x85, 0xc0, 0x9b, 0xbf, 0x53, 0x7d,
+ 0x2b, 0x09, 0xf4, 0x8c, 0x9d, 0x91, 0x1f, 0x97, 0x6a, 0x52, 0xcb, 0xde,
+ 0x09, 0x36, 0xa4, 0x77, 0xd8, 0x7b, 0x87, 0x50, 0x44, 0xd5, 0x3e, 0x6e,
+ 0x29, 0x69, 0xfb, 0x39, 0x49, 0x26, 0x1e, 0x09, 0xa5, 0x80, 0x7b, 0x40,
+ 0x2d, 0xeb, 0xe8, 0x27, 0x85, 0xc9, 0xfe, 0x61, 0xfd, 0x7e, 0xe6, 0x7c,
+ 0x97, 0x1d, 0xd5, 0x9d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xc2,
+ 0x30, 0x81, 0xbf, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3b, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74,
+ 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a,
+ 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x40, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xa0, 0x33, 0xa0,
+ 0x31, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x72, 0x65, 0x6d, 0x69,
+ 0x75, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x84, 0xa8, 0x4c,
+ 0xc9, 0x3e, 0x2a, 0xbc, 0x9a, 0xe2, 0xcc, 0x8f, 0x0b, 0xb2, 0x25, 0x77,
+ 0xc4, 0x61, 0x89, 0x89, 0x63, 0x5a, 0xd4, 0xa3, 0x15, 0x40, 0xd4, 0xfb,
+ 0x5e, 0x3f, 0xb4, 0x43, 0xea, 0x63, 0x17, 0x2b, 0x6b, 0x99, 0x74, 0x9e,
+ 0x09, 0xa8, 0xdd, 0xd4, 0x56, 0x15, 0x2e, 0x7a, 0x79, 0x31, 0x5f, 0x63,
+ 0x96, 0x53, 0x1b, 0x34, 0xd9, 0x15, 0xea, 0x4f, 0x6d, 0x70, 0xca, 0xbe,
+ 0xf6, 0x82, 0xa9, 0xed, 0xda, 0x85, 0x77, 0xcc, 0x76, 0x1c, 0x6a, 0x81,
+ 0x0a, 0x21, 0xd8, 0x41, 0x99, 0x7f, 0x5e, 0x2e, 0x82, 0xc1, 0xe8, 0xaa,
+ 0xf7, 0x93, 0x81, 0x05, 0xaa, 0x92, 0xb4, 0x1f, 0xb7, 0x9a, 0xc0, 0x07,
+ 0x17, 0xf5, 0xcb, 0xc6, 0xb4, 0x4c, 0x0e, 0xd7, 0x56, 0xdc, 0x71, 0x20,
+ 0x74, 0x38, 0xd6, 0x74, 0xc6, 0xd6, 0x8f, 0x6b, 0xaf, 0x8b, 0x8d, 0xa0,
+ 0x6c, 0x29, 0x0b, 0x61, 0xe0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 06:7f:94:57:85:87:e8:ac:77:de:b2:53:32:5b:bc:99:8b:56:0d
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Amazon, CN=Amazon Root CA 1
+ Validity
+ Not Before: Oct 22 00:00:00 2015 GMT
+ Not After : Oct 19 00:00:00 2025 GMT
+ Subject: C=US, O=Amazon, OU=Server CA 1B, CN=Amazon
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c2:4e:16:67:dd:ce:bc:6a:c8:37:5a:ec:3a:30:
+ b0:1d:e6:d1:12:e8:12:28:48:cc:e8:29:c1:b9:6e:
+ 53:d5:a3:eb:03:39:1a:cc:77:87:f6:01:b9:d9:70:
+ cc:cf:6b:8d:e3:e3:03:71:86:99:6d:cb:a6:94:2a:
+ 4e:13:d6:a7:bd:04:ec:0a:16:3c:0a:eb:39:b1:c4:
+ b5:58:a3:b6:c7:56:25:ec:3e:52:7a:a8:e3:29:16:
+ 07:b9:6e:50:cf:fb:5f:31:f8:1d:ba:03:4a:62:89:
+ 03:ae:3e:47:f2:0f:27:91:e3:14:20:85:f8:fa:e9:
+ 8a:35:f5:5f:9e:99:4d:e7:6b:37:ef:a4:50:3e:44:
+ ec:fa:5a:85:66:07:9c:7e:17:6a:55:f3:17:8a:35:
+ 1e:ee:e9:ac:c3:75:4e:58:55:7d:53:6b:0a:6b:9b:
+ 14:42:d7:e5:ac:01:89:b3:ea:a3:fe:cf:c0:2b:0c:
+ 84:c2:d8:53:15:cb:67:f0:d0:88:ca:3a:d1:17:73:
+ f5:5f:9a:d4:c5:72:1e:7e:01:f1:98:30:63:2a:aa:
+ f2:7a:2d:c5:e2:02:1a:86:e5:32:3e:0e:bd:11:b4:
+ cf:3c:93:ef:17:50:10:9e:43:c2:06:2a:e0:0d:68:
+ be:d3:88:8b:4a:65:8c:4a:d4:c3:2e:4c:9b:55:f4:
+ 86:e5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 59:A4:66:06:52:A0:7B:95:92:3C:A3:94:07:27:96:74:5B:F9:3D:D0
+ X509v3 Authority Key Identifier:
+ keyid:84:18:CC:85:34:EC:BC:0C:94:94:2E:08:59:9C:C7:B2:10:4E:0A:08
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.rootca1.amazontrust.com
+ CA Issuers - URI:http://crt.rootca1.amazontrust.com/rootca1.cer
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.rootca1.amazontrust.com/rootca1.crl
+
+ X509v3 Certificate Policies:
+ Policy: 2.23.140.1.2.1
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 85:92:be:35:bb:79:cf:a3:81:42:1c:e4:e3:63:73:53:39:52:
+ 35:e7:d1:ad:fd:ae:99:8a:ac:89:12:2f:bb:e7:6f:9a:d5:4e:
+ 72:ea:20:30:61:f9:97:b2:cd:a5:27:02:45:a8:ca:76:3e:98:
+ 4a:83:9e:b6:e6:45:e0:f2:43:f6:08:de:6d:e8:6e:db:31:07:
+ 13:f0:2f:31:0d:93:6d:61:37:7b:58:f0:fc:51:98:91:28:02:
+ 4f:05:76:b7:d3:f0:1b:c2:e6:5e:d0:66:85:11:0f:2e:81:c6:
+ 10:81:29:fe:20:60:48:f3:f2:f0:84:13:53:65:35:15:11:6b:
+ 82:51:40:55:57:5f:18:b5:b0:22:3e:ad:f2:5e:a3:01:e3:c3:
+ b3:f9:cb:41:5a:e6:52:91:bb:e4:36:87:4f:2d:a9:a4:07:68:
+ 35:ba:94:72:cd:0e:ea:0e:7d:57:f2:79:fc:37:c5:7b:60:9e:
+ b2:eb:c0:2d:90:77:0d:49:10:27:a5:38:ad:c4:12:a3:b4:a3:
+ c8:48:b3:15:0b:1e:e2:e2:19:dc:c4:76:52:c8:bc:8a:41:78:
+ 70:d9:6d:97:b3:4a:8b:78:2d:5e:b4:0f:a3:4c:60:ca:e1:47:
+ cb:78:2d:12:17:b1:52:8b:ca:39:2c:bd:b5:2f:c2:33:02:96:
+ ab:da:94:7f
+-----BEGIN CERTIFICATE-----
+MIIESTCCAzGgAwIBAgITBn+UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF
+ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
+b24gUm9vdCBDQSAxMB4XDTE1MTAyMjAwMDAwMFoXDTI1MTAxOTAwMDAwMFowRjEL
+MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEVMBMGA1UECxMMU2VydmVyIENB
+IDFCMQ8wDQYDVQQDEwZBbWF6b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDCThZn3c68asg3Wuw6MLAd5tES6BIoSMzoKcG5blPVo+sDORrMd4f2AbnZ
+cMzPa43j4wNxhplty6aUKk4T1qe9BOwKFjwK6zmxxLVYo7bHViXsPlJ6qOMpFge5
+blDP+18x+B26A0piiQOuPkfyDyeR4xQghfj66Yo19V+emU3nazfvpFA+ROz6WoVm
+B5x+F2pV8xeKNR7u6azDdU5YVX1TawprmxRC1+WsAYmz6qP+z8ArDITC2FMVy2fw
+0IjKOtEXc/VfmtTFch5+AfGYMGMqqvJ6LcXiAhqG5TI+Dr0RtM88k+8XUBCeQ8IG
+KuANaL7TiItKZYxK1MMuTJtV9IblAgMBAAGjggE7MIIBNzASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUWaRmBlKge5WSPKOUByeW
+dFv5PdAwHwYDVR0jBBgwFoAUhBjMhTTsvAyUlC4IWZzHshBOCggwewYIKwYBBQUH
+AQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8vb2NzcC5yb290Y2ExLmFtYXpvbnRy
+dXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDovL2NydC5yb290Y2ExLmFtYXpvbnRy
+dXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3Js
+LnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jvb3RjYTEuY3JsMBMGA1UdIAQMMAow
+CAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IBAQCFkr41u3nPo4FCHOTjY3NTOVI1
+59Gt/a6ZiqyJEi+752+a1U5y6iAwYfmXss2lJwJFqMp2PphKg5625kXg8kP2CN5t
+6G7bMQcT8C8xDZNtYTd7WPD8UZiRKAJPBXa30/AbwuZe0GaFEQ8ugcYQgSn+IGBI
+8/LwhBNTZTUVEWuCUUBVV18YtbAiPq3yXqMB48Oz+ctBWuZSkbvkNodPLamkB2g1
+upRyzQ7qDn1X8nn8N8V7YJ6y68AtkHcNSRAnpTitxBKjtKPISLMVCx7i4hncxHZS
+yLyKQXhw2W2Xs0qLeC1etA+jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert7[] = {
+ 0x30, 0x82, 0x04, 0x49, 0x30, 0x82, 0x03, 0x31, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x13, 0x06, 0x7f, 0x94, 0x57, 0x85, 0x87, 0xe8, 0xac, 0x77,
+ 0xde, 0xb2, 0x53, 0x32, 0x5b, 0xbc, 0x99, 0x8b, 0x56, 0x0d, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x30, 0x39, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x06, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6d, 0x61, 0x7a,
+ 0x6f, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x31, 0x30, 0x32, 0x32, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x31, 0x30, 0x31,
+ 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x46, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x06, 0x41, 0x6d,
+ 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41,
+ 0x20, 0x31, 0x42, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x06, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xc2, 0x4e, 0x16, 0x67, 0xdd, 0xce, 0xbc,
+ 0x6a, 0xc8, 0x37, 0x5a, 0xec, 0x3a, 0x30, 0xb0, 0x1d, 0xe6, 0xd1, 0x12,
+ 0xe8, 0x12, 0x28, 0x48, 0xcc, 0xe8, 0x29, 0xc1, 0xb9, 0x6e, 0x53, 0xd5,
+ 0xa3, 0xeb, 0x03, 0x39, 0x1a, 0xcc, 0x77, 0x87, 0xf6, 0x01, 0xb9, 0xd9,
+ 0x70, 0xcc, 0xcf, 0x6b, 0x8d, 0xe3, 0xe3, 0x03, 0x71, 0x86, 0x99, 0x6d,
+ 0xcb, 0xa6, 0x94, 0x2a, 0x4e, 0x13, 0xd6, 0xa7, 0xbd, 0x04, 0xec, 0x0a,
+ 0x16, 0x3c, 0x0a, 0xeb, 0x39, 0xb1, 0xc4, 0xb5, 0x58, 0xa3, 0xb6, 0xc7,
+ 0x56, 0x25, 0xec, 0x3e, 0x52, 0x7a, 0xa8, 0xe3, 0x29, 0x16, 0x07, 0xb9,
+ 0x6e, 0x50, 0xcf, 0xfb, 0x5f, 0x31, 0xf8, 0x1d, 0xba, 0x03, 0x4a, 0x62,
+ 0x89, 0x03, 0xae, 0x3e, 0x47, 0xf2, 0x0f, 0x27, 0x91, 0xe3, 0x14, 0x20,
+ 0x85, 0xf8, 0xfa, 0xe9, 0x8a, 0x35, 0xf5, 0x5f, 0x9e, 0x99, 0x4d, 0xe7,
+ 0x6b, 0x37, 0xef, 0xa4, 0x50, 0x3e, 0x44, 0xec, 0xfa, 0x5a, 0x85, 0x66,
+ 0x07, 0x9c, 0x7e, 0x17, 0x6a, 0x55, 0xf3, 0x17, 0x8a, 0x35, 0x1e, 0xee,
+ 0xe9, 0xac, 0xc3, 0x75, 0x4e, 0x58, 0x55, 0x7d, 0x53, 0x6b, 0x0a, 0x6b,
+ 0x9b, 0x14, 0x42, 0xd7, 0xe5, 0xac, 0x01, 0x89, 0xb3, 0xea, 0xa3, 0xfe,
+ 0xcf, 0xc0, 0x2b, 0x0c, 0x84, 0xc2, 0xd8, 0x53, 0x15, 0xcb, 0x67, 0xf0,
+ 0xd0, 0x88, 0xca, 0x3a, 0xd1, 0x17, 0x73, 0xf5, 0x5f, 0x9a, 0xd4, 0xc5,
+ 0x72, 0x1e, 0x7e, 0x01, 0xf1, 0x98, 0x30, 0x63, 0x2a, 0xaa, 0xf2, 0x7a,
+ 0x2d, 0xc5, 0xe2, 0x02, 0x1a, 0x86, 0xe5, 0x32, 0x3e, 0x0e, 0xbd, 0x11,
+ 0xb4, 0xcf, 0x3c, 0x93, 0xef, 0x17, 0x50, 0x10, 0x9e, 0x43, 0xc2, 0x06,
+ 0x2a, 0xe0, 0x0d, 0x68, 0xbe, 0xd3, 0x88, 0x8b, 0x4a, 0x65, 0x8c, 0x4a,
+ 0xd4, 0xc3, 0x2e, 0x4c, 0x9b, 0x55, 0xf4, 0x86, 0xe5, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x3b, 0x30, 0x82, 0x01, 0x37, 0x30, 0x12,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
+ 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x59, 0xa4, 0x66,
+ 0x06, 0x52, 0xa0, 0x7b, 0x95, 0x92, 0x3c, 0xa3, 0x94, 0x07, 0x27, 0x96,
+ 0x74, 0x5b, 0xf9, 0x3d, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x84, 0x18, 0xcc, 0x85, 0x34, 0xec,
+ 0xbc, 0x0c, 0x94, 0x94, 0x2e, 0x08, 0x59, 0x9c, 0xc7, 0xb2, 0x10, 0x4e,
+ 0x0a, 0x08, 0x30, 0x7b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x6f, 0x30, 0x6d, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x74,
+ 0x63, 0x61, 0x31, 0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x3a, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2e, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x72, 0x6f, 0x6f, 0x74,
+ 0x63, 0x61, 0x31, 0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74,
+ 0x63, 0x61, 0x31, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x3f, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x38, 0x30, 0x36, 0x30, 0x34, 0xa0, 0x32, 0xa0, 0x30,
+ 0x86, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61, 0x31, 0x2e, 0x61, 0x6d, 0x61,
+ 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61, 0x31, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0c, 0x30, 0x0a, 0x30,
+ 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x01, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x85, 0x92, 0xbe, 0x35, 0xbb, 0x79, 0xcf,
+ 0xa3, 0x81, 0x42, 0x1c, 0xe4, 0xe3, 0x63, 0x73, 0x53, 0x39, 0x52, 0x35,
+ 0xe7, 0xd1, 0xad, 0xfd, 0xae, 0x99, 0x8a, 0xac, 0x89, 0x12, 0x2f, 0xbb,
+ 0xe7, 0x6f, 0x9a, 0xd5, 0x4e, 0x72, 0xea, 0x20, 0x30, 0x61, 0xf9, 0x97,
+ 0xb2, 0xcd, 0xa5, 0x27, 0x02, 0x45, 0xa8, 0xca, 0x76, 0x3e, 0x98, 0x4a,
+ 0x83, 0x9e, 0xb6, 0xe6, 0x45, 0xe0, 0xf2, 0x43, 0xf6, 0x08, 0xde, 0x6d,
+ 0xe8, 0x6e, 0xdb, 0x31, 0x07, 0x13, 0xf0, 0x2f, 0x31, 0x0d, 0x93, 0x6d,
+ 0x61, 0x37, 0x7b, 0x58, 0xf0, 0xfc, 0x51, 0x98, 0x91, 0x28, 0x02, 0x4f,
+ 0x05, 0x76, 0xb7, 0xd3, 0xf0, 0x1b, 0xc2, 0xe6, 0x5e, 0xd0, 0x66, 0x85,
+ 0x11, 0x0f, 0x2e, 0x81, 0xc6, 0x10, 0x81, 0x29, 0xfe, 0x20, 0x60, 0x48,
+ 0xf3, 0xf2, 0xf0, 0x84, 0x13, 0x53, 0x65, 0x35, 0x15, 0x11, 0x6b, 0x82,
+ 0x51, 0x40, 0x55, 0x57, 0x5f, 0x18, 0xb5, 0xb0, 0x22, 0x3e, 0xad, 0xf2,
+ 0x5e, 0xa3, 0x01, 0xe3, 0xc3, 0xb3, 0xf9, 0xcb, 0x41, 0x5a, 0xe6, 0x52,
+ 0x91, 0xbb, 0xe4, 0x36, 0x87, 0x4f, 0x2d, 0xa9, 0xa4, 0x07, 0x68, 0x35,
+ 0xba, 0x94, 0x72, 0xcd, 0x0e, 0xea, 0x0e, 0x7d, 0x57, 0xf2, 0x79, 0xfc,
+ 0x37, 0xc5, 0x7b, 0x60, 0x9e, 0xb2, 0xeb, 0xc0, 0x2d, 0x90, 0x77, 0x0d,
+ 0x49, 0x10, 0x27, 0xa5, 0x38, 0xad, 0xc4, 0x12, 0xa3, 0xb4, 0xa3, 0xc8,
+ 0x48, 0xb3, 0x15, 0x0b, 0x1e, 0xe2, 0xe2, 0x19, 0xdc, 0xc4, 0x76, 0x52,
+ 0xc8, 0xbc, 0x8a, 0x41, 0x78, 0x70, 0xd9, 0x6d, 0x97, 0xb3, 0x4a, 0x8b,
+ 0x78, 0x2d, 0x5e, 0xb4, 0x0f, 0xa3, 0x4c, 0x60, 0xca, 0xe1, 0x47, 0xcb,
+ 0x78, 0x2d, 0x12, 0x17, 0xb1, 0x52, 0x8b, 0xca, 0x39, 0x2c, 0xbd, 0xb5,
+ 0x2f, 0xc2, 0x33, 0x02, 0x96, 0xab, 0xda, 0x94, 0x7f,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146033 (0x23a71)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Dec 11 23:45:51 2013 GMT
+ Not After : May 20 23:45:51 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bb:58:c1:12:01:2e:97:d8:7d:18:aa:c8:c2:e5:
+ 85:e2:17:6c:60:2e:c9:8d:31:05:39:1a:06:98:56:
+ dd:54:d7:11:8c:59:5b:3d:b1:54:ae:4b:21:85:32:
+ 16:5f:54:86:e6:d9:b1:d8:60:89:6b:58:be:72:da:
+ a0:00:42:76:b1:27:59:4c:cd:e3:ba:d4:5c:d9:a6:
+ 7f:bb:2b:75:d5:46:44:bd:ec:40:5c:59:b7:dd:59:
+ 9f:f1:6a:f7:06:fc:d6:2f:19:8a:95:12:ba:9a:ca:
+ d5:30:d2:38:fc:19:3b:5b:15:3b:36:d0:43:4d:d1:
+ 65:a1:d4:8b:c1:60:41:b3:d6:70:17:cc:39:c0:9c:
+ 0c:a0:3d:b7:11:22:4e:ce:d9:a9:7a:d2:2a:62:9c:
+ a0:0b:4e:2a:d7:c3:61:5a:85:dd:5c:10:b9:54:3d:
+ 2d:03:f8:49:f0:bc:92:b7:b7:9c:31:c7:e9:b8:aa:
+ 82:0b:05:b9:31:cd:08:5b:bb:22:0b:f6:9c:8e:8a:
+ 55:1c:76:43:76:f0:e2:6e:f0:df:a8:29:75:e7:c8:
+ a4:87:8b:6a:f1:bb:08:c9:36:18:65:ee:50:43:b8:
+ 5d:72:d5:28:39:e1:53:3e:25:2c:da:2b:4f:dd:8a:
+ 9e:50:50:e0:6f:9a:c4:d5:19:26:89:01:75:73:09:
+ 9b:3b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ 97:C2:27:50:9E:C2:C9:EC:0C:88:32:C8:7C:AD:E2:A6:01:4F:DA:6F
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g1.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-569
+ Signature Algorithm: sha256WithRSAEncryption
+ 35:eb:e1:8b:20:56:94:ba:7a:bd:79:a9:f6:e3:fe:6e:38:b4:
+ 32:c1:a3:db:58:56:20:3e:7d:c7:3a:b1:67:69:d5:79:14:1b:
+ f6:fa:ec:60:f2:79:cd:0a:0c:60:8a:74:4c:a3:93:2a:a0:f0:
+ 51:7f:cd:e9:f9:92:fd:96:ab:45:f5:62:3d:3f:60:46:50:13:
+ 3d:20:13:18:2e:94:46:ae:d5:21:fe:43:a1:c9:23:fe:53:c4:
+ bf:1a:d8:ac:3a:ca:de:66:97:23:ae:d3:df:4a:4d:73:1f:6f:
+ 31:a2:51:04:16:6a:00:eb:f9:8d:43:81:f0:50:a1:1f:a6:ca:
+ 3a:f3:28:3c:5f:51:ac:d7:0a:45:77:4b:0e:52:62:1b:d8:38:
+ 51:a0:92:2d:3f:90:6e:c8:7e:40:9f:20:46:15:5d:e0:50:7c:
+ e1:76:af:5e:ed:11:d3:2f:13:b9:b8:25:a4:af:58:09:af:35:
+ b4:62:54:85:e3:48:de:bc:d2:90:7a:7a:a4:84:0d:a3:42:f2:
+ 51:c0:d4:ad:53:65:5d:6c:f8:3f:1f:06:f2:4f:cb:97:a0:4a:
+ 59:c6:78:d1:e8:03:b9:85:6d:2c:ba:e1:5f:b6:ad:2b:3e:25:
+ 79:c5:8b:56:d5:e3:09:80:ea:c1:27:c2:d9:0e:ec:47:0a:e9:
+ d0:ca:fc:d8
+-----BEGIN CERTIFICATE-----
+MIIETTCCAzWgAwIBAgIDAjpxMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTMxMjExMjM0NTUxWhcNMjIwNTIwMjM0NTUxWjBCMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSUmFwaWRTU0wg
+U0hBMjU2IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1jBEgEu
+l9h9GKrIwuWF4hdsYC7JjTEFORoGmFbdVNcRjFlbPbFUrkshhTIWX1SG5tmx2GCJ
+a1i+ctqgAEJ2sSdZTM3jutRc2aZ/uyt11UZEvexAXFm33Vmf8Wr3BvzWLxmKlRK6
+msrVMNI4/Bk7WxU7NtBDTdFlodSLwWBBs9ZwF8w5wJwMoD23ESJOztmpetIqYpyg
+C04q18NhWoXdXBC5VD0tA/hJ8LySt7ecMcfpuKqCCwW5Mc0IW7siC/acjopVHHZD
+dvDibvDfqCl158ikh4tq8bsIyTYYZe5QQ7hdctUoOeFTPiUs2itP3YqeUFDgb5rE
+1RkmiQF1cwmbOwIDAQABo4IBSjCCAUYwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwR
+fap9ZbjKzE4wHQYDVR0OBBYEFJfCJ1CewsnsDIgyyHyt4qYBT9pvMBIGA1UdEwEB
+/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMDYGA1UdHwQvMC0wK6ApoCeGJWh0
+dHA6Ly9nMS5zeW1jYi5jb20vY3Jscy9ndGdsb2JhbC5jcmwwLwYIKwYBBQUHAQEE
+IzAhMB8GCCsGAQUFBzABhhNodHRwOi8vZzIuc3ltY2IuY29tMEwGA1UdIARFMEMw
+QQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3RydXN0
+LmNvbS9yZXNvdXJjZXMvY3BzMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1h
+bnRlY1BLSS0xLTU2OTANBgkqhkiG9w0BAQsFAAOCAQEANevhiyBWlLp6vXmp9uP+
+bji0MsGj21hWID59xzqxZ2nVeRQb9vrsYPJ5zQoMYIp0TKOTKqDwUX/N6fmS/Zar
+RfViPT9gRlATPSATGC6URq7VIf5Dockj/lPEvxrYrDrK3maXI67T30pNcx9vMaJR
+BBZqAOv5jUOB8FChH6bKOvMoPF9RrNcKRXdLDlJiG9g4UaCSLT+Qbsh+QJ8gRhVd
+4FB84XavXu0R0y8TubglpK9YCa81tGJUheNI3rzSkHp6pIQNo0LyUcDUrVNlXWz4
+Px8G8k/Ll6BKWcZ40egDuYVtLLrhX7atKz4lecWLVtXjCYDqwSfC2Q7sRwrp0Mr8
+2A==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert8[] = {
+ 0x30, 0x82, 0x04, 0x4d, 0x30, 0x82, 0x03, 0x35, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31,
+ 0x32, 0x31, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x31, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x33, 0x34, 0x35, 0x35, 0x31,
+ 0x5a, 0x30, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x12, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20,
+ 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbb, 0x58, 0xc1, 0x12, 0x01, 0x2e,
+ 0x97, 0xd8, 0x7d, 0x18, 0xaa, 0xc8, 0xc2, 0xe5, 0x85, 0xe2, 0x17, 0x6c,
+ 0x60, 0x2e, 0xc9, 0x8d, 0x31, 0x05, 0x39, 0x1a, 0x06, 0x98, 0x56, 0xdd,
+ 0x54, 0xd7, 0x11, 0x8c, 0x59, 0x5b, 0x3d, 0xb1, 0x54, 0xae, 0x4b, 0x21,
+ 0x85, 0x32, 0x16, 0x5f, 0x54, 0x86, 0xe6, 0xd9, 0xb1, 0xd8, 0x60, 0x89,
+ 0x6b, 0x58, 0xbe, 0x72, 0xda, 0xa0, 0x00, 0x42, 0x76, 0xb1, 0x27, 0x59,
+ 0x4c, 0xcd, 0xe3, 0xba, 0xd4, 0x5c, 0xd9, 0xa6, 0x7f, 0xbb, 0x2b, 0x75,
+ 0xd5, 0x46, 0x44, 0xbd, 0xec, 0x40, 0x5c, 0x59, 0xb7, 0xdd, 0x59, 0x9f,
+ 0xf1, 0x6a, 0xf7, 0x06, 0xfc, 0xd6, 0x2f, 0x19, 0x8a, 0x95, 0x12, 0xba,
+ 0x9a, 0xca, 0xd5, 0x30, 0xd2, 0x38, 0xfc, 0x19, 0x3b, 0x5b, 0x15, 0x3b,
+ 0x36, 0xd0, 0x43, 0x4d, 0xd1, 0x65, 0xa1, 0xd4, 0x8b, 0xc1, 0x60, 0x41,
+ 0xb3, 0xd6, 0x70, 0x17, 0xcc, 0x39, 0xc0, 0x9c, 0x0c, 0xa0, 0x3d, 0xb7,
+ 0x11, 0x22, 0x4e, 0xce, 0xd9, 0xa9, 0x7a, 0xd2, 0x2a, 0x62, 0x9c, 0xa0,
+ 0x0b, 0x4e, 0x2a, 0xd7, 0xc3, 0x61, 0x5a, 0x85, 0xdd, 0x5c, 0x10, 0xb9,
+ 0x54, 0x3d, 0x2d, 0x03, 0xf8, 0x49, 0xf0, 0xbc, 0x92, 0xb7, 0xb7, 0x9c,
+ 0x31, 0xc7, 0xe9, 0xb8, 0xaa, 0x82, 0x0b, 0x05, 0xb9, 0x31, 0xcd, 0x08,
+ 0x5b, 0xbb, 0x22, 0x0b, 0xf6, 0x9c, 0x8e, 0x8a, 0x55, 0x1c, 0x76, 0x43,
+ 0x76, 0xf0, 0xe2, 0x6e, 0xf0, 0xdf, 0xa8, 0x29, 0x75, 0xe7, 0xc8, 0xa4,
+ 0x87, 0x8b, 0x6a, 0xf1, 0xbb, 0x08, 0xc9, 0x36, 0x18, 0x65, 0xee, 0x50,
+ 0x43, 0xb8, 0x5d, 0x72, 0xd5, 0x28, 0x39, 0xe1, 0x53, 0x3e, 0x25, 0x2c,
+ 0xda, 0x2b, 0x4f, 0xdd, 0x8a, 0x9e, 0x50, 0x50, 0xe0, 0x6f, 0x9a, 0xc4,
+ 0xd5, 0x19, 0x26, 0x89, 0x01, 0x75, 0x73, 0x09, 0x9b, 0x3b, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x4a, 0x30, 0x82, 0x01, 0x46, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11,
+ 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x97, 0xc2, 0x27, 0x50, 0x9e,
+ 0xc2, 0xc9, 0xec, 0x0c, 0x88, 0x32, 0xc8, 0x7c, 0xad, 0xe2, 0xa6, 0x01,
+ 0x4f, 0xda, 0x6f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2f,
+ 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63,
+ 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67,
+ 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30,
+ 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07,
+ 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d,
+ 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30,
+ 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61,
+ 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35, 0x36,
+ 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x35, 0xeb, 0xe1,
+ 0x8b, 0x20, 0x56, 0x94, 0xba, 0x7a, 0xbd, 0x79, 0xa9, 0xf6, 0xe3, 0xfe,
+ 0x6e, 0x38, 0xb4, 0x32, 0xc1, 0xa3, 0xdb, 0x58, 0x56, 0x20, 0x3e, 0x7d,
+ 0xc7, 0x3a, 0xb1, 0x67, 0x69, 0xd5, 0x79, 0x14, 0x1b, 0xf6, 0xfa, 0xec,
+ 0x60, 0xf2, 0x79, 0xcd, 0x0a, 0x0c, 0x60, 0x8a, 0x74, 0x4c, 0xa3, 0x93,
+ 0x2a, 0xa0, 0xf0, 0x51, 0x7f, 0xcd, 0xe9, 0xf9, 0x92, 0xfd, 0x96, 0xab,
+ 0x45, 0xf5, 0x62, 0x3d, 0x3f, 0x60, 0x46, 0x50, 0x13, 0x3d, 0x20, 0x13,
+ 0x18, 0x2e, 0x94, 0x46, 0xae, 0xd5, 0x21, 0xfe, 0x43, 0xa1, 0xc9, 0x23,
+ 0xfe, 0x53, 0xc4, 0xbf, 0x1a, 0xd8, 0xac, 0x3a, 0xca, 0xde, 0x66, 0x97,
+ 0x23, 0xae, 0xd3, 0xdf, 0x4a, 0x4d, 0x73, 0x1f, 0x6f, 0x31, 0xa2, 0x51,
+ 0x04, 0x16, 0x6a, 0x00, 0xeb, 0xf9, 0x8d, 0x43, 0x81, 0xf0, 0x50, 0xa1,
+ 0x1f, 0xa6, 0xca, 0x3a, 0xf3, 0x28, 0x3c, 0x5f, 0x51, 0xac, 0xd7, 0x0a,
+ 0x45, 0x77, 0x4b, 0x0e, 0x52, 0x62, 0x1b, 0xd8, 0x38, 0x51, 0xa0, 0x92,
+ 0x2d, 0x3f, 0x90, 0x6e, 0xc8, 0x7e, 0x40, 0x9f, 0x20, 0x46, 0x15, 0x5d,
+ 0xe0, 0x50, 0x7c, 0xe1, 0x76, 0xaf, 0x5e, 0xed, 0x11, 0xd3, 0x2f, 0x13,
+ 0xb9, 0xb8, 0x25, 0xa4, 0xaf, 0x58, 0x09, 0xaf, 0x35, 0xb4, 0x62, 0x54,
+ 0x85, 0xe3, 0x48, 0xde, 0xbc, 0xd2, 0x90, 0x7a, 0x7a, 0xa4, 0x84, 0x0d,
+ 0xa3, 0x42, 0xf2, 0x51, 0xc0, 0xd4, 0xad, 0x53, 0x65, 0x5d, 0x6c, 0xf8,
+ 0x3f, 0x1f, 0x06, 0xf2, 0x4f, 0xcb, 0x97, 0xa0, 0x4a, 0x59, 0xc6, 0x78,
+ 0xd1, 0xe8, 0x03, 0xb9, 0x85, 0x6d, 0x2c, 0xba, 0xe1, 0x5f, 0xb6, 0xad,
+ 0x2b, 0x3e, 0x25, 0x79, 0xc5, 0x8b, 0x56, 0xd5, 0xe3, 0x09, 0x80, 0xea,
+ 0xc1, 0x27, 0xc2, 0xd9, 0x0e, 0xec, 0x47, 0x0a, 0xe9, 0xd0, 0xca, 0xfc,
+ 0xd8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:44:4e:f0:36:31
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Feb 20 10:00:00 2014 GMT
+ Not After : Feb 20 10:00:00 2024 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=AlphaSSL CA - SHA256 - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:da:01:ec:e4:ec:73:60:fb:7e:8f:6a:b7:c6:17:
+ e3:92:64:32:d4:ac:00:d9:a2:0f:b9:ed:ee:6b:8a:
+ 86:ca:92:67:d9:74:d7:5d:47:02:3c:8f:40:d6:9e:
+ 6d:14:cd:c3:da:29:39:a7:0f:05:0a:68:a2:66:1a:
+ 1e:c4:b2:8b:76:58:e5:ab:5d:1d:8f:40:b3:39:8b:
+ ef:1e:83:7d:22:d0:e3:a9:00:2e:ec:53:cf:62:19:
+ 85:44:28:4c:c0:27:cb:7b:0e:ec:10:64:00:10:a4:
+ 05:cc:a0:72:be:41:6c:31:5b:48:e4:b1:ec:b9:23:
+ eb:55:4d:d0:7d:62:4a:a5:b4:a5:a4:59:85:c5:25:
+ 91:a6:fe:a6:09:9f:06:10:6d:8f:81:0c:64:40:5e:
+ 73:00:9a:e0:2e:65:98:54:10:00:70:98:c8:e1:ed:
+ 34:5f:d8:9c:c7:0d:c0:d6:23:59:45:fc:fe:55:7a:
+ 86:ee:94:60:22:f1:ae:d1:e6:55:46:f6:99:c5:1b:
+ 08:74:5f:ac:b0:64:84:8f:89:38:1c:a1:a7:90:21:
+ 4f:02:6e:bd:e0:61:67:d4:f8:42:87:0f:0a:f7:c9:
+ 04:6d:2a:a9:2f:ef:42:a5:df:dd:a3:53:db:98:1e:
+ 81:f9:9a:72:7b:5a:de:4f:3e:7f:a2:58:a0:e2:17:
+ ad:67
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ F5:CD:D5:3C:08:50:F9:6A:4F:3A:B7:97:DA:56:83:E6:69:D2:68:F7
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.alphassl.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 60:40:68:16:47:e7:16:8d:db:5c:a1:56:2a:cb:f4:5c:9b:b0:
+ 1e:a2:4b:f5:cb:02:3f:f8:0b:a1:f2:a7:42:d4:b7:4c:eb:e3:
+ 66:80:f3:25:43:78:2e:1b:17:56:07:52:18:cb:d1:a8:ec:e6:
+ fb:73:3e:a4:62:8c:80:b4:d2:c5:12:73:a3:d3:fa:02:38:be:
+ 63:3d:84:b8:99:c1:f1:ba:f7:9f:c3:40:d1:58:18:53:c1:62:
+ dd:af:18:42:7f:34:4e:c5:43:d5:71:b0:30:00:c7:e3:90:ae:
+ 3f:57:86:97:ce:ea:0c:12:8e:22:70:e3:66:a7:54:7f:2e:28:
+ cb:d4:54:d0:b3:1e:62:67:08:f9:27:e1:cb:e3:66:b8:24:1b:
+ 89:6a:89:44:65:f2:d9:4c:d2:58:1c:8c:4e:c0:95:a1:d4:ef:
+ 67:2f:38:20:e8:2e:ff:96:51:f0:ba:d8:3d:92:70:47:65:1c:
+ 9e:73:72:b4:60:0c:5c:e2:d1:73:76:e0:af:4e:e2:e5:37:a5:
+ 45:2f:8a:23:3e:87:c7:30:e6:31:38:7c:f4:dd:52:ca:f3:53:
+ 04:25:57:56:66:94:e8:0b:ee:e6:03:14:4e:ee:fd:6d:94:64:
+ 9e:5e:ce:79:d4:b2:a6:cf:40:b1:44:a8:3e:87:19:5e:e9:f8:
+ 21:16:59:53
+-----BEGIN CERTIFICATE-----
+MIIETTCCAzWgAwIBAgILBAAAAAABRE7wNjEwDQYJKoZIhvcNAQELBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw
+MDBaFw0yNDAyMjAxMDAwMDBaMEwxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMSIwIAYDVQQDExlBbHBoYVNTTCBDQSAtIFNIQTI1NiAtIEcy
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2gHs5OxzYPt+j2q3xhfj
+kmQy1KwA2aIPue3ua4qGypJn2XTXXUcCPI9A1p5tFM3D2ik5pw8FCmiiZhoexLKL
+dljlq10dj0CzOYvvHoN9ItDjqQAu7FPPYhmFRChMwCfLew7sEGQAEKQFzKByvkFs
+MVtI5LHsuSPrVU3QfWJKpbSlpFmFxSWRpv6mCZ8GEG2PgQxkQF5zAJrgLmWYVBAA
+cJjI4e00X9icxw3A1iNZRfz+VXqG7pRgIvGu0eZVRvaZxRsIdF+ssGSEj4k4HKGn
+kCFPAm694GFn1PhChw8K98kEbSqpL+9Cpd/do1PbmB6B+Zpye1reTz5/olig4het
+ZwIDAQABo4IBIzCCAR8wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwHQYDVR0OBBYEFPXN1TwIUPlqTzq3l9pWg+Zp0mj3MEUGA1UdIAQ+MDwwOgYE
+VR0gADAyMDAGCCsGAQUFBwIBFiRodHRwczovL3d3dy5hbHBoYXNzbC5jb20vcmVw
+b3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWdu
+Lm5ldC9yb290LmNybDA9BggrBgEFBQcBAQQxMC8wLQYIKwYBBQUHMAGGIWh0dHA6
+Ly9vY3NwLmdsb2JhbHNpZ24uY29tL3Jvb3RyMTAfBgNVHSMEGDAWgBRge2YaRQ2X
+yolQL30EzTSo//z9SzANBgkqhkiG9w0BAQsFAAOCAQEAYEBoFkfnFo3bXKFWKsv0
+XJuwHqJL9csCP/gLofKnQtS3TOvjZoDzJUN4LhsXVgdSGMvRqOzm+3M+pGKMgLTS
+xRJzo9P6Aji+Yz2EuJnB8br3n8NA0VgYU8Fi3a8YQn80TsVD1XGwMADH45CuP1eG
+l87qDBKOInDjZqdUfy4oy9RU0LMeYmcI+Sfhy+NmuCQbiWqJRGXy2UzSWByMTsCV
+odTvZy84IOgu/5ZR8LrYPZJwR2UcnnNytGAMXOLRc3bgr07i5TelRS+KIz6HxzDm
+MTh89N1SyvNTBCVXVmaU6Avu5gMUTu79bZRknl7OedSyps9AsUSoPocZXun4IRZZ
+Uw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert9[] = {
+ 0x30, 0x82, 0x04, 0x4d, 0x30, 0x82, 0x03, 0x35, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0,
+ 0x36, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4c, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x41,
+ 0x6c, 0x70, 0x68, 0x61, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d,
+ 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0x01, 0xec,
+ 0xe4, 0xec, 0x73, 0x60, 0xfb, 0x7e, 0x8f, 0x6a, 0xb7, 0xc6, 0x17, 0xe3,
+ 0x92, 0x64, 0x32, 0xd4, 0xac, 0x00, 0xd9, 0xa2, 0x0f, 0xb9, 0xed, 0xee,
+ 0x6b, 0x8a, 0x86, 0xca, 0x92, 0x67, 0xd9, 0x74, 0xd7, 0x5d, 0x47, 0x02,
+ 0x3c, 0x8f, 0x40, 0xd6, 0x9e, 0x6d, 0x14, 0xcd, 0xc3, 0xda, 0x29, 0x39,
+ 0xa7, 0x0f, 0x05, 0x0a, 0x68, 0xa2, 0x66, 0x1a, 0x1e, 0xc4, 0xb2, 0x8b,
+ 0x76, 0x58, 0xe5, 0xab, 0x5d, 0x1d, 0x8f, 0x40, 0xb3, 0x39, 0x8b, 0xef,
+ 0x1e, 0x83, 0x7d, 0x22, 0xd0, 0xe3, 0xa9, 0x00, 0x2e, 0xec, 0x53, 0xcf,
+ 0x62, 0x19, 0x85, 0x44, 0x28, 0x4c, 0xc0, 0x27, 0xcb, 0x7b, 0x0e, 0xec,
+ 0x10, 0x64, 0x00, 0x10, 0xa4, 0x05, 0xcc, 0xa0, 0x72, 0xbe, 0x41, 0x6c,
+ 0x31, 0x5b, 0x48, 0xe4, 0xb1, 0xec, 0xb9, 0x23, 0xeb, 0x55, 0x4d, 0xd0,
+ 0x7d, 0x62, 0x4a, 0xa5, 0xb4, 0xa5, 0xa4, 0x59, 0x85, 0xc5, 0x25, 0x91,
+ 0xa6, 0xfe, 0xa6, 0x09, 0x9f, 0x06, 0x10, 0x6d, 0x8f, 0x81, 0x0c, 0x64,
+ 0x40, 0x5e, 0x73, 0x00, 0x9a, 0xe0, 0x2e, 0x65, 0x98, 0x54, 0x10, 0x00,
+ 0x70, 0x98, 0xc8, 0xe1, 0xed, 0x34, 0x5f, 0xd8, 0x9c, 0xc7, 0x0d, 0xc0,
+ 0xd6, 0x23, 0x59, 0x45, 0xfc, 0xfe, 0x55, 0x7a, 0x86, 0xee, 0x94, 0x60,
+ 0x22, 0xf1, 0xae, 0xd1, 0xe6, 0x55, 0x46, 0xf6, 0x99, 0xc5, 0x1b, 0x08,
+ 0x74, 0x5f, 0xac, 0xb0, 0x64, 0x84, 0x8f, 0x89, 0x38, 0x1c, 0xa1, 0xa7,
+ 0x90, 0x21, 0x4f, 0x02, 0x6e, 0xbd, 0xe0, 0x61, 0x67, 0xd4, 0xf8, 0x42,
+ 0x87, 0x0f, 0x0a, 0xf7, 0xc9, 0x04, 0x6d, 0x2a, 0xa9, 0x2f, 0xef, 0x42,
+ 0xa5, 0xdf, 0xdd, 0xa3, 0x53, 0xdb, 0x98, 0x1e, 0x81, 0xf9, 0x9a, 0x72,
+ 0x7b, 0x5a, 0xde, 0x4f, 0x3e, 0x7f, 0xa2, 0x58, 0xa0, 0xe2, 0x17, 0xad,
+ 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x23, 0x30, 0x82,
+ 0x01, 0x1f, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0xf5, 0xcd, 0xd5, 0x3c, 0x08, 0x50, 0xf9, 0x6a, 0x4f, 0x3a, 0xb7,
+ 0x97, 0xda, 0x56, 0x83, 0xe6, 0x69, 0xd2, 0x68, 0xf7, 0x30, 0x45, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x04,
+ 0x55, 0x1d, 0x20, 0x00, 0x30, 0x32, 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x24, 0x68, 0x74, 0x74, 0x70,
+ 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61, 0x6c, 0x70, 0x68,
+ 0x61, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70,
+ 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0,
+ 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f,
+ 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97,
+ 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd,
+ 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x60, 0x40, 0x68,
+ 0x16, 0x47, 0xe7, 0x16, 0x8d, 0xdb, 0x5c, 0xa1, 0x56, 0x2a, 0xcb, 0xf4,
+ 0x5c, 0x9b, 0xb0, 0x1e, 0xa2, 0x4b, 0xf5, 0xcb, 0x02, 0x3f, 0xf8, 0x0b,
+ 0xa1, 0xf2, 0xa7, 0x42, 0xd4, 0xb7, 0x4c, 0xeb, 0xe3, 0x66, 0x80, 0xf3,
+ 0x25, 0x43, 0x78, 0x2e, 0x1b, 0x17, 0x56, 0x07, 0x52, 0x18, 0xcb, 0xd1,
+ 0xa8, 0xec, 0xe6, 0xfb, 0x73, 0x3e, 0xa4, 0x62, 0x8c, 0x80, 0xb4, 0xd2,
+ 0xc5, 0x12, 0x73, 0xa3, 0xd3, 0xfa, 0x02, 0x38, 0xbe, 0x63, 0x3d, 0x84,
+ 0xb8, 0x99, 0xc1, 0xf1, 0xba, 0xf7, 0x9f, 0xc3, 0x40, 0xd1, 0x58, 0x18,
+ 0x53, 0xc1, 0x62, 0xdd, 0xaf, 0x18, 0x42, 0x7f, 0x34, 0x4e, 0xc5, 0x43,
+ 0xd5, 0x71, 0xb0, 0x30, 0x00, 0xc7, 0xe3, 0x90, 0xae, 0x3f, 0x57, 0x86,
+ 0x97, 0xce, 0xea, 0x0c, 0x12, 0x8e, 0x22, 0x70, 0xe3, 0x66, 0xa7, 0x54,
+ 0x7f, 0x2e, 0x28, 0xcb, 0xd4, 0x54, 0xd0, 0xb3, 0x1e, 0x62, 0x67, 0x08,
+ 0xf9, 0x27, 0xe1, 0xcb, 0xe3, 0x66, 0xb8, 0x24, 0x1b, 0x89, 0x6a, 0x89,
+ 0x44, 0x65, 0xf2, 0xd9, 0x4c, 0xd2, 0x58, 0x1c, 0x8c, 0x4e, 0xc0, 0x95,
+ 0xa1, 0xd4, 0xef, 0x67, 0x2f, 0x38, 0x20, 0xe8, 0x2e, 0xff, 0x96, 0x51,
+ 0xf0, 0xba, 0xd8, 0x3d, 0x92, 0x70, 0x47, 0x65, 0x1c, 0x9e, 0x73, 0x72,
+ 0xb4, 0x60, 0x0c, 0x5c, 0xe2, 0xd1, 0x73, 0x76, 0xe0, 0xaf, 0x4e, 0xe2,
+ 0xe5, 0x37, 0xa5, 0x45, 0x2f, 0x8a, 0x23, 0x3e, 0x87, 0xc7, 0x30, 0xe6,
+ 0x31, 0x38, 0x7c, 0xf4, 0xdd, 0x52, 0xca, 0xf3, 0x53, 0x04, 0x25, 0x57,
+ 0x56, 0x66, 0x94, 0xe8, 0x0b, 0xee, 0xe6, 0x03, 0x14, 0x4e, 0xee, 0xfd,
+ 0x6d, 0x94, 0x64, 0x9e, 0x5e, 0xce, 0x79, 0xd4, 0xb2, 0xa6, 0xcf, 0x40,
+ 0xb1, 0x44, 0xa8, 0x3e, 0x87, 0x19, 0x5e, 0xe9, 0xf8, 0x21, 0x16, 0x59,
+ 0x53,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146031 (0x23a6f)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Nov 5 21:36:50 2013 GMT
+ Not After : May 20 21:36:50 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e3:be:7e:0a:86:a3:cf:6b:6d:3d:2b:a1:97:ad:
+ 49:24:4d:d7:77:b9:34:79:08:a5:9e:a2:9e:de:47:
+ 12:92:3d:7e:ea:19:86:b1:e8:4f:3d:5f:f7:d0:a7:
+ 77:9a:5b:1f:0a:03:b5:19:53:db:a5:21:94:69:63:
+ 9d:6a:4c:91:0c:10:47:be:11:fa:6c:86:25:b7:ab:
+ 04:68:42:38:09:65:f0:14:da:19:9e:fa:6b:0b:ab:
+ 62:ef:8d:a7:ef:63:70:23:a8:af:81:f3:d1:6e:88:
+ 67:53:ec:12:a4:29:75:8a:a7:f2:57:3d:a2:83:98:
+ 97:f2:0a:7d:d4:e7:43:6e:30:78:62:22:59:59:b8:
+ 71:27:45:aa:0f:66:c6:55:3f:fa:32:17:2b:31:8f:
+ 46:a0:fa:69:14:7c:9d:9f:5a:e2:eb:33:4e:10:a6:
+ b3:ed:77:63:d8:c3:9e:f4:dd:df:79:9a:7a:d4:ee:
+ de:dd:9a:cc:c3:b7:a9:5d:cc:11:3a:07:bb:6f:97:
+ a4:01:23:47:95:1f:a3:77:fa:58:92:c6:c7:d0:bd:
+ cf:93:18:42:b7:7e:f7:9e:65:ea:d5:3b:ca:ed:ac:
+ c5:70:a1:fe:d4:10:9a:f0:12:04:44:ac:1a:5b:78:
+ 50:45:57:4c:6f:bd:80:cb:81:5c:2d:b3:bc:76:a1:
+ 1e:65
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ D2:6F:F7:96:F4:85:3F:72:3C:30:7D:23:DA:85:78:9B:A3:7C:5A:7C
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g1.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-539
+ Signature Algorithm: sha256WithRSAEncryption
+ a0:d4:f7:2c:fb:74:0b:7f:64:f1:cd:43:6a:9f:62:53:1c:02:
+ 7c:98:90:a2:ee:4f:68:d4:20:1a:73:12:3e:77:b3:50:eb:72:
+ bc:ee:88:be:7f:17:ea:77:8f:83:61:95:4f:84:a1:cb:32:4f:
+ 6c:21:be:d2:69:96:7d:63:bd:dc:2b:a8:1f:d0:13:84:70:fe:
+ f6:35:95:89:f9:a6:77:b0:46:c8:bb:b7:13:f5:c9:60:69:d6:
+ 4c:fe:d2:8e:ef:d3:60:c1:80:80:e1:e7:fb:8b:6f:21:79:4a:
+ e0:dc:a9:1b:c1:b7:fb:c3:49:59:5c:b5:77:07:44:d4:97:fc:
+ 49:00:89:6f:06:4e:01:70:19:ac:2f:11:c0:e2:e6:0f:2f:86:
+ 4b:8d:7b:c3:b9:a7:2e:f4:f1:ac:16:3e:39:49:51:9e:17:4b:
+ 4f:10:3a:5b:a5:a8:92:6f:fd:fa:d6:0b:03:4d:47:56:57:19:
+ f3:cb:6b:f5:f3:d6:cf:b0:f5:f5:a3:11:d2:20:53:13:34:37:
+ 05:2c:43:5a:63:df:8d:40:d6:85:1e:51:e9:51:17:1e:03:56:
+ c9:f1:30:ad:e7:9b:11:a2:b9:d0:31:81:9b:68:b1:d9:e8:f3:
+ e6:94:7e:c7:ae:13:2f:87:ed:d0:25:b0:68:f9:de:08:5a:f3:
+ 29:cc:d4:92
+-----BEGIN CERTIFICATE-----
+MIIETzCCAzegAwIBAgIDAjpvMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTMxMTA1MjEzNjUwWhcNMjIwNTIwMjEzNjUwWjBEMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
+U1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjvn4K
+hqPPa209K6GXrUkkTdd3uTR5CKWeop7eRxKSPX7qGYax6E89X/fQp3eaWx8KA7UZ
+U9ulIZRpY51qTJEMEEe+EfpshiW3qwRoQjgJZfAU2hme+msLq2LvjafvY3AjqK+B
+89FuiGdT7BKkKXWKp/JXPaKDmJfyCn3U50NuMHhiIllZuHEnRaoPZsZVP/oyFysx
+j0ag+mkUfJ2fWuLrM04QprPtd2PYw5703d95mnrU7t7dmszDt6ldzBE6B7tvl6QB
+I0eVH6N3+liSxsfQvc+TGEK3fveeZerVO8rtrMVwof7UEJrwEgRErBpbeFBFV0xv
+vYDLgVwts7x2oR5lAgMBAAGjggFKMIIBRjAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjAdBgNVHQ4EFgQU0m/3lvSFP3I8MH0j2oV4m6N8WnwwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNgYDVR0fBC8wLTAroCmgJ4Yl
+aHR0cDovL2cxLnN5bWNiLmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAvBggrBgEFBQcB
+AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9nMi5zeW1jYi5jb20wTAYDVR0gBEUw
+QzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1
+c3QuY29tL3Jlc291cmNlcy9jcHMwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVN5
+bWFudGVjUEtJLTEtNTM5MA0GCSqGSIb3DQEBCwUAA4IBAQCg1Pcs+3QLf2TxzUNq
+n2JTHAJ8mJCi7k9o1CAacxI+d7NQ63K87oi+fxfqd4+DYZVPhKHLMk9sIb7SaZZ9
+Y73cK6gf0BOEcP72NZWJ+aZ3sEbIu7cT9clgadZM/tKO79NgwYCA4ef7i28heUrg
+3Kkbwbf7w0lZXLV3B0TUl/xJAIlvBk4BcBmsLxHA4uYPL4ZLjXvDuacu9PGsFj45
+SVGeF0tPEDpbpaiSb/361gsDTUdWVxnzy2v189bPsPX1oxHSIFMTNDcFLENaY9+N
+QNaFHlHpURceA1bJ8TCt55sRornQMYGbaLHZ6PPmlH7HrhMvh+3QJbBo+d4IWvMp
+zNSS
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert10[] = {
+ 0x30, 0x82, 0x04, 0x4f, 0x30, 0x82, 0x03, 0x37, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x6f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31,
+ 0x31, 0x30, 0x35, 0x32, 0x31, 0x33, 0x36, 0x35, 0x30, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x31, 0x33, 0x36, 0x35, 0x30,
+ 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe3, 0xbe, 0x7e, 0x0a,
+ 0x86, 0xa3, 0xcf, 0x6b, 0x6d, 0x3d, 0x2b, 0xa1, 0x97, 0xad, 0x49, 0x24,
+ 0x4d, 0xd7, 0x77, 0xb9, 0x34, 0x79, 0x08, 0xa5, 0x9e, 0xa2, 0x9e, 0xde,
+ 0x47, 0x12, 0x92, 0x3d, 0x7e, 0xea, 0x19, 0x86, 0xb1, 0xe8, 0x4f, 0x3d,
+ 0x5f, 0xf7, 0xd0, 0xa7, 0x77, 0x9a, 0x5b, 0x1f, 0x0a, 0x03, 0xb5, 0x19,
+ 0x53, 0xdb, 0xa5, 0x21, 0x94, 0x69, 0x63, 0x9d, 0x6a, 0x4c, 0x91, 0x0c,
+ 0x10, 0x47, 0xbe, 0x11, 0xfa, 0x6c, 0x86, 0x25, 0xb7, 0xab, 0x04, 0x68,
+ 0x42, 0x38, 0x09, 0x65, 0xf0, 0x14, 0xda, 0x19, 0x9e, 0xfa, 0x6b, 0x0b,
+ 0xab, 0x62, 0xef, 0x8d, 0xa7, 0xef, 0x63, 0x70, 0x23, 0xa8, 0xaf, 0x81,
+ 0xf3, 0xd1, 0x6e, 0x88, 0x67, 0x53, 0xec, 0x12, 0xa4, 0x29, 0x75, 0x8a,
+ 0xa7, 0xf2, 0x57, 0x3d, 0xa2, 0x83, 0x98, 0x97, 0xf2, 0x0a, 0x7d, 0xd4,
+ 0xe7, 0x43, 0x6e, 0x30, 0x78, 0x62, 0x22, 0x59, 0x59, 0xb8, 0x71, 0x27,
+ 0x45, 0xaa, 0x0f, 0x66, 0xc6, 0x55, 0x3f, 0xfa, 0x32, 0x17, 0x2b, 0x31,
+ 0x8f, 0x46, 0xa0, 0xfa, 0x69, 0x14, 0x7c, 0x9d, 0x9f, 0x5a, 0xe2, 0xeb,
+ 0x33, 0x4e, 0x10, 0xa6, 0xb3, 0xed, 0x77, 0x63, 0xd8, 0xc3, 0x9e, 0xf4,
+ 0xdd, 0xdf, 0x79, 0x9a, 0x7a, 0xd4, 0xee, 0xde, 0xdd, 0x9a, 0xcc, 0xc3,
+ 0xb7, 0xa9, 0x5d, 0xcc, 0x11, 0x3a, 0x07, 0xbb, 0x6f, 0x97, 0xa4, 0x01,
+ 0x23, 0x47, 0x95, 0x1f, 0xa3, 0x77, 0xfa, 0x58, 0x92, 0xc6, 0xc7, 0xd0,
+ 0xbd, 0xcf, 0x93, 0x18, 0x42, 0xb7, 0x7e, 0xf7, 0x9e, 0x65, 0xea, 0xd5,
+ 0x3b, 0xca, 0xed, 0xac, 0xc5, 0x70, 0xa1, 0xfe, 0xd4, 0x10, 0x9a, 0xf0,
+ 0x12, 0x04, 0x44, 0xac, 0x1a, 0x5b, 0x78, 0x50, 0x45, 0x57, 0x4c, 0x6f,
+ 0xbd, 0x80, 0xcb, 0x81, 0x5c, 0x2d, 0xb3, 0xbc, 0x76, 0xa1, 0x1e, 0x65,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x4a, 0x30, 0x82, 0x01,
+ 0x46, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64,
+ 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xd2, 0x6f, 0xf7,
+ 0x96, 0xf4, 0x85, 0x3f, 0x72, 0x3c, 0x30, 0x7d, 0x23, 0xda, 0x85, 0x78,
+ 0x9b, 0xa3, 0x7c, 0x5a, 0x7c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79,
+ 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73,
+ 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30,
+ 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45,
+ 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03,
+ 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31,
+ 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79,
+ 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d,
+ 0x35, 0x33, 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa0,
+ 0xd4, 0xf7, 0x2c, 0xfb, 0x74, 0x0b, 0x7f, 0x64, 0xf1, 0xcd, 0x43, 0x6a,
+ 0x9f, 0x62, 0x53, 0x1c, 0x02, 0x7c, 0x98, 0x90, 0xa2, 0xee, 0x4f, 0x68,
+ 0xd4, 0x20, 0x1a, 0x73, 0x12, 0x3e, 0x77, 0xb3, 0x50, 0xeb, 0x72, 0xbc,
+ 0xee, 0x88, 0xbe, 0x7f, 0x17, 0xea, 0x77, 0x8f, 0x83, 0x61, 0x95, 0x4f,
+ 0x84, 0xa1, 0xcb, 0x32, 0x4f, 0x6c, 0x21, 0xbe, 0xd2, 0x69, 0x96, 0x7d,
+ 0x63, 0xbd, 0xdc, 0x2b, 0xa8, 0x1f, 0xd0, 0x13, 0x84, 0x70, 0xfe, 0xf6,
+ 0x35, 0x95, 0x89, 0xf9, 0xa6, 0x77, 0xb0, 0x46, 0xc8, 0xbb, 0xb7, 0x13,
+ 0xf5, 0xc9, 0x60, 0x69, 0xd6, 0x4c, 0xfe, 0xd2, 0x8e, 0xef, 0xd3, 0x60,
+ 0xc1, 0x80, 0x80, 0xe1, 0xe7, 0xfb, 0x8b, 0x6f, 0x21, 0x79, 0x4a, 0xe0,
+ 0xdc, 0xa9, 0x1b, 0xc1, 0xb7, 0xfb, 0xc3, 0x49, 0x59, 0x5c, 0xb5, 0x77,
+ 0x07, 0x44, 0xd4, 0x97, 0xfc, 0x49, 0x00, 0x89, 0x6f, 0x06, 0x4e, 0x01,
+ 0x70, 0x19, 0xac, 0x2f, 0x11, 0xc0, 0xe2, 0xe6, 0x0f, 0x2f, 0x86, 0x4b,
+ 0x8d, 0x7b, 0xc3, 0xb9, 0xa7, 0x2e, 0xf4, 0xf1, 0xac, 0x16, 0x3e, 0x39,
+ 0x49, 0x51, 0x9e, 0x17, 0x4b, 0x4f, 0x10, 0x3a, 0x5b, 0xa5, 0xa8, 0x92,
+ 0x6f, 0xfd, 0xfa, 0xd6, 0x0b, 0x03, 0x4d, 0x47, 0x56, 0x57, 0x19, 0xf3,
+ 0xcb, 0x6b, 0xf5, 0xf3, 0xd6, 0xcf, 0xb0, 0xf5, 0xf5, 0xa3, 0x11, 0xd2,
+ 0x20, 0x53, 0x13, 0x34, 0x37, 0x05, 0x2c, 0x43, 0x5a, 0x63, 0xdf, 0x8d,
+ 0x40, 0xd6, 0x85, 0x1e, 0x51, 0xe9, 0x51, 0x17, 0x1e, 0x03, 0x56, 0xc9,
+ 0xf1, 0x30, 0xad, 0xe7, 0x9b, 0x11, 0xa2, 0xb9, 0xd0, 0x31, 0x81, 0x9b,
+ 0x68, 0xb1, 0xd9, 0xe8, 0xf3, 0xe6, 0x94, 0x7e, 0xc7, 0xae, 0x13, 0x2f,
+ 0x87, 0xed, 0xd0, 0x25, 0xb0, 0x68, 0xf9, 0xde, 0x08, 0x5a, 0xf3, 0x29,
+ 0xcc, 0xd4, 0x92,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146019 (0x23a63)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Aug 27 20:40:40 2012 GMT
+ Not After : May 20 20:40:40 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SSL CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b9:27:f9:4f:d8:f6:b7:15:3f:8f:cd:ce:d6:8d:
+ 1c:6b:fd:7f:da:54:21:4e:03:d8:ca:d0:72:52:15:
+ b8:c9:82:5b:58:79:84:ff:24:72:6f:f2:69:7f:bc:
+ 96:d9:9a:7a:c3:3e:a9:cf:50:22:13:0e:86:19:db:
+ e8:49:ef:8b:e6:d6:47:f2:fd:73:45:08:ae:8f:ac:
+ 5e:b6:f8:9e:7c:f7:10:ff:92:43:66:ef:1c:d4:ee:
+ a1:46:88:11:89:49:79:7a:25:ce:4b:6a:f0:d7:1c:
+ 76:1a:29:3c:c9:e4:fd:1e:85:dc:e0:31:65:05:47:
+ 16:ac:0a:07:4b:2e:70:5e:6b:06:a7:6b:3a:6c:af:
+ 05:12:c4:b2:11:25:d6:3e:97:29:f0:83:6c:57:1c:
+ d8:a5:ef:cc:ec:fd:d6:12:f1:3f:db:40:b4:ae:0f:
+ 18:d3:c5:af:40:92:5d:07:5e:4e:fe:62:17:37:89:
+ e9:8b:74:26:a2:ed:b8:0a:e7:6c:15:5b:35:90:72:
+ dd:d8:4d:21:d4:40:23:5c:8f:ee:80:31:16:ab:68:
+ 55:f4:0e:3b:54:e9:04:4d:f0:cc:4e:81:5e:e9:6f:
+ 52:69:4e:be:a6:16:6d:42:f5:51:ff:e0:0b:56:3c:
+ 98:4f:73:8f:0e:6f:1a:23:f1:c9:c8:d9:df:bc:ec:
+ 52:d7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ 11:4A:D0:73:39:D5:5B:69:08:5C:BA:3D:BF:64:9A:A8:8B:1C:55:BC
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.geotrust.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-254
+ Signature Algorithm: sha1WithRSAEncryption
+ 3c:e5:3d:5a:1b:a2:37:2a:e3:46:cf:36:96:18:3c:7b:f1:84:
+ c5:57:86:77:40:9d:35:f0:12:f0:78:18:fb:22:a4:de:98:4b:
+ 78:81:e6:4d:86:e3:91:0f:42:e3:b9:dc:a0:d6:ff:a9:f8:b1:
+ 79:97:99:d1:c3:6c:42:a5:92:94:e0:5d:0c:33:18:25:c9:2b:
+ 95:53:e0:e5:a9:0c:7d:47:fe:7f:51:31:44:5e:f7:2a:1e:35:
+ a2:94:32:f7:c9:ee:c0:b6:c6:9a:ac:de:99:21:6a:23:a0:38:
+ 64:ee:a3:c4:88:73:32:3b:50:ce:bf:ad:d3:75:1e:a6:f4:e9:
+ f9:42:6b:60:b2:dd:45:fd:5d:57:08:ce:2d:50:e6:12:32:16:
+ 13:8a:f2:94:a2:9b:47:a8:86:7f:d9:98:e5:f7:e5:76:74:64:
+ d8:91:bc:84:16:28:d8:25:44:30:7e:82:d8:ac:b1:e4:c0:e4:
+ 15:6c:db:b6:24:27:02:2a:01:12:85:ba:31:88:58:47:74:e3:
+ b8:d2:64:a6:c3:32:59:2e:29:4b:45:f1:5b:89:49:2e:82:9a:
+ c6:18:15:44:d0:2e:64:01:15:68:38:f9:f6:f9:66:03:0c:55:
+ 1b:9d:bf:00:40:ae:f0:48:27:4c:e0:80:5e:2d:b9:2a:15:7a:
+ bc:66:f8:35
+-----BEGIN CERTIFICATE-----
+MIIEWTCCA0GgAwIBAgIDAjpjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTIwODI3MjA0MDQwWhcNMjIwNTIwMjA0MDQwWjBEMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
+U1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5J/lP
+2Pa3FT+Pzc7WjRxr/X/aVCFOA9jK0HJSFbjJgltYeYT/JHJv8ml/vJbZmnrDPqnP
+UCITDoYZ2+hJ74vm1kfy/XNFCK6PrF62+J589xD/kkNm7xzU7qFGiBGJSXl6Jc5L
+avDXHHYaKTzJ5P0ehdzgMWUFRxasCgdLLnBeawanazpsrwUSxLIRJdY+lynwg2xX
+HNil78zs/dYS8T/bQLSuDxjTxa9Akl0HXk7+Yhc3iemLdCai7bgK52wVWzWQct3Y
+TSHUQCNcj+6AMRaraFX0DjtU6QRN8MxOgV7pb1JpTr6mFm1C9VH/4AtWPJhPc48O
+bxoj8cnI2d+87FLXAgMBAAGjggFUMIIBUDAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjAdBgNVHQ4EFgQUEUrQcznVW2kIXLo9v2SaqIscVbwwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2gK4Yp
+aHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwNAYIKwYB
+BQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nZW90cnVzdC5jb20w
+TAYDVR0gBEUwQzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93
+d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwKgYDVR0RBCMwIaQfMB0xGzAZ
+BgNVBAMTElZlcmlTaWduTVBLSS0yLTI1NDANBgkqhkiG9w0BAQUFAAOCAQEAPOU9
+WhuiNyrjRs82lhg8e/GExVeGd0CdNfAS8HgY+yKk3phLeIHmTYbjkQ9C47ncoNb/
+qfixeZeZ0cNsQqWSlOBdDDMYJckrlVPg5akMfUf+f1ExRF73Kh41opQy98nuwLbG
+mqzemSFqI6A4ZO6jxIhzMjtQzr+t03UepvTp+UJrYLLdRf1dVwjOLVDmEjIWE4ry
+lKKbR6iGf9mY5ffldnRk2JG8hBYo2CVEMH6C2Kyx5MDkFWzbtiQnAioBEoW6MYhY
+R3TjuNJkpsMyWS4pS0XxW4lJLoKaxhgVRNAuZAEVaDj59vlmAwxVG52/AECu8Egn
+TOCAXi25KhV6vGb4NQ==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert11[] = {
+ 0x30, 0x82, 0x04, 0x59, 0x30, 0x82, 0x03, 0x41, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x63, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30,
+ 0x38, 0x32, 0x37, 0x32, 0x30, 0x34, 0x30, 0x34, 0x30, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x30, 0x34, 0x30, 0x34, 0x30,
+ 0x5a, 0x30, 0x44, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x14, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x27, 0xf9, 0x4f,
+ 0xd8, 0xf6, 0xb7, 0x15, 0x3f, 0x8f, 0xcd, 0xce, 0xd6, 0x8d, 0x1c, 0x6b,
+ 0xfd, 0x7f, 0xda, 0x54, 0x21, 0x4e, 0x03, 0xd8, 0xca, 0xd0, 0x72, 0x52,
+ 0x15, 0xb8, 0xc9, 0x82, 0x5b, 0x58, 0x79, 0x84, 0xff, 0x24, 0x72, 0x6f,
+ 0xf2, 0x69, 0x7f, 0xbc, 0x96, 0xd9, 0x9a, 0x7a, 0xc3, 0x3e, 0xa9, 0xcf,
+ 0x50, 0x22, 0x13, 0x0e, 0x86, 0x19, 0xdb, 0xe8, 0x49, 0xef, 0x8b, 0xe6,
+ 0xd6, 0x47, 0xf2, 0xfd, 0x73, 0x45, 0x08, 0xae, 0x8f, 0xac, 0x5e, 0xb6,
+ 0xf8, 0x9e, 0x7c, 0xf7, 0x10, 0xff, 0x92, 0x43, 0x66, 0xef, 0x1c, 0xd4,
+ 0xee, 0xa1, 0x46, 0x88, 0x11, 0x89, 0x49, 0x79, 0x7a, 0x25, 0xce, 0x4b,
+ 0x6a, 0xf0, 0xd7, 0x1c, 0x76, 0x1a, 0x29, 0x3c, 0xc9, 0xe4, 0xfd, 0x1e,
+ 0x85, 0xdc, 0xe0, 0x31, 0x65, 0x05, 0x47, 0x16, 0xac, 0x0a, 0x07, 0x4b,
+ 0x2e, 0x70, 0x5e, 0x6b, 0x06, 0xa7, 0x6b, 0x3a, 0x6c, 0xaf, 0x05, 0x12,
+ 0xc4, 0xb2, 0x11, 0x25, 0xd6, 0x3e, 0x97, 0x29, 0xf0, 0x83, 0x6c, 0x57,
+ 0x1c, 0xd8, 0xa5, 0xef, 0xcc, 0xec, 0xfd, 0xd6, 0x12, 0xf1, 0x3f, 0xdb,
+ 0x40, 0xb4, 0xae, 0x0f, 0x18, 0xd3, 0xc5, 0xaf, 0x40, 0x92, 0x5d, 0x07,
+ 0x5e, 0x4e, 0xfe, 0x62, 0x17, 0x37, 0x89, 0xe9, 0x8b, 0x74, 0x26, 0xa2,
+ 0xed, 0xb8, 0x0a, 0xe7, 0x6c, 0x15, 0x5b, 0x35, 0x90, 0x72, 0xdd, 0xd8,
+ 0x4d, 0x21, 0xd4, 0x40, 0x23, 0x5c, 0x8f, 0xee, 0x80, 0x31, 0x16, 0xab,
+ 0x68, 0x55, 0xf4, 0x0e, 0x3b, 0x54, 0xe9, 0x04, 0x4d, 0xf0, 0xcc, 0x4e,
+ 0x81, 0x5e, 0xe9, 0x6f, 0x52, 0x69, 0x4e, 0xbe, 0xa6, 0x16, 0x6d, 0x42,
+ 0xf5, 0x51, 0xff, 0xe0, 0x0b, 0x56, 0x3c, 0x98, 0x4f, 0x73, 0x8f, 0x0e,
+ 0x6f, 0x1a, 0x23, 0xf1, 0xc9, 0xc8, 0xd9, 0xdf, 0xbc, 0xec, 0x52, 0xd7,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x54, 0x30, 0x82, 0x01,
+ 0x50, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64,
+ 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x11, 0x4a, 0xd0,
+ 0x73, 0x39, 0xd5, 0x5b, 0x69, 0x08, 0x5c, 0xba, 0x3d, 0xbf, 0x64, 0x9a,
+ 0xa8, 0x8b, 0x1c, 0x55, 0xbc, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b, 0x86, 0x29,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67,
+ 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67,
+ 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41,
+ 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36,
+ 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11,
+ 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53,
+ 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x32, 0x35,
+ 0x34, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0xe5, 0x3d,
+ 0x5a, 0x1b, 0xa2, 0x37, 0x2a, 0xe3, 0x46, 0xcf, 0x36, 0x96, 0x18, 0x3c,
+ 0x7b, 0xf1, 0x84, 0xc5, 0x57, 0x86, 0x77, 0x40, 0x9d, 0x35, 0xf0, 0x12,
+ 0xf0, 0x78, 0x18, 0xfb, 0x22, 0xa4, 0xde, 0x98, 0x4b, 0x78, 0x81, 0xe6,
+ 0x4d, 0x86, 0xe3, 0x91, 0x0f, 0x42, 0xe3, 0xb9, 0xdc, 0xa0, 0xd6, 0xff,
+ 0xa9, 0xf8, 0xb1, 0x79, 0x97, 0x99, 0xd1, 0xc3, 0x6c, 0x42, 0xa5, 0x92,
+ 0x94, 0xe0, 0x5d, 0x0c, 0x33, 0x18, 0x25, 0xc9, 0x2b, 0x95, 0x53, 0xe0,
+ 0xe5, 0xa9, 0x0c, 0x7d, 0x47, 0xfe, 0x7f, 0x51, 0x31, 0x44, 0x5e, 0xf7,
+ 0x2a, 0x1e, 0x35, 0xa2, 0x94, 0x32, 0xf7, 0xc9, 0xee, 0xc0, 0xb6, 0xc6,
+ 0x9a, 0xac, 0xde, 0x99, 0x21, 0x6a, 0x23, 0xa0, 0x38, 0x64, 0xee, 0xa3,
+ 0xc4, 0x88, 0x73, 0x32, 0x3b, 0x50, 0xce, 0xbf, 0xad, 0xd3, 0x75, 0x1e,
+ 0xa6, 0xf4, 0xe9, 0xf9, 0x42, 0x6b, 0x60, 0xb2, 0xdd, 0x45, 0xfd, 0x5d,
+ 0x57, 0x08, 0xce, 0x2d, 0x50, 0xe6, 0x12, 0x32, 0x16, 0x13, 0x8a, 0xf2,
+ 0x94, 0xa2, 0x9b, 0x47, 0xa8, 0x86, 0x7f, 0xd9, 0x98, 0xe5, 0xf7, 0xe5,
+ 0x76, 0x74, 0x64, 0xd8, 0x91, 0xbc, 0x84, 0x16, 0x28, 0xd8, 0x25, 0x44,
+ 0x30, 0x7e, 0x82, 0xd8, 0xac, 0xb1, 0xe4, 0xc0, 0xe4, 0x15, 0x6c, 0xdb,
+ 0xb6, 0x24, 0x27, 0x02, 0x2a, 0x01, 0x12, 0x85, 0xba, 0x31, 0x88, 0x58,
+ 0x47, 0x74, 0xe3, 0xb8, 0xd2, 0x64, 0xa6, 0xc3, 0x32, 0x59, 0x2e, 0x29,
+ 0x4b, 0x45, 0xf1, 0x5b, 0x89, 0x49, 0x2e, 0x82, 0x9a, 0xc6, 0x18, 0x15,
+ 0x44, 0xd0, 0x2e, 0x64, 0x01, 0x15, 0x68, 0x38, 0xf9, 0xf6, 0xf9, 0x66,
+ 0x03, 0x0c, 0x55, 0x1b, 0x9d, 0xbf, 0x00, 0x40, 0xae, 0xf0, 0x48, 0x27,
+ 0x4c, 0xe0, 0x80, 0x5e, 0x2d, 0xb9, 0x2a, 0x15, 0x7a, 0xbc, 0x66, 0xf8,
+ 0x35,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:44:4e:f0:3e:20
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Feb 20 10:00:00 2014 GMT
+ Not After : Feb 20 10:00:00 2024 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Domain Validation CA - SHA256 - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a9:dd:cc:0e:b3:e2:32:39:dd:49:22:a8:13:69:
+ 93:87:88:e1:0c:ee:71:7d:bd:90:87:96:5d:59:f2:
+ cc:b3:d2:58:57:57:f9:46:ef:6c:26:d8:36:42:8e:
+ 7e:30:b3:2f:9a:3e:53:7b:1f:6e:b6:a2:4c:45:1f:
+ 3c:d3:15:93:1c:89:ed:3c:f4:57:de:ca:bd:ec:06:
+ 9a:6a:2a:a0:19:52:7f:51:d1:74:39:08:9f:ab:eb:
+ d7:86:13:15:97:ae:36:c3:54:66:0e:5a:f2:a0:73:
+ 85:31:e3:b2:64:14:6a:ff:a5:a2:8e:24:bb:bd:85:
+ 52:15:a2:79:ee:f0:b5:ee:3d:b8:f4:7d:80:bc:d9:
+ 90:35:65:b8:17:a9:ad:b3:98:9f:a0:7e:7d:6e:fb:
+ 3f:ad:7c:c2:1b:59:36:96:da:37:32:4b:4b:5d:35:
+ 02:63:8e:db:a7:cf:62:ee:cc:2e:d4:8d:c9:bd:3c:
+ 6a:91:72:a2:22:a7:72:2d:20:d1:fa:ca:37:da:18:
+ 98:e6:16:24:71:25:4b:c4:e5:7b:89:52:09:02:fd:
+ 59:2b:04:6e:ca:07:81:d4:b3:da:da:db:e3:cc:80:
+ a8:56:07:06:7c:96:08:37:9d:db:38:b6:62:34:91:
+ 62:07:74:01:38:d8:72:30:e2:eb:90:71:26:62:c0:
+ 57:f3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ EA:4E:7C:D4:80:2D:E5:15:81:86:26:8C:82:6D:C0:98:A4:CF:97:0F
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha256WithRSAEncryption
+ d7:45:9e:a0:dc:e0:e3:61:5a:0b:7d:77:84:17:2d:65:5a:82:
+ 9a:8d:a3:27:2a:85:f7:c9:ef:e9:86:fd:d4:47:cd:01:52:96:
+ c5:43:bd:37:b1:e1:b8:f2:a9:d2:8a:11:84:71:91:15:89:dc:
+ 02:9d:0b:cb:6c:33:85:34:28:9e:20:b2:b1:97:dc:6d:0b:10:
+ c1:3c:cd:5f:ea:5d:d7:98:31:c5:34:99:5c:00:61:55:c4:1b:
+ 02:5b:c5:e3:89:c8:b4:b8:6f:1e:38:f2:56:26:e9:41:ef:3d:
+ cd:ac:99:4f:59:4a:57:2d:4b:7d:ae:c7:88:fb:d6:98:3b:f5:
+ e5:f0:e8:89:89:b9:8b:03:cb:5a:23:1f:a4:fd:b8:ea:fb:2e:
+ 9d:ae:6a:73:09:bc:fc:d5:a0:b5:44:82:ab:44:91:2e:50:2e:
+ 57:c1:43:d8:91:04:8b:e9:11:2e:5f:b4:3f:79:df:1e:fb:3f:
+ 30:00:8b:53:e3:b7:2c:1d:3b:4d:8b:dc:e4:64:1d:04:58:33:
+ af:1b:55:e7:ab:0c:bf:30:04:74:e4:f3:0e:2f:30:39:8d:4b:
+ 04:8c:1e:75:66:66:49:e0:be:40:34:c7:5c:5a:51:92:ba:12:
+ 3c:52:d5:04:82:55:2d:67:a5:df:b7:95:7c:ee:3f:c3:08:ba:
+ 04:be:c0:46
+-----BEGIN CERTIFICATE-----
+MIIEYzCCA0ugAwIBAgILBAAAAAABRE7wPiAwDQYJKoZIhvcNAQELBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw
+MDBaFw0yNDAyMjAxMDAwMDBaMGAxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMTYwNAYDVQQDEy1HbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0
+aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCp3cwOs+IyOd1JIqgTaZOHiOEM7nF9vZCHll1Z8syz0lhXV/lG72wm2DZC
+jn4wsy+aPlN7H262okxFHzzTFZMcie089Ffeyr3sBppqKqAZUn9R0XQ5CJ+r69eG
+ExWXrjbDVGYOWvKgc4Ux47JkFGr/paKOJLu9hVIVonnu8LXuPbj0fYC82ZA1ZbgX
+qa2zmJ+gfn1u+z+tfMIbWTaW2jcyS0tdNQJjjtunz2LuzC7Ujcm9PGqRcqIip3It
+INH6yjfaGJjmFiRxJUvE5XuJUgkC/VkrBG7KB4HUs9ra2+PMgKhWBwZ8lgg3nds4
+tmI0kWIHdAE42HIw4uuQcSZiwFfzAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMC
+AQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU6k581IAt5RWBhiaMgm3A
+mKTPlw8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v
+d3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSG
+Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEE
+MTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290
+cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEL
+BQADggEBANdFnqDc4ONhWgt9d4QXLWVagpqNoycqhffJ7+mG/dRHzQFSlsVDvTex
+4bjyqdKKEYRxkRWJ3AKdC8tsM4U0KJ4gsrGX3G0LEME8zV/qXdeYMcU0mVwAYVXE
+GwJbxeOJyLS4bx448lYm6UHvPc2smU9ZSlctS32ux4j71pg79eXw6ImJuYsDy1oj
+H6T9uOr7Lp2uanMJvPzVoLVEgqtEkS5QLlfBQ9iRBIvpES5ftD953x77PzAAi1Pj
+tywdO02L3ORkHQRYM68bVeerDL8wBHTk8w4vMDmNSwSMHnVmZkngvkA0x1xaUZK6
+EjxS1QSCVS1npd+3lXzuP8MIugS+wEY=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert12[] = {
+ 0x30, 0x82, 0x04, 0x63, 0x30, 0x82, 0x03, 0x4b, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0,
+ 0x3e, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x60, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x44, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41,
+ 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xa9, 0xdd, 0xcc, 0x0e, 0xb3, 0xe2, 0x32,
+ 0x39, 0xdd, 0x49, 0x22, 0xa8, 0x13, 0x69, 0x93, 0x87, 0x88, 0xe1, 0x0c,
+ 0xee, 0x71, 0x7d, 0xbd, 0x90, 0x87, 0x96, 0x5d, 0x59, 0xf2, 0xcc, 0xb3,
+ 0xd2, 0x58, 0x57, 0x57, 0xf9, 0x46, 0xef, 0x6c, 0x26, 0xd8, 0x36, 0x42,
+ 0x8e, 0x7e, 0x30, 0xb3, 0x2f, 0x9a, 0x3e, 0x53, 0x7b, 0x1f, 0x6e, 0xb6,
+ 0xa2, 0x4c, 0x45, 0x1f, 0x3c, 0xd3, 0x15, 0x93, 0x1c, 0x89, 0xed, 0x3c,
+ 0xf4, 0x57, 0xde, 0xca, 0xbd, 0xec, 0x06, 0x9a, 0x6a, 0x2a, 0xa0, 0x19,
+ 0x52, 0x7f, 0x51, 0xd1, 0x74, 0x39, 0x08, 0x9f, 0xab, 0xeb, 0xd7, 0x86,
+ 0x13, 0x15, 0x97, 0xae, 0x36, 0xc3, 0x54, 0x66, 0x0e, 0x5a, 0xf2, 0xa0,
+ 0x73, 0x85, 0x31, 0xe3, 0xb2, 0x64, 0x14, 0x6a, 0xff, 0xa5, 0xa2, 0x8e,
+ 0x24, 0xbb, 0xbd, 0x85, 0x52, 0x15, 0xa2, 0x79, 0xee, 0xf0, 0xb5, 0xee,
+ 0x3d, 0xb8, 0xf4, 0x7d, 0x80, 0xbc, 0xd9, 0x90, 0x35, 0x65, 0xb8, 0x17,
+ 0xa9, 0xad, 0xb3, 0x98, 0x9f, 0xa0, 0x7e, 0x7d, 0x6e, 0xfb, 0x3f, 0xad,
+ 0x7c, 0xc2, 0x1b, 0x59, 0x36, 0x96, 0xda, 0x37, 0x32, 0x4b, 0x4b, 0x5d,
+ 0x35, 0x02, 0x63, 0x8e, 0xdb, 0xa7, 0xcf, 0x62, 0xee, 0xcc, 0x2e, 0xd4,
+ 0x8d, 0xc9, 0xbd, 0x3c, 0x6a, 0x91, 0x72, 0xa2, 0x22, 0xa7, 0x72, 0x2d,
+ 0x20, 0xd1, 0xfa, 0xca, 0x37, 0xda, 0x18, 0x98, 0xe6, 0x16, 0x24, 0x71,
+ 0x25, 0x4b, 0xc4, 0xe5, 0x7b, 0x89, 0x52, 0x09, 0x02, 0xfd, 0x59, 0x2b,
+ 0x04, 0x6e, 0xca, 0x07, 0x81, 0xd4, 0xb3, 0xda, 0xda, 0xdb, 0xe3, 0xcc,
+ 0x80, 0xa8, 0x56, 0x07, 0x06, 0x7c, 0x96, 0x08, 0x37, 0x9d, 0xdb, 0x38,
+ 0xb6, 0x62, 0x34, 0x91, 0x62, 0x07, 0x74, 0x01, 0x38, 0xd8, 0x72, 0x30,
+ 0xe2, 0xeb, 0x90, 0x71, 0x26, 0x62, 0xc0, 0x57, 0xf3, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25, 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xea, 0x4e, 0x7c,
+ 0xd4, 0x80, 0x2d, 0xe5, 0x15, 0x81, 0x86, 0x26, 0x8c, 0x82, 0x6d, 0xc0,
+ 0x98, 0xa4, 0xcf, 0x97, 0x0f, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
+ 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
+ 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74,
+ 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89,
+ 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xd7, 0x45, 0x9e, 0xa0, 0xdc,
+ 0xe0, 0xe3, 0x61, 0x5a, 0x0b, 0x7d, 0x77, 0x84, 0x17, 0x2d, 0x65, 0x5a,
+ 0x82, 0x9a, 0x8d, 0xa3, 0x27, 0x2a, 0x85, 0xf7, 0xc9, 0xef, 0xe9, 0x86,
+ 0xfd, 0xd4, 0x47, 0xcd, 0x01, 0x52, 0x96, 0xc5, 0x43, 0xbd, 0x37, 0xb1,
+ 0xe1, 0xb8, 0xf2, 0xa9, 0xd2, 0x8a, 0x11, 0x84, 0x71, 0x91, 0x15, 0x89,
+ 0xdc, 0x02, 0x9d, 0x0b, 0xcb, 0x6c, 0x33, 0x85, 0x34, 0x28, 0x9e, 0x20,
+ 0xb2, 0xb1, 0x97, 0xdc, 0x6d, 0x0b, 0x10, 0xc1, 0x3c, 0xcd, 0x5f, 0xea,
+ 0x5d, 0xd7, 0x98, 0x31, 0xc5, 0x34, 0x99, 0x5c, 0x00, 0x61, 0x55, 0xc4,
+ 0x1b, 0x02, 0x5b, 0xc5, 0xe3, 0x89, 0xc8, 0xb4, 0xb8, 0x6f, 0x1e, 0x38,
+ 0xf2, 0x56, 0x26, 0xe9, 0x41, 0xef, 0x3d, 0xcd, 0xac, 0x99, 0x4f, 0x59,
+ 0x4a, 0x57, 0x2d, 0x4b, 0x7d, 0xae, 0xc7, 0x88, 0xfb, 0xd6, 0x98, 0x3b,
+ 0xf5, 0xe5, 0xf0, 0xe8, 0x89, 0x89, 0xb9, 0x8b, 0x03, 0xcb, 0x5a, 0x23,
+ 0x1f, 0xa4, 0xfd, 0xb8, 0xea, 0xfb, 0x2e, 0x9d, 0xae, 0x6a, 0x73, 0x09,
+ 0xbc, 0xfc, 0xd5, 0xa0, 0xb5, 0x44, 0x82, 0xab, 0x44, 0x91, 0x2e, 0x50,
+ 0x2e, 0x57, 0xc1, 0x43, 0xd8, 0x91, 0x04, 0x8b, 0xe9, 0x11, 0x2e, 0x5f,
+ 0xb4, 0x3f, 0x79, 0xdf, 0x1e, 0xfb, 0x3f, 0x30, 0x00, 0x8b, 0x53, 0xe3,
+ 0xb7, 0x2c, 0x1d, 0x3b, 0x4d, 0x8b, 0xdc, 0xe4, 0x64, 0x1d, 0x04, 0x58,
+ 0x33, 0xaf, 0x1b, 0x55, 0xe7, 0xab, 0x0c, 0xbf, 0x30, 0x04, 0x74, 0xe4,
+ 0xf3, 0x0e, 0x2f, 0x30, 0x39, 0x8d, 0x4b, 0x04, 0x8c, 0x1e, 0x75, 0x66,
+ 0x66, 0x49, 0xe0, 0xbe, 0x40, 0x34, 0xc7, 0x5c, 0x5a, 0x51, 0x92, 0xba,
+ 0x12, 0x3c, 0x52, 0xd5, 0x04, 0x82, 0x55, 0x2d, 0x67, 0xa5, 0xdf, 0xb7,
+ 0x95, 0x7c, 0xee, 0x3f, 0xc3, 0x08, 0xba, 0x04, 0xbe, 0xc0, 0x46,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:44:4e:f0:42:47
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Feb 20 10:00:00 2014 GMT
+ Not After : Feb 20 10:00:00 2024 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Organization Validation CA - SHA256 - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c7:0e:6c:3f:23:93:7f:cc:70:a5:9d:20:c3:0e:
+ 53:3f:7e:c0:4e:c2:98:49:ca:47:d5:23:ef:03:34:
+ 85:74:c8:a3:02:2e:46:5c:0b:7d:c9:88:9d:4f:8b:
+ f0:f8:9c:6c:8c:55:35:db:bf:f2:b3:ea:fb:e3:56:
+ e7:4a:46:d9:13:22:ca:36:d5:9b:c1:a8:e3:96:43:
+ 93:f2:0c:bc:e6:f9:e6:e8:99:c8:63:48:78:7f:57:
+ 36:69:1a:19:1d:5a:d1:d4:7d:c2:9c:d4:7f:e1:80:
+ 12:ae:7a:ea:88:ea:57:d8:ca:0a:0a:3a:12:49:a2:
+ 62:19:7a:0d:24:f7:37:eb:b4:73:92:7b:05:23:9b:
+ 12:b5:ce:eb:29:df:a4:14:02:b9:01:a5:d4:a6:9c:
+ 43:64:88:de:f8:7e:fe:e3:f5:1e:e5:fe:dc:a3:a8:
+ e4:66:31:d9:4c:25:e9:18:b9:89:59:09:ae:e9:9d:
+ 1c:6d:37:0f:4a:1e:35:20:28:e2:af:d4:21:8b:01:
+ c4:45:ad:6e:2b:63:ab:92:6b:61:0a:4d:20:ed:73:
+ ba:7c:ce:fe:16:b5:db:9f:80:f0:d6:8b:6c:d9:08:
+ 79:4a:4f:78:65:da:92:bc:be:35:f9:b3:c4:f9:27:
+ 80:4e:ff:96:52:e6:02:20:e1:07:73:e9:5d:2b:bd:
+ b2:f1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 96:DE:61:F1:BD:1C:16:29:53:1C:C0:CC:7D:3B:83:00:40:E6:1A:7C
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 46:2a:ee:5e:bd:ae:01:60:37:31:11:86:71:74:b6:46:49:c8:
+ 10:16:fe:2f:62:23:17:ab:1f:87:f8:82:ed:ca:df:0e:2c:df:
+ 64:75:8e:e5:18:72:a7:8c:3a:8b:c9:ac:a5:77:50:f7:ef:9e:
+ a4:e0:a0:8f:14:57:a3:2a:5f:ec:7e:6d:10:e6:ba:8d:b0:08:
+ 87:76:0e:4c:b2:d9:51:bb:11:02:f2:5c:dd:1c:bd:f3:55:96:
+ 0f:d4:06:c0:fc:e2:23:8a:24:70:d3:bb:f0:79:1a:a7:61:70:
+ 83:8a:af:06:c5:20:d8:a1:63:d0:6c:ae:4f:32:d7:ae:7c:18:
+ 45:75:05:29:77:df:42:40:64:64:86:be:2a:76:09:31:6f:1d:
+ 24:f4:99:d0:85:fe:f2:21:08:f9:c6:f6:f1:d0:59:ed:d6:56:
+ 3c:08:28:03:67:ba:f0:f9:f1:90:16:47:ae:67:e6:bc:80:48:
+ e9:42:76:34:97:55:69:24:0e:83:d6:a0:2d:b4:f5:f3:79:8a:
+ 49:28:74:1a:41:a1:c2:d3:24:88:35:30:60:94:17:b4:e1:04:
+ 22:31:3d:3b:2f:17:06:b2:b8:9d:86:2b:5a:69:ef:83:f5:4b:
+ c4:aa:b4:2a:f8:7c:a1:b1:85:94:8c:f4:0c:87:0c:f4:ac:40:
+ f8:59:49:98
+-----BEGIN CERTIFICATE-----
+MIIEaTCCA1GgAwIBAgILBAAAAAABRE7wQkcwDQYJKoZIhvcNAQELBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw
+MDBaFw0yNDAyMjAxMDAwMDBaMGYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMTwwOgYDVQQDEzNHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBW
+YWxpZGF0aW9uIENBIC0gU0hBMjU2IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDHDmw/I5N/zHClnSDDDlM/fsBOwphJykfVI+8DNIV0yKMCLkZc
+C33JiJ1Pi/D4nGyMVTXbv/Kz6vvjVudKRtkTIso21ZvBqOOWQ5PyDLzm+ebomchj
+SHh/VzZpGhkdWtHUfcKc1H/hgBKueuqI6lfYygoKOhJJomIZeg0k9zfrtHOSewUj
+mxK1zusp36QUArkBpdSmnENkiN74fv7j9R7l/tyjqORmMdlMJekYuYlZCa7pnRxt
+Nw9KHjUgKOKv1CGLAcRFrW4rY6uSa2EKTSDtc7p8zv4WtdufgPDWi2zZCHlKT3hl
+2pK8vjX5s8T5J4BO/5ZS5gIg4Qdz6V0rvbLxAgMBAAGjggElMIIBITAOBgNVHQ8B
+Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUlt5h8b0cFilT
+HMDMfTuDAEDmGnwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0
+dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCow
+KKAmoCSGImh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYB
+BQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNv
+bS9yb290cjEwHwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZI
+hvcNAQELBQADggEBAEYq7l69rgFgNzERhnF0tkZJyBAW/i9iIxerH4f4gu3K3w4s
+32R1juUYcqeMOovJrKV3UPfvnqTgoI8UV6MqX+x+bRDmuo2wCId2Dkyy2VG7EQLy
+XN0cvfNVlg/UBsD84iOKJHDTu/B5GqdhcIOKrwbFINihY9Bsrk8y1658GEV1BSl3
+30JAZGSGvip2CTFvHST0mdCF/vIhCPnG9vHQWe3WVjwIKANnuvD58ZAWR65n5ryA
+SOlCdjSXVWkkDoPWoC209fN5ikkodBpBocLTJIg1MGCUF7ThBCIxPTsvFwayuJ2G
+K1pp74P1S8SqtCr4fKGxhZSM9AyHDPSsQPhZSZg=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert13[] = {
+ 0x30, 0x82, 0x04, 0x69, 0x30, 0x82, 0x03, 0x51, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x44, 0x4e, 0xf0,
+ 0x42, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x32, 0x30, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x3c, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x33, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x4f, 0x72,
+ 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20,
+ 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc7,
+ 0x0e, 0x6c, 0x3f, 0x23, 0x93, 0x7f, 0xcc, 0x70, 0xa5, 0x9d, 0x20, 0xc3,
+ 0x0e, 0x53, 0x3f, 0x7e, 0xc0, 0x4e, 0xc2, 0x98, 0x49, 0xca, 0x47, 0xd5,
+ 0x23, 0xef, 0x03, 0x34, 0x85, 0x74, 0xc8, 0xa3, 0x02, 0x2e, 0x46, 0x5c,
+ 0x0b, 0x7d, 0xc9, 0x88, 0x9d, 0x4f, 0x8b, 0xf0, 0xf8, 0x9c, 0x6c, 0x8c,
+ 0x55, 0x35, 0xdb, 0xbf, 0xf2, 0xb3, 0xea, 0xfb, 0xe3, 0x56, 0xe7, 0x4a,
+ 0x46, 0xd9, 0x13, 0x22, 0xca, 0x36, 0xd5, 0x9b, 0xc1, 0xa8, 0xe3, 0x96,
+ 0x43, 0x93, 0xf2, 0x0c, 0xbc, 0xe6, 0xf9, 0xe6, 0xe8, 0x99, 0xc8, 0x63,
+ 0x48, 0x78, 0x7f, 0x57, 0x36, 0x69, 0x1a, 0x19, 0x1d, 0x5a, 0xd1, 0xd4,
+ 0x7d, 0xc2, 0x9c, 0xd4, 0x7f, 0xe1, 0x80, 0x12, 0xae, 0x7a, 0xea, 0x88,
+ 0xea, 0x57, 0xd8, 0xca, 0x0a, 0x0a, 0x3a, 0x12, 0x49, 0xa2, 0x62, 0x19,
+ 0x7a, 0x0d, 0x24, 0xf7, 0x37, 0xeb, 0xb4, 0x73, 0x92, 0x7b, 0x05, 0x23,
+ 0x9b, 0x12, 0xb5, 0xce, 0xeb, 0x29, 0xdf, 0xa4, 0x14, 0x02, 0xb9, 0x01,
+ 0xa5, 0xd4, 0xa6, 0x9c, 0x43, 0x64, 0x88, 0xde, 0xf8, 0x7e, 0xfe, 0xe3,
+ 0xf5, 0x1e, 0xe5, 0xfe, 0xdc, 0xa3, 0xa8, 0xe4, 0x66, 0x31, 0xd9, 0x4c,
+ 0x25, 0xe9, 0x18, 0xb9, 0x89, 0x59, 0x09, 0xae, 0xe9, 0x9d, 0x1c, 0x6d,
+ 0x37, 0x0f, 0x4a, 0x1e, 0x35, 0x20, 0x28, 0xe2, 0xaf, 0xd4, 0x21, 0x8b,
+ 0x01, 0xc4, 0x45, 0xad, 0x6e, 0x2b, 0x63, 0xab, 0x92, 0x6b, 0x61, 0x0a,
+ 0x4d, 0x20, 0xed, 0x73, 0xba, 0x7c, 0xce, 0xfe, 0x16, 0xb5, 0xdb, 0x9f,
+ 0x80, 0xf0, 0xd6, 0x8b, 0x6c, 0xd9, 0x08, 0x79, 0x4a, 0x4f, 0x78, 0x65,
+ 0xda, 0x92, 0xbc, 0xbe, 0x35, 0xf9, 0xb3, 0xc4, 0xf9, 0x27, 0x80, 0x4e,
+ 0xff, 0x96, 0x52, 0xe6, 0x02, 0x20, 0xe1, 0x07, 0x73, 0xe9, 0x5d, 0x2b,
+ 0xbd, 0xb2, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25,
+ 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x96, 0xde, 0x61, 0xf1, 0xbd, 0x1c, 0x16, 0x29, 0x53,
+ 0x1c, 0xc0, 0xcc, 0x7d, 0x3b, 0x83, 0x00, 0x40, 0xe6, 0x1a, 0x7c, 0x30,
+ 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30,
+ 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66,
+ 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34,
+ 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x46, 0x2a, 0xee, 0x5e, 0xbd, 0xae, 0x01, 0x60, 0x37, 0x31, 0x11,
+ 0x86, 0x71, 0x74, 0xb6, 0x46, 0x49, 0xc8, 0x10, 0x16, 0xfe, 0x2f, 0x62,
+ 0x23, 0x17, 0xab, 0x1f, 0x87, 0xf8, 0x82, 0xed, 0xca, 0xdf, 0x0e, 0x2c,
+ 0xdf, 0x64, 0x75, 0x8e, 0xe5, 0x18, 0x72, 0xa7, 0x8c, 0x3a, 0x8b, 0xc9,
+ 0xac, 0xa5, 0x77, 0x50, 0xf7, 0xef, 0x9e, 0xa4, 0xe0, 0xa0, 0x8f, 0x14,
+ 0x57, 0xa3, 0x2a, 0x5f, 0xec, 0x7e, 0x6d, 0x10, 0xe6, 0xba, 0x8d, 0xb0,
+ 0x08, 0x87, 0x76, 0x0e, 0x4c, 0xb2, 0xd9, 0x51, 0xbb, 0x11, 0x02, 0xf2,
+ 0x5c, 0xdd, 0x1c, 0xbd, 0xf3, 0x55, 0x96, 0x0f, 0xd4, 0x06, 0xc0, 0xfc,
+ 0xe2, 0x23, 0x8a, 0x24, 0x70, 0xd3, 0xbb, 0xf0, 0x79, 0x1a, 0xa7, 0x61,
+ 0x70, 0x83, 0x8a, 0xaf, 0x06, 0xc5, 0x20, 0xd8, 0xa1, 0x63, 0xd0, 0x6c,
+ 0xae, 0x4f, 0x32, 0xd7, 0xae, 0x7c, 0x18, 0x45, 0x75, 0x05, 0x29, 0x77,
+ 0xdf, 0x42, 0x40, 0x64, 0x64, 0x86, 0xbe, 0x2a, 0x76, 0x09, 0x31, 0x6f,
+ 0x1d, 0x24, 0xf4, 0x99, 0xd0, 0x85, 0xfe, 0xf2, 0x21, 0x08, 0xf9, 0xc6,
+ 0xf6, 0xf1, 0xd0, 0x59, 0xed, 0xd6, 0x56, 0x3c, 0x08, 0x28, 0x03, 0x67,
+ 0xba, 0xf0, 0xf9, 0xf1, 0x90, 0x16, 0x47, 0xae, 0x67, 0xe6, 0xbc, 0x80,
+ 0x48, 0xe9, 0x42, 0x76, 0x34, 0x97, 0x55, 0x69, 0x24, 0x0e, 0x83, 0xd6,
+ 0xa0, 0x2d, 0xb4, 0xf5, 0xf3, 0x79, 0x8a, 0x49, 0x28, 0x74, 0x1a, 0x41,
+ 0xa1, 0xc2, 0xd3, 0x24, 0x88, 0x35, 0x30, 0x60, 0x94, 0x17, 0xb4, 0xe1,
+ 0x04, 0x22, 0x31, 0x3d, 0x3b, 0x2f, 0x17, 0x06, 0xb2, 0xb8, 0x9d, 0x86,
+ 0x2b, 0x5a, 0x69, 0xef, 0x83, 0xf5, 0x4b, 0xc4, 0xaa, 0xb4, 0x2a, 0xf8,
+ 0x7c, 0xa1, 0xb1, 0x85, 0x94, 0x8c, 0xf4, 0x0c, 0x87, 0x0c, 0xf4, 0xac,
+ 0x40, 0xf8, 0x59, 0x49, 0x98,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4d:5f:2c:34:08:b2:4c:20:cd:6d:50:7e:24:4d:c9:ec
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Feb 8 00:00:00 2010 GMT
+ Not After : Feb 7 23:59:59 2020 GMT
+ Subject: C=US, O=Thawte, Inc., CN=Thawte SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:99:e4:85:5b:76:49:7d:2f:05:d8:c5:ac:c8:c8:
+ a9:d3:dc:98:e6:d7:34:a6:2f:0c:f2:22:26:d8:a3:
+ c9:14:4c:8f:05:a4:45:e8:14:0c:58:90:05:1a:b7:
+ c5:c1:06:a5:80:af:bb:1d:49:6b:52:34:88:c3:59:
+ e7:ef:6b:c4:27:41:8c:2b:66:1d:d0:e0:a3:97:98:
+ 19:34:4b:41:d5:98:d5:c7:05:ad:a2:e4:d7:ed:0c:
+ ad:4f:c1:b5:b0:21:fd:3e:50:53:b2:c4:90:d0:d4:
+ 30:67:6c:9a:f1:0e:74:c4:c2:dc:8a:e8:97:ff:c9:
+ 92:ae:01:8a:56:0a:98:32:b0:00:23:ec:90:1a:60:
+ c3:ed:bb:3a:cb:0f:63:9f:0d:44:c9:52:e1:25:96:
+ bf:ed:50:95:89:7f:56:14:b1:b7:61:1d:1c:07:8c:
+ 3a:2c:f7:ff:80:de:39:45:d5:af:1a:d1:78:d8:c7:
+ 71:6a:a3:19:a7:32:50:21:e9:f2:0e:a1:c6:13:03:
+ 44:48:d1:66:a8:52:57:d7:11:b4:93:8b:e5:99:9f:
+ 5d:e7:78:51:e5:4d:f6:b7:59:b4:76:b5:09:37:4d:
+ 06:38:13:7a:1c:08:98:5c:c4:48:4a:cb:52:a0:a9:
+ f8:b1:9d:8e:7b:79:b0:20:2f:3c:96:a8:11:62:47:
+ bb:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.thawte.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePCA.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-9
+ X509v3 Subject Key Identifier:
+ A7:A2:83:BB:34:45:40:3D:FC:D5:30:4F:12:B9:3E:A1:01:9F:F6:DB
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 80:22:80:e0:6c:c8:95:16:d7:57:26:87:f3:72:34:db:c6:72:
+ 56:27:3e:d3:96:f6:2e:25:91:a5:3e:33:97:a7:4b:e5:2f:fb:
+ 25:7d:2f:07:61:fa:6f:83:74:4c:4c:53:72:20:a4:7a:cf:51:
+ 51:56:81:88:b0:6d:1f:36:2c:c8:2b:b1:88:99:c1:fe:44:ab:
+ 48:51:7c:d8:f2:44:64:2a:d8:71:a7:fb:1a:2f:f9:19:8d:34:
+ b2:23:bf:c4:4c:55:1d:8e:44:e8:aa:5d:9a:dd:9f:fd:03:c7:
+ ba:24:43:8d:2d:47:44:db:f6:d8:98:c8:b2:f9:da:ef:ed:29:
+ 5c:69:12:fa:d1:23:96:0f:bf:9c:0d:f2:79:45:53:37:9a:56:
+ 2f:e8:57:10:70:f6:ee:89:0c:49:89:9a:c1:23:f5:c2:2a:cc:
+ 41:cf:22:ab:65:6e:b7:94:82:6d:2f:40:5f:58:de:eb:95:2b:
+ a6:72:68:52:19:91:2a:ae:75:9d:4e:92:e6:ca:de:54:ea:18:
+ ab:25:3c:e6:64:a6:79:1f:26:7d:61:ed:7d:d2:e5:71:55:d8:
+ 93:17:7c:14:38:30:3c:df:86:e3:4c:ad:49:e3:97:59:ce:1b:
+ 9b:2b:ce:dc:65:d4:0b:28:6b:4e:84:46:51:44:f7:33:08:2d:
+ 58:97:21:ae
+-----BEGIN CERTIFICATE-----
+MIIEbDCCA1SgAwIBAgIQTV8sNAiyTCDNbVB+JE3J7DANBgkqhkiG9w0BAQUFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTAwMjA4MDAwMDAwWhcNMjAw
+MjA3MjM1OTU5WjA8MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMu
+MRYwFAYDVQQDEw1UaGF3dGUgU1NMIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAmeSFW3ZJfS8F2MWsyMip09yY5tc0pi8M8iIm2KPJFEyPBaRF6BQM
+WJAFGrfFwQalgK+7HUlrUjSIw1nn72vEJ0GMK2Yd0OCjl5gZNEtB1ZjVxwWtouTX
+7QytT8G1sCH9PlBTssSQ0NQwZ2ya8Q50xMLciuiX/8mSrgGKVgqYMrAAI+yQGmDD
+7bs6yw9jnw1EyVLhJZa/7VCViX9WFLG3YR0cB4w6LPf/gN45RdWvGtF42MdxaqMZ
+pzJQIenyDqHGEwNESNFmqFJX1xG0k4vlmZ9d53hR5U32t1m0drUJN00GOBN6HAiY
+XMRISstSoKn4sZ2Oe3mwIC88lqgRYke7EQIDAQABo4H7MIH4MDIGCCsGAQUFBwEB
+BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTASBgNVHRMB
+Af8ECDAGAQH/AgEAMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudGhhd3Rl
+LmNvbS9UaGF3dGVQQ0EuY3JsMA4GA1UdDwEB/wQEAwIBBjAoBgNVHREEITAfpB0w
+GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItOTAdBgNVHQ4EFgQUp6KDuzRFQD38
+1TBPErk+oQGf9tswHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutXSFAwDQYJ
+KoZIhvcNAQEFBQADggEBAIAigOBsyJUW11cmh/NyNNvGclYnPtOW9i4lkaU+M5en
+S+Uv+yV9Lwdh+m+DdExMU3IgpHrPUVFWgYiwbR82LMgrsYiZwf5Eq0hRfNjyRGQq
+2HGn+xov+RmNNLIjv8RMVR2OROiqXZrdn/0Dx7okQ40tR0Tb9tiYyLL52u/tKVxp
+EvrRI5YPv5wN8nlFUzeaVi/oVxBw9u6JDEmJmsEj9cIqzEHPIqtlbreUgm0vQF9Y
+3uuVK6ZyaFIZkSqudZ1OkubK3lTqGKslPOZkpnkfJn1h7X3S5XFV2JMXfBQ4MDzf
+huNMrUnjl1nOG5srztxl1Asoa06ERlFE9zMILViXIa4=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert14[] = {
+ 0x30, 0x82, 0x04, 0x6c, 0x30, 0x82, 0x03, 0x54, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x4d, 0x5f, 0x2c, 0x34, 0x08, 0xb2, 0x4c, 0x20, 0xcd,
+ 0x6d, 0x50, 0x7e, 0x24, 0x4d, 0xc9, 0xec, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30,
+ 0x32, 0x30, 0x37, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x3c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0d, 0x54,
+ 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x99, 0xe4, 0x85,
+ 0x5b, 0x76, 0x49, 0x7d, 0x2f, 0x05, 0xd8, 0xc5, 0xac, 0xc8, 0xc8, 0xa9,
+ 0xd3, 0xdc, 0x98, 0xe6, 0xd7, 0x34, 0xa6, 0x2f, 0x0c, 0xf2, 0x22, 0x26,
+ 0xd8, 0xa3, 0xc9, 0x14, 0x4c, 0x8f, 0x05, 0xa4, 0x45, 0xe8, 0x14, 0x0c,
+ 0x58, 0x90, 0x05, 0x1a, 0xb7, 0xc5, 0xc1, 0x06, 0xa5, 0x80, 0xaf, 0xbb,
+ 0x1d, 0x49, 0x6b, 0x52, 0x34, 0x88, 0xc3, 0x59, 0xe7, 0xef, 0x6b, 0xc4,
+ 0x27, 0x41, 0x8c, 0x2b, 0x66, 0x1d, 0xd0, 0xe0, 0xa3, 0x97, 0x98, 0x19,
+ 0x34, 0x4b, 0x41, 0xd5, 0x98, 0xd5, 0xc7, 0x05, 0xad, 0xa2, 0xe4, 0xd7,
+ 0xed, 0x0c, 0xad, 0x4f, 0xc1, 0xb5, 0xb0, 0x21, 0xfd, 0x3e, 0x50, 0x53,
+ 0xb2, 0xc4, 0x90, 0xd0, 0xd4, 0x30, 0x67, 0x6c, 0x9a, 0xf1, 0x0e, 0x74,
+ 0xc4, 0xc2, 0xdc, 0x8a, 0xe8, 0x97, 0xff, 0xc9, 0x92, 0xae, 0x01, 0x8a,
+ 0x56, 0x0a, 0x98, 0x32, 0xb0, 0x00, 0x23, 0xec, 0x90, 0x1a, 0x60, 0xc3,
+ 0xed, 0xbb, 0x3a, 0xcb, 0x0f, 0x63, 0x9f, 0x0d, 0x44, 0xc9, 0x52, 0xe1,
+ 0x25, 0x96, 0xbf, 0xed, 0x50, 0x95, 0x89, 0x7f, 0x56, 0x14, 0xb1, 0xb7,
+ 0x61, 0x1d, 0x1c, 0x07, 0x8c, 0x3a, 0x2c, 0xf7, 0xff, 0x80, 0xde, 0x39,
+ 0x45, 0xd5, 0xaf, 0x1a, 0xd1, 0x78, 0xd8, 0xc7, 0x71, 0x6a, 0xa3, 0x19,
+ 0xa7, 0x32, 0x50, 0x21, 0xe9, 0xf2, 0x0e, 0xa1, 0xc6, 0x13, 0x03, 0x44,
+ 0x48, 0xd1, 0x66, 0xa8, 0x52, 0x57, 0xd7, 0x11, 0xb4, 0x93, 0x8b, 0xe5,
+ 0x99, 0x9f, 0x5d, 0xe7, 0x78, 0x51, 0xe5, 0x4d, 0xf6, 0xb7, 0x59, 0xb4,
+ 0x76, 0xb5, 0x09, 0x37, 0x4d, 0x06, 0x38, 0x13, 0x7a, 0x1c, 0x08, 0x98,
+ 0x5c, 0xc4, 0x48, 0x4a, 0xcb, 0x52, 0xa0, 0xa9, 0xf8, 0xb1, 0x9d, 0x8e,
+ 0x7b, 0x79, 0xb0, 0x20, 0x2f, 0x3c, 0x96, 0xa8, 0x11, 0x62, 0x47, 0xbb,
+ 0x11, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xfb, 0x30, 0x81, 0xf8,
+ 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30,
+ 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50,
+ 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x28,
+ 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, 0x30,
+ 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49,
+ 0x2d, 0x32, 0x2d, 0x39, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0xa7, 0xa2, 0x83, 0xbb, 0x34, 0x45, 0x40, 0x3d, 0xfc,
+ 0xd5, 0x30, 0x4f, 0x12, 0xb9, 0x3e, 0xa1, 0x01, 0x9f, 0xf6, 0xdb, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a,
+ 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x80, 0x22, 0x80, 0xe0, 0x6c, 0xc8, 0x95, 0x16,
+ 0xd7, 0x57, 0x26, 0x87, 0xf3, 0x72, 0x34, 0xdb, 0xc6, 0x72, 0x56, 0x27,
+ 0x3e, 0xd3, 0x96, 0xf6, 0x2e, 0x25, 0x91, 0xa5, 0x3e, 0x33, 0x97, 0xa7,
+ 0x4b, 0xe5, 0x2f, 0xfb, 0x25, 0x7d, 0x2f, 0x07, 0x61, 0xfa, 0x6f, 0x83,
+ 0x74, 0x4c, 0x4c, 0x53, 0x72, 0x20, 0xa4, 0x7a, 0xcf, 0x51, 0x51, 0x56,
+ 0x81, 0x88, 0xb0, 0x6d, 0x1f, 0x36, 0x2c, 0xc8, 0x2b, 0xb1, 0x88, 0x99,
+ 0xc1, 0xfe, 0x44, 0xab, 0x48, 0x51, 0x7c, 0xd8, 0xf2, 0x44, 0x64, 0x2a,
+ 0xd8, 0x71, 0xa7, 0xfb, 0x1a, 0x2f, 0xf9, 0x19, 0x8d, 0x34, 0xb2, 0x23,
+ 0xbf, 0xc4, 0x4c, 0x55, 0x1d, 0x8e, 0x44, 0xe8, 0xaa, 0x5d, 0x9a, 0xdd,
+ 0x9f, 0xfd, 0x03, 0xc7, 0xba, 0x24, 0x43, 0x8d, 0x2d, 0x47, 0x44, 0xdb,
+ 0xf6, 0xd8, 0x98, 0xc8, 0xb2, 0xf9, 0xda, 0xef, 0xed, 0x29, 0x5c, 0x69,
+ 0x12, 0xfa, 0xd1, 0x23, 0x96, 0x0f, 0xbf, 0x9c, 0x0d, 0xf2, 0x79, 0x45,
+ 0x53, 0x37, 0x9a, 0x56, 0x2f, 0xe8, 0x57, 0x10, 0x70, 0xf6, 0xee, 0x89,
+ 0x0c, 0x49, 0x89, 0x9a, 0xc1, 0x23, 0xf5, 0xc2, 0x2a, 0xcc, 0x41, 0xcf,
+ 0x22, 0xab, 0x65, 0x6e, 0xb7, 0x94, 0x82, 0x6d, 0x2f, 0x40, 0x5f, 0x58,
+ 0xde, 0xeb, 0x95, 0x2b, 0xa6, 0x72, 0x68, 0x52, 0x19, 0x91, 0x2a, 0xae,
+ 0x75, 0x9d, 0x4e, 0x92, 0xe6, 0xca, 0xde, 0x54, 0xea, 0x18, 0xab, 0x25,
+ 0x3c, 0xe6, 0x64, 0xa6, 0x79, 0x1f, 0x26, 0x7d, 0x61, 0xed, 0x7d, 0xd2,
+ 0xe5, 0x71, 0x55, 0xd8, 0x93, 0x17, 0x7c, 0x14, 0x38, 0x30, 0x3c, 0xdf,
+ 0x86, 0xe3, 0x4c, 0xad, 0x49, 0xe3, 0x97, 0x59, 0xce, 0x1b, 0x9b, 0x2b,
+ 0xce, 0xdc, 0x65, 0xd4, 0x0b, 0x28, 0x6b, 0x4e, 0x84, 0x46, 0x51, 0x44,
+ 0xf7, 0x33, 0x08, 0x2d, 0x58, 0x97, 0x21, 0xae,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 6e:8a:90:eb:cf:f0:44:8a:72:0d:08:05:d0:82:a5:44
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust EV SSL CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d9:b4:05:f2:38:67:0f:09:e7:7c:f5:63:2a:e5:
+ b9:5e:a8:11:ae:75:71:d9:4c:84:67:ad:89:5d:fc:
+ 28:3d:2a:b0:a5:d5:d4:e6:30:0a:84:d4:e4:18:cb:
+ 85:37:c5:46:71:eb:1c:7b:69:db:65:69:8c:30:05:
+ 3e:07:e1:6f:3c:c1:0b:61:e6:38:44:fc:bc:8c:2f:
+ 4e:75:57:f5:96:99:7c:3e:87:1f:0f:90:4b:70:c3:
+ 3f:39:45:3b:3a:6b:cb:bb:7b:40:54:d1:8b:4b:a1:
+ 72:d2:04:e9:e0:72:1a:93:11:7a:2f:f1:ab:9d:9c:
+ 98:58:ae:2c:ea:77:5f:2f:2e:87:af:b8:6b:e3:e2:
+ e2:3f:d6:3d:e0:96:44:df:11:55:63:52:2f:f4:26:
+ 78:c4:0f:20:4d:0a:c0:68:70:15:86:38:ee:b7:76:
+ 88:ab:18:8f:4f:35:1e:d4:8c:c9:db:7e:3d:44:d4:
+ 36:8c:c1:37:b5:59:5b:87:f9:e9:f1:d4:c5:28:bd:
+ 1d:dc:cc:96:72:d1:7a:a1:a7:20:b5:b8:af:f8:6e:
+ a5:60:7b:2b:8d:1f:ee:f4:2b:d6:69:cd:af:ca:80:
+ 58:29:e8:4c:00:20:8a:49:0a:6e:8e:8c:a8:d1:00:
+ 12:84:b6:c5:e2:95:a2:c0:3b:a4:6b:f0:82:d0:96:
+ 5d:25
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://g2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.geotrust.com/resources/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g1.symcb.com/GeoTrustPCA.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-538
+ X509v3 Subject Key Identifier:
+ DE:CF:5C:50:B7:AE:02:1F:15:17:AA:16:E8:0D:B5:28:9D:6A:5A:F3
+ X509v3 Authority Key Identifier:
+ keyid:2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92
+
+ Signature Algorithm: sha256WithRSAEncryption
+ b4:8e:bd:07:b9:9a:85:ec:3b:67:bd:07:60:61:e6:84:d1:d4:
+ ef:eb:1b:ba:0b:82:4b:95:64:b6:66:53:23:bd:b7:84:dd:e4:
+ 7b:8d:09:da:cf:b2:f5:f1:c3:bf:87:84:be:4e:a6:a8:c2:e7:
+ 12:39:28:34:e0:a4:56:44:40:0c:9f:88:a3:15:d3:e8:d3:5e:
+ e3:1c:04:60:fb:69:36:4f:6a:7e:0c:2a:28:c1:f3:aa:58:0e:
+ 6c:ce:1d:07:c3:4a:c0:9c:8d:c3:74:b1:ae:82:f0:1a:e1:f9:
+ 4e:29:bd:46:de:b7:1d:f9:7d:db:d9:0f:84:cb:92:45:cc:1c:
+ b3:18:f6:a0:cf:71:6f:0c:2e:9b:d2:2d:b3:99:93:83:44:ac:
+ 15:aa:9b:2e:67:ec:4f:88:69:05:56:7b:8b:b2:43:a9:3a:6c:
+ 1c:13:33:25:1b:fd:a8:c8:57:02:fb:1c:e0:d1:bd:3b:56:44:
+ 65:c3:63:f5:1b:ef:ec:30:d9:e3:6e:2e:13:e9:39:08:2a:0c:
+ 72:f3:9a:cc:f6:27:29:84:d3:ef:4c:c7:84:11:65:1f:c6:e3:
+ 81:03:db:87:cc:78:f7:b5:9d:96:3e:6a:7f:bc:11:85:7a:75:
+ e6:41:7d:0d:cf:f9:e5:85:69:25:8f:c7:8d:07:2d:f8:69:0f:
+ cb:41:53:00
+-----BEGIN CERTIFICATE-----
+MIIEbjCCA1agAwIBAgIQboqQ68/wRIpyDQgF0IKlRDANBgkqhkiG9w0BAQsFADBY
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
+R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMzEw
+MzEwMDAwMDBaFw0yMzEwMzAyMzU5NTlaMEcxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMSAwHgYDVQQDExdHZW9UcnVzdCBFViBTU0wgQ0EgLSBH
+NDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANm0BfI4Zw8J53z1Yyrl
+uV6oEa51cdlMhGetiV38KD0qsKXV1OYwCoTU5BjLhTfFRnHrHHtp22VpjDAFPgfh
+bzzBC2HmOET8vIwvTnVX9ZaZfD6HHw+QS3DDPzlFOzpry7t7QFTRi0uhctIE6eBy
+GpMRei/xq52cmFiuLOp3Xy8uh6+4a+Pi4j/WPeCWRN8RVWNSL/QmeMQPIE0KwGhw
+FYY47rd2iKsYj081HtSMydt+PUTUNozBN7VZW4f56fHUxSi9HdzMlnLReqGnILW4
+r/hupWB7K40f7vQr1mnNr8qAWCnoTAAgikkKbo6MqNEAEoS2xeKVosA7pGvwgtCW
+XSUCAwEAAaOCAUMwggE/MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQD
+AgEGMC8GCCsGAQUFBwEBBCMwITAfBggrBgEFBQcwAYYTaHR0cDovL2cyLnN5bWNi
+LmNvbTBHBgNVHSAEQDA+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93
+d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwNAYDVR0fBC0wKzApoCegJYYj
+aHR0cDovL2cxLnN5bWNiLmNvbS9HZW9UcnVzdFBDQS5jcmwwKQYDVR0RBCIwIKQe
+MBwxGjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTM4MB0GA1UdDgQWBBTez1xQt64C
+HxUXqhboDbUonWpa8zAfBgNVHSMEGDAWgBQs1VBBlxWL8I82YVtK+2vZmckzkjAN
+BgkqhkiG9w0BAQsFAAOCAQEAtI69B7mahew7Z70HYGHmhNHU7+sbuguCS5VktmZT
+I723hN3ke40J2s+y9fHDv4eEvk6mqMLnEjkoNOCkVkRADJ+IoxXT6NNe4xwEYPtp
+Nk9qfgwqKMHzqlgObM4dB8NKwJyNw3SxroLwGuH5Tim9Rt63Hfl929kPhMuSRcwc
+sxj2oM9xbwwum9Its5mTg0SsFaqbLmfsT4hpBVZ7i7JDqTpsHBMzJRv9qMhXAvsc
+4NG9O1ZEZcNj9Rvv7DDZ424uE+k5CCoMcvOazPYnKYTT70zHhBFlH8bjgQPbh8x4
+97Wdlj5qf7wRhXp15kF9Dc/55YVpJY/HjQct+GkPy0FTAA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert15[] = {
+ 0x30, 0x82, 0x04, 0x6e, 0x30, 0x82, 0x03, 0x56, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x6e, 0x8a, 0x90, 0xeb, 0xcf, 0xf0, 0x44, 0x8a, 0x72,
+ 0x0d, 0x08, 0x05, 0xd0, 0x82, 0xa5, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x58,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69,
+ 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30,
+ 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32,
+ 0x33, 0x31, 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+ 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x17, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47,
+ 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+ 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd9, 0xb4,
+ 0x05, 0xf2, 0x38, 0x67, 0x0f, 0x09, 0xe7, 0x7c, 0xf5, 0x63, 0x2a, 0xe5,
+ 0xb9, 0x5e, 0xa8, 0x11, 0xae, 0x75, 0x71, 0xd9, 0x4c, 0x84, 0x67, 0xad,
+ 0x89, 0x5d, 0xfc, 0x28, 0x3d, 0x2a, 0xb0, 0xa5, 0xd5, 0xd4, 0xe6, 0x30,
+ 0x0a, 0x84, 0xd4, 0xe4, 0x18, 0xcb, 0x85, 0x37, 0xc5, 0x46, 0x71, 0xeb,
+ 0x1c, 0x7b, 0x69, 0xdb, 0x65, 0x69, 0x8c, 0x30, 0x05, 0x3e, 0x07, 0xe1,
+ 0x6f, 0x3c, 0xc1, 0x0b, 0x61, 0xe6, 0x38, 0x44, 0xfc, 0xbc, 0x8c, 0x2f,
+ 0x4e, 0x75, 0x57, 0xf5, 0x96, 0x99, 0x7c, 0x3e, 0x87, 0x1f, 0x0f, 0x90,
+ 0x4b, 0x70, 0xc3, 0x3f, 0x39, 0x45, 0x3b, 0x3a, 0x6b, 0xcb, 0xbb, 0x7b,
+ 0x40, 0x54, 0xd1, 0x8b, 0x4b, 0xa1, 0x72, 0xd2, 0x04, 0xe9, 0xe0, 0x72,
+ 0x1a, 0x93, 0x11, 0x7a, 0x2f, 0xf1, 0xab, 0x9d, 0x9c, 0x98, 0x58, 0xae,
+ 0x2c, 0xea, 0x77, 0x5f, 0x2f, 0x2e, 0x87, 0xaf, 0xb8, 0x6b, 0xe3, 0xe2,
+ 0xe2, 0x3f, 0xd6, 0x3d, 0xe0, 0x96, 0x44, 0xdf, 0x11, 0x55, 0x63, 0x52,
+ 0x2f, 0xf4, 0x26, 0x78, 0xc4, 0x0f, 0x20, 0x4d, 0x0a, 0xc0, 0x68, 0x70,
+ 0x15, 0x86, 0x38, 0xee, 0xb7, 0x76, 0x88, 0xab, 0x18, 0x8f, 0x4f, 0x35,
+ 0x1e, 0xd4, 0x8c, 0xc9, 0xdb, 0x7e, 0x3d, 0x44, 0xd4, 0x36, 0x8c, 0xc1,
+ 0x37, 0xb5, 0x59, 0x5b, 0x87, 0xf9, 0xe9, 0xf1, 0xd4, 0xc5, 0x28, 0xbd,
+ 0x1d, 0xdc, 0xcc, 0x96, 0x72, 0xd1, 0x7a, 0xa1, 0xa7, 0x20, 0xb5, 0xb8,
+ 0xaf, 0xf8, 0x6e, 0xa5, 0x60, 0x7b, 0x2b, 0x8d, 0x1f, 0xee, 0xf4, 0x2b,
+ 0xd6, 0x69, 0xcd, 0xaf, 0xca, 0x80, 0x58, 0x29, 0xe8, 0x4c, 0x00, 0x20,
+ 0x8a, 0x49, 0x0a, 0x6e, 0x8e, 0x8c, 0xa8, 0xd1, 0x00, 0x12, 0x84, 0xb6,
+ 0xc5, 0xe2, 0x95, 0xa2, 0xc0, 0x3b, 0xa4, 0x6b, 0xf0, 0x82, 0xd0, 0x96,
+ 0x5d, 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x43, 0x30,
+ 0x82, 0x01, 0x3f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30,
+ 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x31, 0x2e, 0x73, 0x79,
+ 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e,
+ 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49,
+ 0x2d, 0x31, 0x2d, 0x35, 0x33, 0x38, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xde, 0xcf, 0x5c, 0x50, 0xb7, 0xae, 0x02,
+ 0x1f, 0x15, 0x17, 0xaa, 0x16, 0xe8, 0x0d, 0xb5, 0x28, 0x9d, 0x6a, 0x5a,
+ 0xf3, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x2c, 0xd5, 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36,
+ 0x61, 0x5b, 0x4a, 0xfb, 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xb4, 0x8e, 0xbd, 0x07, 0xb9, 0x9a,
+ 0x85, 0xec, 0x3b, 0x67, 0xbd, 0x07, 0x60, 0x61, 0xe6, 0x84, 0xd1, 0xd4,
+ 0xef, 0xeb, 0x1b, 0xba, 0x0b, 0x82, 0x4b, 0x95, 0x64, 0xb6, 0x66, 0x53,
+ 0x23, 0xbd, 0xb7, 0x84, 0xdd, 0xe4, 0x7b, 0x8d, 0x09, 0xda, 0xcf, 0xb2,
+ 0xf5, 0xf1, 0xc3, 0xbf, 0x87, 0x84, 0xbe, 0x4e, 0xa6, 0xa8, 0xc2, 0xe7,
+ 0x12, 0x39, 0x28, 0x34, 0xe0, 0xa4, 0x56, 0x44, 0x40, 0x0c, 0x9f, 0x88,
+ 0xa3, 0x15, 0xd3, 0xe8, 0xd3, 0x5e, 0xe3, 0x1c, 0x04, 0x60, 0xfb, 0x69,
+ 0x36, 0x4f, 0x6a, 0x7e, 0x0c, 0x2a, 0x28, 0xc1, 0xf3, 0xaa, 0x58, 0x0e,
+ 0x6c, 0xce, 0x1d, 0x07, 0xc3, 0x4a, 0xc0, 0x9c, 0x8d, 0xc3, 0x74, 0xb1,
+ 0xae, 0x82, 0xf0, 0x1a, 0xe1, 0xf9, 0x4e, 0x29, 0xbd, 0x46, 0xde, 0xb7,
+ 0x1d, 0xf9, 0x7d, 0xdb, 0xd9, 0x0f, 0x84, 0xcb, 0x92, 0x45, 0xcc, 0x1c,
+ 0xb3, 0x18, 0xf6, 0xa0, 0xcf, 0x71, 0x6f, 0x0c, 0x2e, 0x9b, 0xd2, 0x2d,
+ 0xb3, 0x99, 0x93, 0x83, 0x44, 0xac, 0x15, 0xaa, 0x9b, 0x2e, 0x67, 0xec,
+ 0x4f, 0x88, 0x69, 0x05, 0x56, 0x7b, 0x8b, 0xb2, 0x43, 0xa9, 0x3a, 0x6c,
+ 0x1c, 0x13, 0x33, 0x25, 0x1b, 0xfd, 0xa8, 0xc8, 0x57, 0x02, 0xfb, 0x1c,
+ 0xe0, 0xd1, 0xbd, 0x3b, 0x56, 0x44, 0x65, 0xc3, 0x63, 0xf5, 0x1b, 0xef,
+ 0xec, 0x30, 0xd9, 0xe3, 0x6e, 0x2e, 0x13, 0xe9, 0x39, 0x08, 0x2a, 0x0c,
+ 0x72, 0xf3, 0x9a, 0xcc, 0xf6, 0x27, 0x29, 0x84, 0xd3, 0xef, 0x4c, 0xc7,
+ 0x84, 0x11, 0x65, 0x1f, 0xc6, 0xe3, 0x81, 0x03, 0xdb, 0x87, 0xcc, 0x78,
+ 0xf7, 0xb5, 0x9d, 0x96, 0x3e, 0x6a, 0x7f, 0xbc, 0x11, 0x85, 0x7a, 0x75,
+ 0xe6, 0x41, 0x7d, 0x0d, 0xcf, 0xf9, 0xe5, 0x85, 0x69, 0x25, 0x8f, 0xc7,
+ 0x8d, 0x07, 0x2d, 0xf8, 0x69, 0x0f, 0xcb, 0x41, 0x53, 0x00,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146035 (0x23a73)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Jun 11 22:02:59 2014 GMT
+ Not After : May 20 22:02:59 2022 GMT
+ Subject: C=US, O=GeoTrust Inc., OU=Domain Validated SSL, CN=GeoTrust DV SSL CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b3:44:3a:6c:b0:ae:cb:14:f9:8c:19:74:34:5c:
+ a9:69:e3:88:53:77:a5:a7:ff:bd:d1:3c:0d:27:e4:
+ de:ad:7f:bc:d1:90:58:93:d6:a6:da:39:9c:ad:e1:
+ 0e:56:46:ee:95:9e:10:68:4c:9c:2b:f6:6a:3a:8b:
+ 80:81:87:06:57:25:1a:56:52:94:dd:90:eb:67:3b:
+ de:fa:ae:36:68:d3:62:69:f6:6c:82:24:44:4f:87:
+ 5c:98:11:95:64:6b:e8:0c:d1:dd:e6:27:97:ae:cc:
+ e2:91:6a:41:12:b6:ab:e5:cc:6e:cc:23:b8:63:8a:
+ 1f:31:93:2d:06:c4:f7:e8:3d:58:cd:97:08:46:6c:
+ 7b:74:c0:f8:fc:31:3b:a7:7f:d7:8f:b0:c9:15:63:
+ 50:7a:12:4d:f5:12:1e:a3:7e:55:e3:75:b7:ea:1e:
+ ea:31:2c:08:4e:d8:cb:43:74:89:24:bc:d2:0e:1e:
+ f0:db:05:24:f6:8a:bf:10:27:84:41:1a:f6:18:53:
+ ee:91:d0:54:17:d3:7d:3e:7e:b2:7d:a8:bf:db:b9:
+ 21:2a:f0:89:b9:08:6e:5a:b3:5e:ea:82:b8:7e:27:
+ 0b:cc:56:73:81:05:4f:e3:96:2d:71:d5:78:a7:60:
+ c3:d7:ec:aa:39:1a:05:39:82:81:e0:15:2c:35:d1:
+ ee:25
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ AD:65:22:85:90:D0:3B:E3:A1:49:8B:37:F9:F1:0B:1D:5F:17:A0:77
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-699
+ Signature Algorithm: sha256WithRSAEncryption
+ 4e:27:b8:1a:c7:3b:dc:5d:bb:9e:1a:35:23:1e:88:55:90:d1:
+ ec:86:9c:88:b7:e0:1f:67:87:e2:7c:b5:43:03:0e:b6:02:e8:
+ e0:ff:86:84:19:71:e9:f2:4b:f5:9e:2e:2e:5e:db:ab:d6:1c:
+ 4e:c4:3e:b8:2c:78:86:71:10:ae:8d:c5:70:bf:a4:f9:89:e6:
+ b4:ed:e8:4b:ed:7c:09:2a:09:08:06:3e:d4:e1:de:82:92:0c:
+ 34:30:35:0a:c1:60:75:ca:b6:55:6b:aa:00:42:cb:3f:fb:10:
+ e1:fb:85:c1:21:90:72:2b:6e:c0:e8:9d:d9:b5:5a:50:8e:34:
+ 1e:bb:38:a7:3c:31:bd:7a:f2:43:8b:eb:16:ca:ad:9b:de:6b:
+ 1e:f8:4f:b6:5e:4a:29:1f:7a:14:ee:91:f4:94:4f:a4:bd:9b:
+ 76:7a:bc:f1:51:7a:96:a8:81:0e:83:87:3f:8b:ae:5e:32:9b:
+ 34:9e:b2:e7:db:2f:ec:02:a0:e1:fd:51:52:fe:2c:db:36:ba:
+ c1:d6:5e:4b:58:6d:de:c6:e1:e1:fa:9a:03:2c:5b:a2:e1:b3:
+ 9b:f9:36:ec:c1:73:fa:33:12:66:95:e3:69:10:b6:d7:aa:33:
+ fa:f6:9d:41:6d:96:2a:ba:be:83:31:41:7f:0c:0a:d2:69:d6:
+ fc:35:4c:c3
+-----BEGIN CERTIFICATE-----
+MIIEbzCCA1egAwIBAgIDAjpzMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTQwNjExMjIwMjU5WhcNMjIwNTIwMjIwMjU5WjBmMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UECxMURG9tYWluIFZh
+bGlkYXRlZCBTU0wxIDAeBgNVBAMTF0dlb1RydXN0IERWIFNTTCBDQSAtIEczMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs0Q6bLCuyxT5jBl0NFypaeOI
+U3elp/+90TwNJ+TerX+80ZBYk9am2jmcreEOVkbulZ4QaEycK/ZqOouAgYcGVyUa
+VlKU3ZDrZzve+q42aNNiafZsgiRET4dcmBGVZGvoDNHd5ieXrszikWpBErar5cxu
+zCO4Y4ofMZMtBsT36D1YzZcIRmx7dMD4/DE7p3/Xj7DJFWNQehJN9RIeo35V43W3
+6h7qMSwITtjLQ3SJJLzSDh7w2wUk9oq/ECeEQRr2GFPukdBUF9N9Pn6yfai/27kh
+KvCJuQhuWrNe6oK4ficLzFZzgQVP45YtcdV4p2DD1+yqORoFOYKB4BUsNdHuJQID
+AQABo4IBSDCCAUQwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4wHQYD
+VR0OBBYEFK1lIoWQ0DvjoUmLN/nxCx1fF6B3MBIGA1UdEwEB/wQIMAYBAf8CAQAw
+DgYDVR0PAQH/BAQDAgEGMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9nLnN5bWNi
+LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAuBggrBgEFBQcBAQQiMCAwHgYIKwYBBQUH
+MAGGEmh0dHA6Ly9nLnN5bWNkLmNvbTBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYw
+MzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2Vz
+L2NwczApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS02OTkw
+DQYJKoZIhvcNAQELBQADggEBAE4nuBrHO9xdu54aNSMeiFWQ0eyGnIi34B9nh+J8
+tUMDDrYC6OD/hoQZcenyS/WeLi5e26vWHE7EPrgseIZxEK6NxXC/pPmJ5rTt6Evt
+fAkqCQgGPtTh3oKSDDQwNQrBYHXKtlVrqgBCyz/7EOH7hcEhkHIrbsDondm1WlCO
+NB67OKc8Mb168kOL6xbKrZveax74T7ZeSikfehTukfSUT6S9m3Z6vPFRepaogQ6D
+hz+Lrl4ymzSesufbL+wCoOH9UVL+LNs2usHWXktYbd7G4eH6mgMsW6Lhs5v5NuzB
+c/ozEmaV42kQtteqM/r2nUFtliq6voMxQX8MCtJp1vw1TMM=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert16[] = {
+ 0x30, 0x82, 0x04, 0x6f, 0x30, 0x82, 0x03, 0x57, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x73, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30,
+ 0x36, 0x31, 0x31, 0x32, 0x32, 0x30, 0x32, 0x35, 0x39, 0x5a, 0x17, 0x0d,
+ 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x32, 0x32, 0x30, 0x32, 0x35, 0x39,
+ 0x5a, 0x30, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x14, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61,
+ 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31,
+ 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x47, 0x65,
+ 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x44, 0x56, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb3, 0x44, 0x3a, 0x6c, 0xb0, 0xae,
+ 0xcb, 0x14, 0xf9, 0x8c, 0x19, 0x74, 0x34, 0x5c, 0xa9, 0x69, 0xe3, 0x88,
+ 0x53, 0x77, 0xa5, 0xa7, 0xff, 0xbd, 0xd1, 0x3c, 0x0d, 0x27, 0xe4, 0xde,
+ 0xad, 0x7f, 0xbc, 0xd1, 0x90, 0x58, 0x93, 0xd6, 0xa6, 0xda, 0x39, 0x9c,
+ 0xad, 0xe1, 0x0e, 0x56, 0x46, 0xee, 0x95, 0x9e, 0x10, 0x68, 0x4c, 0x9c,
+ 0x2b, 0xf6, 0x6a, 0x3a, 0x8b, 0x80, 0x81, 0x87, 0x06, 0x57, 0x25, 0x1a,
+ 0x56, 0x52, 0x94, 0xdd, 0x90, 0xeb, 0x67, 0x3b, 0xde, 0xfa, 0xae, 0x36,
+ 0x68, 0xd3, 0x62, 0x69, 0xf6, 0x6c, 0x82, 0x24, 0x44, 0x4f, 0x87, 0x5c,
+ 0x98, 0x11, 0x95, 0x64, 0x6b, 0xe8, 0x0c, 0xd1, 0xdd, 0xe6, 0x27, 0x97,
+ 0xae, 0xcc, 0xe2, 0x91, 0x6a, 0x41, 0x12, 0xb6, 0xab, 0xe5, 0xcc, 0x6e,
+ 0xcc, 0x23, 0xb8, 0x63, 0x8a, 0x1f, 0x31, 0x93, 0x2d, 0x06, 0xc4, 0xf7,
+ 0xe8, 0x3d, 0x58, 0xcd, 0x97, 0x08, 0x46, 0x6c, 0x7b, 0x74, 0xc0, 0xf8,
+ 0xfc, 0x31, 0x3b, 0xa7, 0x7f, 0xd7, 0x8f, 0xb0, 0xc9, 0x15, 0x63, 0x50,
+ 0x7a, 0x12, 0x4d, 0xf5, 0x12, 0x1e, 0xa3, 0x7e, 0x55, 0xe3, 0x75, 0xb7,
+ 0xea, 0x1e, 0xea, 0x31, 0x2c, 0x08, 0x4e, 0xd8, 0xcb, 0x43, 0x74, 0x89,
+ 0x24, 0xbc, 0xd2, 0x0e, 0x1e, 0xf0, 0xdb, 0x05, 0x24, 0xf6, 0x8a, 0xbf,
+ 0x10, 0x27, 0x84, 0x41, 0x1a, 0xf6, 0x18, 0x53, 0xee, 0x91, 0xd0, 0x54,
+ 0x17, 0xd3, 0x7d, 0x3e, 0x7e, 0xb2, 0x7d, 0xa8, 0xbf, 0xdb, 0xb9, 0x21,
+ 0x2a, 0xf0, 0x89, 0xb9, 0x08, 0x6e, 0x5a, 0xb3, 0x5e, 0xea, 0x82, 0xb8,
+ 0x7e, 0x27, 0x0b, 0xcc, 0x56, 0x73, 0x81, 0x05, 0x4f, 0xe3, 0x96, 0x2d,
+ 0x71, 0xd5, 0x78, 0xa7, 0x60, 0xc3, 0xd7, 0xec, 0xaa, 0x39, 0x1a, 0x05,
+ 0x39, 0x82, 0x81, 0xe0, 0x15, 0x2c, 0x35, 0xd1, 0xee, 0x25, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x48, 0x30, 0x82, 0x01, 0x44, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11,
+ 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xad, 0x65, 0x22, 0x85, 0x90,
+ 0xd0, 0x3b, 0xe3, 0xa1, 0x49, 0x8b, 0x37, 0xf9, 0xf1, 0x0b, 0x1d, 0x5f,
+ 0x17, 0xa0, 0x77, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e,
+ 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74,
+ 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x2e,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22,
+ 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67,
+ 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4c,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06,
+ 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30,
+ 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04,
+ 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74,
+ 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x36, 0x39, 0x39, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4e, 0x27, 0xb8, 0x1a, 0xc7,
+ 0x3b, 0xdc, 0x5d, 0xbb, 0x9e, 0x1a, 0x35, 0x23, 0x1e, 0x88, 0x55, 0x90,
+ 0xd1, 0xec, 0x86, 0x9c, 0x88, 0xb7, 0xe0, 0x1f, 0x67, 0x87, 0xe2, 0x7c,
+ 0xb5, 0x43, 0x03, 0x0e, 0xb6, 0x02, 0xe8, 0xe0, 0xff, 0x86, 0x84, 0x19,
+ 0x71, 0xe9, 0xf2, 0x4b, 0xf5, 0x9e, 0x2e, 0x2e, 0x5e, 0xdb, 0xab, 0xd6,
+ 0x1c, 0x4e, 0xc4, 0x3e, 0xb8, 0x2c, 0x78, 0x86, 0x71, 0x10, 0xae, 0x8d,
+ 0xc5, 0x70, 0xbf, 0xa4, 0xf9, 0x89, 0xe6, 0xb4, 0xed, 0xe8, 0x4b, 0xed,
+ 0x7c, 0x09, 0x2a, 0x09, 0x08, 0x06, 0x3e, 0xd4, 0xe1, 0xde, 0x82, 0x92,
+ 0x0c, 0x34, 0x30, 0x35, 0x0a, 0xc1, 0x60, 0x75, 0xca, 0xb6, 0x55, 0x6b,
+ 0xaa, 0x00, 0x42, 0xcb, 0x3f, 0xfb, 0x10, 0xe1, 0xfb, 0x85, 0xc1, 0x21,
+ 0x90, 0x72, 0x2b, 0x6e, 0xc0, 0xe8, 0x9d, 0xd9, 0xb5, 0x5a, 0x50, 0x8e,
+ 0x34, 0x1e, 0xbb, 0x38, 0xa7, 0x3c, 0x31, 0xbd, 0x7a, 0xf2, 0x43, 0x8b,
+ 0xeb, 0x16, 0xca, 0xad, 0x9b, 0xde, 0x6b, 0x1e, 0xf8, 0x4f, 0xb6, 0x5e,
+ 0x4a, 0x29, 0x1f, 0x7a, 0x14, 0xee, 0x91, 0xf4, 0x94, 0x4f, 0xa4, 0xbd,
+ 0x9b, 0x76, 0x7a, 0xbc, 0xf1, 0x51, 0x7a, 0x96, 0xa8, 0x81, 0x0e, 0x83,
+ 0x87, 0x3f, 0x8b, 0xae, 0x5e, 0x32, 0x9b, 0x34, 0x9e, 0xb2, 0xe7, 0xdb,
+ 0x2f, 0xec, 0x02, 0xa0, 0xe1, 0xfd, 0x51, 0x52, 0xfe, 0x2c, 0xdb, 0x36,
+ 0xba, 0xc1, 0xd6, 0x5e, 0x4b, 0x58, 0x6d, 0xde, 0xc6, 0xe1, 0xe1, 0xfa,
+ 0x9a, 0x03, 0x2c, 0x5b, 0xa2, 0xe1, 0xb3, 0x9b, 0xf9, 0x36, 0xec, 0xc1,
+ 0x73, 0xfa, 0x33, 0x12, 0x66, 0x95, 0xe3, 0x69, 0x10, 0xb6, 0xd7, 0xaa,
+ 0x33, 0xfa, 0xf6, 0x9d, 0x41, 0x6d, 0x96, 0x2a, 0xba, 0xbe, 0x83, 0x31,
+ 0x41, 0x7f, 0x0c, 0x0a, 0xd2, 0x69, 0xd6, 0xfc, 0x35, 0x4c, 0xc3,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 12037640545166866303 (0xa70e4a4c3482b77f)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority
+ Validity
+ Not Before: Sep 2 00:00:00 2009 GMT
+ Not After : Jun 28 17:39:16 2034 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Services Root Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d5:0c:3a:c4:2a:f9:4e:e2:f5:be:19:97:5f:8e:
+ 88:53:b1:1f:3f:cb:cf:9f:20:13:6d:29:3a:c8:0f:
+ 7d:3c:f7:6b:76:38:63:d9:36:60:a8:9b:5e:5c:00:
+ 80:b2:2f:59:7f:f6:87:f9:25:43:86:e7:69:1b:52:
+ 9a:90:e1:71:e3:d8:2d:0d:4e:6f:f6:c8:49:d9:b6:
+ f3:1a:56:ae:2b:b6:74:14:eb:cf:fb:26:e3:1a:ba:
+ 1d:96:2e:6a:3b:58:94:89:47:56:ff:25:a0:93:70:
+ 53:83:da:84:74:14:c3:67:9e:04:68:3a:df:8e:40:
+ 5a:1d:4a:4e:cf:43:91:3b:e7:56:d6:00:70:cb:52:
+ ee:7b:7d:ae:3a:e7:bc:31:f9:45:f6:c2:60:cf:13:
+ 59:02:2b:80:cc:34:47:df:b9:de:90:65:6d:02:cf:
+ 2c:91:a6:a6:e7:de:85:18:49:7c:66:4e:a3:3a:6d:
+ a9:b5:ee:34:2e:ba:0d:03:b8:33:df:47:eb:b1:6b:
+ 8d:25:d9:9b:ce:81:d1:45:46:32:96:70:87:de:02:
+ 0e:49:43:85:b6:6c:73:bb:64:ea:61:41:ac:c9:d4:
+ 54:df:87:2f:c7:22:b2:26:cc:9f:59:54:68:9f:fc:
+ be:2a:2f:c4:55:1c:75:40:60:17:85:02:55:39:8b:
+ 7f:05
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 9C:5F:00:DF:AA:01:D7:30:2B:38:88:A2:B8:6D:4A:9C:F2:11:91:83
+ X509v3 Authority Key Identifier:
+ keyid:BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7
+
+ Authority Information Access:
+ OCSP - URI:http://o.ss2.us/
+ CA Issuers - URI:http://x.ss2.us/x.cer
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://s.ss2.us/r.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 23:1d:e3:8a:57:ca:7d:e9:17:79:4c:f1:1e:55:fd:cc:53:6e:
+ 3e:47:0f:df:c6:55:f2:b2:04:36:ed:80:1f:53:c4:5d:34:28:
+ 6b:be:c7:55:fc:67:ea:cb:3f:7f:90:b2:33:cd:1b:58:10:82:
+ 02:f8:f8:2f:f5:13:60:d4:05:ce:f1:81:08:c1:dd:a7:75:97:
+ 4f:18:b9:6d:de:f7:93:91:08:ba:7e:40:2c:ed:c1:ea:bb:76:
+ 9e:33:06:77:1d:0d:08:7f:53:dd:1b:64:ab:82:27:f1:69:d5:
+ 4d:5e:ae:f4:a1:c3:75:a7:58:44:2d:f2:3c:70:98:ac:ba:69:
+ b6:95:77:7f:0f:31:5e:2c:fc:a0:87:3a:47:69:f0:79:5f:f4:
+ 14:54:a4:95:5e:11:78:12:60:27:ce:9f:c2:77:ff:23:53:77:
+ 5d:ba:ff:ea:59:e7:db:cf:af:92:96:ef:24:9a:35:10:7a:9c:
+ 91:c6:0e:7d:99:f6:3f:19:df:f5:72:54:e1:15:a9:07:59:7b:
+ 83:bf:52:2e:46:8c:b2:00:64:76:1c:48:d3:d8:79:e8:6e:56:
+ cc:ae:2c:03:90:d7:19:38:99:e4:ca:09:19:5b:ff:07:96:b0:
+ a8:7f:34:49:df:56:a9:f7:b0:5f:ed:33:ed:8c:47:b7:30:03:
+ 5d:f4:03:8c
+-----BEGIN CERTIFICATE-----
+MIIEdTCCA12gAwIBAgIJAKcOSkw0grd/MA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV
+BAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIw
+MAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eTAeFw0wOTA5MDIwMDAwMDBaFw0zNDA2MjgxNzM5MTZaMIGYMQswCQYDVQQGEwJV
+UzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UE
+ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7MDkGA1UEAxMyU3RhcmZp
+ZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDDrEKvlO4vW+GZdfjohTsR8/
+y8+fIBNtKTrID30892t2OGPZNmCom15cAICyL1l/9of5JUOG52kbUpqQ4XHj2C0N
+Tm/2yEnZtvMaVq4rtnQU68/7JuMauh2WLmo7WJSJR1b/JaCTcFOD2oR0FMNnngRo
+Ot+OQFodSk7PQ5E751bWAHDLUu57fa4657wx+UX2wmDPE1kCK4DMNEffud6QZW0C
+zyyRpqbn3oUYSXxmTqM6bam17jQuug0DuDPfR+uxa40l2ZvOgdFFRjKWcIfeAg5J
+Q4W2bHO7ZOphQazJ1FTfhy/HIrImzJ9ZVGif/L4qL8RVHHVAYBeFAlU5i38FAgMB
+AAGjgfAwge0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O
+BBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtV
+rNzXEMIOqYjnME8GCCsGAQUFBwEBBEMwQTAcBggrBgEFBQcwAYYQaHR0cDovL28u
+c3MyLnVzLzAhBggrBgEFBQcwAoYVaHR0cDovL3guc3MyLnVzL3guY2VyMCYGA1Ud
+HwQfMB0wG6AZoBeGFWh0dHA6Ly9zLnNzMi51cy9yLmNybDARBgNVHSAECjAIMAYG
+BFUdIAAwDQYJKoZIhvcNAQELBQADggEBACMd44pXyn3pF3lM8R5V/cxTbj5HD9/G
+VfKyBDbtgB9TxF00KGu+x1X8Z+rLP3+QsjPNG1gQggL4+C/1E2DUBc7xgQjB3ad1
+l08YuW3e95ORCLp+QCztweq7dp4zBncdDQh/U90bZKuCJ/Fp1U1ervShw3WnWEQt
+8jxwmKy6abaVd38PMV4s/KCHOkdp8Hlf9BRUpJVeEXgSYCfOn8J3/yNTd126/+pZ
+59vPr5KW7ySaNRB6nJHGDn2Z9j8Z3/VyVOEVqQdZe4O/Ui5GjLIAZHYcSNPYeehu
+VsyuLAOQ1xk4meTKCRlb/weWsKh/NEnfVqn3sF/tM+2MR7cwA130A4w=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert17[] = {
+ 0x30, 0x82, 0x04, 0x75, 0x30, 0x82, 0x03, 0x5d, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0xa7, 0x0e, 0x4a, 0x4c, 0x34, 0x82, 0xb7, 0x7f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x30, 0x68, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65,
+ 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67,
+ 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30,
+ 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x29, 0x53, 0x74, 0x61, 0x72,
+ 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x39, 0x30, 0x32, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x34, 0x30, 0x36,
+ 0x32, 0x38, 0x31, 0x37, 0x33, 0x39, 0x31, 0x36, 0x5a, 0x30, 0x81, 0x98,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07,
+ 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73,
+ 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64,
+ 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65,
+ 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x3b, 0x30, 0x39, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x32, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69,
+ 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
+ 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xd5, 0x0c, 0x3a, 0xc4, 0x2a, 0xf9, 0x4e,
+ 0xe2, 0xf5, 0xbe, 0x19, 0x97, 0x5f, 0x8e, 0x88, 0x53, 0xb1, 0x1f, 0x3f,
+ 0xcb, 0xcf, 0x9f, 0x20, 0x13, 0x6d, 0x29, 0x3a, 0xc8, 0x0f, 0x7d, 0x3c,
+ 0xf7, 0x6b, 0x76, 0x38, 0x63, 0xd9, 0x36, 0x60, 0xa8, 0x9b, 0x5e, 0x5c,
+ 0x00, 0x80, 0xb2, 0x2f, 0x59, 0x7f, 0xf6, 0x87, 0xf9, 0x25, 0x43, 0x86,
+ 0xe7, 0x69, 0x1b, 0x52, 0x9a, 0x90, 0xe1, 0x71, 0xe3, 0xd8, 0x2d, 0x0d,
+ 0x4e, 0x6f, 0xf6, 0xc8, 0x49, 0xd9, 0xb6, 0xf3, 0x1a, 0x56, 0xae, 0x2b,
+ 0xb6, 0x74, 0x14, 0xeb, 0xcf, 0xfb, 0x26, 0xe3, 0x1a, 0xba, 0x1d, 0x96,
+ 0x2e, 0x6a, 0x3b, 0x58, 0x94, 0x89, 0x47, 0x56, 0xff, 0x25, 0xa0, 0x93,
+ 0x70, 0x53, 0x83, 0xda, 0x84, 0x74, 0x14, 0xc3, 0x67, 0x9e, 0x04, 0x68,
+ 0x3a, 0xdf, 0x8e, 0x40, 0x5a, 0x1d, 0x4a, 0x4e, 0xcf, 0x43, 0x91, 0x3b,
+ 0xe7, 0x56, 0xd6, 0x00, 0x70, 0xcb, 0x52, 0xee, 0x7b, 0x7d, 0xae, 0x3a,
+ 0xe7, 0xbc, 0x31, 0xf9, 0x45, 0xf6, 0xc2, 0x60, 0xcf, 0x13, 0x59, 0x02,
+ 0x2b, 0x80, 0xcc, 0x34, 0x47, 0xdf, 0xb9, 0xde, 0x90, 0x65, 0x6d, 0x02,
+ 0xcf, 0x2c, 0x91, 0xa6, 0xa6, 0xe7, 0xde, 0x85, 0x18, 0x49, 0x7c, 0x66,
+ 0x4e, 0xa3, 0x3a, 0x6d, 0xa9, 0xb5, 0xee, 0x34, 0x2e, 0xba, 0x0d, 0x03,
+ 0xb8, 0x33, 0xdf, 0x47, 0xeb, 0xb1, 0x6b, 0x8d, 0x25, 0xd9, 0x9b, 0xce,
+ 0x81, 0xd1, 0x45, 0x46, 0x32, 0x96, 0x70, 0x87, 0xde, 0x02, 0x0e, 0x49,
+ 0x43, 0x85, 0xb6, 0x6c, 0x73, 0xbb, 0x64, 0xea, 0x61, 0x41, 0xac, 0xc9,
+ 0xd4, 0x54, 0xdf, 0x87, 0x2f, 0xc7, 0x22, 0xb2, 0x26, 0xcc, 0x9f, 0x59,
+ 0x54, 0x68, 0x9f, 0xfc, 0xbe, 0x2a, 0x2f, 0xc4, 0x55, 0x1c, 0x75, 0x40,
+ 0x60, 0x17, 0x85, 0x02, 0x55, 0x39, 0x8b, 0x7f, 0x05, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x81, 0xf0, 0x30, 0x81, 0xed, 0x30, 0x0f, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01,
+ 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x9c, 0x5f, 0x00, 0xdf, 0xaa, 0x01, 0xd7, 0x30,
+ 0x2b, 0x38, 0x88, 0xa2, 0xb8, 0x6d, 0x4a, 0x9c, 0xf2, 0x11, 0x91, 0x83,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0xbf, 0x5f, 0xb7, 0xd1, 0xce, 0xdd, 0x1f, 0x86, 0xf4, 0x5b, 0x55,
+ 0xac, 0xdc, 0xd7, 0x10, 0xc2, 0x0e, 0xa9, 0x88, 0xe7, 0x30, 0x4f, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x43, 0x30,
+ 0x41, 0x30, 0x1c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x10, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x2e,
+ 0x73, 0x73, 0x32, 0x2e, 0x75, 0x73, 0x2f, 0x30, 0x21, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x15, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x78, 0x2e, 0x73, 0x73, 0x32, 0x2e, 0x75, 0x73,
+ 0x2f, 0x78, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x26, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x1f, 0x30, 0x1d, 0x30, 0x1b, 0xa0, 0x19, 0xa0, 0x17, 0x86,
+ 0x15, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x2e, 0x73, 0x73,
+ 0x32, 0x2e, 0x75, 0x73, 0x2f, 0x72, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08, 0x30, 0x06, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x23, 0x1d, 0xe3, 0x8a, 0x57, 0xca, 0x7d, 0xe9, 0x17, 0x79, 0x4c,
+ 0xf1, 0x1e, 0x55, 0xfd, 0xcc, 0x53, 0x6e, 0x3e, 0x47, 0x0f, 0xdf, 0xc6,
+ 0x55, 0xf2, 0xb2, 0x04, 0x36, 0xed, 0x80, 0x1f, 0x53, 0xc4, 0x5d, 0x34,
+ 0x28, 0x6b, 0xbe, 0xc7, 0x55, 0xfc, 0x67, 0xea, 0xcb, 0x3f, 0x7f, 0x90,
+ 0xb2, 0x33, 0xcd, 0x1b, 0x58, 0x10, 0x82, 0x02, 0xf8, 0xf8, 0x2f, 0xf5,
+ 0x13, 0x60, 0xd4, 0x05, 0xce, 0xf1, 0x81, 0x08, 0xc1, 0xdd, 0xa7, 0x75,
+ 0x97, 0x4f, 0x18, 0xb9, 0x6d, 0xde, 0xf7, 0x93, 0x91, 0x08, 0xba, 0x7e,
+ 0x40, 0x2c, 0xed, 0xc1, 0xea, 0xbb, 0x76, 0x9e, 0x33, 0x06, 0x77, 0x1d,
+ 0x0d, 0x08, 0x7f, 0x53, 0xdd, 0x1b, 0x64, 0xab, 0x82, 0x27, 0xf1, 0x69,
+ 0xd5, 0x4d, 0x5e, 0xae, 0xf4, 0xa1, 0xc3, 0x75, 0xa7, 0x58, 0x44, 0x2d,
+ 0xf2, 0x3c, 0x70, 0x98, 0xac, 0xba, 0x69, 0xb6, 0x95, 0x77, 0x7f, 0x0f,
+ 0x31, 0x5e, 0x2c, 0xfc, 0xa0, 0x87, 0x3a, 0x47, 0x69, 0xf0, 0x79, 0x5f,
+ 0xf4, 0x14, 0x54, 0xa4, 0x95, 0x5e, 0x11, 0x78, 0x12, 0x60, 0x27, 0xce,
+ 0x9f, 0xc2, 0x77, 0xff, 0x23, 0x53, 0x77, 0x5d, 0xba, 0xff, 0xea, 0x59,
+ 0xe7, 0xdb, 0xcf, 0xaf, 0x92, 0x96, 0xef, 0x24, 0x9a, 0x35, 0x10, 0x7a,
+ 0x9c, 0x91, 0xc6, 0x0e, 0x7d, 0x99, 0xf6, 0x3f, 0x19, 0xdf, 0xf5, 0x72,
+ 0x54, 0xe1, 0x15, 0xa9, 0x07, 0x59, 0x7b, 0x83, 0xbf, 0x52, 0x2e, 0x46,
+ 0x8c, 0xb2, 0x00, 0x64, 0x76, 0x1c, 0x48, 0xd3, 0xd8, 0x79, 0xe8, 0x6e,
+ 0x56, 0xcc, 0xae, 0x2c, 0x03, 0x90, 0xd7, 0x19, 0x38, 0x99, 0xe4, 0xca,
+ 0x09, 0x19, 0x5b, 0xff, 0x07, 0x96, 0xb0, 0xa8, 0x7f, 0x34, 0x49, 0xdf,
+ 0x56, 0xa9, 0xf7, 0xb0, 0x5f, 0xed, 0x33, 0xed, 0x8c, 0x47, 0xb7, 0x30,
+ 0x03, 0x5d, 0xf4, 0x03, 0x8c,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120038006 (0x727a276)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Feb 27 18:09:27 2014 GMT
+ Not After : Jun 9 17:07:29 2020 GMT
+ Subject: C=JP, O=Cybertrust Japan Co., Ltd., CN=Cybertrust Japan Public CA G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:94:56:a3:45:44:54:aa:60:64:bf:b8:57:9f:4e:
+ db:d4:79:68:5f:13:05:f4:3f:cd:25:dd:3c:5e:58:
+ 77:1c:9d:e6:9f:e3:32:49:ef:02:3a:34:53:8d:52:
+ e5:e3:39:66:1f:e7:33:61:b6:27:c6:24:55:50:27:
+ 02:65:f0:b0:8c:41:8d:30:5e:47:5b:82:6f:c7:9c:
+ a3:28:43:6d:58:7b:c8:15:98:4e:25:6f:cb:76:27:
+ 5b:0b:2c:2c:b5:98:23:e7:8b:7c:fd:77:1a:c4:52:
+ ba:5d:19:ee:78:21:4d:21:9a:d9:12:7c:33:15:6b:
+ 1a:c9:81:ea:da:da:57:b7:d5:2f:ce:1f:4b:fc:b4:
+ 33:e0:a0:c9:94:27:bb:27:40:b6:90:db:ac:9e:75:
+ a6:11:2b:49:19:2d:c3:c2:43:07:09:bb:3d:6e:88:
+ a3:e3:8a:c5:d2:86:f6:65:5b:34:c3:9f:4c:02:e5:
+ 09:ba:2c:c6:76:66:eb:d1:76:25:f4:30:13:fb:58:
+ 60:a8:58:e3:51:6f:4b:08:04:61:8d:ac:a9:30:2f:
+ 52:41:a3:22:c1:33:59:ab:7b:59:f9:93:67:4b:c9:
+ 89:75:52:ef:29:49:34:93:1c:9c:93:73:9c:19:ce:
+ 5c:18:cd:4c:09:27:c1:3f:f5:49:ec:f4:e2:df:4b:
+ af:8f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.omniroot.com/baltimoreroot
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 73:A8:08:53:29:B8:15:FB:99:80:E5:C5:37:D8:F8:39:7B:A4:13:06
+ Signature Algorithm: sha256WithRSAEncryption
+ 68:df:fe:72:54:4e:1b:fb:5c:6e:5a:45:46:cf:42:be:b2:02:
+ 9c:9d:90:6a:09:2e:b7:36:64:24:b6:b1:e2:48:67:ce:17:46:
+ 9b:23:75:78:11:f6:c6:09:38:42:62:96:97:30:7b:51:77:df:
+ 33:b5:00:51:29:d5:24:fe:b7:98:a2:ac:6c:a1:13:7f:ca:f3:
+ b7:a6:52:c2:16:0d:ec:3a:bf:a3:37:77:4f:ae:7b:55:1d:46:
+ e9:10:da:c3:b4:05:5c:5b:f6:48:21:00:89:f4:bb:38:8e:1e:
+ 33:f3:49:97:81:31:6c:16:74:08:91:17:c0:d3:25:b3:bc:c1:
+ 15:b5:a4:cd:84:4d:b9:c8:eb:c5:59:42:10:14:25:79:f8:db:
+ b6:d0:e6:d3:a0:14:7c:17:1c:20:1e:ed:99:90:65:c0:41:71:
+ c3:ab:3f:29:41:67:f9:e2:d1:98:e3:f8:df:3a:b8:ca:a3:6f:
+ 68:8b:6c:9f:6e:88:7c:9d:41:5c:ba:cb:19:05:83:9c:99:f4:
+ 1a:d2:24:69:57:0a:0f:7a:c3:1b:2c:4b:06:d3:2a:97:7e:07:
+ b0:f9:20:5a:b5:92:4b:5b:a8:eb:eb:36:33:47:36:da:72:9c:
+ bf:68:45:81:31:be:d2:fd:3b:e9:72:d5:70:dd:a6:de:5f:0d:
+ b6:5e:00:49
+-----BEGIN CERTIFICATE-----
+MIIEeTCCA2GgAwIBAgIEByeidjANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDIyNzE4MDkyN1oX
+DTIwMDYwOTE3MDcyOVowWjELMAkGA1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1
+c3QgSmFwYW4gQ28uLCBMdGQuMSYwJAYDVQQDEx1DeWJlcnRydXN0IEphcGFuIFB1
+YmxpYyBDQSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJRWo0VE
+VKpgZL+4V59O29R5aF8TBfQ/zSXdPF5Ydxyd5p/jMknvAjo0U41S5eM5Zh/nM2G2
+J8YkVVAnAmXwsIxBjTBeR1uCb8ecoyhDbVh7yBWYTiVvy3YnWwssLLWYI+eLfP13
+GsRSul0Z7nghTSGa2RJ8MxVrGsmB6traV7fVL84fS/y0M+CgyZQnuydAtpDbrJ51
+phErSRktw8JDBwm7PW6Io+OKxdKG9mVbNMOfTALlCbosxnZm69F2JfQwE/tYYKhY
+41FvSwgEYY2sqTAvUkGjIsEzWat7WfmTZ0vJiXVS7ylJNJMcnJNznBnOXBjNTAkn
+wT/1Sez04t9Lr48CAwEAAaOCAUUwggFBMBIGA1UdEwEB/wQIMAYBAf8CAQAwUwYD
+VR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEFBQcCARYtaHR0cDovL2N5YmVy
+dHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnkuY2ZtMEIGCCsGAQUFBwEBBDYw
+NDAyBggrBgEFBQcwAYYmaHR0cDovL29jc3Aub21uaXJvb3QuY29tL2JhbHRpbW9y
+ZXJvb3QwDgYDVR0PAQH/BAQDAgEGMB8GA1UdIwQYMBaAFOWdWTCCR1jMrPoIVDaG
+ezq1BE3wMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jZHAxLnB1YmxpYy10cnVz
+dC5jb20vQ1JML09tbmlyb290MjAyNS5jcmwwHQYDVR0OBBYEFHOoCFMpuBX7mYDl
+xTfY+Dl7pBMGMA0GCSqGSIb3DQEBCwUAA4IBAQBo3/5yVE4b+1xuWkVGz0K+sgKc
+nZBqCS63NmQktrHiSGfOF0abI3V4EfbGCThCYpaXMHtRd98ztQBRKdUk/reYoqxs
+oRN/yvO3plLCFg3sOr+jN3dPrntVHUbpENrDtAVcW/ZIIQCJ9Ls4jh4z80mXgTFs
+FnQIkRfA0yWzvMEVtaTNhE25yOvFWUIQFCV5+Nu20ObToBR8FxwgHu2ZkGXAQXHD
+qz8pQWf54tGY4/jfOrjKo29oi2yfboh8nUFcussZBYOcmfQa0iRpVwoPesMbLEsG
+0yqXfgew+SBatZJLW6jr6zYzRzbacpy/aEWBMb7S/TvpctVw3abeXw22XgBJ
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert18[] = {
+ 0x30, 0x82, 0x04, 0x79, 0x30, 0x82, 0x03, 0x61, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0xa2, 0x76, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34,
+ 0x30, 0x32, 0x32, 0x37, 0x31, 0x38, 0x30, 0x39, 0x32, 0x37, 0x5a, 0x17,
+ 0x0d, 0x32, 0x30, 0x30, 0x36, 0x30, 0x39, 0x31, 0x37, 0x30, 0x37, 0x32,
+ 0x39, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x1a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x20, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x20, 0x43, 0x6f, 0x2e,
+ 0x2c, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x1d, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x20, 0x50, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x20, 0x43, 0x41, 0x20, 0x47, 0x33, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
+ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x94, 0x56, 0xa3, 0x45, 0x44,
+ 0x54, 0xaa, 0x60, 0x64, 0xbf, 0xb8, 0x57, 0x9f, 0x4e, 0xdb, 0xd4, 0x79,
+ 0x68, 0x5f, 0x13, 0x05, 0xf4, 0x3f, 0xcd, 0x25, 0xdd, 0x3c, 0x5e, 0x58,
+ 0x77, 0x1c, 0x9d, 0xe6, 0x9f, 0xe3, 0x32, 0x49, 0xef, 0x02, 0x3a, 0x34,
+ 0x53, 0x8d, 0x52, 0xe5, 0xe3, 0x39, 0x66, 0x1f, 0xe7, 0x33, 0x61, 0xb6,
+ 0x27, 0xc6, 0x24, 0x55, 0x50, 0x27, 0x02, 0x65, 0xf0, 0xb0, 0x8c, 0x41,
+ 0x8d, 0x30, 0x5e, 0x47, 0x5b, 0x82, 0x6f, 0xc7, 0x9c, 0xa3, 0x28, 0x43,
+ 0x6d, 0x58, 0x7b, 0xc8, 0x15, 0x98, 0x4e, 0x25, 0x6f, 0xcb, 0x76, 0x27,
+ 0x5b, 0x0b, 0x2c, 0x2c, 0xb5, 0x98, 0x23, 0xe7, 0x8b, 0x7c, 0xfd, 0x77,
+ 0x1a, 0xc4, 0x52, 0xba, 0x5d, 0x19, 0xee, 0x78, 0x21, 0x4d, 0x21, 0x9a,
+ 0xd9, 0x12, 0x7c, 0x33, 0x15, 0x6b, 0x1a, 0xc9, 0x81, 0xea, 0xda, 0xda,
+ 0x57, 0xb7, 0xd5, 0x2f, 0xce, 0x1f, 0x4b, 0xfc, 0xb4, 0x33, 0xe0, 0xa0,
+ 0xc9, 0x94, 0x27, 0xbb, 0x27, 0x40, 0xb6, 0x90, 0xdb, 0xac, 0x9e, 0x75,
+ 0xa6, 0x11, 0x2b, 0x49, 0x19, 0x2d, 0xc3, 0xc2, 0x43, 0x07, 0x09, 0xbb,
+ 0x3d, 0x6e, 0x88, 0xa3, 0xe3, 0x8a, 0xc5, 0xd2, 0x86, 0xf6, 0x65, 0x5b,
+ 0x34, 0xc3, 0x9f, 0x4c, 0x02, 0xe5, 0x09, 0xba, 0x2c, 0xc6, 0x76, 0x66,
+ 0xeb, 0xd1, 0x76, 0x25, 0xf4, 0x30, 0x13, 0xfb, 0x58, 0x60, 0xa8, 0x58,
+ 0xe3, 0x51, 0x6f, 0x4b, 0x08, 0x04, 0x61, 0x8d, 0xac, 0xa9, 0x30, 0x2f,
+ 0x52, 0x41, 0xa3, 0x22, 0xc1, 0x33, 0x59, 0xab, 0x7b, 0x59, 0xf9, 0x93,
+ 0x67, 0x4b, 0xc9, 0x89, 0x75, 0x52, 0xef, 0x29, 0x49, 0x34, 0x93, 0x1c,
+ 0x9c, 0x93, 0x73, 0x9c, 0x19, 0xce, 0x5c, 0x18, 0xcd, 0x4c, 0x09, 0x27,
+ 0xc1, 0x3f, 0xf5, 0x49, 0xec, 0xf4, 0xe2, 0xdf, 0x4b, 0xaf, 0x8f, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x45, 0x30, 0x82, 0x01, 0x41,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x53, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b,
+ 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d, 0x30, 0x42, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x36, 0x30,
+ 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72,
+ 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d,
+ 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86,
+ 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86,
+ 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31,
+ 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x73, 0xa8, 0x08, 0x53, 0x29, 0xb8, 0x15, 0xfb, 0x99, 0x80, 0xe5,
+ 0xc5, 0x37, 0xd8, 0xf8, 0x39, 0x7b, 0xa4, 0x13, 0x06, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x68, 0xdf, 0xfe, 0x72, 0x54, 0x4e, 0x1b,
+ 0xfb, 0x5c, 0x6e, 0x5a, 0x45, 0x46, 0xcf, 0x42, 0xbe, 0xb2, 0x02, 0x9c,
+ 0x9d, 0x90, 0x6a, 0x09, 0x2e, 0xb7, 0x36, 0x64, 0x24, 0xb6, 0xb1, 0xe2,
+ 0x48, 0x67, 0xce, 0x17, 0x46, 0x9b, 0x23, 0x75, 0x78, 0x11, 0xf6, 0xc6,
+ 0x09, 0x38, 0x42, 0x62, 0x96, 0x97, 0x30, 0x7b, 0x51, 0x77, 0xdf, 0x33,
+ 0xb5, 0x00, 0x51, 0x29, 0xd5, 0x24, 0xfe, 0xb7, 0x98, 0xa2, 0xac, 0x6c,
+ 0xa1, 0x13, 0x7f, 0xca, 0xf3, 0xb7, 0xa6, 0x52, 0xc2, 0x16, 0x0d, 0xec,
+ 0x3a, 0xbf, 0xa3, 0x37, 0x77, 0x4f, 0xae, 0x7b, 0x55, 0x1d, 0x46, 0xe9,
+ 0x10, 0xda, 0xc3, 0xb4, 0x05, 0x5c, 0x5b, 0xf6, 0x48, 0x21, 0x00, 0x89,
+ 0xf4, 0xbb, 0x38, 0x8e, 0x1e, 0x33, 0xf3, 0x49, 0x97, 0x81, 0x31, 0x6c,
+ 0x16, 0x74, 0x08, 0x91, 0x17, 0xc0, 0xd3, 0x25, 0xb3, 0xbc, 0xc1, 0x15,
+ 0xb5, 0xa4, 0xcd, 0x84, 0x4d, 0xb9, 0xc8, 0xeb, 0xc5, 0x59, 0x42, 0x10,
+ 0x14, 0x25, 0x79, 0xf8, 0xdb, 0xb6, 0xd0, 0xe6, 0xd3, 0xa0, 0x14, 0x7c,
+ 0x17, 0x1c, 0x20, 0x1e, 0xed, 0x99, 0x90, 0x65, 0xc0, 0x41, 0x71, 0xc3,
+ 0xab, 0x3f, 0x29, 0x41, 0x67, 0xf9, 0xe2, 0xd1, 0x98, 0xe3, 0xf8, 0xdf,
+ 0x3a, 0xb8, 0xca, 0xa3, 0x6f, 0x68, 0x8b, 0x6c, 0x9f, 0x6e, 0x88, 0x7c,
+ 0x9d, 0x41, 0x5c, 0xba, 0xcb, 0x19, 0x05, 0x83, 0x9c, 0x99, 0xf4, 0x1a,
+ 0xd2, 0x24, 0x69, 0x57, 0x0a, 0x0f, 0x7a, 0xc3, 0x1b, 0x2c, 0x4b, 0x06,
+ 0xd3, 0x2a, 0x97, 0x7e, 0x07, 0xb0, 0xf9, 0x20, 0x5a, 0xb5, 0x92, 0x4b,
+ 0x5b, 0xa8, 0xeb, 0xeb, 0x36, 0x33, 0x47, 0x36, 0xda, 0x72, 0x9c, 0xbf,
+ 0x68, 0x45, 0x81, 0x31, 0xbe, 0xd2, 0xfd, 0x3b, 0xe9, 0x72, 0xd5, 0x70,
+ 0xdd, 0xa6, 0xde, 0x5f, 0x0d, 0xb6, 0x5e, 0x00, 0x49,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1828629 (0x1be715)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority
+ Validity
+ Not Before: Jan 1 07:00:00 2014 GMT
+ Not After : May 30 07:00:00 2031 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bf:71:62:08:f1:fa:59:34:f7:1b:c9:18:a3:f7:
+ 80:49:58:e9:22:83:13:a6:c5:20:43:01:3b:84:f1:
+ e6:85:49:9f:27:ea:f6:84:1b:4e:a0:b4:db:70:98:
+ c7:32:01:b1:05:3e:07:4e:ee:f4:fa:4f:2f:59:30:
+ 22:e7:ab:19:56:6b:e2:80:07:fc:f3:16:75:80:39:
+ 51:7b:e5:f9:35:b6:74:4e:a9:8d:82:13:e4:b6:3f:
+ a9:03:83:fa:a2:be:8a:15:6a:7f:de:0b:c3:b6:19:
+ 14:05:ca:ea:c3:a8:04:94:3b:46:7c:32:0d:f3:00:
+ 66:22:c8:8d:69:6d:36:8c:11:18:b7:d3:b2:1c:60:
+ b4:38:fa:02:8c:ce:d3:dd:46:07:de:0a:3e:eb:5d:
+ 7c:c8:7c:fb:b0:2b:53:a4:92:62:69:51:25:05:61:
+ 1a:44:81:8c:2c:a9:43:96:23:df:ac:3a:81:9a:0e:
+ 29:c5:1c:a9:e9:5d:1e:b6:9e:9e:30:0a:39:ce:f1:
+ 88:80:fb:4b:5d:cc:32:ec:85:62:43:25:34:02:56:
+ 27:01:91:b4:3b:70:2a:3f:6e:b1:e8:9c:88:01:7d:
+ 9f:d4:f9:db:53:6d:60:9d:bf:2c:e7:58:ab:b8:5f:
+ 46:fc:ce:c4:1b:03:3c:09:eb:49:31:5c:69:46:b3:
+ e0:47
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE
+ X509v3 Authority Key Identifier:
+ keyid:D2:C4:B0:D2:91:D4:4C:11:71:B3:61:CB:3D:A1:FE:DD:A8:6A:D4:E3
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.godaddy.com/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.godaddy.com/gdroot.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://certs.godaddy.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 59:0b:53:bd:92:86:11:a7:24:7b:ed:5b:31:cf:1d:1f:6c:70:
+ c5:b8:6e:be:4e:bb:f6:be:97:50:e1:30:7f:ba:28:5c:62:94:
+ c2:e3:7e:33:f7:fb:42:76:85:db:95:1c:8c:22:58:75:09:0c:
+ 88:65:67:39:0a:16:09:c5:a0:38:97:a4:c5:23:93:3f:b4:18:
+ a6:01:06:44:91:e3:a7:69:27:b4:5a:25:7f:3a:b7:32:cd:dd:
+ 84:ff:2a:38:29:33:a4:dd:67:b2:85:fe:a1:88:20:1c:50:89:
+ c8:dc:2a:f6:42:03:37:4c:e6:88:df:d5:af:24:f2:b1:c3:df:
+ cc:b5:ec:e0:99:5e:b7:49:54:20:3c:94:18:0c:c7:1c:52:18:
+ 49:a4:6d:e1:b3:58:0b:c9:d8:ec:d9:ae:1c:32:8e:28:70:0d:
+ e2:fe:a6:17:9e:84:0f:bd:57:70:b3:5a:e9:1f:a0:86:53:bb:
+ ef:7c:ff:69:0b:e0:48:c3:b7:93:0b:c8:0a:54:c4:ac:5d:14:
+ 67:37:6c:ca:a5:2f:31:08:37:aa:6e:6f:8c:bc:9b:e2:57:5d:
+ 24:81:af:97:97:9c:84:ad:6c:ac:37:4c:66:f3:61:91:11:20:
+ e4:be:30:9f:7a:a4:29:09:b0:e1:34:5f:64:77:18:40:51:df:
+ 8c:30:a6:af
+-----BEGIN CERTIFICATE-----
+MIIEfTCCA2WgAwIBAgIDG+cVMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVT
+MSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIEluYy4xMTAvBgNVBAsTKEdv
+IERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMTAx
+MDcwMDAwWhcNMzEwNTMwMDcwMDAwWjCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHku
+Y29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1
+dGhvcml0eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3Fi
+CPH6WTT3G8kYo/eASVjpIoMTpsUgQwE7hPHmhUmfJ+r2hBtOoLTbcJjHMgGxBT4H
+Tu70+k8vWTAi56sZVmvigAf88xZ1gDlRe+X5NbZ0TqmNghPktj+pA4P6or6KFWp/
+3gvDthkUBcrqw6gElDtGfDIN8wBmIsiNaW02jBEYt9OyHGC0OPoCjM7T3UYH3go+
+6118yHz7sCtTpJJiaVElBWEaRIGMLKlDliPfrDqBmg4pxRyp6V0etp6eMAo5zvGI
+gPtLXcwy7IViQyU0AlYnAZG0O3AqP26x6JyIAX2f1PnbU21gnb8s51iruF9G/M7E
+GwM8CetJMVxpRrPgRwIDAQABo4IBFzCCARMwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9BUFuIMGU2g/eMB8GA1Ud
+IwQYMBaAFNLEsNKR1EwRcbNhyz2h/t2oatTjMDQGCCsGAQUFBwEBBCgwJjAkBggr
+BgEFBQcwAYYYaHR0cDovL29jc3AuZ29kYWRkeS5jb20vMDIGA1UdHwQrMCkwJ6Al
+oCOGIWh0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Ryb290LmNybDBGBgNVHSAEPzA9
+MDsGBFUdIAAwMzAxBggrBgEFBQcCARYlaHR0cHM6Ly9jZXJ0cy5nb2RhZGR5LmNv
+bS9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAWQtTvZKGEacke+1bMc8d
+H2xwxbhuvk679r6XUOEwf7ooXGKUwuN+M/f7QnaF25UcjCJYdQkMiGVnOQoWCcWg
+OJekxSOTP7QYpgEGRJHjp2kntFolfzq3Ms3dhP8qOCkzpN1nsoX+oYggHFCJyNwq
+9kIDN0zmiN/VryTyscPfzLXs4Jlet0lUIDyUGAzHHFIYSaRt4bNYC8nY7NmuHDKO
+KHAN4v6mF56ED71XcLNa6R+ghlO773z/aQvgSMO3kwvIClTErF0UZzdsyqUvMQg3
+qm5vjLyb4lddJIGvl5echK1srDdMZvNhkREg5L4wn3qkKQmw4TRfZHcYQFHfjDCm
+rw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert19[] = {
+ 0x30, 0x82, 0x04, 0x7d, 0x30, 0x82, 0x03, 0x65, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x1b, 0xe7, 0x15, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x63, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x54,
+ 0x68, 0x65, 0x20, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20,
+ 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x47, 0x6f,
+ 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73,
+ 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x31, 0x30, 0x31,
+ 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30,
+ 0x35, 0x33, 0x30, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81,
+ 0x83, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74,
+ 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30,
+ 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44,
+ 0x61, 0x64, 0x64, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbf, 0x71, 0x62,
+ 0x08, 0xf1, 0xfa, 0x59, 0x34, 0xf7, 0x1b, 0xc9, 0x18, 0xa3, 0xf7, 0x80,
+ 0x49, 0x58, 0xe9, 0x22, 0x83, 0x13, 0xa6, 0xc5, 0x20, 0x43, 0x01, 0x3b,
+ 0x84, 0xf1, 0xe6, 0x85, 0x49, 0x9f, 0x27, 0xea, 0xf6, 0x84, 0x1b, 0x4e,
+ 0xa0, 0xb4, 0xdb, 0x70, 0x98, 0xc7, 0x32, 0x01, 0xb1, 0x05, 0x3e, 0x07,
+ 0x4e, 0xee, 0xf4, 0xfa, 0x4f, 0x2f, 0x59, 0x30, 0x22, 0xe7, 0xab, 0x19,
+ 0x56, 0x6b, 0xe2, 0x80, 0x07, 0xfc, 0xf3, 0x16, 0x75, 0x80, 0x39, 0x51,
+ 0x7b, 0xe5, 0xf9, 0x35, 0xb6, 0x74, 0x4e, 0xa9, 0x8d, 0x82, 0x13, 0xe4,
+ 0xb6, 0x3f, 0xa9, 0x03, 0x83, 0xfa, 0xa2, 0xbe, 0x8a, 0x15, 0x6a, 0x7f,
+ 0xde, 0x0b, 0xc3, 0xb6, 0x19, 0x14, 0x05, 0xca, 0xea, 0xc3, 0xa8, 0x04,
+ 0x94, 0x3b, 0x46, 0x7c, 0x32, 0x0d, 0xf3, 0x00, 0x66, 0x22, 0xc8, 0x8d,
+ 0x69, 0x6d, 0x36, 0x8c, 0x11, 0x18, 0xb7, 0xd3, 0xb2, 0x1c, 0x60, 0xb4,
+ 0x38, 0xfa, 0x02, 0x8c, 0xce, 0xd3, 0xdd, 0x46, 0x07, 0xde, 0x0a, 0x3e,
+ 0xeb, 0x5d, 0x7c, 0xc8, 0x7c, 0xfb, 0xb0, 0x2b, 0x53, 0xa4, 0x92, 0x62,
+ 0x69, 0x51, 0x25, 0x05, 0x61, 0x1a, 0x44, 0x81, 0x8c, 0x2c, 0xa9, 0x43,
+ 0x96, 0x23, 0xdf, 0xac, 0x3a, 0x81, 0x9a, 0x0e, 0x29, 0xc5, 0x1c, 0xa9,
+ 0xe9, 0x5d, 0x1e, 0xb6, 0x9e, 0x9e, 0x30, 0x0a, 0x39, 0xce, 0xf1, 0x88,
+ 0x80, 0xfb, 0x4b, 0x5d, 0xcc, 0x32, 0xec, 0x85, 0x62, 0x43, 0x25, 0x34,
+ 0x02, 0x56, 0x27, 0x01, 0x91, 0xb4, 0x3b, 0x70, 0x2a, 0x3f, 0x6e, 0xb1,
+ 0xe8, 0x9c, 0x88, 0x01, 0x7d, 0x9f, 0xd4, 0xf9, 0xdb, 0x53, 0x6d, 0x60,
+ 0x9d, 0xbf, 0x2c, 0xe7, 0x58, 0xab, 0xb8, 0x5f, 0x46, 0xfc, 0xce, 0xc4,
+ 0x1b, 0x03, 0x3c, 0x09, 0xeb, 0x49, 0x31, 0x5c, 0x69, 0x46, 0xb3, 0xe0,
+ 0x47, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x17, 0x30, 0x82,
+ 0x01, 0x13, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3a, 0x9a,
+ 0x85, 0x07, 0x10, 0x67, 0x28, 0xb6, 0xef, 0xf6, 0xbd, 0x05, 0x41, 0x6e,
+ 0x20, 0xc1, 0x94, 0xda, 0x0f, 0xde, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd2, 0xc4, 0xb0, 0xd2, 0x91,
+ 0xd4, 0x4c, 0x11, 0x71, 0xb3, 0x61, 0xcb, 0x3d, 0xa1, 0xfe, 0xdd, 0xa8,
+ 0x6a, 0xd4, 0xe3, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
+ 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x32, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25,
+ 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x67, 0x64, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d,
+ 0x30, 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74,
+ 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
+ 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x59, 0x0b, 0x53,
+ 0xbd, 0x92, 0x86, 0x11, 0xa7, 0x24, 0x7b, 0xed, 0x5b, 0x31, 0xcf, 0x1d,
+ 0x1f, 0x6c, 0x70, 0xc5, 0xb8, 0x6e, 0xbe, 0x4e, 0xbb, 0xf6, 0xbe, 0x97,
+ 0x50, 0xe1, 0x30, 0x7f, 0xba, 0x28, 0x5c, 0x62, 0x94, 0xc2, 0xe3, 0x7e,
+ 0x33, 0xf7, 0xfb, 0x42, 0x76, 0x85, 0xdb, 0x95, 0x1c, 0x8c, 0x22, 0x58,
+ 0x75, 0x09, 0x0c, 0x88, 0x65, 0x67, 0x39, 0x0a, 0x16, 0x09, 0xc5, 0xa0,
+ 0x38, 0x97, 0xa4, 0xc5, 0x23, 0x93, 0x3f, 0xb4, 0x18, 0xa6, 0x01, 0x06,
+ 0x44, 0x91, 0xe3, 0xa7, 0x69, 0x27, 0xb4, 0x5a, 0x25, 0x7f, 0x3a, 0xb7,
+ 0x32, 0xcd, 0xdd, 0x84, 0xff, 0x2a, 0x38, 0x29, 0x33, 0xa4, 0xdd, 0x67,
+ 0xb2, 0x85, 0xfe, 0xa1, 0x88, 0x20, 0x1c, 0x50, 0x89, 0xc8, 0xdc, 0x2a,
+ 0xf6, 0x42, 0x03, 0x37, 0x4c, 0xe6, 0x88, 0xdf, 0xd5, 0xaf, 0x24, 0xf2,
+ 0xb1, 0xc3, 0xdf, 0xcc, 0xb5, 0xec, 0xe0, 0x99, 0x5e, 0xb7, 0x49, 0x54,
+ 0x20, 0x3c, 0x94, 0x18, 0x0c, 0xc7, 0x1c, 0x52, 0x18, 0x49, 0xa4, 0x6d,
+ 0xe1, 0xb3, 0x58, 0x0b, 0xc9, 0xd8, 0xec, 0xd9, 0xae, 0x1c, 0x32, 0x8e,
+ 0x28, 0x70, 0x0d, 0xe2, 0xfe, 0xa6, 0x17, 0x9e, 0x84, 0x0f, 0xbd, 0x57,
+ 0x70, 0xb3, 0x5a, 0xe9, 0x1f, 0xa0, 0x86, 0x53, 0xbb, 0xef, 0x7c, 0xff,
+ 0x69, 0x0b, 0xe0, 0x48, 0xc3, 0xb7, 0x93, 0x0b, 0xc8, 0x0a, 0x54, 0xc4,
+ 0xac, 0x5d, 0x14, 0x67, 0x37, 0x6c, 0xca, 0xa5, 0x2f, 0x31, 0x08, 0x37,
+ 0xaa, 0x6e, 0x6f, 0x8c, 0xbc, 0x9b, 0xe2, 0x57, 0x5d, 0x24, 0x81, 0xaf,
+ 0x97, 0x97, 0x9c, 0x84, 0xad, 0x6c, 0xac, 0x37, 0x4c, 0x66, 0xf3, 0x61,
+ 0x91, 0x11, 0x20, 0xe4, 0xbe, 0x30, 0x9f, 0x7a, 0xa4, 0x29, 0x09, 0xb0,
+ 0xe1, 0x34, 0x5f, 0x64, 0x77, 0x18, 0x40, 0x51, 0xdf, 0x8c, 0x30, 0xa6,
+ 0xaf,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 46:f0:8c:db:cf:2c:54:66:ef:33:01:dd:5f:34
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Aug 19 00:00:00 2015 GMT
+ Not After : Aug 19 00:00:00 2025 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign CloudSSL CA - SHA256 - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:c0:75:e1:32:98:e5:d9:ae:84:7c:8d:e8:23:
+ 5f:46:95:5b:4c:a2:25:70:d7:90:04:85:80:c9:b5:
+ f4:8a:65:4d:92:cb:a5:c4:42:a0:b6:79:25:31:ed:
+ f1:85:20:cd:13:51:3d:67:ac:97:4d:68:9b:33:86:
+ 5c:b3:7b:2d:aa:df:77:a0:61:d1:f5:3c:fb:9a:fc:
+ d3:d5:94:ca:c9:1e:80:1b:90:90:c8:ac:8d:f6:60:
+ 17:9c:31:b8:c5:61:a2:e2:6e:57:25:08:6f:24:99:
+ 99:cf:94:bf:c7:8b:6b:b0:1f:ca:14:fa:18:9b:6c:
+ 10:7c:99:2b:da:4a:63:e5:b2:4e:c2:fd:3e:10:0b:
+ 48:f4:77:0b:2f:f0:96:4b:3a:ee:bd:35:de:85:8d:
+ da:13:0e:ce:01:c4:71:d3:d3:77:c5:08:a6:60:39:
+ 25:a7:27:69:5c:83:d1:6f:76:78:ee:c5:44:5b:45:
+ bd:29:3b:e2:c6:09:0f:a2:be:2b:dc:e3:5c:da:5a:
+ 6f:8e:e7:c9:07:6b:7e:a1:c0:53:95:82:89:e0:78:
+ 5c:72:a8:6c:be:67:6b:ab:e7:33:d9:87:f2:f8:5c:
+ 27:f4:f6:2a:3b:87:ef:da:c2:47:da:bf:ac:eb:27:
+ 64:7b:4c:53:eb:34:e1:2f:9b:20:4d:54:12:6b:7d:
+ 28:bd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ A9:2B:87:E1:CE:24:47:3B:1B:BF:CF:85:37:02:55:9D:0D:94:58:E6
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.com/root.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.4146.1.20
+ Policy: 2.23.140.1.2.2
+ CPS: https://www.globalsign.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ a2:1d:69:8a:0a:8e:c4:14:83:2a:2a:12:4d:39:27:90:4e:f0:
+ 8d:ac:d2:96:62:47:36:5e:92:d1:fa:c5:93:b5:37:07:65:29:
+ d2:f4:53:50:6b:c9:f4:fe:34:f5:dd:b8:1d:fa:fc:dc:14:ac:
+ 56:94:27:9c:42:aa:04:4d:b7:ed:58:d9:99:d2:49:e6:20:2f:
+ d3:a7:77:b8:2a:89:1a:ef:a7:cf:86:2d:d6:53:e9:0b:93:9c:
+ 4e:ab:d9:45:ee:a4:84:85:ff:34:e4:0e:c0:bb:a5:ce:5f:95:
+ 89:85:70:aa:c1:5d:ec:cf:2b:d3:d9:83:df:03:ca:81:a7:02:
+ 32:b7:77:61:10:25:4e:d9:74:f3:d9:79:82:b5:26:70:b4:52:
+ bc:8f:33:d7:8a:ae:19:d0:fc:92:ad:2f:ba:3c:a0:48:58:47:
+ 5e:fd:20:56:95:20:c1:72:1d:ab:66:99:a4:d5:78:37:48:1b:
+ 9f:b2:4c:37:67:7a:fd:42:d2:d3:56:9e:d3:1d:8e:c4:0c:68:
+ 96:b6:47:51:10:f7:7b:eb:15:09:64:f5:f9:f0:63:16:2d:3d:
+ df:23:42:3a:93:63:cc:ab:af:4f:57:06:c7:fe:14:55:62:ce:
+ 27:11:19:e1:f4:42:ed:22:30:6b:35:1a:4a:05:80:a4:65:df:
+ cc:cb:6f:d0
+-----BEGIN CERTIFICATE-----
+MIIEizCCA3OgAwIBAgIORvCM288sVGbvMwHdXzQwDQYJKoZIhvcNAQELBQAwVzEL
+MAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsT
+B1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNTA4MTkw
+MDAwMDBaFw0yNTA4MTkwMDAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBH
+bG9iYWxTaWduIG52LXNhMS0wKwYDVQQDEyRHbG9iYWxTaWduIENsb3VkU1NMIENB
+IC0gU0hBMjU2IC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj
+wHXhMpjl2a6EfI3oI19GlVtMoiVw15AEhYDJtfSKZU2Sy6XEQqC2eSUx7fGFIM0T
+UT1nrJdNaJszhlyzey2q33egYdH1PPua/NPVlMrJHoAbkJDIrI32YBecMbjFYaLi
+blclCG8kmZnPlL/Hi2uwH8oU+hibbBB8mSvaSmPlsk7C/T4QC0j0dwsv8JZLOu69
+Nd6FjdoTDs4BxHHT03fFCKZgOSWnJ2lcg9FvdnjuxURbRb0pO+LGCQ+ivivc41za
+Wm+O58kHa36hwFOVgongeFxyqGy+Z2ur5zPZh/L4XCf09io7h+/awkfav6zrJ2R7
+TFPrNOEvmyBNVBJrfSi9AgMBAAGjggFTMIIBTzAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAw
+HQYDVR0OBBYEFKkrh+HOJEc7G7/PhTcCVZ0NlFjmMB8GA1UdIwQYMBaAFGB7ZhpF
+DZfKiVAvfQTNNKj//P1LMD0GCCsGAQUFBwEBBDEwLzAtBggrBgEFBQcwAYYhaHR0
+cDovL29jc3AuZ2xvYmFsc2lnbi5jb20vcm9vdHIxMDMGA1UdHwQsMCowKKAmoCSG
+Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC5jcmwwVgYDVR0gBE8wTTAL
+BgkrBgEEAaAyARQwPgYGZ4EMAQICMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3
+Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEBCwUAA4IBAQCi
+HWmKCo7EFIMqKhJNOSeQTvCNrNKWYkc2XpLR+sWTtTcHZSnS9FNQa8n0/jT13bgd
++vzcFKxWlCecQqoETbftWNmZ0knmIC/Tp3e4Koka76fPhi3WU+kLk5xOq9lF7qSE
+hf805A7Au6XOX5WJhXCqwV3szyvT2YPfA8qBpwIyt3dhECVO2XTz2XmCtSZwtFK8
+jzPXiq4Z0PySrS+6PKBIWEde/SBWlSDBch2rZpmk1Xg3SBufskw3Z3r9QtLTVp7T
+HY7EDGiWtkdREPd76xUJZPX58GMWLT3fI0I6k2PMq69PVwbH/hRVYs4nERnh9ELt
+IjBrNRpKBYCkZd/My2/Q
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert20[] = {
+ 0x30, 0x82, 0x04, 0x8b, 0x30, 0x82, 0x03, 0x73, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0e, 0x46, 0xf0, 0x8c, 0xdb, 0xcf, 0x2c, 0x54, 0x66, 0xef,
+ 0x33, 0x01, 0xdd, 0x5f, 0x34, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31,
+ 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d,
+ 0x73, 0x61, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x07, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
+ 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x38, 0x31, 0x39, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x38,
+ 0x31, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x57, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76,
+ 0x2d, 0x73, 0x61, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x24, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e,
+ 0x20, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x2d, 0x20,
+ 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3,
+ 0xc0, 0x75, 0xe1, 0x32, 0x98, 0xe5, 0xd9, 0xae, 0x84, 0x7c, 0x8d, 0xe8,
+ 0x23, 0x5f, 0x46, 0x95, 0x5b, 0x4c, 0xa2, 0x25, 0x70, 0xd7, 0x90, 0x04,
+ 0x85, 0x80, 0xc9, 0xb5, 0xf4, 0x8a, 0x65, 0x4d, 0x92, 0xcb, 0xa5, 0xc4,
+ 0x42, 0xa0, 0xb6, 0x79, 0x25, 0x31, 0xed, 0xf1, 0x85, 0x20, 0xcd, 0x13,
+ 0x51, 0x3d, 0x67, 0xac, 0x97, 0x4d, 0x68, 0x9b, 0x33, 0x86, 0x5c, 0xb3,
+ 0x7b, 0x2d, 0xaa, 0xdf, 0x77, 0xa0, 0x61, 0xd1, 0xf5, 0x3c, 0xfb, 0x9a,
+ 0xfc, 0xd3, 0xd5, 0x94, 0xca, 0xc9, 0x1e, 0x80, 0x1b, 0x90, 0x90, 0xc8,
+ 0xac, 0x8d, 0xf6, 0x60, 0x17, 0x9c, 0x31, 0xb8, 0xc5, 0x61, 0xa2, 0xe2,
+ 0x6e, 0x57, 0x25, 0x08, 0x6f, 0x24, 0x99, 0x99, 0xcf, 0x94, 0xbf, 0xc7,
+ 0x8b, 0x6b, 0xb0, 0x1f, 0xca, 0x14, 0xfa, 0x18, 0x9b, 0x6c, 0x10, 0x7c,
+ 0x99, 0x2b, 0xda, 0x4a, 0x63, 0xe5, 0xb2, 0x4e, 0xc2, 0xfd, 0x3e, 0x10,
+ 0x0b, 0x48, 0xf4, 0x77, 0x0b, 0x2f, 0xf0, 0x96, 0x4b, 0x3a, 0xee, 0xbd,
+ 0x35, 0xde, 0x85, 0x8d, 0xda, 0x13, 0x0e, 0xce, 0x01, 0xc4, 0x71, 0xd3,
+ 0xd3, 0x77, 0xc5, 0x08, 0xa6, 0x60, 0x39, 0x25, 0xa7, 0x27, 0x69, 0x5c,
+ 0x83, 0xd1, 0x6f, 0x76, 0x78, 0xee, 0xc5, 0x44, 0x5b, 0x45, 0xbd, 0x29,
+ 0x3b, 0xe2, 0xc6, 0x09, 0x0f, 0xa2, 0xbe, 0x2b, 0xdc, 0xe3, 0x5c, 0xda,
+ 0x5a, 0x6f, 0x8e, 0xe7, 0xc9, 0x07, 0x6b, 0x7e, 0xa1, 0xc0, 0x53, 0x95,
+ 0x82, 0x89, 0xe0, 0x78, 0x5c, 0x72, 0xa8, 0x6c, 0xbe, 0x67, 0x6b, 0xab,
+ 0xe7, 0x33, 0xd9, 0x87, 0xf2, 0xf8, 0x5c, 0x27, 0xf4, 0xf6, 0x2a, 0x3b,
+ 0x87, 0xef, 0xda, 0xc2, 0x47, 0xda, 0xbf, 0xac, 0xeb, 0x27, 0x64, 0x7b,
+ 0x4c, 0x53, 0xeb, 0x34, 0xe1, 0x2f, 0x9b, 0x20, 0x4d, 0x54, 0x12, 0x6b,
+ 0x7d, 0x28, 0xbd, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x53,
+ 0x30, 0x82, 0x01, 0x4f, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x03, 0x02, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa9, 0x2b,
+ 0x87, 0xe1, 0xce, 0x24, 0x47, 0x3b, 0x1b, 0xbf, 0xcf, 0x85, 0x37, 0x02,
+ 0x55, 0x9d, 0x0d, 0x94, 0x58, 0xe6, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45,
+ 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff,
+ 0xfc, 0xfd, 0x4b, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
+ 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
+ 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x56, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4f, 0x30, 0x4d, 0x30, 0x0b,
+ 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xa0, 0x32, 0x01, 0x14, 0x30,
+ 0x3e, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30, 0x34, 0x30,
+ 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa2,
+ 0x1d, 0x69, 0x8a, 0x0a, 0x8e, 0xc4, 0x14, 0x83, 0x2a, 0x2a, 0x12, 0x4d,
+ 0x39, 0x27, 0x90, 0x4e, 0xf0, 0x8d, 0xac, 0xd2, 0x96, 0x62, 0x47, 0x36,
+ 0x5e, 0x92, 0xd1, 0xfa, 0xc5, 0x93, 0xb5, 0x37, 0x07, 0x65, 0x29, 0xd2,
+ 0xf4, 0x53, 0x50, 0x6b, 0xc9, 0xf4, 0xfe, 0x34, 0xf5, 0xdd, 0xb8, 0x1d,
+ 0xfa, 0xfc, 0xdc, 0x14, 0xac, 0x56, 0x94, 0x27, 0x9c, 0x42, 0xaa, 0x04,
+ 0x4d, 0xb7, 0xed, 0x58, 0xd9, 0x99, 0xd2, 0x49, 0xe6, 0x20, 0x2f, 0xd3,
+ 0xa7, 0x77, 0xb8, 0x2a, 0x89, 0x1a, 0xef, 0xa7, 0xcf, 0x86, 0x2d, 0xd6,
+ 0x53, 0xe9, 0x0b, 0x93, 0x9c, 0x4e, 0xab, 0xd9, 0x45, 0xee, 0xa4, 0x84,
+ 0x85, 0xff, 0x34, 0xe4, 0x0e, 0xc0, 0xbb, 0xa5, 0xce, 0x5f, 0x95, 0x89,
+ 0x85, 0x70, 0xaa, 0xc1, 0x5d, 0xec, 0xcf, 0x2b, 0xd3, 0xd9, 0x83, 0xdf,
+ 0x03, 0xca, 0x81, 0xa7, 0x02, 0x32, 0xb7, 0x77, 0x61, 0x10, 0x25, 0x4e,
+ 0xd9, 0x74, 0xf3, 0xd9, 0x79, 0x82, 0xb5, 0x26, 0x70, 0xb4, 0x52, 0xbc,
+ 0x8f, 0x33, 0xd7, 0x8a, 0xae, 0x19, 0xd0, 0xfc, 0x92, 0xad, 0x2f, 0xba,
+ 0x3c, 0xa0, 0x48, 0x58, 0x47, 0x5e, 0xfd, 0x20, 0x56, 0x95, 0x20, 0xc1,
+ 0x72, 0x1d, 0xab, 0x66, 0x99, 0xa4, 0xd5, 0x78, 0x37, 0x48, 0x1b, 0x9f,
+ 0xb2, 0x4c, 0x37, 0x67, 0x7a, 0xfd, 0x42, 0xd2, 0xd3, 0x56, 0x9e, 0xd3,
+ 0x1d, 0x8e, 0xc4, 0x0c, 0x68, 0x96, 0xb6, 0x47, 0x51, 0x10, 0xf7, 0x7b,
+ 0xeb, 0x15, 0x09, 0x64, 0xf5, 0xf9, 0xf0, 0x63, 0x16, 0x2d, 0x3d, 0xdf,
+ 0x23, 0x42, 0x3a, 0x93, 0x63, 0xcc, 0xab, 0xaf, 0x4f, 0x57, 0x06, 0xc7,
+ 0xfe, 0x14, 0x55, 0x62, 0xce, 0x27, 0x11, 0x19, 0xe1, 0xf4, 0x42, 0xed,
+ 0x22, 0x30, 0x6b, 0x35, 0x1a, 0x4a, 0x05, 0x80, 0xa4, 0x65, 0xdf, 0xcc,
+ 0xcb, 0x6f, 0xd0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 1b:09:3b:78:60:96:da:37:bb:a4:51:94:46:c8:96:78
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ a3:cd:7d:1e:f7:c7:75:8d:48:e7:56:34:4c:00:90:75:a9:51:
+ a5:56:c1:6d:bc:fe:f5:53:22:e9:98:a2:ac:9a:7e:70:1e:b3:
+ 8e:3b:45:e3:86:95:31:da:6d:4c:fb:34:50:80:96:cd:24:f2:
+ 40:df:04:3f:e2:65:ce:34:22:61:15:ea:66:70:64:d2:f1:6e:
+ f3:ca:18:59:6a:41:46:7e:82:de:19:b0:70:31:56:69:0d:0c:
+ e6:1d:9d:71:58:dc:cc:de:62:f5:e1:7a:10:02:d8:7a:dc:3b:
+ fa:57:bd:c9:e9:8f:46:21:39:9f:51:65:4c:8e:3a:be:28:41:
+ 70:1d
+-----BEGIN CERTIFICATE-----
+MIIEkDCCA/mgAwIBAgIQGwk7eGCW2je7pFGURsiWeDANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggFbMIIBVzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
+BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
+aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
+KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
+j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
+L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEAo819HvfHdY1I51Y0
+TACQdalRpVbBbbz+9VMi6ZiirJp+cB6zjjtF44aVMdptTPs0UICWzSTyQN8EP+Jl
+zjQiYRXqZnBk0vFu88oYWWpBRn6C3hmwcDFWaQ0M5h2dcVjczN5i9eF6EALYetw7
++le9yemPRiE5n1FlTI46vihBcB0=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert21[] = {
+ 0x30, 0x82, 0x04, 0x90, 0x30, 0x82, 0x03, 0xf9, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x1b, 0x09, 0x3b, 0x78, 0x60, 0x96, 0xda, 0x37, 0xbb,
+ 0xa4, 0x51, 0x94, 0x46, 0xc8, 0x96, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5b, 0x30, 0x82, 0x01, 0x57, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f,
+ 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09,
+ 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30,
+ 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
+ 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80,
+ 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0xa3, 0xcd, 0x7d, 0x1e, 0xf7, 0xc7, 0x75, 0x8d, 0x48, 0xe7, 0x56, 0x34,
+ 0x4c, 0x00, 0x90, 0x75, 0xa9, 0x51, 0xa5, 0x56, 0xc1, 0x6d, 0xbc, 0xfe,
+ 0xf5, 0x53, 0x22, 0xe9, 0x98, 0xa2, 0xac, 0x9a, 0x7e, 0x70, 0x1e, 0xb3,
+ 0x8e, 0x3b, 0x45, 0xe3, 0x86, 0x95, 0x31, 0xda, 0x6d, 0x4c, 0xfb, 0x34,
+ 0x50, 0x80, 0x96, 0xcd, 0x24, 0xf2, 0x40, 0xdf, 0x04, 0x3f, 0xe2, 0x65,
+ 0xce, 0x34, 0x22, 0x61, 0x15, 0xea, 0x66, 0x70, 0x64, 0xd2, 0xf1, 0x6e,
+ 0xf3, 0xca, 0x18, 0x59, 0x6a, 0x41, 0x46, 0x7e, 0x82, 0xde, 0x19, 0xb0,
+ 0x70, 0x31, 0x56, 0x69, 0x0d, 0x0c, 0xe6, 0x1d, 0x9d, 0x71, 0x58, 0xdc,
+ 0xcc, 0xde, 0x62, 0xf5, 0xe1, 0x7a, 0x10, 0x02, 0xd8, 0x7a, 0xdc, 0x3b,
+ 0xfa, 0x57, 0xbd, 0xc9, 0xe9, 0x8f, 0x46, 0x21, 0x39, 0x9f, 0x51, 0x65,
+ 0x4c, 0x8e, 0x3a, 0xbe, 0x28, 0x41, 0x70, 0x1d,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0a:01:41:42:00:00:01:53:85:73:6a:0b:85:ec:a7:08
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: O=Digital Signature Trust Co., CN=DST Root CA X3
+ Validity
+ Not Before: Mar 17 16:40:46 2016 GMT
+ Not After : Mar 17 16:40:46 2021 GMT
+ Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:9c:d3:0c:f0:5a:e5:2e:47:b7:72:5d:37:83:b3:
+ 68:63:30:ea:d7:35:26:19:25:e1:bd:be:35:f1:70:
+ 92:2f:b7:b8:4b:41:05:ab:a9:9e:35:08:58:ec:b1:
+ 2a:c4:68:87:0b:a3:e3:75:e4:e6:f3:a7:62:71:ba:
+ 79:81:60:1f:d7:91:9a:9f:f3:d0:78:67:71:c8:69:
+ 0e:95:91:cf:fe:e6:99:e9:60:3c:48:cc:7e:ca:4d:
+ 77:12:24:9d:47:1b:5a:eb:b9:ec:1e:37:00:1c:9c:
+ ac:7b:a7:05:ea:ce:4a:eb:bd:41:e5:36:98:b9:cb:
+ fd:6d:3c:96:68:df:23:2a:42:90:0c:86:74:67:c8:
+ 7f:a5:9a:b8:52:61:14:13:3f:65:e9:82:87:cb:db:
+ fa:0e:56:f6:86:89:f3:85:3f:97:86:af:b0:dc:1a:
+ ef:6b:0d:95:16:7d:c4:2b:a0:65:b2:99:04:36:75:
+ 80:6b:ac:4a:f3:1b:90:49:78:2f:a2:96:4f:2a:20:
+ 25:29:04:c6:74:c0:d0:31:cd:8f:31:38:95:16:ba:
+ a8:33:b8:43:f1:b1:1f:c3:30:7f:a2:79:31:13:3d:
+ 2d:36:f8:e3:fc:f2:33:6a:b9:39:31:c5:af:c4:8d:
+ 0d:1d:64:16:33:aa:fa:84:29:b6:d4:0b:c0:d8:7d:
+ c3:93
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://isrg.trustid.ocsp.identrust.com
+ CA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c
+
+ X509v3 Authority Key Identifier:
+ keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10
+
+ X509v3 Certificate Policies:
+ Policy: 2.23.140.1.2.1
+ Policy: 1.3.6.1.4.1.44947.1.1.1
+ CPS: http://cps.root-x1.letsencrypt.org
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl
+
+ X509v3 Subject Key Identifier:
+ A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1
+ Signature Algorithm: sha256WithRSAEncryption
+ dd:33:d7:11:f3:63:58:38:dd:18:15:fb:09:55:be:76:56:b9:
+ 70:48:a5:69:47:27:7b:c2:24:08:92:f1:5a:1f:4a:12:29:37:
+ 24:74:51:1c:62:68:b8:cd:95:70:67:e5:f7:a4:bc:4e:28:51:
+ cd:9b:e8:ae:87:9d:ea:d8:ba:5a:a1:01:9a:dc:f0:dd:6a:1d:
+ 6a:d8:3e:57:23:9e:a6:1e:04:62:9a:ff:d7:05:ca:b7:1f:3f:
+ c0:0a:48:bc:94:b0:b6:65:62:e0:c1:54:e5:a3:2a:ad:20:c4:
+ e9:e6:bb:dc:c8:f6:b5:c3:32:a3:98:cc:77:a8:e6:79:65:07:
+ 2b:cb:28:fe:3a:16:52:81:ce:52:0c:2e:5f:83:e8:d5:06:33:
+ fb:77:6c:ce:40:ea:32:9e:1f:92:5c:41:c1:74:6c:5b:5d:0a:
+ 5f:33:cc:4d:9f:ac:38:f0:2f:7b:2c:62:9d:d9:a3:91:6f:25:
+ 1b:2f:90:b1:19:46:3d:f6:7e:1b:a6:7a:87:b9:a3:7a:6d:18:
+ fa:25:a5:91:87:15:e0:f2:16:2f:58:b0:06:2f:2c:68:26:c6:
+ 4b:98:cd:da:9f:0c:f9:7f:90:ed:43:4a:12:44:4e:6f:73:7a:
+ 28:ea:a4:aa:6e:7b:4c:7d:87:dd:e0:c9:02:44:a7:87:af:c3:
+ 34:5b:b4:42
+-----BEGIN CERTIFICATE-----
+MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
+MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
+DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
+SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
+GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
+q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
+SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
+Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
+a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
+/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
+CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
+bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
+c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
+VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
+ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
+MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
+Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
+AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
+uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
+wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
+X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
+PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
+KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert22[] = {
+ 0x30, 0x82, 0x04, 0x92, 0x30, 0x82, 0x03, 0x7a, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x0a, 0x01, 0x41, 0x42, 0x00, 0x00, 0x01, 0x53, 0x85,
+ 0x73, 0x6a, 0x0b, 0x85, 0xec, 0xa7, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x3f,
+ 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, 0x44,
+ 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61,
+ 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43,
+ 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x20, 0x58, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x33, 0x31,
+ 0x37, 0x31, 0x36, 0x34, 0x30, 0x34, 0x36, 0x5a, 0x17, 0x0d, 0x32, 0x31,
+ 0x30, 0x33, 0x31, 0x37, 0x31, 0x36, 0x34, 0x30, 0x34, 0x36, 0x5a, 0x30,
+ 0x4a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x4c, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79,
+ 0x70, 0x74, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x1a, 0x4c, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79,
+ 0x70, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x20, 0x58, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0x9c, 0xd3, 0x0c, 0xf0, 0x5a, 0xe5, 0x2e, 0x47, 0xb7, 0x72, 0x5d, 0x37,
+ 0x83, 0xb3, 0x68, 0x63, 0x30, 0xea, 0xd7, 0x35, 0x26, 0x19, 0x25, 0xe1,
+ 0xbd, 0xbe, 0x35, 0xf1, 0x70, 0x92, 0x2f, 0xb7, 0xb8, 0x4b, 0x41, 0x05,
+ 0xab, 0xa9, 0x9e, 0x35, 0x08, 0x58, 0xec, 0xb1, 0x2a, 0xc4, 0x68, 0x87,
+ 0x0b, 0xa3, 0xe3, 0x75, 0xe4, 0xe6, 0xf3, 0xa7, 0x62, 0x71, 0xba, 0x79,
+ 0x81, 0x60, 0x1f, 0xd7, 0x91, 0x9a, 0x9f, 0xf3, 0xd0, 0x78, 0x67, 0x71,
+ 0xc8, 0x69, 0x0e, 0x95, 0x91, 0xcf, 0xfe, 0xe6, 0x99, 0xe9, 0x60, 0x3c,
+ 0x48, 0xcc, 0x7e, 0xca, 0x4d, 0x77, 0x12, 0x24, 0x9d, 0x47, 0x1b, 0x5a,
+ 0xeb, 0xb9, 0xec, 0x1e, 0x37, 0x00, 0x1c, 0x9c, 0xac, 0x7b, 0xa7, 0x05,
+ 0xea, 0xce, 0x4a, 0xeb, 0xbd, 0x41, 0xe5, 0x36, 0x98, 0xb9, 0xcb, 0xfd,
+ 0x6d, 0x3c, 0x96, 0x68, 0xdf, 0x23, 0x2a, 0x42, 0x90, 0x0c, 0x86, 0x74,
+ 0x67, 0xc8, 0x7f, 0xa5, 0x9a, 0xb8, 0x52, 0x61, 0x14, 0x13, 0x3f, 0x65,
+ 0xe9, 0x82, 0x87, 0xcb, 0xdb, 0xfa, 0x0e, 0x56, 0xf6, 0x86, 0x89, 0xf3,
+ 0x85, 0x3f, 0x97, 0x86, 0xaf, 0xb0, 0xdc, 0x1a, 0xef, 0x6b, 0x0d, 0x95,
+ 0x16, 0x7d, 0xc4, 0x2b, 0xa0, 0x65, 0xb2, 0x99, 0x04, 0x36, 0x75, 0x80,
+ 0x6b, 0xac, 0x4a, 0xf3, 0x1b, 0x90, 0x49, 0x78, 0x2f, 0xa2, 0x96, 0x4f,
+ 0x2a, 0x20, 0x25, 0x29, 0x04, 0xc6, 0x74, 0xc0, 0xd0, 0x31, 0xcd, 0x8f,
+ 0x31, 0x38, 0x95, 0x16, 0xba, 0xa8, 0x33, 0xb8, 0x43, 0xf1, 0xb1, 0x1f,
+ 0xc3, 0x30, 0x7f, 0xa2, 0x79, 0x31, 0x13, 0x3d, 0x2d, 0x36, 0xf8, 0xe3,
+ 0xfc, 0xf2, 0x33, 0x6a, 0xb9, 0x39, 0x31, 0xc5, 0xaf, 0xc4, 0x8d, 0x0d,
+ 0x1d, 0x64, 0x16, 0x33, 0xaa, 0xfa, 0x84, 0x29, 0xb6, 0xd4, 0x0b, 0xc0,
+ 0xd8, 0x7d, 0xc3, 0x93, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0x7d, 0x30, 0x82, 0x01, 0x79, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x32, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x26, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x69, 0x73, 0x72, 0x67, 0x2e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x69, 0x64, 0x2e, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x02, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x70,
+ 0x70, 0x73, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x2f, 0x64,
+ 0x73, 0x74, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61, 0x78, 0x33, 0x2e, 0x70,
+ 0x37, 0x63, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xc4, 0xa7, 0xb1, 0xa4, 0x7b, 0x2c, 0x71, 0xfa, 0xdb,
+ 0xe1, 0x4b, 0x90, 0x75, 0xff, 0xc4, 0x15, 0x60, 0x85, 0x89, 0x10, 0x30,
+ 0x54, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4d, 0x30, 0x4b, 0x30, 0x08,
+ 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x01, 0x30, 0x3f, 0x06, 0x0b,
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xdf, 0x13, 0x01, 0x01, 0x01, 0x30,
+ 0x30, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x70,
+ 0x73, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x78, 0x31, 0x2e, 0x6c, 0x65,
+ 0x74, 0x73, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2e, 0x6f, 0x72,
+ 0x67, 0x30, 0x3c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x35, 0x30, 0x33,
+ 0x30, 0x31, 0xa0, 0x2f, 0xa0, 0x2d, 0x86, 0x2b, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x53, 0x54,
+ 0x52, 0x4f, 0x4f, 0x54, 0x43, 0x41, 0x58, 0x33, 0x43, 0x52, 0x4c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0xa8, 0x4a, 0x6a, 0x63, 0x04, 0x7d, 0xdd, 0xba, 0xe6, 0xd1,
+ 0x39, 0xb7, 0xa6, 0x45, 0x65, 0xef, 0xf3, 0xa8, 0xec, 0xa1, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xdd, 0x33, 0xd7, 0x11, 0xf3, 0x63,
+ 0x58, 0x38, 0xdd, 0x18, 0x15, 0xfb, 0x09, 0x55, 0xbe, 0x76, 0x56, 0xb9,
+ 0x70, 0x48, 0xa5, 0x69, 0x47, 0x27, 0x7b, 0xc2, 0x24, 0x08, 0x92, 0xf1,
+ 0x5a, 0x1f, 0x4a, 0x12, 0x29, 0x37, 0x24, 0x74, 0x51, 0x1c, 0x62, 0x68,
+ 0xb8, 0xcd, 0x95, 0x70, 0x67, 0xe5, 0xf7, 0xa4, 0xbc, 0x4e, 0x28, 0x51,
+ 0xcd, 0x9b, 0xe8, 0xae, 0x87, 0x9d, 0xea, 0xd8, 0xba, 0x5a, 0xa1, 0x01,
+ 0x9a, 0xdc, 0xf0, 0xdd, 0x6a, 0x1d, 0x6a, 0xd8, 0x3e, 0x57, 0x23, 0x9e,
+ 0xa6, 0x1e, 0x04, 0x62, 0x9a, 0xff, 0xd7, 0x05, 0xca, 0xb7, 0x1f, 0x3f,
+ 0xc0, 0x0a, 0x48, 0xbc, 0x94, 0xb0, 0xb6, 0x65, 0x62, 0xe0, 0xc1, 0x54,
+ 0xe5, 0xa3, 0x2a, 0xad, 0x20, 0xc4, 0xe9, 0xe6, 0xbb, 0xdc, 0xc8, 0xf6,
+ 0xb5, 0xc3, 0x32, 0xa3, 0x98, 0xcc, 0x77, 0xa8, 0xe6, 0x79, 0x65, 0x07,
+ 0x2b, 0xcb, 0x28, 0xfe, 0x3a, 0x16, 0x52, 0x81, 0xce, 0x52, 0x0c, 0x2e,
+ 0x5f, 0x83, 0xe8, 0xd5, 0x06, 0x33, 0xfb, 0x77, 0x6c, 0xce, 0x40, 0xea,
+ 0x32, 0x9e, 0x1f, 0x92, 0x5c, 0x41, 0xc1, 0x74, 0x6c, 0x5b, 0x5d, 0x0a,
+ 0x5f, 0x33, 0xcc, 0x4d, 0x9f, 0xac, 0x38, 0xf0, 0x2f, 0x7b, 0x2c, 0x62,
+ 0x9d, 0xd9, 0xa3, 0x91, 0x6f, 0x25, 0x1b, 0x2f, 0x90, 0xb1, 0x19, 0x46,
+ 0x3d, 0xf6, 0x7e, 0x1b, 0xa6, 0x7a, 0x87, 0xb9, 0xa3, 0x7a, 0x6d, 0x18,
+ 0xfa, 0x25, 0xa5, 0x91, 0x87, 0x15, 0xe0, 0xf2, 0x16, 0x2f, 0x58, 0xb0,
+ 0x06, 0x2f, 0x2c, 0x68, 0x26, 0xc6, 0x4b, 0x98, 0xcd, 0xda, 0x9f, 0x0c,
+ 0xf9, 0x7f, 0x90, 0xed, 0x43, 0x4a, 0x12, 0x44, 0x4e, 0x6f, 0x73, 0x7a,
+ 0x28, 0xea, 0xa4, 0xaa, 0x6e, 0x7b, 0x4c, 0x7d, 0x87, 0xdd, 0xe0, 0xc9,
+ 0x02, 0x44, 0xa7, 0x87, 0xaf, 0xc3, 0x34, 0x5b, 0xb4, 0x42,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 06:7f:94:4a:2a:27:cd:f3:fa:c2:ae:2b:01:f9:08:ee:b9:c4:c6
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Services Root Certificate Authority - G2
+ Validity
+ Not Before: May 25 12:00:00 2015 GMT
+ Not After : Dec 31 01:00:00 2037 GMT
+ Subject: C=US, O=Amazon, CN=Amazon Root CA 1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b2:78:80:71:ca:78:d5:e3:71:af:47:80:50:74:
+ 7d:6e:d8:d7:88:76:f4:99:68:f7:58:21:60:f9:74:
+ 84:01:2f:ac:02:2d:86:d3:a0:43:7a:4e:b2:a4:d0:
+ 36:ba:01:be:8d:db:48:c8:07:17:36:4c:f4:ee:88:
+ 23:c7:3e:eb:37:f5:b5:19:f8:49:68:b0:de:d7:b9:
+ 76:38:1d:61:9e:a4:fe:82:36:a5:e5:4a:56:e4:45:
+ e1:f9:fd:b4:16:fa:74:da:9c:9b:35:39:2f:fa:b0:
+ 20:50:06:6c:7a:d0:80:b2:a6:f9:af:ec:47:19:8f:
+ 50:38:07:dc:a2:87:39:58:f8:ba:d5:a9:f9:48:67:
+ 30:96:ee:94:78:5e:6f:89:a3:51:c0:30:86:66:a1:
+ 45:66:ba:54:eb:a3:c3:91:f9:48:dc:ff:d1:e8:30:
+ 2d:7d:2d:74:70:35:d7:88:24:f7:9e:c4:59:6e:bb:
+ 73:87:17:f2:32:46:28:b8:43:fa:b7:1d:aa:ca:b4:
+ f2:9f:24:0e:2d:4b:f7:71:5c:5e:69:ff:ea:95:02:
+ cb:38:8a:ae:50:38:6f:db:fb:2d:62:1b:c5:c7:1e:
+ 54:e1:77:e0:67:c8:0f:9c:87:23:d6:3f:40:20:7f:
+ 20:80:c4:80:4c:3e:3b:24:26:8e:04:ae:6c:9a:c8:
+ aa:0d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 84:18:CC:85:34:EC:BC:0C:94:94:2E:08:59:9C:C7:B2:10:4E:0A:08
+ X509v3 Authority Key Identifier:
+ keyid:9C:5F:00:DF:AA:01:D7:30:2B:38:88:A2:B8:6D:4A:9C:F2:11:91:83
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.rootg2.amazontrust.com
+ CA Issuers - URI:http://crt.rootg2.amazontrust.com/rootg2.cer
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.rootg2.amazontrust.com/rootg2.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 62:37:42:5c:bc:10:b5:3e:8b:2c:e9:0c:9b:6c:45:e2:07:00:
+ 7a:f9:c5:58:0b:b9:08:8c:3e:ed:b3:25:3c:b5:6f:50:e4:cd:
+ 35:6a:a7:93:34:96:32:21:a9:48:44:ab:9c:ed:3d:b4:aa:73:
+ 6d:e4:7f:16:80:89:6c:cf:28:03:18:83:47:79:a3:10:7e:30:
+ 5b:ac:3b:b0:60:e0:77:d4:08:a6:e1:1d:7c:5e:c0:bb:f9:9a:
+ 7b:22:9d:a7:00:09:7e:ac:46:17:83:dc:9c:26:57:99:30:39:
+ 62:96:8f:ed:da:de:aa:c5:cc:1b:3e:ca:43:68:6c:57:16:bc:
+ d5:0e:20:2e:fe:ff:c2:6a:5d:2e:a0:4a:6d:14:58:87:94:e6:
+ 39:31:5f:7c:73:cb:90:88:6a:84:11:96:27:a6:ed:d9:81:46:
+ a6:7e:a3:72:00:0a:52:3e:83:88:07:63:77:89:69:17:0f:39:
+ 85:d2:ab:08:45:4d:d0:51:3a:fd:5d:5d:37:64:4c:7e:30:b2:
+ 55:24:42:9d:36:b0:5d:9c:17:81:61:f1:ca:f9:10:02:24:ab:
+ eb:0d:74:91:8d:7b:45:29:50:39:88:b2:a6:89:35:25:1e:14:
+ 6a:47:23:31:2f:5c:9a:fa:ad:9a:0e:62:51:a4:2a:a9:c4:f9:
+ 34:9d:21:18
+-----BEGIN CERTIFICATE-----
+MIIEkjCCA3qgAwIBAgITBn+USionzfP6wq4rAfkI7rnExjANBgkqhkiG9w0BAQsF
+ADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNj
+b3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4x
+OzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1
+dGhvcml0eSAtIEcyMB4XDTE1MDUyNTEyMDAwMFoXDTM3MTIzMTAxMDAwMFowOTEL
+MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
+b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
+ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
+9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
+IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
+VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
+93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
+jgSubJrIqg0CAwEAAaOCATEwggEtMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgGGMB0GA1UdDgQWBBSEGMyFNOy8DJSULghZnMeyEE4KCDAfBgNVHSMEGDAW
+gBScXwDfqgHXMCs4iKK4bUqc8hGRgzB4BggrBgEFBQcBAQRsMGowLgYIKwYBBQUH
+MAGGImh0dHA6Ly9vY3NwLnJvb3RnMi5hbWF6b250cnVzdC5jb20wOAYIKwYBBQUH
+MAKGLGh0dHA6Ly9jcnQucm9vdGcyLmFtYXpvbnRydXN0LmNvbS9yb290ZzIuY2Vy
+MD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwucm9vdGcyLmFtYXpvbnRydXN0
+LmNvbS9yb290ZzIuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsF
+AAOCAQEAYjdCXLwQtT6LLOkMm2xF4gcAevnFWAu5CIw+7bMlPLVvUOTNNWqnkzSW
+MiGpSESrnO09tKpzbeR/FoCJbM8oAxiDR3mjEH4wW6w7sGDgd9QIpuEdfF7Au/ma
+eyKdpwAJfqxGF4PcnCZXmTA5YpaP7dreqsXMGz7KQ2hsVxa81Q4gLv7/wmpdLqBK
+bRRYh5TmOTFffHPLkIhqhBGWJ6bt2YFGpn6jcgAKUj6DiAdjd4lpFw85hdKrCEVN
+0FE6/V1dN2RMfjCyVSRCnTawXZwXgWHxyvkQAiSr6w10kY17RSlQOYiypok1JR4U
+akcjMS9cmvqtmg5iUaQqqcT5NJ0hGA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert23[] = {
+ 0x30, 0x82, 0x04, 0x92, 0x30, 0x82, 0x03, 0x7a, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x13, 0x06, 0x7f, 0x94, 0x4a, 0x2a, 0x27, 0xcd, 0xf3, 0xfa,
+ 0xc2, 0xae, 0x2b, 0x01, 0xf9, 0x08, 0xee, 0xb9, 0xc4, 0xc6, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x30, 0x81, 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31,
+ 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63,
+ 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c,
+ 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x32, 0x53, 0x74,
+ 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x73, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x35, 0x32, 0x35, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x32, 0x33,
+ 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x39, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x06, 0x41, 0x6d,
+ 0x61, 0x7a, 0x6f, 0x6e, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x10, 0x41, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x20, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xb2, 0x78, 0x80, 0x71, 0xca, 0x78, 0xd5, 0xe3,
+ 0x71, 0xaf, 0x47, 0x80, 0x50, 0x74, 0x7d, 0x6e, 0xd8, 0xd7, 0x88, 0x76,
+ 0xf4, 0x99, 0x68, 0xf7, 0x58, 0x21, 0x60, 0xf9, 0x74, 0x84, 0x01, 0x2f,
+ 0xac, 0x02, 0x2d, 0x86, 0xd3, 0xa0, 0x43, 0x7a, 0x4e, 0xb2, 0xa4, 0xd0,
+ 0x36, 0xba, 0x01, 0xbe, 0x8d, 0xdb, 0x48, 0xc8, 0x07, 0x17, 0x36, 0x4c,
+ 0xf4, 0xee, 0x88, 0x23, 0xc7, 0x3e, 0xeb, 0x37, 0xf5, 0xb5, 0x19, 0xf8,
+ 0x49, 0x68, 0xb0, 0xde, 0xd7, 0xb9, 0x76, 0x38, 0x1d, 0x61, 0x9e, 0xa4,
+ 0xfe, 0x82, 0x36, 0xa5, 0xe5, 0x4a, 0x56, 0xe4, 0x45, 0xe1, 0xf9, 0xfd,
+ 0xb4, 0x16, 0xfa, 0x74, 0xda, 0x9c, 0x9b, 0x35, 0x39, 0x2f, 0xfa, 0xb0,
+ 0x20, 0x50, 0x06, 0x6c, 0x7a, 0xd0, 0x80, 0xb2, 0xa6, 0xf9, 0xaf, 0xec,
+ 0x47, 0x19, 0x8f, 0x50, 0x38, 0x07, 0xdc, 0xa2, 0x87, 0x39, 0x58, 0xf8,
+ 0xba, 0xd5, 0xa9, 0xf9, 0x48, 0x67, 0x30, 0x96, 0xee, 0x94, 0x78, 0x5e,
+ 0x6f, 0x89, 0xa3, 0x51, 0xc0, 0x30, 0x86, 0x66, 0xa1, 0x45, 0x66, 0xba,
+ 0x54, 0xeb, 0xa3, 0xc3, 0x91, 0xf9, 0x48, 0xdc, 0xff, 0xd1, 0xe8, 0x30,
+ 0x2d, 0x7d, 0x2d, 0x74, 0x70, 0x35, 0xd7, 0x88, 0x24, 0xf7, 0x9e, 0xc4,
+ 0x59, 0x6e, 0xbb, 0x73, 0x87, 0x17, 0xf2, 0x32, 0x46, 0x28, 0xb8, 0x43,
+ 0xfa, 0xb7, 0x1d, 0xaa, 0xca, 0xb4, 0xf2, 0x9f, 0x24, 0x0e, 0x2d, 0x4b,
+ 0xf7, 0x71, 0x5c, 0x5e, 0x69, 0xff, 0xea, 0x95, 0x02, 0xcb, 0x38, 0x8a,
+ 0xae, 0x50, 0x38, 0x6f, 0xdb, 0xfb, 0x2d, 0x62, 0x1b, 0xc5, 0xc7, 0x1e,
+ 0x54, 0xe1, 0x77, 0xe0, 0x67, 0xc8, 0x0f, 0x9c, 0x87, 0x23, 0xd6, 0x3f,
+ 0x40, 0x20, 0x7f, 0x20, 0x80, 0xc4, 0x80, 0x4c, 0x3e, 0x3b, 0x24, 0x26,
+ 0x8e, 0x04, 0xae, 0x6c, 0x9a, 0xc8, 0xaa, 0x0d, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x31, 0x30, 0x82, 0x01, 0x2d, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01,
+ 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x84, 0x18, 0xcc, 0x85, 0x34, 0xec, 0xbc,
+ 0x0c, 0x94, 0x94, 0x2e, 0x08, 0x59, 0x9c, 0xc7, 0xb2, 0x10, 0x4e, 0x0a,
+ 0x08, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x9c, 0x5f, 0x00, 0xdf, 0xaa, 0x01, 0xd7, 0x30, 0x2b, 0x38,
+ 0x88, 0xa2, 0xb8, 0x6d, 0x4a, 0x9c, 0xf2, 0x11, 0x91, 0x83, 0x30, 0x78,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x6c,
+ 0x30, 0x6a, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f,
+ 0x63, 0x73, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x61,
+ 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x38, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x74, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x61, 0x6d,
+ 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e, 0x63, 0x65, 0x72,
+ 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x36, 0x30, 0x34, 0x30,
+ 0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32,
+ 0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x67, 0x32, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a,
+ 0x30, 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x62, 0x37, 0x42, 0x5c, 0xbc, 0x10,
+ 0xb5, 0x3e, 0x8b, 0x2c, 0xe9, 0x0c, 0x9b, 0x6c, 0x45, 0xe2, 0x07, 0x00,
+ 0x7a, 0xf9, 0xc5, 0x58, 0x0b, 0xb9, 0x08, 0x8c, 0x3e, 0xed, 0xb3, 0x25,
+ 0x3c, 0xb5, 0x6f, 0x50, 0xe4, 0xcd, 0x35, 0x6a, 0xa7, 0x93, 0x34, 0x96,
+ 0x32, 0x21, 0xa9, 0x48, 0x44, 0xab, 0x9c, 0xed, 0x3d, 0xb4, 0xaa, 0x73,
+ 0x6d, 0xe4, 0x7f, 0x16, 0x80, 0x89, 0x6c, 0xcf, 0x28, 0x03, 0x18, 0x83,
+ 0x47, 0x79, 0xa3, 0x10, 0x7e, 0x30, 0x5b, 0xac, 0x3b, 0xb0, 0x60, 0xe0,
+ 0x77, 0xd4, 0x08, 0xa6, 0xe1, 0x1d, 0x7c, 0x5e, 0xc0, 0xbb, 0xf9, 0x9a,
+ 0x7b, 0x22, 0x9d, 0xa7, 0x00, 0x09, 0x7e, 0xac, 0x46, 0x17, 0x83, 0xdc,
+ 0x9c, 0x26, 0x57, 0x99, 0x30, 0x39, 0x62, 0x96, 0x8f, 0xed, 0xda, 0xde,
+ 0xaa, 0xc5, 0xcc, 0x1b, 0x3e, 0xca, 0x43, 0x68, 0x6c, 0x57, 0x16, 0xbc,
+ 0xd5, 0x0e, 0x20, 0x2e, 0xfe, 0xff, 0xc2, 0x6a, 0x5d, 0x2e, 0xa0, 0x4a,
+ 0x6d, 0x14, 0x58, 0x87, 0x94, 0xe6, 0x39, 0x31, 0x5f, 0x7c, 0x73, 0xcb,
+ 0x90, 0x88, 0x6a, 0x84, 0x11, 0x96, 0x27, 0xa6, 0xed, 0xd9, 0x81, 0x46,
+ 0xa6, 0x7e, 0xa3, 0x72, 0x00, 0x0a, 0x52, 0x3e, 0x83, 0x88, 0x07, 0x63,
+ 0x77, 0x89, 0x69, 0x17, 0x0f, 0x39, 0x85, 0xd2, 0xab, 0x08, 0x45, 0x4d,
+ 0xd0, 0x51, 0x3a, 0xfd, 0x5d, 0x5d, 0x37, 0x64, 0x4c, 0x7e, 0x30, 0xb2,
+ 0x55, 0x24, 0x42, 0x9d, 0x36, 0xb0, 0x5d, 0x9c, 0x17, 0x81, 0x61, 0xf1,
+ 0xca, 0xf9, 0x10, 0x02, 0x24, 0xab, 0xeb, 0x0d, 0x74, 0x91, 0x8d, 0x7b,
+ 0x45, 0x29, 0x50, 0x39, 0x88, 0xb2, 0xa6, 0x89, 0x35, 0x25, 0x1e, 0x14,
+ 0x6a, 0x47, 0x23, 0x31, 0x2f, 0x5c, 0x9a, 0xfa, 0xad, 0x9a, 0x0e, 0x62,
+ 0x51, 0xa4, 0x2a, 0xa9, 0xc4, 0xf9, 0x34, 0x9d, 0x21, 0x18,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 01:fd:a3:eb:6e:ca:75:c8:88:43:8b:72:4b:cf:bc:91
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA
+ Validity
+ Not Before: Mar 8 12:00:00 2013 GMT
+ Not After : Mar 8 12:00:00 2023 GMT
+ Subject: C=US, O=DigiCert Inc, CN=DigiCert SHA2 Secure Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:dc:ae:58:90:4d:c1:c4:30:15:90:35:5b:6e:3c:
+ 82:15:f5:2c:5c:bd:e3:db:ff:71:43:fa:64:25:80:
+ d4:ee:18:a2:4d:f0:66:d0:0a:73:6e:11:98:36:17:
+ 64:af:37:9d:fd:fa:41:84:af:c7:af:8c:fe:1a:73:
+ 4d:cf:33:97:90:a2:96:87:53:83:2b:b9:a6:75:48:
+ 2d:1d:56:37:7b:da:31:32:1a:d7:ac:ab:06:f4:aa:
+ 5d:4b:b7:47:46:dd:2a:93:c3:90:2e:79:80:80:ef:
+ 13:04:6a:14:3b:b5:9b:92:be:c2:07:65:4e:fc:da:
+ fc:ff:7a:ae:dc:5c:7e:55:31:0c:e8:39:07:a4:d7:
+ be:2f:d3:0b:6a:d2:b1:df:5f:fe:57:74:53:3b:35:
+ 80:dd:ae:8e:44:98:b3:9f:0e:d3:da:e0:d7:f4:6b:
+ 29:ab:44:a7:4b:58:84:6d:92:4b:81:c3:da:73:8b:
+ 12:97:48:90:04:45:75:1a:dd:37:31:97:92:e8:cd:
+ 54:0d:3b:e4:c1:3f:39:5e:2e:b8:f3:5c:7e:10:8e:
+ 86:41:00:8d:45:66:47:b0:a1:65:ce:a0:aa:29:09:
+ 4e:f3:97:eb:e8:2e:ab:0f:72:a7:30:0e:fa:c7:f4:
+ fd:14:77:c3:a4:5b:28:57:c2:b3:f9:82:fd:b7:45:
+ 58:9b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertGlobalRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertGlobalRootCA.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.digicert.com/CPS
+
+ X509v3 Subject Key Identifier:
+ 0F:80:61:1C:82:31:61:D5:2F:28:E7:8D:46:38:B4:2C:E1:C6:D9:E2
+ X509v3 Authority Key Identifier:
+ keyid:03:DE:50:35:56:D1:4C:BB:66:F0:A3:E2:1B:1B:C3:97:B2:3D:D1:55
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 23:3e:df:4b:d2:31:42:a5:b6:7e:42:5c:1a:44:cc:69:d1:68:
+ b4:5d:4b:e0:04:21:6c:4b:e2:6d:cc:b1:e0:97:8f:a6:53:09:
+ cd:aa:2a:65:e5:39:4f:1e:83:a5:6e:5c:98:a2:24:26:e6:fb:
+ a1:ed:93:c7:2e:02:c6:4d:4a:bf:b0:42:df:78:da:b3:a8:f9:
+ 6d:ff:21:85:53:36:60:4c:76:ce:ec:38:dc:d6:51:80:f0:c5:
+ d6:e5:d4:4d:27:64:ab:9b:c7:3e:71:fb:48:97:b8:33:6d:c9:
+ 13:07:ee:96:a2:1b:18:15:f6:5c:4c:40:ed:b3:c2:ec:ff:71:
+ c1:e3:47:ff:d4:b9:00:b4:37:42:da:20:c9:ea:6e:8a:ee:14:
+ 06:ae:7d:a2:59:98:88:a8:1b:6f:2d:f4:f2:c9:14:5f:26:cf:
+ 2c:8d:7e:ed:37:c0:a9:d5:39:b9:82:bf:19:0c:ea:34:af:00:
+ 21:68:f8:ad:73:e2:c9:32:da:38:25:0b:55:d3:9a:1d:f0:68:
+ 86:ed:2e:41:34:ef:7c:a5:50:1d:bf:3a:f9:d3:c1:08:0c:e6:
+ ed:1e:8a:58:25:e4:b8:77:ad:2d:6e:f5:52:dd:b4:74:8f:ab:
+ 49:2e:9d:3b:93:34:28:1f:78:ce:94:ea:c7:bd:d3:c9:6d:1c:
+ de:5c:32:f3
+-----BEGIN CERTIFICATE-----
+MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg
+U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83
+nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd
+KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f
+/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX
+kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0
+/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY
+aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6
+Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1
+oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD
+QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
+d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh
+xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB
+CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl
+5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA
+8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC
+2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit
+c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0
+j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert24[] = {
+ 0x30, 0x82, 0x04, 0x94, 0x30, 0x82, 0x03, 0x7c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x01, 0xfd, 0xa3, 0xeb, 0x6e, 0xca, 0x75, 0xc8, 0x88,
+ 0x43, 0x8b, 0x72, 0x4b, 0xcf, 0xbc, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x61,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
+ 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x33, 0x30, 0x38, 0x31,
+ 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x33,
+ 0x30, 0x38, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4d, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44,
+ 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31,
+ 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1e, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41, 0x32, 0x20,
+ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65,
+ 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xdc, 0xae, 0x58, 0x90, 0x4d, 0xc1, 0xc4, 0x30, 0x15, 0x90, 0x35,
+ 0x5b, 0x6e, 0x3c, 0x82, 0x15, 0xf5, 0x2c, 0x5c, 0xbd, 0xe3, 0xdb, 0xff,
+ 0x71, 0x43, 0xfa, 0x64, 0x25, 0x80, 0xd4, 0xee, 0x18, 0xa2, 0x4d, 0xf0,
+ 0x66, 0xd0, 0x0a, 0x73, 0x6e, 0x11, 0x98, 0x36, 0x17, 0x64, 0xaf, 0x37,
+ 0x9d, 0xfd, 0xfa, 0x41, 0x84, 0xaf, 0xc7, 0xaf, 0x8c, 0xfe, 0x1a, 0x73,
+ 0x4d, 0xcf, 0x33, 0x97, 0x90, 0xa2, 0x96, 0x87, 0x53, 0x83, 0x2b, 0xb9,
+ 0xa6, 0x75, 0x48, 0x2d, 0x1d, 0x56, 0x37, 0x7b, 0xda, 0x31, 0x32, 0x1a,
+ 0xd7, 0xac, 0xab, 0x06, 0xf4, 0xaa, 0x5d, 0x4b, 0xb7, 0x47, 0x46, 0xdd,
+ 0x2a, 0x93, 0xc3, 0x90, 0x2e, 0x79, 0x80, 0x80, 0xef, 0x13, 0x04, 0x6a,
+ 0x14, 0x3b, 0xb5, 0x9b, 0x92, 0xbe, 0xc2, 0x07, 0x65, 0x4e, 0xfc, 0xda,
+ 0xfc, 0xff, 0x7a, 0xae, 0xdc, 0x5c, 0x7e, 0x55, 0x31, 0x0c, 0xe8, 0x39,
+ 0x07, 0xa4, 0xd7, 0xbe, 0x2f, 0xd3, 0x0b, 0x6a, 0xd2, 0xb1, 0xdf, 0x5f,
+ 0xfe, 0x57, 0x74, 0x53, 0x3b, 0x35, 0x80, 0xdd, 0xae, 0x8e, 0x44, 0x98,
+ 0xb3, 0x9f, 0x0e, 0xd3, 0xda, 0xe0, 0xd7, 0xf4, 0x6b, 0x29, 0xab, 0x44,
+ 0xa7, 0x4b, 0x58, 0x84, 0x6d, 0x92, 0x4b, 0x81, 0xc3, 0xda, 0x73, 0x8b,
+ 0x12, 0x97, 0x48, 0x90, 0x04, 0x45, 0x75, 0x1a, 0xdd, 0x37, 0x31, 0x97,
+ 0x92, 0xe8, 0xcd, 0x54, 0x0d, 0x3b, 0xe4, 0xc1, 0x3f, 0x39, 0x5e, 0x2e,
+ 0xb8, 0xf3, 0x5c, 0x7e, 0x10, 0x8e, 0x86, 0x41, 0x00, 0x8d, 0x45, 0x66,
+ 0x47, 0xb0, 0xa1, 0x65, 0xce, 0xa0, 0xaa, 0x29, 0x09, 0x4e, 0xf3, 0x97,
+ 0xeb, 0xe8, 0x2e, 0xab, 0x0f, 0x72, 0xa7, 0x30, 0x0e, 0xfa, 0xc7, 0xf4,
+ 0xfd, 0x14, 0x77, 0xc3, 0xa4, 0x5b, 0x28, 0x57, 0xc2, 0xb3, 0xf9, 0x82,
+ 0xfd, 0xb7, 0x45, 0x58, 0x9b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x5a, 0x30, 0x82, 0x01, 0x56, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+ 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72, 0x30,
+ 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63,
+ 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69,
+ 0x43, 0x65, 0x72, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f,
+ 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x37, 0xa0, 0x35,
+ 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72,
+ 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0f, 0x80, 0x61, 0x1c, 0x82,
+ 0x31, 0x61, 0xd5, 0x2f, 0x28, 0xe7, 0x8d, 0x46, 0x38, 0xb4, 0x2c, 0xe1,
+ 0xc6, 0xd9, 0xe2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb,
+ 0x66, 0xf0, 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x23, 0x3e, 0xdf, 0x4b,
+ 0xd2, 0x31, 0x42, 0xa5, 0xb6, 0x7e, 0x42, 0x5c, 0x1a, 0x44, 0xcc, 0x69,
+ 0xd1, 0x68, 0xb4, 0x5d, 0x4b, 0xe0, 0x04, 0x21, 0x6c, 0x4b, 0xe2, 0x6d,
+ 0xcc, 0xb1, 0xe0, 0x97, 0x8f, 0xa6, 0x53, 0x09, 0xcd, 0xaa, 0x2a, 0x65,
+ 0xe5, 0x39, 0x4f, 0x1e, 0x83, 0xa5, 0x6e, 0x5c, 0x98, 0xa2, 0x24, 0x26,
+ 0xe6, 0xfb, 0xa1, 0xed, 0x93, 0xc7, 0x2e, 0x02, 0xc6, 0x4d, 0x4a, 0xbf,
+ 0xb0, 0x42, 0xdf, 0x78, 0xda, 0xb3, 0xa8, 0xf9, 0x6d, 0xff, 0x21, 0x85,
+ 0x53, 0x36, 0x60, 0x4c, 0x76, 0xce, 0xec, 0x38, 0xdc, 0xd6, 0x51, 0x80,
+ 0xf0, 0xc5, 0xd6, 0xe5, 0xd4, 0x4d, 0x27, 0x64, 0xab, 0x9b, 0xc7, 0x3e,
+ 0x71, 0xfb, 0x48, 0x97, 0xb8, 0x33, 0x6d, 0xc9, 0x13, 0x07, 0xee, 0x96,
+ 0xa2, 0x1b, 0x18, 0x15, 0xf6, 0x5c, 0x4c, 0x40, 0xed, 0xb3, 0xc2, 0xec,
+ 0xff, 0x71, 0xc1, 0xe3, 0x47, 0xff, 0xd4, 0xb9, 0x00, 0xb4, 0x37, 0x42,
+ 0xda, 0x20, 0xc9, 0xea, 0x6e, 0x8a, 0xee, 0x14, 0x06, 0xae, 0x7d, 0xa2,
+ 0x59, 0x98, 0x88, 0xa8, 0x1b, 0x6f, 0x2d, 0xf4, 0xf2, 0xc9, 0x14, 0x5f,
+ 0x26, 0xcf, 0x2c, 0x8d, 0x7e, 0xed, 0x37, 0xc0, 0xa9, 0xd5, 0x39, 0xb9,
+ 0x82, 0xbf, 0x19, 0x0c, 0xea, 0x34, 0xaf, 0x00, 0x21, 0x68, 0xf8, 0xad,
+ 0x73, 0xe2, 0xc9, 0x32, 0xda, 0x38, 0x25, 0x0b, 0x55, 0xd3, 0x9a, 0x1d,
+ 0xf0, 0x68, 0x86, 0xed, 0x2e, 0x41, 0x34, 0xef, 0x7c, 0xa5, 0x50, 0x1d,
+ 0xbf, 0x3a, 0xf9, 0xd3, 0xc1, 0x08, 0x0c, 0xe6, 0xed, 0x1e, 0x8a, 0x58,
+ 0x25, 0xe4, 0xb8, 0x77, 0xad, 0x2d, 0x6e, 0xf5, 0x52, 0xdd, 0xb4, 0x74,
+ 0x8f, 0xab, 0x49, 0x2e, 0x9d, 0x3b, 0x93, 0x34, 0x28, 0x1f, 0x78, 0xce,
+ 0x94, 0xea, 0xc7, 0xbd, 0xd3, 0xc9, 0x6d, 0x1c, 0xde, 0x5c, 0x32, 0xf3,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 3740804 (0x391484)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority
+ Validity
+ Not Before: Jan 1 07:00:00 2014 GMT
+ Not After : May 30 07:00:00 2031 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:ed:c1:03:fc:f6:8f:fc:02:b1:6f:5b:9f:48:
+ d9:9d:79:e2:a2:b7:03:61:56:18:c3:47:b6:d7:ca:
+ 3d:35:2e:89:43:f7:a1:69:9b:de:8a:1a:fd:13:20:
+ 9c:b4:49:77:32:29:56:fd:b9:ec:8c:dd:22:fa:72:
+ dc:27:61:97:ee:f6:5a:84:ec:6e:19:b9:89:2c:dc:
+ 84:5b:d5:74:fb:6b:5f:c5:89:a5:10:52:89:46:55:
+ f4:b8:75:1c:e6:7f:e4:54:ae:4b:f8:55:72:57:02:
+ 19:f8:17:71:59:eb:1e:28:07:74:c5:9d:48:be:6c:
+ b4:f4:a4:b0:f3:64:37:79:92:c0:ec:46:5e:7f:e1:
+ 6d:53:4c:62:af:cd:1f:0b:63:bb:3a:9d:fb:fc:79:
+ 00:98:61:74:cf:26:82:40:63:f3:b2:72:6a:19:0d:
+ 99:ca:d4:0e:75:cc:37:fb:8b:89:c1:59:f1:62:7f:
+ 5f:b3:5f:65:30:f8:a7:b7:4d:76:5a:1e:76:5e:34:
+ c0:e8:96:56:99:8a:b3:f0:7f:a4:cd:bd:dc:32:31:
+ 7c:91:cf:e0:5f:11:f8:6b:aa:49:5c:d1:99:94:d1:
+ a2:e3:63:5b:09:76:b5:56:62:e1:4b:74:1d:96:d4:
+ 26:d4:08:04:59:d0:98:0e:0e:e6:de:fc:c3:ec:1f:
+ 90:f1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27
+ X509v3 Authority Key Identifier:
+ keyid:BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.starfieldtech.com/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.starfieldtech.com/sfroot.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://certs.starfieldtech.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 85:63:c1:d9:dd:b9:ff:a9:bd:a6:19:dc:bf:13:3a:11:38:22:
+ 54:b1:ac:05:10:fb:7c:b3:96:3f:31:8b:66:ff:88:f3:e1:bf:
+ fb:c7:1f:00:ff:46:6a:8b:61:32:c9:01:51:76:fb:9a:c6:fa:
+ 20:51:c8:46:c4:98:d7:79:a3:e3:04:72:3f:8b:4d:34:53:67:
+ ec:33:2c:7b:e8:94:01:28:7c:3a:34:5b:02:77:16:8d:40:25:
+ 33:b0:bc:6c:97:d7:05:7a:ff:8c:85:ce:6f:a0:53:00:17:6e:
+ 1e:6c:bd:22:d7:0a:88:37:f6:7d:eb:99:41:ef:27:cb:8c:60:
+ 6b:4c:01:7e:65:50:0b:4f:b8:95:9a:9a:6e:34:fd:73:3a:33:
+ f1:91:d5:f3:4e:2d:74:e8:ef:d3:90:35:f1:06:68:64:d4:d0:
+ 13:fd:52:d3:c6:6d:c1:3a:8a:31:dd:05:26:35:4a:8c:65:b8:
+ 52:6b:81:ec:d2:9c:b5:34:10:97:9c:3e:c6:2f:ed:8e:42:42:
+ 24:2e:e9:73:9a:25:f9:11:f1:f2:23:69:cb:e5:94:69:a0:d2:
+ dc:b0:fc:44:89:ac:17:a8:cc:d5:37:77:16:c5:80:b9:0c:8f:
+ 57:02:55:99:85:7b:49:f0:2e:5b:a0:c2:57:53:5d:a2:e8:a6:
+ 37:c3:01:fa
+-----BEGIN CERTIFICATE-----
+MIIEoDCCA4igAwIBAgIDORSEMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYTAlVT
+MSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQL
+EylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x
+NDAxMDEwNzAwMDBaFw0zMTA1MzAwNzAwMDBaMIGPMQswCQYDVQQGEwJVUzEQMA4G
+A1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UEChMcU3Rh
+cmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UEAxMpU3RhcmZpZWxkIFJv
+b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC97cED/PaP/AKxb1ufSNmdeeKitwNhVhjDR7bXyj01LolD
+96Fpm96KGv0TIJy0SXcyKVb9ueyM3SL6ctwnYZfu9lqE7G4ZuYks3IRb1XT7a1/F
+iaUQUolGVfS4dRzmf+RUrkv4VXJXAhn4F3FZ6x4oB3TFnUi+bLT0pLDzZDd5ksDs
+Rl5/4W1TTGKvzR8LY7s6nfv8eQCYYXTPJoJAY/OycmoZDZnK1A51zDf7i4nBWfFi
+f1+zX2Uw+Ke3TXZaHnZeNMDollaZirPwf6TNvdwyMXyRz+BfEfhrqklc0ZmU0aLj
+Y1sJdrVWYuFLdB2W1CbUCARZ0JgODube/MPsH5DxAgMBAAGjggEpMIIBJTAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfAwyH6fZMH/E
+fWijYqihzqsHWycwHwYDVR0jBBgwFoAUv1+30c7dH4b0W1Ws3NcQwg6piOcwOgYI
+KwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0
+ZWNoLmNvbS8wOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL2NybC5zdGFyZmllbGR0
+ZWNoLmNvbS9zZnJvb3QuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF
+BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQCFY8HZ3bn/qb2mGdy/EzoROCJUsawFEPt8s5Y/
+MYtm/4jz4b/7xx8A/0Zqi2EyyQFRdvuaxvogUchGxJjXeaPjBHI/i000U2fsMyx7
+6JQBKHw6NFsCdxaNQCUzsLxsl9cFev+Mhc5voFMAF24ebL0i1wqIN/Z965lB7yfL
+jGBrTAF+ZVALT7iVmppuNP1zOjPxkdXzTi106O/TkDXxBmhk1NAT/VLTxm3BOoox
+3QUmNUqMZbhSa4Hs0py1NBCXnD7GL+2OQkIkLulzmiX5EfHyI2nL5ZRpoNLcsPxE
+iawXqMzVN3cWxYC5DI9XAlWZhXtJ8C5boMJXU12i6KY3wwH6
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert25[] = {
+ 0x30, 0x82, 0x04, 0xa0, 0x30, 0x82, 0x03, 0x88, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x39, 0x14, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x68, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63,
+ 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20,
+ 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x34, 0x30, 0x31, 0x30, 0x31, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x33, 0x30, 0x30, 0x37, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x30, 0x81, 0x8f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e,
+ 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a,
+ 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25,
+ 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61,
+ 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e,
+ 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x29,
+ 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xbd, 0xed, 0xc1, 0x03, 0xfc, 0xf6, 0x8f, 0xfc, 0x02, 0xb1,
+ 0x6f, 0x5b, 0x9f, 0x48, 0xd9, 0x9d, 0x79, 0xe2, 0xa2, 0xb7, 0x03, 0x61,
+ 0x56, 0x18, 0xc3, 0x47, 0xb6, 0xd7, 0xca, 0x3d, 0x35, 0x2e, 0x89, 0x43,
+ 0xf7, 0xa1, 0x69, 0x9b, 0xde, 0x8a, 0x1a, 0xfd, 0x13, 0x20, 0x9c, 0xb4,
+ 0x49, 0x77, 0x32, 0x29, 0x56, 0xfd, 0xb9, 0xec, 0x8c, 0xdd, 0x22, 0xfa,
+ 0x72, 0xdc, 0x27, 0x61, 0x97, 0xee, 0xf6, 0x5a, 0x84, 0xec, 0x6e, 0x19,
+ 0xb9, 0x89, 0x2c, 0xdc, 0x84, 0x5b, 0xd5, 0x74, 0xfb, 0x6b, 0x5f, 0xc5,
+ 0x89, 0xa5, 0x10, 0x52, 0x89, 0x46, 0x55, 0xf4, 0xb8, 0x75, 0x1c, 0xe6,
+ 0x7f, 0xe4, 0x54, 0xae, 0x4b, 0xf8, 0x55, 0x72, 0x57, 0x02, 0x19, 0xf8,
+ 0x17, 0x71, 0x59, 0xeb, 0x1e, 0x28, 0x07, 0x74, 0xc5, 0x9d, 0x48, 0xbe,
+ 0x6c, 0xb4, 0xf4, 0xa4, 0xb0, 0xf3, 0x64, 0x37, 0x79, 0x92, 0xc0, 0xec,
+ 0x46, 0x5e, 0x7f, 0xe1, 0x6d, 0x53, 0x4c, 0x62, 0xaf, 0xcd, 0x1f, 0x0b,
+ 0x63, 0xbb, 0x3a, 0x9d, 0xfb, 0xfc, 0x79, 0x00, 0x98, 0x61, 0x74, 0xcf,
+ 0x26, 0x82, 0x40, 0x63, 0xf3, 0xb2, 0x72, 0x6a, 0x19, 0x0d, 0x99, 0xca,
+ 0xd4, 0x0e, 0x75, 0xcc, 0x37, 0xfb, 0x8b, 0x89, 0xc1, 0x59, 0xf1, 0x62,
+ 0x7f, 0x5f, 0xb3, 0x5f, 0x65, 0x30, 0xf8, 0xa7, 0xb7, 0x4d, 0x76, 0x5a,
+ 0x1e, 0x76, 0x5e, 0x34, 0xc0, 0xe8, 0x96, 0x56, 0x99, 0x8a, 0xb3, 0xf0,
+ 0x7f, 0xa4, 0xcd, 0xbd, 0xdc, 0x32, 0x31, 0x7c, 0x91, 0xcf, 0xe0, 0x5f,
+ 0x11, 0xf8, 0x6b, 0xaa, 0x49, 0x5c, 0xd1, 0x99, 0x94, 0xd1, 0xa2, 0xe3,
+ 0x63, 0x5b, 0x09, 0x76, 0xb5, 0x56, 0x62, 0xe1, 0x4b, 0x74, 0x1d, 0x96,
+ 0xd4, 0x26, 0xd4, 0x08, 0x04, 0x59, 0xd0, 0x98, 0x0e, 0x0e, 0xe6, 0xde,
+ 0xfc, 0xc3, 0xec, 0x1f, 0x90, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x29, 0x30, 0x82, 0x01, 0x25, 0x30, 0x0f, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x7c, 0x0c, 0x32, 0x1f, 0xa7, 0xd9, 0x30, 0x7f, 0xc4,
+ 0x7d, 0x68, 0xa3, 0x62, 0xa8, 0xa1, 0xce, 0xab, 0x07, 0x5b, 0x27, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0xbf, 0x5f, 0xb7, 0xd1, 0xce, 0xdd, 0x1f, 0x86, 0xf4, 0x5b, 0x55, 0xac,
+ 0xdc, 0xd7, 0x10, 0xc2, 0x0e, 0xa9, 0x88, 0xe7, 0x30, 0x3a, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2e, 0x30, 0x2c,
+ 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74,
+ 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x38, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0xa0, 0x2b, 0xa0,
+ 0x29, 0x86, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74,
+ 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x72, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x85, 0x63, 0xc1, 0xd9,
+ 0xdd, 0xb9, 0xff, 0xa9, 0xbd, 0xa6, 0x19, 0xdc, 0xbf, 0x13, 0x3a, 0x11,
+ 0x38, 0x22, 0x54, 0xb1, 0xac, 0x05, 0x10, 0xfb, 0x7c, 0xb3, 0x96, 0x3f,
+ 0x31, 0x8b, 0x66, 0xff, 0x88, 0xf3, 0xe1, 0xbf, 0xfb, 0xc7, 0x1f, 0x00,
+ 0xff, 0x46, 0x6a, 0x8b, 0x61, 0x32, 0xc9, 0x01, 0x51, 0x76, 0xfb, 0x9a,
+ 0xc6, 0xfa, 0x20, 0x51, 0xc8, 0x46, 0xc4, 0x98, 0xd7, 0x79, 0xa3, 0xe3,
+ 0x04, 0x72, 0x3f, 0x8b, 0x4d, 0x34, 0x53, 0x67, 0xec, 0x33, 0x2c, 0x7b,
+ 0xe8, 0x94, 0x01, 0x28, 0x7c, 0x3a, 0x34, 0x5b, 0x02, 0x77, 0x16, 0x8d,
+ 0x40, 0x25, 0x33, 0xb0, 0xbc, 0x6c, 0x97, 0xd7, 0x05, 0x7a, 0xff, 0x8c,
+ 0x85, 0xce, 0x6f, 0xa0, 0x53, 0x00, 0x17, 0x6e, 0x1e, 0x6c, 0xbd, 0x22,
+ 0xd7, 0x0a, 0x88, 0x37, 0xf6, 0x7d, 0xeb, 0x99, 0x41, 0xef, 0x27, 0xcb,
+ 0x8c, 0x60, 0x6b, 0x4c, 0x01, 0x7e, 0x65, 0x50, 0x0b, 0x4f, 0xb8, 0x95,
+ 0x9a, 0x9a, 0x6e, 0x34, 0xfd, 0x73, 0x3a, 0x33, 0xf1, 0x91, 0xd5, 0xf3,
+ 0x4e, 0x2d, 0x74, 0xe8, 0xef, 0xd3, 0x90, 0x35, 0xf1, 0x06, 0x68, 0x64,
+ 0xd4, 0xd0, 0x13, 0xfd, 0x52, 0xd3, 0xc6, 0x6d, 0xc1, 0x3a, 0x8a, 0x31,
+ 0xdd, 0x05, 0x26, 0x35, 0x4a, 0x8c, 0x65, 0xb8, 0x52, 0x6b, 0x81, 0xec,
+ 0xd2, 0x9c, 0xb5, 0x34, 0x10, 0x97, 0x9c, 0x3e, 0xc6, 0x2f, 0xed, 0x8e,
+ 0x42, 0x42, 0x24, 0x2e, 0xe9, 0x73, 0x9a, 0x25, 0xf9, 0x11, 0xf1, 0xf2,
+ 0x23, 0x69, 0xcb, 0xe5, 0x94, 0x69, 0xa0, 0xd2, 0xdc, 0xb0, 0xfc, 0x44,
+ 0x89, 0xac, 0x17, 0xa8, 0xcc, 0xd5, 0x37, 0x77, 0x16, 0xc5, 0x80, 0xb9,
+ 0x0c, 0x8f, 0x57, 0x02, 0x55, 0x99, 0x85, 0x7b, 0x49, 0xf0, 0x2e, 0x5b,
+ 0xa0, 0xc2, 0x57, 0x53, 0x5d, 0xa2, 0xe8, 0xa6, 0x37, 0xc3, 0x01, 0xfa,
+};
+
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3b.inc b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3b.inc
new file mode 100644
index 00000000000..f175f240509
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_3b.inc
@@ -0,0 +1,5717 @@
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 28:1c:89:29:66:14:43:80:42:63:55:3a:32:40:ae:b3
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., OU=(c) 2008 GeoTrust Inc. - For authorized use only, CN=GeoTrust Primary Certification Authority - G3
+ Validity
+ Not Before: Jun 30 00:00:00 2015 GMT
+ Not After : Jun 29 23:59:59 2025 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c0:9e:3a:0f:9a:b2:ba:d3:d2:dc:15:ec:d0:30:
+ 54:59:30:4d:40:51:ae:42:71:71:d2:8d:53:73:81:
+ fe:b8:e0:c4:96:c5:8e:7e:c2:f1:b7:63:4a:cf:a7:
+ 1e:3f:a8:e7:ce:53:a0:fa:2d:f7:d6:e6:ce:70:11:
+ a6:ee:e1:03:52:d2:68:de:3d:08:0d:87:fd:1c:d7:
+ 0b:97:62:6d:82:30:76:1b:47:3a:c4:f7:ce:ed:1d:
+ 7c:8c:b7:17:8e:53:80:1e:1d:0f:5d:8c:f9:90:e4:
+ 04:1e:02:7e:cb:b0:49:ef:da:52:25:fb:fb:67:ed:
+ dd:84:74:59:84:0e:f3:de:70:66:8d:e4:52:38:f7:
+ 53:5a:37:13:67:0b:3e:bb:a8:58:b7:2e:ed:ff:b7:
+ 5e:11:73:b9:77:45:52:67:46:ae:c4:dc:24:81:89:
+ 76:0a:ca:a1:6c:66:73:04:82:aa:f5:70:6c:5f:1b:
+ 9a:00:79:46:d6:7f:7a:26:17:30:cf:39:4b:2c:74:
+ d9:89:44:76:10:d0:ed:f7:8b:bb:89:05:75:4d:0b:
+ 0d:b3:da:e9:bf:f1:6a:7d:2a:11:db:1e:9f:8c:e3:
+ c4:06:69:e1:1d:88:45:39:d1:6e:55:d8:aa:b7:9b:
+ 6f:ea:f4:de:ac:17:11:92:5d:40:9b:83:7b:9a:e2:
+ f7:a9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.23.140.1.2.1
+ CPS: https://www.geotrust.com/resources/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/GeoTrustPCA-G3.crl
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ F3:B5:56:0C:C4:09:B0:B4:CF:1F:AA:F9:DD:23:56:F0:77:E8:A1:F9
+ X509v3 Authority Key Identifier:
+ keyid:C4:79:CA:8E:A1:4E:03:1D:1C:DC:6B:DB:31:5B:94:3E:3F:30:7F:2D
+
+ Signature Algorithm: sha256WithRSAEncryption
+ c3:7e:d8:83:4b:04:4c:55:29:2a:4f:14:9d:9a:6e:de:90:70:
+ c1:a4:26:4c:88:8e:78:48:ef:bd:9c:b0:a0:f5:f0:66:fc:fe:
+ 59:26:e1:79:ef:c8:b7:60:64:a8:8b:47:ea:2f:e0:83:99:da:
+ 41:19:d7:c5:be:05:fa:f2:90:11:f0:0a:ff:6c:dc:05:b4:d8:
+ 06:6f:a4:6f:8d:be:20:2b:54:db:f9:a2:45:83:9a:1e:a5:21:
+ 89:35:1d:7c:20:5c:17:fd:04:2e:45:d8:b2:c6:f8:42:99:fc:
+ 54:08:4e:4b:80:5f:39:37:ba:95:4e:a6:37:0a:9e:93:5e:87:
+ 5b:e9:90:d6:a8:b6:65:08:8d:61:49:eb:83:20:a9:5d:1b:16:
+ 60:62:6b:2f:54:fb:5a:02:0d:7a:27:e2:4b:e1:05:14:c2:e4:
+ e9:f9:70:c0:d9:f7:34:65:0e:a2:91:4b:ac:28:f2:b7:08:0f:
+ 98:ca:d7:3e:70:b6:c8:0b:f1:8b:9c:51:f8:c6:10:6c:d2:53:
+ 4f:62:8c:11:00:3e:88:df:bf:e6:d2:cc:70:bd:ed:25:9c:fb:
+ dd:24:0a:bd:59:91:4a:42:03:38:12:71:32:88:76:a0:8e:7c:
+ bb:32:ef:88:2a:1b:d4:6a:6f:50:b9:52:67:8b:ab:30:fa:1f:
+ fd:e3:24:9a
+-----BEGIN CERTIFICATE-----
+MIIEpjCCA46gAwIBAgIQKByJKWYUQ4BCY1U6MkCuszANBgkqhkiG9w0BAQsFADCB
+mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
+MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEczMB4XDTE1MDYzMDAwMDAwMFoXDTI1MDYyOTIzNTk1OVowRzELMAkG
+A1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xIDAeBgNVBAMTF1JhcGlk
+U1NMIFNIQTI1NiBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAwJ46D5qyutPS3BXs0DBUWTBNQFGuQnFx0o1Tc4H+uODElsWOfsLxt2NKz6ce
+P6jnzlOg+i331ubOcBGm7uEDUtJo3j0IDYf9HNcLl2JtgjB2G0c6xPfO7R18jLcX
+jlOAHh0PXYz5kOQEHgJ+y7BJ79pSJfv7Z+3dhHRZhA7z3nBmjeRSOPdTWjcTZws+
+u6hYty7t/7deEXO5d0VSZ0auxNwkgYl2CsqhbGZzBIKq9XBsXxuaAHlG1n96Jhcw
+zzlLLHTZiUR2ENDt94u7iQV1TQsNs9rpv/FqfSoR2x6fjOPEBmnhHYhFOdFuVdiq
+t5tv6vTerBcRkl1Am4N7muL3qQIDAQABo4IBOjCCATYwLgYIKwYBBQUHAQEEIjAg
+MB4GCCsGAQUFBzABhhJodHRwOi8vZy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB
+/wIBADBJBgNVHSAEQjBAMD4GBmeBDAECATA0MDIGCCsGAQUFBwIBFiZodHRwczov
+L3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczA2BgNVHR8ELzAtMCugKaAn
+hiVodHRwOi8vZy5zeW1jYi5jb20vR2VvVHJ1c3RQQ0EtRzMuY3JsMB0GA1UdJQQW
+MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
+FPO1VgzECbC0zx+q+d0jVvB36KH5MB8GA1UdIwQYMBaAFMR5yo6hTgMdHNxr2zFb
+lD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQDDftiDSwRMVSkqTxSdmm7ekHDBpCZM
+iI54SO+9nLCg9fBm/P5ZJuF578i3YGSoi0fqL+CDmdpBGdfFvgX68pAR8Ar/bNwF
+tNgGb6Rvjb4gK1Tb+aJFg5oepSGJNR18IFwX/QQuRdiyxvhCmfxUCE5LgF85N7qV
+TqY3Cp6TXodb6ZDWqLZlCI1hSeuDIKldGxZgYmsvVPtaAg16J+JL4QUUwuTp+XDA
+2fc0ZQ6ikUusKPK3CA+Yytc+cLbIC/GLnFH4xhBs0lNPYowRAD6I37/m0sxwve0l
+nPvdJAq9WZFKQgM4EnEyiHagjny7Mu+IKhvUam9QuVJni6sw+h/94ySa
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert26[] = {
+ 0x30, 0x82, 0x04, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x28, 0x1c, 0x89, 0x29, 0x66, 0x14, 0x43, 0x80, 0x42,
+ 0x63, 0x55, 0x3a, 0x32, 0x40, 0xae, 0xb3, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x47, 0x65,
+ 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20,
+ 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c,
+ 0x79, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69,
+ 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x35, 0x30, 0x36, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x35, 0x30, 0x36, 0x32, 0x39, 0x32, 0x33,
+ 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64,
+ 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43,
+ 0x41, 0x20, 0x2d, 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xc0, 0x9e, 0x3a, 0x0f, 0x9a, 0xb2, 0xba, 0xd3, 0xd2,
+ 0xdc, 0x15, 0xec, 0xd0, 0x30, 0x54, 0x59, 0x30, 0x4d, 0x40, 0x51, 0xae,
+ 0x42, 0x71, 0x71, 0xd2, 0x8d, 0x53, 0x73, 0x81, 0xfe, 0xb8, 0xe0, 0xc4,
+ 0x96, 0xc5, 0x8e, 0x7e, 0xc2, 0xf1, 0xb7, 0x63, 0x4a, 0xcf, 0xa7, 0x1e,
+ 0x3f, 0xa8, 0xe7, 0xce, 0x53, 0xa0, 0xfa, 0x2d, 0xf7, 0xd6, 0xe6, 0xce,
+ 0x70, 0x11, 0xa6, 0xee, 0xe1, 0x03, 0x52, 0xd2, 0x68, 0xde, 0x3d, 0x08,
+ 0x0d, 0x87, 0xfd, 0x1c, 0xd7, 0x0b, 0x97, 0x62, 0x6d, 0x82, 0x30, 0x76,
+ 0x1b, 0x47, 0x3a, 0xc4, 0xf7, 0xce, 0xed, 0x1d, 0x7c, 0x8c, 0xb7, 0x17,
+ 0x8e, 0x53, 0x80, 0x1e, 0x1d, 0x0f, 0x5d, 0x8c, 0xf9, 0x90, 0xe4, 0x04,
+ 0x1e, 0x02, 0x7e, 0xcb, 0xb0, 0x49, 0xef, 0xda, 0x52, 0x25, 0xfb, 0xfb,
+ 0x67, 0xed, 0xdd, 0x84, 0x74, 0x59, 0x84, 0x0e, 0xf3, 0xde, 0x70, 0x66,
+ 0x8d, 0xe4, 0x52, 0x38, 0xf7, 0x53, 0x5a, 0x37, 0x13, 0x67, 0x0b, 0x3e,
+ 0xbb, 0xa8, 0x58, 0xb7, 0x2e, 0xed, 0xff, 0xb7, 0x5e, 0x11, 0x73, 0xb9,
+ 0x77, 0x45, 0x52, 0x67, 0x46, 0xae, 0xc4, 0xdc, 0x24, 0x81, 0x89, 0x76,
+ 0x0a, 0xca, 0xa1, 0x6c, 0x66, 0x73, 0x04, 0x82, 0xaa, 0xf5, 0x70, 0x6c,
+ 0x5f, 0x1b, 0x9a, 0x00, 0x79, 0x46, 0xd6, 0x7f, 0x7a, 0x26, 0x17, 0x30,
+ 0xcf, 0x39, 0x4b, 0x2c, 0x74, 0xd9, 0x89, 0x44, 0x76, 0x10, 0xd0, 0xed,
+ 0xf7, 0x8b, 0xbb, 0x89, 0x05, 0x75, 0x4d, 0x0b, 0x0d, 0xb3, 0xda, 0xe9,
+ 0xbf, 0xf1, 0x6a, 0x7d, 0x2a, 0x11, 0xdb, 0x1e, 0x9f, 0x8c, 0xe3, 0xc4,
+ 0x06, 0x69, 0xe1, 0x1d, 0x88, 0x45, 0x39, 0xd1, 0x6e, 0x55, 0xd8, 0xaa,
+ 0xb7, 0x9b, 0x6f, 0xea, 0xf4, 0xde, 0xac, 0x17, 0x11, 0x92, 0x5d, 0x40,
+ 0x9b, 0x83, 0x7b, 0x9a, 0xe2, 0xf7, 0xa9, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x3a, 0x30, 0x82, 0x01, 0x36, 0x30, 0x2e, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20,
+ 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73,
+ 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x49, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x42, 0x30, 0x40, 0x30, 0x3e, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02,
+ 0x01, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x36, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27,
+ 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73,
+ 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2d, 0x47, 0x33, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16,
+ 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0xf3, 0xb5, 0x56, 0x0c, 0xc4, 0x09, 0xb0, 0xb4, 0xcf, 0x1f, 0xaa,
+ 0xf9, 0xdd, 0x23, 0x56, 0xf0, 0x77, 0xe8, 0xa1, 0xf9, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc4, 0x79,
+ 0xca, 0x8e, 0xa1, 0x4e, 0x03, 0x1d, 0x1c, 0xdc, 0x6b, 0xdb, 0x31, 0x5b,
+ 0x94, 0x3e, 0x3f, 0x30, 0x7f, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0xc3, 0x7e, 0xd8, 0x83, 0x4b, 0x04, 0x4c, 0x55, 0x29, 0x2a,
+ 0x4f, 0x14, 0x9d, 0x9a, 0x6e, 0xde, 0x90, 0x70, 0xc1, 0xa4, 0x26, 0x4c,
+ 0x88, 0x8e, 0x78, 0x48, 0xef, 0xbd, 0x9c, 0xb0, 0xa0, 0xf5, 0xf0, 0x66,
+ 0xfc, 0xfe, 0x59, 0x26, 0xe1, 0x79, 0xef, 0xc8, 0xb7, 0x60, 0x64, 0xa8,
+ 0x8b, 0x47, 0xea, 0x2f, 0xe0, 0x83, 0x99, 0xda, 0x41, 0x19, 0xd7, 0xc5,
+ 0xbe, 0x05, 0xfa, 0xf2, 0x90, 0x11, 0xf0, 0x0a, 0xff, 0x6c, 0xdc, 0x05,
+ 0xb4, 0xd8, 0x06, 0x6f, 0xa4, 0x6f, 0x8d, 0xbe, 0x20, 0x2b, 0x54, 0xdb,
+ 0xf9, 0xa2, 0x45, 0x83, 0x9a, 0x1e, 0xa5, 0x21, 0x89, 0x35, 0x1d, 0x7c,
+ 0x20, 0x5c, 0x17, 0xfd, 0x04, 0x2e, 0x45, 0xd8, 0xb2, 0xc6, 0xf8, 0x42,
+ 0x99, 0xfc, 0x54, 0x08, 0x4e, 0x4b, 0x80, 0x5f, 0x39, 0x37, 0xba, 0x95,
+ 0x4e, 0xa6, 0x37, 0x0a, 0x9e, 0x93, 0x5e, 0x87, 0x5b, 0xe9, 0x90, 0xd6,
+ 0xa8, 0xb6, 0x65, 0x08, 0x8d, 0x61, 0x49, 0xeb, 0x83, 0x20, 0xa9, 0x5d,
+ 0x1b, 0x16, 0x60, 0x62, 0x6b, 0x2f, 0x54, 0xfb, 0x5a, 0x02, 0x0d, 0x7a,
+ 0x27, 0xe2, 0x4b, 0xe1, 0x05, 0x14, 0xc2, 0xe4, 0xe9, 0xf9, 0x70, 0xc0,
+ 0xd9, 0xf7, 0x34, 0x65, 0x0e, 0xa2, 0x91, 0x4b, 0xac, 0x28, 0xf2, 0xb7,
+ 0x08, 0x0f, 0x98, 0xca, 0xd7, 0x3e, 0x70, 0xb6, 0xc8, 0x0b, 0xf1, 0x8b,
+ 0x9c, 0x51, 0xf8, 0xc6, 0x10, 0x6c, 0xd2, 0x53, 0x4f, 0x62, 0x8c, 0x11,
+ 0x00, 0x3e, 0x88, 0xdf, 0xbf, 0xe6, 0xd2, 0xcc, 0x70, 0xbd, 0xed, 0x25,
+ 0x9c, 0xfb, 0xdd, 0x24, 0x0a, 0xbd, 0x59, 0x91, 0x4a, 0x42, 0x03, 0x38,
+ 0x12, 0x71, 0x32, 0x88, 0x76, 0xa0, 0x8e, 0x7c, 0xbb, 0x32, 0xef, 0x88,
+ 0x2a, 0x1b, 0xd4, 0x6a, 0x6f, 0x50, 0xb9, 0x52, 0x67, 0x8b, 0xab, 0x30,
+ 0xfa, 0x1f, 0xfd, 0xe3, 0x24, 0x9a,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ e4:05:47:83:0e:0c:64:52:97:6f:7a:35:49:c0:dd:48
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=PL, O=Unizeto Technologies S.A., OU=Certum Certification Authority, CN=Certum Trusted Network CA
+ Validity
+ Not Before: Jan 21 12:00:00 2015 GMT
+ Not After : Jan 18 12:00:00 2025 GMT
+ Subject: C=RU, O=Yandex LLC, OU=Yandex Certification Authority, CN=Yandex CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a6:05:24:76:61:b9:9e:42:60:22:63:85:59:e5:
+ 9d:88:0d:df:ef:21:64:5a:26:94:71:3a:a4:7f:2b:
+ 53:c3:ac:7b:ba:95:42:6d:6a:5b:d6:7e:78:0c:67:
+ 40:98:2f:6a:2d:d0:b7:18:3a:7e:99:60:01:e5:27:
+ bf:ff:49:f5:cd:c4:58:c3:4c:e1:70:d5:fd:08:a8:
+ 79:95:76:1c:0e:05:41:fa:bd:80:38:2a:87:4f:c1:
+ 67:42:aa:17:a6:ee:a7:8c:8e:ef:2d:7f:7a:1d:05:
+ 17:8f:7e:3b:92:35:f5:68:ed:93:03:55:23:4f:4b:
+ a2:00:86:65:91:0f:eb:f6:3c:d5:db:6d:0e:ed:e8:
+ 7c:3a:c8:ba:b7:53:c1:a4:d8:40:02:e5:b5:a2:ca:
+ bf:da:9c:94:0d:fc:c5:1c:2a:59:88:62:57:93:2e:
+ 11:f0:38:2c:7a:81:2a:f2:25:15:17:35:70:2c:4b:
+ f7:23:4c:82:ef:33:9f:c2:9a:0b:a3:e2:5d:6b:38:
+ 77:f9:60:33:cf:2e:7b:56:b7:13:93:1f:34:97:71:
+ 99:76:02:46:35:14:7c:dc:ca:48:8a:0a:72:4b:78:
+ 6d:82:34:96:13:45:cf:02:2f:50:13:39:43:89:c0:
+ e1:74:d7:28:71:21:e5:aa:97:0e:ee:46:ec:93:f7:
+ 23:7d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ 37:5C:E3:19:E0:B2:8E:A1:A8:4E:D2:CF:AB:D0:DC:E3:0B:5C:35:4D
+ X509v3 Authority Key Identifier:
+ keyid:08:76:CD:CB:07:FF:24:F6:C5:CD:ED:BB:90:BC:E2:84:37:46:75:F7
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.certum.pl/ctnca.crl
+
+ Authority Information Access:
+ OCSP - URI:http://subca.ocsp-certum.com
+ CA Issuers - URI:http://repository.certum.pl/ctnca.cer
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.certum.pl/CPS
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 02:5e:8e:7b:e0:66:a1:c6:ab:8b:18:1f:0e:b9:c4:cd:71:db:
+ 44:5c:03:7d:65:ea:b8:47:b5:1e:ce:24:70:a0:7f:d3:df:66:
+ 4b:8c:90:e2:a5:ed:9b:94:36:b4:a8:be:f0:74:8c:26:92:75:
+ 9d:56:50:9e:ad:d0:1a:a0:df:a4:14:56:10:75:93:7a:c1:f4:
+ 53:a0:76:74:2c:72:ba:b5:d1:c9:e2:dc:46:86:3f:1d:f6:33:
+ 87:59:ec:9c:dc:2d:1e:4d:43:1a:ce:ba:d9:87:7e:e2:47:45:
+ 72:3d:28:03:c9:0a:4d:e0:57:a3:5e:6e:7e:cc:5a:c8:c4:78:
+ 01:57:68:7a:38:3b:53:36:e7:92:6d:8a:2c:2f:d7:8b:b6:34:
+ a8:d1:b6:f8:5e:3b:ab:ed:a5:8f:39:6f:45:ad:cb:63:ed:6a:
+ 64:c9:10:a7:03:08:12:53:b1:1c:af:ca:f7:53:fc:d8:29:4b:
+ 1b:fb:38:cd:c0:63:ff:5f:e4:b9:8d:5e:aa:2b:d2:c3:22:35:
+ 31:f6:30:0e:53:32:f4:93:c5:43:cb:c8:f0:15:56:8f:00:19:
+ 87:ca:78:22:8d:a0:2e:db:2f:a0:c3:7e:29:5d:91:25:84:1d:
+ 1d:39:ab:1b:c5:d6:91:fe:69:0e:46:80:bc:45:7b:35:53:2a:
+ df:00:b6:77
+-----BEGIN CERTIFICATE-----
+MIIEqDCCA5CgAwIBAgIRAOQFR4MODGRSl296NUnA3UgwDQYJKoZIhvcNAQELBQAw
+fjELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
+QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEiMCAG
+A1UEAxMZQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQTAeFw0xNTAxMjExMjAwMDBa
+Fw0yNTAxMTgxMjAwMDBaMF8xCzAJBgNVBAYTAlJVMRMwEQYDVQQKEwpZYW5kZXgg
+TExDMScwJQYDVQQLEx5ZYW5kZXggQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEjAQ
+BgNVBAMTCVlhbmRleCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AKYFJHZhuZ5CYCJjhVnlnYgN3+8hZFomlHE6pH8rU8Ose7qVQm1qW9Z+eAxnQJgv
+ai3Qtxg6fplgAeUnv/9J9c3EWMNM4XDV/QioeZV2HA4FQfq9gDgqh0/BZ0KqF6bu
+p4yO7y1/eh0FF49+O5I19WjtkwNVI09LogCGZZEP6/Y81dttDu3ofDrIurdTwaTY
+QALltaLKv9qclA38xRwqWYhiV5MuEfA4LHqBKvIlFRc1cCxL9yNMgu8zn8KaC6Pi
+XWs4d/lgM88ue1a3E5MfNJdxmXYCRjUUfNzKSIoKckt4bYI0lhNFzwIvUBM5Q4nA
+4XTXKHEh5aqXDu5G7JP3I30CAwEAAaOCAT4wggE6MA8GA1UdEwEB/wQFMAMBAf8w
+HQYDVR0OBBYEFDdc4xngso6hqE7Sz6vQ3OMLXDVNMB8GA1UdIwQYMBaAFAh2zcsH
+/yT2xc3tu5C84oQ3RnX3MA4GA1UdDwEB/wQEAwIBBjAvBgNVHR8EKDAmMCSgIqAg
+hh5odHRwOi8vY3JsLmNlcnR1bS5wbC9jdG5jYS5jcmwwawYIKwYBBQUHAQEEXzBd
+MCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0uY29tMDEGCCsG
+AQUFBzAChiVodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvY3RuY2EuY2VyMDkG
+A1UdIAQyMDAwLgYEVR0gADAmMCQGCCsGAQUFBwIBFhhodHRwOi8vd3d3LmNlcnR1
+bS5wbC9DUFMwDQYJKoZIhvcNAQELBQADggEBAAJejnvgZqHGq4sYHw65xM1x20Rc
+A31l6rhHtR7OJHCgf9PfZkuMkOKl7ZuUNrSovvB0jCaSdZ1WUJ6t0Bqg36QUVhB1
+k3rB9FOgdnQscrq10cni3EaGPx32M4dZ7JzcLR5NQxrOutmHfuJHRXI9KAPJCk3g
+V6Nebn7MWsjEeAFXaHo4O1M255Jtiiwv14u2NKjRtvheO6vtpY85b0Wty2PtamTJ
+EKcDCBJTsRyvyvdT/NgpSxv7OM3AY/9f5LmNXqor0sMiNTH2MA5TMvSTxUPLyPAV
+Vo8AGYfKeCKNoC7bL6DDfildkSWEHR05qxvF1pH+aQ5GgLxFezVTKt8Atnc=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert27[] = {
+ 0x30, 0x82, 0x04, 0xa8, 0x30, 0x82, 0x03, 0x90, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x11, 0x00, 0xe4, 0x05, 0x47, 0x83, 0x0e, 0x0c, 0x64, 0x52,
+ 0x97, 0x6f, 0x7a, 0x35, 0x49, 0xc0, 0xdd, 0x48, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
+ 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x50, 0x4c, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x19, 0x55, 0x6e, 0x69, 0x7a, 0x65, 0x74, 0x6f, 0x20, 0x54, 0x65, 0x63,
+ 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x20, 0x53, 0x2e,
+ 0x41, 0x2e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1e, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x22, 0x30, 0x20, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d,
+ 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x4e, 0x65, 0x74,
+ 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x35, 0x30, 0x31, 0x32, 0x31, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x17, 0x0d, 0x32, 0x35, 0x30, 0x31, 0x31, 0x38, 0x31, 0x32, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x52, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x59, 0x61, 0x6e, 0x64, 0x65, 0x78, 0x20,
+ 0x4c, 0x4c, 0x43, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x59, 0x61, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x43, 0x65, 0x72,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x59, 0x61, 0x6e, 0x64, 0x65,
+ 0x78, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xa6, 0x05, 0x24, 0x76, 0x61, 0xb9, 0x9e, 0x42, 0x60, 0x22, 0x63,
+ 0x85, 0x59, 0xe5, 0x9d, 0x88, 0x0d, 0xdf, 0xef, 0x21, 0x64, 0x5a, 0x26,
+ 0x94, 0x71, 0x3a, 0xa4, 0x7f, 0x2b, 0x53, 0xc3, 0xac, 0x7b, 0xba, 0x95,
+ 0x42, 0x6d, 0x6a, 0x5b, 0xd6, 0x7e, 0x78, 0x0c, 0x67, 0x40, 0x98, 0x2f,
+ 0x6a, 0x2d, 0xd0, 0xb7, 0x18, 0x3a, 0x7e, 0x99, 0x60, 0x01, 0xe5, 0x27,
+ 0xbf, 0xff, 0x49, 0xf5, 0xcd, 0xc4, 0x58, 0xc3, 0x4c, 0xe1, 0x70, 0xd5,
+ 0xfd, 0x08, 0xa8, 0x79, 0x95, 0x76, 0x1c, 0x0e, 0x05, 0x41, 0xfa, 0xbd,
+ 0x80, 0x38, 0x2a, 0x87, 0x4f, 0xc1, 0x67, 0x42, 0xaa, 0x17, 0xa6, 0xee,
+ 0xa7, 0x8c, 0x8e, 0xef, 0x2d, 0x7f, 0x7a, 0x1d, 0x05, 0x17, 0x8f, 0x7e,
+ 0x3b, 0x92, 0x35, 0xf5, 0x68, 0xed, 0x93, 0x03, 0x55, 0x23, 0x4f, 0x4b,
+ 0xa2, 0x00, 0x86, 0x65, 0x91, 0x0f, 0xeb, 0xf6, 0x3c, 0xd5, 0xdb, 0x6d,
+ 0x0e, 0xed, 0xe8, 0x7c, 0x3a, 0xc8, 0xba, 0xb7, 0x53, 0xc1, 0xa4, 0xd8,
+ 0x40, 0x02, 0xe5, 0xb5, 0xa2, 0xca, 0xbf, 0xda, 0x9c, 0x94, 0x0d, 0xfc,
+ 0xc5, 0x1c, 0x2a, 0x59, 0x88, 0x62, 0x57, 0x93, 0x2e, 0x11, 0xf0, 0x38,
+ 0x2c, 0x7a, 0x81, 0x2a, 0xf2, 0x25, 0x15, 0x17, 0x35, 0x70, 0x2c, 0x4b,
+ 0xf7, 0x23, 0x4c, 0x82, 0xef, 0x33, 0x9f, 0xc2, 0x9a, 0x0b, 0xa3, 0xe2,
+ 0x5d, 0x6b, 0x38, 0x77, 0xf9, 0x60, 0x33, 0xcf, 0x2e, 0x7b, 0x56, 0xb7,
+ 0x13, 0x93, 0x1f, 0x34, 0x97, 0x71, 0x99, 0x76, 0x02, 0x46, 0x35, 0x14,
+ 0x7c, 0xdc, 0xca, 0x48, 0x8a, 0x0a, 0x72, 0x4b, 0x78, 0x6d, 0x82, 0x34,
+ 0x96, 0x13, 0x45, 0xcf, 0x02, 0x2f, 0x50, 0x13, 0x39, 0x43, 0x89, 0xc0,
+ 0xe1, 0x74, 0xd7, 0x28, 0x71, 0x21, 0xe5, 0xaa, 0x97, 0x0e, 0xee, 0x46,
+ 0xec, 0x93, 0xf7, 0x23, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x3e, 0x30, 0x82, 0x01, 0x3a, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x37, 0x5c,
+ 0xe3, 0x19, 0xe0, 0xb2, 0x8e, 0xa1, 0xa8, 0x4e, 0xd2, 0xcf, 0xab, 0xd0,
+ 0xdc, 0xe3, 0x0b, 0x5c, 0x35, 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x08, 0x76, 0xcd, 0xcb, 0x07,
+ 0xff, 0x24, 0xf6, 0xc5, 0xcd, 0xed, 0xbb, 0x90, 0xbc, 0xe2, 0x84, 0x37,
+ 0x46, 0x75, 0xf7, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0xa0, 0x22, 0xa0, 0x20,
+ 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x2e, 0x70, 0x6c, 0x2f, 0x63,
+ 0x74, 0x6e, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x6b, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x5f, 0x30, 0x5d,
+ 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x75, 0x62,
+ 0x63, 0x61, 0x2e, 0x6f, 0x63, 0x73, 0x70, 0x2d, 0x63, 0x65, 0x72, 0x74,
+ 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x25, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x2e, 0x70, 0x6c, 0x2f,
+ 0x63, 0x74, 0x6e, 0x63, 0x61, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x39, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x32, 0x30, 0x30, 0x30, 0x2e, 0x06, 0x04,
+ 0x55, 0x1d, 0x20, 0x00, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x18, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75,
+ 0x6d, 0x2e, 0x70, 0x6c, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x02, 0x5e, 0x8e, 0x7b, 0xe0, 0x66, 0xa1, 0xc6,
+ 0xab, 0x8b, 0x18, 0x1f, 0x0e, 0xb9, 0xc4, 0xcd, 0x71, 0xdb, 0x44, 0x5c,
+ 0x03, 0x7d, 0x65, 0xea, 0xb8, 0x47, 0xb5, 0x1e, 0xce, 0x24, 0x70, 0xa0,
+ 0x7f, 0xd3, 0xdf, 0x66, 0x4b, 0x8c, 0x90, 0xe2, 0xa5, 0xed, 0x9b, 0x94,
+ 0x36, 0xb4, 0xa8, 0xbe, 0xf0, 0x74, 0x8c, 0x26, 0x92, 0x75, 0x9d, 0x56,
+ 0x50, 0x9e, 0xad, 0xd0, 0x1a, 0xa0, 0xdf, 0xa4, 0x14, 0x56, 0x10, 0x75,
+ 0x93, 0x7a, 0xc1, 0xf4, 0x53, 0xa0, 0x76, 0x74, 0x2c, 0x72, 0xba, 0xb5,
+ 0xd1, 0xc9, 0xe2, 0xdc, 0x46, 0x86, 0x3f, 0x1d, 0xf6, 0x33, 0x87, 0x59,
+ 0xec, 0x9c, 0xdc, 0x2d, 0x1e, 0x4d, 0x43, 0x1a, 0xce, 0xba, 0xd9, 0x87,
+ 0x7e, 0xe2, 0x47, 0x45, 0x72, 0x3d, 0x28, 0x03, 0xc9, 0x0a, 0x4d, 0xe0,
+ 0x57, 0xa3, 0x5e, 0x6e, 0x7e, 0xcc, 0x5a, 0xc8, 0xc4, 0x78, 0x01, 0x57,
+ 0x68, 0x7a, 0x38, 0x3b, 0x53, 0x36, 0xe7, 0x92, 0x6d, 0x8a, 0x2c, 0x2f,
+ 0xd7, 0x8b, 0xb6, 0x34, 0xa8, 0xd1, 0xb6, 0xf8, 0x5e, 0x3b, 0xab, 0xed,
+ 0xa5, 0x8f, 0x39, 0x6f, 0x45, 0xad, 0xcb, 0x63, 0xed, 0x6a, 0x64, 0xc9,
+ 0x10, 0xa7, 0x03, 0x08, 0x12, 0x53, 0xb1, 0x1c, 0xaf, 0xca, 0xf7, 0x53,
+ 0xfc, 0xd8, 0x29, 0x4b, 0x1b, 0xfb, 0x38, 0xcd, 0xc0, 0x63, 0xff, 0x5f,
+ 0xe4, 0xb9, 0x8d, 0x5e, 0xaa, 0x2b, 0xd2, 0xc3, 0x22, 0x35, 0x31, 0xf6,
+ 0x30, 0x0e, 0x53, 0x32, 0xf4, 0x93, 0xc5, 0x43, 0xcb, 0xc8, 0xf0, 0x15,
+ 0x56, 0x8f, 0x00, 0x19, 0x87, 0xca, 0x78, 0x22, 0x8d, 0xa0, 0x2e, 0xdb,
+ 0x2f, 0xa0, 0xc3, 0x7e, 0x29, 0x5d, 0x91, 0x25, 0x84, 0x1d, 0x1d, 0x39,
+ 0xab, 0x1b, 0xc5, 0xd6, 0x91, 0xfe, 0x69, 0x0e, 0x46, 0x80, 0xbc, 0x45,
+ 0x7b, 0x35, 0x53, 0x2a, 0xdf, 0x00, 0xb6, 0x77,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5d:72:fb:33:76:20:f6:4c:72:80:db:e9:12:81:ff:6a
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=thawte, Inc., CN=thawte EV SSL CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c4:dd:da:94:1e:32:b2:2e:a0:83:c0:a6:7d:5f:
+ 65:2d:fd:27:b8:73:0e:f8:0b:a9:d4:56:26:69:98:
+ 67:35:39:64:58:ce:82:6f:98:94:d1:8f:e0:90:d6:
+ ed:55:4b:98:4b:d7:10:59:34:02:1b:e7:51:31:51:
+ c4:38:c2:bc:db:03:5c:ca:e1:7c:dc:4f:59:97:ea:
+ 07:7f:0f:85:3e:92:ea:aa:a7:d9:be:01:41:e4:62:
+ 56:47:36:bd:57:91:e6:21:d3:f8:41:0b:d8:ba:e8:
+ ed:81:ad:70:c0:8b:6e:f3:89:6e:27:9e:a6:a6:73:
+ 59:bb:71:00:d4:4f:4b:48:e9:d5:c9:27:36:9c:7c:
+ 1c:02:aa:ac:bd:3b:d1:53:83:6a:1f:e6:08:47:33:
+ a7:b1:9f:02:be:9b:47:ed:33:04:dc:1c:80:27:d1:
+ 4a:33:a0:8c:eb:01:47:a1:32:90:64:7b:c4:e0:84:
+ c9:32:e9:dd:34:1f:8a:68:67:f3:ad:10:63:eb:ee:
+ 8a:9a:b1:2a:1b:26:74:a1:2a:b0:8f:fe:52:98:46:
+ 97:cf:a3:56:1c:6f:6e:99:97:8d:26:0e:a9:ec:c2:
+ 53:70:fc:7a:a5:19:49:bd:b5:17:82:55:de:97:e0:
+ 5d:62:84:81:f0:70:a8:34:53:4f:14:fd:3d:5d:3d:
+ 6f:b9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://t2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.thawte.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://t1.symcb.com/ThawtePCA.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-536
+ X509v3 Subject Key Identifier:
+ F0:70:51:DA:D3:2A:91:4F:52:77:D7:86:77:74:0F:CE:71:1A:6C:22
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha256WithRSAEncryption
+ a1:2e:94:3e:9b:16:f4:58:1a:6f:c1:fa:c1:7e:43:93:b2:c3:
+ f7:89:eb:13:62:5d:dd:cc:61:13:2b:1d:4e:88:79:11:62:14:
+ 37:30:46:ff:89:62:10:85:2a:87:1e:f8:e2:af:fe:93:02:93:
+ ca:f2:e9:46:03:6b:a1:1a:ac:d5:f0:80:1b:98:6f:b8:3a:50:
+ f8:54:71:06:03:e7:84:cc:8e:61:d2:5f:4d:0c:97:02:65:b5:
+ 8c:26:bc:05:98:f4:dc:c6:af:e4:57:7f:e3:dc:a1:d7:27:47:
+ 2a:e0:2c:3f:09:74:dc:5a:e5:b5:7c:fa:82:9a:15:fa:74:2b:
+ 84:2e:6b:ac:ef:35:a6:30:fa:47:4a:aa:36:44:f6:5a:91:07:
+ d3:e4:4e:97:3f:a6:53:d8:29:33:32:6f:8b:3d:b5:a5:0d:e5:
+ e4:8a:e8:f5:c0:fa:af:d8:37:28:27:c3:ed:34:31:d9:7c:a6:
+ af:4d:12:4f:d0:2b:92:9c:69:95:f2:28:a6:fe:a8:c6:e0:2c:
+ 4d:36:eb:11:34:d6:e1:81:99:9d:41:f2:e7:c5:57:05:0e:19:
+ ca:af:42:39:1f:a7:27:5e:e0:0a:17:b8:ae:47:ab:92:f1:8a:
+ 04:df:30:e0:bb:4f:8a:f9:1b:88:4f:03:b4:25:7a:78:de:2e:
+ 7d:29:d1:31
+-----BEGIN CERTIFICATE-----
+MIIErzCCA5egAwIBAgIQXXL7M3Yg9kxygNvpEoH/ajANBgkqhkiG9w0BAQsFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMDMxMDAwMDAwWhcNMjMx
+MDMwMjM1OTU5WjBEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu
+MR4wHAYDVQQDExV0aGF3dGUgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDE3dqUHjKyLqCDwKZ9X2Ut/Se4cw74C6nUViZpmGc1
+OWRYzoJvmJTRj+CQ1u1VS5hL1xBZNAIb51ExUcQ4wrzbA1zK4XzcT1mX6gd/D4U+
+kuqqp9m+AUHkYlZHNr1XkeYh0/hBC9i66O2BrXDAi27ziW4nnqamc1m7cQDUT0tI
+6dXJJzacfBwCqqy9O9FTg2of5ghHM6exnwK+m0ftMwTcHIAn0UozoIzrAUehMpBk
+e8TghMky6d00H4poZ/OtEGPr7oqasSobJnShKrCP/lKYRpfPo1Ycb26Zl40mDqns
+wlNw/HqlGUm9tReCVd6X4F1ihIHwcKg0U08U/T1dPW+5AgMBAAGjggE1MIIBMTAS
+BgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAvBggrBgEFBQcBAQQj
+MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly90Mi5zeW1jYi5jb20wOwYDVR0gBDQwMjAw
+BgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3Bz
+MDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6Ly90MS5zeW1jYi5jb20vVGhhd3RlUENB
+LmNybDApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01MzYw
+HQYDVR0OBBYEFPBwUdrTKpFPUnfXhnd0D85xGmwiMB8GA1UdIwQYMBaAFHtbRc+v
+zst6/TGSGmq280brV0hQMA0GCSqGSIb3DQEBCwUAA4IBAQChLpQ+mxb0WBpvwfrB
+fkOTssP3iesTYl3dzGETKx1OiHkRYhQ3MEb/iWIQhSqHHvjir/6TApPK8ulGA2uh
+GqzV8IAbmG+4OlD4VHEGA+eEzI5h0l9NDJcCZbWMJrwFmPTcxq/kV3/j3KHXJ0cq
+4Cw/CXTcWuW1fPqCmhX6dCuELmus7zWmMPpHSqo2RPZakQfT5E6XP6ZT2CkzMm+L
+PbWlDeXkiuj1wPqv2DcoJ8PtNDHZfKavTRJP0CuSnGmV8iim/qjG4CxNNusRNNbh
+gZmdQfLnxVcFDhnKr0I5H6cnXuAKF7iuR6uS8YoE3zDgu0+K+RuITwO0JXp43i59
+KdEx
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert28[] = {
+ 0x30, 0x82, 0x04, 0xaf, 0x30, 0x82, 0x03, 0x97, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x5d, 0x72, 0xfb, 0x33, 0x76, 0x20, 0xf6, 0x4c, 0x72,
+ 0x80, 0xdb, 0xe9, 0x12, 0x81, 0xff, 0x6a, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31,
+ 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x44,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x74,
+ 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x45, 0x56, 0x20, 0x53, 0x53, 0x4c,
+ 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xc4, 0xdd, 0xda, 0x94, 0x1e, 0x32, 0xb2,
+ 0x2e, 0xa0, 0x83, 0xc0, 0xa6, 0x7d, 0x5f, 0x65, 0x2d, 0xfd, 0x27, 0xb8,
+ 0x73, 0x0e, 0xf8, 0x0b, 0xa9, 0xd4, 0x56, 0x26, 0x69, 0x98, 0x67, 0x35,
+ 0x39, 0x64, 0x58, 0xce, 0x82, 0x6f, 0x98, 0x94, 0xd1, 0x8f, 0xe0, 0x90,
+ 0xd6, 0xed, 0x55, 0x4b, 0x98, 0x4b, 0xd7, 0x10, 0x59, 0x34, 0x02, 0x1b,
+ 0xe7, 0x51, 0x31, 0x51, 0xc4, 0x38, 0xc2, 0xbc, 0xdb, 0x03, 0x5c, 0xca,
+ 0xe1, 0x7c, 0xdc, 0x4f, 0x59, 0x97, 0xea, 0x07, 0x7f, 0x0f, 0x85, 0x3e,
+ 0x92, 0xea, 0xaa, 0xa7, 0xd9, 0xbe, 0x01, 0x41, 0xe4, 0x62, 0x56, 0x47,
+ 0x36, 0xbd, 0x57, 0x91, 0xe6, 0x21, 0xd3, 0xf8, 0x41, 0x0b, 0xd8, 0xba,
+ 0xe8, 0xed, 0x81, 0xad, 0x70, 0xc0, 0x8b, 0x6e, 0xf3, 0x89, 0x6e, 0x27,
+ 0x9e, 0xa6, 0xa6, 0x73, 0x59, 0xbb, 0x71, 0x00, 0xd4, 0x4f, 0x4b, 0x48,
+ 0xe9, 0xd5, 0xc9, 0x27, 0x36, 0x9c, 0x7c, 0x1c, 0x02, 0xaa, 0xac, 0xbd,
+ 0x3b, 0xd1, 0x53, 0x83, 0x6a, 0x1f, 0xe6, 0x08, 0x47, 0x33, 0xa7, 0xb1,
+ 0x9f, 0x02, 0xbe, 0x9b, 0x47, 0xed, 0x33, 0x04, 0xdc, 0x1c, 0x80, 0x27,
+ 0xd1, 0x4a, 0x33, 0xa0, 0x8c, 0xeb, 0x01, 0x47, 0xa1, 0x32, 0x90, 0x64,
+ 0x7b, 0xc4, 0xe0, 0x84, 0xc9, 0x32, 0xe9, 0xdd, 0x34, 0x1f, 0x8a, 0x68,
+ 0x67, 0xf3, 0xad, 0x10, 0x63, 0xeb, 0xee, 0x8a, 0x9a, 0xb1, 0x2a, 0x1b,
+ 0x26, 0x74, 0xa1, 0x2a, 0xb0, 0x8f, 0xfe, 0x52, 0x98, 0x46, 0x97, 0xcf,
+ 0xa3, 0x56, 0x1c, 0x6f, 0x6e, 0x99, 0x97, 0x8d, 0x26, 0x0e, 0xa9, 0xec,
+ 0xc2, 0x53, 0x70, 0xfc, 0x7a, 0xa5, 0x19, 0x49, 0xbd, 0xb5, 0x17, 0x82,
+ 0x55, 0xde, 0x97, 0xe0, 0x5d, 0x62, 0x84, 0x81, 0xf0, 0x70, 0xa8, 0x34,
+ 0x53, 0x4f, 0x14, 0xfd, 0x3d, 0x5d, 0x3d, 0x6f, 0xb9, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x35, 0x30, 0x82, 0x01, 0x31, 0x30, 0x12,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
+ 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23,
+ 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74,
+ 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68,
+ 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73,
+ 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30,
+ 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x74, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04,
+ 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74,
+ 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35, 0x33, 0x36, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf0, 0x70,
+ 0x51, 0xda, 0xd3, 0x2a, 0x91, 0x4f, 0x52, 0x77, 0xd7, 0x86, 0x77, 0x74,
+ 0x0f, 0xce, 0x71, 0x1a, 0x6c, 0x22, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf,
+ 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb,
+ 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa1,
+ 0x2e, 0x94, 0x3e, 0x9b, 0x16, 0xf4, 0x58, 0x1a, 0x6f, 0xc1, 0xfa, 0xc1,
+ 0x7e, 0x43, 0x93, 0xb2, 0xc3, 0xf7, 0x89, 0xeb, 0x13, 0x62, 0x5d, 0xdd,
+ 0xcc, 0x61, 0x13, 0x2b, 0x1d, 0x4e, 0x88, 0x79, 0x11, 0x62, 0x14, 0x37,
+ 0x30, 0x46, 0xff, 0x89, 0x62, 0x10, 0x85, 0x2a, 0x87, 0x1e, 0xf8, 0xe2,
+ 0xaf, 0xfe, 0x93, 0x02, 0x93, 0xca, 0xf2, 0xe9, 0x46, 0x03, 0x6b, 0xa1,
+ 0x1a, 0xac, 0xd5, 0xf0, 0x80, 0x1b, 0x98, 0x6f, 0xb8, 0x3a, 0x50, 0xf8,
+ 0x54, 0x71, 0x06, 0x03, 0xe7, 0x84, 0xcc, 0x8e, 0x61, 0xd2, 0x5f, 0x4d,
+ 0x0c, 0x97, 0x02, 0x65, 0xb5, 0x8c, 0x26, 0xbc, 0x05, 0x98, 0xf4, 0xdc,
+ 0xc6, 0xaf, 0xe4, 0x57, 0x7f, 0xe3, 0xdc, 0xa1, 0xd7, 0x27, 0x47, 0x2a,
+ 0xe0, 0x2c, 0x3f, 0x09, 0x74, 0xdc, 0x5a, 0xe5, 0xb5, 0x7c, 0xfa, 0x82,
+ 0x9a, 0x15, 0xfa, 0x74, 0x2b, 0x84, 0x2e, 0x6b, 0xac, 0xef, 0x35, 0xa6,
+ 0x30, 0xfa, 0x47, 0x4a, 0xaa, 0x36, 0x44, 0xf6, 0x5a, 0x91, 0x07, 0xd3,
+ 0xe4, 0x4e, 0x97, 0x3f, 0xa6, 0x53, 0xd8, 0x29, 0x33, 0x32, 0x6f, 0x8b,
+ 0x3d, 0xb5, 0xa5, 0x0d, 0xe5, 0xe4, 0x8a, 0xe8, 0xf5, 0xc0, 0xfa, 0xaf,
+ 0xd8, 0x37, 0x28, 0x27, 0xc3, 0xed, 0x34, 0x31, 0xd9, 0x7c, 0xa6, 0xaf,
+ 0x4d, 0x12, 0x4f, 0xd0, 0x2b, 0x92, 0x9c, 0x69, 0x95, 0xf2, 0x28, 0xa6,
+ 0xfe, 0xa8, 0xc6, 0xe0, 0x2c, 0x4d, 0x36, 0xeb, 0x11, 0x34, 0xd6, 0xe1,
+ 0x81, 0x99, 0x9d, 0x41, 0xf2, 0xe7, 0xc5, 0x57, 0x05, 0x0e, 0x19, 0xca,
+ 0xaf, 0x42, 0x39, 0x1f, 0xa7, 0x27, 0x5e, 0xe0, 0x0a, 0x17, 0xb8, 0xae,
+ 0x47, 0xab, 0x92, 0xf1, 0x8a, 0x04, 0xdf, 0x30, 0xe0, 0xbb, 0x4f, 0x8a,
+ 0xf9, 0x1b, 0x88, 0x4f, 0x03, 0xb4, 0x25, 0x7a, 0x78, 0xde, 0x2e, 0x7d,
+ 0x29, 0xd1, 0x31,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:e1:e7:a4:dc:5c:f2:f3:6d:c0:2b:42:b8:5d:15:9f
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Oct 22 12:00:00 2013 GMT
+ Not After : Oct 22 12:00:00 2028 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b6:e0:2f:c2:24:06:c8:6d:04:5f:d7:ef:0a:64:
+ 06:b2:7d:22:26:65:16:ae:42:40:9b:ce:dc:9f:9f:
+ 76:07:3e:c3:30:55:87:19:b9:4f:94:0e:5a:94:1f:
+ 55:56:b4:c2:02:2a:af:d0:98:ee:0b:40:d7:c4:d0:
+ 3b:72:c8:14:9e:ef:90:b1:11:a9:ae:d2:c8:b8:43:
+ 3a:d9:0b:0b:d5:d5:95:f5:40:af:c8:1d:ed:4d:9c:
+ 5f:57:b7:86:50:68:99:f5:8a:da:d2:c7:05:1f:a8:
+ 97:c9:dc:a4:b1:82:84:2d:c6:ad:a5:9c:c7:19:82:
+ a6:85:0f:5e:44:58:2a:37:8f:fd:35:f1:0b:08:27:
+ 32:5a:f5:bb:8b:9e:a4:bd:51:d0:27:e2:dd:3b:42:
+ 33:a3:05:28:c4:bb:28:cc:9a:ac:2b:23:0d:78:c6:
+ 7b:e6:5e:71:b7:4a:3e:08:fb:81:b7:16:16:a1:9d:
+ 23:12:4d:e5:d7:92:08:ac:75:a4:9c:ba:cd:17:b2:
+ 1e:44:35:65:7f:53:25:39:d1:1c:0a:9a:63:1b:19:
+ 92:74:68:0a:37:c2:c2:52:48:cb:39:5a:a2:b6:e1:
+ 5d:c1:dd:a0:20:b8:21:a2:93:26:6f:14:4a:21:41:
+ c7:ed:6d:9b:f2:48:2f:f3:03:f5:a2:68:92:53:2f:
+ 5e:e3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.digicert.com/CPS
+
+ X509v3 Subject Key Identifier:
+ 51:68:FF:90:AF:02:07:75:3C:CC:D9:65:64:62:A2:12:B8:59:72:3B
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 18:8a:95:89:03:e6:6d:df:5c:fc:1d:68:ea:4a:8f:83:d6:51:
+ 2f:8d:6b:44:16:9e:ac:63:f5:d2:6e:6c:84:99:8b:aa:81:71:
+ 84:5b:ed:34:4e:b0:b7:79:92:29:cc:2d:80:6a:f0:8e:20:e1:
+ 79:a4:fe:03:47:13:ea:f5:86:ca:59:71:7d:f4:04:96:6b:d3:
+ 59:58:3d:fe:d3:31:25:5c:18:38:84:a3:e6:9f:82:fd:8c:5b:
+ 98:31:4e:cd:78:9e:1a:fd:85:cb:49:aa:f2:27:8b:99:72:fc:
+ 3e:aa:d5:41:0b:da:d5:36:a1:bf:1c:6e:47:49:7f:5e:d9:48:
+ 7c:03:d9:fd:8b:49:a0:98:26:42:40:eb:d6:92:11:a4:64:0a:
+ 57:54:c4:f5:1d:d6:02:5e:6b:ac:ee:c4:80:9a:12:72:fa:56:
+ 93:d7:ff:bf:30:85:06:30:bf:0b:7f:4e:ff:57:05:9d:24:ed:
+ 85:c3:2b:fb:a6:75:a8:ac:2d:16:ef:7d:79:27:b2:eb:c2:9d:
+ 0b:07:ea:aa:85:d3:01:a3:20:28:41:59:43:28:d2:81:e3:aa:
+ f6:ec:7b:3b:77:b6:40:62:80:05:41:45:01:ef:17:06:3e:de:
+ c0:33:9b:67:d3:61:2e:72:87:e4:69:fc:12:00:57:40:1e:70:
+ f5:1e:c9:b4
+-----BEGIN CERTIFICATE-----
+MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy
+YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2
+4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC
+Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1
+itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn
+4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X
+sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft
+bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA
+MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
+NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
+dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t
+L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG
+BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ
+UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D
+aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd
+aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH
+E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly
+/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu
+xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF
+0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae
+cPUeybQ=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert29[] = {
+ 0x30, 0x82, 0x04, 0xb1, 0x30, 0x82, 0x03, 0x99, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x04, 0xe1, 0xe7, 0xa4, 0xdc, 0x5c, 0xf2, 0xf3, 0x6d,
+ 0xc0, 0x2b, 0x42, 0xb8, 0x5d, 0x15, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x32, 0x32, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x30, 0x32,
+ 0x32, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x70, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41,
+ 0x32, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72,
+ 0x61, 0x6e, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6,
+ 0xe0, 0x2f, 0xc2, 0x24, 0x06, 0xc8, 0x6d, 0x04, 0x5f, 0xd7, 0xef, 0x0a,
+ 0x64, 0x06, 0xb2, 0x7d, 0x22, 0x26, 0x65, 0x16, 0xae, 0x42, 0x40, 0x9b,
+ 0xce, 0xdc, 0x9f, 0x9f, 0x76, 0x07, 0x3e, 0xc3, 0x30, 0x55, 0x87, 0x19,
+ 0xb9, 0x4f, 0x94, 0x0e, 0x5a, 0x94, 0x1f, 0x55, 0x56, 0xb4, 0xc2, 0x02,
+ 0x2a, 0xaf, 0xd0, 0x98, 0xee, 0x0b, 0x40, 0xd7, 0xc4, 0xd0, 0x3b, 0x72,
+ 0xc8, 0x14, 0x9e, 0xef, 0x90, 0xb1, 0x11, 0xa9, 0xae, 0xd2, 0xc8, 0xb8,
+ 0x43, 0x3a, 0xd9, 0x0b, 0x0b, 0xd5, 0xd5, 0x95, 0xf5, 0x40, 0xaf, 0xc8,
+ 0x1d, 0xed, 0x4d, 0x9c, 0x5f, 0x57, 0xb7, 0x86, 0x50, 0x68, 0x99, 0xf5,
+ 0x8a, 0xda, 0xd2, 0xc7, 0x05, 0x1f, 0xa8, 0x97, 0xc9, 0xdc, 0xa4, 0xb1,
+ 0x82, 0x84, 0x2d, 0xc6, 0xad, 0xa5, 0x9c, 0xc7, 0x19, 0x82, 0xa6, 0x85,
+ 0x0f, 0x5e, 0x44, 0x58, 0x2a, 0x37, 0x8f, 0xfd, 0x35, 0xf1, 0x0b, 0x08,
+ 0x27, 0x32, 0x5a, 0xf5, 0xbb, 0x8b, 0x9e, 0xa4, 0xbd, 0x51, 0xd0, 0x27,
+ 0xe2, 0xdd, 0x3b, 0x42, 0x33, 0xa3, 0x05, 0x28, 0xc4, 0xbb, 0x28, 0xcc,
+ 0x9a, 0xac, 0x2b, 0x23, 0x0d, 0x78, 0xc6, 0x7b, 0xe6, 0x5e, 0x71, 0xb7,
+ 0x4a, 0x3e, 0x08, 0xfb, 0x81, 0xb7, 0x16, 0x16, 0xa1, 0x9d, 0x23, 0x12,
+ 0x4d, 0xe5, 0xd7, 0x92, 0x08, 0xac, 0x75, 0xa4, 0x9c, 0xba, 0xcd, 0x17,
+ 0xb2, 0x1e, 0x44, 0x35, 0x65, 0x7f, 0x53, 0x25, 0x39, 0xd1, 0x1c, 0x0a,
+ 0x9a, 0x63, 0x1b, 0x19, 0x92, 0x74, 0x68, 0x0a, 0x37, 0xc2, 0xc2, 0x52,
+ 0x48, 0xcb, 0x39, 0x5a, 0xa2, 0xb6, 0xe1, 0x5d, 0xc1, 0xdd, 0xa0, 0x20,
+ 0xb8, 0x21, 0xa2, 0x93, 0x26, 0x6f, 0x14, 0x4a, 0x21, 0x41, 0xc7, 0xed,
+ 0x6d, 0x9b, 0xf2, 0x48, 0x2f, 0xf3, 0x03, 0xf5, 0xa2, 0x68, 0x92, 0x53,
+ 0x2f, 0x5e, 0xe3, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x49,
+ 0x30, 0x82, 0x01, 0x45, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
+ 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4b, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e,
+ 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67,
+ 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56,
+ 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67,
+ 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50,
+ 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x51, 0x68, 0xff, 0x90, 0xaf, 0x02, 0x07, 0x75, 0x3c, 0xcc, 0xd9, 0x65,
+ 0x64, 0x62, 0xa2, 0x12, 0xb8, 0x59, 0x72, 0x3b, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, 0xc3,
+ 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02,
+ 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x18, 0x8a, 0x95, 0x89, 0x03, 0xe6, 0x6d, 0xdf, 0x5c, 0xfc, 0x1d,
+ 0x68, 0xea, 0x4a, 0x8f, 0x83, 0xd6, 0x51, 0x2f, 0x8d, 0x6b, 0x44, 0x16,
+ 0x9e, 0xac, 0x63, 0xf5, 0xd2, 0x6e, 0x6c, 0x84, 0x99, 0x8b, 0xaa, 0x81,
+ 0x71, 0x84, 0x5b, 0xed, 0x34, 0x4e, 0xb0, 0xb7, 0x79, 0x92, 0x29, 0xcc,
+ 0x2d, 0x80, 0x6a, 0xf0, 0x8e, 0x20, 0xe1, 0x79, 0xa4, 0xfe, 0x03, 0x47,
+ 0x13, 0xea, 0xf5, 0x86, 0xca, 0x59, 0x71, 0x7d, 0xf4, 0x04, 0x96, 0x6b,
+ 0xd3, 0x59, 0x58, 0x3d, 0xfe, 0xd3, 0x31, 0x25, 0x5c, 0x18, 0x38, 0x84,
+ 0xa3, 0xe6, 0x9f, 0x82, 0xfd, 0x8c, 0x5b, 0x98, 0x31, 0x4e, 0xcd, 0x78,
+ 0x9e, 0x1a, 0xfd, 0x85, 0xcb, 0x49, 0xaa, 0xf2, 0x27, 0x8b, 0x99, 0x72,
+ 0xfc, 0x3e, 0xaa, 0xd5, 0x41, 0x0b, 0xda, 0xd5, 0x36, 0xa1, 0xbf, 0x1c,
+ 0x6e, 0x47, 0x49, 0x7f, 0x5e, 0xd9, 0x48, 0x7c, 0x03, 0xd9, 0xfd, 0x8b,
+ 0x49, 0xa0, 0x98, 0x26, 0x42, 0x40, 0xeb, 0xd6, 0x92, 0x11, 0xa4, 0x64,
+ 0x0a, 0x57, 0x54, 0xc4, 0xf5, 0x1d, 0xd6, 0x02, 0x5e, 0x6b, 0xac, 0xee,
+ 0xc4, 0x80, 0x9a, 0x12, 0x72, 0xfa, 0x56, 0x93, 0xd7, 0xff, 0xbf, 0x30,
+ 0x85, 0x06, 0x30, 0xbf, 0x0b, 0x7f, 0x4e, 0xff, 0x57, 0x05, 0x9d, 0x24,
+ 0xed, 0x85, 0xc3, 0x2b, 0xfb, 0xa6, 0x75, 0xa8, 0xac, 0x2d, 0x16, 0xef,
+ 0x7d, 0x79, 0x27, 0xb2, 0xeb, 0xc2, 0x9d, 0x0b, 0x07, 0xea, 0xaa, 0x85,
+ 0xd3, 0x01, 0xa3, 0x20, 0x28, 0x41, 0x59, 0x43, 0x28, 0xd2, 0x81, 0xe3,
+ 0xaa, 0xf6, 0xec, 0x7b, 0x3b, 0x77, 0xb6, 0x40, 0x62, 0x80, 0x05, 0x41,
+ 0x45, 0x01, 0xef, 0x17, 0x06, 0x3e, 0xde, 0xc0, 0x33, 0x9b, 0x67, 0xd3,
+ 0x61, 0x2e, 0x72, 0x87, 0xe4, 0x69, 0xfc, 0x12, 0x00, 0x57, 0x40, 0x1e,
+ 0x70, 0xf5, 0x1e, 0xc9, 0xb4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 16:87:d6:88:6d:e2:30:06:85:23:3d:bf:11:bf:65:97
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=thawte, Inc., CN=thawte SSL CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b2:fc:06:fb:04:93:d2:ea:59:20:3b:44:85:97:
+ 52:39:e7:10:f0:7a:e0:b0:94:40:da:46:f8:0c:28:
+ bb:b9:ce:60:38:3f:d2:d8:11:42:1b:91:ad:49:ee:
+ 8f:c7:de:6c:de:37:6f:fd:8b:20:3c:6d:e7:74:d3:
+ dc:d5:24:88:41:80:89:ee:36:be:c4:d5:be:8d:53:
+ 13:aa:e4:a5:b8:93:0a:be:ec:da:cd:3c:d4:32:56:
+ ef:d0:4e:a0:b8:97:bb:39:50:1e:6e:65:c3:fd:b2:
+ ce:e0:59:a9:48:09:c6:fe:be:ae:fc:3e:3b:81:20:
+ 97:8b:8f:46:df:60:64:07:75:bb:1b:86:38:9f:47:
+ 7b:34:ce:a1:d1:97:ad:76:d8:9f:b7:26:db:79:80:
+ 36:48:f2:c5:37:f8:d9:32:ae:7c:a4:53:81:c7:99:
+ a1:54:38:2f:4f:75:a0:bb:5a:a5:bb:cd:ac:02:5b:
+ 19:02:d5:13:18:a7:ce:ac:74:55:12:05:8b:9b:a2:
+ 95:46:64:72:38:cd:5a:1b:3a:16:a7:be:71:99:8c:
+ 54:03:b8:96:6c:01:d3:3e:06:98:3f:21:81:3b:02:
+ 7e:00:47:53:01:1e:0e:46:43:fb:4b:2d:dc:0b:1a:
+ e8:2f:98:f8:7e:d1:99:ab:13:6c:a4:17:de:6f:f6:
+ 15:f5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://t1.symcb.com/ThawtePCA.crl
+
+ Authority Information Access:
+ OCSP - URI:http://t2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: https://www.thawte.com/cps
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-537
+ X509v3 Subject Key Identifier:
+ C2:4F:48:57:FC:D1:4F:9A:C0:5D:38:7D:0E:05:DB:D9:2E:B5:52:60
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 8d:06:de:43:c9:76:02:ca:d9:23:97:5e:f3:63:d7:7d:44:c2:
+ 0f:6b:0a:f5:07:e5:8b:b8:fa:e0:a3:fa:6b:80:92:b5:03:2c:
+ c5:37:e0:c2:e5:95:b5:92:70:18:28:42:94:ee:4b:77:6a:01:
+ 0f:8b:23:ec:56:4d:f4:00:69:e5:84:c8:e2:ea:de:5b:3e:f6:
+ 3c:07:3a:94:ca:6c:27:b1:cc:83:1a:60:71:27:d2:bf:02:f5:
+ 1e:44:d3:48:d5:a6:d3:76:21:00:9c:fa:98:64:eb:17:36:3f:
+ eb:1b:3c:3e:a6:b1:d9:58:06:0e:72:d9:68:be:f1:a7:20:d7:
+ 52:e4:a4:77:1f:71:70:9d:55:35:85:37:e1:1d:4d:94:c2:70:
+ 7f:95:40:6e:4b:7d:b2:b4:29:2a:03:79:c8:b9:4c:67:61:04:
+ a0:8b:27:ff:59:00:eb:55:7f:c6:b7:33:35:2d:5e:4e:ac:b8:
+ ea:12:c5:e8:f7:b9:ab:be:74:92:2c:b7:d9:4d:ca:84:2f:1c:
+ c2:f0:72:7c:b2:31:6e:cf:80:e5:88:07:36:51:7b:ba:61:af:
+ 6d:8d:23:5b:34:a3:95:bc:a2:31:7f:f2:f5:e7:b7:e8:ef:c4:
+ b5:27:32:e9:f7:9e:69:c7:2b:e8:be:bb:0c:aa:e7:ea:60:12:
+ ea:26:8a:78
+-----BEGIN CERTIFICATE-----
+MIIEsjCCA5qgAwIBAgIQFofWiG3iMAaFIz2/Eb9llzANBgkqhkiG9w0BAQsFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTMxMDMxMDAwMDAwWhcNMjMx
+MDMwMjM1OTU5WjBBMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu
+MRswGQYDVQQDExJ0aGF3dGUgU1NMIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCy/Ab7BJPS6lkgO0SFl1I55xDweuCwlEDaRvgMKLu5zmA4
+P9LYEUIbka1J7o/H3mzeN2/9iyA8bed009zVJIhBgInuNr7E1b6NUxOq5KW4kwq+
+7NrNPNQyVu/QTqC4l7s5UB5uZcP9ss7gWalICcb+vq78PjuBIJeLj0bfYGQHdbsb
+hjifR3s0zqHRl6122J+3Jtt5gDZI8sU3+NkyrnykU4HHmaFUOC9PdaC7WqW7zawC
+WxkC1RMYp86sdFUSBYubopVGZHI4zVobOhanvnGZjFQDuJZsAdM+Bpg/IYE7An4A
+R1MBHg5GQ/tLLdwLGugvmPh+0ZmrE2ykF95v9hX1AgMBAAGjggE7MIIBNzASBgNV
+HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAyBgNVHR8EKzApMCegJaAj
+hiFodHRwOi8vdDEuc3ltY2IuY29tL1RoYXd0ZVBDQS5jcmwwLwYIKwYBBQUHAQEE
+IzAhMB8GCCsGAQUFBzABhhNodHRwOi8vdDIuc3ltY2IuY29tMEEGA1UdIAQ6MDgw
+NgYKYIZIAYb4RQEHNjAoMCYGCCsGAQUFBwIBFhpodHRwczovL3d3dy50aGF3dGUu
+Y29tL2NwczApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01
+MzcwHQYDVR0OBBYEFMJPSFf80U+awF04fQ4F29kutVJgMB8GA1UdIwQYMBaAFHtb
+Rc+vzst6/TGSGmq280brV0hQMA0GCSqGSIb3DQEBCwUAA4IBAQCNBt5DyXYCytkj
+l17zY9d9RMIPawr1B+WLuPrgo/prgJK1AyzFN+DC5ZW1knAYKEKU7kt3agEPiyPs
+Vk30AGnlhMji6t5bPvY8BzqUymwnscyDGmBxJ9K/AvUeRNNI1abTdiEAnPqYZOsX
+Nj/rGzw+prHZWAYOctlovvGnINdS5KR3H3FwnVU1hTfhHU2UwnB/lUBuS32ytCkq
+A3nIuUxnYQSgiyf/WQDrVX/GtzM1LV5OrLjqEsXo97mrvnSSLLfZTcqELxzC8HJ8
+sjFuz4DliAc2UXu6Ya9tjSNbNKOVvKIxf/L157fo78S1JzLp955pxyvovrsMqufq
+YBLqJop4
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert30[] = {
+ 0x30, 0x82, 0x04, 0xb2, 0x30, 0x82, 0x03, 0x9a, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x16, 0x87, 0xd6, 0x88, 0x6d, 0xe2, 0x30, 0x06, 0x85,
+ 0x23, 0x3d, 0xbf, 0x11, 0xbf, 0x65, 0x97, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31,
+ 0x30, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x41,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x74,
+ 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xb2, 0xfc, 0x06, 0xfb, 0x04, 0x93, 0xd2, 0xea, 0x59, 0x20,
+ 0x3b, 0x44, 0x85, 0x97, 0x52, 0x39, 0xe7, 0x10, 0xf0, 0x7a, 0xe0, 0xb0,
+ 0x94, 0x40, 0xda, 0x46, 0xf8, 0x0c, 0x28, 0xbb, 0xb9, 0xce, 0x60, 0x38,
+ 0x3f, 0xd2, 0xd8, 0x11, 0x42, 0x1b, 0x91, 0xad, 0x49, 0xee, 0x8f, 0xc7,
+ 0xde, 0x6c, 0xde, 0x37, 0x6f, 0xfd, 0x8b, 0x20, 0x3c, 0x6d, 0xe7, 0x74,
+ 0xd3, 0xdc, 0xd5, 0x24, 0x88, 0x41, 0x80, 0x89, 0xee, 0x36, 0xbe, 0xc4,
+ 0xd5, 0xbe, 0x8d, 0x53, 0x13, 0xaa, 0xe4, 0xa5, 0xb8, 0x93, 0x0a, 0xbe,
+ 0xec, 0xda, 0xcd, 0x3c, 0xd4, 0x32, 0x56, 0xef, 0xd0, 0x4e, 0xa0, 0xb8,
+ 0x97, 0xbb, 0x39, 0x50, 0x1e, 0x6e, 0x65, 0xc3, 0xfd, 0xb2, 0xce, 0xe0,
+ 0x59, 0xa9, 0x48, 0x09, 0xc6, 0xfe, 0xbe, 0xae, 0xfc, 0x3e, 0x3b, 0x81,
+ 0x20, 0x97, 0x8b, 0x8f, 0x46, 0xdf, 0x60, 0x64, 0x07, 0x75, 0xbb, 0x1b,
+ 0x86, 0x38, 0x9f, 0x47, 0x7b, 0x34, 0xce, 0xa1, 0xd1, 0x97, 0xad, 0x76,
+ 0xd8, 0x9f, 0xb7, 0x26, 0xdb, 0x79, 0x80, 0x36, 0x48, 0xf2, 0xc5, 0x37,
+ 0xf8, 0xd9, 0x32, 0xae, 0x7c, 0xa4, 0x53, 0x81, 0xc7, 0x99, 0xa1, 0x54,
+ 0x38, 0x2f, 0x4f, 0x75, 0xa0, 0xbb, 0x5a, 0xa5, 0xbb, 0xcd, 0xac, 0x02,
+ 0x5b, 0x19, 0x02, 0xd5, 0x13, 0x18, 0xa7, 0xce, 0xac, 0x74, 0x55, 0x12,
+ 0x05, 0x8b, 0x9b, 0xa2, 0x95, 0x46, 0x64, 0x72, 0x38, 0xcd, 0x5a, 0x1b,
+ 0x3a, 0x16, 0xa7, 0xbe, 0x71, 0x99, 0x8c, 0x54, 0x03, 0xb8, 0x96, 0x6c,
+ 0x01, 0xd3, 0x3e, 0x06, 0x98, 0x3f, 0x21, 0x81, 0x3b, 0x02, 0x7e, 0x00,
+ 0x47, 0x53, 0x01, 0x1e, 0x0e, 0x46, 0x43, 0xfb, 0x4b, 0x2d, 0xdc, 0x0b,
+ 0x1a, 0xe8, 0x2f, 0x98, 0xf8, 0x7e, 0xd1, 0x99, 0xab, 0x13, 0x6c, 0xa4,
+ 0x17, 0xde, 0x6f, 0xf6, 0x15, 0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x3b, 0x30, 0x82, 0x01, 0x37, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x32, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23,
+ 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, 0x31, 0x2e,
+ 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68,
+ 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x23, 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x74, 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, 0x38, 0x30,
+ 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07,
+ 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x29, 0x06, 0x03, 0x55,
+ 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a,
+ 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d,
+ 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x35,
+ 0x33, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0xc2, 0x4f, 0x48, 0x57, 0xfc, 0xd1, 0x4f, 0x9a, 0xc0, 0x5d, 0x38,
+ 0x7d, 0x0e, 0x05, 0xdb, 0xd9, 0x2e, 0xb5, 0x52, 0x60, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b,
+ 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6,
+ 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x8d, 0x06, 0xde, 0x43, 0xc9, 0x76, 0x02, 0xca, 0xd9, 0x23,
+ 0x97, 0x5e, 0xf3, 0x63, 0xd7, 0x7d, 0x44, 0xc2, 0x0f, 0x6b, 0x0a, 0xf5,
+ 0x07, 0xe5, 0x8b, 0xb8, 0xfa, 0xe0, 0xa3, 0xfa, 0x6b, 0x80, 0x92, 0xb5,
+ 0x03, 0x2c, 0xc5, 0x37, 0xe0, 0xc2, 0xe5, 0x95, 0xb5, 0x92, 0x70, 0x18,
+ 0x28, 0x42, 0x94, 0xee, 0x4b, 0x77, 0x6a, 0x01, 0x0f, 0x8b, 0x23, 0xec,
+ 0x56, 0x4d, 0xf4, 0x00, 0x69, 0xe5, 0x84, 0xc8, 0xe2, 0xea, 0xde, 0x5b,
+ 0x3e, 0xf6, 0x3c, 0x07, 0x3a, 0x94, 0xca, 0x6c, 0x27, 0xb1, 0xcc, 0x83,
+ 0x1a, 0x60, 0x71, 0x27, 0xd2, 0xbf, 0x02, 0xf5, 0x1e, 0x44, 0xd3, 0x48,
+ 0xd5, 0xa6, 0xd3, 0x76, 0x21, 0x00, 0x9c, 0xfa, 0x98, 0x64, 0xeb, 0x17,
+ 0x36, 0x3f, 0xeb, 0x1b, 0x3c, 0x3e, 0xa6, 0xb1, 0xd9, 0x58, 0x06, 0x0e,
+ 0x72, 0xd9, 0x68, 0xbe, 0xf1, 0xa7, 0x20, 0xd7, 0x52, 0xe4, 0xa4, 0x77,
+ 0x1f, 0x71, 0x70, 0x9d, 0x55, 0x35, 0x85, 0x37, 0xe1, 0x1d, 0x4d, 0x94,
+ 0xc2, 0x70, 0x7f, 0x95, 0x40, 0x6e, 0x4b, 0x7d, 0xb2, 0xb4, 0x29, 0x2a,
+ 0x03, 0x79, 0xc8, 0xb9, 0x4c, 0x67, 0x61, 0x04, 0xa0, 0x8b, 0x27, 0xff,
+ 0x59, 0x00, 0xeb, 0x55, 0x7f, 0xc6, 0xb7, 0x33, 0x35, 0x2d, 0x5e, 0x4e,
+ 0xac, 0xb8, 0xea, 0x12, 0xc5, 0xe8, 0xf7, 0xb9, 0xab, 0xbe, 0x74, 0x92,
+ 0x2c, 0xb7, 0xd9, 0x4d, 0xca, 0x84, 0x2f, 0x1c, 0xc2, 0xf0, 0x72, 0x7c,
+ 0xb2, 0x31, 0x6e, 0xcf, 0x80, 0xe5, 0x88, 0x07, 0x36, 0x51, 0x7b, 0xba,
+ 0x61, 0xaf, 0x6d, 0x8d, 0x23, 0x5b, 0x34, 0xa3, 0x95, 0xbc, 0xa2, 0x31,
+ 0x7f, 0xf2, 0xf5, 0xe7, 0xb7, 0xe8, 0xef, 0xc4, 0xb5, 0x27, 0x32, 0xe9,
+ 0xf7, 0x9e, 0x69, 0xc7, 0x2b, 0xe8, 0xbe, 0xbb, 0x0c, 0xaa, 0xe7, 0xea,
+ 0x60, 0x12, 0xea, 0x26, 0x8a, 0x78,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 93:92:85:40:01:65:71:5f:94:7f:28:8f:ef:c9:9b:28
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=PL, O=Unizeto Sp. z o.o., CN=Certum CA
+ Validity
+ Not Before: Oct 22 12:07:37 2008 GMT
+ Not After : Jun 10 10:46:39 2027 GMT
+ Subject: C=PL, O=Unizeto Technologies S.A., OU=Certum Certification Authority, CN=Certum Trusted Network CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e3:fb:7d:a3:72:ba:c2:f0:c9:14:87:f5:6b:01:
+ 4e:e1:6e:40:07:ba:6d:27:5d:7f:f7:5b:2d:b3:5a:
+ c7:51:5f:ab:a4:32:a6:61:87:b6:6e:0f:86:d2:30:
+ 02:97:f8:d7:69:57:a1:18:39:5d:6a:64:79:c6:01:
+ 59:ac:3c:31:4a:38:7c:d2:04:d2:4b:28:e8:20:5f:
+ 3b:07:a2:cc:4d:73:db:f3:ae:4f:c7:56:d5:5a:a7:
+ 96:89:fa:f3:ab:68:d4:23:86:59:27:cf:09:27:bc:
+ ac:6e:72:83:1c:30:72:df:e0:a2:e9:d2:e1:74:75:
+ 19:bd:2a:9e:7b:15:54:04:1b:d7:43:39:ad:55:28:
+ c5:e2:1a:bb:f4:c0:e4:ae:38:49:33:cc:76:85:9f:
+ 39:45:d2:a4:9e:f2:12:8c:51:f8:7c:e4:2d:7f:f5:
+ ac:5f:eb:16:9f:b1:2d:d1:ba:cc:91:42:77:4c:25:
+ c9:90:38:6f:db:f0:cc:fb:8e:1e:97:59:3e:d5:60:
+ 4e:e6:05:28:ed:49:79:13:4b:ba:48:db:2f:f9:72:
+ d3:39:ca:fe:1f:d8:34:72:f5:b4:40:cf:31:01:c3:
+ ec:de:11:2d:17:5d:1f:b8:50:d1:5e:19:a7:69:de:
+ 07:33:28:ca:50:95:f9:a7:54:cb:54:86:50:45:a9:
+ f9:49
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ 08:76:CD:CB:07:FF:24:F6:C5:CD:ED:BB:90:BC:E2:84:37:46:75:F7
+ X509v3 Authority Key Identifier:
+ DirName:/C=PL/O=Unizeto Sp. z o.o./CN=Certum CA
+ serial:01:00:20
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.certum.pl/ca.crl
+
+ Authority Information Access:
+ OCSP - URI:http://subca.ocsp-certum.com
+ CA Issuers - URI:http://repository.certum.pl/ca.cer
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.certum.pl/CPS
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 8d:e6:fd:40:66:a3:4c:9c:a7:ab:a1:da:84:dd:1c:30:07:e6:
+ db:c7:2d:ec:83:a1:56:e4:1d:3c:26:a1:a5:09:2b:e8:7d:62:
+ be:b2:75:94:dd:08:f2:7f:28:41:e4:80:67:02:4e:8a:8f:c3:
+ 35:d0:d5:a9:27:28:ea:d2:f4:ab:06:86:43:ae:8c:e3:f9:88:
+ 7d:e0:db:bd:42:81:80:02:12:75:b2:e8:17:71:ab:21:95:31:
+ 46:42:0d:88:10:39:d3:6f:ec:2f:42:ea:40:53:62:bf:eb:ca:
+ 78:9e:ab:a2:d5:2e:05:ea:33:ab:e9:d6:97:94:42:5e:04:ed:
+ 2c:ed:6a:9c:7a:95:7d:05:2a:05:7f:08:5d:66:ad:61:d4:76:
+ ac:75:96:97:73:63:bd:1a:41:59:29:a5:5e:22:83:c3:8b:59:
+ fa:9a:a2:f6:bd:30:bf:72:1d:1c:99:86:9c:f2:85:3c:1d:f7:
+ 26:96:2f:2e:f9:02:b1:b5:a9:50:e8:38:fa:9b:0a:5e:b4:04:
+ c0:ce:4e:39:2c:ca:0b:5b:62:f0:4d:58:50:34:99:e6:9a:2c:
+ d2:90:d7:09:81:d6:c0:aa:5e:ce:fe:d2:f7:a1:ba:4b:d9:d6:
+ 86:8e:19:1f:a6:06:47:42:72:e0:56:0a:00:1c:78:b9:8d:cc:
+ 99:04:37:49
+-----BEGIN CERTIFICATE-----
+MIIEtDCCA5ygAwIBAgIRAJOShUABZXFflH8oj+/JmygwDQYJKoZIhvcNAQELBQAw
+PjELMAkGA1UEBhMCUEwxGzAZBgNVBAoTElVuaXpldG8gU3AuIHogby5vLjESMBAG
+A1UEAxMJQ2VydHVtIENBMB4XDTA4MTAyMjEyMDczN1oXDTI3MDYxMDEwNDYzOVow
+fjELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
+QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEiMCAG
+A1UEAxMZQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAOP7faNyusLwyRSH9WsBTuFuQAe6bSddf/dbLbNax1Ff
+q6QypmGHtm4PhtIwApf412lXoRg5XWpkecYBWaw8MUo4fNIE0kso6CBfOweizE1z
+2/OuT8dW1Vqnlon686to1COGWSfPCSe8rG5ygxwwct/gounS4XR1Gb0qnnsVVAQb
+10M5rVUoxeIau/TA5K44STPMdoWfOUXSpJ7yEoxR+HzkLX/1rF/rFp+xLdG6zJFC
+d0wlyZA4b9vwzPuOHpdZPtVgTuYFKO1JeRNLukjbL/ly0znK/h/YNHL1tEDPMQHD
+7N4RLRddH7hQ0V4Zp2neBzMoylCV+adUy1SGUEWp+UkCAwEAAaOCAWswggFnMA8G
+A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAh2zcsH/yT2xc3tu5C84oQ3RnX3MFIG
+A1UdIwRLMEmhQqRAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNw
+LiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQYIDAQAgMA4GA1UdDwEB/wQEAwIB
+BjAsBgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vY3JsLmNlcnR1bS5wbC9jYS5jcmww
+aAYIKwYBBQUHAQEEXDBaMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1j
+ZXJ0dW0uY29tMC4GCCsGAQUFBzAChiJodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0u
+cGwvY2EuY2VyMDkGA1UdIAQyMDAwLgYEVR0gADAmMCQGCCsGAQUFBwIBFhhodHRw
+Oi8vd3d3LmNlcnR1bS5wbC9DUFMwDQYJKoZIhvcNAQELBQADggEBAI3m/UBmo0yc
+p6uh2oTdHDAH5tvHLeyDoVbkHTwmoaUJK+h9Yr6ydZTdCPJ/KEHkgGcCToqPwzXQ
+1aknKOrS9KsGhkOujOP5iH3g271CgYACEnWy6BdxqyGVMUZCDYgQOdNv7C9C6kBT
+Yr/rynieq6LVLgXqM6vp1peUQl4E7Sztapx6lX0FKgV/CF1mrWHUdqx1lpdzY70a
+QVkppV4ig8OLWfqaova9ML9yHRyZhpzyhTwd9yaWLy75ArG1qVDoOPqbCl60BMDO
+TjksygtbYvBNWFA0meaaLNKQ1wmB1sCqXs7+0vehukvZ1oaOGR+mBkdCcuBWCgAc
+eLmNzJkEN0k=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert31[] = {
+ 0x30, 0x82, 0x04, 0xb4, 0x30, 0x82, 0x03, 0x9c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x11, 0x00, 0x93, 0x92, 0x85, 0x40, 0x01, 0x65, 0x71, 0x5f,
+ 0x94, 0x7f, 0x28, 0x8f, 0xef, 0xc9, 0x9b, 0x28, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
+ 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x50, 0x4c, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x12, 0x55, 0x6e, 0x69, 0x7a, 0x65, 0x74, 0x6f, 0x20, 0x53, 0x70, 0x2e,
+ 0x20, 0x7a, 0x20, 0x6f, 0x2e, 0x6f, 0x2e, 0x31, 0x12, 0x30, 0x10, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d,
+ 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x31, 0x30, 0x32,
+ 0x32, 0x31, 0x32, 0x30, 0x37, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x37,
+ 0x30, 0x36, 0x31, 0x30, 0x31, 0x30, 0x34, 0x36, 0x33, 0x39, 0x5a, 0x30,
+ 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x50, 0x4c, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x19, 0x55, 0x6e, 0x69, 0x7a, 0x65, 0x74, 0x6f, 0x20, 0x54, 0x65, 0x63,
+ 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x20, 0x53, 0x2e,
+ 0x41, 0x2e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1e, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x22, 0x30, 0x20, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x43, 0x65, 0x72, 0x74, 0x75, 0x6d,
+ 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x4e, 0x65, 0x74,
+ 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xe3, 0xfb, 0x7d, 0xa3, 0x72, 0xba, 0xc2, 0xf0,
+ 0xc9, 0x14, 0x87, 0xf5, 0x6b, 0x01, 0x4e, 0xe1, 0x6e, 0x40, 0x07, 0xba,
+ 0x6d, 0x27, 0x5d, 0x7f, 0xf7, 0x5b, 0x2d, 0xb3, 0x5a, 0xc7, 0x51, 0x5f,
+ 0xab, 0xa4, 0x32, 0xa6, 0x61, 0x87, 0xb6, 0x6e, 0x0f, 0x86, 0xd2, 0x30,
+ 0x02, 0x97, 0xf8, 0xd7, 0x69, 0x57, 0xa1, 0x18, 0x39, 0x5d, 0x6a, 0x64,
+ 0x79, 0xc6, 0x01, 0x59, 0xac, 0x3c, 0x31, 0x4a, 0x38, 0x7c, 0xd2, 0x04,
+ 0xd2, 0x4b, 0x28, 0xe8, 0x20, 0x5f, 0x3b, 0x07, 0xa2, 0xcc, 0x4d, 0x73,
+ 0xdb, 0xf3, 0xae, 0x4f, 0xc7, 0x56, 0xd5, 0x5a, 0xa7, 0x96, 0x89, 0xfa,
+ 0xf3, 0xab, 0x68, 0xd4, 0x23, 0x86, 0x59, 0x27, 0xcf, 0x09, 0x27, 0xbc,
+ 0xac, 0x6e, 0x72, 0x83, 0x1c, 0x30, 0x72, 0xdf, 0xe0, 0xa2, 0xe9, 0xd2,
+ 0xe1, 0x74, 0x75, 0x19, 0xbd, 0x2a, 0x9e, 0x7b, 0x15, 0x54, 0x04, 0x1b,
+ 0xd7, 0x43, 0x39, 0xad, 0x55, 0x28, 0xc5, 0xe2, 0x1a, 0xbb, 0xf4, 0xc0,
+ 0xe4, 0xae, 0x38, 0x49, 0x33, 0xcc, 0x76, 0x85, 0x9f, 0x39, 0x45, 0xd2,
+ 0xa4, 0x9e, 0xf2, 0x12, 0x8c, 0x51, 0xf8, 0x7c, 0xe4, 0x2d, 0x7f, 0xf5,
+ 0xac, 0x5f, 0xeb, 0x16, 0x9f, 0xb1, 0x2d, 0xd1, 0xba, 0xcc, 0x91, 0x42,
+ 0x77, 0x4c, 0x25, 0xc9, 0x90, 0x38, 0x6f, 0xdb, 0xf0, 0xcc, 0xfb, 0x8e,
+ 0x1e, 0x97, 0x59, 0x3e, 0xd5, 0x60, 0x4e, 0xe6, 0x05, 0x28, 0xed, 0x49,
+ 0x79, 0x13, 0x4b, 0xba, 0x48, 0xdb, 0x2f, 0xf9, 0x72, 0xd3, 0x39, 0xca,
+ 0xfe, 0x1f, 0xd8, 0x34, 0x72, 0xf5, 0xb4, 0x40, 0xcf, 0x31, 0x01, 0xc3,
+ 0xec, 0xde, 0x11, 0x2d, 0x17, 0x5d, 0x1f, 0xb8, 0x50, 0xd1, 0x5e, 0x19,
+ 0xa7, 0x69, 0xde, 0x07, 0x33, 0x28, 0xca, 0x50, 0x95, 0xf9, 0xa7, 0x54,
+ 0xcb, 0x54, 0x86, 0x50, 0x45, 0xa9, 0xf9, 0x49, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x6b, 0x30, 0x82, 0x01, 0x67, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01,
+ 0x01, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x08, 0x76, 0xcd, 0xcb, 0x07, 0xff, 0x24, 0xf6, 0xc5, 0xcd, 0xed,
+ 0xbb, 0x90, 0xbc, 0xe2, 0x84, 0x37, 0x46, 0x75, 0xf7, 0x30, 0x52, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x4b, 0x30, 0x49, 0xa1, 0x42, 0xa4, 0x40,
+ 0x30, 0x3e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x50, 0x4c, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x12, 0x55, 0x6e, 0x69, 0x7a, 0x65, 0x74, 0x6f, 0x20, 0x53, 0x70,
+ 0x2e, 0x20, 0x7a, 0x20, 0x6f, 0x2e, 0x6f, 0x2e, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x43, 0x65, 0x72, 0x74, 0x75,
+ 0x6d, 0x20, 0x43, 0x41, 0x82, 0x03, 0x01, 0x00, 0x20, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x25, 0x30, 0x23,
+ 0x30, 0x21, 0xa0, 0x1f, 0xa0, 0x1d, 0x86, 0x1b, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75,
+ 0x6d, 0x2e, 0x70, 0x6c, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x68, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x5c, 0x30, 0x5a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x73, 0x75, 0x62, 0x63, 0x61, 0x2e, 0x6f, 0x63, 0x73, 0x70, 0x2d, 0x63,
+ 0x65, 0x72, 0x74, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x2e, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x22, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69,
+ 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75, 0x6d, 0x2e,
+ 0x70, 0x6c, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x65, 0x72, 0x30, 0x39, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x32, 0x30, 0x30, 0x30, 0x2e, 0x06, 0x04,
+ 0x55, 0x1d, 0x20, 0x00, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x18, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x75,
+ 0x6d, 0x2e, 0x70, 0x6c, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x8d, 0xe6, 0xfd, 0x40, 0x66, 0xa3, 0x4c, 0x9c,
+ 0xa7, 0xab, 0xa1, 0xda, 0x84, 0xdd, 0x1c, 0x30, 0x07, 0xe6, 0xdb, 0xc7,
+ 0x2d, 0xec, 0x83, 0xa1, 0x56, 0xe4, 0x1d, 0x3c, 0x26, 0xa1, 0xa5, 0x09,
+ 0x2b, 0xe8, 0x7d, 0x62, 0xbe, 0xb2, 0x75, 0x94, 0xdd, 0x08, 0xf2, 0x7f,
+ 0x28, 0x41, 0xe4, 0x80, 0x67, 0x02, 0x4e, 0x8a, 0x8f, 0xc3, 0x35, 0xd0,
+ 0xd5, 0xa9, 0x27, 0x28, 0xea, 0xd2, 0xf4, 0xab, 0x06, 0x86, 0x43, 0xae,
+ 0x8c, 0xe3, 0xf9, 0x88, 0x7d, 0xe0, 0xdb, 0xbd, 0x42, 0x81, 0x80, 0x02,
+ 0x12, 0x75, 0xb2, 0xe8, 0x17, 0x71, 0xab, 0x21, 0x95, 0x31, 0x46, 0x42,
+ 0x0d, 0x88, 0x10, 0x39, 0xd3, 0x6f, 0xec, 0x2f, 0x42, 0xea, 0x40, 0x53,
+ 0x62, 0xbf, 0xeb, 0xca, 0x78, 0x9e, 0xab, 0xa2, 0xd5, 0x2e, 0x05, 0xea,
+ 0x33, 0xab, 0xe9, 0xd6, 0x97, 0x94, 0x42, 0x5e, 0x04, 0xed, 0x2c, 0xed,
+ 0x6a, 0x9c, 0x7a, 0x95, 0x7d, 0x05, 0x2a, 0x05, 0x7f, 0x08, 0x5d, 0x66,
+ 0xad, 0x61, 0xd4, 0x76, 0xac, 0x75, 0x96, 0x97, 0x73, 0x63, 0xbd, 0x1a,
+ 0x41, 0x59, 0x29, 0xa5, 0x5e, 0x22, 0x83, 0xc3, 0x8b, 0x59, 0xfa, 0x9a,
+ 0xa2, 0xf6, 0xbd, 0x30, 0xbf, 0x72, 0x1d, 0x1c, 0x99, 0x86, 0x9c, 0xf2,
+ 0x85, 0x3c, 0x1d, 0xf7, 0x26, 0x96, 0x2f, 0x2e, 0xf9, 0x02, 0xb1, 0xb5,
+ 0xa9, 0x50, 0xe8, 0x38, 0xfa, 0x9b, 0x0a, 0x5e, 0xb4, 0x04, 0xc0, 0xce,
+ 0x4e, 0x39, 0x2c, 0xca, 0x0b, 0x5b, 0x62, 0xf0, 0x4d, 0x58, 0x50, 0x34,
+ 0x99, 0xe6, 0x9a, 0x2c, 0xd2, 0x90, 0xd7, 0x09, 0x81, 0xd6, 0xc0, 0xaa,
+ 0x5e, 0xce, 0xfe, 0xd2, 0xf7, 0xa1, 0xba, 0x4b, 0xd9, 0xd6, 0x86, 0x8e,
+ 0x19, 0x1f, 0xa6, 0x06, 0x47, 0x42, 0x72, 0xe0, 0x56, 0x0a, 0x00, 0x1c,
+ 0x78, 0xb9, 0x8d, 0xcc, 0x99, 0x04, 0x37, 0x49,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 48:e9:94:40:d4:36:49:1c:b8:b8:82:3d:09:43:94:c7
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., OU=(c) 2008 GeoTrust Inc. - For authorized use only, CN=GeoTrust Primary Certification Authority - G3
+ Validity
+ Not Before: Jun 10 00:00:00 2014 GMT
+ Not After : Jun 9 23:59:59 2024 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c4:95:63:28:d0:4e:30:45:af:8b:97:34:14:45:
+ f8:5c:58:4a:fa:33:8e:6e:9c:60:ab:f3:86:ff:34:
+ 74:b2:2b:be:a1:8c:d5:a2:a3:60:7a:40:b9:e1:fc:
+ 22:ca:67:ba:60:aa:c7:9a:f9:06:7f:ee:f7:ba:85:
+ 05:b0:03:ff:72:ae:15:41:4a:98:64:d7:17:4b:54:
+ ef:05:c6:98:07:93:27:3e:4f:dc:0f:c6:7b:8b:e7:
+ f3:06:5e:8d:e8:b4:ae:29:b4:1e:1e:2d:16:90:d3:
+ ea:aa:e7:8c:3b:6d:af:36:59:ff:c5:0a:fa:c7:4c:
+ bd:36:8b:64:c4:4a:f5:ce:33:f9:07:be:7f:45:90:
+ a8:08:14:b0:d0:a5:4f:df:82:80:da:1b:ee:c3:13:
+ b0:98:f5:0f:f9:7e:76:b5:e6:b9:5d:68:b9:5c:50:
+ 90:89:a4:36:b1:70:16:ea:b1:10:b5:6a:76:df:e1:
+ bb:fc:78:f2:72:99:cf:c9:a2:d4:73:54:77:bf:c0:
+ 39:77:e5:ae:12:c5:78:5a:19:45:d4:41:19:d3:7c:
+ f5:6f:99:6b:d7:8b:bc:2d:09:9d:4b:10:61:c0:da:
+ 52:c3:af:22:43:c6:eb:37:7e:63:74:30:0d:6a:71:
+ 8e:de:5d:5b:8a:c8:c5:d7:9b:29:e8:ae:b6:25:61:
+ 81:eb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://g.symcd.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://g.symcb.com/GeoTrustPCA-G3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-697
+ X509v3 Subject Key Identifier:
+ 4C:F4:BF:E8:3B:BE:C2:24:F3:1B:47:3B:B5:6E:48:8E:16:AB:AF:12
+ X509v3 Authority Key Identifier:
+ keyid:C4:79:CA:8E:A1:4E:03:1D:1C:DC:6B:DB:31:5B:94:3E:3F:30:7F:2D
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 7a:53:b5:de:b6:ef:52:a3:5f:8a:f5:89:f1:42:cc:5e:46:88:
+ ae:a5:08:87:51:de:0f:0f:02:eb:0c:82:78:e3:73:7d:71:bd:
+ 43:e9:ca:8a:3f:e0:25:92:9b:33:33:74:49:5e:00:d9:73:14:
+ 1c:0b:46:76:1c:8a:0d:4d:8c:6c:7e:4b:f7:60:d8:81:78:a0:
+ 78:d0:25:62:ab:10:ca:22:e8:1c:19:dd:52:83:64:05:e5:87:
+ 66:ae:e7:7a:a4:3b:3e:d8:70:7a:76:a2:67:39:d4:c9:fa:e5:
+ b7:1e:41:e2:09:39:88:1c:18:55:0a:c4:41:af:b2:f3:f3:0f:
+ 42:14:61:74:81:e3:da:87:5a:9a:4d:8b:d3:c9:8f:89:66:13:
+ 29:11:e4:ff:e2:df:8e:96:0c:5a:a1:aa:6b:9b:fd:fc:03:3b:
+ 55:0d:a6:a2:25:48:17:1f:42:a8:da:6c:7e:69:6e:a0:df:67:
+ d2:6d:f4:0e:6a:12:79:f5:7c:c8:a5:32:1c:c4:31:b2:e6:bb:
+ a8:6b:6a:a2:8a:60:69:c0:57:7d:b2:f2:31:0c:98:65:32:ec:
+ 08:5a:ce:c6:98:e9:21:97:3f:2c:79:29:03:f5:f6:94:2b:53:
+ 31:f3:93:68:57:e1:d7:4f:3a:d1:61:a1:60:ce:b9:ab:98:ae:
+ 35:54:63:8b
+-----BEGIN CERTIFICATE-----
+MIIEtTCCA52gAwIBAgIQSOmUQNQ2SRy4uII9CUOUxzANBgkqhkiG9w0BAQsFADCB
+mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
+MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEczMB4XDTE0MDYxMDAwMDAwMFoXDTI0MDYwOTIzNTk1OVowRzELMAkG
+A1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xIDAeBgNVBAMTF1JhcGlk
+U1NMIFNIQTI1NiBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAxJVjKNBOMEWvi5c0FEX4XFhK+jOObpxgq/OG/zR0siu+oYzVoqNgekC54fwi
+yme6YKrHmvkGf+73uoUFsAP/cq4VQUqYZNcXS1TvBcaYB5MnPk/cD8Z7i+fzBl6N
+6LSuKbQeHi0WkNPqqueMO22vNln/xQr6x0y9NotkxEr1zjP5B75/RZCoCBSw0KVP
+34KA2hvuwxOwmPUP+X52tea5XWi5XFCQiaQ2sXAW6rEQtWp23+G7/HjycpnPyaLU
+c1R3v8A5d+WuEsV4WhlF1EEZ03z1b5lr14u8LQmdSxBhwNpSw68iQ8brN35jdDAN
+anGO3l1bisjF15sp6K62JWGB6wIDAQABo4IBSTCCAUUwLgYIKwYBBQUHAQEEIjAg
+MB4GCCsGAQUFBzABhhJodHRwOi8vZy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB
+/wIBADBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYwMzAxBggrBgEFBQcCARYlaHR0
+cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczA2BgNVHR8ELzAtMCug
+KaAnhiVodHRwOi8vZy5zeW1jYi5jb20vR2VvVHJ1c3RQQ0EtRzMuY3JsMA4GA1Ud
+DwEB/wQEAwIBBjApBgNVHREEIjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0kt
+MS02OTcwHQYDVR0OBBYEFEz0v+g7vsIk8xtHO7VuSI4Wq68SMB8GA1UdIwQYMBaA
+FMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQB6U7Xetu9S
+o1+K9YnxQsxeRoiupQiHUd4PDwLrDIJ443N9cb1D6cqKP+AlkpszM3RJXgDZcxQc
+C0Z2HIoNTYxsfkv3YNiBeKB40CViqxDKIugcGd1Sg2QF5Ydmrud6pDs+2HB6dqJn
+OdTJ+uW3HkHiCTmIHBhVCsRBr7Lz8w9CFGF0gePah1qaTYvTyY+JZhMpEeT/4t+O
+lgxaoaprm/38AztVDaaiJUgXH0Ko2mx+aW6g32fSbfQOahJ59XzIpTIcxDGy5ruo
+a2qiimBpwFd9svIxDJhlMuwIWs7GmOkhlz8seSkD9faUK1Mx85NoV+HXTzrRYaFg
+zrmrmK41VGOL
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert32[] = {
+ 0x30, 0x82, 0x04, 0xb5, 0x30, 0x82, 0x03, 0x9d, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x48, 0xe9, 0x94, 0x40, 0xd4, 0x36, 0x49, 0x1c, 0xb8,
+ 0xb8, 0x82, 0x3d, 0x09, 0x43, 0x94, 0xc7, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x47, 0x65,
+ 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20,
+ 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c,
+ 0x79, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69,
+ 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x34, 0x30, 0x36, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x36, 0x30, 0x39, 0x32, 0x33,
+ 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x20, 0x30, 0x1e,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x17, 0x52, 0x61, 0x70, 0x69, 0x64,
+ 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x43,
+ 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xc4, 0x95, 0x63, 0x28, 0xd0, 0x4e, 0x30, 0x45, 0xaf,
+ 0x8b, 0x97, 0x34, 0x14, 0x45, 0xf8, 0x5c, 0x58, 0x4a, 0xfa, 0x33, 0x8e,
+ 0x6e, 0x9c, 0x60, 0xab, 0xf3, 0x86, 0xff, 0x34, 0x74, 0xb2, 0x2b, 0xbe,
+ 0xa1, 0x8c, 0xd5, 0xa2, 0xa3, 0x60, 0x7a, 0x40, 0xb9, 0xe1, 0xfc, 0x22,
+ 0xca, 0x67, 0xba, 0x60, 0xaa, 0xc7, 0x9a, 0xf9, 0x06, 0x7f, 0xee, 0xf7,
+ 0xba, 0x85, 0x05, 0xb0, 0x03, 0xff, 0x72, 0xae, 0x15, 0x41, 0x4a, 0x98,
+ 0x64, 0xd7, 0x17, 0x4b, 0x54, 0xef, 0x05, 0xc6, 0x98, 0x07, 0x93, 0x27,
+ 0x3e, 0x4f, 0xdc, 0x0f, 0xc6, 0x7b, 0x8b, 0xe7, 0xf3, 0x06, 0x5e, 0x8d,
+ 0xe8, 0xb4, 0xae, 0x29, 0xb4, 0x1e, 0x1e, 0x2d, 0x16, 0x90, 0xd3, 0xea,
+ 0xaa, 0xe7, 0x8c, 0x3b, 0x6d, 0xaf, 0x36, 0x59, 0xff, 0xc5, 0x0a, 0xfa,
+ 0xc7, 0x4c, 0xbd, 0x36, 0x8b, 0x64, 0xc4, 0x4a, 0xf5, 0xce, 0x33, 0xf9,
+ 0x07, 0xbe, 0x7f, 0x45, 0x90, 0xa8, 0x08, 0x14, 0xb0, 0xd0, 0xa5, 0x4f,
+ 0xdf, 0x82, 0x80, 0xda, 0x1b, 0xee, 0xc3, 0x13, 0xb0, 0x98, 0xf5, 0x0f,
+ 0xf9, 0x7e, 0x76, 0xb5, 0xe6, 0xb9, 0x5d, 0x68, 0xb9, 0x5c, 0x50, 0x90,
+ 0x89, 0xa4, 0x36, 0xb1, 0x70, 0x16, 0xea, 0xb1, 0x10, 0xb5, 0x6a, 0x76,
+ 0xdf, 0xe1, 0xbb, 0xfc, 0x78, 0xf2, 0x72, 0x99, 0xcf, 0xc9, 0xa2, 0xd4,
+ 0x73, 0x54, 0x77, 0xbf, 0xc0, 0x39, 0x77, 0xe5, 0xae, 0x12, 0xc5, 0x78,
+ 0x5a, 0x19, 0x45, 0xd4, 0x41, 0x19, 0xd3, 0x7c, 0xf5, 0x6f, 0x99, 0x6b,
+ 0xd7, 0x8b, 0xbc, 0x2d, 0x09, 0x9d, 0x4b, 0x10, 0x61, 0xc0, 0xda, 0x52,
+ 0xc3, 0xaf, 0x22, 0x43, 0xc6, 0xeb, 0x37, 0x7e, 0x63, 0x74, 0x30, 0x0d,
+ 0x6a, 0x71, 0x8e, 0xde, 0x5d, 0x5b, 0x8a, 0xc8, 0xc5, 0xd7, 0x9b, 0x29,
+ 0xe8, 0xae, 0xb6, 0x25, 0x61, 0x81, 0xeb, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x49, 0x30, 0x82, 0x01, 0x45, 0x30, 0x2e, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x22, 0x30, 0x20,
+ 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e, 0x73,
+ 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x36,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0,
+ 0x29, 0xa0, 0x27, 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x50, 0x43, 0x41, 0x2d,
+ 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x29,
+ 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30,
+ 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11,
+ 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d,
+ 0x31, 0x2d, 0x36, 0x39, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x4c, 0xf4, 0xbf, 0xe8, 0x3b, 0xbe, 0xc2, 0x24,
+ 0xf3, 0x1b, 0x47, 0x3b, 0xb5, 0x6e, 0x48, 0x8e, 0x16, 0xab, 0xaf, 0x12,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0xc4, 0x79, 0xca, 0x8e, 0xa1, 0x4e, 0x03, 0x1d, 0x1c, 0xdc, 0x6b,
+ 0xdb, 0x31, 0x5b, 0x94, 0x3e, 0x3f, 0x30, 0x7f, 0x2d, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x7a, 0x53, 0xb5, 0xde, 0xb6, 0xef, 0x52,
+ 0xa3, 0x5f, 0x8a, 0xf5, 0x89, 0xf1, 0x42, 0xcc, 0x5e, 0x46, 0x88, 0xae,
+ 0xa5, 0x08, 0x87, 0x51, 0xde, 0x0f, 0x0f, 0x02, 0xeb, 0x0c, 0x82, 0x78,
+ 0xe3, 0x73, 0x7d, 0x71, 0xbd, 0x43, 0xe9, 0xca, 0x8a, 0x3f, 0xe0, 0x25,
+ 0x92, 0x9b, 0x33, 0x33, 0x74, 0x49, 0x5e, 0x00, 0xd9, 0x73, 0x14, 0x1c,
+ 0x0b, 0x46, 0x76, 0x1c, 0x8a, 0x0d, 0x4d, 0x8c, 0x6c, 0x7e, 0x4b, 0xf7,
+ 0x60, 0xd8, 0x81, 0x78, 0xa0, 0x78, 0xd0, 0x25, 0x62, 0xab, 0x10, 0xca,
+ 0x22, 0xe8, 0x1c, 0x19, 0xdd, 0x52, 0x83, 0x64, 0x05, 0xe5, 0x87, 0x66,
+ 0xae, 0xe7, 0x7a, 0xa4, 0x3b, 0x3e, 0xd8, 0x70, 0x7a, 0x76, 0xa2, 0x67,
+ 0x39, 0xd4, 0xc9, 0xfa, 0xe5, 0xb7, 0x1e, 0x41, 0xe2, 0x09, 0x39, 0x88,
+ 0x1c, 0x18, 0x55, 0x0a, 0xc4, 0x41, 0xaf, 0xb2, 0xf3, 0xf3, 0x0f, 0x42,
+ 0x14, 0x61, 0x74, 0x81, 0xe3, 0xda, 0x87, 0x5a, 0x9a, 0x4d, 0x8b, 0xd3,
+ 0xc9, 0x8f, 0x89, 0x66, 0x13, 0x29, 0x11, 0xe4, 0xff, 0xe2, 0xdf, 0x8e,
+ 0x96, 0x0c, 0x5a, 0xa1, 0xaa, 0x6b, 0x9b, 0xfd, 0xfc, 0x03, 0x3b, 0x55,
+ 0x0d, 0xa6, 0xa2, 0x25, 0x48, 0x17, 0x1f, 0x42, 0xa8, 0xda, 0x6c, 0x7e,
+ 0x69, 0x6e, 0xa0, 0xdf, 0x67, 0xd2, 0x6d, 0xf4, 0x0e, 0x6a, 0x12, 0x79,
+ 0xf5, 0x7c, 0xc8, 0xa5, 0x32, 0x1c, 0xc4, 0x31, 0xb2, 0xe6, 0xbb, 0xa8,
+ 0x6b, 0x6a, 0xa2, 0x8a, 0x60, 0x69, 0xc0, 0x57, 0x7d, 0xb2, 0xf2, 0x31,
+ 0x0c, 0x98, 0x65, 0x32, 0xec, 0x08, 0x5a, 0xce, 0xc6, 0x98, 0xe9, 0x21,
+ 0x97, 0x3f, 0x2c, 0x79, 0x29, 0x03, 0xf5, 0xf6, 0x94, 0x2b, 0x53, 0x31,
+ 0xf3, 0x93, 0x68, 0x57, 0xe1, 0xd7, 0x4f, 0x3a, 0xd1, 0x61, 0xa1, 0x60,
+ 0xce, 0xb9, 0xab, 0x98, 0xae, 0x35, 0x54, 0x63, 0x8b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0c:79:a9:44:b0:8c:11:95:20:92:61:5f:e2:6b:1d:83
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Oct 22 12:00:00 2013 GMT
+ Not After : Oct 22 12:00:00 2028 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 Extended Validation Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d7:53:a4:04:51:f8:99:a6:16:48:4b:67:27:aa:
+ 93:49:d0:39:ed:0c:b0:b0:00:87:f1:67:28:86:85:
+ 8c:8e:63:da:bc:b1:40:38:e2:d3:f5:ec:a5:05:18:
+ b8:3d:3e:c5:99:17:32:ec:18:8c:fa:f1:0c:a6:64:
+ 21:85:cb:07:10:34:b0:52:88:2b:1f:68:9b:d2:b1:
+ 8f:12:b0:b3:d2:e7:88:1f:1f:ef:38:77:54:53:5f:
+ 80:79:3f:2e:1a:aa:a8:1e:4b:2b:0d:ab:b7:63:b9:
+ 35:b7:7d:14:bc:59:4b:df:51:4a:d2:a1:e2:0c:e2:
+ 90:82:87:6a:ae:ea:d7:64:d6:98:55:e8:fd:af:1a:
+ 50:6c:54:bc:11:f2:fd:4a:f2:9d:bb:7f:0e:f4:d5:
+ be:8e:16:89:12:55:d8:c0:71:34:ee:f6:dc:2d:ec:
+ c4:87:25:86:8d:d8:21:e4:b0:4d:0c:89:dc:39:26:
+ 17:dd:f6:d7:94:85:d8:04:21:70:9d:6f:6f:ff:5c:
+ ba:19:e1:45:cb:56:57:28:7e:1c:0d:41:57:aa:b7:
+ b8:27:bb:b1:e4:fa:2a:ef:21:23:75:1a:ad:2d:9b:
+ 86:35:8c:9c:77:b5:73:ad:d8:94:2d:e4:f3:0c:9d:
+ ee:c1:4e:62:7e:17:c0:71:9e:2c:de:f1:f9:10:28:
+ 19:33
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.digicert.com/CPS
+
+ X509v3 Subject Key Identifier:
+ 3D:D3:50:A5:D6:A0:AD:EE:F3:4A:60:0A:65:D3:21:D4:F8:F8:D6:0F
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 9d:b6:d0:90:86:e1:86:02:ed:c5:a0:f0:34:1c:74:c1:8d:76:
+ cc:86:0a:a8:f0:4a:8a:42:d6:3f:c8:a9:4d:ad:7c:08:ad:e6:
+ b6:50:b8:a2:1a:4d:88:07:b1:29:21:dc:e7:da:c6:3c:21:e0:
+ e3:11:49:70:ac:7a:1d:01:a4:ca:11:3a:57:ab:7d:57:2a:40:
+ 74:fd:d3:1d:85:18:50:df:57:47:75:a1:7d:55:20:2e:47:37:
+ 50:72:8c:7f:82:1b:d2:62:8f:2d:03:5a:da:c3:c8:a1:ce:2c:
+ 52:a2:00:63:eb:73:ba:71:c8:49:27:23:97:64:85:9e:38:0e:
+ ad:63:68:3c:ba:52:81:58:79:a3:2c:0c:df:de:6d:eb:31:f2:
+ ba:a0:7c:6c:f1:2c:d4:e1:bd:77:84:37:03:ce:32:b5:c8:9a:
+ 81:1a:4a:92:4e:3b:46:9a:85:fe:83:a2:f9:9e:8c:a3:cc:0d:
+ 5e:b3:3d:cf:04:78:8f:14:14:7b:32:9c:c7:00:a6:5c:c4:b5:
+ a1:55:8d:5a:56:68:a4:22:70:aa:3c:81:71:d9:9d:a8:45:3b:
+ f4:e5:f6:a2:51:dd:c7:7b:62:e8:6f:0c:74:eb:b8:da:f8:bf:
+ 87:0d:79:50:91:90:9b:18:3b:91:59:27:f1:35:28:13:ab:26:
+ 7e:d5:f7:7a
+-----BEGIN CERTIFICATE-----
+MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW
+YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY
+uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/
+LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy
+/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh
+cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k
+8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB
+Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
+BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
+Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy
+dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2
+MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j
+b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW
+gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh
+hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg
+4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa
+2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs
+1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1
+oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn
+8TUoE6smftX3eg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert33[] = {
+ 0x30, 0x82, 0x04, 0xb6, 0x30, 0x82, 0x03, 0x9e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x0c, 0x79, 0xa9, 0x44, 0xb0, 0x8c, 0x11, 0x95, 0x20,
+ 0x92, 0x61, 0x5f, 0xe2, 0x6b, 0x1d, 0x83, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x32, 0x32, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x30, 0x32,
+ 0x32, 0x31, 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x75, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x48, 0x41,
+ 0x32, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xd7, 0x53, 0xa4, 0x04, 0x51, 0xf8, 0x99, 0xa6,
+ 0x16, 0x48, 0x4b, 0x67, 0x27, 0xaa, 0x93, 0x49, 0xd0, 0x39, 0xed, 0x0c,
+ 0xb0, 0xb0, 0x00, 0x87, 0xf1, 0x67, 0x28, 0x86, 0x85, 0x8c, 0x8e, 0x63,
+ 0xda, 0xbc, 0xb1, 0x40, 0x38, 0xe2, 0xd3, 0xf5, 0xec, 0xa5, 0x05, 0x18,
+ 0xb8, 0x3d, 0x3e, 0xc5, 0x99, 0x17, 0x32, 0xec, 0x18, 0x8c, 0xfa, 0xf1,
+ 0x0c, 0xa6, 0x64, 0x21, 0x85, 0xcb, 0x07, 0x10, 0x34, 0xb0, 0x52, 0x88,
+ 0x2b, 0x1f, 0x68, 0x9b, 0xd2, 0xb1, 0x8f, 0x12, 0xb0, 0xb3, 0xd2, 0xe7,
+ 0x88, 0x1f, 0x1f, 0xef, 0x38, 0x77, 0x54, 0x53, 0x5f, 0x80, 0x79, 0x3f,
+ 0x2e, 0x1a, 0xaa, 0xa8, 0x1e, 0x4b, 0x2b, 0x0d, 0xab, 0xb7, 0x63, 0xb9,
+ 0x35, 0xb7, 0x7d, 0x14, 0xbc, 0x59, 0x4b, 0xdf, 0x51, 0x4a, 0xd2, 0xa1,
+ 0xe2, 0x0c, 0xe2, 0x90, 0x82, 0x87, 0x6a, 0xae, 0xea, 0xd7, 0x64, 0xd6,
+ 0x98, 0x55, 0xe8, 0xfd, 0xaf, 0x1a, 0x50, 0x6c, 0x54, 0xbc, 0x11, 0xf2,
+ 0xfd, 0x4a, 0xf2, 0x9d, 0xbb, 0x7f, 0x0e, 0xf4, 0xd5, 0xbe, 0x8e, 0x16,
+ 0x89, 0x12, 0x55, 0xd8, 0xc0, 0x71, 0x34, 0xee, 0xf6, 0xdc, 0x2d, 0xec,
+ 0xc4, 0x87, 0x25, 0x86, 0x8d, 0xd8, 0x21, 0xe4, 0xb0, 0x4d, 0x0c, 0x89,
+ 0xdc, 0x39, 0x26, 0x17, 0xdd, 0xf6, 0xd7, 0x94, 0x85, 0xd8, 0x04, 0x21,
+ 0x70, 0x9d, 0x6f, 0x6f, 0xff, 0x5c, 0xba, 0x19, 0xe1, 0x45, 0xcb, 0x56,
+ 0x57, 0x28, 0x7e, 0x1c, 0x0d, 0x41, 0x57, 0xaa, 0xb7, 0xb8, 0x27, 0xbb,
+ 0xb1, 0xe4, 0xfa, 0x2a, 0xef, 0x21, 0x23, 0x75, 0x1a, 0xad, 0x2d, 0x9b,
+ 0x86, 0x35, 0x8c, 0x9c, 0x77, 0xb5, 0x73, 0xad, 0xd8, 0x94, 0x2d, 0xe4,
+ 0xf3, 0x0c, 0x9d, 0xee, 0xc1, 0x4e, 0x62, 0x7e, 0x17, 0xc0, 0x71, 0x9e,
+ 0x2c, 0xde, 0xf1, 0xf9, 0x10, 0x28, 0x19, 0x33, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x49, 0x30, 0x82, 0x01, 0x45, 0x30, 0x12, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01,
+ 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x02, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69,
+ 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4b,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0xa0,
+ 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65,
+ 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61,
+ 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36,
+ 0x30, 0x34, 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a,
+ 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01,
+ 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3d, 0xd3, 0x50, 0xa5, 0xd6, 0xa0, 0xad,
+ 0xee, 0xf3, 0x4a, 0x60, 0x0a, 0x65, 0xd3, 0x21, 0xd4, 0xf8, 0xf8, 0xd6,
+ 0x0f, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4,
+ 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9d, 0xb6, 0xd0, 0x90, 0x86, 0xe1,
+ 0x86, 0x02, 0xed, 0xc5, 0xa0, 0xf0, 0x34, 0x1c, 0x74, 0xc1, 0x8d, 0x76,
+ 0xcc, 0x86, 0x0a, 0xa8, 0xf0, 0x4a, 0x8a, 0x42, 0xd6, 0x3f, 0xc8, 0xa9,
+ 0x4d, 0xad, 0x7c, 0x08, 0xad, 0xe6, 0xb6, 0x50, 0xb8, 0xa2, 0x1a, 0x4d,
+ 0x88, 0x07, 0xb1, 0x29, 0x21, 0xdc, 0xe7, 0xda, 0xc6, 0x3c, 0x21, 0xe0,
+ 0xe3, 0x11, 0x49, 0x70, 0xac, 0x7a, 0x1d, 0x01, 0xa4, 0xca, 0x11, 0x3a,
+ 0x57, 0xab, 0x7d, 0x57, 0x2a, 0x40, 0x74, 0xfd, 0xd3, 0x1d, 0x85, 0x18,
+ 0x50, 0xdf, 0x57, 0x47, 0x75, 0xa1, 0x7d, 0x55, 0x20, 0x2e, 0x47, 0x37,
+ 0x50, 0x72, 0x8c, 0x7f, 0x82, 0x1b, 0xd2, 0x62, 0x8f, 0x2d, 0x03, 0x5a,
+ 0xda, 0xc3, 0xc8, 0xa1, 0xce, 0x2c, 0x52, 0xa2, 0x00, 0x63, 0xeb, 0x73,
+ 0xba, 0x71, 0xc8, 0x49, 0x27, 0x23, 0x97, 0x64, 0x85, 0x9e, 0x38, 0x0e,
+ 0xad, 0x63, 0x68, 0x3c, 0xba, 0x52, 0x81, 0x58, 0x79, 0xa3, 0x2c, 0x0c,
+ 0xdf, 0xde, 0x6d, 0xeb, 0x31, 0xf2, 0xba, 0xa0, 0x7c, 0x6c, 0xf1, 0x2c,
+ 0xd4, 0xe1, 0xbd, 0x77, 0x84, 0x37, 0x03, 0xce, 0x32, 0xb5, 0xc8, 0x9a,
+ 0x81, 0x1a, 0x4a, 0x92, 0x4e, 0x3b, 0x46, 0x9a, 0x85, 0xfe, 0x83, 0xa2,
+ 0xf9, 0x9e, 0x8c, 0xa3, 0xcc, 0x0d, 0x5e, 0xb3, 0x3d, 0xcf, 0x04, 0x78,
+ 0x8f, 0x14, 0x14, 0x7b, 0x32, 0x9c, 0xc7, 0x00, 0xa6, 0x5c, 0xc4, 0xb5,
+ 0xa1, 0x55, 0x8d, 0x5a, 0x56, 0x68, 0xa4, 0x22, 0x70, 0xaa, 0x3c, 0x81,
+ 0x71, 0xd9, 0x9d, 0xa8, 0x45, 0x3b, 0xf4, 0xe5, 0xf6, 0xa2, 0x51, 0xdd,
+ 0xc7, 0x7b, 0x62, 0xe8, 0x6f, 0x0c, 0x74, 0xeb, 0xb8, 0xda, 0xf8, 0xbf,
+ 0x87, 0x0d, 0x79, 0x50, 0x91, 0x90, 0x9b, 0x18, 0x3b, 0x91, 0x59, 0x27,
+ 0xf1, 0x35, 0x28, 0x13, 0xab, 0x26, 0x7e, 0xd5, 0xf7, 0x7a,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 36:34:9e:18:c9:9c:26:69:b6:56:2e:6c:e5:ad:71:32
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2008 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA - G3
+ Validity
+ Not Before: May 23 00:00:00 2013 GMT
+ Not After : May 22 23:59:59 2023 GMT
+ Subject: C=US, O=thawte, Inc., CN=thawte SHA256 SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:63:2b:d4:ba:5d:38:ae:b0:cf:b9:4c:38:df:
+ 20:7d:f1:2b:47:71:1d:8b:68:f3:56:f9:9c:da:aa:
+ e5:84:26:de:a5:71:30:bc:f3:31:23:9d:e8:3b:80:
+ c8:66:57:75:b6:57:0e:db:93:f5:26:8e:70:ba:64:
+ 52:66:8a:2a:88:5c:44:18:4d:a8:a2:7c:bd:56:61:
+ 32:90:12:f9:35:87:48:60:b0:6e:90:67:44:01:8d:
+ e7:c9:0d:63:68:72:72:ab:63:3c:86:b8:1f:7d:ad:
+ 88:25:a7:6a:88:29:fb:59:c6:78:71:5f:2c:ba:89:
+ e6:d3:80:fd:57:ec:b9:51:5f:43:33:2e:7e:25:3b:
+ a4:04:d1:60:8c:b3:44:33:93:0c:ad:2a:b6:44:a2:
+ 19:3b:af:c4:90:6f:7b:05:87:86:9b:2c:6a:9d:2b:
+ 6c:77:c9:00:9f:c9:cf:ac:ed:3e:1b:f7:c3:f3:d9:
+ f8:6c:d4:a0:57:c4:fb:28:32:aa:33:f0:e6:ba:98:
+ df:e5:c2:4e:9c:74:bf:8a:48:c2:f2:1b:f0:77:40:
+ 41:07:04:b2:3a:d5:4c:c4:29:a9:11:40:3f:02:46:
+ f0:91:d5:d2:81:83:86:13:b3:31:ed:46:ab:a8:87:
+ 76:a9:99:7d:bc:cd:31:50:f4:a5:b5:dc:a5:32:b3:
+ 8b:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.thawte.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: https://www.thawte.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePCA-G3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-415
+ X509v3 Subject Key Identifier:
+ 2B:9A:35:AE:01:18:38:30:E1:70:7A:05:E0:11:76:A3:CE:BD:90:14
+ X509v3 Authority Key Identifier:
+ keyid:AD:6C:AA:94:60:9C:ED:E4:FF:FA:3E:0A:74:2B:63:03:F7:B6:59:BF
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 74:a6:56:e8:af:93:96:19:fb:26:f9:0d:b0:44:a5:cd:e9:7a:
+ 48:03:74:01:6c:13:71:b7:e0:82:90:99:62:23:e3:d6:99:af:
+ f0:c7:1e:9e:a8:18:21:db:b4:94:3f:34:56:1b:99:55:2f:8e:
+ f0:45:33:32:b7:72:c1:13:5b:34:d3:f5:60:e5:2e:18:d1:5c:
+ c5:6a:c1:aa:87:50:0c:1c:9d:64:2b:ff:1b:dc:d5:2e:61:0b:
+ e7:b9:b6:91:53:86:d9:03:2a:d1:3d:7b:4a:da:2b:07:be:29:
+ f2:60:42:a9:91:1a:0e:2e:3c:d1:7d:a5:13:14:02:fa:ee:8b:
+ 8d:b6:c8:b8:3e:56:81:57:21:24:3f:65:c3:b4:c9:ce:5c:8d:
+ 46:ac:53:f3:f9:55:74:c8:2b:fd:d2:78:70:f5:f8:11:e5:f4:
+ a7:ad:20:f5:9d:f1:ec:70:f6:13:ac:e6:8c:8d:db:3f:c6:f2:
+ 79:0e:ab:52:f2:cc:1b:79:27:cf:16:b3:d6:f3:c6:36:80:43:
+ ec:c5:94:f0:dd:90:8d:f8:c6:52:46:56:eb:74:47:be:a6:f3:
+ 19:ae:71:4c:c0:e1:e7:d4:cf:ed:d4:06:28:2a:11:3c:ba:d9:
+ 41:6e:00:e7:81:37:93:e4:da:62:c6:1d:67:6f:63:b4:14:86:
+ d9:a6:62:f0
+-----BEGIN CERTIFICATE-----
+MIIEwjCCA6qgAwIBAgIQNjSeGMmcJmm2Vi5s5a1xMjANBgkqhkiG9w0BAQsFADCB
+rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
+BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0xMzA1MjMwMDAwMDBa
+Fw0yMzA1MjIyMzU5NTlaMEMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUs
+IEluYy4xHTAbBgNVBAMTFHRoYXd0ZSBTSEEyNTYgU1NMIENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2Mr1LpdOK6wz7lMON8gffErR3Edi2jzVvmc
+2qrlhCbepXEwvPMxI53oO4DIZld1tlcO25P1Jo5wumRSZooqiFxEGE2oony9VmEy
+kBL5NYdIYLBukGdEAY3nyQ1jaHJyq2M8hrgffa2IJadqiCn7WcZ4cV8suonm04D9
+V+y5UV9DMy5+JTukBNFgjLNEM5MMrSq2RKIZO6/EkG97BYeGmyxqnStsd8kAn8nP
+rO0+G/fD89n4bNSgV8T7KDKqM/Dmupjf5cJOnHS/ikjC8hvwd0BBBwSyOtVMxCmp
+EUA/AkbwkdXSgYOGE7Mx7UarqId2qZl9vM0xUPSltdylMrOLiwIDAQABo4IBRDCC
+AUAwMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3
+dGUuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwQQYDVR0gBDowODA2BgpghkgBhvhF
+AQc2MCgwJgYIKwYBBQUHAgEWGmh0dHBzOi8vd3d3LnRoYXd0ZS5jb20vY3BzMDcG
+A1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVQQ0Et
+RzMuY3JsMA4GA1UdDwEB/wQEAwIBBjAqBgNVHREEIzAhpB8wHTEbMBkGA1UEAxMS
+VmVyaVNpZ25NUEtJLTItNDE1MB0GA1UdDgQWBBQrmjWuARg4MOFwegXgEXajzr2Q
+FDAfBgNVHSMEGDAWgBStbKqUYJzt5P/6Pgp0K2MD97ZZvzANBgkqhkiG9w0BAQsF
+AAOCAQEAdKZW6K+Tlhn7JvkNsESlzel6SAN0AWwTcbfggpCZYiPj1pmv8McenqgY
+Idu0lD80VhuZVS+O8EUzMrdywRNbNNP1YOUuGNFcxWrBqodQDBydZCv/G9zVLmEL
+57m2kVOG2QMq0T17StorB74p8mBCqZEaDi480X2lExQC+u6LjbbIuD5WgVchJD9l
+w7TJzlyNRqxT8/lVdMgr/dJ4cPX4EeX0p60g9Z3x7HD2E6zmjI3bP8byeQ6rUvLM
+G3knzxaz1vPGNoBD7MWU8N2QjfjGUkZW63RHvqbzGa5xTMDh59TP7dQGKCoRPLrZ
+QW4A54E3k+TaYsYdZ29jtBSG2aZi8A==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert34[] = {
+ 0x30, 0x82, 0x04, 0xc2, 0x30, 0x82, 0x03, 0xaa, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x36, 0x34, 0x9e, 0x18, 0xc9, 0x9c, 0x26, 0x69, 0xb6,
+ 0x56, 0x2e, 0x6c, 0xe5, 0xad, 0x71, 0x32, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x38, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x1b, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x33, 0x30, 0x35, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x17, 0x0d, 0x32, 0x33, 0x30, 0x35, 0x32, 0x32, 0x32, 0x33, 0x35, 0x39,
+ 0x35, 0x39, 0x5a, 0x30, 0x43, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x14, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53,
+ 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x63, 0x2b,
+ 0xd4, 0xba, 0x5d, 0x38, 0xae, 0xb0, 0xcf, 0xb9, 0x4c, 0x38, 0xdf, 0x20,
+ 0x7d, 0xf1, 0x2b, 0x47, 0x71, 0x1d, 0x8b, 0x68, 0xf3, 0x56, 0xf9, 0x9c,
+ 0xda, 0xaa, 0xe5, 0x84, 0x26, 0xde, 0xa5, 0x71, 0x30, 0xbc, 0xf3, 0x31,
+ 0x23, 0x9d, 0xe8, 0x3b, 0x80, 0xc8, 0x66, 0x57, 0x75, 0xb6, 0x57, 0x0e,
+ 0xdb, 0x93, 0xf5, 0x26, 0x8e, 0x70, 0xba, 0x64, 0x52, 0x66, 0x8a, 0x2a,
+ 0x88, 0x5c, 0x44, 0x18, 0x4d, 0xa8, 0xa2, 0x7c, 0xbd, 0x56, 0x61, 0x32,
+ 0x90, 0x12, 0xf9, 0x35, 0x87, 0x48, 0x60, 0xb0, 0x6e, 0x90, 0x67, 0x44,
+ 0x01, 0x8d, 0xe7, 0xc9, 0x0d, 0x63, 0x68, 0x72, 0x72, 0xab, 0x63, 0x3c,
+ 0x86, 0xb8, 0x1f, 0x7d, 0xad, 0x88, 0x25, 0xa7, 0x6a, 0x88, 0x29, 0xfb,
+ 0x59, 0xc6, 0x78, 0x71, 0x5f, 0x2c, 0xba, 0x89, 0xe6, 0xd3, 0x80, 0xfd,
+ 0x57, 0xec, 0xb9, 0x51, 0x5f, 0x43, 0x33, 0x2e, 0x7e, 0x25, 0x3b, 0xa4,
+ 0x04, 0xd1, 0x60, 0x8c, 0xb3, 0x44, 0x33, 0x93, 0x0c, 0xad, 0x2a, 0xb6,
+ 0x44, 0xa2, 0x19, 0x3b, 0xaf, 0xc4, 0x90, 0x6f, 0x7b, 0x05, 0x87, 0x86,
+ 0x9b, 0x2c, 0x6a, 0x9d, 0x2b, 0x6c, 0x77, 0xc9, 0x00, 0x9f, 0xc9, 0xcf,
+ 0xac, 0xed, 0x3e, 0x1b, 0xf7, 0xc3, 0xf3, 0xd9, 0xf8, 0x6c, 0xd4, 0xa0,
+ 0x57, 0xc4, 0xfb, 0x28, 0x32, 0xaa, 0x33, 0xf0, 0xe6, 0xba, 0x98, 0xdf,
+ 0xe5, 0xc2, 0x4e, 0x9c, 0x74, 0xbf, 0x8a, 0x48, 0xc2, 0xf2, 0x1b, 0xf0,
+ 0x77, 0x40, 0x41, 0x07, 0x04, 0xb2, 0x3a, 0xd5, 0x4c, 0xc4, 0x29, 0xa9,
+ 0x11, 0x40, 0x3f, 0x02, 0x46, 0xf0, 0x91, 0xd5, 0xd2, 0x81, 0x83, 0x86,
+ 0x13, 0xb3, 0x31, 0xed, 0x46, 0xab, 0xa8, 0x87, 0x76, 0xa9, 0x99, 0x7d,
+ 0xbc, 0xcd, 0x31, 0x50, 0xf4, 0xa5, 0xb5, 0xdc, 0xa5, 0x32, 0xb3, 0x8b,
+ 0x8b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x44, 0x30, 0x82,
+ 0x01, 0x40, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30,
+ 0x38, 0x30, 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45,
+ 0x01, 0x07, 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74,
+ 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x37, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30, 0x30, 0x2e, 0x30, 0x2c, 0xa0, 0x2a,
+ 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2d,
+ 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2a,
+ 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, 0x30, 0x21, 0xa4, 0x1f, 0x30,
+ 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49,
+ 0x2d, 0x32, 0x2d, 0x34, 0x31, 0x35, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2b, 0x9a, 0x35, 0xae, 0x01, 0x18, 0x38,
+ 0x30, 0xe1, 0x70, 0x7a, 0x05, 0xe0, 0x11, 0x76, 0xa3, 0xce, 0xbd, 0x90,
+ 0x14, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xad, 0x6c, 0xaa, 0x94, 0x60, 0x9c, 0xed, 0xe4, 0xff, 0xfa,
+ 0x3e, 0x0a, 0x74, 0x2b, 0x63, 0x03, 0xf7, 0xb6, 0x59, 0xbf, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x74, 0xa6, 0x56, 0xe8, 0xaf, 0x93,
+ 0x96, 0x19, 0xfb, 0x26, 0xf9, 0x0d, 0xb0, 0x44, 0xa5, 0xcd, 0xe9, 0x7a,
+ 0x48, 0x03, 0x74, 0x01, 0x6c, 0x13, 0x71, 0xb7, 0xe0, 0x82, 0x90, 0x99,
+ 0x62, 0x23, 0xe3, 0xd6, 0x99, 0xaf, 0xf0, 0xc7, 0x1e, 0x9e, 0xa8, 0x18,
+ 0x21, 0xdb, 0xb4, 0x94, 0x3f, 0x34, 0x56, 0x1b, 0x99, 0x55, 0x2f, 0x8e,
+ 0xf0, 0x45, 0x33, 0x32, 0xb7, 0x72, 0xc1, 0x13, 0x5b, 0x34, 0xd3, 0xf5,
+ 0x60, 0xe5, 0x2e, 0x18, 0xd1, 0x5c, 0xc5, 0x6a, 0xc1, 0xaa, 0x87, 0x50,
+ 0x0c, 0x1c, 0x9d, 0x64, 0x2b, 0xff, 0x1b, 0xdc, 0xd5, 0x2e, 0x61, 0x0b,
+ 0xe7, 0xb9, 0xb6, 0x91, 0x53, 0x86, 0xd9, 0x03, 0x2a, 0xd1, 0x3d, 0x7b,
+ 0x4a, 0xda, 0x2b, 0x07, 0xbe, 0x29, 0xf2, 0x60, 0x42, 0xa9, 0x91, 0x1a,
+ 0x0e, 0x2e, 0x3c, 0xd1, 0x7d, 0xa5, 0x13, 0x14, 0x02, 0xfa, 0xee, 0x8b,
+ 0x8d, 0xb6, 0xc8, 0xb8, 0x3e, 0x56, 0x81, 0x57, 0x21, 0x24, 0x3f, 0x65,
+ 0xc3, 0xb4, 0xc9, 0xce, 0x5c, 0x8d, 0x46, 0xac, 0x53, 0xf3, 0xf9, 0x55,
+ 0x74, 0xc8, 0x2b, 0xfd, 0xd2, 0x78, 0x70, 0xf5, 0xf8, 0x11, 0xe5, 0xf4,
+ 0xa7, 0xad, 0x20, 0xf5, 0x9d, 0xf1, 0xec, 0x70, 0xf6, 0x13, 0xac, 0xe6,
+ 0x8c, 0x8d, 0xdb, 0x3f, 0xc6, 0xf2, 0x79, 0x0e, 0xab, 0x52, 0xf2, 0xcc,
+ 0x1b, 0x79, 0x27, 0xcf, 0x16, 0xb3, 0xd6, 0xf3, 0xc6, 0x36, 0x80, 0x43,
+ 0xec, 0xc5, 0x94, 0xf0, 0xdd, 0x90, 0x8d, 0xf8, 0xc6, 0x52, 0x46, 0x56,
+ 0xeb, 0x74, 0x47, 0xbe, 0xa6, 0xf3, 0x19, 0xae, 0x71, 0x4c, 0xc0, 0xe1,
+ 0xe7, 0xd4, 0xcf, 0xed, 0xd4, 0x06, 0x28, 0x2a, 0x11, 0x3c, 0xba, 0xd9,
+ 0x41, 0x6e, 0x00, 0xe7, 0x81, 0x37, 0x93, 0xe4, 0xda, 0x62, 0xc6, 0x1d,
+ 0x67, 0x6f, 0x63, 0xb4, 0x14, 0x86, 0xd9, 0xa6, 0x62, 0xf0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 41:82:12:7d:12:d9:c6:b3:21:39:43:12:56:64:00:b8
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., OU=(c) 2008 GeoTrust Inc. - For authorized use only, CN=GeoTrust Primary Certification Authority - G3
+ Validity
+ Not Before: May 23 00:00:00 2013 GMT
+ Not After : May 22 23:59:59 2023 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust SHA256 SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c6:a9:0b:5d:17:a5:7d:c6:cf:2a:ef:c6:66:d1:
+ 42:1e:5f:83:78:68:91:af:e6:a7:8b:f0:1d:44:01:
+ 0a:19:ca:9c:d4:8b:1d:e1:a1:90:a3:c1:5b:b4:d7:
+ 5b:6a:8b:fc:0e:49:1e:c2:62:29:fe:80:15:39:8b:
+ 81:2a:27:b5:fb:12:a8:05:22:0b:c5:2c:f5:d9:98:
+ dd:16:2f:3b:66:e7:62:a2:43:32:ac:8f:b5:85:c8:
+ 52:06:2c:5c:c0:77:fa:67:f7:83:e8:5e:05:8d:c8:
+ ab:a1:16:32:8a:d2:40:ec:86:3a:1c:23:a9:8d:b5:
+ 00:de:72:bd:85:55:fe:06:01:60:5d:ad:b3:e0:65:
+ 73:a5:92:14:9e:94:56:6f:93:ee:af:a9:3a:30:25:
+ 4a:8e:09:84:ef:b7:d2:d5:d7:9b:49:cd:e9:c0:5e:
+ 67:71:22:ac:50:90:43:20:5d:a1:a3:15:83:fd:fc:
+ a7:39:bc:6b:65:48:12:60:ff:dd:23:b3:3a:aa:f4:
+ 9f:9c:37:53:41:a2:47:93:81:33:09:e5:22:c6:c8:
+ 1c:49:a1:6e:8d:cc:83:b3:9a:cd:ea:43:f2:19:d3:
+ 24:cb:a8:29:ae:52:cc:f4:08:27:b0:84:ea:ce:27:
+ b5:e1:34:13:73:92:5c:87:86:2a:c6:b0:68:36:ad:
+ cb:09
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://pca-g3-ocsp.geotrust.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/GeoTrustPCA-G3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-416
+ X509v3 Subject Key Identifier:
+ 14:67:8E:ED:83:4F:D6:1E:9D:40:04:0C:04:46:A1:70:34:B2:0F:72
+ X509v3 Authority Key Identifier:
+ keyid:C4:79:CA:8E:A1:4E:03:1D:1C:DC:6B:DB:31:5B:94:3E:3F:30:7F:2D
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 10:10:ea:f2:10:d6:08:46:e2:c1:8f:3e:36:59:c8:2b:0f:fe:
+ 4d:ec:e3:f8:b6:56:31:78:25:d4:76:f2:08:dd:ef:3f:cd:8b:
+ 1c:7e:aa:7f:fc:0b:a8:23:64:51:b3:87:d6:09:fa:22:fa:c7:
+ 0a:51:e8:ce:b8:f6:03:70:e0:1b:5a:b9:b1:b2:93:11:10:f9:
+ 97:05:07:29:6c:6d:57:25:54:e8:f9:66:9b:0e:fb:db:9f:ee:
+ 96:6f:65:cb:1f:d8:55:ce:31:fa:cf:02:f4:d0:7f:50:66:ff:
+ 2f:79:9b:a5:c2:df:d6:cf:c8:15:83:96:84:98:b2:46:d4:5f:
+ 13:a8:3e:a7:34:9c:05:38:da:cf:d6:69:95:a9:26:87:76:01:
+ d7:b2:51:0f:81:69:46:26:1c:99:b6:83:58:e3:3b:58:8f:dc:
+ b4:71:c0:b9:bf:42:9c:1c:03:9e:e4:46:a8:ea:b9:c1:cd:f6:
+ 5b:a9:3c:96:fb:79:a4:33:73:a7:9e:78:b9:70:dc:72:74:c4:
+ 32:c8:00:1b:c9:ef:48:d3:fb:3a:9b:fa:fe:7a:9a:40:69:1c:
+ c8:da:28:37:0b:d3:a3:b9:7e:96:cc:2b:28:c3:56:6c:6f:e9:
+ db:52:b1:fa:9a:fb:e7:af:b5:97:a6:22:c3:c5:a8:93:b1:00:
+ c9:07:b2:7d
+-----BEGIN CERTIFICATE-----
+MIIExzCCA6+gAwIBAgIQQYISfRLZxrMhOUMSVmQAuDANBgkqhkiG9w0BAQsFADCB
+mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
+MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEczMB4XDTEzMDUyMzAwMDAwMFoXDTIzMDUyMjIzNTk1OVowRjELMAkG
+A1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHzAdBgNVBAMTFkdlb1Ry
+dXN0IFNIQTI1NiBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDGqQtdF6V9xs8q78Zm0UIeX4N4aJGv5qeL8B1EAQoZypzUix3hoZCjwVu011tq
+i/wOSR7CYin+gBU5i4EqJ7X7EqgFIgvFLPXZmN0WLztm52KiQzKsj7WFyFIGLFzA
+d/pn94PoXgWNyKuhFjKK0kDshjocI6mNtQDecr2FVf4GAWBdrbPgZXOlkhSelFZv
+k+6vqTowJUqOCYTvt9LV15tJzenAXmdxIqxQkEMgXaGjFYP9/Kc5vGtlSBJg/90j
+szqq9J+cN1NBokeTgTMJ5SLGyBxJoW6NzIOzms3qQ/IZ0yTLqCmuUsz0CCewhOrO
+J7XhNBNzklyHhirGsGg2rcsJAgMBAAGjggFcMIIBWDA7BggrBgEFBQcBAQQvMC0w
+KwYIKwYBBQUHMAGGH2h0dHA6Ly9wY2EtZzMtb2NzcC5nZW90cnVzdC5jb20wEgYD
+VR0TAQH/BAgwBgEB/wIBADBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYwMzAxBggr
+BgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczA7
+BgNVHR8ENDAyMDCgLqAshipodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9HZW9UcnVz
+dFBDQS1HMy5jcmwwDgYDVR0PAQH/BAQDAgEGMCoGA1UdEQQjMCGkHzAdMRswGQYD
+VQQDExJWZXJpU2lnbk1QS0ktMi00MTYwHQYDVR0OBBYEFBRnju2DT9YenUAEDARG
+oXA0sg9yMB8GA1UdIwQYMBaAFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3
+DQEBCwUAA4IBAQAQEOryENYIRuLBjz42WcgrD/5N7OP4tlYxeCXUdvII3e8/zYsc
+fqp//AuoI2RRs4fWCfoi+scKUejOuPYDcOAbWrmxspMREPmXBQcpbG1XJVTo+Wab
+Dvvbn+6Wb2XLH9hVzjH6zwL00H9QZv8veZulwt/Wz8gVg5aEmLJG1F8TqD6nNJwF
+ONrP1mmVqSaHdgHXslEPgWlGJhyZtoNY4ztYj9y0ccC5v0KcHAOe5Eao6rnBzfZb
+qTyW+3mkM3Onnni5cNxydMQyyAAbye9I0/s6m/r+eppAaRzI2ig3C9OjuX6WzCso
+w1Zsb+nbUrH6mvvnr7WXpiLDxaiTsQDJB7J9
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert35[] = {
+ 0x30, 0x82, 0x04, 0xc7, 0x30, 0x82, 0x03, 0xaf, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x41, 0x82, 0x12, 0x7d, 0x12, 0xd9, 0xc6, 0xb3, 0x21,
+ 0x39, 0x43, 0x12, 0x56, 0x64, 0x00, 0xb8, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x47, 0x65,
+ 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20,
+ 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c,
+ 0x79, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69,
+ 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x33, 0x30, 0x35, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x35, 0x32, 0x32, 0x32, 0x33,
+ 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x46, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x47, 0x65, 0x6f, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x53,
+ 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xc6, 0xa9, 0x0b, 0x5d, 0x17, 0xa5, 0x7d, 0xc6, 0xcf, 0x2a,
+ 0xef, 0xc6, 0x66, 0xd1, 0x42, 0x1e, 0x5f, 0x83, 0x78, 0x68, 0x91, 0xaf,
+ 0xe6, 0xa7, 0x8b, 0xf0, 0x1d, 0x44, 0x01, 0x0a, 0x19, 0xca, 0x9c, 0xd4,
+ 0x8b, 0x1d, 0xe1, 0xa1, 0x90, 0xa3, 0xc1, 0x5b, 0xb4, 0xd7, 0x5b, 0x6a,
+ 0x8b, 0xfc, 0x0e, 0x49, 0x1e, 0xc2, 0x62, 0x29, 0xfe, 0x80, 0x15, 0x39,
+ 0x8b, 0x81, 0x2a, 0x27, 0xb5, 0xfb, 0x12, 0xa8, 0x05, 0x22, 0x0b, 0xc5,
+ 0x2c, 0xf5, 0xd9, 0x98, 0xdd, 0x16, 0x2f, 0x3b, 0x66, 0xe7, 0x62, 0xa2,
+ 0x43, 0x32, 0xac, 0x8f, 0xb5, 0x85, 0xc8, 0x52, 0x06, 0x2c, 0x5c, 0xc0,
+ 0x77, 0xfa, 0x67, 0xf7, 0x83, 0xe8, 0x5e, 0x05, 0x8d, 0xc8, 0xab, 0xa1,
+ 0x16, 0x32, 0x8a, 0xd2, 0x40, 0xec, 0x86, 0x3a, 0x1c, 0x23, 0xa9, 0x8d,
+ 0xb5, 0x00, 0xde, 0x72, 0xbd, 0x85, 0x55, 0xfe, 0x06, 0x01, 0x60, 0x5d,
+ 0xad, 0xb3, 0xe0, 0x65, 0x73, 0xa5, 0x92, 0x14, 0x9e, 0x94, 0x56, 0x6f,
+ 0x93, 0xee, 0xaf, 0xa9, 0x3a, 0x30, 0x25, 0x4a, 0x8e, 0x09, 0x84, 0xef,
+ 0xb7, 0xd2, 0xd5, 0xd7, 0x9b, 0x49, 0xcd, 0xe9, 0xc0, 0x5e, 0x67, 0x71,
+ 0x22, 0xac, 0x50, 0x90, 0x43, 0x20, 0x5d, 0xa1, 0xa3, 0x15, 0x83, 0xfd,
+ 0xfc, 0xa7, 0x39, 0xbc, 0x6b, 0x65, 0x48, 0x12, 0x60, 0xff, 0xdd, 0x23,
+ 0xb3, 0x3a, 0xaa, 0xf4, 0x9f, 0x9c, 0x37, 0x53, 0x41, 0xa2, 0x47, 0x93,
+ 0x81, 0x33, 0x09, 0xe5, 0x22, 0xc6, 0xc8, 0x1c, 0x49, 0xa1, 0x6e, 0x8d,
+ 0xcc, 0x83, 0xb3, 0x9a, 0xcd, 0xea, 0x43, 0xf2, 0x19, 0xd3, 0x24, 0xcb,
+ 0xa8, 0x29, 0xae, 0x52, 0xcc, 0xf4, 0x08, 0x27, 0xb0, 0x84, 0xea, 0xce,
+ 0x27, 0xb5, 0xe1, 0x34, 0x13, 0x73, 0x92, 0x5c, 0x87, 0x86, 0x2a, 0xc6,
+ 0xb0, 0x68, 0x36, 0xad, 0xcb, 0x09, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x5c, 0x30, 0x82, 0x01, 0x58, 0x30, 0x3b, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2f, 0x30, 0x2d, 0x30,
+ 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86,
+ 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x63, 0x61, 0x2d,
+ 0x67, 0x33, 0x2d, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x65, 0x6f, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x3b,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0xa0,
+ 0x2e, 0xa0, 0x2c, 0x86, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x50, 0x43, 0x41, 0x2d, 0x47, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23,
+ 0x30, 0x21, 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x34, 0x31, 0x36, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x14, 0x67,
+ 0x8e, 0xed, 0x83, 0x4f, 0xd6, 0x1e, 0x9d, 0x40, 0x04, 0x0c, 0x04, 0x46,
+ 0xa1, 0x70, 0x34, 0xb2, 0x0f, 0x72, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc4, 0x79, 0xca, 0x8e, 0xa1,
+ 0x4e, 0x03, 0x1d, 0x1c, 0xdc, 0x6b, 0xdb, 0x31, 0x5b, 0x94, 0x3e, 0x3f,
+ 0x30, 0x7f, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x10,
+ 0x10, 0xea, 0xf2, 0x10, 0xd6, 0x08, 0x46, 0xe2, 0xc1, 0x8f, 0x3e, 0x36,
+ 0x59, 0xc8, 0x2b, 0x0f, 0xfe, 0x4d, 0xec, 0xe3, 0xf8, 0xb6, 0x56, 0x31,
+ 0x78, 0x25, 0xd4, 0x76, 0xf2, 0x08, 0xdd, 0xef, 0x3f, 0xcd, 0x8b, 0x1c,
+ 0x7e, 0xaa, 0x7f, 0xfc, 0x0b, 0xa8, 0x23, 0x64, 0x51, 0xb3, 0x87, 0xd6,
+ 0x09, 0xfa, 0x22, 0xfa, 0xc7, 0x0a, 0x51, 0xe8, 0xce, 0xb8, 0xf6, 0x03,
+ 0x70, 0xe0, 0x1b, 0x5a, 0xb9, 0xb1, 0xb2, 0x93, 0x11, 0x10, 0xf9, 0x97,
+ 0x05, 0x07, 0x29, 0x6c, 0x6d, 0x57, 0x25, 0x54, 0xe8, 0xf9, 0x66, 0x9b,
+ 0x0e, 0xfb, 0xdb, 0x9f, 0xee, 0x96, 0x6f, 0x65, 0xcb, 0x1f, 0xd8, 0x55,
+ 0xce, 0x31, 0xfa, 0xcf, 0x02, 0xf4, 0xd0, 0x7f, 0x50, 0x66, 0xff, 0x2f,
+ 0x79, 0x9b, 0xa5, 0xc2, 0xdf, 0xd6, 0xcf, 0xc8, 0x15, 0x83, 0x96, 0x84,
+ 0x98, 0xb2, 0x46, 0xd4, 0x5f, 0x13, 0xa8, 0x3e, 0xa7, 0x34, 0x9c, 0x05,
+ 0x38, 0xda, 0xcf, 0xd6, 0x69, 0x95, 0xa9, 0x26, 0x87, 0x76, 0x01, 0xd7,
+ 0xb2, 0x51, 0x0f, 0x81, 0x69, 0x46, 0x26, 0x1c, 0x99, 0xb6, 0x83, 0x58,
+ 0xe3, 0x3b, 0x58, 0x8f, 0xdc, 0xb4, 0x71, 0xc0, 0xb9, 0xbf, 0x42, 0x9c,
+ 0x1c, 0x03, 0x9e, 0xe4, 0x46, 0xa8, 0xea, 0xb9, 0xc1, 0xcd, 0xf6, 0x5b,
+ 0xa9, 0x3c, 0x96, 0xfb, 0x79, 0xa4, 0x33, 0x73, 0xa7, 0x9e, 0x78, 0xb9,
+ 0x70, 0xdc, 0x72, 0x74, 0xc4, 0x32, 0xc8, 0x00, 0x1b, 0xc9, 0xef, 0x48,
+ 0xd3, 0xfb, 0x3a, 0x9b, 0xfa, 0xfe, 0x7a, 0x9a, 0x40, 0x69, 0x1c, 0xc8,
+ 0xda, 0x28, 0x37, 0x0b, 0xd3, 0xa3, 0xb9, 0x7e, 0x96, 0xcc, 0x2b, 0x28,
+ 0xc3, 0x56, 0x6c, 0x6f, 0xe9, 0xdb, 0x52, 0xb1, 0xfa, 0x9a, 0xfb, 0xe7,
+ 0xaf, 0xb5, 0x97, 0xa6, 0x22, 0xc3, 0xc5, 0xa8, 0x93, 0xb1, 0x00, 0xc9,
+ 0x07, 0xb2, 0x7d,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 7 (0x7)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., CN=Go Daddy Root Certificate Authority - G2
+ Validity
+ Not Before: May 3 07:00:00 2011 GMT
+ Not After : May 3 07:00:00 2031 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., OU=http://certs.godaddy.com/repository/, CN=Go Daddy Secure Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b9:e0:cb:10:d4:af:76:bd:d4:93:62:eb:30:64:
+ b8:81:08:6c:c3:04:d9:62:17:8e:2f:ff:3e:65:cf:
+ 8f:ce:62:e6:3c:52:1c:da:16:45:4b:55:ab:78:6b:
+ 63:83:62:90:ce:0f:69:6c:99:c8:1a:14:8b:4c:cc:
+ 45:33:ea:88:dc:9e:a3:af:2b:fe:80:61:9d:79:57:
+ c4:cf:2e:f4:3f:30:3c:5d:47:fc:9a:16:bc:c3:37:
+ 96:41:51:8e:11:4b:54:f8:28:be:d0:8c:be:f0:30:
+ 38:1e:f3:b0:26:f8:66:47:63:6d:de:71:26:47:8f:
+ 38:47:53:d1:46:1d:b4:e3:dc:00:ea:45:ac:bd:bc:
+ 71:d9:aa:6f:00:db:db:cd:30:3a:79:4f:5f:4c:47:
+ f8:1d:ef:5b:c2:c4:9d:60:3b:b1:b2:43:91:d8:a4:
+ 33:4e:ea:b3:d6:27:4f:ad:25:8a:a5:c6:f4:d5:d0:
+ a6:ae:74:05:64:57:88:b5:44:55:d4:2d:2a:3a:3e:
+ f8:b8:bd:e9:32:0a:02:94:64:c4:16:3a:50:f1:4a:
+ ae:e7:79:33:af:0c:20:07:7f:e8:df:04:39:c2:69:
+ 02:6c:63:52:fa:77:c1:1b:c8:74:87:c8:b9:93:18:
+ 50:54:35:4b:69:4e:bc:3b:d3:49:2e:1f:dc:c1:d2:
+ 52:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 40:C2:BD:27:8E:CC:34:83:30:A2:33:D7:FB:6C:B3:F0:B4:2C:80:CE
+ X509v3 Authority Key Identifier:
+ keyid:3A:9A:85:07:10:67:28:B6:EF:F6:BD:05:41:6E:20:C1:94:DA:0F:DE
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.godaddy.com/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.godaddy.com/gdroot-g2.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://certs.godaddy.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 08:7e:6c:93:10:c8:38:b8:96:a9:90:4b:ff:a1:5f:4f:04:ef:
+ 6c:3e:9c:88:06:c9:50:8f:a6:73:f7:57:31:1b:be:bc:e4:2f:
+ db:f8:ba:d3:5b:e0:b4:e7:e6:79:62:0e:0c:a2:d7:6a:63:73:
+ 31:b5:f5:a8:48:a4:3b:08:2d:a2:5d:90:d7:b4:7c:25:4f:11:
+ 56:30:c4:b6:44:9d:7b:2c:9d:e5:5e:e6:ef:0c:61:aa:bf:e4:
+ 2a:1b:ee:84:9e:b8:83:7d:c1:43:ce:44:a7:13:70:0d:91:1f:
+ f4:c8:13:ad:83:60:d9:d8:72:a8:73:24:1e:b5:ac:22:0e:ca:
+ 17:89:62:58:44:1b:ab:89:25:01:00:0f:cd:c4:1b:62:db:51:
+ b4:d3:0f:51:2a:9b:f4:bc:73:fc:76:ce:36:a4:cd:d9:d8:2c:
+ ea:ae:9b:f5:2a:b2:90:d1:4d:75:18:8a:3f:8a:41:90:23:7d:
+ 5b:4b:fe:a4:03:58:9b:46:b2:c3:60:60:83:f8:7d:50:41:ce:
+ c2:a1:90:c3:bb:ef:02:2f:d2:15:54:ee:44:15:d9:0a:ae:a7:
+ 8a:33:ed:b1:2d:76:36:26:dc:04:eb:9f:f7:61:1f:15:dc:87:
+ 6f:ee:46:96:28:ad:a1:26:7d:0a:09:a7:2e:04:a3:8d:bc:f8:
+ bc:04:30:01
+-----BEGIN CERTIFICATE-----
+MIIE0DCCA7igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
+EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
+ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3
+MDAwMFowgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
+EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UE
+CxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQD
+EypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzD
+BNliF44v/z5lz4/OYuY8UhzaFkVLVat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOv
+K/6AYZ15V8TPLvQ/MDxdR/yaFrzDN5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23e
+cSZHjzhHU9FGHbTj3ADqRay9vHHZqm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HY
+pDNO6rPWJ0+tJYqlxvTV0KaudAVkV4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7n
+eTOvDCAHf+jfBDnCaQJsY1L6d8EbyHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMB
+AAGjggEaMIIBFjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
+HQ4EFgQUQMK9J47MNIMwojPX+2yz8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv
+9r0FQW4gwZTaD94wNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC5nb2RhZGR5LmNvbS8wNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5n
+b2RhZGR5LmNvbS9nZHJvb3QtZzIuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEG
+CCsGAQUFBwIBFiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQAIfmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz
+91cxG7685C/b+LrTW+C05+Z5Yg4MotdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2
+RJ17LJ3lXubvDGGqv+QqG+6EnriDfcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawi
+DsoXiWJYRBuriSUBAA/NxBti21G00w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11
+GIo/ikGQI31bS/6kA1ibRrLDYGCD+H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2x
+LXY2JtwE65/3YR8V3Idv7kaWKK2hJn0KCacuBKONvPi8BDAB
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert36[] = {
+ 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x03, 0xb8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x83, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72,
+ 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61,
+ 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64,
+ 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, 0xb4, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a,
+ 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65,
+ 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47,
+ 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65,
+ 0x72, 0x74, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x2f, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x2a, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0xe0, 0xcb, 0x10, 0xd4, 0xaf, 0x76,
+ 0xbd, 0xd4, 0x93, 0x62, 0xeb, 0x30, 0x64, 0xb8, 0x81, 0x08, 0x6c, 0xc3,
+ 0x04, 0xd9, 0x62, 0x17, 0x8e, 0x2f, 0xff, 0x3e, 0x65, 0xcf, 0x8f, 0xce,
+ 0x62, 0xe6, 0x3c, 0x52, 0x1c, 0xda, 0x16, 0x45, 0x4b, 0x55, 0xab, 0x78,
+ 0x6b, 0x63, 0x83, 0x62, 0x90, 0xce, 0x0f, 0x69, 0x6c, 0x99, 0xc8, 0x1a,
+ 0x14, 0x8b, 0x4c, 0xcc, 0x45, 0x33, 0xea, 0x88, 0xdc, 0x9e, 0xa3, 0xaf,
+ 0x2b, 0xfe, 0x80, 0x61, 0x9d, 0x79, 0x57, 0xc4, 0xcf, 0x2e, 0xf4, 0x3f,
+ 0x30, 0x3c, 0x5d, 0x47, 0xfc, 0x9a, 0x16, 0xbc, 0xc3, 0x37, 0x96, 0x41,
+ 0x51, 0x8e, 0x11, 0x4b, 0x54, 0xf8, 0x28, 0xbe, 0xd0, 0x8c, 0xbe, 0xf0,
+ 0x30, 0x38, 0x1e, 0xf3, 0xb0, 0x26, 0xf8, 0x66, 0x47, 0x63, 0x6d, 0xde,
+ 0x71, 0x26, 0x47, 0x8f, 0x38, 0x47, 0x53, 0xd1, 0x46, 0x1d, 0xb4, 0xe3,
+ 0xdc, 0x00, 0xea, 0x45, 0xac, 0xbd, 0xbc, 0x71, 0xd9, 0xaa, 0x6f, 0x00,
+ 0xdb, 0xdb, 0xcd, 0x30, 0x3a, 0x79, 0x4f, 0x5f, 0x4c, 0x47, 0xf8, 0x1d,
+ 0xef, 0x5b, 0xc2, 0xc4, 0x9d, 0x60, 0x3b, 0xb1, 0xb2, 0x43, 0x91, 0xd8,
+ 0xa4, 0x33, 0x4e, 0xea, 0xb3, 0xd6, 0x27, 0x4f, 0xad, 0x25, 0x8a, 0xa5,
+ 0xc6, 0xf4, 0xd5, 0xd0, 0xa6, 0xae, 0x74, 0x05, 0x64, 0x57, 0x88, 0xb5,
+ 0x44, 0x55, 0xd4, 0x2d, 0x2a, 0x3a, 0x3e, 0xf8, 0xb8, 0xbd, 0xe9, 0x32,
+ 0x0a, 0x02, 0x94, 0x64, 0xc4, 0x16, 0x3a, 0x50, 0xf1, 0x4a, 0xae, 0xe7,
+ 0x79, 0x33, 0xaf, 0x0c, 0x20, 0x07, 0x7f, 0xe8, 0xdf, 0x04, 0x39, 0xc2,
+ 0x69, 0x02, 0x6c, 0x63, 0x52, 0xfa, 0x77, 0xc1, 0x1b, 0xc8, 0x74, 0x87,
+ 0xc8, 0xb9, 0x93, 0x18, 0x50, 0x54, 0x35, 0x4b, 0x69, 0x4e, 0xbc, 0x3b,
+ 0xd3, 0x49, 0x2e, 0x1f, 0xdc, 0xc1, 0xd2, 0x52, 0xfb, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1a, 0x30, 0x82, 0x01, 0x16, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x40, 0xc2, 0xbd, 0x27, 0x8e, 0xcc,
+ 0x34, 0x83, 0x30, 0xa2, 0x33, 0xd7, 0xfb, 0x6c, 0xb3, 0xf0, 0xb4, 0x2c,
+ 0x80, 0xce, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0x3a, 0x9a, 0x85, 0x07, 0x10, 0x67, 0x28, 0xb6, 0xef,
+ 0xf6, 0xbd, 0x05, 0x41, 0x6e, 0x20, 0xc1, 0x94, 0xda, 0x0f, 0xde, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67,
+ 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67,
+ 0x64, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30,
+ 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73,
+ 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x08, 0x7e, 0x6c, 0x93,
+ 0x10, 0xc8, 0x38, 0xb8, 0x96, 0xa9, 0x90, 0x4b, 0xff, 0xa1, 0x5f, 0x4f,
+ 0x04, 0xef, 0x6c, 0x3e, 0x9c, 0x88, 0x06, 0xc9, 0x50, 0x8f, 0xa6, 0x73,
+ 0xf7, 0x57, 0x31, 0x1b, 0xbe, 0xbc, 0xe4, 0x2f, 0xdb, 0xf8, 0xba, 0xd3,
+ 0x5b, 0xe0, 0xb4, 0xe7, 0xe6, 0x79, 0x62, 0x0e, 0x0c, 0xa2, 0xd7, 0x6a,
+ 0x63, 0x73, 0x31, 0xb5, 0xf5, 0xa8, 0x48, 0xa4, 0x3b, 0x08, 0x2d, 0xa2,
+ 0x5d, 0x90, 0xd7, 0xb4, 0x7c, 0x25, 0x4f, 0x11, 0x56, 0x30, 0xc4, 0xb6,
+ 0x44, 0x9d, 0x7b, 0x2c, 0x9d, 0xe5, 0x5e, 0xe6, 0xef, 0x0c, 0x61, 0xaa,
+ 0xbf, 0xe4, 0x2a, 0x1b, 0xee, 0x84, 0x9e, 0xb8, 0x83, 0x7d, 0xc1, 0x43,
+ 0xce, 0x44, 0xa7, 0x13, 0x70, 0x0d, 0x91, 0x1f, 0xf4, 0xc8, 0x13, 0xad,
+ 0x83, 0x60, 0xd9, 0xd8, 0x72, 0xa8, 0x73, 0x24, 0x1e, 0xb5, 0xac, 0x22,
+ 0x0e, 0xca, 0x17, 0x89, 0x62, 0x58, 0x44, 0x1b, 0xab, 0x89, 0x25, 0x01,
+ 0x00, 0x0f, 0xcd, 0xc4, 0x1b, 0x62, 0xdb, 0x51, 0xb4, 0xd3, 0x0f, 0x51,
+ 0x2a, 0x9b, 0xf4, 0xbc, 0x73, 0xfc, 0x76, 0xce, 0x36, 0xa4, 0xcd, 0xd9,
+ 0xd8, 0x2c, 0xea, 0xae, 0x9b, 0xf5, 0x2a, 0xb2, 0x90, 0xd1, 0x4d, 0x75,
+ 0x18, 0x8a, 0x3f, 0x8a, 0x41, 0x90, 0x23, 0x7d, 0x5b, 0x4b, 0xfe, 0xa4,
+ 0x03, 0x58, 0x9b, 0x46, 0xb2, 0xc3, 0x60, 0x60, 0x83, 0xf8, 0x7d, 0x50,
+ 0x41, 0xce, 0xc2, 0xa1, 0x90, 0xc3, 0xbb, 0xef, 0x02, 0x2f, 0xd2, 0x15,
+ 0x54, 0xee, 0x44, 0x15, 0xd9, 0x0a, 0xae, 0xa7, 0x8a, 0x33, 0xed, 0xb1,
+ 0x2d, 0x76, 0x36, 0x26, 0xdc, 0x04, 0xeb, 0x9f, 0xf7, 0x61, 0x1f, 0x15,
+ 0xdc, 0x87, 0x6f, 0xee, 0x46, 0x96, 0x28, 0xad, 0xa1, 0x26, 0x7d, 0x0a,
+ 0x09, 0xa7, 0x2e, 0x04, 0xa3, 0x8d, 0xbc, 0xf8, 0xbc, 0x04, 0x30, 0x01,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 25:0c:e8:e0:30:61:2e:9f:2b:89:f7:05:4d:7c:f8:fd
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ Signature Algorithm: sha1WithRSAEncryption
+ 13:02:dd:f8:e8:86:00:f2:5a:f8:f8:20:0c:59:88:62:07:ce:
+ ce:f7:4e:f9:bb:59:a1:98:e5:e1:38:dd:4e:bc:66:18:d3:ad:
+ eb:18:f2:0d:c9:6d:3e:4a:94:20:c3:3c:ba:bd:65:54:c6:af:
+ 44:b3:10:ad:2c:6b:3e:ab:d7:07:b6:b8:81:63:c5:f9:5e:2e:
+ e5:2a:67:ce:cd:33:0c:2a:d7:89:56:03:23:1f:b3:be:e8:3a:
+ 08:59:b4:ec:45:35:f7:8a:5b:ff:66:cf:50:af:c6:6d:57:8d:
+ 19:78:b7:b9:a2:d1:57:ea:1f:9a:4b:af:ba:c9:8e:12:7e:c6:
+ bd:ff
+-----BEGIN CERTIFICATE-----
+MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggGbMIIBlzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
+BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
+aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
+KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
+j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
+L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC52ZXJpc2lnbi5jb20wPgYDVR0lBDcwNQYIKwYBBQUHAwEGCCsGAQUFBwMC
+BggrBgEFBQcDAwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUA
+A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K
+lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ
+tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert37[] = {
+ 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x04, 0x39, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x25, 0x0c, 0xe8, 0xe0, 0x30, 0x61, 0x2e, 0x9f, 0x2b,
+ 0x89, 0xf7, 0x05, 0x4d, 0x7c, 0xf8, 0xfd, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x9b, 0x30, 0x82, 0x01, 0x97, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f,
+ 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09,
+ 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30,
+ 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
+ 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80,
+ 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d, 0x25,
+ 0x04, 0x37, 0x30, 0x35, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60,
+ 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x81, 0x81, 0x00, 0x13, 0x02, 0xdd, 0xf8, 0xe8, 0x86, 0x00, 0xf2,
+ 0x5a, 0xf8, 0xf8, 0x20, 0x0c, 0x59, 0x88, 0x62, 0x07, 0xce, 0xce, 0xf7,
+ 0x4e, 0xf9, 0xbb, 0x59, 0xa1, 0x98, 0xe5, 0xe1, 0x38, 0xdd, 0x4e, 0xbc,
+ 0x66, 0x18, 0xd3, 0xad, 0xeb, 0x18, 0xf2, 0x0d, 0xc9, 0x6d, 0x3e, 0x4a,
+ 0x94, 0x20, 0xc3, 0x3c, 0xba, 0xbd, 0x65, 0x54, 0xc6, 0xaf, 0x44, 0xb3,
+ 0x10, 0xad, 0x2c, 0x6b, 0x3e, 0xab, 0xd7, 0x07, 0xb6, 0xb8, 0x81, 0x63,
+ 0xc5, 0xf9, 0x5e, 0x2e, 0xe5, 0x2a, 0x67, 0xce, 0xcd, 0x33, 0x0c, 0x2a,
+ 0xd7, 0x89, 0x56, 0x03, 0x23, 0x1f, 0xb3, 0xbe, 0xe8, 0x3a, 0x08, 0x59,
+ 0xb4, 0xec, 0x45, 0x35, 0xf7, 0x8a, 0x5b, 0xff, 0x66, 0xcf, 0x50, 0xaf,
+ 0xc6, 0x6d, 0x57, 0x8d, 0x19, 0x78, 0xb7, 0xb9, 0xa2, 0xd1, 0x57, 0xea,
+ 0x1f, 0x9a, 0x4b, 0xaf, 0xba, 0xc9, 0x8e, 0x12, 0x7e, 0xc6, 0xbd, 0xff,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 2c:69:e1:2f:6a:67:0b:d9:9d:d2:0f:91:9e:f0:9e:51
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Jun 10 00:00:00 2014 GMT
+ Not After : Jun 9 23:59:59 2024 GMT
+ Subject: C=US, O=thawte, Inc., OU=Domain Validated SSL, CN=thawte DV SSL CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ea:94:07:85:c8:41:2c:f6:83:12:6c:92:5f:ab:
+ 1f:00:d4:96:6f:74:cd:2e:11:e9:6c:0f:39:01:b9:
+ 48:90:40:39:4d:c4:a2:c8:79:6a:a5:9a:bd:91:44:
+ 65:77:54:ad:ff:25:5f:ee:42:fb:b3:02:0f:ea:5d:
+ 7a:dd:1a:54:9e:d7:73:42:9b:cc:79:5f:c5:4d:f4:
+ b7:0b:18:39:20:7a:dd:50:01:5d:34:45:5f:4c:11:
+ 0e:f5:87:26:26:b4:b0:f3:7e:71:a0:31:71:50:89:
+ 68:5a:63:8a:14:62:e5:8c:3a:16:55:0d:3e:eb:aa:
+ 80:1d:71:7a:e3:87:07:ab:bd:a2:74:cd:da:08:01:
+ 9d:1b:cc:27:88:8c:47:d4:69:25:42:d6:bb:50:6d:
+ 85:50:d0:48:82:0d:08:9f:e9:23:e3:42:c6:3c:98:
+ b8:bb:6e:c5:70:13:df:19:1d:01:fd:d2:b5:4e:e6:
+ 62:f4:07:fa:6b:7d:11:77:c4:62:4f:40:4e:a5:78:
+ 97:ab:2c:4d:0c:a7:7c:c3:c4:50:32:9f:d0:70:9b:
+ 0f:ff:ff:75:59:34:85:ad:49:d5:35:ee:4f:5b:d4:
+ d4:36:95:a0:7e:e8:c5:a1:1c:bd:13:4e:7d:ee:63:
+ 6a:96:19:99:c8:a7:2a:00:e6:51:8d:46:eb:30:58:
+ e8:2d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: https://www.thawte.com/cps
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://t.symcd.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://t.symcb.com/ThawtePCA.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-698
+ X509v3 Subject Key Identifier:
+ 9F:B8:C1:A9:6C:F2:F5:C0:22:2A:94:ED:5C:99:AC:D4:EC:D7:C6:07
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 53:54:f2:47:a8:02:d7:ef:aa:35:78:be:4a:08:0d:90:18:4b:
+ 6d:9e:2a:53:2b:e9:54:17:77:74:29:7e:d0:37:07:05:b8:e4:
+ fa:b8:b4:63:98:44:dc:c6:4f:81:06:8c:3a:be:c7:30:57:c6:
+ 70:fc:d6:93:19:9f:c3:55:d7:3e:1f:72:8a:9d:30:5a:35:97:
+ 32:cb:63:e4:c6:72:df:fb:68:ca:69:2f:db:cd:50:38:3e:2b:
+ bb:ab:3b:82:c7:fd:4b:9b:bd:7c:41:98:ef:01:53:d8:35:8f:
+ 25:c9:03:06:e6:9c:57:c1:51:0f:9e:f6:7d:93:4d:f8:76:c8:
+ 3a:6b:f4:c4:8f:33:32:7f:9d:21:84:34:d9:a7:f9:92:fa:41:
+ 91:61:84:05:9d:a3:79:46:ce:67:e7:81:f2:5e:ac:4c:bc:a8:
+ ab:6a:6d:15:e2:9c:4e:5a:d9:63:80:bc:f7:42:eb:9a:44:c6:
+ 8c:6b:06:36:b4:8b:32:89:de:c2:f1:a8:26:aa:a9:ac:ff:ea:
+ 71:a6:e7:8c:41:fa:17:35:bb:b3:87:31:a9:93:c2:c8:58:e1:
+ 0a:4e:95:83:9c:b9:ed:3b:a5:ef:08:e0:74:f9:c3:1b:e6:07:
+ a3:ee:07:d7:42:22:79:21:a0:a1:d4:1d:26:d3:d0:d6:a6:5d:
+ 2b:41:c0:79
+-----BEGIN CERTIFICATE-----
+MIIE0jCCA7qgAwIBAgIQLGnhL2pnC9md0g+RnvCeUTANBgkqhkiG9w0BAQsFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTQwNjEwMDAwMDAwWhcNMjQw
+NjA5MjM1OTU5WjBjMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMu
+MR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEeMBwGA1UEAxMVdGhhd3Rl
+IERWIFNTTCBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+6pQHhchBLPaDEmySX6sfANSWb3TNLhHpbA85AblIkEA5TcSiyHlqpZq9kURld1St
+/yVf7kL7swIP6l163RpUntdzQpvMeV/FTfS3Cxg5IHrdUAFdNEVfTBEO9YcmJrSw
+835xoDFxUIloWmOKFGLljDoWVQ0+66qAHXF644cHq72idM3aCAGdG8wniIxH1Gkl
+Qta7UG2FUNBIgg0In+kj40LGPJi4u27FcBPfGR0B/dK1TuZi9Af6a30Rd8RiT0BO
+pXiXqyxNDKd8w8RQMp/QcJsP//91WTSFrUnVNe5PW9TUNpWgfujFoRy9E0597mNq
+lhmZyKcqAOZRjUbrMFjoLQIDAQABo4IBOTCCATUwEgYDVR0TAQH/BAgwBgEB/wIB
+ADBBBgNVHSAEOjA4MDYGCmCGSAGG+EUBBzYwKDAmBggrBgEFBQcCARYaaHR0cHM6
+Ly93d3cudGhhd3RlLmNvbS9jcHMwDgYDVR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEB
+BCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL3Quc3ltY2QuY29tMDEGA1UdHwQqMCgw
+JqAkoCKGIGh0dHA6Ly90LnN5bWNiLmNvbS9UaGF3dGVQQ0EuY3JsMCkGA1UdEQQi
+MCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTY5ODAdBgNVHQ4EFgQUn7jB
+qWzy9cAiKpTtXJms1OzXxgcwHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutX
+SFAwDQYJKoZIhvcNAQELBQADggEBAFNU8keoAtfvqjV4vkoIDZAYS22eKlMr6VQX
+d3QpftA3BwW45Pq4tGOYRNzGT4EGjDq+xzBXxnD81pMZn8NV1z4fcoqdMFo1lzLL
+Y+TGct/7aMppL9vNUDg+K7urO4LH/UubvXxBmO8BU9g1jyXJAwbmnFfBUQ+e9n2T
+Tfh2yDpr9MSPMzJ/nSGENNmn+ZL6QZFhhAWdo3lGzmfngfJerEy8qKtqbRXinE5a
+2WOAvPdC65pExoxrBja0izKJ3sLxqCaqqaz/6nGm54xB+hc1u7OHMamTwshY4QpO
+lYOcue07pe8I4HT5wxvmB6PuB9dCInkhoKHUHSbT0NamXStBwHk=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert38[] = {
+ 0x30, 0x82, 0x04, 0xd2, 0x30, 0x82, 0x03, 0xba, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x2c, 0x69, 0xe1, 0x2f, 0x6a, 0x67, 0x0b, 0xd9, 0x9d,
+ 0xd2, 0x0f, 0x91, 0x9e, 0xf0, 0x9e, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x36, 0x31, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30,
+ 0x36, 0x30, 0x39, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x63,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x14, 0x44,
+ 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61,
+ 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31, 0x1e, 0x30, 0x1c, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x15, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x20, 0x44, 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x2d,
+ 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xea, 0x94, 0x07, 0x85, 0xc8, 0x41, 0x2c, 0xf6, 0x83, 0x12, 0x6c, 0x92,
+ 0x5f, 0xab, 0x1f, 0x00, 0xd4, 0x96, 0x6f, 0x74, 0xcd, 0x2e, 0x11, 0xe9,
+ 0x6c, 0x0f, 0x39, 0x01, 0xb9, 0x48, 0x90, 0x40, 0x39, 0x4d, 0xc4, 0xa2,
+ 0xc8, 0x79, 0x6a, 0xa5, 0x9a, 0xbd, 0x91, 0x44, 0x65, 0x77, 0x54, 0xad,
+ 0xff, 0x25, 0x5f, 0xee, 0x42, 0xfb, 0xb3, 0x02, 0x0f, 0xea, 0x5d, 0x7a,
+ 0xdd, 0x1a, 0x54, 0x9e, 0xd7, 0x73, 0x42, 0x9b, 0xcc, 0x79, 0x5f, 0xc5,
+ 0x4d, 0xf4, 0xb7, 0x0b, 0x18, 0x39, 0x20, 0x7a, 0xdd, 0x50, 0x01, 0x5d,
+ 0x34, 0x45, 0x5f, 0x4c, 0x11, 0x0e, 0xf5, 0x87, 0x26, 0x26, 0xb4, 0xb0,
+ 0xf3, 0x7e, 0x71, 0xa0, 0x31, 0x71, 0x50, 0x89, 0x68, 0x5a, 0x63, 0x8a,
+ 0x14, 0x62, 0xe5, 0x8c, 0x3a, 0x16, 0x55, 0x0d, 0x3e, 0xeb, 0xaa, 0x80,
+ 0x1d, 0x71, 0x7a, 0xe3, 0x87, 0x07, 0xab, 0xbd, 0xa2, 0x74, 0xcd, 0xda,
+ 0x08, 0x01, 0x9d, 0x1b, 0xcc, 0x27, 0x88, 0x8c, 0x47, 0xd4, 0x69, 0x25,
+ 0x42, 0xd6, 0xbb, 0x50, 0x6d, 0x85, 0x50, 0xd0, 0x48, 0x82, 0x0d, 0x08,
+ 0x9f, 0xe9, 0x23, 0xe3, 0x42, 0xc6, 0x3c, 0x98, 0xb8, 0xbb, 0x6e, 0xc5,
+ 0x70, 0x13, 0xdf, 0x19, 0x1d, 0x01, 0xfd, 0xd2, 0xb5, 0x4e, 0xe6, 0x62,
+ 0xf4, 0x07, 0xfa, 0x6b, 0x7d, 0x11, 0x77, 0xc4, 0x62, 0x4f, 0x40, 0x4e,
+ 0xa5, 0x78, 0x97, 0xab, 0x2c, 0x4d, 0x0c, 0xa7, 0x7c, 0xc3, 0xc4, 0x50,
+ 0x32, 0x9f, 0xd0, 0x70, 0x9b, 0x0f, 0xff, 0xff, 0x75, 0x59, 0x34, 0x85,
+ 0xad, 0x49, 0xd5, 0x35, 0xee, 0x4f, 0x5b, 0xd4, 0xd4, 0x36, 0x95, 0xa0,
+ 0x7e, 0xe8, 0xc5, 0xa1, 0x1c, 0xbd, 0x13, 0x4e, 0x7d, 0xee, 0x63, 0x6a,
+ 0x96, 0x19, 0x99, 0xc8, 0xa7, 0x2a, 0x00, 0xe6, 0x51, 0x8d, 0x46, 0xeb,
+ 0x30, 0x58, 0xe8, 0x2d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0x39, 0x30, 0x82, 0x01, 0x35, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3a, 0x30, 0x38,
+ 0x30, 0x36, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01,
+ 0x07, 0x36, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x74, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, 0x30, 0x28, 0x30,
+ 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x74, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22,
+ 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65,
+ 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d, 0x36, 0x39, 0x38, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9f, 0xb8, 0xc1,
+ 0xa9, 0x6c, 0xf2, 0xf5, 0xc0, 0x22, 0x2a, 0x94, 0xed, 0x5c, 0x99, 0xac,
+ 0xd4, 0xec, 0xd7, 0xc6, 0x07, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce,
+ 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57,
+ 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x53, 0x54,
+ 0xf2, 0x47, 0xa8, 0x02, 0xd7, 0xef, 0xaa, 0x35, 0x78, 0xbe, 0x4a, 0x08,
+ 0x0d, 0x90, 0x18, 0x4b, 0x6d, 0x9e, 0x2a, 0x53, 0x2b, 0xe9, 0x54, 0x17,
+ 0x77, 0x74, 0x29, 0x7e, 0xd0, 0x37, 0x07, 0x05, 0xb8, 0xe4, 0xfa, 0xb8,
+ 0xb4, 0x63, 0x98, 0x44, 0xdc, 0xc6, 0x4f, 0x81, 0x06, 0x8c, 0x3a, 0xbe,
+ 0xc7, 0x30, 0x57, 0xc6, 0x70, 0xfc, 0xd6, 0x93, 0x19, 0x9f, 0xc3, 0x55,
+ 0xd7, 0x3e, 0x1f, 0x72, 0x8a, 0x9d, 0x30, 0x5a, 0x35, 0x97, 0x32, 0xcb,
+ 0x63, 0xe4, 0xc6, 0x72, 0xdf, 0xfb, 0x68, 0xca, 0x69, 0x2f, 0xdb, 0xcd,
+ 0x50, 0x38, 0x3e, 0x2b, 0xbb, 0xab, 0x3b, 0x82, 0xc7, 0xfd, 0x4b, 0x9b,
+ 0xbd, 0x7c, 0x41, 0x98, 0xef, 0x01, 0x53, 0xd8, 0x35, 0x8f, 0x25, 0xc9,
+ 0x03, 0x06, 0xe6, 0x9c, 0x57, 0xc1, 0x51, 0x0f, 0x9e, 0xf6, 0x7d, 0x93,
+ 0x4d, 0xf8, 0x76, 0xc8, 0x3a, 0x6b, 0xf4, 0xc4, 0x8f, 0x33, 0x32, 0x7f,
+ 0x9d, 0x21, 0x84, 0x34, 0xd9, 0xa7, 0xf9, 0x92, 0xfa, 0x41, 0x91, 0x61,
+ 0x84, 0x05, 0x9d, 0xa3, 0x79, 0x46, 0xce, 0x67, 0xe7, 0x81, 0xf2, 0x5e,
+ 0xac, 0x4c, 0xbc, 0xa8, 0xab, 0x6a, 0x6d, 0x15, 0xe2, 0x9c, 0x4e, 0x5a,
+ 0xd9, 0x63, 0x80, 0xbc, 0xf7, 0x42, 0xeb, 0x9a, 0x44, 0xc6, 0x8c, 0x6b,
+ 0x06, 0x36, 0xb4, 0x8b, 0x32, 0x89, 0xde, 0xc2, 0xf1, 0xa8, 0x26, 0xaa,
+ 0xa9, 0xac, 0xff, 0xea, 0x71, 0xa6, 0xe7, 0x8c, 0x41, 0xfa, 0x17, 0x35,
+ 0xbb, 0xb3, 0x87, 0x31, 0xa9, 0x93, 0xc2, 0xc8, 0x58, 0xe1, 0x0a, 0x4e,
+ 0x95, 0x83, 0x9c, 0xb9, 0xed, 0x3b, 0xa5, 0xef, 0x08, 0xe0, 0x74, 0xf9,
+ 0xc3, 0x1b, 0xe6, 0x07, 0xa3, 0xee, 0x07, 0xd7, 0x42, 0x22, 0x79, 0x21,
+ 0xa0, 0xa1, 0xd4, 0x1d, 0x26, 0xd3, 0xd0, 0xd6, 0xa6, 0x5d, 0x2b, 0x41,
+ 0xc0, 0x79,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1372799044 (0x51d34044)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority
+ Validity
+ Not Before: Sep 22 17:14:57 2014 GMT
+ Not After : Sep 23 01:31:53 2024 GMT
+ Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ba:84:b6:72:db:9e:0c:6b:e2:99:e9:30:01:a7:
+ 76:ea:32:b8:95:41:1a:c9:da:61:4e:58:72:cf:fe:
+ f6:82:79:bf:73:61:06:0a:a5:27:d8:b3:5f:d3:45:
+ 4e:1c:72:d6:4e:32:f2:72:8a:0f:f7:83:19:d0:6a:
+ 80:80:00:45:1e:b0:c7:e7:9a:bf:12:57:27:1c:a3:
+ 68:2f:0a:87:bd:6a:6b:0e:5e:65:f3:1c:77:d5:d4:
+ 85:8d:70:21:b4:b3:32:e7:8b:a2:d5:86:39:02:b1:
+ b8:d2:47:ce:e4:c9:49:c4:3b:a7:de:fb:54:7d:57:
+ be:f0:e8:6e:c2:79:b2:3a:0b:55:e2:50:98:16:32:
+ 13:5c:2f:78:56:c1:c2:94:b3:f2:5a:e4:27:9a:9f:
+ 24:d7:c6:ec:d0:9b:25:82:e3:cc:c2:c4:45:c5:8c:
+ 97:7a:06:6b:2a:11:9f:a9:0a:6e:48:3b:6f:db:d4:
+ 11:19:42:f7:8f:07:bf:f5:53:5f:9c:3e:f4:17:2c:
+ e6:69:ac:4e:32:4c:62:77:ea:b7:e8:e5:bb:34:bc:
+ 19:8b:ae:9c:51:e7:b7:7e:b5:53:b1:33:22:e5:6d:
+ cf:70:3c:1a:fa:e2:9b:67:b6:83:f4:8d:a5:af:62:
+ 4c:4d:e0:58:ac:64:34:12:03:f8:b6:8d:94:63:24:
+ a4:71
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/rootca1.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/CPS
+
+ X509v3 Subject Key Identifier:
+ 6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB
+ X509v3 Authority Key Identifier:
+ keyid:68:90:E4:67:A4:A6:53:80:C7:86:66:A4:F1:F7:4B:43:FB:84:BD:6D
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 69:33:83:fc:28:7a:6f:7d:ef:9d:55:eb:c5:3e:7a:9d:75:b3:
+ cc:c3:38:36:d9:34:a2:28:68:18:ea:1e:69:d3:bd:e7:d0:77:
+ da:b8:00:83:4e:4a:cf:6f:d1:f1:c1:22:3f:74:e4:f7:98:49:
+ 9e:9b:b6:9e:e1:db:98:77:2d:56:34:b1:a8:3c:d9:fd:c0:cd:
+ c7:bf:05:03:d4:02:c5:f1:e5:c6:da:08:a5:13:c7:62:23:11:
+ d1:61:30:1d:60:84:45:ef:79:a8:c6:26:93:a4:b7:cd:34:b8:
+ 69:c5:13:f6:91:b3:c9:45:73:76:b6:92:f6:76:0a:5b:e1:03:
+ 47:b7:e9:29:4c:91:32:23:37:4a:9c:35:d8:78:fd:1d:1f:e4:
+ 83:89:24:80:ad:b7:f9:cf:e4:5d:a5:d4:71:c4:85:5b:70:1f:
+ db:3f:1c:01:eb:1a:45:26:31:14:cc:65:bf:67:de:ca:cc:33:
+ 65:e5:41:91:d7:37:be:41:1a:96:9d:e6:8a:97:9d:a7:ce:ac:
+ 4e:9a:3d:bd:01:a0:6a:d9:4f:22:00:8b:44:d5:69:62:7b:2e:
+ eb:cc:ba:e7:92:7d:69:67:3d:fc:b8:7c:de:41:87:d0:69:ea:
+ ba:0a:18:7a:1a:95:43:b3:79:71:28:76:6d:a1:fb:57:4a:ec:
+ 4d:c8:0e:10
+-----BEGIN CERTIFICATE-----
+MIIE/zCCA+egAwIBAgIEUdNARDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
+Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
+KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDkyMjE3MTQ1N1oXDTI0MDkyMzAx
+MzE1M1owgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgw
+JgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQL
+EzAoYykgMjAwOSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9u
+bHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuoS2ctueDGvi
+mekwAad26jK4lUEaydphTlhyz/72gnm/c2EGCqUn2LNf00VOHHLWTjLycooP94MZ
+0GqAgABFHrDH55q/ElcnHKNoLwqHvWprDl5l8xx31dSFjXAhtLMy54ui1YY5ArG4
+0kfO5MlJxDun3vtUfVe+8OhuwnmyOgtV4lCYFjITXC94VsHClLPyWuQnmp8k18bs
+0JslguPMwsRFxYyXegZrKhGfqQpuSDtv29QRGUL3jwe/9VNfnD70FyzmaaxOMkxi
+d+q36OW7NLwZi66cUee3frVTsTMi5W3PcDwa+uKbZ7aD9I2lr2JMTeBYrGQ0EgP4
+to2UYySkcQIDAQABo4IBDzCCAQswDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
+MAYBAf8CAQEwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz
+cC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmVudHJ1
+c3QubmV0L3Jvb3RjYTEuY3JsMDsGA1UdIAQ0MDIwMAYEVR0gADAoMCYGCCsGAQUF
+BwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NQUzAdBgNVHQ4EFgQUanImetAe
+733nO2lR1GyNn5ASZqswHwYDVR0jBBgwFoAUaJDkZ6SmU4DHhmak8fdLQ/uEvW0w
+DQYJKoZIhvcNAQELBQADggEBAGkzg/woem99751V68U+ep11s8zDODbZNKIoaBjq
+HmnTvefQd9q4AINOSs9v0fHBIj905PeYSZ6btp7h25h3LVY0sag82f3Azce/BQPU
+AsXx5cbaCKUTx2IjEdFhMB1ghEXveajGJpOkt800uGnFE/aRs8lFc3a2kvZ2Clvh
+A0e36SlMkTIjN0qcNdh4/R0f5IOJJICtt/nP5F2l1HHEhVtwH9s/HAHrGkUmMRTM
+Zb9n3srMM2XlQZHXN75BGpad5oqXnafOrE6aPb0BoGrZTyIAi0TVaWJ7LuvMuueS
+fWlnPfy4fN5Bh9Bp6roKGHoalUOzeXEodm2h+1dK7E3IDhA=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert39[] = {
+ 0x30, 0x82, 0x04, 0xff, 0x30, 0x82, 0x03, 0xe7, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x51, 0xd3, 0x40, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xb0, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x30, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x73, 0x20,
+ 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+ 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16,
+ 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d,
+ 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x34, 0x30, 0x39, 0x32, 0x32, 0x31, 0x37, 0x31, 0x34, 0x35,
+ 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x30, 0x39, 0x32, 0x33, 0x30, 0x31,
+ 0x33, 0x31, 0x35, 0x33, 0x5a, 0x30, 0x81, 0xbe, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30,
+ 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x28, 0x30,
+ 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, 0x65, 0x20,
+ 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x74, 0x65,
+ 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, 0x45,
+ 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e,
+ 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f,
+ 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xba, 0x84, 0xb6, 0x72, 0xdb, 0x9e, 0x0c, 0x6b, 0xe2,
+ 0x99, 0xe9, 0x30, 0x01, 0xa7, 0x76, 0xea, 0x32, 0xb8, 0x95, 0x41, 0x1a,
+ 0xc9, 0xda, 0x61, 0x4e, 0x58, 0x72, 0xcf, 0xfe, 0xf6, 0x82, 0x79, 0xbf,
+ 0x73, 0x61, 0x06, 0x0a, 0xa5, 0x27, 0xd8, 0xb3, 0x5f, 0xd3, 0x45, 0x4e,
+ 0x1c, 0x72, 0xd6, 0x4e, 0x32, 0xf2, 0x72, 0x8a, 0x0f, 0xf7, 0x83, 0x19,
+ 0xd0, 0x6a, 0x80, 0x80, 0x00, 0x45, 0x1e, 0xb0, 0xc7, 0xe7, 0x9a, 0xbf,
+ 0x12, 0x57, 0x27, 0x1c, 0xa3, 0x68, 0x2f, 0x0a, 0x87, 0xbd, 0x6a, 0x6b,
+ 0x0e, 0x5e, 0x65, 0xf3, 0x1c, 0x77, 0xd5, 0xd4, 0x85, 0x8d, 0x70, 0x21,
+ 0xb4, 0xb3, 0x32, 0xe7, 0x8b, 0xa2, 0xd5, 0x86, 0x39, 0x02, 0xb1, 0xb8,
+ 0xd2, 0x47, 0xce, 0xe4, 0xc9, 0x49, 0xc4, 0x3b, 0xa7, 0xde, 0xfb, 0x54,
+ 0x7d, 0x57, 0xbe, 0xf0, 0xe8, 0x6e, 0xc2, 0x79, 0xb2, 0x3a, 0x0b, 0x55,
+ 0xe2, 0x50, 0x98, 0x16, 0x32, 0x13, 0x5c, 0x2f, 0x78, 0x56, 0xc1, 0xc2,
+ 0x94, 0xb3, 0xf2, 0x5a, 0xe4, 0x27, 0x9a, 0x9f, 0x24, 0xd7, 0xc6, 0xec,
+ 0xd0, 0x9b, 0x25, 0x82, 0xe3, 0xcc, 0xc2, 0xc4, 0x45, 0xc5, 0x8c, 0x97,
+ 0x7a, 0x06, 0x6b, 0x2a, 0x11, 0x9f, 0xa9, 0x0a, 0x6e, 0x48, 0x3b, 0x6f,
+ 0xdb, 0xd4, 0x11, 0x19, 0x42, 0xf7, 0x8f, 0x07, 0xbf, 0xf5, 0x53, 0x5f,
+ 0x9c, 0x3e, 0xf4, 0x17, 0x2c, 0xe6, 0x69, 0xac, 0x4e, 0x32, 0x4c, 0x62,
+ 0x77, 0xea, 0xb7, 0xe8, 0xe5, 0xbb, 0x34, 0xbc, 0x19, 0x8b, 0xae, 0x9c,
+ 0x51, 0xe7, 0xb7, 0x7e, 0xb5, 0x53, 0xb1, 0x33, 0x22, 0xe5, 0x6d, 0xcf,
+ 0x70, 0x3c, 0x1a, 0xfa, 0xe2, 0x9b, 0x67, 0xb6, 0x83, 0xf4, 0x8d, 0xa5,
+ 0xaf, 0x62, 0x4c, 0x4d, 0xe0, 0x58, 0xac, 0x64, 0x34, 0x12, 0x03, 0xf8,
+ 0xb6, 0x8d, 0x94, 0x63, 0x24, 0xa4, 0x71, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x0f, 0x30, 0x82, 0x01, 0x0b, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x33, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25,
+ 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a,
+ 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x63,
+ 0x61, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0, 0x1e,
+ 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90, 0x12,
+ 0x66, 0xab, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0x68, 0x90, 0xe4, 0x67, 0xa4, 0xa6, 0x53, 0x80, 0xc7,
+ 0x86, 0x66, 0xa4, 0xf1, 0xf7, 0x4b, 0x43, 0xfb, 0x84, 0xbd, 0x6d, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x69, 0x33, 0x83, 0xfc, 0x28,
+ 0x7a, 0x6f, 0x7d, 0xef, 0x9d, 0x55, 0xeb, 0xc5, 0x3e, 0x7a, 0x9d, 0x75,
+ 0xb3, 0xcc, 0xc3, 0x38, 0x36, 0xd9, 0x34, 0xa2, 0x28, 0x68, 0x18, 0xea,
+ 0x1e, 0x69, 0xd3, 0xbd, 0xe7, 0xd0, 0x77, 0xda, 0xb8, 0x00, 0x83, 0x4e,
+ 0x4a, 0xcf, 0x6f, 0xd1, 0xf1, 0xc1, 0x22, 0x3f, 0x74, 0xe4, 0xf7, 0x98,
+ 0x49, 0x9e, 0x9b, 0xb6, 0x9e, 0xe1, 0xdb, 0x98, 0x77, 0x2d, 0x56, 0x34,
+ 0xb1, 0xa8, 0x3c, 0xd9, 0xfd, 0xc0, 0xcd, 0xc7, 0xbf, 0x05, 0x03, 0xd4,
+ 0x02, 0xc5, 0xf1, 0xe5, 0xc6, 0xda, 0x08, 0xa5, 0x13, 0xc7, 0x62, 0x23,
+ 0x11, 0xd1, 0x61, 0x30, 0x1d, 0x60, 0x84, 0x45, 0xef, 0x79, 0xa8, 0xc6,
+ 0x26, 0x93, 0xa4, 0xb7, 0xcd, 0x34, 0xb8, 0x69, 0xc5, 0x13, 0xf6, 0x91,
+ 0xb3, 0xc9, 0x45, 0x73, 0x76, 0xb6, 0x92, 0xf6, 0x76, 0x0a, 0x5b, 0xe1,
+ 0x03, 0x47, 0xb7, 0xe9, 0x29, 0x4c, 0x91, 0x32, 0x23, 0x37, 0x4a, 0x9c,
+ 0x35, 0xd8, 0x78, 0xfd, 0x1d, 0x1f, 0xe4, 0x83, 0x89, 0x24, 0x80, 0xad,
+ 0xb7, 0xf9, 0xcf, 0xe4, 0x5d, 0xa5, 0xd4, 0x71, 0xc4, 0x85, 0x5b, 0x70,
+ 0x1f, 0xdb, 0x3f, 0x1c, 0x01, 0xeb, 0x1a, 0x45, 0x26, 0x31, 0x14, 0xcc,
+ 0x65, 0xbf, 0x67, 0xde, 0xca, 0xcc, 0x33, 0x65, 0xe5, 0x41, 0x91, 0xd7,
+ 0x37, 0xbe, 0x41, 0x1a, 0x96, 0x9d, 0xe6, 0x8a, 0x97, 0x9d, 0xa7, 0xce,
+ 0xac, 0x4e, 0x9a, 0x3d, 0xbd, 0x01, 0xa0, 0x6a, 0xd9, 0x4f, 0x22, 0x00,
+ 0x8b, 0x44, 0xd5, 0x69, 0x62, 0x7b, 0x2e, 0xeb, 0xcc, 0xba, 0xe7, 0x92,
+ 0x7d, 0x69, 0x67, 0x3d, 0xfc, 0xb8, 0x7c, 0xde, 0x41, 0x87, 0xd0, 0x69,
+ 0xea, 0xba, 0x0a, 0x18, 0x7a, 0x1a, 0x95, 0x43, 0xb3, 0x79, 0x71, 0x28,
+ 0x76, 0x6d, 0xa1, 0xfb, 0x57, 0x4a, 0xec, 0x4d, 0xc8, 0x0e, 0x10,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 7 (0x7)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2
+ Validity
+ Not Before: May 3 07:00:00 2011 GMT
+ Not After : May 3 07:00:00 2031 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., OU=http://certs.starfieldtech.com/repository/, CN=Starfield Secure Certificate Authority - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e5:90:66:4b:ec:f9:46:71:a9:20:83:be:e9:6c:
+ bf:4a:c9:48:69:81:75:4e:6d:24:f6:cb:17:13:f8:
+ b0:71:59:84:7a:6b:2b:85:a4:34:b5:16:e5:cb:cc:
+ e9:41:70:2c:a4:2e:d6:fa:32:7d:e1:a8:de:94:10:
+ ac:31:c1:c0:d8:6a:ff:59:27:ab:76:d6:fc:0b:74:
+ 6b:b8:a7:ae:3f:c4:54:f4:b4:31:44:dd:93:56:8c:
+ a4:4c:5e:9b:89:cb:24:83:9b:e2:57:7d:b7:d8:12:
+ 1f:c9:85:6d:f4:d1:80:f1:50:9b:87:ae:d4:0b:10:
+ 05:fb:27:ba:28:6d:17:e9:0e:d6:4d:b9:39:55:06:
+ ff:0a:24:05:7e:2f:c6:1d:72:6c:d4:8b:29:8c:57:
+ 7d:da:d9:eb:66:1a:d3:4f:a7:df:7f:52:c4:30:c5:
+ a5:c9:0e:02:c5:53:bf:77:38:68:06:24:c3:66:c8:
+ 37:7e:30:1e:45:71:23:35:ff:90:d8:2a:9d:8d:e7:
+ b0:92:4d:3c:7f:2a:0a:93:dc:cd:16:46:65:f7:60:
+ 84:8b:76:4b:91:27:73:14:92:e0:ea:ee:8f:16:ea:
+ 8d:0e:3e:76:17:bf:7d:89:80:80:44:43:e7:2d:e0:
+ 43:09:75:da:36:e8:ad:db:89:3a:f5:5d:12:8e:23:
+ 04:83
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 25:45:81:68:50:26:38:3D:3B:2D:2C:BE:CD:6A:D9:B6:3D:B3:66:63
+ X509v3 Authority Key Identifier:
+ keyid:7C:0C:32:1F:A7:D9:30:7F:C4:7D:68:A3:62:A8:A1:CE:AB:07:5B:27
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.starfieldtech.com/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.starfieldtech.com/sfroot-g2.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://certs.starfieldtech.com/repository/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 56:65:ca:fe:f3:3f:0a:a8:93:8b:18:c7:de:43:69:13:34:20:
+ be:4e:5f:78:a8:6b:9c:db:6a:4d:41:db:c1:13:ec:dc:31:00:
+ 22:5e:f7:00:9e:0c:e0:34:65:34:f9:b1:3a:4e:48:c8:12:81:
+ 88:5c:5b:3e:08:53:7a:f7:1a:64:df:b8:50:61:cc:53:51:40:
+ 29:4b:c2:f4:ae:3a:5f:e4:ca:ad:26:cc:4e:61:43:e5:fd:57:
+ a6:37:70:ce:43:2b:b0:94:c3:92:e9:e1:5f:aa:10:49:b7:69:
+ e4:e0:d0:1f:64:a4:2b:cd:1f:6f:a0:f8:84:24:18:ce:79:3d:
+ a9:91:bf:54:18:13:89:99:54:11:0d:55:c5:26:0b:79:4f:5a:
+ 1c:6e:f9:63:db:14:80:a4:07:ab:fa:b2:a5:b9:88:dd:91:fe:
+ 65:3b:a4:a3:79:be:89:4d:e1:d0:b0:f4:c8:17:0c:0a:96:14:
+ 7c:09:b7:6c:e1:c2:d8:55:d4:18:a0:aa:41:69:70:24:a3:b9:
+ ef:e9:5a:dc:3e:eb:94:4a:f0:b7:de:5f:0e:76:fa:fb:fb:69:
+ 03:45:40:50:ee:72:0c:a4:12:86:81:cd:13:d1:4e:c4:3c:ca:
+ 4e:0d:d2:26:f1:00:b7:b4:a6:a2:e1:6e:7a:81:fd:30:ac:7a:
+ 1f:c7:59:7b
+-----BEGIN CERTIFICATE-----
+MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
+ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw
+MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
+aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk
+dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg
+Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF
+pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE
+3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV
+Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+
+MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX
+v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB
+Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+
+zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB
+BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo
+LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo
+LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF
+BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN
+QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0
+rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO
+eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ
+sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ
+7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert40[] = {
+ 0x30, 0x82, 0x05, 0x00, 0x30, 0x82, 0x03, 0xe8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x8f, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72,
+ 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61,
+ 0x6c, 0x65, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54,
+ 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c,
+ 0x64, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x35, 0x30, 0x33, 0x30, 0x37,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x81, 0xc6, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a,
+ 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65,
+ 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63,
+ 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72,
+ 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64,
+ 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70,
+ 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x31, 0x34, 0x30, 0x32,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b, 0x53, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20,
+ 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5,
+ 0x90, 0x66, 0x4b, 0xec, 0xf9, 0x46, 0x71, 0xa9, 0x20, 0x83, 0xbe, 0xe9,
+ 0x6c, 0xbf, 0x4a, 0xc9, 0x48, 0x69, 0x81, 0x75, 0x4e, 0x6d, 0x24, 0xf6,
+ 0xcb, 0x17, 0x13, 0xf8, 0xb0, 0x71, 0x59, 0x84, 0x7a, 0x6b, 0x2b, 0x85,
+ 0xa4, 0x34, 0xb5, 0x16, 0xe5, 0xcb, 0xcc, 0xe9, 0x41, 0x70, 0x2c, 0xa4,
+ 0x2e, 0xd6, 0xfa, 0x32, 0x7d, 0xe1, 0xa8, 0xde, 0x94, 0x10, 0xac, 0x31,
+ 0xc1, 0xc0, 0xd8, 0x6a, 0xff, 0x59, 0x27, 0xab, 0x76, 0xd6, 0xfc, 0x0b,
+ 0x74, 0x6b, 0xb8, 0xa7, 0xae, 0x3f, 0xc4, 0x54, 0xf4, 0xb4, 0x31, 0x44,
+ 0xdd, 0x93, 0x56, 0x8c, 0xa4, 0x4c, 0x5e, 0x9b, 0x89, 0xcb, 0x24, 0x83,
+ 0x9b, 0xe2, 0x57, 0x7d, 0xb7, 0xd8, 0x12, 0x1f, 0xc9, 0x85, 0x6d, 0xf4,
+ 0xd1, 0x80, 0xf1, 0x50, 0x9b, 0x87, 0xae, 0xd4, 0x0b, 0x10, 0x05, 0xfb,
+ 0x27, 0xba, 0x28, 0x6d, 0x17, 0xe9, 0x0e, 0xd6, 0x4d, 0xb9, 0x39, 0x55,
+ 0x06, 0xff, 0x0a, 0x24, 0x05, 0x7e, 0x2f, 0xc6, 0x1d, 0x72, 0x6c, 0xd4,
+ 0x8b, 0x29, 0x8c, 0x57, 0x7d, 0xda, 0xd9, 0xeb, 0x66, 0x1a, 0xd3, 0x4f,
+ 0xa7, 0xdf, 0x7f, 0x52, 0xc4, 0x30, 0xc5, 0xa5, 0xc9, 0x0e, 0x02, 0xc5,
+ 0x53, 0xbf, 0x77, 0x38, 0x68, 0x06, 0x24, 0xc3, 0x66, 0xc8, 0x37, 0x7e,
+ 0x30, 0x1e, 0x45, 0x71, 0x23, 0x35, 0xff, 0x90, 0xd8, 0x2a, 0x9d, 0x8d,
+ 0xe7, 0xb0, 0x92, 0x4d, 0x3c, 0x7f, 0x2a, 0x0a, 0x93, 0xdc, 0xcd, 0x16,
+ 0x46, 0x65, 0xf7, 0x60, 0x84, 0x8b, 0x76, 0x4b, 0x91, 0x27, 0x73, 0x14,
+ 0x92, 0xe0, 0xea, 0xee, 0x8f, 0x16, 0xea, 0x8d, 0x0e, 0x3e, 0x76, 0x17,
+ 0xbf, 0x7d, 0x89, 0x80, 0x80, 0x44, 0x43, 0xe7, 0x2d, 0xe0, 0x43, 0x09,
+ 0x75, 0xda, 0x36, 0xe8, 0xad, 0xdb, 0x89, 0x3a, 0xf5, 0x5d, 0x12, 0x8e,
+ 0x23, 0x04, 0x83, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x2c,
+ 0x30, 0x82, 0x01, 0x28, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x25, 0x45, 0x81, 0x68, 0x50, 0x26, 0x38, 0x3d, 0x3b, 0x2d, 0x2c, 0xbe,
+ 0xcd, 0x6a, 0xd9, 0xb6, 0x3d, 0xb3, 0x66, 0x63, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7c, 0x0c, 0x32,
+ 0x1f, 0xa7, 0xd9, 0x30, 0x7f, 0xc4, 0x7d, 0x68, 0xa3, 0x62, 0xa8, 0xa1,
+ 0xce, 0xab, 0x07, 0x5b, 0x27, 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1e, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0xa0, 0x2e, 0xa0, 0x2c, 0x86, 0x2a,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x72, 0x6f, 0x6f, 0x74, 0x2d,
+ 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x56, 0x65, 0xca, 0xfe,
+ 0xf3, 0x3f, 0x0a, 0xa8, 0x93, 0x8b, 0x18, 0xc7, 0xde, 0x43, 0x69, 0x13,
+ 0x34, 0x20, 0xbe, 0x4e, 0x5f, 0x78, 0xa8, 0x6b, 0x9c, 0xdb, 0x6a, 0x4d,
+ 0x41, 0xdb, 0xc1, 0x13, 0xec, 0xdc, 0x31, 0x00, 0x22, 0x5e, 0xf7, 0x00,
+ 0x9e, 0x0c, 0xe0, 0x34, 0x65, 0x34, 0xf9, 0xb1, 0x3a, 0x4e, 0x48, 0xc8,
+ 0x12, 0x81, 0x88, 0x5c, 0x5b, 0x3e, 0x08, 0x53, 0x7a, 0xf7, 0x1a, 0x64,
+ 0xdf, 0xb8, 0x50, 0x61, 0xcc, 0x53, 0x51, 0x40, 0x29, 0x4b, 0xc2, 0xf4,
+ 0xae, 0x3a, 0x5f, 0xe4, 0xca, 0xad, 0x26, 0xcc, 0x4e, 0x61, 0x43, 0xe5,
+ 0xfd, 0x57, 0xa6, 0x37, 0x70, 0xce, 0x43, 0x2b, 0xb0, 0x94, 0xc3, 0x92,
+ 0xe9, 0xe1, 0x5f, 0xaa, 0x10, 0x49, 0xb7, 0x69, 0xe4, 0xe0, 0xd0, 0x1f,
+ 0x64, 0xa4, 0x2b, 0xcd, 0x1f, 0x6f, 0xa0, 0xf8, 0x84, 0x24, 0x18, 0xce,
+ 0x79, 0x3d, 0xa9, 0x91, 0xbf, 0x54, 0x18, 0x13, 0x89, 0x99, 0x54, 0x11,
+ 0x0d, 0x55, 0xc5, 0x26, 0x0b, 0x79, 0x4f, 0x5a, 0x1c, 0x6e, 0xf9, 0x63,
+ 0xdb, 0x14, 0x80, 0xa4, 0x07, 0xab, 0xfa, 0xb2, 0xa5, 0xb9, 0x88, 0xdd,
+ 0x91, 0xfe, 0x65, 0x3b, 0xa4, 0xa3, 0x79, 0xbe, 0x89, 0x4d, 0xe1, 0xd0,
+ 0xb0, 0xf4, 0xc8, 0x17, 0x0c, 0x0a, 0x96, 0x14, 0x7c, 0x09, 0xb7, 0x6c,
+ 0xe1, 0xc2, 0xd8, 0x55, 0xd4, 0x18, 0xa0, 0xaa, 0x41, 0x69, 0x70, 0x24,
+ 0xa3, 0xb9, 0xef, 0xe9, 0x5a, 0xdc, 0x3e, 0xeb, 0x94, 0x4a, 0xf0, 0xb7,
+ 0xde, 0x5f, 0x0e, 0x76, 0xfa, 0xfb, 0xfb, 0x69, 0x03, 0x45, 0x40, 0x50,
+ 0xee, 0x72, 0x0c, 0xa4, 0x12, 0x86, 0x81, 0xcd, 0x13, 0xd1, 0x4e, 0xc4,
+ 0x3c, 0xca, 0x4e, 0x0d, 0xd2, 0x26, 0xf1, 0x00, 0xb7, 0xb4, 0xa6, 0xa2,
+ 0xe1, 0x6e, 0x7a, 0x81, 0xfd, 0x30, 0xac, 0x7a, 0x1f, 0xc7, 0x59, 0x7b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1372807406 (0x51d360ee)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2
+ Validity
+ Not Before: Oct 22 17:05:14 2014 GMT
+ Not After : Oct 23 07:33:22 2024 GMT
+ Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Certification Authority - L1K
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:da:3f:96:d0:4d:b9:2f:44:e7:db:39:5e:9b:50:
+ ee:5c:a5:61:da:41:67:53:09:aa:00:9a:8e:57:7f:
+ 29:6b:db:c7:e1:21:24:aa:3a:d0:8d:47:23:d2:ed:
+ 72:16:f0:91:21:d2:5d:b7:b8:4b:a8:83:8f:b7:91:
+ 32:68:cf:ce:25:93:2c:b2:7d:97:c8:fe:c1:b4:17:
+ ba:09:9e:03:90:93:7b:7c:49:83:22:68:8a:9b:de:
+ 47:c3:31:98:7a:2e:7d:40:0b:d2:ef:3e:d3:b2:8c:
+ aa:8f:48:a9:ff:00:e8:29:58:06:f7:b6:93:5a:94:
+ 73:26:26:ad:58:0e:e5:42:b8:d5:ea:73:79:64:68:
+ 53:25:b8:84:cf:94:7a:ae:06:45:0c:a3:6b:4d:d0:
+ c6:be:ea:18:a4:36:f0:92:b2:ba:1c:88:8f:3a:52:
+ 7f:f7:5e:6d:83:1c:9d:f0:1f:e5:c3:d6:dd:a5:78:
+ 92:3d:b0:6d:2c:ea:c9:cf:94:41:19:71:44:68:ba:
+ 47:3c:04:e9:5d:ba:3e:f0:35:f7:15:b6:9e:f2:2e:
+ 15:1e:3f:47:c8:c8:38:a7:73:45:5d:4d:b0:3b:b1:
+ 8e:17:29:37:ea:dd:05:01:22:bb:94:36:2a:8d:5b:
+ 35:fe:53:19:2f:08:46:c1:2a:b3:1a:62:1d:4e:2b:
+ d9:1b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints:
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/g2ca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/rpa
+
+ X509v3 Subject Key Identifier:
+ 82:A2:70:74:DD:BC:53:3F:CF:7B:D4:F7:CD:7F:A7:60:C6:0A:4C:BF
+ X509v3 Authority Key Identifier:
+ keyid:6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 3f:1c:1a:5b:ff:40:22:1d:8f:35:0c:2d:aa:99:27:ab:c0:11:
+ 32:70:d7:36:28:69:a5:8d:b1:27:99:42:be:c4:93:eb:48:57:
+ 43:71:23:c4:e5:4e:ad:ae:43:6f:92:76:c5:19:ef:ca:bc:6f:
+ 42:4c:16:9a:86:a9:04:38:c7:65:f0:f5:0c:e0:4a:df:a2:fa:
+ ce:1a:11:a8:9c:69:2f:1b:df:ea:e2:32:f3:ce:4c:bc:46:0c:
+ c0:89:80:d1:87:6b:a2:cf:6b:d4:7f:fd:f5:60:52:67:57:a0:
+ 6d:d1:64:41:14:6d:34:62:ed:06:6c:24:f2:06:bc:28:02:af:
+ 03:2d:c2:33:05:fb:cb:aa:16:e8:65:10:43:f5:69:5c:e3:81:
+ 58:99:cd:6b:d3:b8:c7:7b:19:55:c9:40:ce:79:55:b8:73:89:
+ e9:5c:40:66:43:12:7f:07:b8:65:56:d5:8d:c3:a7:f5:b1:b6:
+ 65:9e:c0:83:36:7f:16:45:3c:74:4b:93:8a:3c:f1:2b:f5:35:
+ 70:73:7b:e7:82:04:b1:18:98:0e:d4:9c:6f:1a:fc:fc:a7:33:
+ a5:bb:bb:18:f3:6b:7a:5d:32:87:f7:6d:25:e4:e2:76:86:21:
+ 1e:11:46:cd:76:0e:6f:4f:a4:21:71:0a:84:a7:2d:36:a9:48:
+ 22:51:7e:82
+-----BEGIN CERTIFICATE-----
+MIIFAzCCA+ugAwIBAgIEUdNg7jANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50
+cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs
+IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz
+dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMTQxMDIyMTcw
+NTE0WhcNMjQxMDIzMDczMzIyWjCBujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu
+dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt
+dGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0
+aG9yaXplZCB1c2Ugb25seTEuMCwGA1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eSAtIEwxSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANo/ltBNuS9E59s5XptQ7lylYdpBZ1MJqgCajld/KWvbx+EhJKo60I1HI9Ltchbw
+kSHSXbe4S6iDj7eRMmjPziWTLLJ9l8j+wbQXugmeA5CTe3xJgyJoipveR8MxmHou
+fUAL0u8+07KMqo9Iqf8A6ClYBve2k1qUcyYmrVgO5UK41epzeWRoUyW4hM+Ueq4G
+RQyja03Qxr7qGKQ28JKyuhyIjzpSf/debYMcnfAf5cPW3aV4kj2wbSzqyc+UQRlx
+RGi6RzwE6V26PvA19xW2nvIuFR4/R8jIOKdzRV1NsDuxjhcpN+rdBQEiu5Q2Ko1b
+Nf5TGS8IRsEqsxpiHU4r2RsCAwEAAaOCAQkwggEFMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMECDAGAQH/AgEAMDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcwAYYXaHR0
+cDovL29jc3AuZW50cnVzdC5uZXQwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL2Ny
+bC5lbnRydXN0Lm5ldC9nMmNhLmNybDA7BgNVHSAENDAyMDAGBFUdIAAwKDAmBggr
+BgEFBQcCARYaaHR0cDovL3d3dy5lbnRydXN0Lm5ldC9ycGEwHQYDVR0OBBYEFIKi
+cHTdvFM/z3vU981/p2DGCky/MB8GA1UdIwQYMBaAFGpyJnrQHu995ztpUdRsjZ+Q
+EmarMA0GCSqGSIb3DQEBCwUAA4IBAQA/HBpb/0AiHY81DC2qmSerwBEycNc2KGml
+jbEnmUK+xJPrSFdDcSPE5U6trkNvknbFGe/KvG9CTBaahqkEOMdl8PUM4ErfovrO
+GhGonGkvG9/q4jLzzky8RgzAiYDRh2uiz2vUf/31YFJnV6Bt0WRBFG00Yu0GbCTy
+BrwoAq8DLcIzBfvLqhboZRBD9Wlc44FYmc1r07jHexlVyUDOeVW4c4npXEBmQxJ/
+B7hlVtWNw6f1sbZlnsCDNn8WRTx0S5OKPPEr9TVwc3vnggSxGJgO1JxvGvz8pzOl
+u7sY82t6XTKH920l5OJ2hiEeEUbNdg5vT6QhcQqEpy02qUgiUX6C
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert41[] = {
+ 0x30, 0x82, 0x05, 0x03, 0x30, 0x82, 0x03, 0xeb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x51, 0xd3, 0x60, 0xee, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xbe, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1f, 0x53, 0x65, 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67,
+ 0x61, 0x6c, 0x2d, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32,
+ 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75,
+ 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x32, 0x32, 0x31, 0x37, 0x30,
+ 0x35, 0x31, 0x34, 0x5a, 0x17, 0x0d, 0x32, 0x34, 0x31, 0x30, 0x32, 0x33,
+ 0x30, 0x37, 0x33, 0x33, 0x32, 0x32, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65,
+ 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x32,
+ 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20,
+ 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d,
+ 0x20, 0x4c, 0x31, 0x4b, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xda, 0x3f, 0x96, 0xd0, 0x4d, 0xb9, 0x2f, 0x44, 0xe7, 0xdb, 0x39,
+ 0x5e, 0x9b, 0x50, 0xee, 0x5c, 0xa5, 0x61, 0xda, 0x41, 0x67, 0x53, 0x09,
+ 0xaa, 0x00, 0x9a, 0x8e, 0x57, 0x7f, 0x29, 0x6b, 0xdb, 0xc7, 0xe1, 0x21,
+ 0x24, 0xaa, 0x3a, 0xd0, 0x8d, 0x47, 0x23, 0xd2, 0xed, 0x72, 0x16, 0xf0,
+ 0x91, 0x21, 0xd2, 0x5d, 0xb7, 0xb8, 0x4b, 0xa8, 0x83, 0x8f, 0xb7, 0x91,
+ 0x32, 0x68, 0xcf, 0xce, 0x25, 0x93, 0x2c, 0xb2, 0x7d, 0x97, 0xc8, 0xfe,
+ 0xc1, 0xb4, 0x17, 0xba, 0x09, 0x9e, 0x03, 0x90, 0x93, 0x7b, 0x7c, 0x49,
+ 0x83, 0x22, 0x68, 0x8a, 0x9b, 0xde, 0x47, 0xc3, 0x31, 0x98, 0x7a, 0x2e,
+ 0x7d, 0x40, 0x0b, 0xd2, 0xef, 0x3e, 0xd3, 0xb2, 0x8c, 0xaa, 0x8f, 0x48,
+ 0xa9, 0xff, 0x00, 0xe8, 0x29, 0x58, 0x06, 0xf7, 0xb6, 0x93, 0x5a, 0x94,
+ 0x73, 0x26, 0x26, 0xad, 0x58, 0x0e, 0xe5, 0x42, 0xb8, 0xd5, 0xea, 0x73,
+ 0x79, 0x64, 0x68, 0x53, 0x25, 0xb8, 0x84, 0xcf, 0x94, 0x7a, 0xae, 0x06,
+ 0x45, 0x0c, 0xa3, 0x6b, 0x4d, 0xd0, 0xc6, 0xbe, 0xea, 0x18, 0xa4, 0x36,
+ 0xf0, 0x92, 0xb2, 0xba, 0x1c, 0x88, 0x8f, 0x3a, 0x52, 0x7f, 0xf7, 0x5e,
+ 0x6d, 0x83, 0x1c, 0x9d, 0xf0, 0x1f, 0xe5, 0xc3, 0xd6, 0xdd, 0xa5, 0x78,
+ 0x92, 0x3d, 0xb0, 0x6d, 0x2c, 0xea, 0xc9, 0xcf, 0x94, 0x41, 0x19, 0x71,
+ 0x44, 0x68, 0xba, 0x47, 0x3c, 0x04, 0xe9, 0x5d, 0xba, 0x3e, 0xf0, 0x35,
+ 0xf7, 0x15, 0xb6, 0x9e, 0xf2, 0x2e, 0x15, 0x1e, 0x3f, 0x47, 0xc8, 0xc8,
+ 0x38, 0xa7, 0x73, 0x45, 0x5d, 0x4d, 0xb0, 0x3b, 0xb1, 0x8e, 0x17, 0x29,
+ 0x37, 0xea, 0xdd, 0x05, 0x01, 0x22, 0xbb, 0x94, 0x36, 0x2a, 0x8d, 0x5b,
+ 0x35, 0xfe, 0x53, 0x19, 0x2f, 0x08, 0x46, 0xc1, 0x2a, 0xb3, 0x1a, 0x62,
+ 0x1d, 0x4e, 0x2b, 0xd9, 0x1b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x09, 0x30, 0x82, 0x01, 0x05, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x30, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0,
+ 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x2f, 0x67, 0x32, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x82, 0xa2,
+ 0x70, 0x74, 0xdd, 0xbc, 0x53, 0x3f, 0xcf, 0x7b, 0xd4, 0xf7, 0xcd, 0x7f,
+ 0xa7, 0x60, 0xc6, 0x0a, 0x4c, 0xbf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0,
+ 0x1e, 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90,
+ 0x12, 0x66, 0xab, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3f,
+ 0x1c, 0x1a, 0x5b, 0xff, 0x40, 0x22, 0x1d, 0x8f, 0x35, 0x0c, 0x2d, 0xaa,
+ 0x99, 0x27, 0xab, 0xc0, 0x11, 0x32, 0x70, 0xd7, 0x36, 0x28, 0x69, 0xa5,
+ 0x8d, 0xb1, 0x27, 0x99, 0x42, 0xbe, 0xc4, 0x93, 0xeb, 0x48, 0x57, 0x43,
+ 0x71, 0x23, 0xc4, 0xe5, 0x4e, 0xad, 0xae, 0x43, 0x6f, 0x92, 0x76, 0xc5,
+ 0x19, 0xef, 0xca, 0xbc, 0x6f, 0x42, 0x4c, 0x16, 0x9a, 0x86, 0xa9, 0x04,
+ 0x38, 0xc7, 0x65, 0xf0, 0xf5, 0x0c, 0xe0, 0x4a, 0xdf, 0xa2, 0xfa, 0xce,
+ 0x1a, 0x11, 0xa8, 0x9c, 0x69, 0x2f, 0x1b, 0xdf, 0xea, 0xe2, 0x32, 0xf3,
+ 0xce, 0x4c, 0xbc, 0x46, 0x0c, 0xc0, 0x89, 0x80, 0xd1, 0x87, 0x6b, 0xa2,
+ 0xcf, 0x6b, 0xd4, 0x7f, 0xfd, 0xf5, 0x60, 0x52, 0x67, 0x57, 0xa0, 0x6d,
+ 0xd1, 0x64, 0x41, 0x14, 0x6d, 0x34, 0x62, 0xed, 0x06, 0x6c, 0x24, 0xf2,
+ 0x06, 0xbc, 0x28, 0x02, 0xaf, 0x03, 0x2d, 0xc2, 0x33, 0x05, 0xfb, 0xcb,
+ 0xaa, 0x16, 0xe8, 0x65, 0x10, 0x43, 0xf5, 0x69, 0x5c, 0xe3, 0x81, 0x58,
+ 0x99, 0xcd, 0x6b, 0xd3, 0xb8, 0xc7, 0x7b, 0x19, 0x55, 0xc9, 0x40, 0xce,
+ 0x79, 0x55, 0xb8, 0x73, 0x89, 0xe9, 0x5c, 0x40, 0x66, 0x43, 0x12, 0x7f,
+ 0x07, 0xb8, 0x65, 0x56, 0xd5, 0x8d, 0xc3, 0xa7, 0xf5, 0xb1, 0xb6, 0x65,
+ 0x9e, 0xc0, 0x83, 0x36, 0x7f, 0x16, 0x45, 0x3c, 0x74, 0x4b, 0x93, 0x8a,
+ 0x3c, 0xf1, 0x2b, 0xf5, 0x35, 0x70, 0x73, 0x7b, 0xe7, 0x82, 0x04, 0xb1,
+ 0x18, 0x98, 0x0e, 0xd4, 0x9c, 0x6f, 0x1a, 0xfc, 0xfc, 0xa7, 0x33, 0xa5,
+ 0xbb, 0xbb, 0x18, 0xf3, 0x6b, 0x7a, 0x5d, 0x32, 0x87, 0xf7, 0x6d, 0x25,
+ 0xe4, 0xe2, 0x76, 0x86, 0x21, 0x1e, 0x11, 0x46, 0xcd, 0x76, 0x0e, 0x6f,
+ 0x4f, 0xa4, 0x21, 0x71, 0x0a, 0x84, 0xa7, 0x2d, 0x36, 0xa9, 0x48, 0x22,
+ 0x51, 0x7e, 0x82,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0e:e9:4c:c3:00:00:00:00:51:d3:77:85
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2
+ Validity
+ Not Before: Oct 5 19:13:56 2015 GMT
+ Not After : Dec 5 19:43:56 2030 GMT
+ Subject: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Certification Authority - L1K
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:da:3f:96:d0:4d:b9:2f:44:e7:db:39:5e:9b:50:
+ ee:5c:a5:61:da:41:67:53:09:aa:00:9a:8e:57:7f:
+ 29:6b:db:c7:e1:21:24:aa:3a:d0:8d:47:23:d2:ed:
+ 72:16:f0:91:21:d2:5d:b7:b8:4b:a8:83:8f:b7:91:
+ 32:68:cf:ce:25:93:2c:b2:7d:97:c8:fe:c1:b4:17:
+ ba:09:9e:03:90:93:7b:7c:49:83:22:68:8a:9b:de:
+ 47:c3:31:98:7a:2e:7d:40:0b:d2:ef:3e:d3:b2:8c:
+ aa:8f:48:a9:ff:00:e8:29:58:06:f7:b6:93:5a:94:
+ 73:26:26:ad:58:0e:e5:42:b8:d5:ea:73:79:64:68:
+ 53:25:b8:84:cf:94:7a:ae:06:45:0c:a3:6b:4d:d0:
+ c6:be:ea:18:a4:36:f0:92:b2:ba:1c:88:8f:3a:52:
+ 7f:f7:5e:6d:83:1c:9d:f0:1f:e5:c3:d6:dd:a5:78:
+ 92:3d:b0:6d:2c:ea:c9:cf:94:41:19:71:44:68:ba:
+ 47:3c:04:e9:5d:ba:3e:f0:35:f7:15:b6:9e:f2:2e:
+ 15:1e:3f:47:c8:c8:38:a7:73:45:5d:4d:b0:3b:b1:
+ 8e:17:29:37:ea:dd:05:01:22:bb:94:36:2a:8d:5b:
+ 35:fe:53:19:2f:08:46:c1:2a:b3:1a:62:1d:4e:2b:
+ d9:1b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/g2ca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/rpa
+
+ X509v3 Subject Key Identifier:
+ 82:A2:70:74:DD:BC:53:3F:CF:7B:D4:F7:CD:7F:A7:60:C6:0A:4C:BF
+ X509v3 Authority Key Identifier:
+ keyid:6A:72:26:7A:D0:1E:EF:7D:E7:3B:69:51:D4:6C:8D:9F:90:12:66:AB
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 39:d5:8e:98:83:61:c8:2c:63:d3:70:1d:19:30:cb:f6:09:ac:
+ cc:69:d5:c9:dc:37:41:f2:32:0f:ef:74:c3:58:f6:78:27:09:
+ 34:08:95:92:2f:d7:df:b8:a3:fd:0e:81:e9:a4:9c:d3:3f:4d:
+ 68:2b:15:31:0a:15:cc:52:04:93:e8:93:50:c3:d9:b1:e2:e1:
+ 68:b7:3a:09:74:f1:34:58:0a:3f:77:98:40:b8:e6:68:ff:5d:
+ e4:c8:46:c5:ec:81:d7:c9:82:18:5c:83:ce:71:d8:bc:bf:ac:
+ 99:02:93:db:94:98:84:d2:9c:a6:b5:fe:5c:bb:f0:4a:af:21:
+ ac:c2:3f:49:24:67:d6:2e:8e:cf:ac:cc:64:15:18:72:e5:6c:
+ 77:d3:52:a8:b9:dd:8d:ac:00:4a:35:19:d4:6f:73:a3:75:ef:
+ 6b:64:c3:e0:8d:83:12:a1:8a:e7:0e:86:4d:d8:b4:20:1b:be:
+ 6a:a5:8c:4b:68:66:e3:2b:c7:58:0b:fb:56:10:d4:91:fb:1d:
+ d3:31:58:10:8c:44:e3:75:7b:10:9d:b5:38:b1:f6:aa:ca:81:
+ 64:6c:e8:f2:e2:81:55:97:51:7f:e1:c2:27:50:a2:c9:3c:5b:
+ 00:43:f6:5b:b9:d5:a5:fc:ff:07:50:40:67:07:b0:55:f0:b7:
+ 7e:6e:2d:cc
+-----BEGIN CERTIFICATE-----
+MIIFDjCCA/agAwIBAgIMDulMwwAAAABR03eFMA0GCSqGSIb3DQEBCwUAMIG+MQsw
+CQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2Vl
+IHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMDkg
+RW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIwMAYDVQQD
+EylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjAeFw0x
+NTEwMDUxOTEzNTZaFw0zMDEyMDUxOTQzNTZaMIG6MQswCQYDVQQGEwJVUzEWMBQG
+A1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5l
+dC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMTIgRW50cnVzdCwgSW5jLiAt
+IGZvciBhdXRob3JpemVkIHVzZSBvbmx5MS4wLAYDVQQDEyVFbnRydXN0IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5IC0gTDFLMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEA2j+W0E25L0Tn2zlem1DuXKVh2kFnUwmqAJqOV38pa9vH4SEkqjrQ
+jUcj0u1yFvCRIdJdt7hLqIOPt5EyaM/OJZMssn2XyP7BtBe6CZ4DkJN7fEmDImiK
+m95HwzGYei59QAvS7z7Tsoyqj0ip/wDoKVgG97aTWpRzJiatWA7lQrjV6nN5ZGhT
+JbiEz5R6rgZFDKNrTdDGvuoYpDbwkrK6HIiPOlJ/915tgxyd8B/lw9bdpXiSPbBt
+LOrJz5RBGXFEaLpHPATpXbo+8DX3Fbae8i4VHj9HyMg4p3NFXU2wO7GOFyk36t0F
+ASK7lDYqjVs1/lMZLwhGwSqzGmIdTivZGwIDAQABo4IBDDCCAQgwDgYDVR0PAQH/
+BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwMwYIKwYBBQUHAQEEJzAlMCMGCCsG
+AQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAwBgNVHR8EKTAnMCWgI6Ah
+hh9odHRwOi8vY3JsLmVudHJ1c3QubmV0L2cyY2EuY3JsMDsGA1UdIAQ0MDIwMAYE
+VR0gADAoMCYGCCsGAQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAd
+BgNVHQ4EFgQUgqJwdN28Uz/Pe9T3zX+nYMYKTL8wHwYDVR0jBBgwFoAUanImetAe
+733nO2lR1GyNn5ASZqswDQYJKoZIhvcNAQELBQADggEBADnVjpiDYcgsY9NwHRkw
+y/YJrMxp1cncN0HyMg/vdMNY9ngnCTQIlZIv19+4o/0OgemknNM/TWgrFTEKFcxS
+BJPok1DD2bHi4Wi3Ogl08TRYCj93mEC45mj/XeTIRsXsgdfJghhcg85x2Ly/rJkC
+k9uUmITSnKa1/ly78EqvIazCP0kkZ9Yujs+szGQVGHLlbHfTUqi53Y2sAEo1GdRv
+c6N172tkw+CNgxKhiucOhk3YtCAbvmqljEtoZuMrx1gL+1YQ1JH7HdMxWBCMRON1
+exCdtTix9qrKgWRs6PLigVWXUX/hwidQosk8WwBD9lu51aX8/wdQQGcHsFXwt35u
+Lcw=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert42[] = {
+ 0x30, 0x82, 0x05, 0x0e, 0x30, 0x82, 0x03, 0xf6, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0c, 0x0e, 0xe9, 0x4c, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x51,
+ 0xd3, 0x77, 0x85, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0xbe, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16,
+ 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x28,
+ 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, 0x65,
+ 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x74,
+ 0x65, 0x72, 0x6d, 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x30, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20,
+ 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f,
+ 0x6e, 0x6c, 0x79, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x29, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x35, 0x31, 0x30, 0x30, 0x35, 0x31, 0x39, 0x31, 0x33, 0x35, 0x36, 0x5a,
+ 0x17, 0x0d, 0x33, 0x30, 0x31, 0x32, 0x30, 0x35, 0x31, 0x39, 0x34, 0x33,
+ 0x35, 0x36, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f, 0x53, 0x65, 0x65, 0x20, 0x77, 0x77,
+ 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x74, 0x65, 0x72, 0x6d,
+ 0x73, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30,
+ 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x32, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, 0x45,
+ 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x4c, 0x31, 0x4b,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0x3f, 0x96,
+ 0xd0, 0x4d, 0xb9, 0x2f, 0x44, 0xe7, 0xdb, 0x39, 0x5e, 0x9b, 0x50, 0xee,
+ 0x5c, 0xa5, 0x61, 0xda, 0x41, 0x67, 0x53, 0x09, 0xaa, 0x00, 0x9a, 0x8e,
+ 0x57, 0x7f, 0x29, 0x6b, 0xdb, 0xc7, 0xe1, 0x21, 0x24, 0xaa, 0x3a, 0xd0,
+ 0x8d, 0x47, 0x23, 0xd2, 0xed, 0x72, 0x16, 0xf0, 0x91, 0x21, 0xd2, 0x5d,
+ 0xb7, 0xb8, 0x4b, 0xa8, 0x83, 0x8f, 0xb7, 0x91, 0x32, 0x68, 0xcf, 0xce,
+ 0x25, 0x93, 0x2c, 0xb2, 0x7d, 0x97, 0xc8, 0xfe, 0xc1, 0xb4, 0x17, 0xba,
+ 0x09, 0x9e, 0x03, 0x90, 0x93, 0x7b, 0x7c, 0x49, 0x83, 0x22, 0x68, 0x8a,
+ 0x9b, 0xde, 0x47, 0xc3, 0x31, 0x98, 0x7a, 0x2e, 0x7d, 0x40, 0x0b, 0xd2,
+ 0xef, 0x3e, 0xd3, 0xb2, 0x8c, 0xaa, 0x8f, 0x48, 0xa9, 0xff, 0x00, 0xe8,
+ 0x29, 0x58, 0x06, 0xf7, 0xb6, 0x93, 0x5a, 0x94, 0x73, 0x26, 0x26, 0xad,
+ 0x58, 0x0e, 0xe5, 0x42, 0xb8, 0xd5, 0xea, 0x73, 0x79, 0x64, 0x68, 0x53,
+ 0x25, 0xb8, 0x84, 0xcf, 0x94, 0x7a, 0xae, 0x06, 0x45, 0x0c, 0xa3, 0x6b,
+ 0x4d, 0xd0, 0xc6, 0xbe, 0xea, 0x18, 0xa4, 0x36, 0xf0, 0x92, 0xb2, 0xba,
+ 0x1c, 0x88, 0x8f, 0x3a, 0x52, 0x7f, 0xf7, 0x5e, 0x6d, 0x83, 0x1c, 0x9d,
+ 0xf0, 0x1f, 0xe5, 0xc3, 0xd6, 0xdd, 0xa5, 0x78, 0x92, 0x3d, 0xb0, 0x6d,
+ 0x2c, 0xea, 0xc9, 0xcf, 0x94, 0x41, 0x19, 0x71, 0x44, 0x68, 0xba, 0x47,
+ 0x3c, 0x04, 0xe9, 0x5d, 0xba, 0x3e, 0xf0, 0x35, 0xf7, 0x15, 0xb6, 0x9e,
+ 0xf2, 0x2e, 0x15, 0x1e, 0x3f, 0x47, 0xc8, 0xc8, 0x38, 0xa7, 0x73, 0x45,
+ 0x5d, 0x4d, 0xb0, 0x3b, 0xb1, 0x8e, 0x17, 0x29, 0x37, 0xea, 0xdd, 0x05,
+ 0x01, 0x22, 0xbb, 0x94, 0x36, 0x2a, 0x8d, 0x5b, 0x35, 0xfe, 0x53, 0x19,
+ 0x2f, 0x08, 0x46, 0xc1, 0x2a, 0xb3, 0x1a, 0x62, 0x1d, 0x4e, 0x2b, 0xd9,
+ 0x1b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x0c, 0x30, 0x82,
+ 0x01, 0x08, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x30, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21,
+ 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x2f, 0x67, 0x32, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04,
+ 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x82, 0xa2, 0x70,
+ 0x74, 0xdd, 0xbc, 0x53, 0x3f, 0xcf, 0x7b, 0xd4, 0xf7, 0xcd, 0x7f, 0xa7,
+ 0x60, 0xc6, 0x0a, 0x4c, 0xbf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0x72, 0x26, 0x7a, 0xd0, 0x1e,
+ 0xef, 0x7d, 0xe7, 0x3b, 0x69, 0x51, 0xd4, 0x6c, 0x8d, 0x9f, 0x90, 0x12,
+ 0x66, 0xab, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x39, 0xd5,
+ 0x8e, 0x98, 0x83, 0x61, 0xc8, 0x2c, 0x63, 0xd3, 0x70, 0x1d, 0x19, 0x30,
+ 0xcb, 0xf6, 0x09, 0xac, 0xcc, 0x69, 0xd5, 0xc9, 0xdc, 0x37, 0x41, 0xf2,
+ 0x32, 0x0f, 0xef, 0x74, 0xc3, 0x58, 0xf6, 0x78, 0x27, 0x09, 0x34, 0x08,
+ 0x95, 0x92, 0x2f, 0xd7, 0xdf, 0xb8, 0xa3, 0xfd, 0x0e, 0x81, 0xe9, 0xa4,
+ 0x9c, 0xd3, 0x3f, 0x4d, 0x68, 0x2b, 0x15, 0x31, 0x0a, 0x15, 0xcc, 0x52,
+ 0x04, 0x93, 0xe8, 0x93, 0x50, 0xc3, 0xd9, 0xb1, 0xe2, 0xe1, 0x68, 0xb7,
+ 0x3a, 0x09, 0x74, 0xf1, 0x34, 0x58, 0x0a, 0x3f, 0x77, 0x98, 0x40, 0xb8,
+ 0xe6, 0x68, 0xff, 0x5d, 0xe4, 0xc8, 0x46, 0xc5, 0xec, 0x81, 0xd7, 0xc9,
+ 0x82, 0x18, 0x5c, 0x83, 0xce, 0x71, 0xd8, 0xbc, 0xbf, 0xac, 0x99, 0x02,
+ 0x93, 0xdb, 0x94, 0x98, 0x84, 0xd2, 0x9c, 0xa6, 0xb5, 0xfe, 0x5c, 0xbb,
+ 0xf0, 0x4a, 0xaf, 0x21, 0xac, 0xc2, 0x3f, 0x49, 0x24, 0x67, 0xd6, 0x2e,
+ 0x8e, 0xcf, 0xac, 0xcc, 0x64, 0x15, 0x18, 0x72, 0xe5, 0x6c, 0x77, 0xd3,
+ 0x52, 0xa8, 0xb9, 0xdd, 0x8d, 0xac, 0x00, 0x4a, 0x35, 0x19, 0xd4, 0x6f,
+ 0x73, 0xa3, 0x75, 0xef, 0x6b, 0x64, 0xc3, 0xe0, 0x8d, 0x83, 0x12, 0xa1,
+ 0x8a, 0xe7, 0x0e, 0x86, 0x4d, 0xd8, 0xb4, 0x20, 0x1b, 0xbe, 0x6a, 0xa5,
+ 0x8c, 0x4b, 0x68, 0x66, 0xe3, 0x2b, 0xc7, 0x58, 0x0b, 0xfb, 0x56, 0x10,
+ 0xd4, 0x91, 0xfb, 0x1d, 0xd3, 0x31, 0x58, 0x10, 0x8c, 0x44, 0xe3, 0x75,
+ 0x7b, 0x10, 0x9d, 0xb5, 0x38, 0xb1, 0xf6, 0xaa, 0xca, 0x81, 0x64, 0x6c,
+ 0xe8, 0xf2, 0xe2, 0x81, 0x55, 0x97, 0x51, 0x7f, 0xe1, 0xc2, 0x27, 0x50,
+ 0xa2, 0xc9, 0x3c, 0x5b, 0x00, 0x43, 0xf6, 0x5b, 0xb9, 0xd5, 0xa5, 0xfc,
+ 0xff, 0x07, 0x50, 0x40, 0x67, 0x07, 0xb0, 0x55, 0xf0, 0xb7, 0x7e, 0x6e,
+ 0x2d, 0xcc,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120038507 (0x727a46b)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Apr 2 14:36:10 2014 GMT
+ Not After : Apr 2 14:35:52 2021 GMT
+ Subject: C=NL, L=Amsterdam, O=Verizon Enterprise Solutions, OU=Cybertrust, CN=Verizon Akamai SureServer CA G14-SHA2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:dd:6e:9e:02:69:02:b5:a3:99:2e:08:64:32:6a:
+ 59:f3:c6:9e:a6:20:07:d2:48:d1:a8:93:c7:ea:47:
+ 8f:83:39:40:d7:20:5d:8d:9a:ba:ab:d8:70:ec:9d:
+ 88:d1:bd:62:f6:db:ec:9d:5e:35:01:76:03:23:e5:
+ 6f:d2:af:46:35:59:5a:5c:d1:a8:23:c1:eb:e9:20:
+ d4:49:d6:3f:00:d8:a8:22:de:43:79:81:ac:e9:a4:
+ 92:f5:77:70:05:1e:5c:b6:a0:f7:90:a4:cd:ab:28:
+ 2c:90:c2:e7:0f:c3:af:1c:47:59:d5:84:2e:df:26:
+ 07:45:23:5a:c6:e8:90:c8:85:4b:8c:16:1e:60:f9:
+ 01:13:f1:14:1f:e6:e8:14:ed:c5:d2:6f:63:28:6e:
+ 72:8c:49:ae:08:72:c7:93:95:b4:0b:0c:ae:8f:9a:
+ 67:84:f5:57:1b:db:81:d7:17:9d:41:11:43:19:bd:
+ 6d:4a:85:ed:8f:70:25:ab:66:ab:f6:fa:6d:1c:3c:
+ ab:ed:17:bd:56:84:e1:db:75:33:b2:28:4b:99:8e:
+ f9:4b:82:33:50:9f:92:53:ed:fa:ad:0f:95:9c:a3:
+ f2:cb:60:f0:77:1d:c9:01:8b:5f:2d:86:be:bf:36:
+ b8:24:96:13:7c:c1:86:5a:6c:c1:48:2a:7f:3e:93:
+ 60:c5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:2
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.50
+ CPS: https://secure.omniroot.com/repository
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.omniroot.com/baltimoreroot
+ CA Issuers - URI:https://cacert.omniroot.com/baltimoreroot.crt
+ CA Issuers - URI:https://cacert.omniroot.com/baltimoreroot.der
+
+ X509v3 Key Usage: critical
+ Digital Signature, Non Repudiation, Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ F8:BD:FA:AF:73:77:C6:C7:1B:F9:4B:4D:11:A7:D1:33:AF:AF:72:11
+ Signature Algorithm: sha256WithRSAEncryption
+ 80:d9:7a:ed:72:05:37:8f:61:aa:73:7c:9a:6a:fc:fe:01:e2:
+ 19:81:70:07:25:32:b0:f0:6f:3b:c7:6a:28:3d:e4:51:87:e6:
+ 7e:82:ec:ae:48:a7:b1:77:38:c2:d6:56:af:8f:f2:01:fc:65:
+ 65:10:09:f7:74:29:b5:0e:92:ee:90:98:d1:88:a2:65:b7:cd:
+ 9c:0e:a7:86:98:28:bc:ae:15:83:b6:1a:d7:1d:ec:19:da:7a:
+ 8e:40:f9:99:15:d5:7d:a5:ba:ab:fd:26:98:6e:9c:41:3b:b6:
+ 81:18:ec:70:48:d7:6e:7f:a6:e1:77:25:d6:dd:62:e8:52:f3:
+ 8c:16:39:67:e2:22:0d:77:2e:fb:11:6c:e4:dd:38:b4:27:5f:
+ 03:a8:3d:44:e2:f2:84:4b:84:fd:56:a6:9e:4d:7b:a2:16:4f:
+ 07:f5:34:24:72:a5:a2:fa:16:66:2a:a4:4a:0e:c8:0d:27:44:
+ 9c:77:d4:12:10:87:d2:00:2c:7a:bb:8e:88:22:91:15:be:a2:
+ 59:ca:34:e0:1c:61:94:86:20:33:cd:e7:4c:5d:3b:92:3e:cb:
+ d6:2d:ea:54:fa:fb:af:54:f5:a8:c5:0b:ca:8b:87:00:e6:9f:
+ e6:95:bf:b7:c4:a3:59:f5:16:6c:5f:3e:69:55:80:39:f6:75:
+ 50:14:3e:32
+-----BEGIN CERTIFICATE-----
+MIIFHzCCBAegAwIBAgIEByekazANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDQwMjE0MzYxMFoX
+DTIxMDQwMjE0MzU1MlowgY0xCzAJBgNVBAYTAk5MMRIwEAYDVQQHEwlBbXN0ZXJk
+YW0xJTAjBgNVBAoTHFZlcml6b24gRW50ZXJwcmlzZSBTb2x1dGlvbnMxEzARBgNV
+BAsTCkN5YmVydHJ1c3QxLjAsBgNVBAMTJVZlcml6b24gQWthbWFpIFN1cmVTZXJ2
+ZXIgQ0EgRzE0LVNIQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDd
+bp4CaQK1o5kuCGQyalnzxp6mIAfSSNGok8fqR4+DOUDXIF2Nmrqr2HDsnYjRvWL2
+2+ydXjUBdgMj5W/Sr0Y1WVpc0agjwevpINRJ1j8A2Kgi3kN5gazppJL1d3AFHly2
+oPeQpM2rKCyQwucPw68cR1nVhC7fJgdFI1rG6JDIhUuMFh5g+QET8RQf5ugU7cXS
+b2MobnKMSa4IcseTlbQLDK6PmmeE9Vcb24HXF51BEUMZvW1Khe2PcCWrZqv2+m0c
+PKvtF71WhOHbdTOyKEuZjvlLgjNQn5JT7fqtD5Wco/LLYPB3HckBi18thr6/Nrgk
+lhN8wYZabMFIKn8+k2DFAgMBAAGjggG3MIIBszASBgNVHRMBAf8ECDAGAQH/AgEC
+MEwGA1UdIARFMEMwQQYJKwYBBAGxPgEyMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v
+c2VjdXJlLm9tbmlyb290LmNvbS9yZXBvc2l0b3J5MIG6BggrBgEFBQcBAQSBrTCB
+qjAyBggrBgEFBQcwAYYmaHR0cDovL29jc3Aub21uaXJvb3QuY29tL2JhbHRpbW9y
+ZXJvb3QwOQYIKwYBBQUHMAKGLWh0dHBzOi8vY2FjZXJ0Lm9tbmlyb290LmNvbS9i
+YWx0aW1vcmVyb290LmNydDA5BggrBgEFBQcwAoYtaHR0cHM6Ly9jYWNlcnQub21u
+aXJvb3QuY29tL2JhbHRpbW9yZXJvb3QuZGVyMA4GA1UdDwEB/wQEAwIBxjAfBgNV
+HSMEGDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DBCBgNVHR8EOzA5MDegNaAzhjFo
+dHRwOi8vY2RwMS5wdWJsaWMtdHJ1c3QuY29tL0NSTC9PbW5pcm9vdDIwMjUuY3Js
+MB0GA1UdDgQWBBT4vfqvc3fGxxv5S00Rp9Ezr69yETANBgkqhkiG9w0BAQsFAAOC
+AQEAgNl67XIFN49hqnN8mmr8/gHiGYFwByUysPBvO8dqKD3kUYfmfoLsrkinsXc4
+wtZWr4/yAfxlZRAJ93QptQ6S7pCY0YiiZbfNnA6nhpgovK4Vg7Ya1x3sGdp6jkD5
+mRXVfaW6q/0mmG6cQTu2gRjscEjXbn+m4Xcl1t1i6FLzjBY5Z+IiDXcu+xFs5N04
+tCdfA6g9ROLyhEuE/Vamnk17ohZPB/U0JHKlovoWZiqkSg7IDSdEnHfUEhCH0gAs
+eruOiCKRFb6iWco04BxhlIYgM83nTF07kj7L1i3qVPr7r1T1qMULyouHAOaf5pW/
+t8SjWfUWbF8+aVWAOfZ1UBQ+Mg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert43[] = {
+ 0x30, 0x82, 0x05, 0x1f, 0x30, 0x82, 0x04, 0x07, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0xa4, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34,
+ 0x30, 0x34, 0x30, 0x32, 0x31, 0x34, 0x33, 0x36, 0x31, 0x30, 0x5a, 0x17,
+ 0x0d, 0x32, 0x31, 0x30, 0x34, 0x30, 0x32, 0x31, 0x34, 0x33, 0x35, 0x35,
+ 0x32, 0x5a, 0x30, 0x81, 0x8d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x4e, 0x4c, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x13, 0x09, 0x41, 0x6d, 0x73, 0x74, 0x65, 0x72, 0x64,
+ 0x61, 0x6d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x1c, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x45, 0x6e, 0x74,
+ 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x20, 0x53, 0x6f, 0x6c, 0x75,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x25, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x41, 0x6b, 0x61,
+ 0x6d, 0x61, 0x69, 0x20, 0x53, 0x75, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x47, 0x31, 0x34, 0x2d, 0x53, 0x48,
+ 0x41, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdd,
+ 0x6e, 0x9e, 0x02, 0x69, 0x02, 0xb5, 0xa3, 0x99, 0x2e, 0x08, 0x64, 0x32,
+ 0x6a, 0x59, 0xf3, 0xc6, 0x9e, 0xa6, 0x20, 0x07, 0xd2, 0x48, 0xd1, 0xa8,
+ 0x93, 0xc7, 0xea, 0x47, 0x8f, 0x83, 0x39, 0x40, 0xd7, 0x20, 0x5d, 0x8d,
+ 0x9a, 0xba, 0xab, 0xd8, 0x70, 0xec, 0x9d, 0x88, 0xd1, 0xbd, 0x62, 0xf6,
+ 0xdb, 0xec, 0x9d, 0x5e, 0x35, 0x01, 0x76, 0x03, 0x23, 0xe5, 0x6f, 0xd2,
+ 0xaf, 0x46, 0x35, 0x59, 0x5a, 0x5c, 0xd1, 0xa8, 0x23, 0xc1, 0xeb, 0xe9,
+ 0x20, 0xd4, 0x49, 0xd6, 0x3f, 0x00, 0xd8, 0xa8, 0x22, 0xde, 0x43, 0x79,
+ 0x81, 0xac, 0xe9, 0xa4, 0x92, 0xf5, 0x77, 0x70, 0x05, 0x1e, 0x5c, 0xb6,
+ 0xa0, 0xf7, 0x90, 0xa4, 0xcd, 0xab, 0x28, 0x2c, 0x90, 0xc2, 0xe7, 0x0f,
+ 0xc3, 0xaf, 0x1c, 0x47, 0x59, 0xd5, 0x84, 0x2e, 0xdf, 0x26, 0x07, 0x45,
+ 0x23, 0x5a, 0xc6, 0xe8, 0x90, 0xc8, 0x85, 0x4b, 0x8c, 0x16, 0x1e, 0x60,
+ 0xf9, 0x01, 0x13, 0xf1, 0x14, 0x1f, 0xe6, 0xe8, 0x14, 0xed, 0xc5, 0xd2,
+ 0x6f, 0x63, 0x28, 0x6e, 0x72, 0x8c, 0x49, 0xae, 0x08, 0x72, 0xc7, 0x93,
+ 0x95, 0xb4, 0x0b, 0x0c, 0xae, 0x8f, 0x9a, 0x67, 0x84, 0xf5, 0x57, 0x1b,
+ 0xdb, 0x81, 0xd7, 0x17, 0x9d, 0x41, 0x11, 0x43, 0x19, 0xbd, 0x6d, 0x4a,
+ 0x85, 0xed, 0x8f, 0x70, 0x25, 0xab, 0x66, 0xab, 0xf6, 0xfa, 0x6d, 0x1c,
+ 0x3c, 0xab, 0xed, 0x17, 0xbd, 0x56, 0x84, 0xe1, 0xdb, 0x75, 0x33, 0xb2,
+ 0x28, 0x4b, 0x99, 0x8e, 0xf9, 0x4b, 0x82, 0x33, 0x50, 0x9f, 0x92, 0x53,
+ 0xed, 0xfa, 0xad, 0x0f, 0x95, 0x9c, 0xa3, 0xf2, 0xcb, 0x60, 0xf0, 0x77,
+ 0x1d, 0xc9, 0x01, 0x8b, 0x5f, 0x2d, 0x86, 0xbe, 0xbf, 0x36, 0xb8, 0x24,
+ 0x96, 0x13, 0x7c, 0xc1, 0x86, 0x5a, 0x6c, 0xc1, 0x48, 0x2a, 0x7f, 0x3e,
+ 0x93, 0x60, 0xc5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xb7,
+ 0x30, 0x82, 0x01, 0xb3, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x02,
+ 0x30, 0x4c, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x45, 0x30, 0x43, 0x30,
+ 0x41, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x32,
+ 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x81, 0xba, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xad, 0x30, 0x81,
+ 0xaa, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72,
+ 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62,
+ 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x72, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x6d, 0x6e,
+ 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61,
+ 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74, 0x2e,
+ 0x64, 0x65, 0x72, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0xc6, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30,
+ 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a,
+ 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69,
+ 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf8,
+ 0xbd, 0xfa, 0xaf, 0x73, 0x77, 0xc6, 0xc7, 0x1b, 0xf9, 0x4b, 0x4d, 0x11,
+ 0xa7, 0xd1, 0x33, 0xaf, 0xaf, 0x72, 0x11, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x80, 0xd9, 0x7a, 0xed, 0x72, 0x05, 0x37, 0x8f, 0x61,
+ 0xaa, 0x73, 0x7c, 0x9a, 0x6a, 0xfc, 0xfe, 0x01, 0xe2, 0x19, 0x81, 0x70,
+ 0x07, 0x25, 0x32, 0xb0, 0xf0, 0x6f, 0x3b, 0xc7, 0x6a, 0x28, 0x3d, 0xe4,
+ 0x51, 0x87, 0xe6, 0x7e, 0x82, 0xec, 0xae, 0x48, 0xa7, 0xb1, 0x77, 0x38,
+ 0xc2, 0xd6, 0x56, 0xaf, 0x8f, 0xf2, 0x01, 0xfc, 0x65, 0x65, 0x10, 0x09,
+ 0xf7, 0x74, 0x29, 0xb5, 0x0e, 0x92, 0xee, 0x90, 0x98, 0xd1, 0x88, 0xa2,
+ 0x65, 0xb7, 0xcd, 0x9c, 0x0e, 0xa7, 0x86, 0x98, 0x28, 0xbc, 0xae, 0x15,
+ 0x83, 0xb6, 0x1a, 0xd7, 0x1d, 0xec, 0x19, 0xda, 0x7a, 0x8e, 0x40, 0xf9,
+ 0x99, 0x15, 0xd5, 0x7d, 0xa5, 0xba, 0xab, 0xfd, 0x26, 0x98, 0x6e, 0x9c,
+ 0x41, 0x3b, 0xb6, 0x81, 0x18, 0xec, 0x70, 0x48, 0xd7, 0x6e, 0x7f, 0xa6,
+ 0xe1, 0x77, 0x25, 0xd6, 0xdd, 0x62, 0xe8, 0x52, 0xf3, 0x8c, 0x16, 0x39,
+ 0x67, 0xe2, 0x22, 0x0d, 0x77, 0x2e, 0xfb, 0x11, 0x6c, 0xe4, 0xdd, 0x38,
+ 0xb4, 0x27, 0x5f, 0x03, 0xa8, 0x3d, 0x44, 0xe2, 0xf2, 0x84, 0x4b, 0x84,
+ 0xfd, 0x56, 0xa6, 0x9e, 0x4d, 0x7b, 0xa2, 0x16, 0x4f, 0x07, 0xf5, 0x34,
+ 0x24, 0x72, 0xa5, 0xa2, 0xfa, 0x16, 0x66, 0x2a, 0xa4, 0x4a, 0x0e, 0xc8,
+ 0x0d, 0x27, 0x44, 0x9c, 0x77, 0xd4, 0x12, 0x10, 0x87, 0xd2, 0x00, 0x2c,
+ 0x7a, 0xbb, 0x8e, 0x88, 0x22, 0x91, 0x15, 0xbe, 0xa2, 0x59, 0xca, 0x34,
+ 0xe0, 0x1c, 0x61, 0x94, 0x86, 0x20, 0x33, 0xcd, 0xe7, 0x4c, 0x5d, 0x3b,
+ 0x92, 0x3e, 0xcb, 0xd6, 0x2d, 0xea, 0x54, 0xfa, 0xfb, 0xaf, 0x54, 0xf5,
+ 0xa8, 0xc5, 0x0b, 0xca, 0x8b, 0x87, 0x00, 0xe6, 0x9f, 0xe6, 0x95, 0xbf,
+ 0xb7, 0xc4, 0xa3, 0x59, 0xf5, 0x16, 0x6c, 0x5f, 0x3e, 0x69, 0x55, 0x80,
+ 0x39, 0xf6, 0x75, 0x50, 0x14, 0x3e, 0x32,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 7e:e1:4a:6f:6f:ef:f2:d3:7f:3f:ad:65:4d:3a:da:b4
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 EV SSL CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d8:a1:65:74:23:e8:2b:64:e2:32:d7:33:37:3d:
+ 8e:f5:34:16:48:dd:4f:7f:87:1c:f8:44:23:13:8e:
+ fb:11:d8:44:5a:18:71:8e:60:16:26:92:9b:fd:17:
+ 0b:e1:71:70:42:fe:bf:fa:1c:c0:aa:a3:a7:b5:71:
+ e8:ff:18:83:f6:df:10:0a:13:62:c8:3d:9c:a7:de:
+ 2e:3f:0c:d9:1d:e7:2e:fb:2a:ce:c8:9a:7f:87:bf:
+ d8:4c:04:15:32:c9:d1:cc:95:71:a0:4e:28:4f:84:
+ d9:35:fb:e3:86:6f:94:53:e6:72:8a:63:67:2e:be:
+ 69:f6:f7:6e:8e:9c:60:04:eb:29:fa:c4:47:42:d2:
+ 78:98:e3:ec:0b:a5:92:dc:b7:9a:bd:80:64:2b:38:
+ 7c:38:09:5b:66:f6:2d:95:7a:86:b2:34:2e:85:9e:
+ 90:0e:5f:b7:5d:a4:51:72:46:70:13:bf:67:f2:b6:
+ a7:4d:14:1e:6c:b9:53:ee:23:1a:4e:8d:48:55:43:
+ 41:b1:89:75:6a:40:28:c5:7d:dd:d2:6e:d2:02:19:
+ 2f:7b:24:94:4b:eb:f1:1a:a9:9b:e3:23:9a:ea:fa:
+ 33:ab:0a:2c:b7:f4:60:08:dd:9f:1c:cd:dd:2d:01:
+ 66:80:af:b3:2f:29:1d:23:b8:8a:e1:a1:70:07:0c:
+ 34:0f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://s2.symcb.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.symauth.com/cps
+ User Notice:
+ Explicit Text: http://www.symauth.com/rpa
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://s1.symcb.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-533
+ X509v3 Subject Key Identifier:
+ 01:59:AB:E7:DD:3A:0B:59:A6:64:63:D6:CF:20:07:57:D5:91:E7:6A
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 42:01:55:7b:d0:16:1a:5d:58:e8:bb:9b:a8:4d:d7:f3:d7:eb:
+ 13:94:86:d6:7f:21:0b:47:bc:57:9b:92:5d:4f:05:9f:38:a4:
+ 10:7c:cf:83:be:06:43:46:8d:08:bc:6a:d7:10:a6:fa:ab:af:
+ 2f:61:a8:63:f2:65:df:7f:4c:88:12:88:4f:b3:69:d9:ff:27:
+ c0:0a:97:91:8f:56:fb:89:c4:a8:bb:92:2d:1b:73:b0:c6:ab:
+ 36:f4:96:6c:20:08:ef:0a:1e:66:24:45:4f:67:00:40:c8:07:
+ 54:74:33:3b:a6:ad:bb:23:9f:66:ed:a2:44:70:34:fb:0e:ea:
+ 01:fd:cf:78:74:df:a7:ad:55:b7:5f:4d:f6:d6:3f:e0:86:ce:
+ 24:c7:42:a9:13:14:44:35:4b:b6:df:c9:60:ac:0c:7f:d9:93:
+ 21:4b:ee:9c:e4:49:02:98:d3:60:7b:5c:bc:d5:30:2f:07:ce:
+ 44:42:c4:0b:99:fe:e6:9f:fc:b0:78:86:51:6d:d1:2c:9d:c6:
+ 96:fb:85:82:bb:04:2f:f7:62:80:ef:62:da:7f:f6:0e:ac:90:
+ b8:56:bd:79:3f:f2:80:6e:a3:d9:b9:0f:5d:3a:07:1d:91:93:
+ 86:4b:29:4c:e1:dc:b5:e1:e0:33:9d:b3:cb:36:91:4b:fe:a1:
+ b4:ee:f0:f9
+-----BEGIN CERTIFICATE-----
+MIIFKzCCBBOgAwIBAgIQfuFKb2/v8tN/P61lTTratDANBgkqhkiG9w0BAQsFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB3MQsw
+CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
+BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIENs
+YXNzIDMgRVYgU1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDYoWV0I+grZOIy1zM3PY71NBZI3U9/hxz4RCMTjvsR2ERaGHGOYBYmkpv9
+FwvhcXBC/r/6HMCqo6e1cej/GIP23xAKE2LIPZyn3i4/DNkd5y77Ks7Imn+Hv9hM
+BBUyydHMlXGgTihPhNk1++OGb5RT5nKKY2cuvmn2926OnGAE6yn6xEdC0niY4+wL
+pZLct5q9gGQrOHw4CVtm9i2VeoayNC6FnpAOX7ddpFFyRnATv2fytqdNFB5suVPu
+IxpOjUhVQ0GxiXVqQCjFfd3SbtICGS97JJRL6/EaqZvjI5rq+jOrCiy39GAI3Z8c
+zd0tAWaAr7MvKR0juIrhoXAHDDQPAgMBAAGjggFdMIIBWTAvBggrBgEFBQcBAQQj
+MCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wEgYDVR0TAQH/BAgw
+BgEB/wIBADBlBgNVHSAEXjBcMFoGBFUdIAAwUjAmBggrBgEFBQcCARYaaHR0cDov
+L3d3dy5zeW1hdXRoLmNvbS9jcHMwKAYIKwYBBQUHAgIwHBoaaHR0cDovL3d3dy5z
+eW1hdXRoLmNvbS9ycGEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3MxLnN5bWNi
+LmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwx
+GjAYBgNVBAMTEVN5bWFudGVjUEtJLTEtNTMzMB0GA1UdDgQWBBQBWavn3ToLWaZk
+Y9bPIAdX1ZHnajAfBgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkq
+hkiG9w0BAQsFAAOCAQEAQgFVe9AWGl1Y6LubqE3X89frE5SG1n8hC0e8V5uSXU8F
+nzikEHzPg74GQ0aNCLxq1xCm+quvL2GoY/Jl339MiBKIT7Np2f8nwAqXkY9W+4nE
+qLuSLRtzsMarNvSWbCAI7woeZiRFT2cAQMgHVHQzO6atuyOfZu2iRHA0+w7qAf3P
+eHTfp61Vt19N9tY/4IbOJMdCqRMURDVLtt/JYKwMf9mTIUvunORJApjTYHtcvNUw
+LwfORELEC5n+5p/8sHiGUW3RLJ3GlvuFgrsEL/digO9i2n/2DqyQuFa9eT/ygG6j
+2bkPXToHHZGThkspTOHcteHgM52zyzaRS/6htO7w+Q==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert44[] = {
+ 0x30, 0x82, 0x05, 0x2b, 0x30, 0x82, 0x04, 0x13, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x7e, 0xe1, 0x4a, 0x6f, 0x6f, 0xef, 0xf2, 0xd3, 0x7f,
+ 0x3f, 0xad, 0x65, 0x4d, 0x3a, 0xda, 0xb4, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, 0x30, 0x33, 0x30,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x77, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d,
+ 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d,
+ 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63,
+ 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f,
+ 0x72, 0x6b, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x1f, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x56, 0x20, 0x53, 0x53, 0x4c,
+ 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xd8, 0xa1, 0x65, 0x74, 0x23, 0xe8, 0x2b,
+ 0x64, 0xe2, 0x32, 0xd7, 0x33, 0x37, 0x3d, 0x8e, 0xf5, 0x34, 0x16, 0x48,
+ 0xdd, 0x4f, 0x7f, 0x87, 0x1c, 0xf8, 0x44, 0x23, 0x13, 0x8e, 0xfb, 0x11,
+ 0xd8, 0x44, 0x5a, 0x18, 0x71, 0x8e, 0x60, 0x16, 0x26, 0x92, 0x9b, 0xfd,
+ 0x17, 0x0b, 0xe1, 0x71, 0x70, 0x42, 0xfe, 0xbf, 0xfa, 0x1c, 0xc0, 0xaa,
+ 0xa3, 0xa7, 0xb5, 0x71, 0xe8, 0xff, 0x18, 0x83, 0xf6, 0xdf, 0x10, 0x0a,
+ 0x13, 0x62, 0xc8, 0x3d, 0x9c, 0xa7, 0xde, 0x2e, 0x3f, 0x0c, 0xd9, 0x1d,
+ 0xe7, 0x2e, 0xfb, 0x2a, 0xce, 0xc8, 0x9a, 0x7f, 0x87, 0xbf, 0xd8, 0x4c,
+ 0x04, 0x15, 0x32, 0xc9, 0xd1, 0xcc, 0x95, 0x71, 0xa0, 0x4e, 0x28, 0x4f,
+ 0x84, 0xd9, 0x35, 0xfb, 0xe3, 0x86, 0x6f, 0x94, 0x53, 0xe6, 0x72, 0x8a,
+ 0x63, 0x67, 0x2e, 0xbe, 0x69, 0xf6, 0xf7, 0x6e, 0x8e, 0x9c, 0x60, 0x04,
+ 0xeb, 0x29, 0xfa, 0xc4, 0x47, 0x42, 0xd2, 0x78, 0x98, 0xe3, 0xec, 0x0b,
+ 0xa5, 0x92, 0xdc, 0xb7, 0x9a, 0xbd, 0x80, 0x64, 0x2b, 0x38, 0x7c, 0x38,
+ 0x09, 0x5b, 0x66, 0xf6, 0x2d, 0x95, 0x7a, 0x86, 0xb2, 0x34, 0x2e, 0x85,
+ 0x9e, 0x90, 0x0e, 0x5f, 0xb7, 0x5d, 0xa4, 0x51, 0x72, 0x46, 0x70, 0x13,
+ 0xbf, 0x67, 0xf2, 0xb6, 0xa7, 0x4d, 0x14, 0x1e, 0x6c, 0xb9, 0x53, 0xee,
+ 0x23, 0x1a, 0x4e, 0x8d, 0x48, 0x55, 0x43, 0x41, 0xb1, 0x89, 0x75, 0x6a,
+ 0x40, 0x28, 0xc5, 0x7d, 0xdd, 0xd2, 0x6e, 0xd2, 0x02, 0x19, 0x2f, 0x7b,
+ 0x24, 0x94, 0x4b, 0xeb, 0xf1, 0x1a, 0xa9, 0x9b, 0xe3, 0x23, 0x9a, 0xea,
+ 0xfa, 0x33, 0xab, 0x0a, 0x2c, 0xb7, 0xf4, 0x60, 0x08, 0xdd, 0x9f, 0x1c,
+ 0xcd, 0xdd, 0x2d, 0x01, 0x66, 0x80, 0xaf, 0xb3, 0x2f, 0x29, 0x1d, 0x23,
+ 0xb8, 0x8a, 0xe1, 0xa1, 0x70, 0x07, 0x0c, 0x34, 0x0f, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5d, 0x30, 0x82, 0x01, 0x59, 0x30, 0x2f,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23,
+ 0x30, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
+ 0x32, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x65, 0x06, 0x03, 0x55,
+ 0x1d, 0x20, 0x04, 0x5e, 0x30, 0x5c, 0x30, 0x5a, 0x06, 0x04, 0x55, 0x1d,
+ 0x20, 0x00, 0x30, 0x52, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73,
+ 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72,
+ 0x70, 0x61, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30,
+ 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x29, 0x06, 0x03,
+ 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31,
+ 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53, 0x79,
+ 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31, 0x2d,
+ 0x35, 0x33, 0x33, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0x01, 0x59, 0xab, 0xe7, 0xdd, 0x3a, 0x0b, 0x59, 0xa6, 0x64,
+ 0x63, 0xd6, 0xcf, 0x20, 0x07, 0x57, 0xd5, 0x91, 0xe7, 0x6a, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f,
+ 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43,
+ 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x42, 0x01, 0x55, 0x7b, 0xd0, 0x16, 0x1a, 0x5d, 0x58,
+ 0xe8, 0xbb, 0x9b, 0xa8, 0x4d, 0xd7, 0xf3, 0xd7, 0xeb, 0x13, 0x94, 0x86,
+ 0xd6, 0x7f, 0x21, 0x0b, 0x47, 0xbc, 0x57, 0x9b, 0x92, 0x5d, 0x4f, 0x05,
+ 0x9f, 0x38, 0xa4, 0x10, 0x7c, 0xcf, 0x83, 0xbe, 0x06, 0x43, 0x46, 0x8d,
+ 0x08, 0xbc, 0x6a, 0xd7, 0x10, 0xa6, 0xfa, 0xab, 0xaf, 0x2f, 0x61, 0xa8,
+ 0x63, 0xf2, 0x65, 0xdf, 0x7f, 0x4c, 0x88, 0x12, 0x88, 0x4f, 0xb3, 0x69,
+ 0xd9, 0xff, 0x27, 0xc0, 0x0a, 0x97, 0x91, 0x8f, 0x56, 0xfb, 0x89, 0xc4,
+ 0xa8, 0xbb, 0x92, 0x2d, 0x1b, 0x73, 0xb0, 0xc6, 0xab, 0x36, 0xf4, 0x96,
+ 0x6c, 0x20, 0x08, 0xef, 0x0a, 0x1e, 0x66, 0x24, 0x45, 0x4f, 0x67, 0x00,
+ 0x40, 0xc8, 0x07, 0x54, 0x74, 0x33, 0x3b, 0xa6, 0xad, 0xbb, 0x23, 0x9f,
+ 0x66, 0xed, 0xa2, 0x44, 0x70, 0x34, 0xfb, 0x0e, 0xea, 0x01, 0xfd, 0xcf,
+ 0x78, 0x74, 0xdf, 0xa7, 0xad, 0x55, 0xb7, 0x5f, 0x4d, 0xf6, 0xd6, 0x3f,
+ 0xe0, 0x86, 0xce, 0x24, 0xc7, 0x42, 0xa9, 0x13, 0x14, 0x44, 0x35, 0x4b,
+ 0xb6, 0xdf, 0xc9, 0x60, 0xac, 0x0c, 0x7f, 0xd9, 0x93, 0x21, 0x4b, 0xee,
+ 0x9c, 0xe4, 0x49, 0x02, 0x98, 0xd3, 0x60, 0x7b, 0x5c, 0xbc, 0xd5, 0x30,
+ 0x2f, 0x07, 0xce, 0x44, 0x42, 0xc4, 0x0b, 0x99, 0xfe, 0xe6, 0x9f, 0xfc,
+ 0xb0, 0x78, 0x86, 0x51, 0x6d, 0xd1, 0x2c, 0x9d, 0xc6, 0x96, 0xfb, 0x85,
+ 0x82, 0xbb, 0x04, 0x2f, 0xf7, 0x62, 0x80, 0xef, 0x62, 0xda, 0x7f, 0xf6,
+ 0x0e, 0xac, 0x90, 0xb8, 0x56, 0xbd, 0x79, 0x3f, 0xf2, 0x80, 0x6e, 0xa3,
+ 0xd9, 0xb9, 0x0f, 0x5d, 0x3a, 0x07, 0x1d, 0x91, 0x93, 0x86, 0x4b, 0x29,
+ 0x4c, 0xe1, 0xdc, 0xb5, 0xe1, 0xe0, 0x33, 0x9d, 0xb3, 0xcb, 0x36, 0x91,
+ 0x4b, 0xfe, 0xa1, 0xb4, 0xee, 0xf0, 0xf9,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 51:3f:b9:74:38:70:b7:34:40:41:8d:30:93:06:99:ff
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Oct 31 00:00:00 2013 GMT
+ Not After : Oct 30 23:59:59 2023 GMT
+ Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 Secure Server CA - G4
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b2:d8:05:ca:1c:74:2d:b5:17:56:39:c5:4a:52:
+ 09:96:e8:4b:d8:0c:f1:68:9f:9a:42:28:62:c3:a5:
+ 30:53:7e:55:11:82:5b:03:7a:0d:2f:e1:79:04:c9:
+ b4:96:77:19:81:01:94:59:f9:bc:f7:7a:99:27:82:
+ 2d:b7:83:dd:5a:27:7f:b2:03:7a:9c:53:25:e9:48:
+ 1f:46:4f:c8:9d:29:f8:be:79:56:f6:f7:fd:d9:3a:
+ 68:da:8b:4b:82:33:41:12:c3:c8:3c:cc:d6:96:7a:
+ 84:21:1a:22:04:03:27:17:8b:1c:68:61:93:0f:0e:
+ 51:80:33:1d:b4:b5:ce:eb:7e:d0:62:ac:ee:b3:7b:
+ 01:74:ef:69:35:eb:ca:d5:3d:a9:ee:97:98:ca:8d:
+ aa:44:0e:25:99:4a:15:96:a4:ce:6d:02:54:1f:2a:
+ 6a:26:e2:06:3a:63:48:ac:b4:4c:d1:75:93:50:ff:
+ 13:2f:d6:da:e1:c6:18:f5:9f:c9:25:5d:f3:00:3a:
+ de:26:4d:b4:29:09:cd:0f:3d:23:6f:16:4a:81:16:
+ fb:f2:83:10:c3:b8:d6:d8:55:32:3d:f1:bd:0f:bd:
+ 8c:52:95:4a:16:97:7a:52:21:63:75:2f:16:f9:c4:
+ 66:be:f5:b5:09:d8:ff:27:00:cd:44:7c:6f:4b:3f:
+ b0:f7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://s1.symcb.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://s2.symcb.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.symauth.com/cps
+ User Notice:
+ Explicit Text: http://www.symauth.com/rpa
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=SymantecPKI-1-534
+ X509v3 Subject Key Identifier:
+ 5F:60:CF:61:90:55:DF:84:43:14:8A:60:2A:B2:F5:7A:F4:43:18:EF
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 5e:94:56:49:dd:8e:2d:65:f5:c1:36:51:b6:03:e3:da:9e:73:
+ 19:f2:1f:59:ab:58:7e:6c:26:05:2c:fa:81:d7:5c:23:17:22:
+ 2c:37:93:f7:86:ec:85:e6:b0:a3:fd:1f:e2:32:a8:45:6f:e1:
+ d9:fb:b9:af:d2:70:a0:32:42:65:bf:84:fe:16:2a:8f:3f:c5:
+ a6:d6:a3:93:7d:43:e9:74:21:91:35:28:f4:63:e9:2e:ed:f7:
+ f5:5c:7f:4b:9a:b5:20:e9:0a:bd:e0:45:10:0c:14:94:9a:5d:
+ a5:e3:4b:91:e8:24:9b:46:40:65:f4:22:72:cd:99:f8:88:11:
+ f5:f3:7f:e6:33:82:e6:a8:c5:7e:fe:d0:08:e2:25:58:08:71:
+ 68:e6:cd:a2:e6:14:de:4e:52:24:2d:fd:e5:79:13:53:e7:5e:
+ 2f:2d:4d:1b:6d:40:15:52:2b:f7:87:89:78:12:81:6e:d9:4d:
+ aa:2d:78:d4:c2:2c:3d:08:5f:87:91:9e:1f:0e:b0:de:30:52:
+ 64:86:89:aa:9d:66:9c:0e:76:0c:80:f2:74:d8:2a:f8:b8:3a:
+ ce:d7:d6:0f:11:be:6b:ab:14:f5:bd:41:a0:22:63:89:f1:ba:
+ 0f:6f:29:63:66:2d:3f:ac:8c:72:c5:fb:c7:e4:d4:0f:f2:3b:
+ 4f:8c:29:c7
+-----BEGIN CERTIFICATE-----
+MIIFODCCBCCgAwIBAgIQUT+5dDhwtzRAQY0wkwaZ/zANBgkqhkiG9w0BAQsFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB+MQsw
+CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
+BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxLzAtBgNVBAMTJlN5bWFudGVjIENs
+YXNzIDMgU2VjdXJlIFNlcnZlciBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAstgFyhx0LbUXVjnFSlIJluhL2AzxaJ+aQihiw6UwU35VEYJb
+A3oNL+F5BMm0lncZgQGUWfm893qZJ4Itt4PdWid/sgN6nFMl6UgfRk/InSn4vnlW
+9vf92Tpo2otLgjNBEsPIPMzWlnqEIRoiBAMnF4scaGGTDw5RgDMdtLXO637QYqzu
+s3sBdO9pNevK1T2p7peYyo2qRA4lmUoVlqTObQJUHypqJuIGOmNIrLRM0XWTUP8T
+L9ba4cYY9Z/JJV3zADreJk20KQnNDz0jbxZKgRb78oMQw7jW2FUyPfG9D72MUpVK
+Fpd6UiFjdS8W+cRmvvW1Cdj/JwDNRHxvSz+w9wIDAQABo4IBYzCCAV8wEgYDVR0T
+AQH/BAgwBgEB/wIBADAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vczEuc3ltY2Iu
+Y29tL3BjYTMtZzUuY3JsMA4GA1UdDwEB/wQEAwIBBjAvBggrBgEFBQcBAQQjMCEw
+HwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1jYi5jb20wawYDVR0gBGQwYjBgBgpg
+hkgBhvhFAQc2MFIwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20v
+Y3BzMCgGCCsGAQUFBwICMBwaGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20vcnBhMCkG
+A1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTUzNDAdBgNVHQ4E
+FgQUX2DPYZBV34RDFIpgKrL1evRDGO8wHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnz
+Qzn6Aq8zMTMwDQYJKoZIhvcNAQELBQADggEBAF6UVkndji1l9cE2UbYD49qecxny
+H1mrWH5sJgUs+oHXXCMXIiw3k/eG7IXmsKP9H+IyqEVv4dn7ua/ScKAyQmW/hP4W
+Ko8/xabWo5N9Q+l0IZE1KPRj6S7t9/Vcf0uatSDpCr3gRRAMFJSaXaXjS5HoJJtG
+QGX0InLNmfiIEfXzf+YzguaoxX7+0AjiJVgIcWjmzaLmFN5OUiQt/eV5E1PnXi8t
+TRttQBVSK/eHiXgSgW7ZTaoteNTCLD0IX4eRnh8OsN4wUmSGiaqdZpwOdgyA8nTY
+Kvi4Os7X1g8RvmurFPW9QaAiY4nxug9vKWNmLT+sjHLF+8fk1A/yO0+MKcc=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert45[] = {
+ 0x30, 0x82, 0x05, 0x38, 0x30, 0x82, 0x04, 0x20, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x51, 0x3f, 0xb9, 0x74, 0x38, 0x70, 0xb7, 0x34, 0x40,
+ 0x41, 0x8d, 0x30, 0x93, 0x06, 0x99, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x31, 0x30, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x31, 0x30, 0x33, 0x30,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x7e, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d,
+ 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d,
+ 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63,
+ 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f,
+ 0x72, 0x6b, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x26, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65,
+ 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x2d,
+ 0x20, 0x47, 0x34, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xb2, 0xd8, 0x05, 0xca, 0x1c, 0x74, 0x2d, 0xb5, 0x17, 0x56, 0x39, 0xc5,
+ 0x4a, 0x52, 0x09, 0x96, 0xe8, 0x4b, 0xd8, 0x0c, 0xf1, 0x68, 0x9f, 0x9a,
+ 0x42, 0x28, 0x62, 0xc3, 0xa5, 0x30, 0x53, 0x7e, 0x55, 0x11, 0x82, 0x5b,
+ 0x03, 0x7a, 0x0d, 0x2f, 0xe1, 0x79, 0x04, 0xc9, 0xb4, 0x96, 0x77, 0x19,
+ 0x81, 0x01, 0x94, 0x59, 0xf9, 0xbc, 0xf7, 0x7a, 0x99, 0x27, 0x82, 0x2d,
+ 0xb7, 0x83, 0xdd, 0x5a, 0x27, 0x7f, 0xb2, 0x03, 0x7a, 0x9c, 0x53, 0x25,
+ 0xe9, 0x48, 0x1f, 0x46, 0x4f, 0xc8, 0x9d, 0x29, 0xf8, 0xbe, 0x79, 0x56,
+ 0xf6, 0xf7, 0xfd, 0xd9, 0x3a, 0x68, 0xda, 0x8b, 0x4b, 0x82, 0x33, 0x41,
+ 0x12, 0xc3, 0xc8, 0x3c, 0xcc, 0xd6, 0x96, 0x7a, 0x84, 0x21, 0x1a, 0x22,
+ 0x04, 0x03, 0x27, 0x17, 0x8b, 0x1c, 0x68, 0x61, 0x93, 0x0f, 0x0e, 0x51,
+ 0x80, 0x33, 0x1d, 0xb4, 0xb5, 0xce, 0xeb, 0x7e, 0xd0, 0x62, 0xac, 0xee,
+ 0xb3, 0x7b, 0x01, 0x74, 0xef, 0x69, 0x35, 0xeb, 0xca, 0xd5, 0x3d, 0xa9,
+ 0xee, 0x97, 0x98, 0xca, 0x8d, 0xaa, 0x44, 0x0e, 0x25, 0x99, 0x4a, 0x15,
+ 0x96, 0xa4, 0xce, 0x6d, 0x02, 0x54, 0x1f, 0x2a, 0x6a, 0x26, 0xe2, 0x06,
+ 0x3a, 0x63, 0x48, 0xac, 0xb4, 0x4c, 0xd1, 0x75, 0x93, 0x50, 0xff, 0x13,
+ 0x2f, 0xd6, 0xda, 0xe1, 0xc6, 0x18, 0xf5, 0x9f, 0xc9, 0x25, 0x5d, 0xf3,
+ 0x00, 0x3a, 0xde, 0x26, 0x4d, 0xb4, 0x29, 0x09, 0xcd, 0x0f, 0x3d, 0x23,
+ 0x6f, 0x16, 0x4a, 0x81, 0x16, 0xfb, 0xf2, 0x83, 0x10, 0xc3, 0xb8, 0xd6,
+ 0xd8, 0x55, 0x32, 0x3d, 0xf1, 0xbd, 0x0f, 0xbd, 0x8c, 0x52, 0x95, 0x4a,
+ 0x16, 0x97, 0x7a, 0x52, 0x21, 0x63, 0x75, 0x2f, 0x16, 0xf9, 0xc4, 0x66,
+ 0xbe, 0xf5, 0xb5, 0x09, 0xd8, 0xff, 0x27, 0x00, 0xcd, 0x44, 0x7c, 0x6f,
+ 0x4b, 0x3f, 0xb0, 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0x63, 0x30, 0x82, 0x01, 0x5f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x29, 0x30, 0x27,
+ 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x73, 0x31, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2f, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x23, 0x30, 0x21, 0x30,
+ 0x1f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86,
+ 0x13, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73, 0x32, 0x2e, 0x73,
+ 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x6b, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x64, 0x30, 0x62, 0x30, 0x60, 0x06, 0x0a, 0x60,
+ 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x36, 0x30, 0x52, 0x30,
+ 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74,
+ 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x29, 0x06,
+ 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c,
+ 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x53,
+ 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x50, 0x4b, 0x49, 0x2d, 0x31,
+ 0x2d, 0x35, 0x33, 0x34, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x5f, 0x60, 0xcf, 0x61, 0x90, 0x55, 0xdf, 0x84, 0x43,
+ 0x14, 0x8a, 0x60, 0x2a, 0xb2, 0xf5, 0x7a, 0xf4, 0x43, 0x18, 0xef, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x5e, 0x94, 0x56, 0x49, 0xdd, 0x8e, 0x2d, 0x65,
+ 0xf5, 0xc1, 0x36, 0x51, 0xb6, 0x03, 0xe3, 0xda, 0x9e, 0x73, 0x19, 0xf2,
+ 0x1f, 0x59, 0xab, 0x58, 0x7e, 0x6c, 0x26, 0x05, 0x2c, 0xfa, 0x81, 0xd7,
+ 0x5c, 0x23, 0x17, 0x22, 0x2c, 0x37, 0x93, 0xf7, 0x86, 0xec, 0x85, 0xe6,
+ 0xb0, 0xa3, 0xfd, 0x1f, 0xe2, 0x32, 0xa8, 0x45, 0x6f, 0xe1, 0xd9, 0xfb,
+ 0xb9, 0xaf, 0xd2, 0x70, 0xa0, 0x32, 0x42, 0x65, 0xbf, 0x84, 0xfe, 0x16,
+ 0x2a, 0x8f, 0x3f, 0xc5, 0xa6, 0xd6, 0xa3, 0x93, 0x7d, 0x43, 0xe9, 0x74,
+ 0x21, 0x91, 0x35, 0x28, 0xf4, 0x63, 0xe9, 0x2e, 0xed, 0xf7, 0xf5, 0x5c,
+ 0x7f, 0x4b, 0x9a, 0xb5, 0x20, 0xe9, 0x0a, 0xbd, 0xe0, 0x45, 0x10, 0x0c,
+ 0x14, 0x94, 0x9a, 0x5d, 0xa5, 0xe3, 0x4b, 0x91, 0xe8, 0x24, 0x9b, 0x46,
+ 0x40, 0x65, 0xf4, 0x22, 0x72, 0xcd, 0x99, 0xf8, 0x88, 0x11, 0xf5, 0xf3,
+ 0x7f, 0xe6, 0x33, 0x82, 0xe6, 0xa8, 0xc5, 0x7e, 0xfe, 0xd0, 0x08, 0xe2,
+ 0x25, 0x58, 0x08, 0x71, 0x68, 0xe6, 0xcd, 0xa2, 0xe6, 0x14, 0xde, 0x4e,
+ 0x52, 0x24, 0x2d, 0xfd, 0xe5, 0x79, 0x13, 0x53, 0xe7, 0x5e, 0x2f, 0x2d,
+ 0x4d, 0x1b, 0x6d, 0x40, 0x15, 0x52, 0x2b, 0xf7, 0x87, 0x89, 0x78, 0x12,
+ 0x81, 0x6e, 0xd9, 0x4d, 0xaa, 0x2d, 0x78, 0xd4, 0xc2, 0x2c, 0x3d, 0x08,
+ 0x5f, 0x87, 0x91, 0x9e, 0x1f, 0x0e, 0xb0, 0xde, 0x30, 0x52, 0x64, 0x86,
+ 0x89, 0xaa, 0x9d, 0x66, 0x9c, 0x0e, 0x76, 0x0c, 0x80, 0xf2, 0x74, 0xd8,
+ 0x2a, 0xf8, 0xb8, 0x3a, 0xce, 0xd7, 0xd6, 0x0f, 0x11, 0xbe, 0x6b, 0xab,
+ 0x14, 0xf5, 0xbd, 0x41, 0xa0, 0x22, 0x63, 0x89, 0xf1, 0xba, 0x0f, 0x6f,
+ 0x29, 0x63, 0x66, 0x2d, 0x3f, 0xac, 0x8c, 0x72, 0xc5, 0xfb, 0xc7, 0xe4,
+ 0xd4, 0x0f, 0xf2, 0x3b, 0x4f, 0x8c, 0x29, 0xc7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 69:87:94:19:d9:e3:62:70:74:9d:bb:e5:9d:c6:68:5e
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2008 VeriSign, Inc. - For authorized use only, CN=VeriSign Universal Root Certification Authority
+ Validity
+ Not Before: Apr 9 00:00:00 2013 GMT
+ Not After : Apr 8 23:59:59 2023 GMT
+ Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 Secure Server SHA256 SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:be:38:16:51:8b:80:db:ea:0e:4d:ec:e8:3f:5c:
+ c4:7c:a2:5d:ed:3b:af:a5:d6:9e:10:35:2c:e3:c5:
+ e5:a8:de:8c:86:17:26:e6:de:0b:51:4a:2c:d0:fb:
+ d1:14:5a:72:f7:c9:dd:b8:83:1c:c6:46:8c:31:25:
+ 91:0e:59:17:a3:d0:13:8c:92:c1:af:81:54:4e:bc:
+ 62:02:9e:aa:a7:1a:57:d8:ca:a6:99:7a:70:56:4f:
+ 98:07:2e:4b:96:d0:4c:39:53:b9:61:2f:3b:76:7c:
+ 8e:05:9e:99:44:d1:03:54:77:29:2b:56:2a:aa:61:
+ e4:84:2f:12:15:3c:bd:d7:8a:e8:09:1e:56:f1:b5:
+ 14:ac:8a:84:ce:ae:78:a2:60:0a:53:7e:13:4c:1a:
+ 40:70:0e:52:59:ff:5a:68:2e:4c:46:13:3b:39:09:
+ 82:78:02:35:49:20:08:82:b3:b1:6c:89:0f:6e:1e:
+ 35:25:b0:2c:24:83:e3:c5:50:2c:ba:46:90:45:87:
+ 0d:72:ff:5d:11:38:c5:91:76:c5:2c:fb:05:2a:82:
+ 95:a1:59:63:e3:d0:26:58:cd:67:56:3a:ba:df:7c:
+ d2:d2:3b:d8:de:1a:7a:77:e4:0c:8c:0b:eb:2b:c2:
+ 22:b0:bd:55:ba:d9:b9:55:d1:22:7a:c6:02:4e:3f:
+ c3:35
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.ws.symantec.com/universal-root.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://ocsp.ws.symantec.com
+
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.54
+ CPS: http://www.symauth.com/cps
+ User Notice:
+ Explicit Text: http://www.symauth.com/rpa
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-373
+ X509v3 Subject Key Identifier:
+ DB:62:20:FB:7D:02:89:7C:D2:3B:6F:C7:E4:32:6C:05:52:1D:AD:B1
+ X509v3 Authority Key Identifier:
+ keyid:B6:77:FA:69:48:47:9F:53:12:D5:C2:EA:07:32:76:07:D1:97:07:19
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 19:cc:95:e2:2f:7b:49:d0:48:90:53:f4:07:b1:20:44:35:70:
+ 14:d5:44:37:31:ef:ef:70:d1:2d:4c:e9:2d:b0:53:91:01:4c:
+ 54:e7:7d:9b:da:3a:ff:b7:cb:14:ad:30:0f:69:1a:2a:f0:bc:
+ cd:35:eb:48:dc:b9:87:fd:cf:b1:5a:f6:05:da:3c:64:e6:2b:
+ e6:dc:73:5e:9a:d8:0c:9b:d2:97:b3:e8:fa:87:95:53:e1:99:
+ ad:88:e8:fa:bc:09:4d:a2:c4:6a:1b:28:3b:2d:c3:21:15:ee:
+ 14:fa:9d:98:10:eb:9f:3e:e6:24:24:5f:7a:1c:05:bb:9a:31:
+ 23:58:79:4c:ec:6d:18:19:4d:51:1f:08:61:bd:91:05:0c:5a:
+ 9c:26:fc:0b:a5:20:25:bf:6a:1b:2b:f7:02:09:72:69:83:32:
+ 14:c3:60:5b:7e:fd:9a:32:fa:b4:95:0e:1a:f9:3b:09:a4:54:
+ 47:9a:0c:ce:32:af:d1:21:cc:7f:d2:06:ef:60:0e:62:6f:6f:
+ 81:1a:17:9d:c8:cb:28:cc:e2:5f:6e:2c:7a:b4:cb:47:7c:74:
+ 68:7b:48:71:02:9c:23:09:f3:5a:ae:5f:42:2e:5f:2b:59:2d:
+ 52:88:e5:8d:0b:b3:a8:61:f9:4b:9b:55:d6:da:b1:92:3b:bf:
+ c3:9b:f9:2c
+-----BEGIN CERTIFICATE-----
+MIIFSTCCBDGgAwIBAgIQaYeUGdnjYnB0nbvlncZoXjANBgkqhkiG9w0BAQsFADCB
+vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
+ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
+Fw0xMzA0MDkwMDAwMDBaFw0yMzA0MDgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEd
+MBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVj
+IFRydXN0IE5ldHdvcmsxNTAzBgNVBAMTLFN5bWFudGVjIENsYXNzIDMgU2VjdXJl
+IFNlcnZlciBTSEEyNTYgU1NMIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAvjgWUYuA2+oOTezoP1zEfKJd7TuvpdaeEDUs48XlqN6Mhhcm5t4LUUos
+0PvRFFpy98nduIMcxkaMMSWRDlkXo9ATjJLBr4FUTrxiAp6qpxpX2MqmmXpwVk+Y
+By5LltBMOVO5YS87dnyOBZ6ZRNEDVHcpK1YqqmHkhC8SFTy914roCR5W8bUUrIqE
+zq54omAKU34TTBpAcA5SWf9aaC5MRhM7OQmCeAI1SSAIgrOxbIkPbh41JbAsJIPj
+xVAsukaQRYcNcv9dETjFkXbFLPsFKoKVoVlj49AmWM1nVjq633zS0jvY3hp6d+QM
+jAvrK8IisL1Vutm5VdEiesYCTj/DNQIDAQABo4IBejCCAXYwEgYDVR0TAQH/BAgw
+BgEB/wIBADA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vY3JsLndzLnN5bWFudGVj
+LmNvbS91bml2ZXJzYWwtcm9vdC5jcmwwDgYDVR0PAQH/BAQDAgEGMDcGCCsGAQUF
+BwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3Aud3Muc3ltYW50ZWMuY29t
+MGsGA1UdIARkMGIwYAYKYIZIAYb4RQEHNjBSMCYGCCsGAQUFBwIBFhpodHRwOi8v
+d3d3LnN5bWF1dGguY29tL2NwczAoBggrBgEFBQcCAjAcGhpodHRwOi8vd3d3LnN5
+bWF1dGguY29tL3JwYTAqBgNVHREEIzAhpB8wHTEbMBkGA1UEAxMSVmVyaVNpZ25N
+UEtJLTItMzczMB0GA1UdDgQWBBTbYiD7fQKJfNI7b8fkMmwFUh2tsTAfBgNVHSME
+GDAWgBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEAGcyV
+4i97SdBIkFP0B7EgRDVwFNVENzHv73DRLUzpLbBTkQFMVOd9m9o6/7fLFK0wD2ka
+KvC8zTXrSNy5h/3PsVr2Bdo8ZOYr5txzXprYDJvSl7Po+oeVU+GZrYjo+rwJTaLE
+ahsoOy3DIRXuFPqdmBDrnz7mJCRfehwFu5oxI1h5TOxtGBlNUR8IYb2RBQxanCb8
+C6UgJb9qGyv3AglyaYMyFMNgW379mjL6tJUOGvk7CaRUR5oMzjKv0SHMf9IG72AO
+Ym9vgRoXncjLKMziX24serTLR3x0aHtIcQKcIwnzWq5fQi5fK1ktUojljQuzqGH5
+S5tV1tqxkju/w5v5LA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert46[] = {
+ 0x30, 0x82, 0x05, 0x49, 0x30, 0x82, 0x04, 0x31, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x69, 0x87, 0x94, 0x19, 0xd9, 0xe3, 0x62, 0x70, 0x74,
+ 0x9d, 0xbb, 0xe5, 0x9d, 0xc6, 0x68, 0x5e, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81,
+ 0xbd, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x38, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x55, 0x6e, 0x69, 0x76,
+ 0x65, 0x72, 0x73, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x33, 0x30, 0x34, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x34, 0x30, 0x38, 0x32,
+ 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0x84, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1d,
+ 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x53, 0x79, 0x6d,
+ 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x16, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63,
+ 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f,
+ 0x72, 0x6b, 0x31, 0x35, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x2c, 0x53, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65,
+ 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x53, 0x48, 0x41, 0x32,
+ 0x35, 0x36, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbe, 0x38, 0x16, 0x51, 0x8b, 0x80,
+ 0xdb, 0xea, 0x0e, 0x4d, 0xec, 0xe8, 0x3f, 0x5c, 0xc4, 0x7c, 0xa2, 0x5d,
+ 0xed, 0x3b, 0xaf, 0xa5, 0xd6, 0x9e, 0x10, 0x35, 0x2c, 0xe3, 0xc5, 0xe5,
+ 0xa8, 0xde, 0x8c, 0x86, 0x17, 0x26, 0xe6, 0xde, 0x0b, 0x51, 0x4a, 0x2c,
+ 0xd0, 0xfb, 0xd1, 0x14, 0x5a, 0x72, 0xf7, 0xc9, 0xdd, 0xb8, 0x83, 0x1c,
+ 0xc6, 0x46, 0x8c, 0x31, 0x25, 0x91, 0x0e, 0x59, 0x17, 0xa3, 0xd0, 0x13,
+ 0x8c, 0x92, 0xc1, 0xaf, 0x81, 0x54, 0x4e, 0xbc, 0x62, 0x02, 0x9e, 0xaa,
+ 0xa7, 0x1a, 0x57, 0xd8, 0xca, 0xa6, 0x99, 0x7a, 0x70, 0x56, 0x4f, 0x98,
+ 0x07, 0x2e, 0x4b, 0x96, 0xd0, 0x4c, 0x39, 0x53, 0xb9, 0x61, 0x2f, 0x3b,
+ 0x76, 0x7c, 0x8e, 0x05, 0x9e, 0x99, 0x44, 0xd1, 0x03, 0x54, 0x77, 0x29,
+ 0x2b, 0x56, 0x2a, 0xaa, 0x61, 0xe4, 0x84, 0x2f, 0x12, 0x15, 0x3c, 0xbd,
+ 0xd7, 0x8a, 0xe8, 0x09, 0x1e, 0x56, 0xf1, 0xb5, 0x14, 0xac, 0x8a, 0x84,
+ 0xce, 0xae, 0x78, 0xa2, 0x60, 0x0a, 0x53, 0x7e, 0x13, 0x4c, 0x1a, 0x40,
+ 0x70, 0x0e, 0x52, 0x59, 0xff, 0x5a, 0x68, 0x2e, 0x4c, 0x46, 0x13, 0x3b,
+ 0x39, 0x09, 0x82, 0x78, 0x02, 0x35, 0x49, 0x20, 0x08, 0x82, 0xb3, 0xb1,
+ 0x6c, 0x89, 0x0f, 0x6e, 0x1e, 0x35, 0x25, 0xb0, 0x2c, 0x24, 0x83, 0xe3,
+ 0xc5, 0x50, 0x2c, 0xba, 0x46, 0x90, 0x45, 0x87, 0x0d, 0x72, 0xff, 0x5d,
+ 0x11, 0x38, 0xc5, 0x91, 0x76, 0xc5, 0x2c, 0xfb, 0x05, 0x2a, 0x82, 0x95,
+ 0xa1, 0x59, 0x63, 0xe3, 0xd0, 0x26, 0x58, 0xcd, 0x67, 0x56, 0x3a, 0xba,
+ 0xdf, 0x7c, 0xd2, 0xd2, 0x3b, 0xd8, 0xde, 0x1a, 0x7a, 0x77, 0xe4, 0x0c,
+ 0x8c, 0x0b, 0xeb, 0x2b, 0xc2, 0x22, 0xb0, 0xbd, 0x55, 0xba, 0xd9, 0xb9,
+ 0x55, 0xd1, 0x22, 0x7a, 0xc6, 0x02, 0x4e, 0x3f, 0xc3, 0x35, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x7a, 0x30, 0x82, 0x01, 0x76, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3e, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x37, 0x30, 0x35, 0x30, 0x33, 0xa0, 0x31, 0xa0, 0x2f,
+ 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x77, 0x73, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73,
+ 0x61, 0x6c, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x77, 0x73, 0x2e,
+ 0x73, 0x79, 0x6d, 0x61, 0x6e, 0x74, 0x65, 0x63, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x6b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x64, 0x30, 0x62, 0x30,
+ 0x60, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07,
+ 0x36, 0x30, 0x52, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x1c, 0x1a, 0x1a, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79,
+ 0x6d, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70,
+ 0x61, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x23, 0x30, 0x21,
+ 0xa4, 0x1f, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x12, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d,
+ 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x33, 0x37, 0x33, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xdb, 0x62, 0x20, 0xfb,
+ 0x7d, 0x02, 0x89, 0x7c, 0xd2, 0x3b, 0x6f, 0xc7, 0xe4, 0x32, 0x6c, 0x05,
+ 0x52, 0x1d, 0xad, 0xb1, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xb6, 0x77, 0xfa, 0x69, 0x48, 0x47, 0x9f,
+ 0x53, 0x12, 0xd5, 0xc2, 0xea, 0x07, 0x32, 0x76, 0x07, 0xd1, 0x97, 0x07,
+ 0x19, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x19, 0xcc, 0x95,
+ 0xe2, 0x2f, 0x7b, 0x49, 0xd0, 0x48, 0x90, 0x53, 0xf4, 0x07, 0xb1, 0x20,
+ 0x44, 0x35, 0x70, 0x14, 0xd5, 0x44, 0x37, 0x31, 0xef, 0xef, 0x70, 0xd1,
+ 0x2d, 0x4c, 0xe9, 0x2d, 0xb0, 0x53, 0x91, 0x01, 0x4c, 0x54, 0xe7, 0x7d,
+ 0x9b, 0xda, 0x3a, 0xff, 0xb7, 0xcb, 0x14, 0xad, 0x30, 0x0f, 0x69, 0x1a,
+ 0x2a, 0xf0, 0xbc, 0xcd, 0x35, 0xeb, 0x48, 0xdc, 0xb9, 0x87, 0xfd, 0xcf,
+ 0xb1, 0x5a, 0xf6, 0x05, 0xda, 0x3c, 0x64, 0xe6, 0x2b, 0xe6, 0xdc, 0x73,
+ 0x5e, 0x9a, 0xd8, 0x0c, 0x9b, 0xd2, 0x97, 0xb3, 0xe8, 0xfa, 0x87, 0x95,
+ 0x53, 0xe1, 0x99, 0xad, 0x88, 0xe8, 0xfa, 0xbc, 0x09, 0x4d, 0xa2, 0xc4,
+ 0x6a, 0x1b, 0x28, 0x3b, 0x2d, 0xc3, 0x21, 0x15, 0xee, 0x14, 0xfa, 0x9d,
+ 0x98, 0x10, 0xeb, 0x9f, 0x3e, 0xe6, 0x24, 0x24, 0x5f, 0x7a, 0x1c, 0x05,
+ 0xbb, 0x9a, 0x31, 0x23, 0x58, 0x79, 0x4c, 0xec, 0x6d, 0x18, 0x19, 0x4d,
+ 0x51, 0x1f, 0x08, 0x61, 0xbd, 0x91, 0x05, 0x0c, 0x5a, 0x9c, 0x26, 0xfc,
+ 0x0b, 0xa5, 0x20, 0x25, 0xbf, 0x6a, 0x1b, 0x2b, 0xf7, 0x02, 0x09, 0x72,
+ 0x69, 0x83, 0x32, 0x14, 0xc3, 0x60, 0x5b, 0x7e, 0xfd, 0x9a, 0x32, 0xfa,
+ 0xb4, 0x95, 0x0e, 0x1a, 0xf9, 0x3b, 0x09, 0xa4, 0x54, 0x47, 0x9a, 0x0c,
+ 0xce, 0x32, 0xaf, 0xd1, 0x21, 0xcc, 0x7f, 0xd2, 0x06, 0xef, 0x60, 0x0e,
+ 0x62, 0x6f, 0x6f, 0x81, 0x1a, 0x17, 0x9d, 0xc8, 0xcb, 0x28, 0xcc, 0xe2,
+ 0x5f, 0x6e, 0x2c, 0x7a, 0xb4, 0xcb, 0x47, 0x7c, 0x74, 0x68, 0x7b, 0x48,
+ 0x71, 0x02, 0x9c, 0x23, 0x09, 0xf3, 0x5a, 0xae, 0x5f, 0x42, 0x2e, 0x5f,
+ 0x2b, 0x59, 0x2d, 0x52, 0x88, 0xe5, 0x8d, 0x0b, 0xb3, 0xa8, 0x61, 0xf9,
+ 0x4b, 0x9b, 0x55, 0xd6, 0xda, 0xb1, 0x92, 0x3b, 0xbf, 0xc3, 0x9b, 0xf9,
+ 0x2c,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120036009 (0x7279aa9)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Dec 19 20:07:32 2013 GMT
+ Not After : Dec 19 20:06:55 2017 GMT
+ Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT SSL SHA2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:d1:e8:37:a7:76:8a:70:4b:19:f0:20:37:09:24:
+ 37:7f:ea:fb:78:e6:05:ba:6a:ad:4e:27:0d:fc:72:
+ 6a:d9:6c:21:c4:64:11:95:73:10:0a:5c:25:7b:88:
+ 6c:94:04:fd:c7:db:ae:7b:dc:4a:08:b3:3e:16:f1:
+ d0:ad:db:30:6d:d7:1a:1e:52:b5:3d:f0:47:19:03:
+ e2:7d:a6:bd:57:13:3f:54:ea:3a:a3:b1:77:fc:42:
+ f0:63:49:6a:91:80:2e:30:49:c0:8a:eb:2b:af:fe:
+ 3a:eb:07:5d:06:f7:e9:fd:84:0e:91:bd:09:20:29:
+ e8:6e:5d:09:ce:15:d3:e7:ef:db:50:eb:44:ef:18:
+ 57:ab:04:1d:bc:31:f9:f7:7b:2a:13:cf:d1:3d:51:
+ af:1b:c5:b5:7b:e7:b0:fc:53:bb:9a:e7:63:de:41:
+ 33:b6:47:24:69:5d:b8:46:a7:ff:ad:ab:df:4f:7a:
+ 78:25:27:21:26:34:ca:02:6e:37:51:f0:ed:58:1a:
+ 60:94:f6:c4:93:d8:dd:30:24:25:d7:1c:eb:19:94:
+ 35:5d:93:b2:ae:aa:29:83:73:c4:74:59:05:52:67:
+ 9d:da:67:51:39:05:3a:36:ea:f2:1e:76:2b:14:ae:
+ ec:3d:f9:14:99:8b:07:6e:bc:e7:0c:56:de:ac:be:
+ ae:db:75:32:90:9e:63:bd:74:bf:e0:0a:ca:f8:34:
+ 96:67:84:cd:d1:42:38:78:c7:99:b6:0c:ce:b6:0f:
+ e9:1b:cb:f4:59:be:11:0e:cb:2c:32:c8:fa:83:29:
+ 64:79:3c:8b:4b:f0:32:74:6c:f3:93:b8:96:6b:5d:
+ 57:5a:68:c1:cc:0c:79:8a:19:de:f5:49:02:5e:08:
+ 80:01:89:0c:32:cd:d2:d6:96:d5:4b:a0:f3:ec:bf:
+ ab:f4:7d:b3:a1:b9:7c:da:4e:d7:e5:b7:ac:b9:f2:
+ 25:5f:01:cb:8c:96:a8:28:ae:c1:33:5a:f6:3f:08:
+ 90:dc:eb:ff:39:d8:26:c8:12:9d:1c:9a:aa:a9:c0:
+ 16:8e:86:ed:67:52:96:00:7f:0d:92:3d:3d:d9:70:
+ 36:e5:ea:42:6f:1f:ae:95:e5:5b:5d:f8:d0:3a:c7:
+ d4:de:77:86:d0:fc:9e:4e:e2:e2:b8:a9:68:37:09:
+ c4:39:e3:85:b8:89:f3:1f:6e:b7:6d:1f:4a:2f:18:
+ 09:6f:de:4a:01:8f:14:c9:b7:a6:ee:a7:63:9f:33:
+ a4:54:7c:42:83:68:b8:a5:df:bf:ec:b9:1a:5d:13:
+ 3b:d9:ad:68:fd:20:0a:55:91:21:64:f9:d7:13:01:
+ a0:08:5d:59:89:1b:44:af:a4:ac:c7:05:10:fa:41:
+ 4a:a8:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 51:AF:24:26:9C:F4:68:22:57:80:26:2B:3B:46:62:15:7B:1E:CC:A5
+ Signature Algorithm: sha256WithRSAEncryption
+ 76:85:c5:23:31:1f:b4:73:ea:a0:bc:a5:ed:df:45:43:6a:7f:
+ 69:20:1b:80:b2:fb:1c:dd:aa:7f:88:d3:31:41:36:f7:fb:fb:
+ 6b:ad:98:8c:78:1f:9d:11:67:3a:cd:4b:ec:a8:bc:9d:15:19:
+ c4:3b:0b:a7:93:ce:e8:fc:9d:5b:e8:1f:cb:56:ae:76:43:2b:
+ c7:13:51:77:41:a8:66:4c:5f:a7:d1:d7:aa:75:c5:1b:29:4c:
+ c9:f4:6d:a1:5e:a1:85:93:16:c2:cb:3b:ab:14:7d:44:fd:da:
+ 25:29:86:2a:fe:63:20:ca:d2:0b:c2:34:15:bb:af:5b:7f:8a:
+ e0:aa:ed:45:a6:ea:79:db:d8:35:66:54:43:de:37:33:d1:e4:
+ e0:cd:57:ca:71:b0:7d:e9:16:77:64:e8:59:97:b9:d5:2e:d1:
+ b4:91:da:77:71:f3:4a:0f:48:d2:34:99:60:95:37:ac:1f:01:
+ cd:10:9d:e8:2a:a5:20:c7:50:9b:b3:6c:49:78:2b:58:92:64:
+ 89:b8:95:36:a8:34:aa:f0:41:d2:95:5a:24:54:97:4d:6e:05:
+ c4:95:ad:c4:7a:a3:39:fb:79:06:8a:9b:a6:4f:d9:22:fa:44:
+ 4e:36:f3:c9:0f:a6:39:e7:80:b2:5e:bf:bd:39:d1:46:e5:55:
+ 47:db:bc:6e
+-----BEGIN CERTIFICATE-----
+MIIFhjCCBG6gAwIBAgIEByeaqTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEzMTIxOTIwMDczMloX
+DTE3MTIxOTIwMDY1NVowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
+YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0
+IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3
+p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu
+e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA
+iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R
+PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg
+lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU
+mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R
+DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV
+S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq
+qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE
+OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o
+/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCASAwggEcMBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwUwYDVR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEF
+BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku
+Y2ZtMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH
+AwIwHwYDVR0jBBgwFoAU5Z1ZMIJHWMys+ghUNoZ7OrUETfAwQgYDVR0fBDswOTA3
+oDWgM4YxaHR0cDovL2NkcDEucHVibGljLXRydXN0LmNvbS9DUkwvT21uaXJvb3Qy
+MDI1LmNybDAdBgNVHQ4EFgQUUa8kJpz0aCJXgCYrO0ZiFXsezKUwDQYJKoZIhvcN
+AQELBQADggEBAHaFxSMxH7Rz6qC8pe3fRUNqf2kgG4Cy+xzdqn+I0zFBNvf7+2ut
+mIx4H50RZzrNS+yovJ0VGcQ7C6eTzuj8nVvoH8tWrnZDK8cTUXdBqGZMX6fR16p1
+xRspTMn0baFeoYWTFsLLO6sUfUT92iUphir+YyDK0gvCNBW7r1t/iuCq7UWm6nnb
+2DVmVEPeNzPR5ODNV8pxsH3pFndk6FmXudUu0bSR2ndx80oPSNI0mWCVN6wfAc0Q
+negqpSDHUJuzbEl4K1iSZIm4lTaoNKrwQdKVWiRUl01uBcSVrcR6ozn7eQaKm6ZP
+2SL6RE4288kPpjnngLJev7050UblVUfbvG4=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert47[] = {
+ 0x30, 0x82, 0x05, 0x86, 0x30, 0x82, 0x04, 0x6e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x9a, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33,
+ 0x31, 0x32, 0x31, 0x39, 0x32, 0x30, 0x30, 0x37, 0x33, 0x32, 0x5a, 0x17,
+ 0x0d, 0x31, 0x37, 0x31, 0x32, 0x31, 0x39, 0x32, 0x30, 0x30, 0x36, 0x35,
+ 0x35, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67,
+ 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30,
+ 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72,
+ 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
+ 0x74, 0x20, 0x49, 0x54, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x49, 0x54, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32,
+ 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
+ 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xd1, 0xe8, 0x37,
+ 0xa7, 0x76, 0x8a, 0x70, 0x4b, 0x19, 0xf0, 0x20, 0x37, 0x09, 0x24, 0x37,
+ 0x7f, 0xea, 0xfb, 0x78, 0xe6, 0x05, 0xba, 0x6a, 0xad, 0x4e, 0x27, 0x0d,
+ 0xfc, 0x72, 0x6a, 0xd9, 0x6c, 0x21, 0xc4, 0x64, 0x11, 0x95, 0x73, 0x10,
+ 0x0a, 0x5c, 0x25, 0x7b, 0x88, 0x6c, 0x94, 0x04, 0xfd, 0xc7, 0xdb, 0xae,
+ 0x7b, 0xdc, 0x4a, 0x08, 0xb3, 0x3e, 0x16, 0xf1, 0xd0, 0xad, 0xdb, 0x30,
+ 0x6d, 0xd7, 0x1a, 0x1e, 0x52, 0xb5, 0x3d, 0xf0, 0x47, 0x19, 0x03, 0xe2,
+ 0x7d, 0xa6, 0xbd, 0x57, 0x13, 0x3f, 0x54, 0xea, 0x3a, 0xa3, 0xb1, 0x77,
+ 0xfc, 0x42, 0xf0, 0x63, 0x49, 0x6a, 0x91, 0x80, 0x2e, 0x30, 0x49, 0xc0,
+ 0x8a, 0xeb, 0x2b, 0xaf, 0xfe, 0x3a, 0xeb, 0x07, 0x5d, 0x06, 0xf7, 0xe9,
+ 0xfd, 0x84, 0x0e, 0x91, 0xbd, 0x09, 0x20, 0x29, 0xe8, 0x6e, 0x5d, 0x09,
+ 0xce, 0x15, 0xd3, 0xe7, 0xef, 0xdb, 0x50, 0xeb, 0x44, 0xef, 0x18, 0x57,
+ 0xab, 0x04, 0x1d, 0xbc, 0x31, 0xf9, 0xf7, 0x7b, 0x2a, 0x13, 0xcf, 0xd1,
+ 0x3d, 0x51, 0xaf, 0x1b, 0xc5, 0xb5, 0x7b, 0xe7, 0xb0, 0xfc, 0x53, 0xbb,
+ 0x9a, 0xe7, 0x63, 0xde, 0x41, 0x33, 0xb6, 0x47, 0x24, 0x69, 0x5d, 0xb8,
+ 0x46, 0xa7, 0xff, 0xad, 0xab, 0xdf, 0x4f, 0x7a, 0x78, 0x25, 0x27, 0x21,
+ 0x26, 0x34, 0xca, 0x02, 0x6e, 0x37, 0x51, 0xf0, 0xed, 0x58, 0x1a, 0x60,
+ 0x94, 0xf6, 0xc4, 0x93, 0xd8, 0xdd, 0x30, 0x24, 0x25, 0xd7, 0x1c, 0xeb,
+ 0x19, 0x94, 0x35, 0x5d, 0x93, 0xb2, 0xae, 0xaa, 0x29, 0x83, 0x73, 0xc4,
+ 0x74, 0x59, 0x05, 0x52, 0x67, 0x9d, 0xda, 0x67, 0x51, 0x39, 0x05, 0x3a,
+ 0x36, 0xea, 0xf2, 0x1e, 0x76, 0x2b, 0x14, 0xae, 0xec, 0x3d, 0xf9, 0x14,
+ 0x99, 0x8b, 0x07, 0x6e, 0xbc, 0xe7, 0x0c, 0x56, 0xde, 0xac, 0xbe, 0xae,
+ 0xdb, 0x75, 0x32, 0x90, 0x9e, 0x63, 0xbd, 0x74, 0xbf, 0xe0, 0x0a, 0xca,
+ 0xf8, 0x34, 0x96, 0x67, 0x84, 0xcd, 0xd1, 0x42, 0x38, 0x78, 0xc7, 0x99,
+ 0xb6, 0x0c, 0xce, 0xb6, 0x0f, 0xe9, 0x1b, 0xcb, 0xf4, 0x59, 0xbe, 0x11,
+ 0x0e, 0xcb, 0x2c, 0x32, 0xc8, 0xfa, 0x83, 0x29, 0x64, 0x79, 0x3c, 0x8b,
+ 0x4b, 0xf0, 0x32, 0x74, 0x6c, 0xf3, 0x93, 0xb8, 0x96, 0x6b, 0x5d, 0x57,
+ 0x5a, 0x68, 0xc1, 0xcc, 0x0c, 0x79, 0x8a, 0x19, 0xde, 0xf5, 0x49, 0x02,
+ 0x5e, 0x08, 0x80, 0x01, 0x89, 0x0c, 0x32, 0xcd, 0xd2, 0xd6, 0x96, 0xd5,
+ 0x4b, 0xa0, 0xf3, 0xec, 0xbf, 0xab, 0xf4, 0x7d, 0xb3, 0xa1, 0xb9, 0x7c,
+ 0xda, 0x4e, 0xd7, 0xe5, 0xb7, 0xac, 0xb9, 0xf2, 0x25, 0x5f, 0x01, 0xcb,
+ 0x8c, 0x96, 0xa8, 0x28, 0xae, 0xc1, 0x33, 0x5a, 0xf6, 0x3f, 0x08, 0x90,
+ 0xdc, 0xeb, 0xff, 0x39, 0xd8, 0x26, 0xc8, 0x12, 0x9d, 0x1c, 0x9a, 0xaa,
+ 0xa9, 0xc0, 0x16, 0x8e, 0x86, 0xed, 0x67, 0x52, 0x96, 0x00, 0x7f, 0x0d,
+ 0x92, 0x3d, 0x3d, 0xd9, 0x70, 0x36, 0xe5, 0xea, 0x42, 0x6f, 0x1f, 0xae,
+ 0x95, 0xe5, 0x5b, 0x5d, 0xf8, 0xd0, 0x3a, 0xc7, 0xd4, 0xde, 0x77, 0x86,
+ 0xd0, 0xfc, 0x9e, 0x4e, 0xe2, 0xe2, 0xb8, 0xa9, 0x68, 0x37, 0x09, 0xc4,
+ 0x39, 0xe3, 0x85, 0xb8, 0x89, 0xf3, 0x1f, 0x6e, 0xb7, 0x6d, 0x1f, 0x4a,
+ 0x2f, 0x18, 0x09, 0x6f, 0xde, 0x4a, 0x01, 0x8f, 0x14, 0xc9, 0xb7, 0xa6,
+ 0xee, 0xa7, 0x63, 0x9f, 0x33, 0xa4, 0x54, 0x7c, 0x42, 0x83, 0x68, 0xb8,
+ 0xa5, 0xdf, 0xbf, 0xec, 0xb9, 0x1a, 0x5d, 0x13, 0x3b, 0xd9, 0xad, 0x68,
+ 0xfd, 0x20, 0x0a, 0x55, 0x91, 0x21, 0x64, 0xf9, 0xd7, 0x13, 0x01, 0xa0,
+ 0x08, 0x5d, 0x59, 0x89, 0x1b, 0x44, 0xaf, 0xa4, 0xac, 0xc7, 0x05, 0x10,
+ 0xfa, 0x41, 0x4a, 0xa8, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x20, 0x30, 0x82, 0x01, 0x1c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30,
+ 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e,
+ 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
+ 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac,
+ 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30,
+ 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37,
+ 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
+ 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43,
+ 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32,
+ 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x51, 0xaf, 0x24, 0x26, 0x9c, 0xf4,
+ 0x68, 0x22, 0x57, 0x80, 0x26, 0x2b, 0x3b, 0x46, 0x62, 0x15, 0x7b, 0x1e,
+ 0xcc, 0xa5, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x76, 0x85,
+ 0xc5, 0x23, 0x31, 0x1f, 0xb4, 0x73, 0xea, 0xa0, 0xbc, 0xa5, 0xed, 0xdf,
+ 0x45, 0x43, 0x6a, 0x7f, 0x69, 0x20, 0x1b, 0x80, 0xb2, 0xfb, 0x1c, 0xdd,
+ 0xaa, 0x7f, 0x88, 0xd3, 0x31, 0x41, 0x36, 0xf7, 0xfb, 0xfb, 0x6b, 0xad,
+ 0x98, 0x8c, 0x78, 0x1f, 0x9d, 0x11, 0x67, 0x3a, 0xcd, 0x4b, 0xec, 0xa8,
+ 0xbc, 0x9d, 0x15, 0x19, 0xc4, 0x3b, 0x0b, 0xa7, 0x93, 0xce, 0xe8, 0xfc,
+ 0x9d, 0x5b, 0xe8, 0x1f, 0xcb, 0x56, 0xae, 0x76, 0x43, 0x2b, 0xc7, 0x13,
+ 0x51, 0x77, 0x41, 0xa8, 0x66, 0x4c, 0x5f, 0xa7, 0xd1, 0xd7, 0xaa, 0x75,
+ 0xc5, 0x1b, 0x29, 0x4c, 0xc9, 0xf4, 0x6d, 0xa1, 0x5e, 0xa1, 0x85, 0x93,
+ 0x16, 0xc2, 0xcb, 0x3b, 0xab, 0x14, 0x7d, 0x44, 0xfd, 0xda, 0x25, 0x29,
+ 0x86, 0x2a, 0xfe, 0x63, 0x20, 0xca, 0xd2, 0x0b, 0xc2, 0x34, 0x15, 0xbb,
+ 0xaf, 0x5b, 0x7f, 0x8a, 0xe0, 0xaa, 0xed, 0x45, 0xa6, 0xea, 0x79, 0xdb,
+ 0xd8, 0x35, 0x66, 0x54, 0x43, 0xde, 0x37, 0x33, 0xd1, 0xe4, 0xe0, 0xcd,
+ 0x57, 0xca, 0x71, 0xb0, 0x7d, 0xe9, 0x16, 0x77, 0x64, 0xe8, 0x59, 0x97,
+ 0xb9, 0xd5, 0x2e, 0xd1, 0xb4, 0x91, 0xda, 0x77, 0x71, 0xf3, 0x4a, 0x0f,
+ 0x48, 0xd2, 0x34, 0x99, 0x60, 0x95, 0x37, 0xac, 0x1f, 0x01, 0xcd, 0x10,
+ 0x9d, 0xe8, 0x2a, 0xa5, 0x20, 0xc7, 0x50, 0x9b, 0xb3, 0x6c, 0x49, 0x78,
+ 0x2b, 0x58, 0x92, 0x64, 0x89, 0xb8, 0x95, 0x36, 0xa8, 0x34, 0xaa, 0xf0,
+ 0x41, 0xd2, 0x95, 0x5a, 0x24, 0x54, 0x97, 0x4d, 0x6e, 0x05, 0xc4, 0x95,
+ 0xad, 0xc4, 0x7a, 0xa3, 0x39, 0xfb, 0x79, 0x06, 0x8a, 0x9b, 0xa6, 0x4f,
+ 0xd9, 0x22, 0xfa, 0x44, 0x4e, 0x36, 0xf3, 0xc9, 0x0f, 0xa6, 0x39, 0xe7,
+ 0x80, 0xb2, 0x5e, 0xbf, 0xbd, 0x39, 0xd1, 0x46, 0xe5, 0x55, 0x47, 0xdb,
+ 0xbc, 0x6e,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 75:96:c2:3e:fa:89:59:45:6e:79:f7:17:ba:cf:64:f3
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=CN, O=WoSign CA Limited, CN=Certification Authority of WoSign
+ Validity
+ Not Before: Nov 8 00:58:58 2014 GMT
+ Not After : Nov 8 00:58:58 2029 GMT
+ Subject: C=CN, O=WoSign CA Limited, CN=WoSign Class 3 OV Server CA G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d6:74:87:af:99:c0:57:96:99:c2:89:74:3c:92:
+ 55:99:bf:1f:07:00:35:05:26:96:16:5b:03:c1:42:
+ 37:33:be:3f:0d:4f:ff:bb:94:26:91:d7:14:16:78:
+ 1b:f7:13:a2:4b:4c:e5:5c:a7:10:40:35:59:30:d1:
+ 77:99:e3:9d:29:c2:be:31:95:bd:92:61:5b:b0:23:
+ fb:67:58:d5:52:e4:7b:2f:f0:73:1c:73:94:55:ba:
+ c8:68:59:02:10:10:e4:f7:11:f0:c3:b6:d7:ae:56:
+ 80:00:9e:65:64:a6:83:91:41:e6:ed:a7:7a:65:a5:
+ 1f:30:2e:13:3c:bf:df:63:97:f3:96:f0:52:32:b4:
+ f4:7b:98:57:ed:36:4f:f7:21:4a:28:9d:dd:1c:92:
+ b3:4d:8d:9c:58:8b:17:21:d8:dc:a1:b7:ae:73:78:
+ 8a:c4:b6:e9:7f:28:8e:9a:d5:2e:9e:39:e9:da:59:
+ 74:e3:c8:97:10:32:94:19:59:d4:0f:89:57:44:e6:
+ e5:2b:17:30:62:52:98:7f:ab:0d:a5:01:ea:04:41:
+ ca:fa:13:0e:3b:87:06:ba:bd:47:31:d7:63:03:01:
+ f4:be:a1:37:11:9f:1e:01:95:4e:0f:3f:54:1e:92:
+ a6:9f:30:8c:fe:98:e8:56:96:66:04:e1:35:fe:59:
+ ac:57
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication, TLS Web Server Authentication
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crls1.wosign.com/ca1.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp1.wosign.com/ca1
+ CA Issuers - URI:http://aia1.wosign.com/ca1g2-server3.cer
+
+ X509v3 Subject Key Identifier:
+ F9:8B:EC:04:38:6A:3F:AA:06:C6:94:AD:73:95:2A:B0:C8:E6:B8:FB
+ X509v3 Authority Key Identifier:
+ keyid:E1:66:CF:0E:D1:F1:B3:4B:B7:06:20:14:FE:87:12:D5:F6:FE:FB:3E
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.36305.6.3.2.1
+ CPS: http://www.wosign.com/policy/
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 5e:67:ba:78:32:05:b6:b7:af:e7:de:6a:7a:82:64:0e:a0:0b:
+ f2:9e:9a:ba:c6:2b:6f:56:3a:b4:62:57:ab:7c:ad:60:50:96:
+ 34:9c:a3:88:cf:d9:8f:50:af:f6:f0:00:36:1b:1f:1f:87:55:
+ 3c:60:9a:f0:b0:0d:9a:80:2d:8a:3b:be:05:b3:d7:a0:80:b6:
+ b8:19:eb:51:db:ec:64:54:f1:1a:89:4a:48:a1:4d:3f:31:7d:
+ c4:79:94:4b:f1:de:ab:83:af:5f:86:be:96:1c:b3:3e:1c:e7:
+ bc:96:b2:e8:5a:ac:b5:58:cb:3c:56:6f:0a:a7:a5:d0:36:89:
+ 82:26:8c:b9:1f:b6:eb:8f:7e:78:fc:5b:8b:79:1c:d6:df:47:
+ a7:56:f4:98:4e:c7:a9:d5:0e:75:56:06:7f:b4:37:46:08:c6:
+ e9:4f:8b:5b:43:1c:e0:45:3e:95:20:71:c0:1c:98:16:ef:f2:
+ 78:df:ac:4d:bb:bf:56:0e:cf:85:af:cf:bf:04:ed:72:6b:fd:
+ 1f:57:0e:58:91:44:11:58:3b:62:3b:09:78:b3:a4:75:6a:ec:
+ b3:c2:2b:32:cc:b3:8d:c3:a3:6e:dc:8a:d5:e8:4a:c4:0b:7b:
+ db:30:5d:95:33:c3:d1:a3:69:64:5b:a8:aa:96:48:73:73:e3:
+ c9:b9:24:df:17:75:aa:af:07:3a:cf:be:9b:8a:80:a7:bf:7c:
+ e2:e9:2a:e6:fd:b0:2c:e7:e6:e6:7e:b3:35:15:65:00:f4:e1:
+ 39:73:0e:28:4b:f0:0c:98:9e:3a:eb:ce:7b:7a:9e:40:c1:50:
+ 65:96:9a:e7:4b:77:cd:dd:cb:7d:97:b4:ea:09:b2:e9:49:28:
+ c3:30:e0:87:15:f0:26:ea:d8:03:fd:ec:da:08:83:65:dc:77:
+ c5:6e:3d:34:f7:87:c3:1c:1d:26:33:ec:33:ac:c6:99:53:ab:
+ 60:f4:b0:d9:ee:64:5a:33:07:70:13:74:88:07:f5:86:f9:18:
+ d3:b2:47:c8:ae:03:4a:53:de:1c:65:d6:0a:2e:3a:51:93:ee:
+ b7:e3:6f:0a:fb:e9:fe:4e:e8:bb:1d:c2:97:ab:0a:b9:ed:36:
+ 32:1b:4d:a1:cc:03:a6:9d:b3:d9:1c:d5:67:e2:8f:74:3c:92:
+ 2a:74:b1:56:50:df:53:15:d7:21:d6:eb:f3:fb:63:e3:20:2c:
+ 0a:74:37:0b:c1:a1:35:6a:84:70:f4:45:f8:b2:b6:81:49:aa:
+ fd:54:45:90:4d:e7:04:07:5f:78:14:dd:3a:bb:2b:f9:72:50:
+ ec:68:ea:3c:a8:d1:80:bb:be:35:43:97:c3:32:b2:f5:aa:ad:
+ c9:7f:83:9f:7d:69:1e:15
+-----BEGIN CERTIFICATE-----
+MIIFozCCA4ugAwIBAgIQdZbCPvqJWUVuefcXus9k8zANBgkqhkiG9w0BAQsFADBV
+MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV
+BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0xNDExMDgw
+MDU4NThaFw0yOTExMDgwMDU4NThaMFIxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX
+b1NpZ24gQ0EgTGltaXRlZDEnMCUGA1UEAxMeV29TaWduIENsYXNzIDMgT1YgU2Vy
+dmVyIENBIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1nSHr5nA
+V5aZwol0PJJVmb8fBwA1BSaWFlsDwUI3M74/DU//u5QmkdcUFngb9xOiS0zlXKcQ
+QDVZMNF3meOdKcK+MZW9kmFbsCP7Z1jVUuR7L/BzHHOUVbrIaFkCEBDk9xHww7bX
+rlaAAJ5lZKaDkUHm7ad6ZaUfMC4TPL/fY5fzlvBSMrT0e5hX7TZP9yFKKJ3dHJKz
+TY2cWIsXIdjcobeuc3iKxLbpfyiOmtUunjnp2ll048iXEDKUGVnUD4lXROblKxcw
+YlKYf6sNpQHqBEHK+hMOO4cGur1HMddjAwH0vqE3EZ8eAZVODz9UHpKmnzCM/pjo
+VpZmBOE1/lmsVwIDAQABo4IBcDCCAWwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdJQQW
+MBQGCCsGAQUFBwMCBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMDAGA1Ud
+HwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmxzMS53b3NpZ24uY29tL2NhMS5jcmwwbQYI
+KwYBBQUHAQEEYTBfMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcDEud29zaWduLmNv
+bS9jYTEwNAYIKwYBBQUHMAKGKGh0dHA6Ly9haWExLndvc2lnbi5jb20vY2ExZzIt
+c2VydmVyMy5jZXIwHQYDVR0OBBYEFPmL7AQ4aj+qBsaUrXOVKrDI5rj7MB8GA1Ud
+IwQYMBaAFOFmzw7R8bNLtwYgFP6HEtX2/vs+MEYGA1UdIAQ/MD0wOwYMKwYBBAGC
+m1EGAwIBMCswKQYIKwYBBQUHAgEWHWh0dHA6Ly93d3cud29zaWduLmNvbS9wb2xp
+Y3kvMA0GCSqGSIb3DQEBCwUAA4ICAQBeZ7p4MgW2t6/n3mp6gmQOoAvynpq6xitv
+Vjq0YlerfK1gUJY0nKOIz9mPUK/28AA2Gx8fh1U8YJrwsA2agC2KO74Fs9eggLa4
+GetR2+xkVPEaiUpIoU0/MX3EeZRL8d6rg69fhr6WHLM+HOe8lrLoWqy1WMs8Vm8K
+p6XQNomCJoy5H7brj354/FuLeRzW30enVvSYTsep1Q51VgZ/tDdGCMbpT4tbQxzg
+RT6VIHHAHJgW7/J436xNu79WDs+Fr8+/BO1ya/0fVw5YkUQRWDtiOwl4s6R1auyz
+wisyzLONw6Nu3IrV6ErEC3vbMF2VM8PRo2lkW6iqlkhzc+PJuSTfF3Wqrwc6z76b
+ioCnv3zi6Srm/bAs5+bmfrM1FWUA9OE5cw4oS/AMmJ466857ep5AwVBllprnS3fN
+3ct9l7TqCbLpSSjDMOCHFfAm6tgD/ezaCINl3HfFbj0094fDHB0mM+wzrMaZU6tg
+9LDZ7mRaMwdwE3SIB/WG+RjTskfIrgNKU94cZdYKLjpRk+63428K++n+Tui7HcKX
+qwq57TYyG02hzAOmnbPZHNVn4o90PJIqdLFWUN9TFdch1uvz+2PjICwKdDcLwaE1
+aoRw9EX4sraBSar9VEWQTecEB194FN06uyv5clDsaOo8qNGAu741Q5fDMrL1qq3J
+f4OffWkeFQ==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert48[] = {
+ 0x30, 0x82, 0x05, 0xa3, 0x30, 0x82, 0x03, 0x8b, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x75, 0x96, 0xc2, 0x3e, 0xfa, 0x89, 0x59, 0x45, 0x6e,
+ 0x79, 0xf7, 0x17, 0xba, 0xcf, 0x64, 0xf3, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x55,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43,
+ 0x4e, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x57, 0x6f, 0x53, 0x69, 0x67,
+ 0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x31, 0x30, 0x38, 0x30,
+ 0x30, 0x35, 0x38, 0x35, 0x38, 0x5a, 0x17, 0x0d, 0x32, 0x39, 0x31, 0x31,
+ 0x30, 0x38, 0x30, 0x30, 0x35, 0x38, 0x35, 0x38, 0x5a, 0x30, 0x52, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4e,
+ 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x57,
+ 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x1e, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x4f, 0x56, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd6, 0x74, 0x87, 0xaf, 0x99, 0xc0,
+ 0x57, 0x96, 0x99, 0xc2, 0x89, 0x74, 0x3c, 0x92, 0x55, 0x99, 0xbf, 0x1f,
+ 0x07, 0x00, 0x35, 0x05, 0x26, 0x96, 0x16, 0x5b, 0x03, 0xc1, 0x42, 0x37,
+ 0x33, 0xbe, 0x3f, 0x0d, 0x4f, 0xff, 0xbb, 0x94, 0x26, 0x91, 0xd7, 0x14,
+ 0x16, 0x78, 0x1b, 0xf7, 0x13, 0xa2, 0x4b, 0x4c, 0xe5, 0x5c, 0xa7, 0x10,
+ 0x40, 0x35, 0x59, 0x30, 0xd1, 0x77, 0x99, 0xe3, 0x9d, 0x29, 0xc2, 0xbe,
+ 0x31, 0x95, 0xbd, 0x92, 0x61, 0x5b, 0xb0, 0x23, 0xfb, 0x67, 0x58, 0xd5,
+ 0x52, 0xe4, 0x7b, 0x2f, 0xf0, 0x73, 0x1c, 0x73, 0x94, 0x55, 0xba, 0xc8,
+ 0x68, 0x59, 0x02, 0x10, 0x10, 0xe4, 0xf7, 0x11, 0xf0, 0xc3, 0xb6, 0xd7,
+ 0xae, 0x56, 0x80, 0x00, 0x9e, 0x65, 0x64, 0xa6, 0x83, 0x91, 0x41, 0xe6,
+ 0xed, 0xa7, 0x7a, 0x65, 0xa5, 0x1f, 0x30, 0x2e, 0x13, 0x3c, 0xbf, 0xdf,
+ 0x63, 0x97, 0xf3, 0x96, 0xf0, 0x52, 0x32, 0xb4, 0xf4, 0x7b, 0x98, 0x57,
+ 0xed, 0x36, 0x4f, 0xf7, 0x21, 0x4a, 0x28, 0x9d, 0xdd, 0x1c, 0x92, 0xb3,
+ 0x4d, 0x8d, 0x9c, 0x58, 0x8b, 0x17, 0x21, 0xd8, 0xdc, 0xa1, 0xb7, 0xae,
+ 0x73, 0x78, 0x8a, 0xc4, 0xb6, 0xe9, 0x7f, 0x28, 0x8e, 0x9a, 0xd5, 0x2e,
+ 0x9e, 0x39, 0xe9, 0xda, 0x59, 0x74, 0xe3, 0xc8, 0x97, 0x10, 0x32, 0x94,
+ 0x19, 0x59, 0xd4, 0x0f, 0x89, 0x57, 0x44, 0xe6, 0xe5, 0x2b, 0x17, 0x30,
+ 0x62, 0x52, 0x98, 0x7f, 0xab, 0x0d, 0xa5, 0x01, 0xea, 0x04, 0x41, 0xca,
+ 0xfa, 0x13, 0x0e, 0x3b, 0x87, 0x06, 0xba, 0xbd, 0x47, 0x31, 0xd7, 0x63,
+ 0x03, 0x01, 0xf4, 0xbe, 0xa1, 0x37, 0x11, 0x9f, 0x1e, 0x01, 0x95, 0x4e,
+ 0x0f, 0x3f, 0x54, 0x1e, 0x92, 0xa6, 0x9f, 0x30, 0x8c, 0xfe, 0x98, 0xe8,
+ 0x56, 0x96, 0x66, 0x04, 0xe1, 0x35, 0xfe, 0x59, 0xac, 0x57, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x70, 0x30, 0x82, 0x01, 0x6c, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16,
+ 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x12,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
+ 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x30, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x29, 0x30, 0x27, 0x30, 0x25, 0xa0, 0x23, 0xa0, 0x21, 0x86,
+ 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x73,
+ 0x31, 0x2e, 0x77, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x63, 0x61, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x6d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x61, 0x30, 0x5f,
+ 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x31, 0x2e, 0x77, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x63, 0x61, 0x31, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x61, 0x69, 0x61, 0x31, 0x2e, 0x77, 0x6f, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x31, 0x67, 0x32, 0x2d,
+ 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x33, 0x2e, 0x63, 0x65, 0x72, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf9, 0x8b,
+ 0xec, 0x04, 0x38, 0x6a, 0x3f, 0xaa, 0x06, 0xc6, 0x94, 0xad, 0x73, 0x95,
+ 0x2a, 0xb0, 0xc8, 0xe6, 0xb8, 0xfb, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe1, 0x66, 0xcf, 0x0e, 0xd1,
+ 0xf1, 0xb3, 0x4b, 0xb7, 0x06, 0x20, 0x14, 0xfe, 0x87, 0x12, 0xd5, 0xf6,
+ 0xfe, 0xfb, 0x3e, 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f,
+ 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x0c, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
+ 0x9b, 0x51, 0x06, 0x03, 0x02, 0x01, 0x30, 0x2b, 0x30, 0x29, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1d, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x6f, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x69,
+ 0x63, 0x79, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x5e,
+ 0x67, 0xba, 0x78, 0x32, 0x05, 0xb6, 0xb7, 0xaf, 0xe7, 0xde, 0x6a, 0x7a,
+ 0x82, 0x64, 0x0e, 0xa0, 0x0b, 0xf2, 0x9e, 0x9a, 0xba, 0xc6, 0x2b, 0x6f,
+ 0x56, 0x3a, 0xb4, 0x62, 0x57, 0xab, 0x7c, 0xad, 0x60, 0x50, 0x96, 0x34,
+ 0x9c, 0xa3, 0x88, 0xcf, 0xd9, 0x8f, 0x50, 0xaf, 0xf6, 0xf0, 0x00, 0x36,
+ 0x1b, 0x1f, 0x1f, 0x87, 0x55, 0x3c, 0x60, 0x9a, 0xf0, 0xb0, 0x0d, 0x9a,
+ 0x80, 0x2d, 0x8a, 0x3b, 0xbe, 0x05, 0xb3, 0xd7, 0xa0, 0x80, 0xb6, 0xb8,
+ 0x19, 0xeb, 0x51, 0xdb, 0xec, 0x64, 0x54, 0xf1, 0x1a, 0x89, 0x4a, 0x48,
+ 0xa1, 0x4d, 0x3f, 0x31, 0x7d, 0xc4, 0x79, 0x94, 0x4b, 0xf1, 0xde, 0xab,
+ 0x83, 0xaf, 0x5f, 0x86, 0xbe, 0x96, 0x1c, 0xb3, 0x3e, 0x1c, 0xe7, 0xbc,
+ 0x96, 0xb2, 0xe8, 0x5a, 0xac, 0xb5, 0x58, 0xcb, 0x3c, 0x56, 0x6f, 0x0a,
+ 0xa7, 0xa5, 0xd0, 0x36, 0x89, 0x82, 0x26, 0x8c, 0xb9, 0x1f, 0xb6, 0xeb,
+ 0x8f, 0x7e, 0x78, 0xfc, 0x5b, 0x8b, 0x79, 0x1c, 0xd6, 0xdf, 0x47, 0xa7,
+ 0x56, 0xf4, 0x98, 0x4e, 0xc7, 0xa9, 0xd5, 0x0e, 0x75, 0x56, 0x06, 0x7f,
+ 0xb4, 0x37, 0x46, 0x08, 0xc6, 0xe9, 0x4f, 0x8b, 0x5b, 0x43, 0x1c, 0xe0,
+ 0x45, 0x3e, 0x95, 0x20, 0x71, 0xc0, 0x1c, 0x98, 0x16, 0xef, 0xf2, 0x78,
+ 0xdf, 0xac, 0x4d, 0xbb, 0xbf, 0x56, 0x0e, 0xcf, 0x85, 0xaf, 0xcf, 0xbf,
+ 0x04, 0xed, 0x72, 0x6b, 0xfd, 0x1f, 0x57, 0x0e, 0x58, 0x91, 0x44, 0x11,
+ 0x58, 0x3b, 0x62, 0x3b, 0x09, 0x78, 0xb3, 0xa4, 0x75, 0x6a, 0xec, 0xb3,
+ 0xc2, 0x2b, 0x32, 0xcc, 0xb3, 0x8d, 0xc3, 0xa3, 0x6e, 0xdc, 0x8a, 0xd5,
+ 0xe8, 0x4a, 0xc4, 0x0b, 0x7b, 0xdb, 0x30, 0x5d, 0x95, 0x33, 0xc3, 0xd1,
+ 0xa3, 0x69, 0x64, 0x5b, 0xa8, 0xaa, 0x96, 0x48, 0x73, 0x73, 0xe3, 0xc9,
+ 0xb9, 0x24, 0xdf, 0x17, 0x75, 0xaa, 0xaf, 0x07, 0x3a, 0xcf, 0xbe, 0x9b,
+ 0x8a, 0x80, 0xa7, 0xbf, 0x7c, 0xe2, 0xe9, 0x2a, 0xe6, 0xfd, 0xb0, 0x2c,
+ 0xe7, 0xe6, 0xe6, 0x7e, 0xb3, 0x35, 0x15, 0x65, 0x00, 0xf4, 0xe1, 0x39,
+ 0x73, 0x0e, 0x28, 0x4b, 0xf0, 0x0c, 0x98, 0x9e, 0x3a, 0xeb, 0xce, 0x7b,
+ 0x7a, 0x9e, 0x40, 0xc1, 0x50, 0x65, 0x96, 0x9a, 0xe7, 0x4b, 0x77, 0xcd,
+ 0xdd, 0xcb, 0x7d, 0x97, 0xb4, 0xea, 0x09, 0xb2, 0xe9, 0x49, 0x28, 0xc3,
+ 0x30, 0xe0, 0x87, 0x15, 0xf0, 0x26, 0xea, 0xd8, 0x03, 0xfd, 0xec, 0xda,
+ 0x08, 0x83, 0x65, 0xdc, 0x77, 0xc5, 0x6e, 0x3d, 0x34, 0xf7, 0x87, 0xc3,
+ 0x1c, 0x1d, 0x26, 0x33, 0xec, 0x33, 0xac, 0xc6, 0x99, 0x53, 0xab, 0x60,
+ 0xf4, 0xb0, 0xd9, 0xee, 0x64, 0x5a, 0x33, 0x07, 0x70, 0x13, 0x74, 0x88,
+ 0x07, 0xf5, 0x86, 0xf9, 0x18, 0xd3, 0xb2, 0x47, 0xc8, 0xae, 0x03, 0x4a,
+ 0x53, 0xde, 0x1c, 0x65, 0xd6, 0x0a, 0x2e, 0x3a, 0x51, 0x93, 0xee, 0xb7,
+ 0xe3, 0x6f, 0x0a, 0xfb, 0xe9, 0xfe, 0x4e, 0xe8, 0xbb, 0x1d, 0xc2, 0x97,
+ 0xab, 0x0a, 0xb9, 0xed, 0x36, 0x32, 0x1b, 0x4d, 0xa1, 0xcc, 0x03, 0xa6,
+ 0x9d, 0xb3, 0xd9, 0x1c, 0xd5, 0x67, 0xe2, 0x8f, 0x74, 0x3c, 0x92, 0x2a,
+ 0x74, 0xb1, 0x56, 0x50, 0xdf, 0x53, 0x15, 0xd7, 0x21, 0xd6, 0xeb, 0xf3,
+ 0xfb, 0x63, 0xe3, 0x20, 0x2c, 0x0a, 0x74, 0x37, 0x0b, 0xc1, 0xa1, 0x35,
+ 0x6a, 0x84, 0x70, 0xf4, 0x45, 0xf8, 0xb2, 0xb6, 0x81, 0x49, 0xaa, 0xfd,
+ 0x54, 0x45, 0x90, 0x4d, 0xe7, 0x04, 0x07, 0x5f, 0x78, 0x14, 0xdd, 0x3a,
+ 0xbb, 0x2b, 0xf9, 0x72, 0x50, 0xec, 0x68, 0xea, 0x3c, 0xa8, 0xd1, 0x80,
+ 0xbb, 0xbe, 0x35, 0x43, 0x97, 0xc3, 0x32, 0xb2, 0xf5, 0xaa, 0xad, 0xc9,
+ 0x7f, 0x83, 0x9f, 0x7d, 0x69, 0x1e, 0x15,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120040007 (0x727aa47)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: May 7 17:04:09 2014 GMT
+ Not After : May 7 17:03:30 2018 GMT
+ Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT SSL SHA2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:d1:e8:37:a7:76:8a:70:4b:19:f0:20:37:09:24:
+ 37:7f:ea:fb:78:e6:05:ba:6a:ad:4e:27:0d:fc:72:
+ 6a:d9:6c:21:c4:64:11:95:73:10:0a:5c:25:7b:88:
+ 6c:94:04:fd:c7:db:ae:7b:dc:4a:08:b3:3e:16:f1:
+ d0:ad:db:30:6d:d7:1a:1e:52:b5:3d:f0:47:19:03:
+ e2:7d:a6:bd:57:13:3f:54:ea:3a:a3:b1:77:fc:42:
+ f0:63:49:6a:91:80:2e:30:49:c0:8a:eb:2b:af:fe:
+ 3a:eb:07:5d:06:f7:e9:fd:84:0e:91:bd:09:20:29:
+ e8:6e:5d:09:ce:15:d3:e7:ef:db:50:eb:44:ef:18:
+ 57:ab:04:1d:bc:31:f9:f7:7b:2a:13:cf:d1:3d:51:
+ af:1b:c5:b5:7b:e7:b0:fc:53:bb:9a:e7:63:de:41:
+ 33:b6:47:24:69:5d:b8:46:a7:ff:ad:ab:df:4f:7a:
+ 78:25:27:21:26:34:ca:02:6e:37:51:f0:ed:58:1a:
+ 60:94:f6:c4:93:d8:dd:30:24:25:d7:1c:eb:19:94:
+ 35:5d:93:b2:ae:aa:29:83:73:c4:74:59:05:52:67:
+ 9d:da:67:51:39:05:3a:36:ea:f2:1e:76:2b:14:ae:
+ ec:3d:f9:14:99:8b:07:6e:bc:e7:0c:56:de:ac:be:
+ ae:db:75:32:90:9e:63:bd:74:bf:e0:0a:ca:f8:34:
+ 96:67:84:cd:d1:42:38:78:c7:99:b6:0c:ce:b6:0f:
+ e9:1b:cb:f4:59:be:11:0e:cb:2c:32:c8:fa:83:29:
+ 64:79:3c:8b:4b:f0:32:74:6c:f3:93:b8:96:6b:5d:
+ 57:5a:68:c1:cc:0c:79:8a:19:de:f5:49:02:5e:08:
+ 80:01:89:0c:32:cd:d2:d6:96:d5:4b:a0:f3:ec:bf:
+ ab:f4:7d:b3:a1:b9:7c:da:4e:d7:e5:b7:ac:b9:f2:
+ 25:5f:01:cb:8c:96:a8:28:ae:c1:33:5a:f6:3f:08:
+ 90:dc:eb:ff:39:d8:26:c8:12:9d:1c:9a:aa:a9:c0:
+ 16:8e:86:ed:67:52:96:00:7f:0d:92:3d:3d:d9:70:
+ 36:e5:ea:42:6f:1f:ae:95:e5:5b:5d:f8:d0:3a:c7:
+ d4:de:77:86:d0:fc:9e:4e:e2:e2:b8:a9:68:37:09:
+ c4:39:e3:85:b8:89:f3:1f:6e:b7:6d:1f:4a:2f:18:
+ 09:6f:de:4a:01:8f:14:c9:b7:a6:ee:a7:63:9f:33:
+ a4:54:7c:42:83:68:b8:a5:df:bf:ec:b9:1a:5d:13:
+ 3b:d9:ad:68:fd:20:0a:55:91:21:64:f9:d7:13:01:
+ a0:08:5d:59:89:1b:44:af:a4:ac:c7:05:10:fa:41:
+ 4a:a8:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+ Policy: 1.3.6.1.4.1.311.42.1
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.omniroot.com/baltimoreroot
+
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, OCSP Signing
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 51:AF:24:26:9C:F4:68:22:57:80:26:2B:3B:46:62:15:7B:1E:CC:A5
+ Signature Algorithm: sha256WithRSAEncryption
+ 69:62:f6:84:91:00:c4:6f:82:7b:24:e1:42:a2:a5:8b:82:5c:
+ a7:c5:44:cb:e7:52:76:63:d3:76:9e:78:e2:69:35:b1:38:ba:
+ b0:96:c6:1f:ac:7b:c6:b2:65:77:8b:7d:8d:ae:64:b9:a5:8c:
+ 17:ca:58:65:c3:ad:82:f5:c5:a2:f5:01:13:93:c6:7e:44:e5:
+ c4:61:fa:03:b6:56:c1:72:e1:c8:28:c5:69:21:8f:ac:6e:fd:
+ 7f:43:83:36:b8:c0:d6:a0:28:fe:1a:45:be:fd:93:8c:8d:a4:
+ 64:79:1f:14:db:a1:9f:21:dc:c0:4e:7b:17:22:17:b1:b6:3c:
+ d3:9b:e2:0a:a3:7e:99:b0:c1:ac:d8:f4:86:df:3c:da:7d:14:
+ 9c:40:c1:7c:d2:18:6f:f1:4f:26:45:09:95:94:5c:da:d0:98:
+ f8:f4:4c:82:96:10:de:ac:30:cb:2b:ae:f9:92:ea:bf:79:03:
+ fc:1e:3f:ac:09:a4:3f:65:fd:91:4f:96:24:a7:ce:b4:4e:6a:
+ 96:29:17:ae:c0:a8:df:17:22:f4:17:e3:dc:1c:39:06:56:10:
+ ea:ea:b5:74:17:3c:4e:dd:7e:91:0a:a8:0b:78:07:a7:31:44:
+ 08:31:ab:18:84:0f:12:9c:e7:de:84:2c:e9:6d:93:45:bf:a8:
+ c1:3f:34:dc
+-----BEGIN CERTIFICATE-----
+MIIF4TCCBMmgAwIBAgIEByeqRzANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTE0MDUwNzE3MDQwOVoX
+DTE4MDUwNzE3MDMzMFowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
+YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0
+IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3
+p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu
+e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA
+iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R
+PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg
+lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU
+mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R
+DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV
+S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq
+qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE
+OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o
+/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCAXswggF3MBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwYAYDVR0gBFkwVzBIBgkrBgEEAbE+AQAwOzA5BggrBgEF
+BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku
+Y2ZtMAsGCSsGAQQBgjcqATBCBggrBgEFBQcBAQQ2MDQwMgYIKwYBBQUHMAGGJmh0
+dHA6Ly9vY3NwLm9tbmlyb290LmNvbS9iYWx0aW1vcmVyb290MA4GA1UdDwEB/wQE
+AwIBhjAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMJMB8G
+A1UdIwQYMBaAFOWdWTCCR1jMrPoIVDaGezq1BE3wMEIGA1UdHwQ7MDkwN6A1oDOG
+MWh0dHA6Ly9jZHAxLnB1YmxpYy10cnVzdC5jb20vQ1JML09tbmlyb290MjAyNS5j
+cmwwHQYDVR0OBBYEFFGvJCac9GgiV4AmKztGYhV7HsylMA0GCSqGSIb3DQEBCwUA
+A4IBAQBpYvaEkQDEb4J7JOFCoqWLglynxUTL51J2Y9N2nnjiaTWxOLqwlsYfrHvG
+smV3i32NrmS5pYwXylhlw62C9cWi9QETk8Z+ROXEYfoDtlbBcuHIKMVpIY+sbv1/
+Q4M2uMDWoCj+GkW+/ZOMjaRkeR8U26GfIdzATnsXIhextjzTm+IKo36ZsMGs2PSG
+3zzafRScQMF80hhv8U8mRQmVlFza0Jj49EyClhDerDDLK675kuq/eQP8Hj+sCaQ/
+Zf2RT5Ykp860TmqWKReuwKjfFyL0F+PcHDkGVhDq6rV0FzxO3X6RCqgLeAenMUQI
+MasYhA8SnOfehCzpbZNFv6jBPzTc
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert49[] = {
+ 0x30, 0x82, 0x05, 0xe1, 0x30, 0x82, 0x04, 0xc9, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0xaa, 0x47, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34,
+ 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x34, 0x30, 0x39, 0x5a, 0x17,
+ 0x0d, 0x31, 0x38, 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x33, 0x33,
+ 0x30, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67,
+ 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x07, 0x52, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1e, 0x30,
+ 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72,
+ 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,
+ 0x74, 0x20, 0x49, 0x54, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x15, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x49, 0x54, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x53, 0x48, 0x41, 0x32,
+ 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
+ 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xd1, 0xe8, 0x37,
+ 0xa7, 0x76, 0x8a, 0x70, 0x4b, 0x19, 0xf0, 0x20, 0x37, 0x09, 0x24, 0x37,
+ 0x7f, 0xea, 0xfb, 0x78, 0xe6, 0x05, 0xba, 0x6a, 0xad, 0x4e, 0x27, 0x0d,
+ 0xfc, 0x72, 0x6a, 0xd9, 0x6c, 0x21, 0xc4, 0x64, 0x11, 0x95, 0x73, 0x10,
+ 0x0a, 0x5c, 0x25, 0x7b, 0x88, 0x6c, 0x94, 0x04, 0xfd, 0xc7, 0xdb, 0xae,
+ 0x7b, 0xdc, 0x4a, 0x08, 0xb3, 0x3e, 0x16, 0xf1, 0xd0, 0xad, 0xdb, 0x30,
+ 0x6d, 0xd7, 0x1a, 0x1e, 0x52, 0xb5, 0x3d, 0xf0, 0x47, 0x19, 0x03, 0xe2,
+ 0x7d, 0xa6, 0xbd, 0x57, 0x13, 0x3f, 0x54, 0xea, 0x3a, 0xa3, 0xb1, 0x77,
+ 0xfc, 0x42, 0xf0, 0x63, 0x49, 0x6a, 0x91, 0x80, 0x2e, 0x30, 0x49, 0xc0,
+ 0x8a, 0xeb, 0x2b, 0xaf, 0xfe, 0x3a, 0xeb, 0x07, 0x5d, 0x06, 0xf7, 0xe9,
+ 0xfd, 0x84, 0x0e, 0x91, 0xbd, 0x09, 0x20, 0x29, 0xe8, 0x6e, 0x5d, 0x09,
+ 0xce, 0x15, 0xd3, 0xe7, 0xef, 0xdb, 0x50, 0xeb, 0x44, 0xef, 0x18, 0x57,
+ 0xab, 0x04, 0x1d, 0xbc, 0x31, 0xf9, 0xf7, 0x7b, 0x2a, 0x13, 0xcf, 0xd1,
+ 0x3d, 0x51, 0xaf, 0x1b, 0xc5, 0xb5, 0x7b, 0xe7, 0xb0, 0xfc, 0x53, 0xbb,
+ 0x9a, 0xe7, 0x63, 0xde, 0x41, 0x33, 0xb6, 0x47, 0x24, 0x69, 0x5d, 0xb8,
+ 0x46, 0xa7, 0xff, 0xad, 0xab, 0xdf, 0x4f, 0x7a, 0x78, 0x25, 0x27, 0x21,
+ 0x26, 0x34, 0xca, 0x02, 0x6e, 0x37, 0x51, 0xf0, 0xed, 0x58, 0x1a, 0x60,
+ 0x94, 0xf6, 0xc4, 0x93, 0xd8, 0xdd, 0x30, 0x24, 0x25, 0xd7, 0x1c, 0xeb,
+ 0x19, 0x94, 0x35, 0x5d, 0x93, 0xb2, 0xae, 0xaa, 0x29, 0x83, 0x73, 0xc4,
+ 0x74, 0x59, 0x05, 0x52, 0x67, 0x9d, 0xda, 0x67, 0x51, 0x39, 0x05, 0x3a,
+ 0x36, 0xea, 0xf2, 0x1e, 0x76, 0x2b, 0x14, 0xae, 0xec, 0x3d, 0xf9, 0x14,
+ 0x99, 0x8b, 0x07, 0x6e, 0xbc, 0xe7, 0x0c, 0x56, 0xde, 0xac, 0xbe, 0xae,
+ 0xdb, 0x75, 0x32, 0x90, 0x9e, 0x63, 0xbd, 0x74, 0xbf, 0xe0, 0x0a, 0xca,
+ 0xf8, 0x34, 0x96, 0x67, 0x84, 0xcd, 0xd1, 0x42, 0x38, 0x78, 0xc7, 0x99,
+ 0xb6, 0x0c, 0xce, 0xb6, 0x0f, 0xe9, 0x1b, 0xcb, 0xf4, 0x59, 0xbe, 0x11,
+ 0x0e, 0xcb, 0x2c, 0x32, 0xc8, 0xfa, 0x83, 0x29, 0x64, 0x79, 0x3c, 0x8b,
+ 0x4b, 0xf0, 0x32, 0x74, 0x6c, 0xf3, 0x93, 0xb8, 0x96, 0x6b, 0x5d, 0x57,
+ 0x5a, 0x68, 0xc1, 0xcc, 0x0c, 0x79, 0x8a, 0x19, 0xde, 0xf5, 0x49, 0x02,
+ 0x5e, 0x08, 0x80, 0x01, 0x89, 0x0c, 0x32, 0xcd, 0xd2, 0xd6, 0x96, 0xd5,
+ 0x4b, 0xa0, 0xf3, 0xec, 0xbf, 0xab, 0xf4, 0x7d, 0xb3, 0xa1, 0xb9, 0x7c,
+ 0xda, 0x4e, 0xd7, 0xe5, 0xb7, 0xac, 0xb9, 0xf2, 0x25, 0x5f, 0x01, 0xcb,
+ 0x8c, 0x96, 0xa8, 0x28, 0xae, 0xc1, 0x33, 0x5a, 0xf6, 0x3f, 0x08, 0x90,
+ 0xdc, 0xeb, 0xff, 0x39, 0xd8, 0x26, 0xc8, 0x12, 0x9d, 0x1c, 0x9a, 0xaa,
+ 0xa9, 0xc0, 0x16, 0x8e, 0x86, 0xed, 0x67, 0x52, 0x96, 0x00, 0x7f, 0x0d,
+ 0x92, 0x3d, 0x3d, 0xd9, 0x70, 0x36, 0xe5, 0xea, 0x42, 0x6f, 0x1f, 0xae,
+ 0x95, 0xe5, 0x5b, 0x5d, 0xf8, 0xd0, 0x3a, 0xc7, 0xd4, 0xde, 0x77, 0x86,
+ 0xd0, 0xfc, 0x9e, 0x4e, 0xe2, 0xe2, 0xb8, 0xa9, 0x68, 0x37, 0x09, 0xc4,
+ 0x39, 0xe3, 0x85, 0xb8, 0x89, 0xf3, 0x1f, 0x6e, 0xb7, 0x6d, 0x1f, 0x4a,
+ 0x2f, 0x18, 0x09, 0x6f, 0xde, 0x4a, 0x01, 0x8f, 0x14, 0xc9, 0xb7, 0xa6,
+ 0xee, 0xa7, 0x63, 0x9f, 0x33, 0xa4, 0x54, 0x7c, 0x42, 0x83, 0x68, 0xb8,
+ 0xa5, 0xdf, 0xbf, 0xec, 0xb9, 0x1a, 0x5d, 0x13, 0x3b, 0xd9, 0xad, 0x68,
+ 0xfd, 0x20, 0x0a, 0x55, 0x91, 0x21, 0x64, 0xf9, 0xd7, 0x13, 0x01, 0xa0,
+ 0x08, 0x5d, 0x59, 0x89, 0x1b, 0x44, 0xaf, 0xa4, 0xac, 0xc7, 0x05, 0x10,
+ 0xfa, 0x41, 0x4a, 0xa8, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x7b, 0x30, 0x82, 0x01, 0x77, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x60, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x59, 0x30,
+ 0x57, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e,
+ 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
+ 0x63, 0x66, 0x6d, 0x30, 0x0b, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01,
+ 0x82, 0x37, 0x2a, 0x01, 0x30, 0x42, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x26, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x6f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62,
+ 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x72, 0x6f, 0x6f, 0x74,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x86, 0x30, 0x27, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
+ 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d,
+ 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86,
+ 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86,
+ 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31,
+ 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x51, 0xaf, 0x24, 0x26, 0x9c, 0xf4, 0x68, 0x22, 0x57, 0x80, 0x26,
+ 0x2b, 0x3b, 0x46, 0x62, 0x15, 0x7b, 0x1e, 0xcc, 0xa5, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x69, 0x62, 0xf6, 0x84, 0x91, 0x00, 0xc4,
+ 0x6f, 0x82, 0x7b, 0x24, 0xe1, 0x42, 0xa2, 0xa5, 0x8b, 0x82, 0x5c, 0xa7,
+ 0xc5, 0x44, 0xcb, 0xe7, 0x52, 0x76, 0x63, 0xd3, 0x76, 0x9e, 0x78, 0xe2,
+ 0x69, 0x35, 0xb1, 0x38, 0xba, 0xb0, 0x96, 0xc6, 0x1f, 0xac, 0x7b, 0xc6,
+ 0xb2, 0x65, 0x77, 0x8b, 0x7d, 0x8d, 0xae, 0x64, 0xb9, 0xa5, 0x8c, 0x17,
+ 0xca, 0x58, 0x65, 0xc3, 0xad, 0x82, 0xf5, 0xc5, 0xa2, 0xf5, 0x01, 0x13,
+ 0x93, 0xc6, 0x7e, 0x44, 0xe5, 0xc4, 0x61, 0xfa, 0x03, 0xb6, 0x56, 0xc1,
+ 0x72, 0xe1, 0xc8, 0x28, 0xc5, 0x69, 0x21, 0x8f, 0xac, 0x6e, 0xfd, 0x7f,
+ 0x43, 0x83, 0x36, 0xb8, 0xc0, 0xd6, 0xa0, 0x28, 0xfe, 0x1a, 0x45, 0xbe,
+ 0xfd, 0x93, 0x8c, 0x8d, 0xa4, 0x64, 0x79, 0x1f, 0x14, 0xdb, 0xa1, 0x9f,
+ 0x21, 0xdc, 0xc0, 0x4e, 0x7b, 0x17, 0x22, 0x17, 0xb1, 0xb6, 0x3c, 0xd3,
+ 0x9b, 0xe2, 0x0a, 0xa3, 0x7e, 0x99, 0xb0, 0xc1, 0xac, 0xd8, 0xf4, 0x86,
+ 0xdf, 0x3c, 0xda, 0x7d, 0x14, 0x9c, 0x40, 0xc1, 0x7c, 0xd2, 0x18, 0x6f,
+ 0xf1, 0x4f, 0x26, 0x45, 0x09, 0x95, 0x94, 0x5c, 0xda, 0xd0, 0x98, 0xf8,
+ 0xf4, 0x4c, 0x82, 0x96, 0x10, 0xde, 0xac, 0x30, 0xcb, 0x2b, 0xae, 0xf9,
+ 0x92, 0xea, 0xbf, 0x79, 0x03, 0xfc, 0x1e, 0x3f, 0xac, 0x09, 0xa4, 0x3f,
+ 0x65, 0xfd, 0x91, 0x4f, 0x96, 0x24, 0xa7, 0xce, 0xb4, 0x4e, 0x6a, 0x96,
+ 0x29, 0x17, 0xae, 0xc0, 0xa8, 0xdf, 0x17, 0x22, 0xf4, 0x17, 0xe3, 0xdc,
+ 0x1c, 0x39, 0x06, 0x56, 0x10, 0xea, 0xea, 0xb5, 0x74, 0x17, 0x3c, 0x4e,
+ 0xdd, 0x7e, 0x91, 0x0a, 0xa8, 0x0b, 0x78, 0x07, 0xa7, 0x31, 0x44, 0x08,
+ 0x31, 0xab, 0x18, 0x84, 0x0f, 0x12, 0x9c, 0xe7, 0xde, 0x84, 0x2c, 0xe9,
+ 0x6d, 0x93, 0x45, 0xbf, 0xa8, 0xc1, 0x3f, 0x34, 0xdc,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 13:8b:fe:f3:32:94:f9:d8:16:f9:45:c2:71:95:29:98
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+ Validity
+ Not Before: Dec 16 01:00:05 2015 GMT
+ Not After : Dec 16 01:00:05 2030 GMT
+ Subject: C=IL, O=StartCom Ltd., OU=StartCom Certification Authority, CN=StartCom Class 3 OV Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:67:1c:6f:e5:45:e0:d7:46:4b:75:2c:b6:80:
+ f2:9a:17:4d:2d:ff:de:ae:d2:d4:00:8a:3a:b8:31:
+ fe:8e:37:9e:fa:aa:d5:a3:5b:16:12:c1:19:3e:34:
+ 85:96:c3:be:d3:b3:43:f4:8d:6f:16:bd:30:ba:07:
+ fc:d8:9a:c1:79:89:80:6d:a0:8c:be:dd:37:f7:eb:
+ 05:d3:53:7f:57:58:76:55:b6:a8:a8:86:44:b8:bb:
+ d0:13:da:fd:8f:e1:f2:cd:a0:15:38:55:56:ce:26:
+ cf:7c:93:75:29:7a:0a:ab:fb:ba:09:38:20:11:57:
+ 07:5d:7f:49:9f:2a:4a:67:1e:9e:58:e9:c7:7f:f9:
+ c3:ed:fe:5f:4d:af:b8:4f:9d:df:69:2d:69:1b:3a:
+ 58:81:69:63:30:ea:87:8d:0f:52:9d:5a:da:39:44:
+ ba:9f:89:9f:36:b6:c2:19:5c:d9:26:78:d9:ae:5e:
+ fc:95:90:bf:e8:11:c0:47:0f:77:89:dd:6a:28:4f:
+ 0a:bc:32:64:57:43:3d:08:65:93:e5:45:ae:dd:28:
+ 0c:27:2c:8e:a6:2b:09:03:5d:a1:78:d2:8c:ab:b6:
+ 6b:b9:46:c9:19:00:39:b9:bf:c6:13:2b:73:72:1f:
+ f2:3e:37:b8:e8:b9:14:65:88:4d:e2:f1:1b:d8:a5:
+ 1d:3b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication, TLS Web Server Authentication
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.startssl.com/sfsca.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.startssl.com
+ CA Issuers - URI:http://aia.startssl.com/certs/ca.crt
+
+ X509v3 Subject Key Identifier:
+ B1:3F:1C:92:7B:92:B0:5A:25:B3:38:FB:9C:07:A4:26:50:32:E3:51
+ X509v3 Authority Key Identifier:
+ keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.startssl.com/policy
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 85:f2:e8:14:d3:1b:c1:a1:16:1d:a4:f4:4d:ba:51:8b:5c:52:
+ b1:54:54:12:16:17:9c:96:78:6f:d3:bf:df:43:36:f5:12:89:
+ 61:72:44:df:1c:9b:09:4f:60:26:68:c1:e6:66:50:70:b3:6a:
+ f1:a8:6a:0c:1e:2e:93:f1:ee:07:3e:09:dd:30:45:b2:56:8e:
+ dc:2c:5c:ab:49:fa:b9:04:03:40:15:7a:b5:30:e0:1d:91:8f:
+ a6:d6:6f:1f:99:a0:84:95:39:bd:ac:77:7f:72:4b:dd:2d:ae:
+ ff:a8:58:1d:46:27:d4:83:c7:69:64:9f:19:bb:10:f8:04:42:
+ 87:59:5d:02:b1:d6:e5:c8:da:43:30:a3:e8:37:a5:d2:48:0b:
+ a2:83:4e:9d:4f:83:58:9d:d7:47:22:b1:89:f0:89:3b:3d:28:
+ 43:2c:9b:17:7c:03:ee:9d:26:25:e0:04:b8:1d:04:57:42:47:
+ da:58:69:f0:d3:29:ab:12:02:99:2b:2a:d8:9d:a0:1f:54:5e:
+ 23:9a:0c:d2:99:58:c4:a1:e5:49:c2:25:a7:64:20:52:2e:e7:
+ 89:f5:19:c0:8b:d0:63:b1:78:1e:be:01:47:be:76:81:46:f1:
+ 99:1f:94:9a:be:fa:82:15:b5:84:84:79:75:93:ba:9f:b5:e4:
+ 9b:c2:cb:69:5c:bd:1f:55:0a:a7:26:30:05:51:be:65:ee:57:
+ a9:6a:df:bd:f9:36:2f:ad:1e:46:41:2b:b1:88:d0:88:25:85:
+ 40:17:79:bf:3d:8d:e2:f4:2d:ea:30:31:df:a1:40:cb:35:ff:
+ 82:9f:f5:99:3c:4a:fd:9d:a1:d1:55:cc:20:a8:1c:d8:20:05:
+ ab:b3:14:65:95:53:d8:e8:8e:57:c5:77:6b:2d:4d:88:e9:5d:
+ 62:d5:a2:f8:70:e1:70:eb:45:23:0e:f0:00:46:c2:48:31:e8:
+ e7:36:80:36:2d:22:f2:01:27:53:eb:ce:a7:69:49:82:bf:e7:
+ 0f:9c:f3:20:2e:f5:fa:5d:ce:ea:58:3a:8f:d8:aa:7d:30:b7:
+ 74:96:7c:3d:6e:b4:ec:4a:3b:59:b6:a9:50:0d:0f:05:06:70:
+ 26:b9:95:91:d1:5e:24:8c:8f:ca:74:57:97:90:8b:5a:b7:fe:
+ 8d:ad:d8:e8:c2:06:bc:08:56:21:02:12:53:c6:9f:86:04:58:
+ ca:2d:f8:03:0d:57:0b:1c:37:bd:f0:5a:35:f2:fe:3b:d6:a4:
+ 37:15:e9:f8:08:92:96:3d:74:c8:b5:5c:6e:65:08:e7:df:69:
+ 73:9c:ec:e3:30:5a:a6:df:5c:be:da:7f:00:ee:a5:da:2b:5c:
+ 1e:2a:6a:c0:a3:ae:1e:f1
+-----BEGIN CERTIFICATE-----
+MIIF5TCCA82gAwIBAgIQE4v+8zKU+dgW+UXCcZUpmDANBgkqhkiG9w0BAQsFADB9
+MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
+U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
+cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTUxMjE2MDEwMDA1WhcN
+MzAxMjE2MDEwMDA1WjB4MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20g
+THRkLjEpMCcGA1UECxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx
+JjAkBgNVBAMTHVN0YXJ0Q29tIENsYXNzIDMgT1YgU2VydmVyIENBMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr2ccb+VF4NdGS3UstoDymhdNLf/ertLU
+AIo6uDH+jjee+qrVo1sWEsEZPjSFlsO+07ND9I1vFr0wugf82JrBeYmAbaCMvt03
+9+sF01N/V1h2VbaoqIZEuLvQE9r9j+HyzaAVOFVWzibPfJN1KXoKq/u6CTggEVcH
+XX9JnypKZx6eWOnHf/nD7f5fTa+4T53faS1pGzpYgWljMOqHjQ9SnVraOUS6n4mf
+NrbCGVzZJnjZrl78lZC/6BHARw93id1qKE8KvDJkV0M9CGWT5UWu3SgMJyyOpisJ
+A12heNKMq7ZruUbJGQA5ub/GEytzch/yPje46LkUZYhN4vEb2KUdOwIDAQABo4IB
+ZDCCAWAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEF
+BQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6
+Ly9jcmwuc3RhcnRzc2wuY29tL3Nmc2NhLmNybDBmBggrBgEFBQcBAQRaMFgwJAYI
+KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnN0YXJ0c3NsLmNvbTAwBggrBgEFBQcwAoYk
+aHR0cDovL2FpYS5zdGFydHNzbC5jb20vY2VydHMvY2EuY3J0MB0GA1UdDgQWBBSx
+PxySe5KwWiWzOPucB6QmUDLjUTAfBgNVHSMEGDAWgBROC+8apEBbpRdphzDKNGhD
+0EGu8jA/BgNVHSAEODA2MDQGBFUdIAAwLDAqBggrBgEFBQcCARYeaHR0cDovL3d3
+dy5zdGFydHNzbC5jb20vcG9saWN5MA0GCSqGSIb3DQEBCwUAA4ICAQCF8ugU0xvB
+oRYdpPRNulGLXFKxVFQSFheclnhv07/fQzb1EolhckTfHJsJT2AmaMHmZlBws2rx
+qGoMHi6T8e4HPgndMEWyVo7cLFyrSfq5BANAFXq1MOAdkY+m1m8fmaCElTm9rHd/
+ckvdLa7/qFgdRifUg8dpZJ8ZuxD4BEKHWV0CsdblyNpDMKPoN6XSSAuig06dT4NY
+nddHIrGJ8Ik7PShDLJsXfAPunSYl4AS4HQRXQkfaWGnw0ymrEgKZKyrYnaAfVF4j
+mgzSmVjEoeVJwiWnZCBSLueJ9RnAi9BjsXgevgFHvnaBRvGZH5SavvqCFbWEhHl1
+k7qfteSbwstpXL0fVQqnJjAFUb5l7lepat+9+TYvrR5GQSuxiNCIJYVAF3m/PY3i
+9C3qMDHfoUDLNf+Cn/WZPEr9naHRVcwgqBzYIAWrsxRllVPY6I5XxXdrLU2I6V1i
+1aL4cOFw60UjDvAARsJIMejnNoA2LSLyASdT686naUmCv+cPnPMgLvX6Xc7qWDqP
+2Kp9MLd0lnw9brTsSjtZtqlQDQ8FBnAmuZWR0V4kjI/KdFeXkItat/6Nrdjowga8
+CFYhAhJTxp+GBFjKLfgDDVcLHDe98Fo18v471qQ3Fen4CJKWPXTItVxuZQjn32lz
+nOzjMFqm31y+2n8A7qXaK1weKmrAo64e8Q==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert50[] = {
+ 0x30, 0x82, 0x05, 0xe5, 0x30, 0x82, 0x03, 0xcd, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x13, 0x8b, 0xfe, 0xf3, 0x32, 0x94, 0xf9, 0xd8, 0x16,
+ 0xf9, 0x45, 0xc2, 0x71, 0x95, 0x29, 0x98, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7d,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64,
+ 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22,
+ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29,
+ 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61,
+ 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x31,
+ 0x32, 0x31, 0x36, 0x30, 0x31, 0x30, 0x30, 0x30, 0x35, 0x5a, 0x17, 0x0d,
+ 0x33, 0x30, 0x31, 0x32, 0x31, 0x36, 0x30, 0x31, 0x30, 0x30, 0x30, 0x35,
+ 0x5a, 0x30, 0x78, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20,
+ 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1d, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73,
+ 0x20, 0x33, 0x20, 0x4f, 0x56, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xaf, 0x67, 0x1c, 0x6f, 0xe5, 0x45, 0xe0, 0xd7, 0x46, 0x4b, 0x75, 0x2c,
+ 0xb6, 0x80, 0xf2, 0x9a, 0x17, 0x4d, 0x2d, 0xff, 0xde, 0xae, 0xd2, 0xd4,
+ 0x00, 0x8a, 0x3a, 0xb8, 0x31, 0xfe, 0x8e, 0x37, 0x9e, 0xfa, 0xaa, 0xd5,
+ 0xa3, 0x5b, 0x16, 0x12, 0xc1, 0x19, 0x3e, 0x34, 0x85, 0x96, 0xc3, 0xbe,
+ 0xd3, 0xb3, 0x43, 0xf4, 0x8d, 0x6f, 0x16, 0xbd, 0x30, 0xba, 0x07, 0xfc,
+ 0xd8, 0x9a, 0xc1, 0x79, 0x89, 0x80, 0x6d, 0xa0, 0x8c, 0xbe, 0xdd, 0x37,
+ 0xf7, 0xeb, 0x05, 0xd3, 0x53, 0x7f, 0x57, 0x58, 0x76, 0x55, 0xb6, 0xa8,
+ 0xa8, 0x86, 0x44, 0xb8, 0xbb, 0xd0, 0x13, 0xda, 0xfd, 0x8f, 0xe1, 0xf2,
+ 0xcd, 0xa0, 0x15, 0x38, 0x55, 0x56, 0xce, 0x26, 0xcf, 0x7c, 0x93, 0x75,
+ 0x29, 0x7a, 0x0a, 0xab, 0xfb, 0xba, 0x09, 0x38, 0x20, 0x11, 0x57, 0x07,
+ 0x5d, 0x7f, 0x49, 0x9f, 0x2a, 0x4a, 0x67, 0x1e, 0x9e, 0x58, 0xe9, 0xc7,
+ 0x7f, 0xf9, 0xc3, 0xed, 0xfe, 0x5f, 0x4d, 0xaf, 0xb8, 0x4f, 0x9d, 0xdf,
+ 0x69, 0x2d, 0x69, 0x1b, 0x3a, 0x58, 0x81, 0x69, 0x63, 0x30, 0xea, 0x87,
+ 0x8d, 0x0f, 0x52, 0x9d, 0x5a, 0xda, 0x39, 0x44, 0xba, 0x9f, 0x89, 0x9f,
+ 0x36, 0xb6, 0xc2, 0x19, 0x5c, 0xd9, 0x26, 0x78, 0xd9, 0xae, 0x5e, 0xfc,
+ 0x95, 0x90, 0xbf, 0xe8, 0x11, 0xc0, 0x47, 0x0f, 0x77, 0x89, 0xdd, 0x6a,
+ 0x28, 0x4f, 0x0a, 0xbc, 0x32, 0x64, 0x57, 0x43, 0x3d, 0x08, 0x65, 0x93,
+ 0xe5, 0x45, 0xae, 0xdd, 0x28, 0x0c, 0x27, 0x2c, 0x8e, 0xa6, 0x2b, 0x09,
+ 0x03, 0x5d, 0xa1, 0x78, 0xd2, 0x8c, 0xab, 0xb6, 0x6b, 0xb9, 0x46, 0xc9,
+ 0x19, 0x00, 0x39, 0xb9, 0xbf, 0xc6, 0x13, 0x2b, 0x73, 0x72, 0x1f, 0xf2,
+ 0x3e, 0x37, 0xb8, 0xe8, 0xb9, 0x14, 0x65, 0x88, 0x4d, 0xe2, 0xf1, 0x1b,
+ 0xd8, 0xa5, 0x1d, 0x3b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0x64, 0x30, 0x82, 0x01, 0x60, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30,
+ 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73,
+ 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63, 0x61,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x66, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x5a, 0x30, 0x58, 0x30, 0x24, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x30,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x24,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x69, 0x61, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1,
+ 0x3f, 0x1c, 0x92, 0x7b, 0x92, 0xb0, 0x5a, 0x25, 0xb3, 0x38, 0xfb, 0x9c,
+ 0x07, 0xa4, 0x26, 0x50, 0x32, 0xe3, 0x51, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef, 0x1a,
+ 0xa4, 0x40, 0x5b, 0xa5, 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68, 0x43,
+ 0xd0, 0x41, 0xae, 0xf2, 0x30, 0x3f, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x38, 0x30, 0x36, 0x30, 0x34, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30,
+ 0x2c, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x02, 0x01, 0x00, 0x85, 0xf2, 0xe8, 0x14, 0xd3, 0x1b, 0xc1,
+ 0xa1, 0x16, 0x1d, 0xa4, 0xf4, 0x4d, 0xba, 0x51, 0x8b, 0x5c, 0x52, 0xb1,
+ 0x54, 0x54, 0x12, 0x16, 0x17, 0x9c, 0x96, 0x78, 0x6f, 0xd3, 0xbf, 0xdf,
+ 0x43, 0x36, 0xf5, 0x12, 0x89, 0x61, 0x72, 0x44, 0xdf, 0x1c, 0x9b, 0x09,
+ 0x4f, 0x60, 0x26, 0x68, 0xc1, 0xe6, 0x66, 0x50, 0x70, 0xb3, 0x6a, 0xf1,
+ 0xa8, 0x6a, 0x0c, 0x1e, 0x2e, 0x93, 0xf1, 0xee, 0x07, 0x3e, 0x09, 0xdd,
+ 0x30, 0x45, 0xb2, 0x56, 0x8e, 0xdc, 0x2c, 0x5c, 0xab, 0x49, 0xfa, 0xb9,
+ 0x04, 0x03, 0x40, 0x15, 0x7a, 0xb5, 0x30, 0xe0, 0x1d, 0x91, 0x8f, 0xa6,
+ 0xd6, 0x6f, 0x1f, 0x99, 0xa0, 0x84, 0x95, 0x39, 0xbd, 0xac, 0x77, 0x7f,
+ 0x72, 0x4b, 0xdd, 0x2d, 0xae, 0xff, 0xa8, 0x58, 0x1d, 0x46, 0x27, 0xd4,
+ 0x83, 0xc7, 0x69, 0x64, 0x9f, 0x19, 0xbb, 0x10, 0xf8, 0x04, 0x42, 0x87,
+ 0x59, 0x5d, 0x02, 0xb1, 0xd6, 0xe5, 0xc8, 0xda, 0x43, 0x30, 0xa3, 0xe8,
+ 0x37, 0xa5, 0xd2, 0x48, 0x0b, 0xa2, 0x83, 0x4e, 0x9d, 0x4f, 0x83, 0x58,
+ 0x9d, 0xd7, 0x47, 0x22, 0xb1, 0x89, 0xf0, 0x89, 0x3b, 0x3d, 0x28, 0x43,
+ 0x2c, 0x9b, 0x17, 0x7c, 0x03, 0xee, 0x9d, 0x26, 0x25, 0xe0, 0x04, 0xb8,
+ 0x1d, 0x04, 0x57, 0x42, 0x47, 0xda, 0x58, 0x69, 0xf0, 0xd3, 0x29, 0xab,
+ 0x12, 0x02, 0x99, 0x2b, 0x2a, 0xd8, 0x9d, 0xa0, 0x1f, 0x54, 0x5e, 0x23,
+ 0x9a, 0x0c, 0xd2, 0x99, 0x58, 0xc4, 0xa1, 0xe5, 0x49, 0xc2, 0x25, 0xa7,
+ 0x64, 0x20, 0x52, 0x2e, 0xe7, 0x89, 0xf5, 0x19, 0xc0, 0x8b, 0xd0, 0x63,
+ 0xb1, 0x78, 0x1e, 0xbe, 0x01, 0x47, 0xbe, 0x76, 0x81, 0x46, 0xf1, 0x99,
+ 0x1f, 0x94, 0x9a, 0xbe, 0xfa, 0x82, 0x15, 0xb5, 0x84, 0x84, 0x79, 0x75,
+ 0x93, 0xba, 0x9f, 0xb5, 0xe4, 0x9b, 0xc2, 0xcb, 0x69, 0x5c, 0xbd, 0x1f,
+ 0x55, 0x0a, 0xa7, 0x26, 0x30, 0x05, 0x51, 0xbe, 0x65, 0xee, 0x57, 0xa9,
+ 0x6a, 0xdf, 0xbd, 0xf9, 0x36, 0x2f, 0xad, 0x1e, 0x46, 0x41, 0x2b, 0xb1,
+ 0x88, 0xd0, 0x88, 0x25, 0x85, 0x40, 0x17, 0x79, 0xbf, 0x3d, 0x8d, 0xe2,
+ 0xf4, 0x2d, 0xea, 0x30, 0x31, 0xdf, 0xa1, 0x40, 0xcb, 0x35, 0xff, 0x82,
+ 0x9f, 0xf5, 0x99, 0x3c, 0x4a, 0xfd, 0x9d, 0xa1, 0xd1, 0x55, 0xcc, 0x20,
+ 0xa8, 0x1c, 0xd8, 0x20, 0x05, 0xab, 0xb3, 0x14, 0x65, 0x95, 0x53, 0xd8,
+ 0xe8, 0x8e, 0x57, 0xc5, 0x77, 0x6b, 0x2d, 0x4d, 0x88, 0xe9, 0x5d, 0x62,
+ 0xd5, 0xa2, 0xf8, 0x70, 0xe1, 0x70, 0xeb, 0x45, 0x23, 0x0e, 0xf0, 0x00,
+ 0x46, 0xc2, 0x48, 0x31, 0xe8, 0xe7, 0x36, 0x80, 0x36, 0x2d, 0x22, 0xf2,
+ 0x01, 0x27, 0x53, 0xeb, 0xce, 0xa7, 0x69, 0x49, 0x82, 0xbf, 0xe7, 0x0f,
+ 0x9c, 0xf3, 0x20, 0x2e, 0xf5, 0xfa, 0x5d, 0xce, 0xea, 0x58, 0x3a, 0x8f,
+ 0xd8, 0xaa, 0x7d, 0x30, 0xb7, 0x74, 0x96, 0x7c, 0x3d, 0x6e, 0xb4, 0xec,
+ 0x4a, 0x3b, 0x59, 0xb6, 0xa9, 0x50, 0x0d, 0x0f, 0x05, 0x06, 0x70, 0x26,
+ 0xb9, 0x95, 0x91, 0xd1, 0x5e, 0x24, 0x8c, 0x8f, 0xca, 0x74, 0x57, 0x97,
+ 0x90, 0x8b, 0x5a, 0xb7, 0xfe, 0x8d, 0xad, 0xd8, 0xe8, 0xc2, 0x06, 0xbc,
+ 0x08, 0x56, 0x21, 0x02, 0x12, 0x53, 0xc6, 0x9f, 0x86, 0x04, 0x58, 0xca,
+ 0x2d, 0xf8, 0x03, 0x0d, 0x57, 0x0b, 0x1c, 0x37, 0xbd, 0xf0, 0x5a, 0x35,
+ 0xf2, 0xfe, 0x3b, 0xd6, 0xa4, 0x37, 0x15, 0xe9, 0xf8, 0x08, 0x92, 0x96,
+ 0x3d, 0x74, 0xc8, 0xb5, 0x5c, 0x6e, 0x65, 0x08, 0xe7, 0xdf, 0x69, 0x73,
+ 0x9c, 0xec, 0xe3, 0x30, 0x5a, 0xa6, 0xdf, 0x5c, 0xbe, 0xda, 0x7f, 0x00,
+ 0xee, 0xa5, 0xda, 0x2b, 0x5c, 0x1e, 0x2a, 0x6a, 0xc0, 0xa3, 0xae, 0x1e,
+ 0xf1,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 7250751724796726 (0x19c28530e93b36)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+ Validity
+ Not Before: Sep 17 22:46:36 2006 GMT
+ Not After : Dec 31 23:59:59 2019 GMT
+ Subject: C=CN, O=WoSign CA Limited, CN=Certification Authority of WoSign
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:bd:ca:8d:ac:b8:91:15:56:97:7b:6b:5c:7a:c2:
+ de:6b:d9:a1:b0:c3:10:23:fa:a7:a1:b2:cc:31:fa:
+ 3e:d9:a6:29:6f:16:3d:e0:6b:f8:b8:40:5f:db:39:
+ a8:00:7a:8b:a0:4d:54:7d:c2:22:78:fc:8e:09:b8:
+ a8:85:d7:cc:95:97:4b:74:d8:9e:7e:f0:00:e4:0e:
+ 89:ae:49:28:44:1a:10:99:32:0f:25:88:53:a4:0d:
+ b3:0f:12:08:16:0b:03:71:27:1c:7f:e1:db:d2:fd:
+ 67:68:c4:05:5d:0a:0e:5d:70:d7:d8:97:a0:bc:53:
+ 41:9a:91:8d:f4:9e:36:66:7a:7e:56:c1:90:5f:e6:
+ b1:68:20:36:a4:8c:24:2c:2c:47:0b:59:76:66:30:
+ b5:be:de:ed:8f:f8:9d:d3:bb:01:30:e6:f2:f3:0e:
+ e0:2c:92:80:f3:85:f9:28:8a:b4:54:2e:9a:ed:f7:
+ 76:fc:15:68:16:eb:4a:6c:eb:2e:12:8f:d4:cf:fe:
+ 0c:c7:5c:1d:0b:7e:05:32:be:5e:b0:09:2a:42:d5:
+ c9:4e:90:b3:59:0d:bb:7a:7e:cd:d5:08:5a:b4:7f:
+ d8:1c:69:11:f9:27:0f:7b:06:af:54:83:18:7b:e1:
+ dd:54:7a:51:68:6e:77:fc:c6:bf:52:4a:66:46:a1:
+ b2:67:1a:bb:a3:4f:77:a0:be:5d:ff:fc:56:0b:43:
+ 72:77:90:ca:9e:f9:f2:39:f5:0d:a9:f4:ea:d7:e7:
+ b3:10:2f:30:42:37:21:cc:30:70:c9:86:98:0f:cc:
+ 58:4d:83:bb:7d:e5:1a:a5:37:8d:b6:ac:32:97:00:
+ 3a:63:71:24:1e:9e:37:c4:ff:74:d4:37:c0:e2:fe:
+ 88:46:60:11:dd:08:3f:50:36:ab:b8:7a:a4:95:62:
+ 6a:6e:b0:ca:6a:21:5a:69:f3:f3:fb:1d:70:39:95:
+ f3:a7:6e:a6:81:89:a1:88:c5:3b:71:ca:a3:52:ee:
+ 83:bb:fd:a0:77:f4:e4:6f:e7:42:db:6d:4a:99:8a:
+ 34:48:bc:17:dc:e4:80:08:22:b6:f2:31:c0:3f:04:
+ 3e:eb:9f:20:79:d6:b8:06:64:64:02:31:d7:a9:cd:
+ 52:fb:84:45:69:09:00:2a:dc:55:8b:c4:06:46:4b:
+ c0:4a:1d:09:5b:39:28:fd:a9:ab:ce:00:f9:2e:48:
+ 4b:26:e6:30:4c:a5:58:ca:b4:44:82:4f:e7:91:1e:
+ 33:c3:b0:93:ff:11:fc:81:d2:ca:1f:71:29:dd:76:
+ 4f:92:25:af:1d:81:b7:0f:2f:8c:c3:06:cc:2f:27:
+ a3:4a:e4:0e:99:ba:7c:1e:45:1f:7f:aa:19:45:96:
+ fd:fc:3d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:2
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ E1:66:CF:0E:D1:F1:B3:4B:B7:06:20:14:FE:87:12:D5:F6:FE:FB:3E
+ X509v3 Authority Key Identifier:
+ keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.startssl.com/ca
+ CA Issuers - URI:http://aia.startssl.com/certs/ca.crt
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.startssl.com/sfsca.crl
+
+ Signature Algorithm: sha256WithRSAEncryption
+ b6:6d:f8:70:fb:e2:0d:4c:98:b3:07:49:15:f5:04:c4:6c:ca:
+ ca:f5:68:a0:08:fe:12:6d:9c:04:06:c9:ad:9a:91:52:3e:78:
+ c4:5c:ee:9f:54:1d:ee:e3:f1:5e:30:c9:49:e1:39:e0:a6:9d:
+ 36:6c:57:fa:e6:34:4f:55:e8:87:a8:2c:dd:05:f1:58:12:91:
+ e8:ca:ce:28:78:8f:df:07:85:01:a5:dc:45:96:05:d4:80:b2:
+ 2b:05:9a:cb:9a:a5:8b:e0:3a:67:e6:73:47:be:4a:fd:27:b1:
+ 88:ef:e6:ca:cf:8d:0e:26:9f:fa:5f:57:78:ad:6d:fe:ae:9b:
+ 35:08:b1:c3:ba:c1:00:4a:4b:7d:14:bd:f7:f1:d3:55:18:ac:
+ d0:33:70:88:6d:c4:09:71:14:a6:2b:4f:88:81:e7:0b:00:37:
+ a9:15:7d:7e:d7:01:96:3f:2f:af:7b:62:ae:0a:4a:bf:4b:39:
+ 2e:35:10:8b:fe:04:39:e4:3c:3a:0c:09:56:40:3a:b5:f4:c2:
+ 68:0c:b5:f9:52:cd:ee:9d:f8:98:fc:78:e7:58:47:8f:1c:73:
+ 58:69:33:ab:ff:dd:df:8e:24:01:77:98:19:3a:b0:66:79:bc:
+ e1:08:a3:0e:4f:c1:04:b3:f3:01:c8:eb:d3:59:1c:35:d2:93:
+ 1e:70:65:82:7f:db:cf:fb:c8:99:12:60:c3:44:6f:3a:80:4b:
+ d7:be:21:aa:14:7a:64:cb:dd:37:43:45:5b:32:2e:45:f0:d9:
+ 59:1f:6b:18:f0:7c:e9:55:36:19:61:5f:b5:7d:f1:8d:bd:88:
+ e4:75:4b:98:dd:27:b0:e4:84:44:2a:61:84:57:05:82:11:1f:
+ aa:35:58:f3:20:0e:af:59:ef:fa:55:72:72:0d:26:d0:9b:53:
+ 49:ac:ce:37:2e:65:61:ff:f6:ec:1b:ea:f6:f1:a6:d3:d1:b5:
+ 7b:be:35:f4:22:c1:bc:8d:01:bd:68:5e:83:0d:2f:ec:d6:da:
+ 63:0c:27:d1:54:3e:e4:a8:d3:ce:4b:32:b8:91:94:ff:fb:5b:
+ 49:2d:75:18:a8:ba:71:9a:3b:ae:d9:c0:a9:4f:87:91:ed:8b:
+ 7b:6b:20:98:89:39:83:4f:80:c4:69:cc:17:c9:c8:4e:be:e4:
+ a9:a5:81:76:70:06:04:32:cd:83:65:f4:bc:7d:3e:13:bc:d2:
+ e8:6f:63:aa:b5:3b:da:8d:86:32:82:78:9d:d9:cc:ff:bf:57:
+ 64:74:ed:28:3d:44:62:15:61:4b:f7:94:b0:0d:2a:67:1c:f0:
+ cb:9b:a5:92:bf:f8:41:5a:c1:3d:60:ed:9f:bb:b8:6d:9b:ce:
+ a9:6a:16:3f:7e:ea:06:f1
+-----BEGIN CERTIFICATE-----
+MIIGXDCCBESgAwIBAgIHGcKFMOk7NjANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQG
+EwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERp
+Z2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MjI0NjM2WhcNMTkxMjMxMjM1
+OTU5WjBVMQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQx
+KjAoBgNVBAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL3Kjay4kRVWl3trXHrC3mvZobDD
+ECP6p6GyzDH6PtmmKW8WPeBr+LhAX9s5qAB6i6BNVH3CInj8jgm4qIXXzJWXS3TY
+nn7wAOQOia5JKEQaEJkyDyWIU6QNsw8SCBYLA3EnHH/h29L9Z2jEBV0KDl1w19iX
+oLxTQZqRjfSeNmZ6flbBkF/msWggNqSMJCwsRwtZdmYwtb7e7Y/4ndO7ATDm8vMO
+4CySgPOF+SiKtFQumu33dvwVaBbrSmzrLhKP1M/+DMdcHQt+BTK+XrAJKkLVyU6Q
+s1kNu3p+zdUIWrR/2BxpEfknD3sGr1SDGHvh3VR6UWhud/zGv1JKZkahsmcau6NP
+d6C+Xf/8VgtDcneQyp758jn1Dan06tfnsxAvMEI3IcwwcMmGmA/MWE2Du33lGqU3
+jbasMpcAOmNxJB6eN8T/dNQ3wOL+iEZgEd0IP1A2q7h6pJViam6wymohWmnz8/sd
+cDmV86dupoGJoYjFO3HKo1Lug7v9oHf05G/nQtttSpmKNEi8F9zkgAgitvIxwD8E
+PuufIHnWuAZkZAIx16nNUvuERWkJACrcVYvEBkZLwEodCVs5KP2pq84A+S5ISybm
+MEylWMq0RIJP55EeM8Owk/8R/IHSyh9xKd12T5Ilrx2Btw8vjMMGzC8no0rkDpm6
+fB5FH3+qGUWW/fw9AgMBAAGjggEHMIIBAzASBgNVHRMBAf8ECDAGAQH/AgECMA4G
+A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU4WbPDtHxs0u3BiAU/ocS1fb++z4wHwYD
+VR0jBBgwFoAUTgvvGqRAW6UXaYcwyjRoQ9BBrvIwaQYIKwYBBQUHAQEEXTBbMCcG
+CCsGAQUFBzABhhtodHRwOi8vb2NzcC5zdGFydHNzbC5jb20vY2EwMAYIKwYBBQUH
+MAKGJGh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2NlcnRzL2NhLmNydDAyBgNVHR8E
+KzApMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5jcmwwDQYJ
+KoZIhvcNAQELBQADggIBALZt+HD74g1MmLMHSRX1BMRsysr1aKAI/hJtnAQGya2a
+kVI+eMRc7p9UHe7j8V4wyUnhOeCmnTZsV/rmNE9V6IeoLN0F8VgSkejKzih4j98H
+hQGl3EWWBdSAsisFmsuapYvgOmfmc0e+Sv0nsYjv5srPjQ4mn/pfV3itbf6umzUI
+scO6wQBKS30Uvffx01UYrNAzcIhtxAlxFKYrT4iB5wsAN6kVfX7XAZY/L697Yq4K
+Sr9LOS41EIv+BDnkPDoMCVZAOrX0wmgMtflSze6d+Jj8eOdYR48cc1hpM6v/3d+O
+JAF3mBk6sGZ5vOEIow5PwQSz8wHI69NZHDXSkx5wZYJ/28/7yJkSYMNEbzqAS9e+
+IaoUemTL3TdDRVsyLkXw2VkfaxjwfOlVNhlhX7V98Y29iOR1S5jdJ7DkhEQqYYRX
+BYIRH6o1WPMgDq9Z7/pVcnINJtCbU0mszjcuZWH/9uwb6vbxptPRtXu+NfQiwbyN
+Ab1oXoMNL+zW2mMMJ9FUPuSo085LMriRlP/7W0ktdRiounGaO67ZwKlPh5Hti3tr
+IJiJOYNPgMRpzBfJyE6+5KmlgXZwBgQyzYNl9Lx9PhO80uhvY6q1O9qNhjKCeJ3Z
+zP+/V2R07Sg9RGIVYUv3lLANKmcc8MubpZK/+EFawT1g7Z+7uG2bzqlqFj9+6gbx
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert51[] = {
+ 0x30, 0x82, 0x06, 0x5c, 0x30, 0x82, 0x04, 0x44, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x07, 0x19, 0xc2, 0x85, 0x30, 0xe9, 0x3b, 0x36, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20,
+ 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e,
+ 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x36, 0x30, 0x39, 0x31, 0x37, 0x32, 0x32, 0x34, 0x36, 0x33, 0x36,
+ 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35,
+ 0x39, 0x35, 0x39, 0x5a, 0x30, 0x55, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x4e, 0x31, 0x1a, 0x30, 0x18, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e,
+ 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31,
+ 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x57, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x30, 0x82, 0x02, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02,
+ 0x82, 0x02, 0x01, 0x00, 0xbd, 0xca, 0x8d, 0xac, 0xb8, 0x91, 0x15, 0x56,
+ 0x97, 0x7b, 0x6b, 0x5c, 0x7a, 0xc2, 0xde, 0x6b, 0xd9, 0xa1, 0xb0, 0xc3,
+ 0x10, 0x23, 0xfa, 0xa7, 0xa1, 0xb2, 0xcc, 0x31, 0xfa, 0x3e, 0xd9, 0xa6,
+ 0x29, 0x6f, 0x16, 0x3d, 0xe0, 0x6b, 0xf8, 0xb8, 0x40, 0x5f, 0xdb, 0x39,
+ 0xa8, 0x00, 0x7a, 0x8b, 0xa0, 0x4d, 0x54, 0x7d, 0xc2, 0x22, 0x78, 0xfc,
+ 0x8e, 0x09, 0xb8, 0xa8, 0x85, 0xd7, 0xcc, 0x95, 0x97, 0x4b, 0x74, 0xd8,
+ 0x9e, 0x7e, 0xf0, 0x00, 0xe4, 0x0e, 0x89, 0xae, 0x49, 0x28, 0x44, 0x1a,
+ 0x10, 0x99, 0x32, 0x0f, 0x25, 0x88, 0x53, 0xa4, 0x0d, 0xb3, 0x0f, 0x12,
+ 0x08, 0x16, 0x0b, 0x03, 0x71, 0x27, 0x1c, 0x7f, 0xe1, 0xdb, 0xd2, 0xfd,
+ 0x67, 0x68, 0xc4, 0x05, 0x5d, 0x0a, 0x0e, 0x5d, 0x70, 0xd7, 0xd8, 0x97,
+ 0xa0, 0xbc, 0x53, 0x41, 0x9a, 0x91, 0x8d, 0xf4, 0x9e, 0x36, 0x66, 0x7a,
+ 0x7e, 0x56, 0xc1, 0x90, 0x5f, 0xe6, 0xb1, 0x68, 0x20, 0x36, 0xa4, 0x8c,
+ 0x24, 0x2c, 0x2c, 0x47, 0x0b, 0x59, 0x76, 0x66, 0x30, 0xb5, 0xbe, 0xde,
+ 0xed, 0x8f, 0xf8, 0x9d, 0xd3, 0xbb, 0x01, 0x30, 0xe6, 0xf2, 0xf3, 0x0e,
+ 0xe0, 0x2c, 0x92, 0x80, 0xf3, 0x85, 0xf9, 0x28, 0x8a, 0xb4, 0x54, 0x2e,
+ 0x9a, 0xed, 0xf7, 0x76, 0xfc, 0x15, 0x68, 0x16, 0xeb, 0x4a, 0x6c, 0xeb,
+ 0x2e, 0x12, 0x8f, 0xd4, 0xcf, 0xfe, 0x0c, 0xc7, 0x5c, 0x1d, 0x0b, 0x7e,
+ 0x05, 0x32, 0xbe, 0x5e, 0xb0, 0x09, 0x2a, 0x42, 0xd5, 0xc9, 0x4e, 0x90,
+ 0xb3, 0x59, 0x0d, 0xbb, 0x7a, 0x7e, 0xcd, 0xd5, 0x08, 0x5a, 0xb4, 0x7f,
+ 0xd8, 0x1c, 0x69, 0x11, 0xf9, 0x27, 0x0f, 0x7b, 0x06, 0xaf, 0x54, 0x83,
+ 0x18, 0x7b, 0xe1, 0xdd, 0x54, 0x7a, 0x51, 0x68, 0x6e, 0x77, 0xfc, 0xc6,
+ 0xbf, 0x52, 0x4a, 0x66, 0x46, 0xa1, 0xb2, 0x67, 0x1a, 0xbb, 0xa3, 0x4f,
+ 0x77, 0xa0, 0xbe, 0x5d, 0xff, 0xfc, 0x56, 0x0b, 0x43, 0x72, 0x77, 0x90,
+ 0xca, 0x9e, 0xf9, 0xf2, 0x39, 0xf5, 0x0d, 0xa9, 0xf4, 0xea, 0xd7, 0xe7,
+ 0xb3, 0x10, 0x2f, 0x30, 0x42, 0x37, 0x21, 0xcc, 0x30, 0x70, 0xc9, 0x86,
+ 0x98, 0x0f, 0xcc, 0x58, 0x4d, 0x83, 0xbb, 0x7d, 0xe5, 0x1a, 0xa5, 0x37,
+ 0x8d, 0xb6, 0xac, 0x32, 0x97, 0x00, 0x3a, 0x63, 0x71, 0x24, 0x1e, 0x9e,
+ 0x37, 0xc4, 0xff, 0x74, 0xd4, 0x37, 0xc0, 0xe2, 0xfe, 0x88, 0x46, 0x60,
+ 0x11, 0xdd, 0x08, 0x3f, 0x50, 0x36, 0xab, 0xb8, 0x7a, 0xa4, 0x95, 0x62,
+ 0x6a, 0x6e, 0xb0, 0xca, 0x6a, 0x21, 0x5a, 0x69, 0xf3, 0xf3, 0xfb, 0x1d,
+ 0x70, 0x39, 0x95, 0xf3, 0xa7, 0x6e, 0xa6, 0x81, 0x89, 0xa1, 0x88, 0xc5,
+ 0x3b, 0x71, 0xca, 0xa3, 0x52, 0xee, 0x83, 0xbb, 0xfd, 0xa0, 0x77, 0xf4,
+ 0xe4, 0x6f, 0xe7, 0x42, 0xdb, 0x6d, 0x4a, 0x99, 0x8a, 0x34, 0x48, 0xbc,
+ 0x17, 0xdc, 0xe4, 0x80, 0x08, 0x22, 0xb6, 0xf2, 0x31, 0xc0, 0x3f, 0x04,
+ 0x3e, 0xeb, 0x9f, 0x20, 0x79, 0xd6, 0xb8, 0x06, 0x64, 0x64, 0x02, 0x31,
+ 0xd7, 0xa9, 0xcd, 0x52, 0xfb, 0x84, 0x45, 0x69, 0x09, 0x00, 0x2a, 0xdc,
+ 0x55, 0x8b, 0xc4, 0x06, 0x46, 0x4b, 0xc0, 0x4a, 0x1d, 0x09, 0x5b, 0x39,
+ 0x28, 0xfd, 0xa9, 0xab, 0xce, 0x00, 0xf9, 0x2e, 0x48, 0x4b, 0x26, 0xe6,
+ 0x30, 0x4c, 0xa5, 0x58, 0xca, 0xb4, 0x44, 0x82, 0x4f, 0xe7, 0x91, 0x1e,
+ 0x33, 0xc3, 0xb0, 0x93, 0xff, 0x11, 0xfc, 0x81, 0xd2, 0xca, 0x1f, 0x71,
+ 0x29, 0xdd, 0x76, 0x4f, 0x92, 0x25, 0xaf, 0x1d, 0x81, 0xb7, 0x0f, 0x2f,
+ 0x8c, 0xc3, 0x06, 0xcc, 0x2f, 0x27, 0xa3, 0x4a, 0xe4, 0x0e, 0x99, 0xba,
+ 0x7c, 0x1e, 0x45, 0x1f, 0x7f, 0xaa, 0x19, 0x45, 0x96, 0xfd, 0xfc, 0x3d,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x07, 0x30, 0x82, 0x01,
+ 0x03, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x02, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0xe1, 0x66, 0xcf, 0x0e, 0xd1, 0xf1, 0xb3, 0x4b, 0xb7, 0x06, 0x20, 0x14,
+ 0xfe, 0x87, 0x12, 0xd5, 0xf6, 0xfe, 0xfb, 0x3e, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef,
+ 0x1a, 0xa4, 0x40, 0x5b, 0xa5, 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68,
+ 0x43, 0xd0, 0x41, 0xae, 0xf2, 0x30, 0x69, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x5d, 0x30, 0x5b, 0x30, 0x27, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x61, 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61,
+ 0x69, 0x61, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x63, 0x61,
+ 0x2e, 0x63, 0x72, 0x74, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73,
+ 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
+ 0x82, 0x02, 0x01, 0x00, 0xb6, 0x6d, 0xf8, 0x70, 0xfb, 0xe2, 0x0d, 0x4c,
+ 0x98, 0xb3, 0x07, 0x49, 0x15, 0xf5, 0x04, 0xc4, 0x6c, 0xca, 0xca, 0xf5,
+ 0x68, 0xa0, 0x08, 0xfe, 0x12, 0x6d, 0x9c, 0x04, 0x06, 0xc9, 0xad, 0x9a,
+ 0x91, 0x52, 0x3e, 0x78, 0xc4, 0x5c, 0xee, 0x9f, 0x54, 0x1d, 0xee, 0xe3,
+ 0xf1, 0x5e, 0x30, 0xc9, 0x49, 0xe1, 0x39, 0xe0, 0xa6, 0x9d, 0x36, 0x6c,
+ 0x57, 0xfa, 0xe6, 0x34, 0x4f, 0x55, 0xe8, 0x87, 0xa8, 0x2c, 0xdd, 0x05,
+ 0xf1, 0x58, 0x12, 0x91, 0xe8, 0xca, 0xce, 0x28, 0x78, 0x8f, 0xdf, 0x07,
+ 0x85, 0x01, 0xa5, 0xdc, 0x45, 0x96, 0x05, 0xd4, 0x80, 0xb2, 0x2b, 0x05,
+ 0x9a, 0xcb, 0x9a, 0xa5, 0x8b, 0xe0, 0x3a, 0x67, 0xe6, 0x73, 0x47, 0xbe,
+ 0x4a, 0xfd, 0x27, 0xb1, 0x88, 0xef, 0xe6, 0xca, 0xcf, 0x8d, 0x0e, 0x26,
+ 0x9f, 0xfa, 0x5f, 0x57, 0x78, 0xad, 0x6d, 0xfe, 0xae, 0x9b, 0x35, 0x08,
+ 0xb1, 0xc3, 0xba, 0xc1, 0x00, 0x4a, 0x4b, 0x7d, 0x14, 0xbd, 0xf7, 0xf1,
+ 0xd3, 0x55, 0x18, 0xac, 0xd0, 0x33, 0x70, 0x88, 0x6d, 0xc4, 0x09, 0x71,
+ 0x14, 0xa6, 0x2b, 0x4f, 0x88, 0x81, 0xe7, 0x0b, 0x00, 0x37, 0xa9, 0x15,
+ 0x7d, 0x7e, 0xd7, 0x01, 0x96, 0x3f, 0x2f, 0xaf, 0x7b, 0x62, 0xae, 0x0a,
+ 0x4a, 0xbf, 0x4b, 0x39, 0x2e, 0x35, 0x10, 0x8b, 0xfe, 0x04, 0x39, 0xe4,
+ 0x3c, 0x3a, 0x0c, 0x09, 0x56, 0x40, 0x3a, 0xb5, 0xf4, 0xc2, 0x68, 0x0c,
+ 0xb5, 0xf9, 0x52, 0xcd, 0xee, 0x9d, 0xf8, 0x98, 0xfc, 0x78, 0xe7, 0x58,
+ 0x47, 0x8f, 0x1c, 0x73, 0x58, 0x69, 0x33, 0xab, 0xff, 0xdd, 0xdf, 0x8e,
+ 0x24, 0x01, 0x77, 0x98, 0x19, 0x3a, 0xb0, 0x66, 0x79, 0xbc, 0xe1, 0x08,
+ 0xa3, 0x0e, 0x4f, 0xc1, 0x04, 0xb3, 0xf3, 0x01, 0xc8, 0xeb, 0xd3, 0x59,
+ 0x1c, 0x35, 0xd2, 0x93, 0x1e, 0x70, 0x65, 0x82, 0x7f, 0xdb, 0xcf, 0xfb,
+ 0xc8, 0x99, 0x12, 0x60, 0xc3, 0x44, 0x6f, 0x3a, 0x80, 0x4b, 0xd7, 0xbe,
+ 0x21, 0xaa, 0x14, 0x7a, 0x64, 0xcb, 0xdd, 0x37, 0x43, 0x45, 0x5b, 0x32,
+ 0x2e, 0x45, 0xf0, 0xd9, 0x59, 0x1f, 0x6b, 0x18, 0xf0, 0x7c, 0xe9, 0x55,
+ 0x36, 0x19, 0x61, 0x5f, 0xb5, 0x7d, 0xf1, 0x8d, 0xbd, 0x88, 0xe4, 0x75,
+ 0x4b, 0x98, 0xdd, 0x27, 0xb0, 0xe4, 0x84, 0x44, 0x2a, 0x61, 0x84, 0x57,
+ 0x05, 0x82, 0x11, 0x1f, 0xaa, 0x35, 0x58, 0xf3, 0x20, 0x0e, 0xaf, 0x59,
+ 0xef, 0xfa, 0x55, 0x72, 0x72, 0x0d, 0x26, 0xd0, 0x9b, 0x53, 0x49, 0xac,
+ 0xce, 0x37, 0x2e, 0x65, 0x61, 0xff, 0xf6, 0xec, 0x1b, 0xea, 0xf6, 0xf1,
+ 0xa6, 0xd3, 0xd1, 0xb5, 0x7b, 0xbe, 0x35, 0xf4, 0x22, 0xc1, 0xbc, 0x8d,
+ 0x01, 0xbd, 0x68, 0x5e, 0x83, 0x0d, 0x2f, 0xec, 0xd6, 0xda, 0x63, 0x0c,
+ 0x27, 0xd1, 0x54, 0x3e, 0xe4, 0xa8, 0xd3, 0xce, 0x4b, 0x32, 0xb8, 0x91,
+ 0x94, 0xff, 0xfb, 0x5b, 0x49, 0x2d, 0x75, 0x18, 0xa8, 0xba, 0x71, 0x9a,
+ 0x3b, 0xae, 0xd9, 0xc0, 0xa9, 0x4f, 0x87, 0x91, 0xed, 0x8b, 0x7b, 0x6b,
+ 0x20, 0x98, 0x89, 0x39, 0x83, 0x4f, 0x80, 0xc4, 0x69, 0xcc, 0x17, 0xc9,
+ 0xc8, 0x4e, 0xbe, 0xe4, 0xa9, 0xa5, 0x81, 0x76, 0x70, 0x06, 0x04, 0x32,
+ 0xcd, 0x83, 0x65, 0xf4, 0xbc, 0x7d, 0x3e, 0x13, 0xbc, 0xd2, 0xe8, 0x6f,
+ 0x63, 0xaa, 0xb5, 0x3b, 0xda, 0x8d, 0x86, 0x32, 0x82, 0x78, 0x9d, 0xd9,
+ 0xcc, 0xff, 0xbf, 0x57, 0x64, 0x74, 0xed, 0x28, 0x3d, 0x44, 0x62, 0x15,
+ 0x61, 0x4b, 0xf7, 0x94, 0xb0, 0x0d, 0x2a, 0x67, 0x1c, 0xf0, 0xcb, 0x9b,
+ 0xa5, 0x92, 0xbf, 0xf8, 0x41, 0x5a, 0xc1, 0x3d, 0x60, 0xed, 0x9f, 0xbb,
+ 0xb8, 0x6d, 0x9b, 0xce, 0xa9, 0x6a, 0x16, 0x3f, 0x7e, 0xea, 0x06, 0xf1,
+};
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_test.cc
new file mode 100644
index 00000000000..04720e1fd62
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/common_cert_set_test.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+// Google Internet Authority cert from v2 of the cert set.
+static const unsigned char kGIACertificate2[] = {
+ 0x30, 0x82, 0x03, 0xf0, 0x30, 0x82, 0x02, 0xd8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x83, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30,
+ 0x34, 0x30, 0x35, 0x31, 0x35, 0x31, 0x35, 0x35, 0x36, 0x5a, 0x17, 0x0d,
+ 0x31, 0x36, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39,
+ 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e,
+ 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c,
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3,
+ 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a,
+ 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a,
+ 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f,
+ 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1,
+ 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4,
+ 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53,
+ 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f,
+ 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73,
+ 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b,
+ 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb,
+ 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4,
+ 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19,
+ 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65,
+ 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80,
+ 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99,
+ 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75,
+ 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e,
+ 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf,
+ 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2,
+ 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37,
+ 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81,
+ 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x35, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0,
+ 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e,
+ 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72,
+ 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10,
+ 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
+ 0x79, 0x02, 0x05, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0xaa, 0xfa, 0xa9, 0x20, 0xcd, 0x6a, 0x67, 0x83, 0xed, 0x5e, 0xd4, 0x7e,
+ 0xde, 0x1d, 0xc4, 0x7f, 0xe0, 0x25, 0x06, 0x00, 0xc5, 0x24, 0xfb, 0xa9,
+ 0xc8, 0x2d, 0x6d, 0x7e, 0xde, 0x9d, 0x82, 0x65, 0x2c, 0x81, 0x63, 0x34,
+ 0x66, 0x3e, 0xe9, 0x52, 0xc2, 0x08, 0xb4, 0xcb, 0x2f, 0xf7, 0x5f, 0x99,
+ 0x3a, 0x6a, 0x9c, 0x50, 0x7a, 0x85, 0x05, 0x8c, 0x7d, 0xd1, 0x2a, 0x48,
+ 0x84, 0xd3, 0x09, 0x6c, 0x7c, 0xc2, 0xcd, 0x35, 0x9f, 0xf3, 0x82, 0xee,
+ 0x52, 0xde, 0x68, 0x5f, 0xe4, 0x00, 0x8a, 0x17, 0x20, 0x96, 0xf7, 0x29,
+ 0x8d, 0x9a, 0x4d, 0xcb, 0xa8, 0xde, 0x86, 0xc8, 0x0d, 0x6f, 0x56, 0x87,
+ 0x03, 0x7d, 0x03, 0x3f, 0xdc, 0xfa, 0x79, 0x7d, 0x21, 0x19, 0xf9, 0xc8,
+ 0x3a, 0x2f, 0x51, 0x76, 0x8c, 0xc7, 0x41, 0x92, 0x71, 0x8f, 0x25, 0xce,
+ 0x37, 0xf8, 0x4a, 0x4c, 0x00, 0x23, 0xef, 0xc4, 0x35, 0x10, 0xae, 0xe0,
+ 0x23, 0x80, 0x73, 0x7c, 0x4d, 0x34, 0x2e, 0xc8, 0x6e, 0x90, 0xd6, 0x10,
+ 0x1e, 0x99, 0x84, 0x73, 0x1a, 0x70, 0xf2, 0xed, 0x55, 0x0e, 0xee, 0x17,
+ 0x06, 0xea, 0x67, 0xee, 0x32, 0xeb, 0x2c, 0xdd, 0x67, 0x07, 0x3f, 0xf6,
+ 0x8b, 0xc2, 0x70, 0xde, 0x5b, 0x00, 0xe6, 0xbb, 0x1b, 0xd3, 0x36, 0x1a,
+ 0x22, 0x6c, 0x6c, 0xb0, 0x35, 0x42, 0x6c, 0x90, 0x09, 0x3d, 0x93, 0xe9,
+ 0x64, 0x09, 0x22, 0x0e, 0x85, 0x06, 0x9f, 0xc2, 0x73, 0x21, 0xd3, 0xe6,
+ 0x5f, 0x80, 0xe4, 0x8d, 0x85, 0x22, 0x3a, 0x73, 0x03, 0xb1, 0x60, 0x8e,
+ 0xae, 0x68, 0xe2, 0xf4, 0x3e, 0x97, 0xe7, 0x60, 0x12, 0x09, 0x68, 0x36,
+ 0xde, 0x3a, 0xd6, 0xe2, 0x43, 0x95, 0x5b, 0x37, 0x81, 0x92, 0x81, 0x1f,
+ 0xbb, 0x8d, 0xd7, 0xad, 0x52, 0x64, 0x16, 0x57, 0x96, 0xd9, 0x5e, 0x34,
+ 0x7e, 0xc8, 0x35, 0xd8,
+};
+
+// Google Internet Authority cert from v3 of the cert set.
+static const unsigned char kGIACertificate3[] = {
+ 0x30, 0x82, 0x03, 0xf0, 0x30, 0x82, 0x02, 0xd8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30,
+ 0x34, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d,
+ 0x31, 0x37, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39,
+ 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e,
+ 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c,
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3,
+ 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a,
+ 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a,
+ 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f,
+ 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1,
+ 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4,
+ 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53,
+ 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f,
+ 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73,
+ 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b,
+ 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb,
+ 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4,
+ 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19,
+ 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65,
+ 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80,
+ 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99,
+ 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75,
+ 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e,
+ 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf,
+ 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2,
+ 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37,
+ 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81,
+ 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x22, 0x30, 0x20, 0x30, 0x1e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x67, 0x2e, 0x73, 0x79, 0x6d, 0x63, 0x64, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x35, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0,
+ 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x2e,
+ 0x73, 0x79, 0x6d, 0x63, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72,
+ 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10,
+ 0x30, 0x0e, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6,
+ 0x79, 0x02, 0x05, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x08, 0x4e, 0x04, 0xa7, 0x80, 0x7f, 0x10, 0x16, 0x43, 0x5e, 0x02, 0xad,
+ 0xd7, 0x42, 0x80, 0xf4, 0xb0, 0x8e, 0xd2, 0xae, 0xb3, 0xeb, 0x11, 0x7d,
+ 0x90, 0x84, 0x18, 0x7d, 0xe7, 0x90, 0x15, 0xfb, 0x49, 0x7f, 0xa8, 0x99,
+ 0x05, 0x91, 0xbb, 0x7a, 0xc9, 0xd6, 0x3c, 0x37, 0x18, 0x09, 0x9a, 0xb6,
+ 0xc7, 0x92, 0x20, 0x07, 0x35, 0x33, 0x09, 0xe4, 0x28, 0x63, 0x72, 0x0d,
+ 0xb4, 0xe0, 0x32, 0x9c, 0x87, 0x98, 0xc4, 0x1b, 0x76, 0x89, 0x67, 0xc1,
+ 0x50, 0x58, 0xb0, 0x13, 0xaa, 0x13, 0x1a, 0x1b, 0x32, 0xa5, 0xbe, 0xea,
+ 0x11, 0x95, 0x4c, 0x48, 0x63, 0x49, 0xe9, 0x99, 0x5d, 0x20, 0x37, 0xcc,
+ 0xfe, 0x2a, 0x69, 0x51, 0x16, 0x95, 0x4b, 0xa9, 0xde, 0x49, 0x82, 0xc0,
+ 0x10, 0x70, 0xf4, 0x2c, 0xf3, 0xec, 0xbc, 0x24, 0x24, 0xd0, 0x4e, 0xac,
+ 0xa5, 0xd9, 0x5e, 0x1e, 0x6d, 0x92, 0xc1, 0xa7, 0xac, 0x48, 0x35, 0x81,
+ 0xf9, 0xe5, 0xe4, 0x9c, 0x65, 0x69, 0xcd, 0x87, 0xa4, 0x41, 0x50, 0x3f,
+ 0x2e, 0x57, 0xa5, 0x91, 0x51, 0x12, 0x58, 0x0e, 0x8c, 0x09, 0xa1, 0xac,
+ 0x7a, 0xa4, 0x12, 0xa5, 0x27, 0xf3, 0x9a, 0x10, 0x97, 0x7d, 0x55, 0x03,
+ 0x06, 0xf7, 0x66, 0x58, 0x5f, 0x5f, 0x64, 0xe1, 0xab, 0x5d, 0x6d, 0xa5,
+ 0x39, 0x48, 0x75, 0x98, 0x4c, 0x29, 0x5a, 0x3a, 0x8d, 0xd3, 0x2b, 0xca,
+ 0x9c, 0x55, 0x04, 0xbf, 0xf4, 0xe6, 0x14, 0xd5, 0x80, 0xac, 0x26, 0xed,
+ 0x17, 0x89, 0xa6, 0x93, 0x6c, 0x5c, 0xa4, 0xcc, 0xb8, 0xf0, 0x66, 0x8e,
+ 0x64, 0xe3, 0x7d, 0x9a, 0xe2, 0x00, 0xb3, 0x49, 0xc7, 0xe4, 0x0a, 0xaa,
+ 0xdd, 0x5b, 0x83, 0xc7, 0x70, 0x90, 0x46, 0x4e, 0xbe, 0xd0, 0xdb, 0x59,
+ 0x96, 0x6c, 0x2e, 0xf5, 0x16, 0x36, 0xde, 0x71, 0xcc, 0x01, 0xc2, 0x12,
+ 0xc1, 0x21, 0xc6, 0x16,
+};
+
+class CommonCertSetsTest : public QuicTest {};
+
+TEST_F(CommonCertSetsTest, FindGIA_2) {
+ QuicStringPiece gia(reinterpret_cast<const char*>(kGIACertificate2),
+ sizeof(kGIACertificate2));
+
+ const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC());
+ // Common Cert Set 2's hash.
+ const uint64_t in_hash = UINT64_C(0xe81a92926081e801);
+ uint64_t hash;
+ uint32_t index;
+ ASSERT_TRUE(sets->MatchCert(
+ gia,
+ QuicStringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)),
+ &hash, &index));
+ EXPECT_EQ(in_hash, hash);
+
+ QuicStringPiece gia_copy = sets->GetCert(hash, index);
+ EXPECT_FALSE(gia_copy.empty());
+ ASSERT_EQ(gia.size(), gia_copy.size());
+ EXPECT_EQ(0, memcmp(gia.data(), gia_copy.data(), gia.size()));
+}
+
+TEST_F(CommonCertSetsTest, FindGIA_3) {
+ QuicStringPiece gia(reinterpret_cast<const char*>(kGIACertificate3),
+ sizeof(kGIACertificate3));
+
+ const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC());
+ // Common Cert Set 3's hash.
+ const uint64_t in_hash = UINT64_C(0x918215a28680ed7e);
+ uint64_t hash;
+ uint32_t index;
+ ASSERT_TRUE(sets->MatchCert(
+ gia,
+ QuicStringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)),
+ &hash, &index));
+ EXPECT_EQ(in_hash, hash);
+
+ QuicStringPiece gia_copy = sets->GetCert(hash, index);
+ EXPECT_FALSE(gia_copy.empty());
+ ASSERT_EQ(gia.size(), gia_copy.size());
+ EXPECT_EQ(0, memcmp(gia.data(), gia_copy.data(), gia.size()));
+}
+
+TEST_F(CommonCertSetsTest, NonMatch) {
+ const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC());
+ QuicStringPiece not_a_cert("hello");
+ const uint64_t in_hash = UINT64_C(0xc9fef74053f99f39);
+ uint64_t hash;
+ uint32_t index;
+ EXPECT_FALSE(sets->MatchCert(
+ not_a_cert,
+ QuicStringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)),
+ &hash, &index));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer.cc
new file mode 100644
index 00000000000..ddfd4660e38
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer.cc
@@ -0,0 +1,353 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+namespace {
+
+const size_t kQuicTagSize = sizeof(QuicTag);
+const size_t kCryptoEndOffsetSize = sizeof(uint32_t);
+const size_t kNumEntriesSize = sizeof(uint16_t);
+
+// OneShotVisitor is a framer visitor that records a single handshake message.
+class OneShotVisitor : public CryptoFramerVisitorInterface {
+ public:
+ OneShotVisitor() : error_(false) {}
+
+ void OnError(CryptoFramer* framer) override { error_ = true; }
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ out_ = QuicMakeUnique<CryptoHandshakeMessage>(message);
+ }
+
+ bool error() const { return error_; }
+
+ std::unique_ptr<CryptoHandshakeMessage> release() { return std::move(out_); }
+
+ private:
+ std::unique_ptr<CryptoHandshakeMessage> out_;
+ bool error_;
+};
+
+} // namespace
+
+CryptoFramer::CryptoFramer()
+ : visitor_(nullptr),
+ error_detail_(""),
+ num_entries_(0),
+ values_len_(0),
+ process_truncated_messages_(false) {
+ Clear();
+}
+
+CryptoFramer::~CryptoFramer() {}
+
+// static
+std::unique_ptr<CryptoHandshakeMessage> CryptoFramer::ParseMessage(
+ QuicStringPiece in) {
+ OneShotVisitor visitor;
+ CryptoFramer framer;
+
+ framer.set_visitor(&visitor);
+ if (!framer.ProcessInput(in) || visitor.error() ||
+ framer.InputBytesRemaining()) {
+ return nullptr;
+ }
+
+ return visitor.release();
+}
+
+QuicErrorCode CryptoFramer::error() const {
+ return error_;
+}
+
+const std::string& CryptoFramer::error_detail() const {
+ return error_detail_;
+}
+
+bool CryptoFramer::ProcessInput(QuicStringPiece input, EncryptionLevel level) {
+ return ProcessInput(input);
+}
+
+bool CryptoFramer::ProcessInput(QuicStringPiece input) {
+ DCHECK_EQ(QUIC_NO_ERROR, error_);
+ if (error_ != QUIC_NO_ERROR) {
+ return false;
+ }
+ error_ = Process(input);
+ if (error_ != QUIC_NO_ERROR) {
+ DCHECK(!error_detail_.empty());
+ visitor_->OnError(this);
+ return false;
+ }
+
+ return true;
+}
+
+size_t CryptoFramer::InputBytesRemaining() const {
+ return buffer_.length();
+}
+
+bool CryptoFramer::HasTag(QuicTag tag) const {
+ if (state_ != STATE_READING_VALUES) {
+ return false;
+ }
+ for (const auto& it : tags_and_lengths_) {
+ if (it.first == tag) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void CryptoFramer::ForceHandshake() {
+ QuicDataReader reader(buffer_.data(), buffer_.length(), HOST_BYTE_ORDER);
+ for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) {
+ QuicStringPiece value;
+ if (reader.BytesRemaining() < item.second) {
+ break;
+ }
+ reader.ReadStringPiece(&value, item.second);
+ message_.SetStringPiece(item.first, value);
+ }
+ visitor_->OnHandshakeMessage(message_);
+}
+
+// static
+std::unique_ptr<QuicData> CryptoFramer::ConstructHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ size_t num_entries = message.tag_value_map().size();
+ size_t pad_length = 0;
+ bool need_pad_tag = false;
+ bool need_pad_value = false;
+
+ size_t len = message.size();
+ if (len < message.minimum_size()) {
+ need_pad_tag = true;
+ need_pad_value = true;
+ num_entries++;
+
+ size_t delta = message.minimum_size() - len;
+ const size_t overhead = kQuicTagSize + kCryptoEndOffsetSize;
+ if (delta > overhead) {
+ pad_length = delta - overhead;
+ }
+ len += overhead + pad_length;
+ }
+
+ if (num_entries > kMaxEntries) {
+ return nullptr;
+ }
+
+ std::unique_ptr<char[]> buffer(new char[len]);
+ QuicDataWriter writer(len, buffer.get(), HOST_BYTE_ORDER);
+ if (!writer.WriteTag(message.tag())) {
+ DCHECK(false) << "Failed to write message tag.";
+ return nullptr;
+ }
+ if (!writer.WriteUInt16(static_cast<uint16_t>(num_entries))) {
+ DCHECK(false) << "Failed to write size.";
+ return nullptr;
+ }
+ if (!writer.WriteUInt16(0)) {
+ DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+
+ uint32_t end_offset = 0;
+ // Tags and offsets
+ for (auto it = message.tag_value_map().begin();
+ it != message.tag_value_map().end(); ++it) {
+ if (it->first == kPAD && need_pad_tag) {
+ // Existing PAD tags are only checked when padding needs to be added
+ // because parts of the code may need to reserialize received messages
+ // and those messages may, legitimately include padding.
+ DCHECK(false) << "Message needed padding but already contained a PAD tag";
+ return nullptr;
+ }
+
+ if (it->first > kPAD && need_pad_tag) {
+ need_pad_tag = false;
+ if (!WritePadTag(&writer, pad_length, &end_offset)) {
+ return nullptr;
+ }
+ }
+
+ if (!writer.WriteTag(it->first)) {
+ DCHECK(false) << "Failed to write tag.";
+ return nullptr;
+ }
+ end_offset += it->second.length();
+ if (!writer.WriteUInt32(end_offset)) {
+ DCHECK(false) << "Failed to write end offset.";
+ return nullptr;
+ }
+ }
+
+ if (need_pad_tag) {
+ if (!WritePadTag(&writer, pad_length, &end_offset)) {
+ return nullptr;
+ }
+ }
+
+ // Values
+ for (auto it = message.tag_value_map().begin();
+ it != message.tag_value_map().end(); ++it) {
+ if (it->first > kPAD && need_pad_value) {
+ need_pad_value = false;
+ if (!writer.WriteRepeatedByte('-', pad_length)) {
+ DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+ }
+
+ if (!writer.WriteBytes(it->second.data(), it->second.length())) {
+ DCHECK(false) << "Failed to write value.";
+ return nullptr;
+ }
+ }
+
+ if (need_pad_value) {
+ if (!writer.WriteRepeatedByte('-', pad_length)) {
+ DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+ }
+
+ return QuicMakeUnique<QuicData>(buffer.release(), len, true);
+}
+
+void CryptoFramer::Clear() {
+ message_.Clear();
+ tags_and_lengths_.clear();
+ error_ = QUIC_NO_ERROR;
+ error_detail_ = "";
+ state_ = STATE_READING_TAG;
+}
+
+QuicErrorCode CryptoFramer::Process(QuicStringPiece input) {
+ // Add this data to the buffer.
+ buffer_.append(input.data(), input.length());
+ QuicDataReader reader(buffer_.data(), buffer_.length(), HOST_BYTE_ORDER);
+
+ switch (state_) {
+ case STATE_READING_TAG:
+ if (reader.BytesRemaining() < kQuicTagSize) {
+ break;
+ }
+ QuicTag message_tag;
+ reader.ReadTag(&message_tag);
+ message_.set_tag(message_tag);
+ state_ = STATE_READING_NUM_ENTRIES;
+ QUIC_FALLTHROUGH_INTENDED;
+ case STATE_READING_NUM_ENTRIES:
+ if (reader.BytesRemaining() < kNumEntriesSize + sizeof(uint16_t)) {
+ break;
+ }
+ reader.ReadUInt16(&num_entries_);
+ if (num_entries_ > kMaxEntries) {
+ error_detail_ = QuicStrCat(num_entries_, " entries");
+ return QUIC_CRYPTO_TOO_MANY_ENTRIES;
+ }
+ uint16_t padding;
+ reader.ReadUInt16(&padding);
+
+ tags_and_lengths_.reserve(num_entries_);
+ state_ = STATE_READING_TAGS_AND_LENGTHS;
+ values_len_ = 0;
+ QUIC_FALLTHROUGH_INTENDED;
+ case STATE_READING_TAGS_AND_LENGTHS: {
+ if (reader.BytesRemaining() <
+ num_entries_ * (kQuicTagSize + kCryptoEndOffsetSize)) {
+ break;
+ }
+
+ uint32_t last_end_offset = 0;
+ for (unsigned i = 0; i < num_entries_; ++i) {
+ QuicTag tag;
+ reader.ReadTag(&tag);
+ if (i > 0 && tag <= tags_and_lengths_[i - 1].first) {
+ if (tag == tags_and_lengths_[i - 1].first) {
+ error_detail_ = QuicStrCat("Duplicate tag:", tag);
+ return QUIC_CRYPTO_DUPLICATE_TAG;
+ }
+ error_detail_ = QuicStrCat("Tag ", tag, " out of order");
+ return QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
+ }
+
+ uint32_t end_offset;
+ reader.ReadUInt32(&end_offset);
+
+ if (end_offset < last_end_offset) {
+ error_detail_ =
+ QuicStrCat("End offset: ", end_offset, " vs ", last_end_offset);
+ return QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
+ }
+ tags_and_lengths_.push_back(std::make_pair(
+ tag, static_cast<size_t>(end_offset - last_end_offset)));
+ last_end_offset = end_offset;
+ }
+ values_len_ = last_end_offset;
+ state_ = STATE_READING_VALUES;
+ QUIC_FALLTHROUGH_INTENDED;
+ }
+ case STATE_READING_VALUES:
+ if (reader.BytesRemaining() < values_len_) {
+ if (!process_truncated_messages_) {
+ break;
+ }
+ QUIC_LOG(ERROR) << "Trunacted message. Missing "
+ << values_len_ - reader.BytesRemaining() << " bytes.";
+ }
+ for (const std::pair<QuicTag, size_t>& item : tags_and_lengths_) {
+ QuicStringPiece value;
+ if (!reader.ReadStringPiece(&value, item.second)) {
+ DCHECK(process_truncated_messages_);
+ // Store an empty value.
+ message_.SetStringPiece(item.first, "");
+ continue;
+ }
+ message_.SetStringPiece(item.first, value);
+ }
+ visitor_->OnHandshakeMessage(message_);
+ Clear();
+ state_ = STATE_READING_TAG;
+ break;
+ }
+ // Save any remaining data.
+ buffer_ = std::string(reader.PeekRemainingPayload());
+ return QUIC_NO_ERROR;
+}
+
+// static
+bool CryptoFramer::WritePadTag(QuicDataWriter* writer,
+ size_t pad_length,
+ uint32_t* end_offset) {
+ if (!writer->WriteTag(kPAD)) {
+ DCHECK(false) << "Failed to write tag.";
+ return false;
+ }
+ *end_offset += pad_length;
+ if (!writer->WriteUInt32(*end_offset)) {
+ DCHECK(false) << "Failed to write end offset.";
+ return false;
+ }
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer.h
new file mode 100644
index 00000000000..f20f0407bd9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer.h
@@ -0,0 +1,138 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_FRAMER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_FRAMER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class CryptoFramer;
+class QuicData;
+class QuicDataWriter;
+
+class QUIC_EXPORT_PRIVATE CryptoFramerVisitorInterface {
+ public:
+ virtual ~CryptoFramerVisitorInterface() {}
+
+ // Called if an error is detected.
+ virtual void OnError(CryptoFramer* framer) = 0;
+
+ // Called when a complete handshake message has been parsed.
+ virtual void OnHandshakeMessage(const CryptoHandshakeMessage& message) = 0;
+};
+
+// A class for framing the crypto messages that are exchanged in a QUIC
+// session.
+class QUIC_EXPORT_PRIVATE CryptoFramer : public CryptoMessageParser {
+ public:
+ CryptoFramer();
+
+ ~CryptoFramer() override;
+
+ // ParseMessage parses exactly one message from the given QuicStringPiece. If
+ // there is an error, the message is truncated, or the message has trailing
+ // garbage then nullptr will be returned.
+ static std::unique_ptr<CryptoHandshakeMessage> ParseMessage(
+ QuicStringPiece in);
+
+ // Set callbacks to be called from the framer. A visitor must be set, or
+ // else the framer will crash. It is acceptable for the visitor to do
+ // nothing. If this is called multiple times, only the last visitor
+ // will be used. |visitor| will be owned by the framer.
+ void set_visitor(CryptoFramerVisitorInterface* visitor) {
+ visitor_ = visitor;
+ }
+
+ QuicErrorCode error() const override;
+ const std::string& error_detail() const override;
+
+ // Processes input data, which must be delivered in order. Returns
+ // false if there was an error, and true otherwise. ProcessInput optionally
+ // takes an EncryptionLevel, but it is ignored. The variant with the
+ // EncryptionLevel is provided to match the CryptoMessageParser interface.
+ bool ProcessInput(QuicStringPiece input, EncryptionLevel level) override;
+ bool ProcessInput(QuicStringPiece input);
+
+ // Returns the number of bytes of buffered input data remaining to be
+ // parsed.
+ size_t InputBytesRemaining() const override;
+
+ // Checks if the specified tag has been seen. Returns |true| if it
+ // has, and |false| if it has not or a CHLO has not been seen.
+ bool HasTag(QuicTag tag) const;
+
+ // Even if the CHLO has not been fully received, force processing of
+ // the handshake message. This is dangerous and should not be used
+ // except as a mechanism of last resort.
+ void ForceHandshake();
+
+ // Returns a new QuicData owned by the caller that contains a serialized
+ // |message|, or nullptr if there was an error.
+ static std::unique_ptr<QuicData> ConstructHandshakeMessage(
+ const CryptoHandshakeMessage& message);
+
+ // Debug only method which permits processing truncated messages.
+ void set_process_truncated_messages(bool process_truncated_messages) {
+ process_truncated_messages_ = process_truncated_messages;
+ }
+
+ private:
+ // Clears per-message state. Does not clear the visitor.
+ void Clear();
+
+ // Process does does the work of |ProcessInput|, but returns an error code,
+ // doesn't set error_ and doesn't call |visitor_->OnError()|.
+ QuicErrorCode Process(QuicStringPiece input);
+
+ static bool WritePadTag(QuicDataWriter* writer,
+ size_t pad_length,
+ uint32_t* end_offset);
+
+ // Represents the current state of the parsing state machine.
+ enum CryptoFramerState {
+ STATE_READING_TAG,
+ STATE_READING_NUM_ENTRIES,
+ STATE_READING_TAGS_AND_LENGTHS,
+ STATE_READING_VALUES
+ };
+
+ // Visitor to invoke when messages are parsed.
+ CryptoFramerVisitorInterface* visitor_;
+ // Last error.
+ QuicErrorCode error_;
+ // Remaining unparsed data.
+ std::string buffer_;
+ // Current state of the parsing.
+ CryptoFramerState state_;
+ // The message currently being parsed.
+ CryptoHandshakeMessage message_;
+ // The issue which caused |error_|
+ std::string error_detail_;
+ // Number of entires in the message currently being parsed.
+ uint16_t num_entries_;
+ // tags_and_lengths_ contains the tags that are currently being parsed and
+ // their lengths.
+ std::vector<std::pair<QuicTag, size_t>> tags_and_lengths_;
+ // Cumulative length of all values in the message currently being parsed.
+ size_t values_len_;
+ // Set to true to allow of processing of truncated messages for debugging.
+ bool process_truncated_messages_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_FRAMER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer_test.cc
new file mode 100644
index 00000000000..4ca98a55b3f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_framer_test.cc
@@ -0,0 +1,464 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+char* AsChars(unsigned char* data) {
+ return reinterpret_cast<char*>(data);
+}
+
+class TestCryptoVisitor : public CryptoFramerVisitorInterface {
+ public:
+ TestCryptoVisitor() : error_count_(0) {}
+
+ void OnError(CryptoFramer* framer) override {
+ QUIC_DLOG(ERROR) << "CryptoFramer Error: " << framer->error();
+ ++error_count_;
+ }
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ messages_.push_back(message);
+ }
+
+ // Counters from the visitor callbacks.
+ int error_count_;
+
+ std::vector<CryptoHandshakeMessage> messages_;
+};
+
+TEST(CryptoFramerTest, ConstructHandshakeMessage) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "abcdef");
+ message.SetStringPiece(0x12345679, "ghijk");
+ message.SetStringPiece(0x1234567A, "lmnopqr");
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x03, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // tag 3
+ 0x7A, 0x56, 0x34, 0x12,
+ // end offset 3
+ 0x12, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k',
+ // value 3
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+ };
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "abcdef");
+ message.SetStringPiece(0x12345679, "ghijk");
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k',
+ };
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageZeroLength) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "");
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x01, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageTooManyEntries) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ for (uint32_t key = 1; key <= kMaxEntries + 1; ++key) {
+ message.SetStringPiece(key, "abcdef");
+ }
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ EXPECT_TRUE(data == nullptr);
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSize) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x01020304, "test");
+ message.set_minimum_size(64);
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 'P', 'A', 'D', 0,
+ // end offset 1
+ 0x24, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x04, 0x03, 0x02, 0x01,
+ // end offset 2
+ 0x28, 0x00, 0x00, 0x00,
+ // 36 bytes of padding.
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-',
+ // value 2
+ 't', 'e', 's', 't',
+ };
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSizePadLast) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(1, "");
+ message.set_minimum_size(64);
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x01, 0x00, 0x00, 0x00,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00,
+ // tag 2
+ 'P', 'A', 'D', 0,
+ // end offset 2
+ 0x28, 0x00, 0x00, 0x00,
+ // 40 bytes of padding.
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-', '-', '-',
+ };
+
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST(CryptoFramerTest, ProcessInput) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k',
+ };
+
+ EXPECT_TRUE(framer.ProcessInput(
+ QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input))));
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ EXPECT_EQ(0, visitor.error_count_);
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(2u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679));
+}
+
+TEST(CryptoFramerTest, ProcessInputWithThreeKeys) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x03, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // tag 3
+ 0x7A, 0x56, 0x34, 0x12,
+ // end offset 3
+ 0x12, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k',
+ // value 3
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+ };
+
+ EXPECT_TRUE(framer.ProcessInput(
+ QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input))));
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ EXPECT_EQ(0, visitor.error_count_);
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(3u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679));
+ EXPECT_EQ("lmnopqr", crypto_test_utils::GetValueForTag(message, 0x1234567A));
+}
+
+TEST(CryptoFramerTest, ProcessInputIncrementally) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j', 'k',
+ };
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(input); i++) {
+ EXPECT_TRUE(framer.ProcessInput(QuicStringPiece(AsChars(input) + i, 1)));
+ }
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(2u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", crypto_test_utils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", crypto_test_utils::GetValueForTag(message, 0x12345679));
+}
+
+TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x13,
+ // end offset 1
+ 0x01, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x02, 0x00, 0x00, 0x00,
+ };
+
+ EXPECT_FALSE(framer.ProcessInput(
+ QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input))));
+ EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error());
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessEndOffsetsOutOfOrder) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x01, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x78, 0x56, 0x34, 0x13,
+ // end offset 2
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ EXPECT_FALSE(framer.ProcessInput(
+ QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input))));
+ EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error());
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessInputTooManyEntries) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0xA0, 0x00,
+ // padding
+ 0x00, 0x00,
+ };
+
+ EXPECT_FALSE(framer.ProcessInput(
+ QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input))));
+ EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error());
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessInputZeroLength) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x05, 0x00, 0x00, 0x00,
+ };
+
+ EXPECT_TRUE(framer.ProcessInput(
+ QuicStringPiece(AsChars(input), QUIC_ARRAYSIZE(input))));
+ EXPECT_EQ(0, visitor.error_count_);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake.cc
new file mode 100644
index 00000000000..3d6baac26d6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+
+namespace quic {
+
+QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters()
+ : key_exchange(0),
+ aead(0),
+ token_binding_key_param(0),
+ sct_supported_by_client(false) {}
+
+QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {}
+
+CrypterPair::CrypterPair() {}
+
+CrypterPair::~CrypterPair() {}
+
+// static
+const char QuicCryptoConfig::kInitialLabel[] = "QUIC key expansion";
+
+// static
+const char QuicCryptoConfig::kCETVLabel[] = "QUIC CETV block";
+
+// static
+const char QuicCryptoConfig::kForwardSecureLabel[] =
+ "QUIC forward secure key expansion";
+
+QuicCryptoConfig::QuicCryptoConfig()
+ : common_cert_sets(CommonCertSets::GetInstanceQUIC()) {}
+
+QuicCryptoConfig::~QuicCryptoConfig() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h
new file mode 100644
index 00000000000..4608889df45
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class CommonCertSets;
+class SynchronousKeyExchange;
+class QuicDecrypter;
+class QuicEncrypter;
+
+// HandshakeFailureReason enum values are uploaded to UMA, they cannot be
+// changed.
+enum HandshakeFailureReason {
+ HANDSHAKE_OK = 0,
+
+ // Failure reasons for an invalid client nonce in CHLO.
+ //
+ // The default error value for nonce verification failures from strike
+ // register (covers old strike registers and unknown failures).
+ CLIENT_NONCE_UNKNOWN_FAILURE = 1,
+ // Client nonce had incorrect length.
+ CLIENT_NONCE_INVALID_FAILURE = 2,
+ // Client nonce is not unique.
+ CLIENT_NONCE_NOT_UNIQUE_FAILURE = 3,
+ // Client orbit is invalid or incorrect.
+ CLIENT_NONCE_INVALID_ORBIT_FAILURE = 4,
+ // Client nonce's timestamp is not in the strike register's valid time range.
+ CLIENT_NONCE_INVALID_TIME_FAILURE = 5,
+ // Strike register's RPC call timed out, client nonce couldn't be verified.
+ CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT = 6,
+ // Strike register is down, client nonce couldn't be verified.
+ CLIENT_NONCE_STRIKE_REGISTER_FAILURE = 7,
+
+ // Failure reasons for an invalid server nonce in CHLO.
+ //
+ // Unbox of server nonce failed.
+ SERVER_NONCE_DECRYPTION_FAILURE = 8,
+ // Decrypted server nonce had incorrect length.
+ SERVER_NONCE_INVALID_FAILURE = 9,
+ // Server nonce is not unique.
+ SERVER_NONCE_NOT_UNIQUE_FAILURE = 10,
+ // Server nonce's timestamp is not in the strike register's valid time range.
+ SERVER_NONCE_INVALID_TIME_FAILURE = 11,
+ // The server requires handshake confirmation.
+ SERVER_NONCE_REQUIRED_FAILURE = 20,
+
+ // Failure reasons for an invalid server config in CHLO.
+ //
+ // Missing Server config id (kSCID) tag.
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE = 12,
+ // Couldn't find the Server config id (kSCID).
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE = 13,
+
+ // Failure reasons for an invalid source-address token.
+ //
+ // Missing Source-address token (kSourceAddressTokenTag) tag.
+ SOURCE_ADDRESS_TOKEN_INVALID_FAILURE = 14,
+ // Unbox of Source-address token failed.
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE = 15,
+ // Couldn't parse the unbox'ed Source-address token.
+ SOURCE_ADDRESS_TOKEN_PARSE_FAILURE = 16,
+ // Source-address token is for a different IP address.
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE = 17,
+ // The source-address token has a timestamp in the future.
+ SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE = 18,
+ // The source-address token has expired.
+ SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE = 19,
+
+ // The expected leaf certificate hash could not be validated.
+ INVALID_EXPECTED_LEAF_CERTIFICATE = 21,
+
+ MAX_FAILURE_REASON = 22,
+};
+
+// These errors will be packed into an uint32_t and we don't want to set the
+// most significant bit, which may be misinterpreted as the sign bit.
+static_assert(MAX_FAILURE_REASON <= 32, "failure reason out of sync");
+
+// A CrypterPair contains the encrypter and decrypter for an encryption level.
+struct QUIC_EXPORT_PRIVATE CrypterPair {
+ CrypterPair();
+ ~CrypterPair();
+ std::unique_ptr<QuicEncrypter> encrypter;
+ std::unique_ptr<QuicDecrypter> decrypter;
+};
+
+// Parameters negotiated by the crypto handshake.
+struct QUIC_EXPORT_PRIVATE QuicCryptoNegotiatedParameters
+ : public QuicReferenceCounted {
+ // Initializes the members to 0 or empty values.
+ QuicCryptoNegotiatedParameters();
+
+ QuicTag key_exchange;
+ QuicTag aead;
+ std::string initial_premaster_secret;
+ std::string forward_secure_premaster_secret;
+ // initial_subkey_secret is used as the PRK input to the HKDF used when
+ // performing key extraction that needs to happen before forward-secure keys
+ // are available.
+ std::string initial_subkey_secret;
+ // subkey_secret is used as the PRK input to the HKDF used for key extraction.
+ std::string subkey_secret;
+ CrypterPair initial_crypters;
+ CrypterPair forward_secure_crypters;
+ // Normalized SNI: converted to lower case and trailing '.' removed.
+ std::string sni;
+ std::string client_nonce;
+ std::string server_nonce;
+ // hkdf_input_suffix contains the HKDF input following the label: the
+ // ConnectionId, client hello and server config. This is only populated in the
+ // client because only the client needs to derive the forward secure keys at a
+ // later time from the initial keys.
+ std::string hkdf_input_suffix;
+ // cached_certs contains the cached certificates that a client used when
+ // sending a client hello.
+ std::vector<std::string> cached_certs;
+ // client_key_exchange is used by clients to store the ephemeral KeyExchange
+ // for the connection.
+ std::unique_ptr<SynchronousKeyExchange> client_key_exchange;
+ // channel_id is set by servers to a ChannelID key when the client correctly
+ // proves possession of the corresponding private key. It consists of 32
+ // bytes of x coordinate, followed by 32 bytes of y coordinate. Both values
+ // are big-endian and the pair is a P-256 public key.
+ std::string channel_id;
+ QuicTag token_binding_key_param;
+
+ // Used when generating proof signature when sending server config updates.
+
+ // Used to generate cert chain when sending server config updates.
+ std::string client_common_set_hashes;
+ std::string client_cached_cert_hashes;
+
+ // Default to false; set to true if the client indicates that it supports sct
+ // by sending CSCT tag with an empty value in client hello.
+ bool sct_supported_by_client;
+
+ protected:
+ ~QuicCryptoNegotiatedParameters() override;
+};
+
+// QuicCryptoConfig contains common configuration between clients and servers.
+class QUIC_EXPORT_PRIVATE QuicCryptoConfig {
+ public:
+ // kInitialLabel is a constant that is used when deriving the initial
+ // (non-forward secure) keys for the connection in order to tie the resulting
+ // key to this protocol.
+ static const char kInitialLabel[];
+
+ // kCETVLabel is a constant that is used when deriving the keys for the
+ // encrypted tag/value block in the client hello.
+ static const char kCETVLabel[];
+
+ // kForwardSecureLabel is a constant that is used when deriving the forward
+ // secure keys for the connection in order to tie the resulting key to this
+ // protocol.
+ static const char kForwardSecureLabel[];
+
+ QuicCryptoConfig();
+ QuicCryptoConfig(const QuicCryptoConfig&) = delete;
+ QuicCryptoConfig& operator=(const QuicCryptoConfig&) = delete;
+ ~QuicCryptoConfig();
+
+ // Key exchange methods. The following two members' values correspond by
+ // index.
+ QuicTagVector kexs;
+ // Authenticated encryption with associated data (AEAD) algorithms.
+ QuicTagVector aead;
+
+ // Supported Token Binding key parameters that can be negotiated in the client
+ // hello.
+ QuicTagVector tb_key_params;
+
+ const CommonCertSets* common_cert_sets;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_H_
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
new file mode 100644
index 00000000000..1bbed745bd0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.cc
@@ -0,0 +1,379 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+CryptoHandshakeMessage::CryptoHandshakeMessage() : tag_(0), minimum_size_(0) {}
+
+CryptoHandshakeMessage::CryptoHandshakeMessage(
+ const CryptoHandshakeMessage& other)
+ : tag_(other.tag_),
+ tag_value_map_(other.tag_value_map_),
+ minimum_size_(other.minimum_size_) {
+ // Don't copy serialized_. unique_ptr doesn't have a copy constructor.
+ // The new object can lazily reconstruct serialized_.
+}
+
+CryptoHandshakeMessage::CryptoHandshakeMessage(CryptoHandshakeMessage&& other) =
+ default;
+
+CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
+
+CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
+ const CryptoHandshakeMessage& other) {
+ tag_ = other.tag_;
+ tag_value_map_ = other.tag_value_map_;
+ // Don't copy serialized_. unique_ptr doesn't have an assignment operator.
+ // However, invalidate serialized_.
+ serialized_.reset();
+ minimum_size_ = other.minimum_size_;
+ return *this;
+}
+
+CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
+ CryptoHandshakeMessage&& other) = default;
+
+void CryptoHandshakeMessage::Clear() {
+ tag_ = 0;
+ tag_value_map_.clear();
+ minimum_size_ = 0;
+ serialized_.reset();
+}
+
+const QuicData& CryptoHandshakeMessage::GetSerialized() const {
+ if (!serialized_.get()) {
+ serialized_ = CryptoFramer::ConstructHandshakeMessage(*this);
+ }
+ return *serialized_;
+}
+
+void CryptoHandshakeMessage::MarkDirty() {
+ serialized_.reset();
+}
+
+void CryptoHandshakeMessage::SetVersionVector(
+ QuicTag tag,
+ ParsedQuicVersionVector versions) {
+ QuicVersionLabelVector version_labels;
+ for (ParsedQuicVersion version : versions) {
+ version_labels.push_back(
+ QuicEndian::HostToNet32(CreateQuicVersionLabel(version)));
+ }
+ SetVector(tag, version_labels);
+}
+
+void CryptoHandshakeMessage::SetVersion(QuicTag tag,
+ ParsedQuicVersion version) {
+ SetValue(tag, QuicEndian::HostToNet32(CreateQuicVersionLabel(version)));
+}
+
+void CryptoHandshakeMessage::SetStringPiece(QuicTag tag,
+ QuicStringPiece value) {
+ tag_value_map_[tag] = std::string(value);
+}
+
+void CryptoHandshakeMessage::Erase(QuicTag tag) {
+ tag_value_map_.erase(tag);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetTaglist(
+ QuicTag tag,
+ QuicTagVector* out_tags) const {
+ auto it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() % sizeof(QuicTag) != 0) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ out_tags->clear();
+ return ret;
+ }
+
+ size_t num_tags = it->second.size() / sizeof(QuicTag);
+ out_tags->resize(num_tags);
+ for (size_t i = 0; i < num_tags; ++i) {
+ QuicTag tag;
+ memcpy(&tag, it->second.data() + i * sizeof(tag), sizeof(tag));
+ (*out_tags)[i] = tag;
+ }
+ return ret;
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetVersionLabelList(
+ QuicTag tag,
+ QuicVersionLabelVector* out) const {
+ QuicErrorCode error = GetTaglist(tag, out);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ for (size_t i = 0; i < out->size(); ++i) {
+ (*out)[i] = QuicEndian::HostToNet32((*out)[i]);
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetVersionLabel(
+ QuicTag tag,
+ QuicVersionLabel* out) const {
+ QuicErrorCode error = GetUint32(tag, out);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ *out = QuicEndian::HostToNet32(*out);
+ return QUIC_NO_ERROR;
+}
+
+bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
+ QuicStringPiece* out) const {
+ auto it = tag_value_map_.find(tag);
+ if (it == tag_value_map_.end()) {
+ return false;
+ }
+ *out = it->second;
+ return true;
+}
+
+bool CryptoHandshakeMessage::HasStringPiece(QuicTag tag) const {
+ return QuicContainsKey(tag_value_map_, tag);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetNthValue24(
+ QuicTag tag,
+ unsigned index,
+ QuicStringPiece* out) const {
+ QuicStringPiece value;
+ if (!GetStringPiece(tag, &value)) {
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ for (unsigned i = 0;; i++) {
+ if (value.empty()) {
+ return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
+ }
+ if (value.size() < 3) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(value.data());
+ size_t size = static_cast<size_t>(data[0]) |
+ (static_cast<size_t>(data[1]) << 8) |
+ (static_cast<size_t>(data[2]) << 16);
+ value.remove_prefix(3);
+
+ if (value.size() < size) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (i == index) {
+ *out = QuicStringPiece(value.data(), size);
+ return QUIC_NO_ERROR;
+ }
+
+ value.remove_prefix(size);
+ }
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
+ uint32_t* out) const {
+ return GetPOD(tag, out, sizeof(uint32_t));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
+ uint64_t* out) const {
+ return GetPOD(tag, out, sizeof(uint64_t));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint128(QuicTag tag,
+ QuicUint128* out) const {
+ return GetPOD(tag, out, sizeof(QuicUint128));
+}
+
+size_t CryptoHandshakeMessage::size() const {
+ size_t ret = sizeof(QuicTag) + sizeof(uint16_t) /* number of entries */ +
+ sizeof(uint16_t) /* padding */;
+ ret += (sizeof(QuicTag) + sizeof(uint32_t) /* end offset */) *
+ tag_value_map_.size();
+ for (auto i = tag_value_map_.begin(); i != tag_value_map_.end(); ++i) {
+ ret += i->second.size();
+ }
+
+ return ret;
+}
+
+void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
+ if (min_bytes == minimum_size_) {
+ return;
+ }
+ serialized_.reset();
+ minimum_size_ = min_bytes;
+}
+
+size_t CryptoHandshakeMessage::minimum_size() const {
+ return minimum_size_;
+}
+
+std::string CryptoHandshakeMessage::DebugString() const {
+ return DebugStringInternal(0);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetPOD(QuicTag tag,
+ void* out,
+ size_t len) const {
+ auto it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() != len) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ memset(out, 0, len);
+ return ret;
+ }
+
+ memcpy(out, it->second.data(), len);
+ return ret;
+}
+
+std::string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
+ std::string ret =
+ std::string(2 * indent, ' ') + QuicTagToString(tag_) + "<\n";
+ ++indent;
+ for (auto it = tag_value_map_.begin(); it != tag_value_map_.end(); ++it) {
+ ret += std::string(2 * indent, ' ') + QuicTagToString(it->first) + ": ";
+
+ bool done = false;
+ switch (it->first) {
+ case kICSL:
+ case kCFCW:
+ case kSFCW:
+ case kIRTT:
+ case kMIDS:
+ case kSCLS:
+ case kTCID:
+ // uint32_t value
+ if (it->second.size() == 4) {
+ uint32_t value;
+ memcpy(&value, it->second.data(), sizeof(value));
+ ret += QuicTextUtils::Uint64ToString(value);
+ done = true;
+ }
+ break;
+ case kRCID:
+ // uint64_t value
+ if (it->second.size() == 8) {
+ uint64_t value;
+ memcpy(&value, it->second.data(), sizeof(value));
+ value = QuicEndian::NetToHost64(value);
+ ret += QuicTextUtils::Uint64ToString(value);
+ done = true;
+ }
+ break;
+ case kTBKP:
+ case kKEXS:
+ case kAEAD:
+ case kCOPT:
+ case kPDMD:
+ case kVER:
+ // tag lists
+ if (it->second.size() % sizeof(QuicTag) == 0) {
+ for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) {
+ QuicTag tag;
+ memcpy(&tag, it->second.data() + j, sizeof(tag));
+ if (j > 0) {
+ ret += ",";
+ }
+ ret += "'" + QuicTagToString(tag) + "'";
+ }
+ done = true;
+ }
+ break;
+ case kRREJ:
+ // uint32_t lists
+ if (it->second.size() % sizeof(uint32_t) == 0) {
+ for (size_t j = 0; j < it->second.size(); j += sizeof(uint32_t)) {
+ uint32_t value;
+ memcpy(&value, it->second.data() + j, sizeof(value));
+ if (j > 0) {
+ ret += ",";
+ }
+ ret += CryptoUtils::HandshakeFailureReasonToString(
+ static_cast<HandshakeFailureReason>(value));
+ }
+ done = true;
+ }
+ break;
+ case kCADR:
+ // IP address and port
+ if (!it->second.empty()) {
+ QuicSocketAddressCoder decoder;
+ if (decoder.Decode(it->second.data(), it->second.size())) {
+ ret += QuicSocketAddress(decoder.ip(), decoder.port()).ToString();
+ done = true;
+ }
+ }
+ break;
+ case kSCFG:
+ // nested messages.
+ if (!it->second.empty()) {
+ std::unique_ptr<CryptoHandshakeMessage> msg(
+ CryptoFramer::ParseMessage(it->second));
+ if (msg) {
+ ret += "\n";
+ ret += msg->DebugStringInternal(indent + 1);
+
+ done = true;
+ }
+ }
+ break;
+ case kPAD:
+ ret += QuicStringPrintf("(%d bytes of padding)",
+ static_cast<int>(it->second.size()));
+ done = true;
+ break;
+ case kSNI:
+ case kUAID:
+ ret += "\"" + it->second + "\"";
+ done = true;
+ break;
+ }
+
+ if (!done) {
+ // If there's no specific format for this tag, or the value is invalid,
+ // then just use hex.
+ ret += "0x" + QuicTextUtils::HexEncode(it->second);
+ }
+ ret += "\n";
+ }
+ --indent;
+ ret += std::string(2 * indent, ' ') + ">";
+ return ret;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h
new file mode 100644
index 00000000000..6819e3655ce
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h
@@ -0,0 +1,155 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+// An intermediate format of a handshake message that's convenient for a
+// CryptoFramer to serialize from or parse into.
+class QUIC_EXPORT_PRIVATE CryptoHandshakeMessage {
+ public:
+ CryptoHandshakeMessage();
+ CryptoHandshakeMessage(const CryptoHandshakeMessage& other);
+ CryptoHandshakeMessage(CryptoHandshakeMessage&& other);
+ ~CryptoHandshakeMessage();
+
+ CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other);
+ CryptoHandshakeMessage& operator=(CryptoHandshakeMessage&& other);
+
+ // Clears state.
+ void Clear();
+
+ // GetSerialized returns the serialized form of this message and caches the
+ // result. Subsequently altering the message does not invalidate the cache.
+ const QuicData& GetSerialized() const;
+
+ // MarkDirty invalidates the cache created by |GetSerialized|.
+ void MarkDirty();
+
+ // SetValue sets an element with the given tag to the raw, memory contents of
+ // |v|.
+ template <class T>
+ void SetValue(QuicTag tag, const T& v) {
+ tag_value_map_[tag] =
+ std::string(reinterpret_cast<const char*>(&v), sizeof(v));
+ }
+
+ // SetVector sets an element with the given tag to the raw contents of an
+ // array of elements in |v|.
+ template <class T>
+ void SetVector(QuicTag tag, const std::vector<T>& v) {
+ if (v.empty()) {
+ tag_value_map_[tag] = std::string();
+ } else {
+ tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]),
+ v.size() * sizeof(T));
+ }
+ }
+
+ // Sets an element with the given tag to the on-the-wire representation of
+ // |version|.
+ void SetVersion(QuicTag tag, ParsedQuicVersion version);
+
+ // Sets an element with the given tag to the on-the-wire representation of
+ // the elements in |versions|.
+ void SetVersionVector(QuicTag tag, ParsedQuicVersionVector versions);
+
+ // Returns the message tag.
+ QuicTag tag() const { return tag_; }
+ // Sets the message tag.
+ void set_tag(QuicTag tag) { tag_ = tag; }
+
+ const QuicTagValueMap& tag_value_map() const { return tag_value_map_; }
+
+ void SetStringPiece(QuicTag tag, QuicStringPiece value);
+
+ // Erase removes a tag/value, if present, from the message.
+ void Erase(QuicTag tag);
+
+ // GetTaglist finds an element with the given tag containing zero or more
+ // tags. If such a tag doesn't exist, it returns an error code. Otherwise it
+ // populates |out_tags| with the tags and returns QUIC_NO_ERROR.
+ QuicErrorCode GetTaglist(QuicTag tag, QuicTagVector* out_tags) const;
+
+ // GetVersionLabelList finds an element with the given tag containing zero or
+ // more version labels. If such a tag doesn't exist, it returns an error code.
+ // Otherwise it populates |out| with the labels and returns QUIC_NO_ERROR.
+ QuicErrorCode GetVersionLabelList(QuicTag tag,
+ QuicVersionLabelVector* out) const;
+
+ // GetVersionLabel finds an element with the given tag containing a single
+ // version label. If such a tag doesn't exist, it returns an error code.
+ // Otherwise it populates |out| with the label and returns QUIC_NO_ERROR.
+ QuicErrorCode GetVersionLabel(QuicTag tag, QuicVersionLabel* out) const;
+
+ bool GetStringPiece(QuicTag tag, QuicStringPiece* out) const;
+ bool HasStringPiece(QuicTag tag) const;
+
+ // GetNthValue24 interprets the value with the given tag to be a series of
+ // 24-bit, length prefixed values and it returns the subvalue with the given
+ // index.
+ QuicErrorCode GetNthValue24(QuicTag tag,
+ unsigned index,
+ QuicStringPiece* out) const;
+ QuicErrorCode GetUint32(QuicTag tag, uint32_t* out) const;
+ QuicErrorCode GetUint64(QuicTag tag, uint64_t* out) const;
+ QuicErrorCode GetUint128(QuicTag tag, QuicUint128* out) const;
+
+ // size returns 4 (message tag) + 2 (uint16_t, number of entries) +
+ // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes.
+ size_t size() const;
+
+ // set_minimum_size sets the minimum number of bytes that the message should
+ // consume. The CryptoFramer will add a PAD tag as needed when serializing in
+ // order to ensure this. Setting a value of 0 disables padding.
+ //
+ // Padding is useful in order to ensure that messages are a minimum size. A
+ // QUIC server can require a minimum size in order to reduce the
+ // amplification factor of any mirror DoS attack.
+ void set_minimum_size(size_t min_bytes);
+
+ size_t minimum_size() const;
+
+ // DebugString returns a multi-line, string representation of the message
+ // suitable for including in debug output.
+ std::string DebugString() const;
+
+ private:
+ // GetPOD is a utility function for extracting a plain-old-data value. If
+ // |tag| exists in the message, and has a value of exactly |len| bytes then
+ // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out|
+ // are zeroed out.
+ //
+ // If used to copy integers then this assumes that the machine is
+ // little-endian.
+ QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const;
+
+ std::string DebugStringInternal(size_t indent) const;
+
+ QuicTag tag_;
+ QuicTagValueMap tag_value_map_;
+
+ size_t minimum_size_;
+
+ // The serialized form of the handshake message. This member is constructed
+ // lazily.
+ mutable std::unique_ptr<QuicData> serialized_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message_test.cc
new file mode 100644
index 00000000000..fc5298036a9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message_test.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+TEST(CryptoHandshakeMessageTest, DebugString) {
+ const char* str = "SHLO<\n>";
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kSHLO);
+ EXPECT_EQ(str, message.DebugString());
+
+ // Test copy
+ CryptoHandshakeMessage message2(message);
+ EXPECT_EQ(str, message2.DebugString());
+
+ // Test move
+ CryptoHandshakeMessage message3(std::move(message));
+ EXPECT_EQ(str, message3.DebugString());
+
+ // Test assign
+ CryptoHandshakeMessage message4 = message3;
+ EXPECT_EQ(str, message4.DebugString());
+
+ // Test move-assign
+ CryptoHandshakeMessage message5 = std::move(message3);
+ EXPECT_EQ(str, message5.DebugString());
+}
+
+TEST(CryptoHandshakeMessageTest, DebugStringWithUintVector) {
+ const char* str =
+ "REJ <\n RREJ: "
+ "SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,"
+ "CLIENT_NONCE_NOT_UNIQUE_FAILURE\n>";
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kREJ);
+ std::vector<uint32_t> reasons = {
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ CLIENT_NONCE_NOT_UNIQUE_FAILURE};
+ message.SetVector(kRREJ, reasons);
+ EXPECT_EQ(str, message.DebugString());
+
+ // Test copy
+ CryptoHandshakeMessage message2(message);
+ EXPECT_EQ(str, message2.DebugString());
+
+ // Test move
+ CryptoHandshakeMessage message3(std::move(message));
+ EXPECT_EQ(str, message3.DebugString());
+
+ // Test assign
+ CryptoHandshakeMessage message4 = message3;
+ EXPECT_EQ(str, message4.DebugString());
+
+ // Test move-assign
+ CryptoHandshakeMessage message5 = std::move(message3);
+ EXPECT_EQ(str, message5.DebugString());
+}
+
+TEST(CryptoHandshakeMessageTest, DebugStringWithTagVector) {
+ const char* str = "CHLO<\n COPT: 'TBBR','PAD ','BYTE'\n>";
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kCHLO);
+ message.SetVector(kCOPT, QuicTagVector{kTBBR, kPAD, kBYTE});
+ EXPECT_EQ(str, message.DebugString());
+
+ // Test copy
+ CryptoHandshakeMessage message2(message);
+ EXPECT_EQ(str, message2.DebugString());
+
+ // Test move
+ CryptoHandshakeMessage message3(std::move(message));
+ EXPECT_EQ(str, message3.DebugString());
+
+ // Test assign
+ CryptoHandshakeMessage message4 = message3;
+ EXPECT_EQ(str, message4.DebugString());
+
+ // Test move-assign
+ CryptoHandshakeMessage message5 = std::move(message3);
+ EXPECT_EQ(str, message5.DebugString());
+}
+
+TEST(CryptoHandshakeMessageTest, ServerDesignatedConnectionId) {
+ const char* str = "SREJ<\n RCID: 18364758544493064720\n>";
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kSREJ);
+ message.SetValue(kRCID,
+ QuicEndian::NetToHost64(UINT64_C(18364758544493064720)));
+ EXPECT_EQ(str, message.DebugString());
+
+ // Test copy
+ CryptoHandshakeMessage message2(message);
+ EXPECT_EQ(str, message2.DebugString());
+
+ // Test move
+ CryptoHandshakeMessage message3(std::move(message));
+ EXPECT_EQ(str, message3.DebugString());
+
+ // Test assign
+ CryptoHandshakeMessage message4 = message3;
+ EXPECT_EQ(str, message4.DebugString());
+
+ // Test move-assign
+ CryptoHandshakeMessage message5 = std::move(message3);
+ EXPECT_EQ(str, message5.DebugString());
+}
+
+TEST(CryptoHandshakeMessageTest, HasStringPiece) {
+ CryptoHandshakeMessage message;
+ EXPECT_FALSE(message.HasStringPiece(kRCID));
+ message.SetStringPiece(kRCID, "foo");
+ EXPECT_TRUE(message.HasStringPiece(kRCID));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.h
new file mode 100644
index 00000000000..2638a196d37
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE CryptoMessageParser {
+ public:
+ virtual ~CryptoMessageParser() {}
+
+ virtual QuicErrorCode error() const = 0;
+ virtual const std::string& error_detail() const = 0;
+
+ // Processes input data, which must be delivered in order. The input data
+ // being processed was received at encryption level |level|. Returns
+ // false if there was an error, and true otherwise.
+ virtual bool ProcessInput(QuicStringPiece input, EncryptionLevel level) = 0;
+
+ // Returns the number of bytes of buffered input data remaining to be
+ // parsed.
+ virtual size_t InputBytesRemaining() const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_MESSAGE_PARSER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc
new file mode 100644
index 00000000000..c680290bcb4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc
@@ -0,0 +1,56 @@
+// 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.
+
+// Dumps the contents of a QUIC crypto handshake message in a human readable
+// format.
+//
+// Usage: crypto_message_printer_bin <hex of message>
+
+#include <iostream>
+#include <string>
+
+#include "base/init_google.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using quic::Perspective;
+using std::cerr;
+using std::cout;
+using std::endl;
+
+namespace quic {
+
+class CryptoMessagePrinter : public ::quic::CryptoFramerVisitorInterface {
+ public:
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ cout << message.DebugString() << endl;
+ }
+
+ void OnError(CryptoFramer* framer) override {
+ cerr << "Error code: " << framer->error() << endl;
+ cerr << "Error details: " << framer->error_detail() << endl;
+ }
+};
+
+} // namespace quic
+
+int main(int argc, char* argv[]) {
+ InitGoogle(argv[0], &argc, &argv, true);
+
+ quic::CryptoMessagePrinter printer;
+ quic::CryptoFramer framer;
+ framer.set_visitor(&printer);
+ framer.set_process_truncated_messages(true);
+ std::string input = quic::QuicTextUtils::HexDecode(argv[1]);
+ if (!framer.ProcessInput(input)) {
+ return 1;
+ }
+ if (framer.InputBytesRemaining() != 0) {
+ cerr << "Input partially consumed. " << framer.InputBytesRemaining()
+ << " bytes remaining." << endl;
+ return 2;
+ }
+ return 0;
+}
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
new file mode 100644
index 00000000000..d9a1ebce3b5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h
@@ -0,0 +1,313 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_PROTOCOL_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_PROTOCOL_H_
+
+#include <cstddef>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_tag.h"
+
+// Version and Crypto tags are written to the wire with a big-endian
+// representation of the name of the tag. For example
+// the client hello tag (CHLO) will be written as the
+// following 4 bytes: 'C' 'H' 'L' 'O'. Since it is
+// stored in memory as a little endian uint32_t, we need
+// to reverse the order of the bytes.
+//
+// We use a macro to ensure that no static initialisers are created. Use the
+// MakeQuicTag function in normal code.
+#define TAG(a, b, c, d) \
+ static_cast<QuicTag>((d << 24) + (c << 16) + (b << 8) + a)
+
+namespace quic {
+
+typedef std::string ServerConfigID;
+
+// clang-format off
+const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello
+const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello
+const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config
+const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject
+const QuicTag kSREJ = TAG('S', 'R', 'E', 'J'); // Stateless reject
+const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value
+ // pairs
+const QuicTag kPRST = TAG('P', 'R', 'S', 'T'); // Public reset
+const QuicTag kSCUP = TAG('S', 'C', 'U', 'P'); // Server config update
+const QuicTag kALPN = TAG('A', 'L', 'P', 'N'); // Application-layer protocol
+
+// Key exchange methods
+const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256
+const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519
+
+// AEAD algorithms
+const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12
+const QuicTag kCC20 = TAG('C', 'C', '2', '0'); // ChaCha20 + Poly1305 RFC7539
+
+// Congestion control feedback types
+const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic
+
+// Connection options (COPT) values
+const QuicTag kAFCW = TAG('A', 'F', 'C', 'W'); // Auto-tune flow control
+ // receive windows.
+const QuicTag kIFW5 = TAG('I', 'F', 'W', '5'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 32KB. (2^5 KB).
+const QuicTag kIFW6 = TAG('I', 'F', 'W', '6'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 64KB. (2^6 KB).
+const QuicTag kIFW7 = TAG('I', 'F', 'W', '7'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 128KB. (2^7 KB).
+const QuicTag kIFW8 = TAG('I', 'F', 'W', '8'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 256KB. (2^8 KB).
+const QuicTag kIFW9 = TAG('I', 'F', 'W', '9'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 512KB. (2^9 KB).
+const QuicTag kIFWA = TAG('I', 'F', 'W', 'a'); // Set initial size
+ // of stream flow control
+ // receive window to
+ // 1MB. (2^0xa KB).
+const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP
+const QuicTag k1RTT = TAG('1', 'R', 'T', 'T'); // STARTUP in BBR for 1 RTT
+const QuicTag k2RTT = TAG('2', 'R', 'T', 'T'); // STARTUP in BBR for 2 RTTs
+const QuicTag kLRTT = TAG('L', 'R', 'T', 'T'); // Exit STARTUP in BBR on loss
+const QuicTag kBBS1 = TAG('B', 'B', 'S', '1'); // Rate-based recovery in
+ // BBR STARTUP
+const QuicTag kBBS2 = TAG('B', 'B', 'S', '2'); // More aggressive packet
+ // conservation in BBR STARTUP
+const QuicTag kBBS3 = TAG('B', 'B', 'S', '3'); // Slowstart packet
+ // conservation in BBR STARTUP
+const QuicTag kBBS4 = TAG('B', 'B', 'S', '4'); // Reduce rate in STARTUP by
+ // bytes_lost / CWND.
+const QuicTag kBBS5 = TAG('B', 'B', 'S', '5'); // Reduce rate in STARTUP by
+ // 2 * bytes_lost / CWND.
+const QuicTag kBBRR = TAG('B', 'B', 'R', 'R'); // Rate-based recovery in BBR
+const QuicTag kBBR1 = TAG('B', 'B', 'R', '1'); // DEPRECATED
+const QuicTag kBBR2 = TAG('B', 'B', 'R', '2'); // DEPRECATED
+const QuicTag kBBR3 = TAG('B', 'B', 'R', '3'); // Fully drain the queue once
+ // per cycle
+const QuicTag kBBR4 = TAG('B', 'B', 'R', '4'); // 20 RTT ack aggregation
+const QuicTag kBBR5 = TAG('B', 'B', 'R', '5'); // 40 RTT ack aggregation
+const QuicTag kBBR6 = TAG('B', 'B', 'R', '6'); // PROBE_RTT with 0.75 * BDP
+const QuicTag kBBR7 = TAG('B', 'B', 'R', '7'); // Skip PROBE_RTT if rtt has
+ // not changed 12.5%
+const QuicTag kBBR8 = TAG('B', 'B', 'R', '8'); // Disable PROBE_RTT when
+ // recently app-limited
+const QuicTag kBBR9 = TAG('B', 'B', 'R', '9'); // Ignore app-limited calls in
+ // BBR if enough inflight.
+const QuicTag kBBRS = TAG('B', 'B', 'R', 'S'); // Use 1.5x pacing in startup
+ // after a loss has occurred.
+const QuicTag kBBQ1 = TAG('B', 'B', 'Q', '1'); // BBR with lower 2.77 STARTUP
+ // pacing and CWND gain.
+const QuicTag kBBQ2 = TAG('B', 'B', 'Q', '2'); // BBR with lower 2.0 STARTUP
+ // CWND gain.
+const QuicTag kBBQ3 = TAG('B', 'B', 'Q', '3'); // BBR with ack aggregation
+ // compensation in STARTUP.
+const QuicTag kBBQ4 = TAG('B', 'B', 'Q', '4'); // Drain gain of 0.75.
+const QuicTag kBBQ5 = TAG('B', 'B', 'Q', '5'); // Expire ack aggregation upon
+ // bandwidth increase in
+ // STARTUP.
+const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control
+const QuicTag kTPCC = TAG('P', 'C', 'C', '\0'); // Performance-Oriented
+ // Congestion Control
+const QuicTag kBYTE = TAG('B', 'Y', 'T', 'E'); // TCP cubic or reno in bytes
+const QuicTag kIW03 = TAG('I', 'W', '0', '3'); // Force ICWND to 3
+const QuicTag kIW10 = TAG('I', 'W', '1', '0'); // Force ICWND to 10
+const QuicTag kIW20 = TAG('I', 'W', '2', '0'); // Force ICWND to 20
+const QuicTag kIW50 = TAG('I', 'W', '5', '0'); // Force ICWND to 50
+const QuicTag k1CON = TAG('1', 'C', 'O', 'N'); // Emulate a single connection
+const QuicTag kNTLP = TAG('N', 'T', 'L', 'P'); // No tail loss probe
+const QuicTag k1TLP = TAG('1', 'T', 'L', 'P'); // 1 tail loss probe
+const QuicTag k1RTO = TAG('1', 'R', 'T', 'O'); // Send 1 packet upon RTO
+const QuicTag kNRTO = TAG('N', 'R', 'T', 'O'); // CWND reduction on loss
+const QuicTag kTIME = TAG('T', 'I', 'M', 'E'); // Time based loss detection
+const QuicTag kATIM = TAG('A', 'T', 'I', 'M'); // Adaptive time loss detection
+const QuicTag kMIN1 = TAG('M', 'I', 'N', '1'); // Min CWND of 1 packet
+const QuicTag kMIN4 = TAG('M', 'I', 'N', '4'); // Min CWND of 4 packets,
+ // with a min rate of 1 BDP.
+const QuicTag kTLPR = TAG('T', 'L', 'P', 'R'); // Tail loss probe delay of
+ // 0.5RTT.
+const QuicTag kMAD0 = TAG('M', 'A', 'D', '0'); // Ignore ack delay
+const QuicTag kMAD1 = TAG('M', 'A', 'D', '1'); // 25ms initial max ack delay
+const QuicTag kMAD2 = TAG('M', 'A', 'D', '2'); // No min TLP
+const QuicTag kMAD3 = TAG('M', 'A', 'D', '3'); // No min RTO
+const QuicTag kMAD4 = TAG('M', 'A', 'D', '4'); // IETF style TLP
+const QuicTag kMAD5 = TAG('M', 'A', 'D', '5'); // IETF style TLP with 2x mult
+const QuicTag kACD0 = TAG('A', 'D', 'D', '0'); // Disable ack decimation
+const QuicTag kACKD = TAG('A', 'C', 'K', 'D'); // Ack decimation style acking.
+const QuicTag kAKD2 = TAG('A', 'K', 'D', '2'); // Ack decimation tolerating
+ // out of order packets.
+const QuicTag kAKD3 = TAG('A', 'K', 'D', '3'); // Ack decimation style acking
+ // with 1/8 RTT acks.
+const QuicTag kAKD4 = TAG('A', 'K', 'D', '4'); // Ack decimation with 1/8 RTT
+ // tolerating out of order.
+const QuicTag kAKDU = TAG('A', 'K', 'D', 'U'); // Unlimited number of packets
+ // received before acking
+const QuicTag kACKQ = TAG('A', 'C', 'K', 'Q'); // Send an immediate ack after
+ // 1 RTT of not receiving.
+const QuicTag kSSLR = TAG('S', 'S', 'L', 'R'); // Slow Start Large Reduction.
+const QuicTag kNPRR = TAG('N', 'P', 'R', 'R'); // Pace at unity instead of PRR
+const QuicTag k5RTO = TAG('5', 'R', 'T', 'O'); // Close connection on 5 RTOs
+const QuicTag kCONH = TAG('C', 'O', 'N', 'H'); // Conservative Handshake
+ // Retransmissions.
+const QuicTag kLFAK = TAG('L', 'F', 'A', 'K'); // Don't invoke FACK on the
+ // first ack.
+const QuicTag kSTMP = TAG('S', 'T', 'M', 'P'); // Send and process timestamps
+// TODO(fayang): Remove this connection option when QUIC_VERSION_35, is removed
+// Since MAX_HEADER_LIST_SIZE settings frame is supported instead.
+const QuicTag kSMHL = TAG('S', 'M', 'H', 'L'); // Support MAX_HEADER_LIST_SIZE
+ // settings frame.
+const QuicTag kNSTP = TAG('N', 'S', 'T', 'P'); // No stop waiting frames.
+const QuicTag kNRTT = TAG('N', 'R', 'T', 'T'); // Ignore initial RTT
+
+// Optional support of truncated Connection IDs. If sent by a peer, the value
+// is the minimum number of bytes allowed for the connection ID sent to the
+// peer.
+const QuicTag kTCID = TAG('T', 'C', 'I', 'D'); // Connection ID truncation.
+
+// Multipath option.
+const QuicTag kMPTH = TAG('M', 'P', 'T', 'H'); // Enable multipath.
+
+const QuicTag kNCMR = TAG('N', 'C', 'M', 'R'); // Do not attempt connection
+ // migration.
+
+// Disable Pacing offload option.
+const QuicTag kNPCO = TAG('N', 'P', 'C', 'O'); // No pacing offload.
+
+// Enable bandwidth resumption experiment.
+const QuicTag kBWRE = TAG('B', 'W', 'R', 'E'); // Bandwidth resumption.
+const QuicTag kBWMX = TAG('B', 'W', 'M', 'X'); // Max bandwidth resumption.
+const QuicTag kBWRS = TAG('B', 'W', 'R', 'S'); // Server bandwidth resumption.
+const QuicTag kBWS2 = TAG('B', 'W', 'S', '2'); // Server bw resumption v2.
+
+// Enable path MTU discovery experiment.
+const QuicTag kMTUH = TAG('M', 'T', 'U', 'H'); // High-target MTU discovery.
+const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery.
+
+// Proof types (i.e. certificate types)
+// NOTE: although it would be silly to do so, specifying both kX509 and kX59R
+// is allowed and is equivalent to specifying only kX509.
+const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key
+ // types
+const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys
+ // only
+const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID.
+
+// Client hello tags
+const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version
+const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce
+const QuicTag kNONP = TAG('N', 'O', 'N', 'P'); // The client's proof nonce
+const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods
+const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated
+ // encryption algorithms
+const QuicTag kCOPT = TAG('C', 'O', 'P', 'T'); // Connection options
+const QuicTag kCLOP = TAG('C', 'L', 'O', 'P'); // Client connection options
+const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle network timeout
+const QuicTag kSCLS = TAG('S', 'C', 'L', 'S'); // Silently close on timeout
+const QuicTag kMIDS = TAG('M', 'I', 'D', 'S'); // Max incoming dynamic streams
+const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us.
+const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
+ // indication
+const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values
+const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id
+const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit.
+const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand.
+const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature).
+const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set
+const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate
+const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry
+const QuicTag kSTTL = TAG('S', 'T', 'T', 'L'); // Server Config TTL
+const QuicTag kSFCW = TAG('S', 'F', 'C', 'W'); // Initial stream flow control
+ // receive window.
+const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection
+ // flow control receive window.
+const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID.
+const QuicTag kXLCT = TAG('X', 'L', 'C', 'T'); // Expected leaf certificate.
+const QuicTag kTBKP = TAG('T', 'B', 'K', 'P'); // Token Binding key params.
+
+// Token Binding tags
+const QuicTag kTB10 = TAG('T', 'B', '1', '0'); // TB draft 10 with P256.
+
+// Rejection tags
+const QuicTag kRREJ = TAG('R', 'R', 'E', 'J'); // Reasons for server sending
+// Stateless Reject tags
+const QuicTag kRCID = TAG('R', 'C', 'I', 'D'); // Server-designated
+ // connection ID
+// Server hello tags
+const QuicTag kCADR = TAG('C', 'A', 'D', 'R'); // Client IP address and port
+const QuicTag kASAD = TAG('A', 'S', 'A', 'D'); // Alternate Server IP address
+ // and port.
+const QuicTag kSRST = TAG('S', 'R', 'S', 'T'); // Stateless reset token used
+ // in IETF public reset packet
+
+// CETV tags
+const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key
+const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature
+
+// Public reset tags
+const QuicTag kRNON = TAG('R', 'N', 'O', 'N'); // Public reset nonce proof
+const QuicTag kRSEQ = TAG('R', 'S', 'E', 'Q'); // Rejected packet number
+
+// Universal tags
+const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding
+
+// Stats collection tags
+const QuicTag kEPID = TAG('E', 'P', 'I', 'D'); // Endpoint identifier.
+
+// clang-format on
+
+// These tags have a special form so that they appear either at the beginning
+// or the end of a handshake message. Since handshake messages are sorted by
+// tag value, the tags with 0 at the end will sort first and those with 255 at
+// the end will sort last.
+//
+// The certificate chain should have a tag that will cause it to be sorted at
+// the end of any handshake messages because it's likely to be large and the
+// client might be able to get everything that it needs from the small values at
+// the beginning.
+//
+// Likewise tags with random values should be towards the beginning of the
+// message because the server mightn't hold state for a rejected client hello
+// and therefore the client may have issues reassembling the rejection message
+// in the event that it sent two client hellos.
+const QuicTag kServerNonceTag = TAG('S', 'N', 'O', 0); // The server's nonce
+const QuicTag kSourceAddressTokenTag =
+ TAG('S', 'T', 'K', 0); // Source-address token
+const QuicTag kCertificateTag = TAG('C', 'R', 'T', 255); // Certificate chain
+const QuicTag kCertificateSCTTag =
+ TAG('C', 'S', 'C', 'T'); // Signed cert timestamp (RFC6962) of leaf cert.
+
+#undef TAG
+
+const size_t kMaxEntries = 128; // Max number of entries in a message.
+
+const size_t kNonceSize = 32; // Size in bytes of the connection nonce.
+
+const size_t kOrbitSize = 8; // Number of bytes in an orbit value.
+
+// kProofSignatureLabel is prepended to the CHLO hash and server configs before
+// signing to avoid any cross-protocol attacks on the signature.
+const char kProofSignatureLabel[] = "QUIC CHLO and server config signature";
+
+// kClientHelloMinimumSize is the minimum size of a client hello. Client hellos
+// will have PAD tags added in order to ensure this minimum is met and client
+// hellos smaller than this will be an error. This minimum size reduces the
+// amplification factor of any mirror DoS attack.
+//
+// A client may pad an inchoate client hello to a size larger than
+// kClientHelloMinimumSize to make it more likely to receive a complete
+// rejection message.
+const size_t kClientHelloMinimumSize = 1024;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_PROTOCOL_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.cc
new file mode 100644
index 00000000000..40f69df0013
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h"
+
+#include "third_party/boringssl/src/include/openssl/aead.h"
+#include "third_party/boringssl/src/include/openssl/err.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+// kSIVNonceSize contains the number of bytes of nonce in each AES-GCM-SIV box.
+// AES-GCM-SIV takes a 12-byte nonce and, since the messages are so small, each
+// key is good for more than 2^64 source-address tokens. See table 1 of
+// https://eprint.iacr.org/2017/168.pdf
+static const size_t kSIVNonceSize = 12;
+
+// AES-GCM-SIV comes in AES-128 and AES-256 flavours. The AES-256 version is
+// used here so that the key size matches the 256-bit XSalsa20 keys that we
+// used to use.
+static const size_t kBoxKeySize = 32;
+
+struct CryptoSecretBoxer::State {
+ // ctxs are the initialised AEAD contexts. These objects contain the
+ // scheduled AES state for each of the keys.
+ std::vector<bssl::UniquePtr<EVP_AEAD_CTX>> ctxs;
+};
+
+CryptoSecretBoxer::CryptoSecretBoxer() {}
+
+CryptoSecretBoxer::~CryptoSecretBoxer() {}
+
+// static
+size_t CryptoSecretBoxer::GetKeySize() {
+ return kBoxKeySize;
+}
+
+// kAEAD is the AEAD used for boxing: AES-256-GCM-SIV.
+static const EVP_AEAD* (*const kAEAD)() = EVP_aead_aes_256_gcm_siv;
+
+void CryptoSecretBoxer::SetKeys(const std::vector<std::string>& keys) {
+ DCHECK(!keys.empty());
+ const EVP_AEAD* const aead = kAEAD();
+ std::unique_ptr<State> new_state(new State);
+
+ for (const std::string& key : keys) {
+ DCHECK_EQ(kBoxKeySize, key.size());
+ bssl::UniquePtr<EVP_AEAD_CTX> ctx(
+ EVP_AEAD_CTX_new(aead, reinterpret_cast<const uint8_t*>(key.data()),
+ key.size(), EVP_AEAD_DEFAULT_TAG_LENGTH));
+ if (!ctx) {
+ ERR_clear_error();
+ LOG(DFATAL) << "EVP_AEAD_CTX_init failed";
+ return;
+ }
+
+ new_state->ctxs.push_back(std::move(ctx));
+ }
+
+ QuicWriterMutexLock l(&lock_);
+ state_ = std::move(new_state);
+}
+
+std::string CryptoSecretBoxer::Box(QuicRandom* rand,
+ QuicStringPiece plaintext) const {
+ // The box is formatted as:
+ // 12 bytes of random nonce
+ // n bytes of ciphertext
+ // 16 bytes of authenticator
+ size_t out_len =
+ kSIVNonceSize + plaintext.size() + EVP_AEAD_max_overhead(kAEAD());
+
+ std::string ret;
+ ret.resize(out_len);
+ uint8_t* out = reinterpret_cast<uint8_t*>(const_cast<char*>(ret.data()));
+
+ // Write kSIVNonceSize bytes of random nonce to the beginning of the output
+ // buffer.
+ rand->RandBytes(out, kSIVNonceSize);
+ const uint8_t* const nonce = out;
+ out += kSIVNonceSize;
+ out_len -= kSIVNonceSize;
+
+ size_t bytes_written;
+ {
+ QuicReaderMutexLock l(&lock_);
+ if (!EVP_AEAD_CTX_seal(state_->ctxs[0].get(), out, &bytes_written, out_len,
+ nonce, kSIVNonceSize,
+ reinterpret_cast<const uint8_t*>(plaintext.data()),
+ plaintext.size(), nullptr, 0)) {
+ ERR_clear_error();
+ LOG(DFATAL) << "EVP_AEAD_CTX_seal failed";
+ return "";
+ }
+ }
+
+ DCHECK_EQ(out_len, bytes_written);
+ return ret;
+}
+
+bool CryptoSecretBoxer::Unbox(QuicStringPiece in_ciphertext,
+ std::string* out_storage,
+ QuicStringPiece* out) const {
+ if (in_ciphertext.size() < kSIVNonceSize) {
+ return false;
+ }
+
+ const uint8_t* const nonce =
+ reinterpret_cast<const uint8_t*>(in_ciphertext.data());
+ const uint8_t* const ciphertext = nonce + kSIVNonceSize;
+ const size_t ciphertext_len = in_ciphertext.size() - kSIVNonceSize;
+
+ out_storage->resize(ciphertext_len);
+
+ bool ok = false;
+ {
+ QuicReaderMutexLock l(&lock_);
+ for (const bssl::UniquePtr<EVP_AEAD_CTX>& ctx : state_->ctxs) {
+ size_t bytes_written;
+ if (EVP_AEAD_CTX_open(ctx.get(),
+ reinterpret_cast<uint8_t*>(
+ const_cast<char*>(out_storage->data())),
+ &bytes_written, ciphertext_len, nonce,
+ kSIVNonceSize, ciphertext, ciphertext_len, nullptr,
+ 0)) {
+ ok = true;
+ *out = QuicStringPiece(out_storage->data(), bytes_written);
+ break;
+ }
+
+ ERR_clear_error();
+ }
+ }
+
+ return ok;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h
new file mode 100644
index 00000000000..395d3892d4f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_SECRET_BOXER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_SECRET_BOXER_H_
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicRandom;
+
+// CryptoSecretBoxer encrypts small chunks of plaintext (called 'boxing') and
+// then, later, can authenticate+decrypt the resulting boxes. This object is
+// thread-safe.
+class QUIC_EXPORT_PRIVATE CryptoSecretBoxer {
+ public:
+ CryptoSecretBoxer();
+ CryptoSecretBoxer(const CryptoSecretBoxer&) = delete;
+ CryptoSecretBoxer& operator=(const CryptoSecretBoxer&) = delete;
+ ~CryptoSecretBoxer();
+
+ // GetKeySize returns the number of bytes in a key.
+ static size_t GetKeySize();
+
+ // SetKeys sets a list of encryption keys. The first key in the list will be
+ // used by |Box|, but all supplied keys will be tried by |Unbox|, to handle
+ // key skew across the fleet. This must be called before |Box| or |Unbox|.
+ // Keys must be |GetKeySize()| bytes long.
+ void SetKeys(const std::vector<std::string>& keys);
+
+ // Box encrypts |plaintext| using a random nonce generated from |rand| and
+ // returns the resulting ciphertext. Since an authenticator and nonce are
+ // included, the result will be slightly larger than |plaintext|. The first
+ // key in the vector supplied to |SetKeys| will be used.
+ std::string Box(QuicRandom* rand, QuicStringPiece plaintext) const;
+
+ // Unbox takes the result of a previous call to |Box| in |ciphertext| and
+ // authenticates+decrypts it. If |ciphertext| cannot be decrypted with any of
+ // the supplied keys, the function returns false. Otherwise, |out_storage| is
+ // used to store the result and |out| is set to point into |out_storage| and
+ // contains the original plaintext.
+ bool Unbox(QuicStringPiece ciphertext,
+ std::string* out_storage,
+ QuicStringPiece* out) const;
+
+ private:
+ struct State;
+
+ mutable QuicMutex lock_;
+
+ // state_ is an opaque pointer to whatever additional state the concrete
+ // implementation of CryptoSecretBoxer requires.
+ std::unique_ptr<State> state_ GUARDED_BY(lock_);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_SECRET_BOXER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer_test.cc
new file mode 100644
index 00000000000..06505d91308
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer_test.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class CryptoSecretBoxerTest : public QuicTest {};
+
+TEST_F(CryptoSecretBoxerTest, BoxAndUnbox) {
+ QuicStringPiece message("hello world");
+
+ CryptoSecretBoxer boxer;
+ boxer.SetKeys({std::string(CryptoSecretBoxer::GetKeySize(), 0x11)});
+
+ const std::string box = boxer.Box(QuicRandom::GetInstance(), message);
+
+ std::string storage;
+ QuicStringPiece result;
+ EXPECT_TRUE(boxer.Unbox(box, &storage, &result));
+ EXPECT_EQ(result, message);
+
+ EXPECT_FALSE(boxer.Unbox(std::string(1, 'X') + box, &storage, &result));
+ EXPECT_FALSE(
+ boxer.Unbox(box.substr(1, std::string::npos), &storage, &result));
+ EXPECT_FALSE(boxer.Unbox(std::string(), &storage, &result));
+ EXPECT_FALSE(boxer.Unbox(
+ std::string(1, box[0] ^ 0x80) + box.substr(1, std::string::npos),
+ &storage, &result));
+}
+
+// Helper function to test whether one boxer can decode the output of another.
+static bool CanDecode(const CryptoSecretBoxer& decoder,
+ const CryptoSecretBoxer& encoder) {
+ QuicStringPiece message("hello world");
+ const std::string boxed = encoder.Box(QuicRandom::GetInstance(), message);
+ std::string storage;
+ QuicStringPiece result;
+ bool ok = decoder.Unbox(boxed, &storage, &result);
+ if (ok) {
+ EXPECT_EQ(result, message);
+ }
+ return ok;
+}
+
+TEST_F(CryptoSecretBoxerTest, MultipleKeys) {
+ std::string key_11(CryptoSecretBoxer::GetKeySize(), 0x11);
+ std::string key_12(CryptoSecretBoxer::GetKeySize(), 0x12);
+
+ CryptoSecretBoxer boxer_11, boxer_12, boxer;
+ boxer_11.SetKeys({key_11});
+ boxer_12.SetKeys({key_12});
+ boxer.SetKeys({key_12, key_11});
+
+ // Neither single-key boxer can decode the other's tokens.
+ EXPECT_FALSE(CanDecode(boxer_11, boxer_12));
+ EXPECT_FALSE(CanDecode(boxer_12, boxer_11));
+
+ // |boxer| encodes with the first key, which is key_12.
+ EXPECT_TRUE(CanDecode(boxer_12, boxer));
+ EXPECT_FALSE(CanDecode(boxer_11, boxer));
+
+ // The boxer with both keys can decode tokens from either single-key boxer.
+ EXPECT_TRUE(CanDecode(boxer, boxer_11));
+ EXPECT_TRUE(CanDecode(boxer, boxer_12));
+
+ // After we flush key_11 from |boxer|, it can no longer decode tokens from
+ // |boxer_11|.
+ boxer.SetKeys({key_12});
+ EXPECT_FALSE(CanDecode(boxer, boxer_11));
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..4d080c700d2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_server_test.cc
@@ -0,0 +1,1175 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h"
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/failing_proof_source.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+
+class DummyProofVerifierCallback : public ProofVerifierCallback {
+ public:
+ DummyProofVerifierCallback() {}
+ ~DummyProofVerifierCallback() override {}
+
+ void Run(bool ok,
+ const std::string& error_details,
+ std::unique_ptr<ProofVerifyDetails>* details) override {
+ DCHECK(false);
+ }
+};
+
+const char kOldConfigId[] = "old-config-id";
+
+} // namespace
+
+struct TestParams {
+ TestParams(bool enable_stateless_rejects,
+ bool use_stateless_rejects,
+ ParsedQuicVersionVector supported_versions)
+ : enable_stateless_rejects(enable_stateless_rejects),
+ use_stateless_rejects(use_stateless_rejects),
+ supported_versions(std::move(supported_versions)) {}
+
+ friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+ os << " enable_stateless_rejects: " << p.enable_stateless_rejects
+ << std::endl;
+ os << " use_stateless_rejects: " << p.use_stateless_rejects << std::endl;
+ os << " versions: "
+ << ParsedQuicVersionVectorToString(p.supported_versions) << " }";
+ return os;
+ }
+
+ // This only enables the stateless reject feature via the feature-flag.
+ // It does not force the crypto server to emit stateless rejects.
+ bool enable_stateless_rejects;
+ // If true, this forces the server to send a stateless reject when
+ // rejecting messages. This should be a no-op if
+ // enable_stateless_rejects is false.
+ bool use_stateless_rejects;
+ // Versions supported by client and server.
+ ParsedQuicVersionVector supported_versions;
+};
+
+// Constructs various test permutations.
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ static const bool kTrueFalse[] = {true, false};
+ for (bool enable_stateless_rejects : kTrueFalse) {
+ for (bool use_stateless_rejects : kTrueFalse) {
+ // Start with all versions, remove highest on each iteration.
+ ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ while (!supported_versions.empty()) {
+ params.push_back(TestParams(enable_stateless_rejects,
+ use_stateless_rejects, supported_versions));
+ supported_versions.erase(supported_versions.begin());
+ }
+ }
+ }
+ return params;
+}
+
+class CryptoServerTest : public QuicTestWithParam<TestParams> {
+ public:
+ CryptoServerTest()
+ : rand_(QuicRandom::GetInstance()),
+ client_address_(QuicIpAddress::Loopback4(), 1234),
+ client_version_(UnsupportedQuicVersion()),
+ config_(QuicCryptoServerConfig::TESTING,
+ rand_,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ peer_(&config_),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ params_(new QuicCryptoNegotiatedParameters),
+ signed_config_(new QuicSignedServerConfig),
+ chlo_packet_size_(kDefaultMaxPacketSize) {
+ supported_versions_ = GetParam().supported_versions;
+ config_.set_enable_serving_sct(true);
+
+ client_version_ = supported_versions_.front();
+ client_version_string_ = ParsedQuicVersionToString(client_version_);
+
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support,
+ GetParam().enable_stateless_rejects);
+ use_stateless_rejects_ = GetParam().use_stateless_rejects;
+ }
+
+ void SetUp() override {
+ QuicCryptoServerConfig::ConfigOptions old_config_options;
+ old_config_options.id = kOldConfigId;
+ config_.AddDefaultConfig(rand_, &clock_, old_config_options);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+ QuicServerConfigProtobuf primary_config =
+ config_.GenerateConfig(rand_, &clock_, config_options_);
+ primary_config.set_primary_time(clock_.WallNow().ToUNIXSeconds());
+ std::unique_ptr<CryptoHandshakeMessage> msg(
+ config_.AddConfig(std::move(primary_config), clock_.WallNow()));
+
+ QuicStringPiece orbit;
+ CHECK(msg->GetStringPiece(kORBT, &orbit));
+ CHECK_EQ(sizeof(orbit_), orbit.size());
+ memcpy(orbit_, orbit.data(), orbit.size());
+
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+
+ nonce_hex_ = "#" + QuicTextUtils::HexEncode(GenerateNonce());
+ pub_hex_ =
+ "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
+
+ CryptoHandshakeMessage client_hello =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"CSCT", ""},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldSucceed(client_hello);
+ // The message should be rejected because the source-address token is
+ // missing.
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+ CheckForServerDesignatedConnectionId();
+
+ QuicStringPiece srct;
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct));
+ srct_hex_ = "#" + QuicTextUtils::HexEncode(srct);
+
+ QuicStringPiece scfg;
+ ASSERT_TRUE(out_.GetStringPiece(kSCFG, &scfg));
+ server_config_ = CryptoFramer::ParseMessage(scfg);
+
+ QuicStringPiece scid;
+ ASSERT_TRUE(server_config_->GetStringPiece(kSCID, &scid));
+ scid_hex_ = "#" + QuicTextUtils::HexEncode(scid);
+
+ signed_config_ = QuicReferenceCountedPointer<QuicSignedServerConfig>(
+ new QuicSignedServerConfig());
+ DCHECK(signed_config_->chain.get() == nullptr);
+ }
+
+ // Helper used to accept the result of ValidateClientHello and pass
+ // it on to ProcessClientHello.
+ class ValidateCallback : public ValidateClientHelloResultCallback {
+ public:
+ ValidateCallback(CryptoServerTest* test,
+ bool should_succeed,
+ const char* error_substr,
+ bool* called)
+ : test_(test),
+ should_succeed_(should_succeed),
+ error_substr_(error_substr),
+ called_(called) {
+ *called_ = false;
+ }
+
+ void Run(QuicReferenceCountedPointer<Result> result,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ ASSERT_FALSE(*called_);
+ test_->ProcessValidationResult(std::move(result), should_succeed_,
+ error_substr_);
+ *called_ = true;
+ }
+
+ private:
+ CryptoServerTest* test_;
+ const bool should_succeed_;
+ const char* const error_substr_;
+ bool* called_;
+ };
+
+ void CheckServerHello(const CryptoHandshakeMessage& server_hello) {
+ QuicVersionLabelVector versions;
+ server_hello.GetVersionLabelList(kVER, &versions);
+ ASSERT_EQ(supported_versions_.size(), versions.size());
+ for (size_t i = 0; i < versions.size(); ++i) {
+ EXPECT_EQ(CreateQuicVersionLabel(supported_versions_[i]), versions[i]);
+ }
+
+ QuicStringPiece address;
+ ASSERT_TRUE(server_hello.GetStringPiece(kCADR, &address));
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(address.data(), address.size()));
+ EXPECT_EQ(client_address_.host(), decoder.ip());
+ EXPECT_EQ(client_address_.port(), decoder.port());
+ }
+
+ void ShouldSucceed(const CryptoHandshakeMessage& message) {
+ bool called = false;
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
+ config_.ValidateClientHello(
+ message, client_address_.host(), server_address,
+ supported_versions_.front().transport_version, &clock_, signed_config_,
+ QuicMakeUnique<ValidateCallback>(this, true, "", &called));
+ EXPECT_TRUE(called);
+ }
+
+ void ShouldFailMentioning(const char* error_substr,
+ const CryptoHandshakeMessage& message) {
+ bool called = false;
+ ShouldFailMentioning(error_substr, message, &called);
+ EXPECT_TRUE(called);
+ }
+
+ void ShouldFailMentioning(const char* error_substr,
+ const CryptoHandshakeMessage& message,
+ bool* called) {
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
+ config_.ValidateClientHello(
+ message, client_address_.host(), server_address,
+ supported_versions_.front().transport_version, &clock_, signed_config_,
+ QuicMakeUnique<ValidateCallback>(this, false, error_substr, called));
+ }
+
+ class ProcessCallback : public ProcessClientHelloResultCallback {
+ public:
+ ProcessCallback(
+ QuicReferenceCountedPointer<ValidateCallback::Result> result,
+ bool should_succeed,
+ const char* error_substr,
+ bool* called,
+ CryptoHandshakeMessage* out)
+ : result_(std::move(result)),
+ should_succeed_(should_succeed),
+ error_substr_(error_substr),
+ called_(called),
+ out_(out) {
+ *called_ = false;
+ }
+
+ void Run(
+ QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) override {
+ if (should_succeed_) {
+ ASSERT_EQ(error, QUIC_NO_ERROR)
+ << "Message failed with error " << error_details << ": "
+ << result_->client_hello.DebugString();
+ } else {
+ ASSERT_NE(error, QUIC_NO_ERROR)
+ << "Message didn't fail: " << result_->client_hello.DebugString();
+
+ EXPECT_TRUE(error_details.find(error_substr_) != std::string::npos)
+ << error_substr_ << " not in " << error_details;
+ }
+ if (message != nullptr) {
+ *out_ = *message;
+ }
+ *called_ = true;
+ }
+
+ private:
+ const QuicReferenceCountedPointer<ValidateCallback::Result> result_;
+ const bool should_succeed_;
+ const char* const error_substr_;
+ bool* called_;
+ CryptoHandshakeMessage* out_;
+ };
+
+ void ProcessValidationResult(
+ QuicReferenceCountedPointer<ValidateCallback::Result> result,
+ bool should_succeed,
+ const char* error_substr) {
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 5);
+ QuicConnectionId server_designated_connection_id =
+ TestConnectionId(rand_for_id_generation_.RandUint64());
+ bool called;
+ config_.ProcessClientHello(
+ result, /*reject_only=*/false,
+ /*connection_id=*/TestConnectionId(1), server_address, client_address_,
+ supported_versions_.front(), supported_versions_,
+ use_stateless_rejects_, server_designated_connection_id, &clock_, rand_,
+ &compressed_certs_cache_, params_, signed_config_,
+ /*total_framing_overhead=*/50, chlo_packet_size_,
+ QuicMakeUnique<ProcessCallback>(result, should_succeed, error_substr,
+ &called, &out_));
+ EXPECT_TRUE(called);
+ }
+
+ std::string GenerateNonce() {
+ std::string nonce;
+ CryptoUtils::GenerateNonce(
+ clock_.WallNow(), rand_,
+ QuicStringPiece(reinterpret_cast<const char*>(orbit_), sizeof(orbit_)),
+ &nonce);
+ return nonce;
+ }
+
+ void CheckRejectReasons(
+ const HandshakeFailureReason* expected_handshake_failures,
+ size_t expected_count) {
+ QuicTagVector reject_reasons;
+ static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync");
+ QuicErrorCode error_code = out_.GetTaglist(kRREJ, &reject_reasons);
+ ASSERT_EQ(QUIC_NO_ERROR, error_code);
+
+ EXPECT_EQ(expected_count, reject_reasons.size());
+ for (size_t i = 0; i < reject_reasons.size(); ++i) {
+ EXPECT_EQ(static_cast<QuicTag>(expected_handshake_failures[i]),
+ reject_reasons[i]);
+ }
+ }
+
+ // If the server is rejecting statelessly, make sure it contains a
+ // server-designated connection id. Once the check is complete,
+ // allow the random id-generator to move to the next value.
+ void CheckForServerDesignatedConnectionId() {
+ uint64_t server_designated_connection_id;
+ if (!RejectsAreStateless()) {
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND,
+ out_.GetUint64(kRCID, &server_designated_connection_id));
+ } else {
+ ASSERT_EQ(QUIC_NO_ERROR,
+ out_.GetUint64(kRCID, &server_designated_connection_id));
+ server_designated_connection_id =
+ QuicEndian::NetToHost64(server_designated_connection_id);
+ EXPECT_EQ(rand_for_id_generation_.RandUint64(),
+ server_designated_connection_id);
+ }
+ rand_for_id_generation_.ChangeValue();
+ }
+
+ void CheckRejectTag() {
+ if (RejectsAreStateless()) {
+ ASSERT_EQ(kSREJ, out_.tag()) << QuicTagToString(out_.tag());
+ } else {
+ ASSERT_EQ(kREJ, out_.tag()) << QuicTagToString(out_.tag());
+ }
+ }
+
+ bool RejectsAreStateless() {
+ return GetParam().enable_stateless_rejects &&
+ GetParam().use_stateless_rejects;
+ }
+
+ std::string XlctHexString() {
+ uint64_t xlct = crypto_test_utils::LeafCertHashForTesting();
+ return "#" + QuicTextUtils::HexEncode(reinterpret_cast<char*>(&xlct),
+ sizeof(xlct));
+ }
+
+ protected:
+ QuicRandom* const rand_;
+ MockRandom rand_for_id_generation_;
+ MockClock clock_;
+ QuicSocketAddress client_address_;
+ ParsedQuicVersionVector supported_versions_;
+ ParsedQuicVersion client_version_;
+ std::string client_version_string_;
+ QuicCryptoServerConfig config_;
+ QuicCryptoServerConfigPeer peer_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicCryptoServerConfig::ConfigOptions config_options_;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ CryptoHandshakeMessage out_;
+ uint8_t orbit_[kOrbitSize];
+ bool use_stateless_rejects_;
+ size_t chlo_packet_size_;
+
+ // These strings contain hex escaped values from the server suitable for using
+ // when constructing client hello messages.
+ std::string nonce_hex_, pub_hex_, srct_hex_, scid_hex_;
+ std::unique_ptr<CryptoHandshakeMessage> server_config_;
+};
+
+INSTANTIATE_TEST_SUITE_P(CryptoServerTests,
+ CryptoServerTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(CryptoServerTest, BadSNI) {
+ // clang-format off
+ static const char* const kBadSNIs[] = {
+ "",
+ "foo",
+ "#00",
+ "#ff00",
+ "127.0.0.1",
+ "ffee::1",
+ };
+ // clang-format on
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kBadSNIs); i++) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"SNI", kBadSNIs[i]},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldFailMentioning("SNI", msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+ }
+}
+
+TEST_P(CryptoServerTest, DefaultCert) {
+ // Check that the server replies with a default certificate when no SNI is
+ // specified. The CHLO is constructed to generate a REJ with certs, so must
+ // not contain a valid STK, and must include PDMD.
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ QuicStringPiece cert, proof, cert_sct;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct));
+ EXPECT_NE(0u, cert.size());
+ EXPECT_NE(0u, proof.size());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+ EXPECT_LT(0u, cert_sct.size());
+}
+
+TEST_P(CryptoServerTest, RejectTooLarge) {
+ // Check that the server replies with no certificate when a CHLO is
+ // constructed with a PDMD but no SKT when the REJ would be too large.
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // The REJ will be larger than the CHLO so no PROF or CRT will be sent.
+ config_.set_chlo_multiplier(1);
+
+ ShouldSucceed(msg);
+ QuicStringPiece cert, proof, cert_sct;
+ EXPECT_FALSE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_FALSE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_FALSE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, RejectNotTooLarge) {
+ // When the CHLO packet is large enough, ensure that a full REJ is sent.
+ chlo_packet_size_ *= 2;
+
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // The REJ will be larger than the CHLO so no PROF or CRT will be sent.
+ config_.set_chlo_multiplier(1);
+
+ ShouldSucceed(msg);
+ QuicStringPiece cert, proof, cert_sct;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, RejectTooLargeButValidSTK) {
+ // Check that the server replies with no certificate when a CHLO is
+ // constructed with a PDMD but no SKT when the REJ would be too large.
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"#004b5453", srct_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // The REJ will be larger than the CHLO so no PROF or CRT will be sent.
+ config_.set_chlo_multiplier(1);
+
+ ShouldSucceed(msg);
+ QuicStringPiece cert, proof, cert_sct;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateSCTTag, &cert_sct));
+ EXPECT_NE(0u, cert.size());
+ EXPECT_NE(0u, proof.size());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, TooSmall) {
+ ShouldFailMentioning(
+ "too small",
+ crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", client_version_string_.c_str()}}));
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, BadSourceAddressToken) {
+ // Invalid source-address tokens should be ignored.
+ // clang-format off
+ static const char* const kBadSourceAddressTokens[] = {
+ "",
+ "foo",
+ "#0000",
+ "#0000000000000000000000000000000000000000",
+ };
+ // clang-format on
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kBadSourceAddressTokens); i++) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"STK", kBadSourceAddressTokens[i]},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldSucceed(msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+ }
+}
+
+TEST_P(CryptoServerTest, BadClientNonce) {
+ // clang-format off
+ static const char* const kBadNonces[] = {
+ "",
+ "#0000",
+ "#0000000000000000000000000000000000000000",
+ };
+ // clang-format on
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kBadNonces); i++) {
+ // Invalid nonces should be ignored, in an inchoate CHLO.
+
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"NONC", kBadNonces[i]},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+
+ // Invalid nonces should result in CLIENT_NONCE_INVALID_FAILURE.
+ CryptoHandshakeMessage msg1 =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", kBadNonces[i]},
+ {"NONP", kBadNonces[i]},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg1);
+
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons1[] = {
+ CLIENT_NONCE_INVALID_FAILURE};
+ CheckRejectReasons(kRejectReasons1, QUIC_ARRAYSIZE(kRejectReasons1));
+ }
+}
+
+TEST_P(CryptoServerTest, NoClientNonce) {
+ // No client nonces should result in INCHOATE_HELLO_FAILURE.
+
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+
+ CryptoHandshakeMessage msg1 =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg1);
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons1[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons1, QUIC_ARRAYSIZE(kRejectReasons1));
+}
+
+TEST_P(CryptoServerTest, DowngradeAttack) {
+ if (supported_versions_.size() == 1) {
+ // No downgrade attack is possible if the server only supports one version.
+ return;
+ }
+ // Set the client's preferred version to a supported version that
+ // is not the "current" version (supported_versions_.front()).
+ std::string bad_version =
+ ParsedQuicVersionToString(supported_versions_.back());
+
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", bad_version}}, kClientHelloMinimumSize);
+
+ ShouldFailMentioning("Downgrade", msg);
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptServerConfig) {
+ // This tests corrupted server config.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", (std::string(1, 'X') + scid_hex_)},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptSourceAddressToken) {
+ // This tests corrupted source address token.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", (std::string(1, 'X') + srct_hex_)},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptSourceAddressTokenIsStillAccepted) {
+ // This tests corrupted source address token.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", (std::string(1, 'X') + srct_hex_)},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ config_.set_validate_source_address_token(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+}
+
+TEST_P(CryptoServerTest, CorruptClientNonceAndSourceAddressToken) {
+ // This test corrupts client nonce and source address token.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", (std::string(1, 'X') + srct_hex_)},
+ {"PUBS", pub_hex_},
+ {"NONC", (std::string(1, 'X') + nonce_hex_)},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, CLIENT_NONCE_INVALID_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptMultipleTags) {
+ // This test corrupts client nonce, server nonce and source address token.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", (std::string(1, 'X') + srct_hex_)},
+ {"PUBS", pub_hex_},
+ {"NONC", (std::string(1, 'X') + nonce_hex_)},
+ {"NONP", (std::string(1, 'X') + nonce_hex_)},
+ {"SNO\0", (std::string(1, 'X') + nonce_hex_)},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ CheckRejectTag();
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, CLIENT_NONCE_INVALID_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, NoServerNonce) {
+ // When no server nonce is present and no strike register is configured,
+ // the CHLO should be rejected.
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"NONP", nonce_hex_},
+ {"XLCT", XlctHexString()},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+
+ // Even without a server nonce, this ClientHello should be accepted in
+ // version 33.
+ ASSERT_EQ(kSHLO, out_.tag());
+ CheckServerHello(out_);
+}
+
+TEST_P(CryptoServerTest, ProofForSuppliedServerConfig) {
+ client_address_ = QuicSocketAddress(QuicIpAddress::Loopback6(), 1234);
+
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PDMD", "X509"},
+ {"SCID", kOldConfigId},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"NONP", "123456789012345678901234567890"},
+ {"VER\0", client_version_string_},
+ {"XLCT", XlctHexString()}},
+ kClientHelloMinimumSize);
+
+ ShouldSucceed(msg);
+ // The message should be rejected because the source-address token is no
+ // longer valid.
+ CheckRejectTag();
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+
+ QuicStringPiece cert, proof, scfg_str;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_TRUE(out_.GetStringPiece(kSCFG, &scfg_str));
+ std::unique_ptr<CryptoHandshakeMessage> scfg(
+ CryptoFramer::ParseMessage(scfg_str));
+ QuicStringPiece scid;
+ EXPECT_TRUE(scfg->GetStringPiece(kSCID, &scid));
+ EXPECT_NE(scid, kOldConfigId);
+
+ // Get certs from compressed certs.
+ const CommonCertSets* common_cert_sets(CommonCertSets::GetInstanceQUIC());
+ std::vector<std::string> cached_certs;
+
+ std::vector<std::string> certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(cert, cached_certs,
+ common_cert_sets, &certs));
+
+ // Check that the proof in the REJ message is valid.
+ std::unique_ptr<ProofVerifier> proof_verifier(
+ crypto_test_utils::ProofVerifierForTesting());
+ std::unique_ptr<ProofVerifyContext> verify_context(
+ crypto_test_utils::ProofVerifyContextForTesting());
+ std::unique_ptr<ProofVerifyDetails> details;
+ std::string error_details;
+ std::unique_ptr<ProofVerifierCallback> callback(
+ new DummyProofVerifierCallback());
+ const std::string chlo_hash =
+ CryptoUtils::HashHandshakeMessage(msg, Perspective::IS_SERVER);
+ EXPECT_EQ(QUIC_SUCCESS,
+ proof_verifier->VerifyProof(
+ "test.example.com", 443, (std::string(scfg_str)),
+ client_version_.transport_version, chlo_hash, certs, "",
+ (std::string(proof)), verify_context.get(), &error_details,
+ &details, std::move(callback)));
+}
+
+TEST_P(CryptoServerTest, RejectInvalidXlct) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_},
+ {"XLCT", "#0102030405060708"}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ INVALID_EXPECTED_LEAF_CERTIFICATE};
+
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, ValidXlct) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_},
+ {"XLCT", XlctHexString()}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+}
+
+TEST_P(CryptoServerTest, NonceInSHLO) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_},
+ {"XLCT", XlctHexString()}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+
+ QuicStringPiece nonce;
+ EXPECT_TRUE(out_.GetStringPiece(kServerNonceTag, &nonce));
+}
+
+TEST_P(CryptoServerTest, ProofSourceFailure) {
+ // Install a ProofSource which will unconditionally fail
+ peer_.ResetProofSource(std::unique_ptr<ProofSource>(new FailingProofSource));
+
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"PDMD", "X509"},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // Just ensure that we don't crash as occurred in b/33916924.
+ ShouldFailMentioning("", msg);
+}
+
+// Regression test for crbug.com/723604
+// For 2RTT, if the first CHLO from the client contains hashes of cached
+// certs (stored in CCRT tag) but the second CHLO does not, then the second REJ
+// from the server should not contain hashes of cached certs.
+TEST_P(CryptoServerTest, TwoRttServerDropCachedCerts) {
+ // Send inchoate CHLO to get cert chain from server. This CHLO is only for
+ // the purpose of getting the server's certs; it is not part of the 2RTT
+ // handshake.
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+ ShouldSucceed(msg);
+
+ // Decompress cert chain from server to individual certs.
+ QuicStringPiece certs_compressed;
+ ASSERT_TRUE(out_.GetStringPiece(kCertificateTag, &certs_compressed));
+ ASSERT_NE(0u, certs_compressed.size());
+ std::vector<std::string> certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(
+ certs_compressed, /*cached_certs=*/{}, /*common_sets=*/nullptr, &certs));
+
+ // Start 2-RTT. Client sends CHLO with bad source-address token and hashes of
+ // the certs, which tells the server that the client has cached those certs.
+ config_.set_chlo_multiplier(1);
+ const char kBadSourceAddressToken[] = "";
+ msg.SetStringPiece(kSourceAddressTokenTag, kBadSourceAddressToken);
+ std::vector<uint64_t> hashes(certs.size());
+ for (size_t i = 0; i < certs.size(); ++i) {
+ hashes[i] = QuicUtils::QuicUtils::FNV1a_64_Hash(certs[i]);
+ }
+ msg.SetVector(kCCRT, hashes);
+ ShouldSucceed(msg);
+
+ // Server responds with inchoate REJ containing valid source-address token.
+ QuicStringPiece srct;
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct));
+
+ // Client now drops cached certs; sends CHLO with updated source-address
+ // token but no hashes of certs.
+ msg.SetStringPiece(kSourceAddressTokenTag, srct);
+ msg.Erase(kCCRT);
+ ShouldSucceed(msg);
+
+ // Server response's cert chain should not contain hashes of
+ // previously-cached certs.
+ ASSERT_TRUE(out_.GetStringPiece(kCertificateTag, &certs_compressed));
+ ASSERT_NE(0u, certs_compressed.size());
+ ASSERT_TRUE(CertCompressor::DecompressChain(
+ certs_compressed, /*cached_certs=*/{}, /*common_sets=*/nullptr, &certs));
+}
+
+class CryptoServerConfigGenerationTest : public QuicTest {};
+
+TEST_F(CryptoServerConfigGenerationTest, Determinism) {
+ // Test that using a deterministic PRNG causes the server-config to be
+ // deterministic.
+
+ MockRandom rand_a, rand_b;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ std::unique_ptr<CryptoHandshakeMessage> scfg_a(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+ std::unique_ptr<CryptoHandshakeMessage> scfg_b(
+ b.AddDefaultConfig(&rand_b, &clock, options));
+
+ ASSERT_EQ(scfg_a->DebugString(), scfg_b->DebugString());
+}
+
+TEST_F(CryptoServerConfigGenerationTest, SCIDVaries) {
+ // This test ensures that the server config ID varies for different server
+ // configs.
+
+ MockRandom rand_a, rand_b;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ rand_b.ChangeValue();
+ QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ std::unique_ptr<CryptoHandshakeMessage> scfg_a(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+ std::unique_ptr<CryptoHandshakeMessage> scfg_b(
+ b.AddDefaultConfig(&rand_b, &clock, options));
+
+ QuicStringPiece scid_a, scid_b;
+ EXPECT_TRUE(scfg_a->GetStringPiece(kSCID, &scid_a));
+ EXPECT_TRUE(scfg_b->GetStringPiece(kSCID, &scid_b));
+
+ EXPECT_NE(scid_a, scid_b);
+}
+
+TEST_F(CryptoServerConfigGenerationTest, SCIDIsHashOfServerConfig) {
+ MockRandom rand_a;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ std::unique_ptr<CryptoHandshakeMessage> scfg(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+
+ QuicStringPiece scid;
+ EXPECT_TRUE(scfg->GetStringPiece(kSCID, &scid));
+ // Need to take a copy of |scid| has we're about to call |Erase|.
+ const std::string scid_str(scid);
+
+ scfg->Erase(kSCID);
+ scfg->MarkDirty();
+ const QuicData& serialized(scfg->GetSerialized());
+
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(serialized.data()),
+ serialized.length(), digest);
+
+ // scid is a SHA-256 hash, truncated to 16 bytes.
+ ASSERT_EQ(scid.size(), 16u);
+ EXPECT_EQ(0, memcmp(digest, scid_str.c_str(), scid.size()));
+}
+
+class CryptoServerTestNoConfig : public CryptoServerTest {
+ public:
+ void SetUp() override {
+ // Deliberately don't add a config so that we can test this situation.
+ }
+};
+
+TEST_P(CryptoServerTestNoConfig, DontCrash) {
+ CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"}, {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ ShouldFailMentioning("No config", msg);
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
+ CheckRejectReasons(kRejectReasons, QUIC_ARRAYSIZE(kRejectReasons));
+}
+
+class CryptoServerTestOldVersion : public CryptoServerTest {
+ public:
+ void SetUp() override {
+ client_version_ = supported_versions_.back();
+ client_version_string_ = ParsedQuicVersionToString(client_version_);
+ CryptoServerTest::SetUp();
+ }
+};
+
+TEST_P(CryptoServerTestOldVersion, ServerIgnoresXlct) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_},
+ {"XLCT", "#0100000000000000"}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+}
+
+TEST_P(CryptoServerTestOldVersion, XlctNotRequired) {
+ CryptoHandshakeMessage msg =
+ crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"SCID", scid_hex_},
+ {"#004b5453", srct_hex_},
+ {"PUBS", pub_hex_},
+ {"NONC", nonce_hex_},
+ {"VER\0", client_version_string_}},
+ kClientHelloMinimumSize);
+
+ // If replay protection isn't disabled, then
+ // QuicCryptoServerConfig::EvaluateClientHello will leave info.unique as false
+ // and cause ProcessClientHello to exit early (and generate a REJ message).
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ EXPECT_EQ(kSHLO, out_.tag());
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..724f245fb7c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.cc
@@ -0,0 +1,470 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+
+#include <memory>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/hkdf.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+// TODO(nharper): HkdfExpandLabel and SetKeyAndIV (below) implement what is
+// specified in draft-ietf-quic-tls-16. The latest editors' draft has changed
+// derivation again, and this will need to be updated to reflect those (and any
+// other future) changes.
+// static
+std::vector<uint8_t> CryptoUtils::HkdfExpandLabel(
+ const EVP_MD* prf,
+ const std::vector<uint8_t>& secret,
+ const std::string& label,
+ size_t out_len) {
+ bssl::ScopedCBB quic_hkdf_label;
+ CBB inner_label;
+ const char label_prefix[] = "tls13 ";
+ // 19 = size(u16) + size(u8) + len("tls13 ") + len ("client in") + size(u8)
+ static const size_t max_quic_hkdf_label_length = 19;
+ if (!CBB_init(quic_hkdf_label.get(), max_quic_hkdf_label_length) ||
+ !CBB_add_u16(quic_hkdf_label.get(), out_len) ||
+ !CBB_add_u8_length_prefixed(quic_hkdf_label.get(), &inner_label) ||
+ !CBB_add_bytes(&inner_label,
+ reinterpret_cast<const uint8_t*>(label_prefix),
+ QUIC_ARRAYSIZE(label_prefix) - 1) ||
+ !CBB_add_bytes(&inner_label,
+ reinterpret_cast<const uint8_t*>(label.data()),
+ label.size()) ||
+ !CBB_add_u8(quic_hkdf_label.get(), 0) ||
+ !CBB_flush(quic_hkdf_label.get())) {
+ QUIC_LOG(ERROR) << "Building HKDF label failed";
+ return std::vector<uint8_t>();
+ }
+ std::vector<uint8_t> out;
+ out.resize(out_len);
+ if (!HKDF_expand(out.data(), out_len, prf, secret.data(), secret.size(),
+ CBB_data(quic_hkdf_label.get()),
+ CBB_len(quic_hkdf_label.get()))) {
+ QUIC_LOG(ERROR) << "Running HKDF-Expand-Label failed";
+ return std::vector<uint8_t>();
+ }
+ return out;
+}
+
+void CryptoUtils::SetKeyAndIV(const EVP_MD* prf,
+ const std::vector<uint8_t>& pp_secret,
+ QuicCrypter* crypter) {
+ std::vector<uint8_t> key = CryptoUtils::HkdfExpandLabel(
+ prf, pp_secret, "quic key", crypter->GetKeySize());
+ std::vector<uint8_t> iv = CryptoUtils::HkdfExpandLabel(
+ prf, pp_secret, "quic iv", crypter->GetIVSize());
+ crypter->SetKey(
+ QuicStringPiece(reinterpret_cast<char*>(key.data()), key.size()));
+ crypter->SetIV(
+ QuicStringPiece(reinterpret_cast<char*>(iv.data()), iv.size()));
+}
+
+namespace {
+
+const uint8_t kInitialSalt[] = {0xef, 0x4f, 0xb0, 0xab, 0xb4, 0x74, 0x70,
+ 0xc4, 0x1b, 0xef, 0xcf, 0x80, 0x31, 0x33,
+ 0x4f, 0xae, 0x48, 0x5e, 0x09, 0xa0};
+
+const char kPreSharedKeyLabel[] = "QUIC PSK";
+
+} // namespace
+
+// static
+void CryptoUtils::CreateTlsInitialCrypters(Perspective perspective,
+ QuicTransportVersion version,
+ QuicConnectionId connection_id,
+ CrypterPair* crypters) {
+ QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(connection_id, version))
+ << "CreateTlsInitialCrypters: attempted to use connection ID "
+ << connection_id << " which is invalid with version "
+ << QuicVersionToString(version);
+ const EVP_MD* hash = EVP_sha256();
+
+ std::vector<uint8_t> handshake_secret;
+ handshake_secret.resize(EVP_MAX_MD_SIZE);
+ size_t handshake_secret_len;
+ const bool hkdf_extract_success = HKDF_extract(
+ handshake_secret.data(), &handshake_secret_len, hash,
+ reinterpret_cast<const uint8_t*>(connection_id.data()),
+ connection_id.length(), kInitialSalt, QUIC_ARRAYSIZE(kInitialSalt));
+ QUIC_BUG_IF(!hkdf_extract_success)
+ << "HKDF_extract failed when creating initial crypters";
+ handshake_secret.resize(handshake_secret_len);
+
+ const std::string client_label = "client in";
+ const std::string server_label = "server in";
+ std::string encryption_label, decryption_label;
+ if (perspective == Perspective::IS_CLIENT) {
+ encryption_label = client_label;
+ decryption_label = server_label;
+ } else {
+ encryption_label = server_label;
+ decryption_label = client_label;
+ }
+ crypters->encrypter = QuicMakeUnique<Aes128GcmEncrypter>();
+ std::vector<uint8_t> encryption_secret = HkdfExpandLabel(
+ hash, handshake_secret, encryption_label, EVP_MD_size(hash));
+ SetKeyAndIV(hash, encryption_secret, crypters->encrypter.get());
+
+ crypters->decrypter = QuicMakeUnique<Aes128GcmDecrypter>();
+ std::vector<uint8_t> decryption_secret = HkdfExpandLabel(
+ hash, handshake_secret, decryption_label, EVP_MD_size(hash));
+ SetKeyAndIV(hash, decryption_secret, crypters->decrypter.get());
+}
+
+// static
+void CryptoUtils::GenerateNonce(QuicWallTime now,
+ QuicRandom* random_generator,
+ QuicStringPiece orbit,
+ std::string* nonce) {
+ // a 4-byte timestamp + 28 random bytes.
+ nonce->reserve(kNonceSize);
+ nonce->resize(kNonceSize);
+
+ uint32_t gmt_unix_time = static_cast<uint32_t>(now.ToUNIXSeconds());
+ // The time in the nonce must be encoded in big-endian because the
+ // strike-register depends on the nonces being ordered by time.
+ (*nonce)[0] = static_cast<char>(gmt_unix_time >> 24);
+ (*nonce)[1] = static_cast<char>(gmt_unix_time >> 16);
+ (*nonce)[2] = static_cast<char>(gmt_unix_time >> 8);
+ (*nonce)[3] = static_cast<char>(gmt_unix_time);
+ size_t bytes_written = 4;
+
+ if (orbit.size() == 8) {
+ memcpy(&(*nonce)[bytes_written], orbit.data(), orbit.size());
+ bytes_written += orbit.size();
+ }
+
+ random_generator->RandBytes(&(*nonce)[bytes_written],
+ kNonceSize - bytes_written);
+}
+
+// static
+bool CryptoUtils::DeriveKeys(QuicStringPiece premaster_secret,
+ QuicTag aead,
+ QuicStringPiece client_nonce,
+ QuicStringPiece server_nonce,
+ QuicStringPiece pre_shared_key,
+ const std::string& hkdf_input,
+ Perspective perspective,
+ Diversification diversification,
+ CrypterPair* crypters,
+ std::string* subkey_secret) {
+ // If the connection is using PSK, concatenate it with the pre-master secret.
+ std::unique_ptr<char[]> psk_premaster_secret;
+ if (!pre_shared_key.empty()) {
+ const QuicStringPiece label(kPreSharedKeyLabel);
+ const size_t psk_premaster_secret_size = label.size() + 1 +
+ pre_shared_key.size() + 8 +
+ premaster_secret.size() + 8;
+
+ psk_premaster_secret = QuicMakeUnique<char[]>(psk_premaster_secret_size);
+ QuicDataWriter writer(psk_premaster_secret_size, psk_premaster_secret.get(),
+ HOST_BYTE_ORDER);
+
+ if (!writer.WriteStringPiece(label) || !writer.WriteUInt8(0) ||
+ !writer.WriteStringPiece(pre_shared_key) ||
+ !writer.WriteUInt64(pre_shared_key.size()) ||
+ !writer.WriteStringPiece(premaster_secret) ||
+ !writer.WriteUInt64(premaster_secret.size()) ||
+ writer.remaining() != 0) {
+ return false;
+ }
+
+ premaster_secret =
+ QuicStringPiece(psk_premaster_secret.get(), psk_premaster_secret_size);
+ }
+
+ crypters->encrypter = QuicEncrypter::Create(aead);
+ crypters->decrypter = QuicDecrypter::Create(aead);
+ size_t key_bytes = crypters->encrypter->GetKeySize();
+ size_t nonce_prefix_bytes = crypters->encrypter->GetNoncePrefixSize();
+ size_t subkey_secret_bytes =
+ subkey_secret == nullptr ? 0 : premaster_secret.length();
+
+ QuicStringPiece nonce = client_nonce;
+ std::string nonce_storage;
+ if (!server_nonce.empty()) {
+ nonce_storage = std::string(client_nonce) + std::string(server_nonce);
+ nonce = nonce_storage;
+ }
+
+ QuicHKDF hkdf(premaster_secret, nonce, hkdf_input, key_bytes,
+ nonce_prefix_bytes, subkey_secret_bytes);
+
+ // Key derivation depends on the key diversification method being employed.
+ // both the client and the server support never doing key diversification.
+ // The server also supports immediate diversification, and the client
+ // supports pending diversification.
+ switch (diversification.mode()) {
+ case Diversification::NEVER: {
+ if (perspective == Perspective::IS_SERVER) {
+ if (!crypters->encrypter->SetKey(hkdf.server_write_key()) ||
+ !crypters->encrypter->SetNoncePrefix(hkdf.server_write_iv()) ||
+ !crypters->decrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->decrypter->SetNoncePrefix(hkdf.client_write_iv())) {
+ return false;
+ }
+ } else {
+ if (!crypters->encrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->encrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
+ !crypters->decrypter->SetKey(hkdf.server_write_key()) ||
+ !crypters->decrypter->SetNoncePrefix(hkdf.server_write_iv())) {
+ return false;
+ }
+ }
+ break;
+ }
+ case Diversification::PENDING: {
+ if (perspective == Perspective::IS_SERVER) {
+ QUIC_BUG << "Pending diversification is only for clients.";
+ return false;
+ }
+
+ if (!crypters->encrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->encrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
+ !crypters->decrypter->SetPreliminaryKey(hkdf.server_write_key()) ||
+ !crypters->decrypter->SetNoncePrefix(hkdf.server_write_iv())) {
+ return false;
+ }
+ break;
+ }
+ case Diversification::NOW: {
+ if (perspective == Perspective::IS_CLIENT) {
+ QUIC_BUG << "Immediate diversification is only for servers.";
+ return false;
+ }
+
+ std::string key, nonce_prefix;
+ QuicDecrypter::DiversifyPreliminaryKey(
+ hkdf.server_write_key(), hkdf.server_write_iv(),
+ *diversification.nonce(), key_bytes, nonce_prefix_bytes, &key,
+ &nonce_prefix);
+ if (!crypters->decrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->decrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
+ !crypters->encrypter->SetKey(key) ||
+ !crypters->encrypter->SetNoncePrefix(nonce_prefix)) {
+ return false;
+ }
+ break;
+ }
+ default:
+ DCHECK(false);
+ }
+
+ if (subkey_secret != nullptr) {
+ *subkey_secret = std::string(hkdf.subkey_secret());
+ }
+
+ return true;
+}
+
+// static
+bool CryptoUtils::ExportKeyingMaterial(QuicStringPiece subkey_secret,
+ QuicStringPiece label,
+ QuicStringPiece context,
+ size_t result_len,
+ std::string* result) {
+ for (size_t i = 0; i < label.length(); i++) {
+ if (label[i] == '\0') {
+ QUIC_LOG(ERROR) << "ExportKeyingMaterial label may not contain NULs";
+ return false;
+ }
+ }
+ // Create HKDF info input: null-terminated label + length-prefixed context
+ if (context.length() >= std::numeric_limits<uint32_t>::max()) {
+ QUIC_LOG(ERROR) << "Context value longer than 2^32";
+ return false;
+ }
+ uint32_t context_length = static_cast<uint32_t>(context.length());
+ std::string info = std::string(label);
+ info.push_back('\0');
+ info.append(reinterpret_cast<char*>(&context_length), sizeof(context_length));
+ info.append(context.data(), context.length());
+
+ QuicHKDF hkdf(subkey_secret, QuicStringPiece() /* no salt */, info,
+ result_len, 0 /* no fixed IV */, 0 /* no subkey secret */);
+ *result = std::string(hkdf.client_write_key());
+ return true;
+}
+
+// static
+uint64_t CryptoUtils::ComputeLeafCertHash(QuicStringPiece cert) {
+ return QuicUtils::FNV1a_64_Hash(cert);
+}
+
+QuicErrorCode CryptoUtils::ValidateServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ const ParsedQuicVersionVector& negotiated_versions,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ if (server_hello.tag() != kSHLO) {
+ *error_details = "Bad tag";
+ return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ }
+
+ QuicVersionLabelVector supported_version_labels;
+ if (server_hello.GetVersionLabelList(kVER, &supported_version_labels) !=
+ QUIC_NO_ERROR) {
+ *error_details = "server hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ return ValidateServerHelloVersions(supported_version_labels,
+ negotiated_versions, error_details);
+}
+
+QuicErrorCode CryptoUtils::ValidateServerHelloVersions(
+ const QuicVersionLabelVector& server_versions,
+ const ParsedQuicVersionVector& negotiated_versions,
+ std::string* error_details) {
+ if (!negotiated_versions.empty()) {
+ bool mismatch = server_versions.size() != negotiated_versions.size();
+ for (size_t i = 0; i < server_versions.size() && !mismatch; ++i) {
+ mismatch =
+ server_versions[i] != CreateQuicVersionLabel(negotiated_versions[i]);
+ }
+ // The server sent a list of supported versions, and the connection
+ // reports that there was a version negotiation during the handshake.
+ // Ensure that these two lists are identical.
+ if (mismatch) {
+ *error_details = QuicStrCat(
+ "Downgrade attack detected: ServerVersions(", server_versions.size(),
+ ")[", QuicVersionLabelVectorToString(server_versions, ",", 30),
+ "] NegotiatedVersions(", negotiated_versions.size(), ")[",
+ ParsedQuicVersionVectorToString(negotiated_versions, ",", 30), "]");
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
+ }
+ }
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode CryptoUtils::ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions,
+ std::string* error_details) {
+ if (client_hello.tag() != kCHLO) {
+ *error_details = "Bad tag";
+ return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ }
+
+ // If the client's preferred version is not the version we are currently
+ // speaking, then the client went through a version negotiation. In this
+ // case, we need to make sure that we actually do not support this version
+ // and that it wasn't a downgrade attack.
+ QuicVersionLabel client_version_label;
+ if (client_hello.GetVersionLabel(kVER, &client_version_label) !=
+ QUIC_NO_ERROR) {
+ *error_details = "client hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ return ValidateClientHelloVersion(client_version_label, version,
+ supported_versions, error_details);
+}
+
+QuicErrorCode CryptoUtils::ValidateClientHelloVersion(
+ QuicVersionLabel client_version,
+ ParsedQuicVersion connection_version,
+ const ParsedQuicVersionVector& supported_versions,
+ std::string* error_details) {
+ if (client_version != CreateQuicVersionLabel(connection_version)) {
+ // Check to see if |client_version| is actually on the supported versions
+ // list. If not, the server doesn't support that version and it's not a
+ // downgrade attack.
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ if (client_version == CreateQuicVersionLabel(supported_versions[i])) {
+ *error_details = QuicStrCat(
+ "Downgrade attack detected: ClientVersion[",
+ QuicVersionLabelToString(client_version), "] SupportedVersions(",
+ supported_versions.size(), ")[",
+ ParsedQuicVersionVectorToString(supported_versions, ",", 30), "]");
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
+ }
+ }
+ }
+ return QUIC_NO_ERROR;
+}
+
+#define RETURN_STRING_LITERAL(x) \
+ case x: \
+ return #x
+
+// Returns the name of the HandshakeFailureReason as a char*
+// static
+const char* CryptoUtils::HandshakeFailureReasonToString(
+ HandshakeFailureReason reason) {
+ switch (reason) {
+ RETURN_STRING_LITERAL(HANDSHAKE_OK);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_UNKNOWN_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_NOT_UNIQUE_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_ORBIT_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_INVALID_TIME_FAILURE);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT);
+ RETURN_STRING_LITERAL(CLIENT_NONCE_STRIKE_REGISTER_FAILURE);
+
+ RETURN_STRING_LITERAL(SERVER_NONCE_DECRYPTION_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_NONCE_INVALID_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_NONCE_NOT_UNIQUE_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_NONCE_INVALID_TIME_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_NONCE_REQUIRED_FAILURE);
+
+ RETURN_STRING_LITERAL(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
+ RETURN_STRING_LITERAL(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
+
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_INVALID_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_PARSE_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE);
+ RETURN_STRING_LITERAL(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE);
+
+ RETURN_STRING_LITERAL(INVALID_EXPECTED_LEAF_CERTIFICATE);
+ RETURN_STRING_LITERAL(MAX_FAILURE_REASON);
+ }
+ // Return a default value so that we return this when |reason| doesn't match
+ // any HandshakeFailureReason.. This can happen when the message by the peer
+ // (attacker) has invalid reason.
+ return "INVALID_HANDSHAKE_FAILURE_REASON";
+}
+
+// static
+std::string CryptoUtils::HashHandshakeMessage(
+ const CryptoHandshakeMessage& message,
+ Perspective perspective) {
+ std::string output;
+ const QuicData& serialized = message.GetSerialized();
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(serialized.data()),
+ serialized.length(), digest);
+ output.assign(reinterpret_cast<const char*>(digest), sizeof(digest));
+ return output;
+}
+
+#undef RETURN_STRING_LITERAL // undef for jumbo builds
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h
new file mode 100644
index 00000000000..b88177883fd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils.h
@@ -0,0 +1,228 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Some helpers for quic crypto
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicRandom;
+
+class QUIC_EXPORT_PRIVATE CryptoUtils {
+ public:
+ CryptoUtils() = delete;
+
+ // Diversification is a utility class that's used to act like a union type.
+ // Values can be created by calling the functions like |NoDiversification|,
+ // below.
+ class Diversification {
+ public:
+ enum Mode {
+ NEVER, // Key diversification will never be used. Forward secure
+ // crypters will always use this mode.
+
+ PENDING, // Key diversification will happen when a nonce is later
+ // received. This should only be used by clients initial
+ // decrypters which are waiting on the divesification nonce
+ // from the server.
+
+ NOW, // Key diversification will happen immediate based on the nonce.
+ // This should only be used by servers initial encrypters.
+ };
+
+ Diversification(const Diversification& diversification) = default;
+
+ static Diversification Never() { return Diversification(NEVER, nullptr); }
+ static Diversification Pending() {
+ return Diversification(PENDING, nullptr);
+ }
+ static Diversification Now(DiversificationNonce* nonce) {
+ return Diversification(NOW, nonce);
+ }
+
+ Mode mode() const { return mode_; }
+ DiversificationNonce* nonce() const {
+ DCHECK_EQ(mode_, NOW);
+ return nonce_;
+ }
+
+ private:
+ Diversification(Mode mode, DiversificationNonce* nonce)
+ : mode_(mode), nonce_(nonce) {}
+
+ Mode mode_;
+ DiversificationNonce* nonce_;
+ };
+
+ // SetKeyAndIV derives the key and IV from the given packet protection secret
+ // |pp_secret| and sets those fields on the given QuicCrypter |*crypter|.
+ // This follows the derivation described in section 7.3 of RFC 8446, except
+ // with the label prefix in HKDF-Expand-Label changed from "tls13 " to "quic "
+ // as described in draft-ietf-quic-tls-14, section 5.1.
+ static void SetKeyAndIV(const EVP_MD* prf,
+ const std::vector<uint8_t>& pp_secret,
+ QuicCrypter* crypter);
+
+ // QUIC encrypts TLS handshake messages with a version-specific key (to
+ // prevent network observers that are not aware of that QUIC version from
+ // making decisions based on the TLS handshake). This packet protection secret
+ // is derived from the connection ID in the client's Initial packet.
+ //
+ // This function takes that |connection_id| and creates the encrypter and
+ // decrypter (put in |*crypters|) to use for this packet protection, as well
+ // as setting the key and IV on those crypters.
+ static void CreateTlsInitialCrypters(Perspective perspective,
+ QuicTransportVersion version,
+ QuicConnectionId connection_id,
+ CrypterPair* crypters);
+
+ // Generates the connection nonce. The nonce is formed as:
+ // <4 bytes> current time
+ // <8 bytes> |orbit| (or random if |orbit| is empty)
+ // <20 bytes> random
+ static void GenerateNonce(QuicWallTime now,
+ QuicRandom* random_generator,
+ QuicStringPiece orbit,
+ std::string* nonce);
+
+ // DeriveKeys populates |crypters->encrypter|, |crypters->decrypter|, and
+ // |subkey_secret| (optional -- may be null) given the contents of
+ // |premaster_secret|, |client_nonce|, |server_nonce| and |hkdf_input|. |aead|
+ // determines which cipher will be used. |perspective| controls whether the
+ // server's keys are assigned to |encrypter| or |decrypter|. |server_nonce| is
+ // optional and, if non-empty, is mixed into the key derivation.
+ // |subkey_secret| will have the same length as |premaster_secret|.
+ //
+ // If |pre_shared_key| is non-empty, it is incorporated into the key
+ // derivation parameters. If it is empty, the key derivation is unaltered.
+ //
+ // If the mode of |diversification| is NEVER, the the crypters will be
+ // configured to never perform key diversification. If the mode is
+ // NOW (which is only for servers, then the encrypter will be keyed via a
+ // two-step process that uses the nonce from |diversification|.
+ // If the mode is PENDING (which is only for servres), then the
+ // decrypter will only be keyed to a preliminary state: a call to
+ // |SetDiversificationNonce| with a diversification nonce will be needed to
+ // complete keying.
+ static bool DeriveKeys(QuicStringPiece premaster_secret,
+ QuicTag aead,
+ QuicStringPiece client_nonce,
+ QuicStringPiece server_nonce,
+ QuicStringPiece pre_shared_key,
+ const std::string& hkdf_input,
+ Perspective perspective,
+ Diversification diversification,
+ CrypterPair* crypters,
+ std::string* subkey_secret);
+
+ // Performs key extraction to derive a new secret of |result_len| bytes
+ // dependent on |subkey_secret|, |label|, and |context|. Returns false if the
+ // parameters are invalid (e.g. |label| contains null bytes); returns true on
+ // success.
+ static bool ExportKeyingMaterial(QuicStringPiece subkey_secret,
+ QuicStringPiece label,
+ QuicStringPiece context,
+ size_t result_len,
+ std::string* result);
+
+ // Computes the FNV-1a hash of the provided DER-encoded cert for use in the
+ // XLCT tag.
+ static uint64_t ComputeLeafCertHash(QuicStringPiece cert);
+
+ // Validates that |server_hello| is actually an SHLO message and that it is
+ // not part of a downgrade attack.
+ //
+ // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error
+ // code and sets |error_details|.
+ static QuicErrorCode ValidateServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ const ParsedQuicVersionVector& negotiated_versions,
+ std::string* error_details);
+
+ // Validates that the |server_versions| received do not indicate that the
+ // ServerHello is part of a downgrade attack. |negotiated_versions| must
+ // contain the list of versions received in the server's version negotiation
+ // packet (or be empty if no such packet was received).
+ //
+ // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error
+ // code and sets |error_details|.
+ static QuicErrorCode ValidateServerHelloVersions(
+ const QuicVersionLabelVector& server_versions,
+ const ParsedQuicVersionVector& negotiated_versions,
+ std::string* error_details);
+
+ // Validates that |client_hello| is actually a CHLO and that this is not part
+ // of a downgrade attack.
+ // This includes verifiying versions and detecting downgrade attacks.
+ //
+ // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error
+ // code and sets |error_details|.
+ static QuicErrorCode ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions,
+ std::string* error_details);
+
+ // Validates that the |client_version| received does not indicate that a
+ // downgrade attack has occurred. |connection_version| is the version of the
+ // QuicConnection, and |supported_versions| is all versions that that
+ // QuicConnection supports.
+ //
+ // Returns QUIC_NO_ERROR if this is the case or returns the appropriate error
+ // code and sets |error_details|.
+ static QuicErrorCode ValidateClientHelloVersion(
+ QuicVersionLabel client_version,
+ ParsedQuicVersion connection_version,
+ const ParsedQuicVersionVector& supported_versions,
+ std::string* error_details);
+
+ // Returns the name of the HandshakeFailureReason as a char*
+ static const char* HandshakeFailureReasonToString(
+ HandshakeFailureReason reason);
+
+ // Returns a hash of the serialized |message|.
+ static std::string HashHandshakeMessage(const CryptoHandshakeMessage& message,
+ Perspective perspective);
+
+ private:
+ // Implements the HKDF-Expand-Label function as defined in section 7.1 of RFC
+ // 8446, except that it uses "quic " as the prefix instead of "tls13 ", as
+ // specified by draft-ietf-quic-tls-14. The HKDF-Expand-Label function takes 4
+ // explicit arguments (Secret, Label, Context, and Length), as well as
+ // implicit PRF which is the hash function negotiated by TLS. Its use in QUIC
+ // (as needed by the QUIC stack, instead of as used internally by the TLS
+ // stack) is only for deriving initial secrets for obfuscation and for
+ // calculating packet protection keys and IVs from the corresponding packet
+ // protection secret. Neither of these uses need a Context (a zero-length
+ // context is provided), so this argument is omitted here.
+ //
+ // The implicit PRF is explicitly passed into HkdfExpandLabel as |prf|; the
+ // Secret, Label, and Length are passed in as |secret|, |label|, and
+ // |out_len|, respectively. The resulting expanded secret is returned.
+ static std::vector<uint8_t> HkdfExpandLabel(
+ const EVP_MD* prf,
+ const std::vector<uint8_t>& secret,
+ const std::string& label,
+ size_t out_len);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CRYPTO_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc
new file mode 100644
index 00000000000..94b50988f5a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/crypto_utils_test.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class CryptoUtilsTest : public QuicTest {};
+
+TEST_F(CryptoUtilsTest, TestExportKeyingMaterial) {
+ const struct TestVector {
+ // Input (strings of hexadecimal digits):
+ const char* subkey_secret;
+ const char* label;
+ const char* context;
+ size_t result_len;
+
+ // Expected output (string of hexadecimal digits):
+ const char* expected; // Null if it should fail.
+ } test_vector[] = {
+ // Try a typical input
+ {"4823c1189ecc40fce888fbb4cf9ae6254f19ba12e6d9af54788f195a6f509ca3",
+ "e934f78d7a71dd85420fceeb8cea0317",
+ "b8d766b5d3c8aba0009c7ed3de553eba53b4de1030ea91383dcdf724cd8b7217", 32,
+ "a9979da0d5f1c1387d7cbe68f5c4163ddb445a03c4ad6ee72cb49d56726d679e"},
+ // Don't let the label contain nulls
+ {"14fe51e082ffee7d1b4d8d4ab41f8c55", "3132333435363700",
+ "58585858585858585858585858585858", 16, nullptr},
+ // Make sure nulls in the context are fine
+ {"d862c2e36b0a42f7827c67ebc8d44df7", "7a5b95e4e8378123",
+ "4142434445464700", 16, "12d418c6d0738a2e4d85b2d0170f76e1"},
+ // ... and give a different result than without
+ {"d862c2e36b0a42f7827c67ebc8d44df7", "7a5b95e4e8378123", "41424344454647",
+ 16, "abfa1c479a6e3ffb98a11dee7d196408"},
+ // Try weird lengths
+ {"d0ec8a34f6cc9a8c96", "49711798cc6251",
+ "933d4a2f30d22f089cfba842791116adc121e0", 23,
+ "c9a46ed0757bd1812f1f21b4d41e62125fec8364a21db7"},
+ };
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(test_vector); i++) {
+ // Decode the test vector.
+ std::string subkey_secret =
+ QuicTextUtils::HexDecode(test_vector[i].subkey_secret);
+ std::string label = QuicTextUtils::HexDecode(test_vector[i].label);
+ std::string context = QuicTextUtils::HexDecode(test_vector[i].context);
+ size_t result_len = test_vector[i].result_len;
+ bool expect_ok = test_vector[i].expected != nullptr;
+ std::string expected;
+ if (expect_ok) {
+ expected = QuicTextUtils::HexDecode(test_vector[i].expected);
+ }
+
+ std::string result;
+ bool ok = CryptoUtils::ExportKeyingMaterial(subkey_secret, label, context,
+ result_len, &result);
+ EXPECT_EQ(expect_ok, ok);
+ if (expect_ok) {
+ EXPECT_EQ(result_len, result.length());
+ test::CompareCharArraysWithHexError("HKDF output", result.data(),
+ result.length(), expected.data(),
+ expected.length());
+ }
+ }
+}
+
+TEST_F(CryptoUtilsTest, HandshakeFailureReasonToString) {
+ EXPECT_STREQ("HANDSHAKE_OK",
+ CryptoUtils::HandshakeFailureReasonToString(HANDSHAKE_OK));
+ EXPECT_STREQ("CLIENT_NONCE_UNKNOWN_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_UNKNOWN_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_INVALID_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_INVALID_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_NOT_UNIQUE_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_NOT_UNIQUE_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_INVALID_ORBIT_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_INVALID_ORBIT_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_INVALID_TIME_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_INVALID_TIME_FAILURE));
+ EXPECT_STREQ("CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT));
+ EXPECT_STREQ("CLIENT_NONCE_STRIKE_REGISTER_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ CLIENT_NONCE_STRIKE_REGISTER_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_DECRYPTION_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_DECRYPTION_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_INVALID_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_INVALID_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_NOT_UNIQUE_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_NOT_UNIQUE_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_INVALID_TIME_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_INVALID_TIME_FAILURE));
+ EXPECT_STREQ("SERVER_NONCE_REQUIRED_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_NONCE_REQUIRED_FAILURE));
+ EXPECT_STREQ("SERVER_CONFIG_INCHOATE_HELLO_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE));
+ EXPECT_STREQ("SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_INVALID_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_INVALID_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_PARSE_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_PARSE_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE));
+ EXPECT_STREQ("SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE));
+ EXPECT_STREQ("INVALID_EXPECTED_LEAF_CERTIFICATE",
+ CryptoUtils::HandshakeFailureReasonToString(
+ INVALID_EXPECTED_LEAF_CERTIFICATE));
+ EXPECT_STREQ("MAX_FAILURE_REASON",
+ CryptoUtils::HandshakeFailureReasonToString(MAX_FAILURE_REASON));
+ EXPECT_STREQ(
+ "INVALID_HANDSHAKE_FAILURE_REASON",
+ CryptoUtils::HandshakeFailureReasonToString(
+ static_cast<HandshakeFailureReason>(MAX_FAILURE_REASON + 1)));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.cc
new file mode 100644
index 00000000000..044693df724
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h"
+
+#include <cstdint>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/curve25519.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+Curve25519KeyExchange::Curve25519KeyExchange() {}
+
+Curve25519KeyExchange::~Curve25519KeyExchange() {}
+
+// static
+std::unique_ptr<Curve25519KeyExchange> Curve25519KeyExchange::New(
+ QuicRandom* rand) {
+ std::unique_ptr<Curve25519KeyExchange> result =
+ New(Curve25519KeyExchange::NewPrivateKey(rand));
+ QUIC_BUG_IF(result == nullptr);
+ return result;
+}
+
+// static
+std::unique_ptr<Curve25519KeyExchange> Curve25519KeyExchange::New(
+ QuicStringPiece private_key) {
+ // We don't want to #include the BoringSSL headers in the public header file,
+ // so we use literals for the sizes of private_key_ and public_key_. Here we
+ // assert that those values are equal to the values from the BoringSSL
+ // header.
+ static_assert(
+ sizeof(Curve25519KeyExchange::private_key_) == X25519_PRIVATE_KEY_LEN,
+ "header out of sync");
+ static_assert(
+ sizeof(Curve25519KeyExchange::public_key_) == X25519_PUBLIC_VALUE_LEN,
+ "header out of sync");
+
+ if (private_key.size() != X25519_PRIVATE_KEY_LEN) {
+ return nullptr;
+ }
+
+ auto ka = QuicWrapUnique(new Curve25519KeyExchange);
+ memcpy(ka->private_key_, private_key.data(), X25519_PRIVATE_KEY_LEN);
+ X25519_public_from_private(ka->public_key_, ka->private_key_);
+ return ka;
+}
+
+// static
+std::string Curve25519KeyExchange::NewPrivateKey(QuicRandom* rand) {
+ uint8_t private_key[X25519_PRIVATE_KEY_LEN];
+ rand->RandBytes(private_key, sizeof(private_key));
+ return std::string(reinterpret_cast<char*>(private_key), sizeof(private_key));
+}
+
+bool Curve25519KeyExchange::CalculateSharedKeySync(
+ QuicStringPiece peer_public_value,
+ std::string* shared_key) const {
+ if (peer_public_value.size() != X25519_PUBLIC_VALUE_LEN) {
+ return false;
+ }
+
+ uint8_t result[X25519_PUBLIC_VALUE_LEN];
+ if (!X25519(result, private_key_,
+ reinterpret_cast<const uint8_t*>(peer_public_value.data()))) {
+ return false;
+ }
+
+ shared_key->assign(reinterpret_cast<char*>(result), sizeof(result));
+ return true;
+}
+
+QuicStringPiece Curve25519KeyExchange::public_value() const {
+ return QuicStringPiece(reinterpret_cast<const char*>(public_key_),
+ sizeof(public_key_));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h b/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h
new file mode 100644
index 00000000000..31c6c0e8349
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
+
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicRandom;
+
+// Curve25519KeyExchange implements a SynchronousKeyExchange using
+// elliptic-curve Diffie-Hellman on curve25519. See http://cr.yp.to/ecdh.html
+class QUIC_EXPORT_PRIVATE Curve25519KeyExchange
+ : public SynchronousKeyExchange {
+ public:
+ ~Curve25519KeyExchange() override;
+
+ // New generates a private key and then creates new key-exchange object.
+ static std::unique_ptr<Curve25519KeyExchange> New(QuicRandom* rand);
+
+ // New creates a new key-exchange object from a private key. If |private_key|
+ // is invalid, nullptr is returned.
+ static std::unique_ptr<Curve25519KeyExchange> New(
+ QuicStringPiece private_key);
+
+ // NewPrivateKey returns a private key, generated from |rand|, suitable for
+ // passing to |New|.
+ static std::string NewPrivateKey(QuicRandom* rand);
+
+ // SynchronousKeyExchange interface.
+ bool CalculateSharedKeySync(QuicStringPiece peer_public_value,
+ std::string* shared_key) const override;
+ QuicStringPiece public_value() const override;
+ QuicTag type() const override { return kC255; }
+
+ private:
+ Curve25519KeyExchange();
+
+ uint8_t private_key_[32];
+ uint8_t public_key_[32];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange_test.cc
new file mode 100644
index 00000000000..93aa298103f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange_test.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class Curve25519KeyExchangeTest : public QuicTest {
+ public:
+ // Holds the result of a key exchange callback.
+ class TestCallbackResult {
+ public:
+ void set_ok(bool ok) { ok_ = ok; }
+ bool ok() { return ok_; }
+
+ private:
+ bool ok_ = false;
+ };
+
+ // Key exchange callback which sets the result into the specified
+ // TestCallbackResult.
+ class TestCallback : public AsynchronousKeyExchange::Callback {
+ public:
+ TestCallback(TestCallbackResult* result) : result_(result) {}
+ virtual ~TestCallback() = default;
+
+ void Run(bool ok) { result_->set_ok(ok); }
+
+ private:
+ TestCallbackResult* result_;
+ };
+};
+
+// SharedKey just tests that the basic key exchange identity holds: that both
+// parties end up with the same key.
+TEST_F(Curve25519KeyExchangeTest, SharedKey) {
+ QuicRandom* const rand = QuicRandom::GetInstance();
+
+ for (int i = 0; i < 5; i++) {
+ const std::string alice_key(Curve25519KeyExchange::NewPrivateKey(rand));
+ const std::string bob_key(Curve25519KeyExchange::NewPrivateKey(rand));
+
+ std::unique_ptr<Curve25519KeyExchange> alice(
+ Curve25519KeyExchange::New(alice_key));
+ std::unique_ptr<Curve25519KeyExchange> bob(
+ Curve25519KeyExchange::New(bob_key));
+
+ const QuicStringPiece alice_public(alice->public_value());
+ const QuicStringPiece bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ ASSERT_TRUE(alice->CalculateSharedKeySync(bob_public, &alice_shared));
+ ASSERT_TRUE(bob->CalculateSharedKeySync(alice_public, &bob_shared));
+ ASSERT_EQ(alice_shared, bob_shared);
+ }
+}
+
+// SharedKeyAsync just tests that the basic asynchronous key exchange identity
+// holds: that both parties end up with the same key.
+TEST_F(Curve25519KeyExchangeTest, SharedKeyAsync) {
+ QuicRandom* const rand = QuicRandom::GetInstance();
+
+ for (int i = 0; i < 5; i++) {
+ const std::string alice_key(Curve25519KeyExchange::NewPrivateKey(rand));
+ const std::string bob_key(Curve25519KeyExchange::NewPrivateKey(rand));
+
+ std::unique_ptr<Curve25519KeyExchange> alice(
+ Curve25519KeyExchange::New(alice_key));
+ std::unique_ptr<Curve25519KeyExchange> bob(
+ Curve25519KeyExchange::New(bob_key));
+
+ const QuicStringPiece alice_public(alice->public_value());
+ const QuicStringPiece bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ TestCallbackResult alice_result;
+ ASSERT_FALSE(alice_result.ok());
+ alice->CalculateSharedKeyAsync(bob_public, &alice_shared,
+ QuicMakeUnique<TestCallback>(&alice_result));
+ ASSERT_TRUE(alice_result.ok());
+ TestCallbackResult bob_result;
+ ASSERT_FALSE(bob_result.ok());
+ bob->CalculateSharedKeyAsync(alice_public, &bob_shared,
+ QuicMakeUnique<TestCallback>(&bob_result));
+ ASSERT_TRUE(bob_result.ok());
+ ASSERT_EQ(alice_shared, bob_shared);
+ ASSERT_NE(0u, alice_shared.length());
+ ASSERT_NE(0u, bob_shared.length());
+ }
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..1969e75a31f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.cc
@@ -0,0 +1,42 @@
+// 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 "net/third_party/quiche/src/quic/core/crypto/key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
+ QuicTag type,
+ QuicStringPiece private_key) {
+ switch (type) {
+ case kC255:
+ return Curve25519KeyExchange::New(private_key);
+ case kP256:
+ return P256KeyExchange::New(private_key);
+ default:
+ QUIC_BUG << "Unknown key exchange method: " << QuicTagToString(type);
+ return nullptr;
+ }
+}
+
+std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
+ QuicTag type,
+ QuicRandom* rand) {
+ switch (type) {
+ case kC255:
+ return Curve25519KeyExchange::New(rand);
+ break;
+ case kP256:
+ return P256KeyExchange::New();
+ break;
+ default:
+ QUIC_BUG << "Unknown key exchange method: " << QuicTagToString(type);
+ return nullptr;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.h b/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.h
new file mode 100644
index 00000000000..c695523ef32
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/key_exchange.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_KEY_EXCHANGE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_KEY_EXCHANGE_H_
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicRandom;
+
+// Interface for a Diffie-Hellman key exchange with an asynchronous interface.
+// This allows for implementations which hold the private key locally, as well
+// as ones which make an RPC to an external key-exchange service.
+class QUIC_EXPORT_PRIVATE AsynchronousKeyExchange {
+ public:
+ virtual ~AsynchronousKeyExchange() = default;
+
+ // Callback base class for receiving the results of an async call to
+ // CalculateSharedKeys.
+ class Callback {
+ public:
+ Callback() = default;
+ virtual ~Callback() = default;
+
+ // Invoked upon completion of CalculateSharedKeysAsync.
+ //
+ // |ok| indicates whether the operation completed successfully. If false,
+ // then the value pointed to by |shared_key| passed in to
+ // CalculateSharedKeyAsync is undefined.
+ virtual void Run(bool ok) = 0;
+
+ private:
+ Callback(const Callback&) = delete;
+ Callback& operator=(const Callback&) = delete;
+ };
+
+ // CalculateSharedKey computes the shared key between a private key which is
+ // conceptually owned by this object (though it may not be physically located
+ // in this process) and a public value from the peer. Callers should expect
+ // that |callback| might be invoked synchronously. Results will be written
+ // into |*shared_key|.
+ virtual void CalculateSharedKeyAsync(
+ QuicStringPiece peer_public_value,
+ std::string* shared_key,
+ std::unique_ptr<Callback> callback) const = 0;
+
+ // Tag indicating the key-exchange algorithm this object will use.
+ virtual QuicTag type() const = 0;
+};
+
+// Interface for a Diffie-Hellman key exchange with both synchronous and
+// asynchronous interfaces. Only implementations which hold the private key
+// locally should implement this interface.
+class QUIC_EXPORT_PRIVATE SynchronousKeyExchange
+ : public AsynchronousKeyExchange {
+ public:
+ virtual ~SynchronousKeyExchange() = default;
+
+ // AyncKeyExchange API. Note that this method is marked 'final.' Subclasses
+ // should implement CalculateSharedKeySync only.
+ void CalculateSharedKeyAsync(QuicStringPiece peer_public_value,
+ std::string* shared_key,
+ std::unique_ptr<Callback> callback) const final {
+ const bool ok = CalculateSharedKeySync(peer_public_value, shared_key);
+ callback->Run(ok);
+ }
+
+ // CalculateSharedKey computes the shared key between a local private key and
+ // a public value from the peer. Results will be written into |*shared_key|.
+ virtual bool CalculateSharedKeySync(QuicStringPiece peer_public_value,
+ std::string* shared_key) const = 0;
+
+ // public_value returns the local public key which can be sent to a peer in
+ // order to complete a key exchange. The returned QuicStringPiece is a
+ // reference to a member of this object and is only valid for as long as it
+ // exists.
+ virtual QuicStringPiece public_value() const = 0;
+};
+
+// Create a SynchronousKeyExchange object which will use a keypair generated
+// from |private_key|, and a key-exchange algorithm specified by |type|, which
+// must be one of {kC255, kC256}. Returns nullptr if |private_key| or |type| is
+// invalid.
+std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
+ QuicTag type,
+ QuicStringPiece private_key);
+
+// Create a SynchronousKeyExchange object which will use a keypair generated
+// from |rand|, and a key-exchange algorithm specified by |type|, which must be
+// one of {kC255, kC256}. Returns nullptr if |type| is invalid.
+std::unique_ptr<SynchronousKeyExchange> CreateLocalSynchronousKeyExchange(
+ QuicTag type,
+ QuicRandom* rand);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_KEY_EXCHANGE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.cc
new file mode 100644
index 00000000000..4934f6286a3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+NullDecrypter::NullDecrypter(Perspective perspective)
+ : perspective_(perspective) {}
+
+bool NullDecrypter::SetKey(QuicStringPiece key) {
+ return key.empty();
+}
+
+bool NullDecrypter::SetNoncePrefix(QuicStringPiece nonce_prefix) {
+ return nonce_prefix.empty();
+}
+
+bool NullDecrypter::SetIV(QuicStringPiece iv) {
+ return iv.empty();
+}
+
+bool NullDecrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+ return key.empty();
+}
+
+bool NullDecrypter::SetPreliminaryKey(QuicStringPiece key) {
+ QUIC_BUG << "Should not be called";
+ return false;
+}
+
+bool NullDecrypter::SetDiversificationNonce(const DiversificationNonce& nonce) {
+ QUIC_BUG << "Should not be called";
+ return true;
+}
+
+bool NullDecrypter::DecryptPacket(uint64_t /*packet_number*/,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) {
+ QuicDataReader reader(ciphertext.data(), ciphertext.length(),
+ HOST_BYTE_ORDER);
+ QuicUint128 hash;
+
+ if (!ReadHash(&reader, &hash)) {
+ return false;
+ }
+
+ QuicStringPiece plaintext = reader.ReadRemainingPayload();
+ if (plaintext.length() > max_output_length) {
+ QUIC_BUG << "Output buffer must be larger than the plaintext.";
+ return false;
+ }
+ if (hash != ComputeHash(associated_data, plaintext)) {
+ return false;
+ }
+ // Copy the plaintext to output.
+ memcpy(output, plaintext.data(), plaintext.length());
+ *output_length = plaintext.length();
+ return true;
+}
+
+std::string NullDecrypter::GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) {
+ return std::string(5, 0);
+}
+
+size_t NullDecrypter::GetKeySize() const {
+ return 0;
+}
+
+size_t NullDecrypter::GetIVSize() const {
+ return 0;
+}
+
+QuicStringPiece NullDecrypter::GetKey() const {
+ return QuicStringPiece();
+}
+
+QuicStringPiece NullDecrypter::GetNoncePrefix() const {
+ return QuicStringPiece();
+}
+
+uint32_t NullDecrypter::cipher_id() const {
+ return 0;
+}
+
+bool NullDecrypter::ReadHash(QuicDataReader* reader, QuicUint128* hash) {
+ uint64_t lo;
+ uint32_t hi;
+ if (!reader->ReadUInt64(&lo) || !reader->ReadUInt32(&hi)) {
+ return false;
+ }
+ *hash = MakeQuicUint128(hi, lo);
+ return true;
+}
+
+QuicUint128 NullDecrypter::ComputeHash(const QuicStringPiece data1,
+ const QuicStringPiece data2) const {
+ QuicUint128 correct_hash;
+ if (perspective_ == Perspective::IS_CLIENT) {
+ // Peer is a server.
+ correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Server");
+ } else {
+ // Peer is a client.
+ correct_hash = QuicUtils::FNV1a_128_Hash_Three(data1, data2, "Client");
+ }
+ QuicUint128 mask = MakeQuicUint128(UINT64_C(0x0), UINT64_C(0xffffffff));
+ mask <<= 96;
+ correct_hash &= ~mask;
+ return correct_hash;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.h
new file mode 100644
index 00000000000..06c361de2c5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_NULL_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_NULL_DECRYPTER_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+class QuicDataReader;
+
+// A NullDecrypter is a QuicDecrypter used before a crypto negotiation
+// has occurred. It does not actually decrypt the payload, but does
+// verify a hash (fnv128) over both the payload and associated data.
+class QUIC_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter {
+ public:
+ explicit NullDecrypter(Perspective perspective);
+ NullDecrypter(const NullDecrypter&) = delete;
+ NullDecrypter& operator=(const NullDecrypter&) = delete;
+ ~NullDecrypter() override {}
+
+ // QuicDecrypter implementation
+ bool SetKey(QuicStringPiece key) override;
+ bool SetNoncePrefix(QuicStringPiece nonce_prefix) override;
+ bool SetIV(QuicStringPiece iv) override;
+ bool SetHeaderProtectionKey(QuicStringPiece key) override;
+ bool SetPreliminaryKey(QuicStringPiece key) override;
+ bool SetDiversificationNonce(const DiversificationNonce& nonce) override;
+ bool DecryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) override;
+ std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) override;
+ size_t GetKeySize() const override;
+ size_t GetIVSize() const override;
+ QuicStringPiece GetKey() const override;
+ QuicStringPiece GetNoncePrefix() const override;
+
+ uint32_t cipher_id() const override;
+
+ private:
+ bool ReadHash(QuicDataReader* reader, QuicUint128* hash);
+ QuicUint128 ComputeHash(QuicStringPiece data1, QuicStringPiece data2) const;
+
+ Perspective perspective_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_NULL_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter_test.cc
new file mode 100644
index 00000000000..09b1aafdcfe
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_decrypter_test.cc
@@ -0,0 +1,136 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+class NullDecrypterTest : public QuicTestWithParam<bool> {};
+
+TEST_F(NullDecrypterTest, DecryptClient) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x97,
+ 0xdc,
+ 0x27,
+ 0x2f,
+ 0x18,
+ 0xa8,
+ 0x56,
+ 0x73,
+ 0xdf,
+ 0x8d,
+ 0x1d,
+ 0xd0,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = QUIC_ARRAYSIZE(expected);
+ NullDecrypter decrypter(Perspective::IS_SERVER);
+ char buffer[256];
+ size_t length = 0;
+ ASSERT_TRUE(decrypter.DecryptPacket(
+ 0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256));
+ EXPECT_LT(0u, length);
+ EXPECT_EQ("goodbye!", QuicStringPiece(buffer, length));
+}
+
+TEST_F(NullDecrypterTest, DecryptServer) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x63,
+ 0x5e,
+ 0x08,
+ 0x03,
+ 0x32,
+ 0x80,
+ 0x8f,
+ 0x73,
+ 0xdf,
+ 0x8d,
+ 0x1d,
+ 0x1a,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = QUIC_ARRAYSIZE(expected);
+ NullDecrypter decrypter(Perspective::IS_CLIENT);
+ char buffer[256];
+ size_t length = 0;
+ ASSERT_TRUE(decrypter.DecryptPacket(
+ 0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256));
+ EXPECT_LT(0u, length);
+ EXPECT_EQ("goodbye!", QuicStringPiece(buffer, length));
+}
+
+TEST_F(NullDecrypterTest, BadHash) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x46,
+ 0x11,
+ 0xea,
+ 0x5f,
+ 0xcf,
+ 0x1d,
+ 0x66,
+ 0x5b,
+ 0xba,
+ 0xf0,
+ 0xbc,
+ 0xfd,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = QUIC_ARRAYSIZE(expected);
+ NullDecrypter decrypter(Perspective::IS_CLIENT);
+ char buffer[256];
+ size_t length = 0;
+ ASSERT_FALSE(decrypter.DecryptPacket(
+ 0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256));
+}
+
+TEST_F(NullDecrypterTest, ShortInput) {
+ unsigned char expected[] = {
+ // fnv hash (truncated)
+ 0x46, 0x11, 0xea, 0x5f, 0xcf, 0x1d, 0x66, 0x5b, 0xba, 0xf0, 0xbc,
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = QUIC_ARRAYSIZE(expected);
+ NullDecrypter decrypter(Perspective::IS_CLIENT);
+ char buffer[256];
+ size_t length = 0;
+ ASSERT_FALSE(decrypter.DecryptPacket(
+ 0, "hello world!", QuicStringPiece(data, len), buffer, &length, 256));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.cc
new file mode 100644
index 00000000000..69db96d313a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+namespace quic {
+
+const size_t kHashSizeShort = 12; // size of uint128 serialized short
+
+NullEncrypter::NullEncrypter(Perspective perspective)
+ : perspective_(perspective) {}
+
+bool NullEncrypter::SetKey(QuicStringPiece key) {
+ return key.empty();
+}
+
+bool NullEncrypter::SetNoncePrefix(QuicStringPiece nonce_prefix) {
+ return nonce_prefix.empty();
+}
+
+bool NullEncrypter::SetIV(QuicStringPiece iv) {
+ return iv.empty();
+}
+
+bool NullEncrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+ return key.empty();
+}
+
+bool NullEncrypter::EncryptPacket(uint64_t /*packet_number*/,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) {
+ const size_t len = plaintext.size() + GetHashLength();
+ if (max_output_length < len) {
+ return false;
+ }
+ QuicUint128 hash;
+ if (perspective_ == Perspective::IS_SERVER) {
+ hash =
+ QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Server");
+ } else {
+ hash =
+ QuicUtils::FNV1a_128_Hash_Three(associated_data, plaintext, "Client");
+ }
+ // TODO(ianswett): memmove required for in place encryption. Placing the
+ // hash at the end would allow use of memcpy, doing nothing for in place.
+ memmove(output + GetHashLength(), plaintext.data(), plaintext.length());
+ QuicUtils::SerializeUint128Short(hash,
+ reinterpret_cast<unsigned char*>(output));
+ *output_length = len;
+ return true;
+}
+
+std::string NullEncrypter::GenerateHeaderProtectionMask(
+ QuicStringPiece sample) {
+ return std::string(5, 0);
+}
+
+size_t NullEncrypter::GetKeySize() const {
+ return 0;
+}
+
+size_t NullEncrypter::GetNoncePrefixSize() const {
+ return 0;
+}
+
+size_t NullEncrypter::GetIVSize() const {
+ return 0;
+}
+
+size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - GetHashLength();
+}
+
+size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + GetHashLength();
+}
+
+QuicStringPiece NullEncrypter::GetKey() const {
+ return QuicStringPiece();
+}
+
+QuicStringPiece NullEncrypter::GetNoncePrefix() const {
+ return QuicStringPiece();
+}
+
+size_t NullEncrypter::GetHashLength() const {
+ return kHashSizeShort;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.h
new file mode 100644
index 00000000000..efd6e239e04
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_NULL_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_NULL_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// A NullEncrypter is a QuicEncrypter used before a crypto negotiation
+// has occurred. It does not actually encrypt the payload, but does
+// generate a MAC (fnv128) over both the payload and associated data.
+class QUIC_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter {
+ public:
+ explicit NullEncrypter(Perspective perspective);
+ NullEncrypter(const NullEncrypter&) = delete;
+ NullEncrypter& operator=(const NullEncrypter&) = delete;
+ ~NullEncrypter() override {}
+
+ // QuicEncrypter implementation
+ bool SetKey(QuicStringPiece key) override;
+ bool SetNoncePrefix(QuicStringPiece nonce_prefix) override;
+ bool SetIV(QuicStringPiece iv) override;
+ bool SetHeaderProtectionKey(QuicStringPiece key) override;
+ bool EncryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) override;
+ std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override;
+ size_t GetKeySize() const override;
+ size_t GetNoncePrefixSize() const override;
+ size_t GetIVSize() const override;
+ size_t GetMaxPlaintextSize(size_t ciphertext_size) const override;
+ size_t GetCiphertextSize(size_t plaintext_size) const override;
+ QuicStringPiece GetKey() const override;
+ QuicStringPiece GetNoncePrefix() const override;
+
+ private:
+ size_t GetHashLength() const;
+
+ Perspective perspective_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_NULL_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter_test.cc
new file mode 100644
index 00000000000..fd95cc6fb76
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/null_encrypter_test.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+class NullEncrypterTest : public QuicTestWithParam<bool> {};
+
+TEST_F(NullEncrypterTest, EncryptClient) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x97,
+ 0xdc,
+ 0x27,
+ 0x2f,
+ 0x18,
+ 0xa8,
+ 0x56,
+ 0x73,
+ 0xdf,
+ 0x8d,
+ 0x1d,
+ 0xd0,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ char encrypted[256];
+ size_t encrypted_len = 0;
+ NullEncrypter encrypter(Perspective::IS_CLIENT);
+ ASSERT_TRUE(encrypter.EncryptPacket(0, "hello world!", "goodbye!", encrypted,
+ &encrypted_len, 256));
+ test::CompareCharArraysWithHexError(
+ "encrypted data", encrypted, encrypted_len,
+ reinterpret_cast<const char*>(expected), QUIC_ARRAYSIZE(expected));
+}
+
+TEST_F(NullEncrypterTest, EncryptServer) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x63,
+ 0x5e,
+ 0x08,
+ 0x03,
+ 0x32,
+ 0x80,
+ 0x8f,
+ 0x73,
+ 0xdf,
+ 0x8d,
+ 0x1d,
+ 0x1a,
+ // payload
+ 'g',
+ 'o',
+ 'o',
+ 'd',
+ 'b',
+ 'y',
+ 'e',
+ '!',
+ };
+ char encrypted[256];
+ size_t encrypted_len = 0;
+ NullEncrypter encrypter(Perspective::IS_SERVER);
+ ASSERT_TRUE(encrypter.EncryptPacket(0, "hello world!", "goodbye!", encrypted,
+ &encrypted_len, 256));
+ test::CompareCharArraysWithHexError(
+ "encrypted data", encrypted, encrypted_len,
+ reinterpret_cast<const char*>(expected), QUIC_ARRAYSIZE(expected));
+}
+
+TEST_F(NullEncrypterTest, GetMaxPlaintextSize) {
+ NullEncrypter encrypter(Perspective::IS_CLIENT);
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST_F(NullEncrypterTest, GetCiphertextSize) {
+ NullEncrypter encrypter(Perspective::IS_CLIENT);
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.cc
new file mode 100644
index 00000000000..1cabc6459ef
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ecdh.h"
+#include "third_party/boringssl/src/include/openssl/err.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+P256KeyExchange::P256KeyExchange(bssl::UniquePtr<EC_KEY> private_key,
+ const uint8_t* public_key)
+ : private_key_(std::move(private_key)) {
+ memcpy(public_key_, public_key, sizeof(public_key_));
+}
+
+P256KeyExchange::~P256KeyExchange() {}
+
+// static
+std::unique_ptr<P256KeyExchange> P256KeyExchange::New() {
+ return New(P256KeyExchange::NewPrivateKey());
+}
+
+// static
+std::unique_ptr<P256KeyExchange> P256KeyExchange::New(QuicStringPiece key) {
+ if (key.empty()) {
+ QUIC_DLOG(INFO) << "Private key is empty";
+ return nullptr;
+ }
+
+ const uint8_t* keyp = reinterpret_cast<const uint8_t*>(key.data());
+ bssl::UniquePtr<EC_KEY> private_key(
+ d2i_ECPrivateKey(nullptr, &keyp, key.size()));
+ if (!private_key.get() || !EC_KEY_check_key(private_key.get())) {
+ QUIC_DLOG(INFO) << "Private key is invalid.";
+ return nullptr;
+ }
+
+ uint8_t public_key[kUncompressedP256PointBytes];
+ if (EC_POINT_point2oct(EC_KEY_get0_group(private_key.get()),
+ EC_KEY_get0_public_key(private_key.get()),
+ POINT_CONVERSION_UNCOMPRESSED, public_key,
+ sizeof(public_key), nullptr) != sizeof(public_key)) {
+ QUIC_DLOG(INFO) << "Can't get public key.";
+ return nullptr;
+ }
+
+ return QuicWrapUnique(
+ new P256KeyExchange(std::move(private_key), public_key));
+}
+
+// static
+std::string P256KeyExchange::NewPrivateKey() {
+ bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+ if (!key.get() || !EC_KEY_generate_key(key.get())) {
+ QUIC_DLOG(INFO) << "Can't generate a new private key.";
+ return std::string();
+ }
+
+ int key_len = i2d_ECPrivateKey(key.get(), nullptr);
+ if (key_len <= 0) {
+ QUIC_DLOG(INFO) << "Can't convert private key to string";
+ return std::string();
+ }
+ std::unique_ptr<uint8_t[]> private_key(new uint8_t[key_len]);
+ uint8_t* keyp = private_key.get();
+ if (!i2d_ECPrivateKey(key.get(), &keyp)) {
+ QUIC_DLOG(INFO) << "Can't convert private key to string.";
+ return std::string();
+ }
+ return std::string(reinterpret_cast<char*>(private_key.get()), key_len);
+}
+
+bool P256KeyExchange::CalculateSharedKeySync(QuicStringPiece peer_public_value,
+ std::string* shared_key) const {
+ if (peer_public_value.size() != kUncompressedP256PointBytes) {
+ QUIC_DLOG(INFO) << "Peer public value is invalid";
+ return false;
+ }
+
+ bssl::UniquePtr<EC_POINT> point(
+ EC_POINT_new(EC_KEY_get0_group(private_key_.get())));
+ if (!point.get() ||
+ !EC_POINT_oct2point(/* also test if point is on curve */
+ EC_KEY_get0_group(private_key_.get()), point.get(),
+ reinterpret_cast<const uint8_t*>(
+ peer_public_value.data()),
+ peer_public_value.size(), nullptr)) {
+ QUIC_DLOG(INFO) << "Can't convert peer public value to curve point.";
+ return false;
+ }
+
+ uint8_t result[kP256FieldBytes];
+ if (ECDH_compute_key(result, sizeof(result), point.get(), private_key_.get(),
+ nullptr) != sizeof(result)) {
+ QUIC_DLOG(INFO) << "Can't compute ECDH shared key.";
+ return false;
+ }
+
+ shared_key->assign(reinterpret_cast<char*>(result), sizeof(result));
+ return true;
+}
+
+QuicStringPiece P256KeyExchange::public_value() const {
+ return QuicStringPiece(reinterpret_cast<const char*>(public_key_),
+ sizeof(public_key_));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h b/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h
new file mode 100644
index 00000000000..a4e5709871c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_P256_KEY_EXCHANGE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_P256_KEY_EXCHANGE_H_
+
+#include <cstdint>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/base.h"
+#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// P256KeyExchange implements a SynchronousKeyExchange using elliptic-curve
+// Diffie-Hellman on NIST P-256.
+class QUIC_EXPORT_PRIVATE P256KeyExchange : public SynchronousKeyExchange {
+ public:
+ ~P256KeyExchange() override;
+
+ // New generates a private key and then creates new key-exchange object.
+ static std::unique_ptr<P256KeyExchange> New();
+
+ // New creates a new key-exchange object from a private key. If |private_key|
+ // is invalid, nullptr is returned.
+ static std::unique_ptr<P256KeyExchange> New(QuicStringPiece private_key);
+
+ // NewPrivateKey returns a private key, suitable for passing to |New|.
+ // If |NewPrivateKey| can't generate a private key, it returns an empty
+ // string.
+ static std::string NewPrivateKey();
+
+ // SynchronousKeyExchange interface.
+ bool CalculateSharedKeySync(QuicStringPiece peer_public_value,
+ std::string* shared_key) const override;
+ QuicStringPiece public_value() const override;
+ QuicTag type() const override { return kP256; }
+
+ private:
+ enum {
+ // A P-256 field element consists of 32 bytes.
+ kP256FieldBytes = 32,
+ // A P-256 point in uncompressed form consists of 0x04 (to denote
+ // that the point is uncompressed) followed by two, 32-byte field
+ // elements.
+ kUncompressedP256PointBytes = 1 + 2 * kP256FieldBytes,
+ // The first byte in an uncompressed P-256 point.
+ kUncompressedECPointForm = 0x04,
+ };
+
+ // P256KeyExchange wraps |private_key|, and expects |public_key| consists of
+ // |kUncompressedP256PointBytes| bytes.
+ P256KeyExchange(bssl::UniquePtr<EC_KEY> private_key,
+ const uint8_t* public_key);
+ P256KeyExchange(const P256KeyExchange&) = delete;
+ P256KeyExchange& operator=(const P256KeyExchange&) = delete;
+
+ bssl::UniquePtr<EC_KEY> private_key_;
+ // The public key stored as an uncompressed P-256 point.
+ uint8_t public_key_[kUncompressedP256PointBytes];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_P256_KEY_EXCHANGE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange_test.cc
new file mode 100644
index 00000000000..34a49931ca7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/p256_key_exchange_test.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class P256KeyExchangeTest : public QuicTest {
+ public:
+ // Holds the result of a key exchange callback.
+ class TestCallbackResult {
+ public:
+ void set_ok(bool ok) { ok_ = ok; }
+ bool ok() { return ok_; }
+
+ private:
+ bool ok_ = false;
+ };
+
+ // Key exchange callback which sets the result into the specified
+ // TestCallbackResult.
+ class TestCallback : public AsynchronousKeyExchange::Callback {
+ public:
+ TestCallback(TestCallbackResult* result) : result_(result) {}
+ virtual ~TestCallback() = default;
+
+ void Run(bool ok) { result_->set_ok(ok); }
+
+ private:
+ TestCallbackResult* result_;
+ };
+};
+
+// SharedKeyAsync just tests that the basic asynchronous key exchange identity
+// holds: that both parties end up with the same key.
+TEST_F(P256KeyExchangeTest, SharedKey) {
+ for (int i = 0; i < 5; i++) {
+ std::string alice_private(P256KeyExchange::NewPrivateKey());
+ std::string bob_private(P256KeyExchange::NewPrivateKey());
+
+ ASSERT_FALSE(alice_private.empty());
+ ASSERT_FALSE(bob_private.empty());
+ ASSERT_NE(alice_private, bob_private);
+
+ std::unique_ptr<P256KeyExchange> alice(P256KeyExchange::New(alice_private));
+ std::unique_ptr<P256KeyExchange> bob(P256KeyExchange::New(bob_private));
+
+ ASSERT_TRUE(alice != nullptr);
+ ASSERT_TRUE(bob != nullptr);
+
+ const QuicStringPiece alice_public(alice->public_value());
+ const QuicStringPiece bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ ASSERT_TRUE(alice->CalculateSharedKeySync(bob_public, &alice_shared));
+ ASSERT_TRUE(bob->CalculateSharedKeySync(alice_public, &bob_shared));
+ ASSERT_EQ(alice_shared, bob_shared);
+ }
+}
+
+// SharedKey just tests that the basic key exchange identity holds: that both
+// parties end up with the same key.
+TEST_F(P256KeyExchangeTest, AsyncSharedKey) {
+ for (int i = 0; i < 5; i++) {
+ std::string alice_private(P256KeyExchange::NewPrivateKey());
+ std::string bob_private(P256KeyExchange::NewPrivateKey());
+
+ ASSERT_FALSE(alice_private.empty());
+ ASSERT_FALSE(bob_private.empty());
+ ASSERT_NE(alice_private, bob_private);
+
+ std::unique_ptr<P256KeyExchange> alice(P256KeyExchange::New(alice_private));
+ std::unique_ptr<P256KeyExchange> bob(P256KeyExchange::New(bob_private));
+
+ ASSERT_TRUE(alice != nullptr);
+ ASSERT_TRUE(bob != nullptr);
+
+ const QuicStringPiece alice_public(alice->public_value());
+ const QuicStringPiece bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ TestCallbackResult alice_result;
+ ASSERT_FALSE(alice_result.ok());
+ alice->CalculateSharedKeyAsync(bob_public, &alice_shared,
+ QuicMakeUnique<TestCallback>(&alice_result));
+ ASSERT_TRUE(alice_result.ok());
+ TestCallbackResult bob_result;
+ ASSERT_FALSE(bob_result.ok());
+ bob->CalculateSharedKeyAsync(alice_public, &bob_shared,
+ QuicMakeUnique<TestCallback>(&bob_result));
+ ASSERT_TRUE(bob_result.ok());
+ ASSERT_EQ(alice_shared, bob_shared);
+ ASSERT_NE(0u, alice_shared.length());
+ ASSERT_NE(0u, bob_shared.length());
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.cc
new file mode 100644
index 00000000000..73f16721821
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+
+namespace quic {
+
+ProofSource::Chain::Chain(const std::vector<std::string>& certs)
+ : certs(certs) {}
+
+ProofSource::Chain::~Chain() {}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..cc777c7b3cd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_source.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// ProofSource is an interface by which a QUIC server can obtain certificate
+// chains and signatures that prove its identity.
+class QUIC_EXPORT_PRIVATE ProofSource {
+ public:
+ // Chain is a reference-counted wrapper for a vector of stringified
+ // certificates.
+ struct QUIC_EXPORT_PRIVATE Chain : public QuicReferenceCounted {
+ explicit Chain(const std::vector<std::string>& certs);
+ Chain(const Chain&) = delete;
+ Chain& operator=(const Chain&) = delete;
+
+ const std::vector<std::string> certs;
+
+ protected:
+ ~Chain() override;
+ };
+
+ // Details is an abstract class which acts as a container for any
+ // implementation-specific details that a ProofSource wants to return.
+ class Details {
+ public:
+ virtual ~Details() {}
+ };
+
+ // Callback base class for receiving the results of an async call to GetProof.
+ class Callback {
+ public:
+ Callback() {}
+ virtual ~Callback() {}
+
+ // Invoked upon completion of GetProof.
+ //
+ // |ok| indicates whether the operation completed successfully. If false,
+ // the values of the remaining three arguments are undefined.
+ //
+ // |chain| is a reference-counted pointer to an object representing the
+ // certificate chain.
+ //
+ // |signature| contains the signature of the server config.
+ //
+ // |leaf_cert_sct| holds the signed timestamp (RFC6962) of the leaf cert.
+ //
+ // |details| holds a pointer to an object representing the statistics, if
+ // any, gathered during the operation of GetProof. If no stats are
+ // available, this will be nullptr.
+ virtual void Run(bool ok,
+ const QuicReferenceCountedPointer<Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<Details> details) = 0;
+
+ private:
+ Callback(const Callback&) = delete;
+ Callback& operator=(const Callback&) = delete;
+ };
+
+ // Base class for signalling the completion of a call to ComputeTlsSignature.
+ class SignatureCallback {
+ public:
+ SignatureCallback() {}
+ virtual ~SignatureCallback() = default;
+
+ // Invoked upon completion of ComputeTlsSignature.
+ //
+ // |ok| indicates whether the operation completed successfully.
+ //
+ // |signature| contains the signature of the data provided to
+ // ComputeTlsSignature. Its value is undefined if |ok| is false.
+ virtual void Run(bool ok, std::string signature) = 0;
+
+ private:
+ SignatureCallback(const SignatureCallback&) = delete;
+ SignatureCallback& operator=(const SignatureCallback&) = delete;
+ };
+
+ virtual ~ProofSource() {}
+
+ // GetProof finds a certificate chain for |hostname| (in leaf-first order),
+ // and calculates a signature of |server_config| using that chain.
+ //
+ // The signature uses SHA-256 as the hash function and PSS padding when the
+ // key is RSA.
+ //
+ // The signature uses SHA-256 as the hash function when the key is ECDSA.
+ // The signature may use an ECDSA key.
+ //
+ // The signature depends on |chlo_hash| which means that the signature can not
+ // be cached.
+ //
+ // |hostname| may be empty to signify that a default certificate should be
+ // used.
+ //
+ // This function may be called concurrently.
+ //
+ // Callers should expect that |callback| might be invoked synchronously.
+ virtual void GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) = 0;
+
+ // Returns the certificate chain for |hostname| in leaf-first order.
+ virtual QuicReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname) = 0;
+
+ // Computes a signature using the private key of the certificate for
+ // |hostname|. The value in |in| is signed using the algorithm specified by
+ // |signature_algorithm|, which is an |SSL_SIGN_*| value (as defined in TLS
+ // 1.3). Implementations can only assume that |in| is valid during the call to
+ // ComputeTlsSignature - an implementation computing signatures asynchronously
+ // must copy it if the value to be signed is used outside of this function.
+ //
+ // Callers should expect that |callback| might be invoked synchronously.
+ virtual void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_PROOF_SOURCE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/proof_verifier.h b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_verifier.h
new file mode 100644
index 00000000000..e6605bb0961
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/proof_verifier.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_PROOF_VERIFIER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_PROOF_VERIFIER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// ProofVerifyDetails is an abstract class that acts as a container for any
+// implementation specific details that a ProofVerifier wishes to return. These
+// details are saved in the CachedState for the origin in question.
+class QUIC_EXPORT_PRIVATE ProofVerifyDetails {
+ public:
+ virtual ~ProofVerifyDetails() {}
+
+ // Returns an new ProofVerifyDetails object with the same contents
+ // as this one.
+ virtual ProofVerifyDetails* Clone() const = 0;
+};
+
+// ProofVerifyContext is an abstract class that acts as a container for any
+// implementation specific context that a ProofVerifier needs.
+class QUIC_EXPORT_PRIVATE ProofVerifyContext {
+ public:
+ virtual ~ProofVerifyContext() {}
+};
+
+// ProofVerifierCallback provides a generic mechanism for a ProofVerifier to
+// call back after an asynchronous verification.
+class QUIC_EXPORT_PRIVATE ProofVerifierCallback {
+ public:
+ virtual ~ProofVerifierCallback() {}
+
+ // Run is called on the original thread to mark the completion of an
+ // asynchonous verification. If |ok| is true then the certificate is valid
+ // and |error_details| is unused. Otherwise, |error_details| contains a
+ // description of the error. |details| contains implementation-specific
+ // details of the verification. |Run| may take ownership of |details| by
+ // calling |release| on it.
+ virtual void Run(bool ok,
+ const std::string& error_details,
+ std::unique_ptr<ProofVerifyDetails>* details) = 0;
+};
+
+// A ProofVerifier checks the signature on a server config, and the certificate
+// chain that backs the public key.
+class QUIC_EXPORT_PRIVATE ProofVerifier {
+ public:
+ virtual ~ProofVerifier() {}
+
+ // VerifyProof checks that |signature| is a valid signature of
+ // |server_config| by the public key in the leaf certificate of |certs|, and
+ // that |certs| is a valid chain for |hostname|. On success, it returns
+ // QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and sets |*error_details|
+ // to a description of the problem. In either case it may set |*details|,
+ // which the caller takes ownership of.
+ //
+ // |context| specifies an implementation specific struct (which may be nullptr
+ // for some implementations) that provides useful information for the
+ // verifier, e.g. logging handles.
+ //
+ // This function may also return QUIC_PENDING, in which case the ProofVerifier
+ // will call back, on the original thread, via |callback| when complete.
+ //
+ // The signature uses SHA-256 as the hash function and PSS padding in the
+ // case of RSA.
+ virtual QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const uint16_t port,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) = 0;
+
+ // VerifyCertChain checks that |certs| is a valid chain for |hostname|. On
+ // success, it returns QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and
+ // sets |*error_details| to a description of the problem. In either case it
+ // may set |*details|, which the caller takes ownership of.
+ //
+ // |context| specifies an implementation specific struct (which may be nullptr
+ // for some implementations) that provides useful information for the
+ // verifier, e.g. logging handles.
+ //
+ // This function may also return QUIC_PENDING, in which case the ProofVerifier
+ // will call back, on the original thread, via |callback| when complete.
+ // In this case, the ProofVerifier will take ownership of |callback|.
+ virtual QuicAsyncStatus VerifyCertChain(
+ const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) = 0;
+
+ // Returns a ProofVerifyContext instance which can be use for subsequent
+ // verifications. Applications may chose create a different context and
+ // supply it for verifications instead.
+ virtual std::unique_ptr<ProofVerifyContext> CreateDefaultContext() = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_PROOF_VERIFIER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.cc
new file mode 100644
index 00000000000..ae2124c5a89
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.cc
@@ -0,0 +1,129 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
+
+namespace quic {
+
+namespace {
+
+// Inline helper function for extending a 64-bit |seed| in-place with a 64-bit
+// |value|. Based on Boost's hash_combine function.
+inline void hash_combine(uint64_t* seed, const uint64_t& val) {
+ (*seed) ^= val + 0x9e3779b9 + ((*seed) << 6) + ((*seed) >> 2);
+}
+
+} // namespace
+
+const size_t QuicCompressedCertsCache::kQuicCompressedCertsCacheSize = 225;
+
+QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts()
+ : chain(nullptr),
+ client_common_set_hashes(nullptr),
+ client_cached_cert_hashes(nullptr) {}
+
+QuicCompressedCertsCache::UncompressedCerts::UncompressedCerts(
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string* client_common_set_hashes,
+ const std::string* client_cached_cert_hashes)
+ : chain(chain),
+ client_common_set_hashes(client_common_set_hashes),
+ client_cached_cert_hashes(client_cached_cert_hashes) {}
+
+QuicCompressedCertsCache::UncompressedCerts::~UncompressedCerts() {}
+
+QuicCompressedCertsCache::CachedCerts::CachedCerts() {}
+
+QuicCompressedCertsCache::CachedCerts::CachedCerts(
+ const UncompressedCerts& uncompressed_certs,
+ const std::string& compressed_cert)
+ : chain_(uncompressed_certs.chain),
+ client_common_set_hashes_(*uncompressed_certs.client_common_set_hashes),
+ client_cached_cert_hashes_(*uncompressed_certs.client_cached_cert_hashes),
+ compressed_cert_(compressed_cert) {}
+
+QuicCompressedCertsCache::CachedCerts::CachedCerts(const CachedCerts& other) =
+ default;
+
+QuicCompressedCertsCache::CachedCerts::~CachedCerts() {}
+
+bool QuicCompressedCertsCache::CachedCerts::MatchesUncompressedCerts(
+ const UncompressedCerts& uncompressed_certs) const {
+ return (client_common_set_hashes_ ==
+ *uncompressed_certs.client_common_set_hashes &&
+ client_cached_cert_hashes_ ==
+ *uncompressed_certs.client_cached_cert_hashes &&
+ chain_ == uncompressed_certs.chain);
+}
+
+const std::string* QuicCompressedCertsCache::CachedCerts::compressed_cert()
+ const {
+ return &compressed_cert_;
+}
+
+QuicCompressedCertsCache::QuicCompressedCertsCache(int64_t max_num_certs)
+ : certs_cache_(max_num_certs) {}
+
+QuicCompressedCertsCache::~QuicCompressedCertsCache() {
+ // Underlying cache must be cleared before destruction.
+ certs_cache_.Clear();
+}
+
+const std::string* QuicCompressedCertsCache::GetCompressedCert(
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes) {
+ UncompressedCerts uncompressed_certs(chain, &client_common_set_hashes,
+ &client_cached_cert_hashes);
+
+ uint64_t key = ComputeUncompressedCertsHash(uncompressed_certs);
+
+ CachedCerts* cached_value = certs_cache_.Lookup(key);
+ if (cached_value != nullptr &&
+ cached_value->MatchesUncompressedCerts(uncompressed_certs)) {
+ return cached_value->compressed_cert();
+ }
+ return nullptr;
+}
+
+void QuicCompressedCertsCache::Insert(
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes,
+ const std::string& compressed_cert) {
+ UncompressedCerts uncompressed_certs(chain, &client_common_set_hashes,
+ &client_cached_cert_hashes);
+
+ uint64_t key = ComputeUncompressedCertsHash(uncompressed_certs);
+
+ // Insert one unit to the cache.
+ std::unique_ptr<CachedCerts> cached_certs(
+ new CachedCerts(uncompressed_certs, compressed_cert));
+ certs_cache_.Insert(key, std::move(cached_certs));
+}
+
+size_t QuicCompressedCertsCache::MaxSize() {
+ return certs_cache_.MaxSize();
+}
+
+size_t QuicCompressedCertsCache::Size() {
+ return certs_cache_.Size();
+}
+
+uint64_t QuicCompressedCertsCache::ComputeUncompressedCertsHash(
+ const UncompressedCerts& uncompressed_certs) {
+ uint64_t hash =
+ std::hash<std::string>()(*uncompressed_certs.client_common_set_hashes);
+ uint64_t h =
+ std::hash<std::string>()(*uncompressed_certs.client_cached_cert_hashes);
+ hash_combine(&hash, h);
+
+ hash_combine(&hash,
+ reinterpret_cast<uint64_t>(uncompressed_certs.chain.get()));
+ return hash;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h
new file mode 100644
index 00000000000..20031874c83
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_
+
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/quic_lru_cache.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// QuicCompressedCertsCache is a cache to track most recently compressed certs.
+class QUIC_EXPORT_PRIVATE QuicCompressedCertsCache {
+ public:
+ explicit QuicCompressedCertsCache(int64_t max_num_certs);
+ ~QuicCompressedCertsCache();
+
+ // Returns the pointer to the cached compressed cert if
+ // |chain, client_common_set_hashes, client_cached_cert_hashes| hits cache.
+ // Otherwise, return nullptr.
+ // Returned pointer might become invalid on the next call to Insert().
+ const std::string* GetCompressedCert(
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes);
+
+ // Inserts the specified
+ // |chain, client_common_set_hashes,
+ // client_cached_cert_hashes, compressed_cert| tuple to the cache.
+ // If the insertion causes the cache to become overfull, entries will
+ // be deleted in an LRU order to make room.
+ void Insert(const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes,
+ const std::string& compressed_cert);
+
+ // Returns max number of cache entries the cache can carry.
+ size_t MaxSize();
+
+ // Returns current number of cache entries in the cache.
+ size_t Size();
+
+ // Default size of the QuicCompressedCertsCache per server side investigation.
+ static const size_t kQuicCompressedCertsCacheSize;
+
+ private:
+ // A wrapper of the tuple:
+ // |chain, client_common_set_hashes, client_cached_cert_hashes|
+ // to identify uncompressed representation of certs.
+ struct UncompressedCerts {
+ UncompressedCerts();
+ UncompressedCerts(
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string* client_common_set_hashes,
+ const std::string* client_cached_cert_hashes);
+ ~UncompressedCerts();
+
+ const QuicReferenceCountedPointer<ProofSource::Chain> chain;
+ const std::string* client_common_set_hashes;
+ const std::string* client_cached_cert_hashes;
+ };
+
+ // Certs stored by QuicCompressedCertsCache where uncompressed certs data is
+ // used to identify the uncompressed representation of certs and
+ // |compressed_cert| is the cached compressed representation.
+ class CachedCerts {
+ public:
+ CachedCerts();
+ CachedCerts(const UncompressedCerts& uncompressed_certs,
+ const std::string& compressed_cert);
+ CachedCerts(const CachedCerts& other);
+ ~CachedCerts();
+
+ // Returns true if the |uncompressed_certs| matches uncompressed
+ // representation of this cert.
+ bool MatchesUncompressedCerts(
+ const UncompressedCerts& uncompressed_certs) const;
+
+ const std::string* compressed_cert() const;
+
+ private:
+ // Uncompressed certs data.
+ QuicReferenceCountedPointer<ProofSource::Chain> chain_;
+ const std::string client_common_set_hashes_;
+ const std::string client_cached_cert_hashes_;
+
+ // Cached compressed representation derived from uncompressed certs.
+ const std::string compressed_cert_;
+ };
+
+ // Computes a uint64_t hash for |uncompressed_certs|.
+ uint64_t ComputeUncompressedCertsHash(
+ const UncompressedCerts& uncompressed_certs);
+
+ // Key is a unit64_t hash for UncompressedCerts. Stored associated value is
+ // CachedCerts which has both original uncompressed certs data and the
+ // compressed representation of the certs.
+ QuicLRUCache<uint64_t, CachedCerts> certs_cache_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_COMPRESSED_CERTS_CACHE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache_test.cc
new file mode 100644
index 00000000000..f27ed0d6dd4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache_test.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+class QuicCompressedCertsCacheTest : public testing::Test {
+ public:
+ QuicCompressedCertsCacheTest()
+ : certs_cache_(QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {}
+
+ protected:
+ QuicCompressedCertsCache certs_cache_;
+};
+
+TEST_F(QuicCompressedCertsCacheTest, CacheHit) {
+ std::vector<std::string> certs = {"leaf cert", "intermediate cert",
+ "root cert"};
+ QuicReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+ std::string common_certs = "common certs";
+ std::string cached_certs = "cached certs";
+ std::string compressed = "compressed cert";
+
+ certs_cache_.Insert(chain, common_certs, cached_certs, compressed);
+
+ const std::string* cached_value =
+ certs_cache_.GetCompressedCert(chain, common_certs, cached_certs);
+ ASSERT_NE(nullptr, cached_value);
+ EXPECT_EQ(*cached_value, compressed);
+}
+
+TEST_F(QuicCompressedCertsCacheTest, CacheMiss) {
+ std::vector<std::string> certs = {"leaf cert", "intermediate cert",
+ "root cert"};
+ QuicReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+
+ std::string common_certs = "common certs";
+ std::string cached_certs = "cached certs";
+ std::string compressed = "compressed cert";
+
+ certs_cache_.Insert(chain, common_certs, cached_certs, compressed);
+
+ EXPECT_EQ(nullptr, certs_cache_.GetCompressedCert(
+ chain, "mismatched common certs", cached_certs));
+ EXPECT_EQ(nullptr, certs_cache_.GetCompressedCert(chain, common_certs,
+ "mismatched cached certs"));
+
+ // A different chain though with equivalent certs should get a cache miss.
+ QuicReferenceCountedPointer<ProofSource::Chain> chain2(
+ new ProofSource::Chain(certs));
+ EXPECT_EQ(nullptr,
+ certs_cache_.GetCompressedCert(chain2, common_certs, cached_certs));
+}
+
+TEST_F(QuicCompressedCertsCacheTest, CacheMissDueToEviction) {
+ // Test cache returns a miss when a queried uncompressed certs was cached but
+ // then evicted.
+ std::vector<std::string> certs = {"leaf cert", "intermediate cert",
+ "root cert"};
+ QuicReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+
+ std::string common_certs = "common certs";
+ std::string cached_certs = "cached certs";
+ std::string compressed = "compressed cert";
+ certs_cache_.Insert(chain, common_certs, cached_certs, compressed);
+
+ // Insert another kQuicCompressedCertsCacheSize certs to evict the first
+ // cached cert.
+ for (unsigned int i = 0;
+ i < QuicCompressedCertsCache::kQuicCompressedCertsCacheSize; i++) {
+ EXPECT_EQ(certs_cache_.Size(), i + 1);
+ certs_cache_.Insert(chain, QuicTextUtils::Uint64ToString(i), "",
+ QuicTextUtils::Uint64ToString(i));
+ }
+ EXPECT_EQ(certs_cache_.MaxSize(), certs_cache_.Size());
+
+ EXPECT_EQ(nullptr,
+ certs_cache_.GetCompressedCert(chain, common_certs, cached_certs));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypter.h
new file mode 100644
index 00000000000..c698dfb08ac
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypter.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTER_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// QuicCrypter is the parent class for QuicEncrypter and QuicDecrypter.
+// Its purpose is to provide an interface for using methods that are common to
+// both classes when operations are being done that apply to both encrypters and
+// decrypters.
+class QUIC_EXPORT_PRIVATE QuicCrypter {
+ public:
+ virtual ~QuicCrypter() {}
+
+ // Sets the symmetric encryption/decryption key. Returns true on success,
+ // false on failure.
+ //
+ // NOTE: The key is the client_write_key or server_write_key derived from
+ // the master secret.
+ virtual bool SetKey(QuicStringPiece key) = 0;
+
+ // Sets the fixed initial bytes of the nonce. Returns true on success,
+ // false on failure. This method must only be used with Google QUIC crypters.
+ //
+ // NOTE: The nonce prefix is the client_write_iv or server_write_iv
+ // derived from the master secret. A 64-bit packet number will
+ // be appended to form the nonce.
+ //
+ // <------------ 64 bits ----------->
+ // +---------------------+----------------------------------+
+ // | Fixed prefix | packet number |
+ // +---------------------+----------------------------------+
+ // Nonce format
+ //
+ // The security of the nonce format requires that QUIC never reuse a
+ // packet number, even when retransmitting a lost packet.
+ virtual bool SetNoncePrefix(QuicStringPiece nonce_prefix) = 0;
+
+ // Sets |iv| as the initialization vector to use when constructing the nonce.
+ // Returns true on success, false on failure. This method must only be used
+ // with IETF QUIC crypters.
+ //
+ // Google QUIC and IETF QUIC use different nonce constructions. This method
+ // must be used when using IETF QUIC; SetNoncePrefix must be used when using
+ // Google QUIC.
+ //
+ // The nonce is constructed as follows (draft-ietf-quic-tls-14 section 5.2):
+ //
+ // <---------------- max(8, N_MIN) bytes ----------------->
+ // +--------------------------------------------------------+
+ // | packet protection IV |
+ // +--------------------------------------------------------+
+ // XOR
+ // <------------ 64 bits ----------->
+ // +---------------------+----------------------------------+
+ // | zeroes | reconstructed packet number |
+ // +---------------------+----------------------------------+
+ //
+ // The nonce is the packet protection IV (|iv|) XOR'd with the left-padded
+ // reconstructed packet number.
+ //
+ // The security of the nonce format requires that QUIC never reuse a
+ // packet number, even when retransmitting a lost packet.
+ virtual bool SetIV(QuicStringPiece iv) = 0;
+
+ // Sets the key to use for header protection.
+ virtual bool SetHeaderProtectionKey(QuicStringPiece key) = 0;
+
+ // Returns the size in bytes of a key for the algorithm.
+ virtual size_t GetKeySize() const = 0;
+ // Returns the size in bytes of an IV to use with the algorithm.
+ virtual size_t GetIVSize() const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTER_H_
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
new file mode 100644
index 00000000000..39838369e93
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.cc
@@ -0,0 +1,1022 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_client_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+namespace {
+
+// Tracks the reason (the state of the server config) for sending inchoate
+// ClientHello to the server.
+void RecordInchoateClientHelloReason(
+ QuicCryptoClientConfig::CachedState::ServerConfigState state) {
+ QUIC_CLIENT_HISTOGRAM_ENUM(
+ "QuicInchoateClientHelloReason", state,
+ QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT, "");
+}
+
+// Tracks the state of the QUIC server information loaded from the disk cache.
+void RecordDiskCacheServerConfigState(
+ QuicCryptoClientConfig::CachedState::ServerConfigState state) {
+ QUIC_CLIENT_HISTOGRAM_ENUM(
+ "QuicServerInfo.DiskCacheState", state,
+ QuicCryptoClientConfig::CachedState::SERVER_CONFIG_COUNT, "");
+}
+
+} // namespace
+
+QuicCryptoClientConfig::QuicCryptoClientConfig(
+ std::unique_ptr<ProofVerifier> proof_verifier,
+ bssl::UniquePtr<SSL_CTX> ssl_ctx)
+ : proof_verifier_(std::move(proof_verifier)), ssl_ctx_(std::move(ssl_ctx)) {
+ DCHECK(proof_verifier_.get());
+ SetDefaults();
+}
+
+QuicCryptoClientConfig::~QuicCryptoClientConfig() {}
+
+QuicCryptoClientConfig::CachedState::CachedState()
+ : server_config_valid_(false),
+ expiration_time_(QuicWallTime::Zero()),
+ generation_counter_(0) {}
+
+QuicCryptoClientConfig::CachedState::~CachedState() {}
+
+bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const {
+ if (server_config_.empty()) {
+ RecordInchoateClientHelloReason(SERVER_CONFIG_EMPTY);
+ return false;
+ }
+
+ if (!server_config_valid_) {
+ RecordInchoateClientHelloReason(SERVER_CONFIG_INVALID);
+ return false;
+ }
+
+ const CryptoHandshakeMessage* scfg = GetServerConfig();
+ if (!scfg) {
+ // Should be impossible short of cache corruption.
+ RecordInchoateClientHelloReason(SERVER_CONFIG_CORRUPTED);
+ DCHECK(false);
+ return false;
+ }
+
+ if (now.IsBefore(expiration_time_)) {
+ return true;
+ }
+
+ QUIC_CLIENT_HISTOGRAM_TIMES(
+ "QuicClientHelloServerConfig.InvalidDuration",
+ QuicTime::Delta::FromSeconds(now.ToUNIXSeconds() -
+ expiration_time_.ToUNIXSeconds()),
+ QuicTime::Delta::FromSeconds(60), // 1 min.
+ QuicTime::Delta::FromSeconds(20 * 24 * 3600), // 20 days.
+ 50, "");
+ RecordInchoateClientHelloReason(SERVER_CONFIG_EXPIRED);
+ return false;
+}
+
+bool QuicCryptoClientConfig::CachedState::IsEmpty() const {
+ return server_config_.empty();
+}
+
+const CryptoHandshakeMessage*
+QuicCryptoClientConfig::CachedState::GetServerConfig() const {
+ if (server_config_.empty()) {
+ return nullptr;
+ }
+
+ if (!scfg_.get()) {
+ scfg_ = CryptoFramer::ParseMessage(server_config_);
+ DCHECK(scfg_.get());
+ }
+ return scfg_.get();
+}
+
+void QuicCryptoClientConfig::CachedState::add_server_designated_connection_id(
+ QuicConnectionId connection_id) {
+ server_designated_connection_ids_.push(connection_id);
+}
+
+bool QuicCryptoClientConfig::CachedState::has_server_designated_connection_id()
+ const {
+ return !server_designated_connection_ids_.empty();
+}
+
+void QuicCryptoClientConfig::CachedState::add_server_nonce(
+ const std::string& server_nonce) {
+ server_nonces_.push(server_nonce);
+}
+
+bool QuicCryptoClientConfig::CachedState::has_server_nonce() const {
+ return !server_nonces_.empty();
+}
+
+QuicCryptoClientConfig::CachedState::ServerConfigState
+QuicCryptoClientConfig::CachedState::SetServerConfig(
+ QuicStringPiece server_config,
+ QuicWallTime now,
+ QuicWallTime expiry_time,
+ std::string* error_details) {
+ const bool matches_existing = server_config == server_config_;
+
+ // Even if the new server config matches the existing one, we still wish to
+ // reject it if it has expired.
+ std::unique_ptr<CryptoHandshakeMessage> new_scfg_storage;
+ const CryptoHandshakeMessage* new_scfg;
+
+ if (!matches_existing) {
+ new_scfg_storage = CryptoFramer::ParseMessage(server_config);
+ new_scfg = new_scfg_storage.get();
+ } else {
+ new_scfg = GetServerConfig();
+ }
+
+ if (!new_scfg) {
+ *error_details = "SCFG invalid";
+ return SERVER_CONFIG_INVALID;
+ }
+
+ if (expiry_time.IsZero()) {
+ uint64_t expiry_seconds;
+ if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
+ *error_details = "SCFG missing EXPY";
+ return SERVER_CONFIG_INVALID_EXPIRY;
+ }
+ expiration_time_ = QuicWallTime::FromUNIXSeconds(expiry_seconds);
+ } else {
+ expiration_time_ = expiry_time;
+ }
+
+ if (now.IsAfter(expiration_time_)) {
+ *error_details = "SCFG has expired";
+ return SERVER_CONFIG_EXPIRED;
+ }
+
+ if (!matches_existing) {
+ server_config_ = std::string(server_config);
+ SetProofInvalid();
+ scfg_ = std::move(new_scfg_storage);
+ }
+ return SERVER_CONFIG_VALID;
+}
+
+void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() {
+ server_config_.clear();
+ scfg_.reset();
+ SetProofInvalid();
+ QuicQueue<QuicConnectionId> empty_queue;
+ using std::swap;
+ swap(server_designated_connection_ids_, empty_queue);
+}
+
+void QuicCryptoClientConfig::CachedState::SetProof(
+ const std::vector<std::string>& certs,
+ QuicStringPiece cert_sct,
+ QuicStringPiece chlo_hash,
+ QuicStringPiece signature) {
+ bool has_changed = signature != server_config_sig_ ||
+ chlo_hash != chlo_hash_ || certs_.size() != certs.size();
+
+ if (!has_changed) {
+ for (size_t i = 0; i < certs_.size(); i++) {
+ if (certs_[i] != certs[i]) {
+ has_changed = true;
+ break;
+ }
+ }
+ }
+
+ if (!has_changed) {
+ return;
+ }
+
+ // If the proof has changed then it needs to be revalidated.
+ SetProofInvalid();
+ certs_ = certs;
+ cert_sct_ = std::string(cert_sct);
+ chlo_hash_ = std::string(chlo_hash);
+ server_config_sig_ = std::string(signature);
+}
+
+void QuicCryptoClientConfig::CachedState::Clear() {
+ server_config_.clear();
+ source_address_token_.clear();
+ certs_.clear();
+ cert_sct_.clear();
+ chlo_hash_.clear();
+ server_config_sig_.clear();
+ server_config_valid_ = false;
+ proof_verify_details_.reset();
+ scfg_.reset();
+ ++generation_counter_;
+ QuicQueue<QuicConnectionId> empty_queue;
+ using std::swap;
+ swap(server_designated_connection_ids_, empty_queue);
+}
+
+void QuicCryptoClientConfig::CachedState::ClearProof() {
+ SetProofInvalid();
+ certs_.clear();
+ cert_sct_.clear();
+ chlo_hash_.clear();
+ server_config_sig_.clear();
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofValid() {
+ server_config_valid_ = true;
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofInvalid() {
+ server_config_valid_ = false;
+ ++generation_counter_;
+}
+
+bool QuicCryptoClientConfig::CachedState::Initialize(
+ QuicStringPiece server_config,
+ QuicStringPiece source_address_token,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ QuicStringPiece chlo_hash,
+ QuicStringPiece signature,
+ QuicWallTime now,
+ QuicWallTime expiration_time) {
+ DCHECK(server_config_.empty());
+
+ if (server_config.empty()) {
+ RecordDiskCacheServerConfigState(SERVER_CONFIG_EMPTY);
+ return false;
+ }
+
+ std::string error_details;
+ ServerConfigState state =
+ SetServerConfig(server_config, now, expiration_time, &error_details);
+ RecordDiskCacheServerConfigState(state);
+ if (state != SERVER_CONFIG_VALID) {
+ QUIC_DVLOG(1) << "SetServerConfig failed with " << error_details;
+ return false;
+ }
+
+ chlo_hash_.assign(chlo_hash.data(), chlo_hash.size());
+ server_config_sig_.assign(signature.data(), signature.size());
+ source_address_token_.assign(source_address_token.data(),
+ source_address_token.size());
+ certs_ = certs;
+ cert_sct_ = cert_sct;
+ return true;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::server_config() const {
+ return server_config_;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::source_address_token()
+ const {
+ return source_address_token_;
+}
+
+const std::vector<std::string>& QuicCryptoClientConfig::CachedState::certs()
+ const {
+ return certs_;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::cert_sct() const {
+ return cert_sct_;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::chlo_hash() const {
+ return chlo_hash_;
+}
+
+const std::string& QuicCryptoClientConfig::CachedState::signature() const {
+ return server_config_sig_;
+}
+
+bool QuicCryptoClientConfig::CachedState::proof_valid() const {
+ return server_config_valid_;
+}
+
+uint64_t QuicCryptoClientConfig::CachedState::generation_counter() const {
+ return generation_counter_;
+}
+
+const ProofVerifyDetails*
+QuicCryptoClientConfig::CachedState::proof_verify_details() const {
+ return proof_verify_details_.get();
+}
+
+void QuicCryptoClientConfig::CachedState::set_source_address_token(
+ QuicStringPiece token) {
+ source_address_token_ = std::string(token);
+}
+
+void QuicCryptoClientConfig::CachedState::set_cert_sct(
+ QuicStringPiece cert_sct) {
+ cert_sct_ = std::string(cert_sct);
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails(
+ ProofVerifyDetails* details) {
+ proof_verify_details_.reset(details);
+}
+
+void QuicCryptoClientConfig::CachedState::InitializeFrom(
+ const QuicCryptoClientConfig::CachedState& other) {
+ DCHECK(server_config_.empty());
+ DCHECK(!server_config_valid_);
+ server_config_ = other.server_config_;
+ source_address_token_ = other.source_address_token_;
+ certs_ = other.certs_;
+ cert_sct_ = other.cert_sct_;
+ chlo_hash_ = other.chlo_hash_;
+ server_config_sig_ = other.server_config_sig_;
+ server_config_valid_ = other.server_config_valid_;
+ server_designated_connection_ids_ = other.server_designated_connection_ids_;
+ expiration_time_ = other.expiration_time_;
+ if (other.proof_verify_details_ != nullptr) {
+ proof_verify_details_.reset(other.proof_verify_details_->Clone());
+ }
+ ++generation_counter_;
+}
+
+QuicConnectionId
+QuicCryptoClientConfig::CachedState::GetNextServerDesignatedConnectionId() {
+ if (server_designated_connection_ids_.empty()) {
+ QUIC_BUG
+ << "Attempting to consume a connection id that was never designated.";
+ return EmptyQuicConnectionId();
+ }
+ const QuicConnectionId next_id = server_designated_connection_ids_.front();
+ server_designated_connection_ids_.pop();
+ return next_id;
+}
+
+std::string QuicCryptoClientConfig::CachedState::GetNextServerNonce() {
+ if (server_nonces_.empty()) {
+ QUIC_BUG
+ << "Attempting to consume a server nonce that was never designated.";
+ return "";
+ }
+ const std::string server_nonce = server_nonces_.front();
+ server_nonces_.pop();
+ return server_nonce;
+}
+
+void QuicCryptoClientConfig::SetDefaults() {
+ // Key exchange methods.
+ kexs = {kC255, kP256};
+
+ // Authenticated encryption algorithms. Prefer AES-GCM if hardware-supported
+ // fast implementation is available.
+ if (EVP_has_aes_hardware() == 1) {
+ aead = {kAESG, kCC20};
+ } else {
+ aead = {kCC20, kAESG};
+ }
+}
+
+QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate(
+ const QuicServerId& server_id) {
+ auto it = cached_states_.find(server_id);
+ if (it != cached_states_.end()) {
+ return it->second.get();
+ }
+
+ CachedState* cached = new CachedState;
+ cached_states_.insert(std::make_pair(server_id, QuicWrapUnique(cached)));
+ bool cache_populated = PopulateFromCanonicalConfig(server_id, cached);
+ QUIC_CLIENT_HISTOGRAM_BOOL(
+ "QuicCryptoClientConfig.PopulatedFromCanonicalConfig", cache_populated,
+ "");
+ return cached;
+}
+
+void QuicCryptoClientConfig::ClearCachedStates(const ServerIdFilter& filter) {
+ for (auto it = cached_states_.begin(); it != cached_states_.end(); ++it) {
+ if (filter.Matches(it->first))
+ it->second->Clear();
+ }
+}
+
+void QuicCryptoClientConfig::FillInchoateClientHello(
+ const QuicServerId& server_id,
+ const ParsedQuicVersion preferred_version,
+ const CachedState* cached,
+ QuicRandom* rand,
+ bool demand_x509_proof,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ CryptoHandshakeMessage* out) const {
+ out->set_tag(kCHLO);
+ // TODO(rch): Remove this when we remove quic_use_chlo_packet_size flag.
+ if (pad_inchoate_hello_) {
+ out->set_minimum_size(kClientHelloMinimumSize);
+ } else {
+ out->set_minimum_size(1);
+ }
+
+ // Server name indication. We only send SNI if it's a valid domain name, as
+ // per the spec.
+ if (QuicHostnameUtils::IsValidSNI(server_id.host())) {
+ out->SetStringPiece(kSNI, server_id.host());
+ }
+ out->SetVersion(kVER, preferred_version);
+
+ if (!user_agent_id_.empty()) {
+ out->SetStringPiece(kUAID, user_agent_id_);
+ }
+
+ if (!alpn_.empty()) {
+ out->SetStringPiece(kALPN, alpn_);
+ }
+
+ // Even though this is an inchoate CHLO, send the SCID so that
+ // the STK can be validated by the server.
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ if (scfg != nullptr) {
+ QuicStringPiece scid;
+ if (scfg->GetStringPiece(kSCID, &scid)) {
+ out->SetStringPiece(kSCID, scid);
+ }
+ }
+
+ if (!cached->source_address_token().empty()) {
+ out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token());
+ }
+
+ if (!demand_x509_proof) {
+ return;
+ }
+
+ char proof_nonce[32];
+ rand->RandBytes(proof_nonce, QUIC_ARRAYSIZE(proof_nonce));
+ out->SetStringPiece(
+ kNONP, QuicStringPiece(proof_nonce, QUIC_ARRAYSIZE(proof_nonce)));
+
+ out->SetVector(kPDMD, QuicTagVector{kX509});
+
+ if (common_cert_sets) {
+ out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes());
+ }
+
+ out->SetStringPiece(kCertificateSCTTag, "");
+
+ const std::vector<std::string>& certs = cached->certs();
+ // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the
+ // client config is being used for multiple connections, another connection
+ // doesn't update the cached certificates and cause us to be unable to
+ // process the server's compressed certificate chain.
+ out_params->cached_certs = certs;
+ if (!certs.empty()) {
+ std::vector<uint64_t> hashes;
+ hashes.reserve(certs.size());
+ for (auto i = certs.begin(); i != certs.end(); ++i) {
+ hashes.push_back(QuicUtils::FNV1a_64_Hash(*i));
+ }
+ out->SetVector(kCCRT, hashes);
+ }
+}
+
+QuicErrorCode QuicCryptoClientConfig::FillClientHello(
+ const QuicServerId& server_id,
+ QuicConnectionId connection_id,
+ const ParsedQuicVersion preferred_version,
+ const CachedState* cached,
+ QuicWallTime now,
+ QuicRandom* rand,
+ const ChannelIDKey* channel_id_key,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ CryptoHandshakeMessage* out,
+ std::string* error_details) const {
+ DCHECK(error_details != nullptr);
+ QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(
+ connection_id, preferred_version.transport_version))
+ << "FillClientHello: attempted to use connection ID " << connection_id
+ << " which is invalid with version "
+ << QuicVersionToString(preferred_version.transport_version);
+
+ FillInchoateClientHello(server_id, preferred_version, cached, rand,
+ /* demand_x509_proof= */ true, out_params, out);
+
+ if (pad_full_hello_) {
+ out->set_minimum_size(kClientHelloMinimumSize);
+ } else {
+ out->set_minimum_size(1);
+ }
+
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ if (!scfg) {
+ // This should never happen as our caller should have checked
+ // cached->IsComplete() before calling this function.
+ *error_details = "Handshake not ready";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ QuicStringPiece scid;
+ if (!scfg->GetStringPiece(kSCID, &scid)) {
+ *error_details = "SCFG missing SCID";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ out->SetStringPiece(kSCID, scid);
+
+ out->SetStringPiece(kCertificateSCTTag, "");
+
+ QuicTagVector their_aeads;
+ QuicTagVector their_key_exchanges;
+ if (scfg->GetTaglist(kAEAD, &their_aeads) != QUIC_NO_ERROR ||
+ scfg->GetTaglist(kKEXS, &their_key_exchanges) != QUIC_NO_ERROR) {
+ *error_details = "Missing AEAD or KEXS";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ // AEAD: the work loads on the client and server are symmetric. Since the
+ // client is more likely to be CPU-constrained, break the tie by favoring
+ // the client's preference.
+ // Key exchange: the client does more work than the server, so favor the
+ // client's preference.
+ size_t key_exchange_index;
+ if (!FindMutualQuicTag(aead, their_aeads, &out_params->aead, nullptr) ||
+ !FindMutualQuicTag(kexs, their_key_exchanges, &out_params->key_exchange,
+ &key_exchange_index)) {
+ *error_details = "Unsupported AEAD or KEXS";
+ return QUIC_CRYPTO_NO_SUPPORT;
+ }
+ out->SetVector(kAEAD, QuicTagVector{out_params->aead});
+ out->SetVector(kKEXS, QuicTagVector{out_params->key_exchange});
+
+ if (!tb_key_params.empty() && !server_id.privacy_mode_enabled()) {
+ QuicTagVector their_tbkps;
+ switch (scfg->GetTaglist(kTBKP, &their_tbkps)) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ break;
+ case QUIC_NO_ERROR:
+ if (FindMutualQuicTag(tb_key_params, their_tbkps,
+ &out_params->token_binding_key_param, nullptr)) {
+ out->SetVector(kTBKP,
+ QuicTagVector{out_params->token_binding_key_param});
+ }
+ break;
+ default:
+ *error_details = "Invalid TBKP";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ }
+
+ QuicStringPiece public_value;
+ if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) !=
+ QUIC_NO_ERROR) {
+ *error_details = "Missing public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ QuicStringPiece orbit;
+ if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) {
+ *error_details = "SCFG missing OBIT";
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce);
+ out->SetStringPiece(kNONC, out_params->client_nonce);
+ if (!out_params->server_nonce.empty()) {
+ out->SetStringPiece(kServerNonceTag, out_params->server_nonce);
+ }
+
+ switch (out_params->key_exchange) {
+ case kC255:
+ out_params->client_key_exchange = Curve25519KeyExchange::New(
+ Curve25519KeyExchange::NewPrivateKey(rand));
+ break;
+ case kP256:
+ out_params->client_key_exchange =
+ P256KeyExchange::New(P256KeyExchange::NewPrivateKey());
+ break;
+ default:
+ DCHECK(false);
+ *error_details = "Configured to support an unknown key exchange";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ if (!out_params->client_key_exchange->CalculateSharedKeySync(
+ public_value, &out_params->initial_premaster_secret)) {
+ *error_details = "Key exchange failure";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value());
+
+ const std::vector<std::string>& certs = cached->certs();
+ if (certs.empty()) {
+ *error_details = "No certs to calculate XLCT";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+ out->SetValue(kXLCT, CryptoUtils::ComputeLeafCertHash(certs[0]));
+
+ if (channel_id_key) {
+ // In order to calculate the encryption key for the CETV block we need to
+ // serialise the client hello as it currently is (i.e. without the CETV
+ // block). For this, the client hello is serialized without padding.
+ const size_t orig_min_size = out->minimum_size();
+ out->set_minimum_size(0);
+
+ CryptoHandshakeMessage cetv;
+ cetv.set_tag(kCETV);
+
+ std::string hkdf_input;
+ const QuicData& client_hello_serialized = out->GetSerialized();
+ hkdf_input.append(QuicCryptoConfig::kCETVLabel,
+ strlen(QuicCryptoConfig::kCETVLabel) + 1);
+ hkdf_input.append(connection_id.data(), connection_id.length());
+ hkdf_input.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ hkdf_input.append(cached->server_config());
+
+ std::string key = channel_id_key->SerializeKey();
+ std::string signature;
+ if (!channel_id_key->Sign(hkdf_input, &signature)) {
+ *error_details = "Channel ID signature failed";
+ return QUIC_INVALID_CHANNEL_ID_SIGNATURE;
+ }
+
+ cetv.SetStringPiece(kCIDK, key);
+ cetv.SetStringPiece(kCIDS, signature);
+
+ CrypterPair crypters;
+ if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret,
+ out_params->aead, out_params->client_nonce,
+ out_params->server_nonce, pre_shared_key_,
+ hkdf_input, Perspective::IS_CLIENT,
+ CryptoUtils::Diversification::Never(),
+ &crypters, nullptr /* subkey secret */)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ const QuicData& cetv_plaintext = cetv.GetSerialized();
+ const size_t encrypted_len =
+ crypters.encrypter->GetCiphertextSize(cetv_plaintext.length());
+ std::unique_ptr<char[]> output(new char[encrypted_len]);
+ size_t output_size = 0;
+ if (!crypters.encrypter->EncryptPacket(
+ 0 /* packet number */, QuicStringPiece() /* associated data */,
+ cetv_plaintext.AsStringPiece(), output.get(), &output_size,
+ encrypted_len)) {
+ *error_details = "Packet encryption failed";
+ return QUIC_ENCRYPTION_FAILURE;
+ }
+
+ out->SetStringPiece(kCETV, QuicStringPiece(output.get(), output_size));
+ out->MarkDirty();
+
+ out->set_minimum_size(orig_min_size);
+ }
+
+ // Derive the symmetric keys and set up the encrypters and decrypters.
+ // Set the following members of out_params:
+ // out_params->hkdf_input_suffix
+ // out_params->initial_crypters
+ out_params->hkdf_input_suffix.clear();
+ out_params->hkdf_input_suffix.append(connection_id.data(),
+ connection_id.length());
+ const QuicData& client_hello_serialized = out->GetSerialized();
+ out_params->hkdf_input_suffix.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ out_params->hkdf_input_suffix.append(cached->server_config());
+ if (certs.empty()) {
+ *error_details = "No certs found to include in KDF";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+ out_params->hkdf_input_suffix.append(certs[0]);
+
+ std::string hkdf_input;
+ const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1;
+ hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len);
+ hkdf_input.append(out_params->hkdf_input_suffix);
+
+ std::string* subkey_secret = &out_params->initial_subkey_secret;
+
+ if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret,
+ out_params->aead, out_params->client_nonce,
+ out_params->server_nonce, pre_shared_key_,
+ hkdf_input, Perspective::IS_CLIENT,
+ CryptoUtils::Diversification::Pending(),
+ &out_params->initial_crypters, subkey_secret)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::CacheNewServerConfig(
+ const CryptoHandshakeMessage& message,
+ QuicWallTime now,
+ QuicTransportVersion version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& cached_certs,
+ CachedState* cached,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ QuicStringPiece scfg;
+ if (!message.GetStringPiece(kSCFG, &scfg)) {
+ *error_details = "Missing SCFG";
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ QuicWallTime expiration_time = QuicWallTime::Zero();
+ uint64_t expiry_seconds;
+ if (message.GetUint64(kSTTL, &expiry_seconds) == QUIC_NO_ERROR) {
+ // Only cache configs for a maximum of 1 week.
+ expiration_time = now.Add(QuicTime::Delta::FromSeconds(
+ std::min(expiry_seconds, kNumSecondsPerWeek)));
+ }
+
+ CachedState::ServerConfigState state =
+ cached->SetServerConfig(scfg, now, expiration_time, error_details);
+ if (state == CachedState::SERVER_CONFIG_EXPIRED) {
+ return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED;
+ }
+ // TODO(rtenneti): Return more specific error code than returning
+ // QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER.
+ if (state != CachedState::SERVER_CONFIG_VALID) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ QuicStringPiece token;
+ if (message.GetStringPiece(kSourceAddressTokenTag, &token)) {
+ cached->set_source_address_token(token);
+ }
+
+ QuicStringPiece proof, cert_bytes, cert_sct;
+ bool has_proof = message.GetStringPiece(kPROF, &proof);
+ bool has_cert = message.GetStringPiece(kCertificateTag, &cert_bytes);
+ if (has_proof && has_cert) {
+ std::vector<std::string> certs;
+ if (!CertCompressor::DecompressChain(cert_bytes, cached_certs,
+ common_cert_sets, &certs)) {
+ *error_details = "Certificate data invalid";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ message.GetStringPiece(kCertificateSCTTag, &cert_sct);
+ cached->SetProof(certs, cert_sct, chlo_hash, proof);
+ } else {
+ // Secure QUIC: clear existing proof as we have been sent a new SCFG
+ // without matching proof/certs.
+ cached->ClearProof();
+
+ if (has_proof && !has_cert) {
+ *error_details = "Certificate missing";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (!has_proof && has_cert) {
+ *error_details = "Proof missing";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
+ const CryptoHandshakeMessage& rej,
+ QuicWallTime now,
+ const QuicTransportVersion version,
+ QuicStringPiece chlo_hash,
+ CachedState* cached,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ if ((rej.tag() != kREJ) && (rej.tag() != kSREJ)) {
+ *error_details = "Message is not REJ or SREJ";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ QuicErrorCode error =
+ CacheNewServerConfig(rej, now, version, chlo_hash,
+ out_params->cached_certs, cached, error_details);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ QuicStringPiece nonce;
+ if (rej.GetStringPiece(kServerNonceTag, &nonce)) {
+ out_params->server_nonce = std::string(nonce);
+ }
+
+ if (rej.tag() == kSREJ) {
+ QuicConnectionId connection_id;
+
+ QuicStringPiece connection_id_bytes;
+ if (!rej.GetStringPiece(kRCID, &connection_id_bytes)) {
+ *error_details = "Missing kRCID";
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+ connection_id = QuicConnectionId(connection_id_bytes.data(),
+ connection_id_bytes.length());
+ if (!QuicUtils::IsConnectionIdValidForVersion(connection_id, version)) {
+ QUIC_PEER_BUG << "Received server-designated connection ID "
+ << connection_id << " which is invalid with version "
+ << QuicVersionToString(version);
+ *error_details = "Bad kRCID length";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+ cached->add_server_designated_connection_id(connection_id);
+ if (!nonce.empty()) {
+ cached->add_server_nonce(std::string(nonce));
+ }
+ return QUIC_NO_ERROR;
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ QuicConnectionId connection_id,
+ ParsedQuicVersion version,
+ const ParsedQuicVersionVector& negotiated_versions,
+ CachedState* cached,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ QuicErrorCode valid = CryptoUtils::ValidateServerHello(
+ server_hello, negotiated_versions, error_details);
+ if (valid != QUIC_NO_ERROR) {
+ return valid;
+ }
+
+ // Learn about updated source address tokens.
+ QuicStringPiece token;
+ if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) {
+ cached->set_source_address_token(token);
+ }
+
+ QuicStringPiece shlo_nonce;
+ if (!server_hello.GetStringPiece(kServerNonceTag, &shlo_nonce)) {
+ *error_details = "server hello missing server nonce";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ // TODO(agl):
+ // learn about updated SCFGs.
+
+ QuicStringPiece public_value;
+ if (!server_hello.GetStringPiece(kPUBS, &public_value)) {
+ *error_details = "server hello missing forward secure public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (!out_params->client_key_exchange->CalculateSharedKeySync(
+ public_value, &out_params->forward_secure_premaster_secret)) {
+ *error_details = "Key exchange failure";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ std::string hkdf_input;
+ const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1;
+ hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len);
+ hkdf_input.append(out_params->hkdf_input_suffix);
+
+ if (!CryptoUtils::DeriveKeys(
+ out_params->forward_secure_premaster_secret, out_params->aead,
+ out_params->client_nonce,
+ shlo_nonce.empty() ? out_params->server_nonce : shlo_nonce,
+ pre_shared_key_, hkdf_input, Perspective::IS_CLIENT,
+ CryptoUtils::Diversification::Never(),
+ &out_params->forward_secure_crypters, &out_params->subkey_secret)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessServerConfigUpdate(
+ const CryptoHandshakeMessage& server_config_update,
+ QuicWallTime now,
+ const QuicTransportVersion version,
+ QuicStringPiece chlo_hash,
+ CachedState* cached,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ if (server_config_update.tag() != kSCUP) {
+ *error_details = "ServerConfigUpdate must have kSCUP tag.";
+ return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ }
+ return CacheNewServerConfig(server_config_update, now, version, chlo_hash,
+ out_params->cached_certs, cached, error_details);
+}
+
+ProofVerifier* QuicCryptoClientConfig::proof_verifier() const {
+ return proof_verifier_.get();
+}
+
+ChannelIDSource* QuicCryptoClientConfig::channel_id_source() const {
+ return channel_id_source_.get();
+}
+
+SSL_CTX* QuicCryptoClientConfig::ssl_ctx() const {
+ return ssl_ctx_.get();
+}
+
+void QuicCryptoClientConfig::SetChannelIDSource(
+ std::unique_ptr<ChannelIDSource> source) {
+ channel_id_source_ = std::move(source);
+}
+
+void QuicCryptoClientConfig::InitializeFrom(
+ const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
+ QuicCryptoClientConfig* canonical_crypto_config) {
+ CachedState* canonical_cached =
+ canonical_crypto_config->LookupOrCreate(canonical_server_id);
+ if (!canonical_cached->proof_valid()) {
+ return;
+ }
+ CachedState* cached = LookupOrCreate(server_id);
+ cached->InitializeFrom(*canonical_cached);
+}
+
+void QuicCryptoClientConfig::AddCanonicalSuffix(const std::string& suffix) {
+ canonical_suffixes_.push_back(suffix);
+}
+
+bool QuicCryptoClientConfig::PopulateFromCanonicalConfig(
+ const QuicServerId& server_id,
+ CachedState* server_state) {
+ DCHECK(server_state->IsEmpty());
+ size_t i = 0;
+ for (; i < canonical_suffixes_.size(); ++i) {
+ if (QuicTextUtils::EndsWithIgnoreCase(server_id.host(),
+ canonical_suffixes_[i])) {
+ break;
+ }
+ }
+ if (i == canonical_suffixes_.size()) {
+ return false;
+ }
+
+ QuicServerId suffix_server_id(canonical_suffixes_[i], server_id.port(),
+ server_id.privacy_mode_enabled());
+ if (!QuicContainsKey(canonical_server_map_, suffix_server_id)) {
+ // This is the first host we've seen which matches the suffix, so make it
+ // canonical.
+ canonical_server_map_[suffix_server_id] = server_id;
+ return false;
+ }
+
+ const QuicServerId& canonical_server_id =
+ canonical_server_map_[suffix_server_id];
+ 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;
+
+ server_state->InitializeFrom(*canonical_state);
+ return true;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..f4293c0c85e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h
@@ -0,0 +1,430 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
+
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "third_party/boringssl/src/include/openssl/base.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class ChannelIDKey;
+class ChannelIDSource;
+class CryptoHandshakeMessage;
+class ProofVerifier;
+class ProofVerifyDetails;
+class QuicRandom;
+
+// QuicCryptoClientConfig contains crypto-related configuration settings for a
+// client. Note that this object isn't thread-safe. It's designed to be used on
+// a single thread at a time.
+class QUIC_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
+ public:
+ // A CachedState contains the information that the client needs in order to
+ // perform a 0-RTT handshake with a server. This information can be reused
+ // over several connections to the same server.
+ class QUIC_EXPORT_PRIVATE CachedState {
+ public:
+ // Enum to track if the server config is valid or not. If it is not valid,
+ // it specifies why it is invalid.
+ enum ServerConfigState {
+ // WARNING: Do not change the numerical values of any of server config
+ // state. Do not remove deprecated server config states - just comment
+ // them as deprecated.
+ SERVER_CONFIG_EMPTY = 0,
+ SERVER_CONFIG_INVALID = 1,
+ SERVER_CONFIG_CORRUPTED = 2,
+ SERVER_CONFIG_EXPIRED = 3,
+ SERVER_CONFIG_INVALID_EXPIRY = 4,
+ SERVER_CONFIG_VALID = 5,
+ // NOTE: Add new server config states only immediately above this line.
+ // Make sure to update the QuicServerConfigState enum in
+ // tools/metrics/histograms/histograms.xml accordingly.
+ SERVER_CONFIG_COUNT
+ };
+
+ CachedState();
+ CachedState(const CachedState&) = delete;
+ CachedState& operator=(const CachedState&) = delete;
+ ~CachedState();
+
+ // IsComplete returns true if this object contains enough information to
+ // perform a handshake with the server. |now| is used to judge whether any
+ // cached server config has expired.
+ bool IsComplete(QuicWallTime now) const;
+
+ // IsEmpty returns true if |server_config_| is empty.
+ bool IsEmpty() const;
+
+ // GetServerConfig returns the parsed contents of |server_config|, or
+ // nullptr if |server_config| is empty. The return value is owned by this
+ // object and is destroyed when this object is.
+ const CryptoHandshakeMessage* GetServerConfig() const;
+
+ // SetServerConfig checks that |server_config| parses correctly and stores
+ // it in |server_config_|. |now| is used to judge whether |server_config|
+ // has expired.
+ ServerConfigState SetServerConfig(QuicStringPiece server_config,
+ QuicWallTime now,
+ QuicWallTime expiry_time,
+ std::string* error_details);
+
+ // InvalidateServerConfig clears the cached server config (if any).
+ void InvalidateServerConfig();
+
+ // SetProof stores a cert chain, cert signed timestamp and signature.
+ void SetProof(const std::vector<std::string>& certs,
+ QuicStringPiece cert_sct,
+ QuicStringPiece chlo_hash,
+ QuicStringPiece signature);
+
+ // Clears all the data.
+ void Clear();
+
+ // Clears the certificate chain and signature and invalidates the proof.
+ void ClearProof();
+
+ // SetProofValid records that the certificate chain and signature have been
+ // validated and that it's safe to assume that the server is legitimate.
+ // (Note: this does not check the chain or signature.)
+ void SetProofValid();
+
+ // If the server config or the proof has changed then it needs to be
+ // revalidated. Helper function to keep server_config_valid_ and
+ // generation_counter_ in sync.
+ void SetProofInvalid();
+
+ const std::string& server_config() const;
+ const std::string& source_address_token() const;
+ const std::vector<std::string>& certs() const;
+ const std::string& cert_sct() const;
+ const std::string& chlo_hash() const;
+ const std::string& signature() const;
+ bool proof_valid() const;
+ uint64_t generation_counter() const;
+ const ProofVerifyDetails* proof_verify_details() const;
+
+ void set_source_address_token(QuicStringPiece token);
+
+ void set_cert_sct(QuicStringPiece cert_sct);
+
+ // Adds the connection ID to the queue of server-designated connection-ids.
+ void add_server_designated_connection_id(QuicConnectionId connection_id);
+
+ // If true, the crypto config contains at least one connection ID specified
+ // by the server, and the client should use one of these IDs when initiating
+ // the next connection.
+ bool has_server_designated_connection_id() const;
+
+ // This function should only be called when
+ // has_server_designated_connection_id is true. Returns the next
+ // connection_id specified by the server and removes it from the
+ // queue of ids.
+ QuicConnectionId GetNextServerDesignatedConnectionId();
+
+ // Adds the servernonce to the queue of server nonces.
+ void add_server_nonce(const std::string& server_nonce);
+
+ // If true, the crypto config contains at least one server nonce, and the
+ // client should use one of these nonces.
+ bool has_server_nonce() const;
+
+ // This function should only be called when has_server_nonce is true.
+ // Returns the next server_nonce specified by the server and removes it
+ // from the queue of nonces.
+ std::string GetNextServerNonce();
+
+ // SetProofVerifyDetails takes ownership of |details|.
+ void SetProofVerifyDetails(ProofVerifyDetails* details);
+
+ // Copy the |server_config_|, |source_address_token_|, |certs_|,
+ // |expiration_time_|, |cert_sct_|, |chlo_hash_| and |server_config_sig_|
+ // from the |other|. The remaining fields, |generation_counter_|,
+ // |proof_verify_details_|, and |scfg_| remain unchanged.
+ void InitializeFrom(const CachedState& other);
+
+ // Initializes this cached state based on the arguments provided.
+ // Returns false if there is a problem parsing the server config.
+ bool Initialize(QuicStringPiece server_config,
+ QuicStringPiece source_address_token,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ QuicStringPiece chlo_hash,
+ QuicStringPiece signature,
+ QuicWallTime now,
+ QuicWallTime expiration_time);
+
+ private:
+ std::string server_config_; // A serialized handshake message.
+ std::string source_address_token_; // An opaque proof of IP ownership.
+ std::vector<std::string> certs_; // A list of certificates in leaf-first
+ // order.
+ std::string cert_sct_; // Signed timestamp of the leaf cert.
+ std::string chlo_hash_; // Hash of the CHLO message.
+ std::string server_config_sig_; // A signature of |server_config_|.
+ bool server_config_valid_; // True if |server_config_| is correctly
+ // signed and |certs_| has been validated.
+ QuicWallTime expiration_time_; // Time when the config is no longer valid.
+ // Generation counter associated with the |server_config_|, |certs_| and
+ // |server_config_sig_| combination. It is incremented whenever we set
+ // server_config_valid_ to false.
+ uint64_t generation_counter_;
+
+ std::unique_ptr<ProofVerifyDetails> proof_verify_details_;
+
+ // scfg contains the cached, parsed value of |server_config|.
+ mutable std::unique_ptr<CryptoHandshakeMessage> scfg_;
+
+ // TODO(jokulik): Consider using a hash-set as extra book-keeping to ensure
+ // that no connection-id is added twice. Also, consider keeping the server
+ // nonces and connection_ids together in one queue.
+ QuicQueue<QuicConnectionId> server_designated_connection_ids_;
+ QuicQueue<std::string> server_nonces_;
+ };
+
+ // Used to filter server ids for partial config deletion.
+ class ServerIdFilter {
+ public:
+ virtual ~ServerIdFilter() {}
+
+ // Returns true if |server_id| matches the filter.
+ virtual bool Matches(const QuicServerId& server_id) const = 0;
+ };
+
+ QuicCryptoClientConfig(std::unique_ptr<ProofVerifier> proof_verifier,
+ bssl::UniquePtr<SSL_CTX> ssl_ctx);
+ QuicCryptoClientConfig(const QuicCryptoClientConfig&) = delete;
+ QuicCryptoClientConfig& operator=(const QuicCryptoClientConfig&) = delete;
+ ~QuicCryptoClientConfig();
+
+ // LookupOrCreate returns a CachedState for the given |server_id|. If no such
+ // CachedState currently exists, it will be created and cached.
+ CachedState* LookupOrCreate(const QuicServerId& server_id);
+
+ // Delete CachedState objects whose server ids match |filter| from
+ // cached_states.
+ void ClearCachedStates(const ServerIdFilter& filter);
+
+ // FillInchoateClientHello sets |out| to be a CHLO message that elicits a
+ // source-address token or SCFG from a server. If |cached| is non-nullptr, the
+ // source-address token will be taken from it. |out_params| is used in order
+ // to store the cached certs that were sent as hints to the server in
+ // |out_params->cached_certs|. |preferred_version| is the version of the
+ // QUIC protocol that this client chose to use initially. This allows the
+ // server to detect downgrade attacks. If |demand_x509_proof| is true,
+ // then |out| will include an X509 proof demand, and the associated
+ // certificate related fields.
+ void FillInchoateClientHello(
+ const QuicServerId& server_id,
+ const ParsedQuicVersion preferred_version,
+ const CachedState* cached,
+ QuicRandom* rand,
+ bool demand_x509_proof,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ CryptoHandshakeMessage* out) const;
+
+ // FillClientHello sets |out| to be a CHLO message based on the configuration
+ // of this object. This object must have cached enough information about
+ // the server's hostname in order to perform a handshake. This can be checked
+ // with the |IsComplete| member of |CachedState|.
+ //
+ // |now| and |rand| are used to generate the nonce and |out_params| is
+ // filled with the results of the handshake that the server is expected to
+ // accept. |preferred_version| is the version of the QUIC protocol that this
+ // client chose to use initially. This allows the server to detect downgrade
+ // attacks.
+ //
+ // If |channel_id_key| is not null, it is used to sign a secret value derived
+ // from the client and server's keys, and the Channel ID public key and the
+ // signature are placed in the CETV value of the CHLO.
+ QuicErrorCode FillClientHello(
+ const QuicServerId& server_id,
+ QuicConnectionId connection_id,
+ const ParsedQuicVersion preferred_version,
+ const CachedState* cached,
+ QuicWallTime now,
+ QuicRandom* rand,
+ const ChannelIDKey* channel_id_key,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ CryptoHandshakeMessage* out,
+ std::string* error_details) const;
+
+ // ProcessRejection processes a REJ message from a server and updates the
+ // cached information about that server. After this, |IsComplete| may return
+ // true for that server's CachedState. If the rejection message contains state
+ // about a future handshake (i.e. an nonce value from the server), then it
+ // will be saved in |out_params|. |now| is used to judge whether the server
+ // config in the rejection message has expired.
+ QuicErrorCode ProcessRejection(
+ const CryptoHandshakeMessage& rej,
+ QuicWallTime now,
+ QuicTransportVersion version,
+ QuicStringPiece chlo_hash,
+ CachedState* cached,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ std::string* error_details);
+
+ // ProcessServerHello processes the message in |server_hello|, updates the
+ // cached information about that server, writes the negotiated parameters to
+ // |out_params| and returns QUIC_NO_ERROR. If |server_hello| is unacceptable
+ // then it puts an error message in |error_details| and returns an error
+ // code. |version| is the QUIC version for the current connection.
+ // |negotiated_versions| contains the list of version, if any, that were
+ // present in a version negotiation packet previously received from the
+ // server. The contents of this list will be compared against the list of
+ // versions provided in the VER tag of the server hello.
+ QuicErrorCode ProcessServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ QuicConnectionId connection_id,
+ ParsedQuicVersion version,
+ const ParsedQuicVersionVector& negotiated_versions,
+ CachedState* cached,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ std::string* error_details);
+
+ // Processes the message in |server_update|, updating the cached source
+ // address token, and server config.
+ // If |server_update| is invalid then |error_details| will contain an error
+ // message, and an error code will be returned. If all has gone well
+ // QUIC_NO_ERROR is returned.
+ QuicErrorCode ProcessServerConfigUpdate(
+ const CryptoHandshakeMessage& server_update,
+ QuicWallTime now,
+ const QuicTransportVersion version,
+ QuicStringPiece chlo_hash,
+ CachedState* cached,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params,
+ std::string* error_details);
+
+ ProofVerifier* proof_verifier() const;
+
+ ChannelIDSource* channel_id_source() const;
+
+ SSL_CTX* ssl_ctx() const;
+
+ // SetChannelIDSource sets a ChannelIDSource that will be called, when the
+ // server supports channel IDs, to obtain a channel ID for signing a message
+ // proving possession of the channel ID.
+ void SetChannelIDSource(std::unique_ptr<ChannelIDSource> source);
+
+ // Initialize the CachedState from |canonical_crypto_config| for the
+ // |canonical_server_id| as the initial CachedState for |server_id|. We will
+ // copy config data only if |canonical_crypto_config| has valid proof.
+ void InitializeFrom(const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
+ QuicCryptoClientConfig* canonical_crypto_config);
+
+ // Adds |suffix| as a domain suffix for which the server's crypto config
+ // is expected to be shared among servers with the domain suffix. If a server
+ // matches this suffix, then the server config from another server with the
+ // suffix will be used to initialize the cached state for this server.
+ void AddCanonicalSuffix(const std::string& suffix);
+
+ // Saves the |user_agent_id| that will be passed in QUIC's CHLO message.
+ void set_user_agent_id(const std::string& user_agent_id) {
+ user_agent_id_ = user_agent_id;
+ }
+
+ // Returns the user_agent_id that will be provided in the client hello
+ // handshake message.
+ const std::string& user_agent_id() const { return user_agent_id_; }
+
+ // Saves the |alpn| that will be passed in QUIC's CHLO message.
+ void set_alpn(const std::string& alpn) { alpn_ = alpn; }
+
+ void set_pre_shared_key(QuicStringPiece psk) {
+ pre_shared_key_ = std::string(psk);
+ }
+
+ bool pad_inchoate_hello() const { return pad_inchoate_hello_; }
+ void set_pad_inchoate_hello(bool new_value) {
+ pad_inchoate_hello_ = new_value;
+ }
+
+ bool pad_full_hello() const { return pad_full_hello_; }
+ void set_pad_full_hello(bool new_value) { pad_full_hello_ = new_value; }
+
+ private:
+ // Sets the members to reasonable, default values.
+ void SetDefaults();
+
+ // CacheNewServerConfig checks for SCFG, STK, PROF, and CRT tags in |message|,
+ // verifies them, and stores them in the cached state if they validate.
+ // This is used on receipt of a REJ from a server, or when a server sends
+ // updated server config during a connection.
+ QuicErrorCode CacheNewServerConfig(
+ const CryptoHandshakeMessage& message,
+ QuicWallTime now,
+ QuicTransportVersion version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& cached_certs,
+ CachedState* cached,
+ std::string* error_details);
+
+ // If the suffix of the hostname in |server_id| is in |canonical_suffixes_|,
+ // then populate |cached| with the canonical cached state from
+ // |canonical_server_map_| for that suffix. Returns true if |cached| is
+ // initialized with canonical cached state.
+ bool PopulateFromCanonicalConfig(const QuicServerId& server_id,
+ CachedState* cached);
+
+ // cached_states_ maps from the server_id to the cached information about
+ // that server.
+ std::map<QuicServerId, std::unique_ptr<CachedState>> cached_states_;
+
+ // Contains a map of servers which could share the same server config. Map
+ // from a canonical host suffix/port/scheme to a representative server with
+ // the canonical suffix, which has a plausible set of initial certificates
+ // (or at least server public key).
+ std::map<QuicServerId, QuicServerId> canonical_server_map_;
+
+ // Contains list of suffixes (for exmaple ".c.youtube.com",
+ // ".googlevideo.com") of canonical hostnames.
+ std::vector<std::string> canonical_suffixes_;
+
+ std::unique_ptr<ProofVerifier> proof_verifier_;
+ std::unique_ptr<ChannelIDSource> channel_id_source_;
+ bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+
+ // The |user_agent_id_| passed in QUIC's CHLO message.
+ std::string user_agent_id_;
+
+ // The |alpn_| passed in QUIC's CHLO message.
+ std::string alpn_;
+
+ // If non-empty, the client will operate in the pre-shared key mode by
+ // incorporating |pre_shared_key_| into the key schedule.
+ std::string pre_shared_key_;
+
+ // 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
+ // individual messages. It is recommend to leave at least one message in
+ // each direction fully padded (e.g. full CHLO and SHLO), but if you know
+ // the lower-bound MTU, you don't need to pad all of them (keep in mind that
+ // it's not OK to do it according to the standard).
+ //
+ // Also, if you disable padding, you must disable (change) the
+ // anti-amplification protection. You should only do so if you have some
+ // other means of verifying the client.
+ bool pad_inchoate_hello_ = true;
+ bool pad_full_hello_ = true;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
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
new file mode 100644
index 00000000000..2158cd3859e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config_test.cc
@@ -0,0 +1,656 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::StartsWith;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestProofVerifyDetails : public ProofVerifyDetails {
+ ~TestProofVerifyDetails() override {}
+
+ // ProofVerifyDetails implementation
+ ProofVerifyDetails* Clone() const override {
+ return new TestProofVerifyDetails;
+ }
+};
+
+class OneServerIdFilter : public QuicCryptoClientConfig::ServerIdFilter {
+ public:
+ explicit OneServerIdFilter(const QuicServerId* server_id)
+ : server_id_(*server_id) {}
+
+ bool Matches(const QuicServerId& server_id) const override {
+ return server_id == server_id_;
+ }
+
+ private:
+ const QuicServerId server_id_;
+};
+
+class AllServerIdsFilter : public QuicCryptoClientConfig::ServerIdFilter {
+ public:
+ bool Matches(const QuicServerId& server_id) const override { return true; }
+};
+
+} // namespace
+
+class QuicCryptoClientConfigTest : public QuicTest {};
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_IsEmpty) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.IsEmpty());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_IsComplete) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_GenerationCounter) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_EQ(0u, state.generation_counter());
+ state.SetProofInvalid();
+ EXPECT_EQ(1u, state.generation_counter());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.proof_verify_details() == nullptr);
+ ProofVerifyDetails* details = new TestProofVerifyDetails;
+ state.SetProofVerifyDetails(details);
+ EXPECT_EQ(details, state.proof_verify_details());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_ServerDesignatedConnectionId) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.has_server_designated_connection_id());
+
+ uint64_t conn_id = 1234;
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ state.add_server_designated_connection_id(connection_id);
+ EXPECT_TRUE(state.has_server_designated_connection_id());
+ EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId());
+ EXPECT_FALSE(state.has_server_designated_connection_id());
+
+ // Allow the ID to be set multiple times. It's unusual that this would
+ // happen, but not impossible.
+ connection_id = TestConnectionId(++conn_id);
+ state.add_server_designated_connection_id(connection_id);
+ EXPECT_TRUE(state.has_server_designated_connection_id());
+ EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId());
+ connection_id = TestConnectionId(++conn_id);
+ state.add_server_designated_connection_id(connection_id);
+ EXPECT_EQ(connection_id, state.GetNextServerDesignatedConnectionId());
+ EXPECT_FALSE(state.has_server_designated_connection_id());
+
+ // Test FIFO behavior.
+ const QuicConnectionId first_cid = TestConnectionId(0xdeadbeef);
+ const QuicConnectionId second_cid = TestConnectionId(0xfeedbead);
+ state.add_server_designated_connection_id(first_cid);
+ state.add_server_designated_connection_id(second_cid);
+ EXPECT_TRUE(state.has_server_designated_connection_id());
+ EXPECT_EQ(first_cid, state.GetNextServerDesignatedConnectionId());
+ EXPECT_EQ(second_cid, state.GetNextServerDesignatedConnectionId());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_ServerIdConsumedBeforeSet) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.has_server_designated_connection_id());
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+ EXPECT_DEBUG_DEATH(state.GetNextServerDesignatedConnectionId(),
+ "Attempting to consume a connection id "
+ "that was never designated.");
+#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_ServerNonce) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.has_server_nonce());
+
+ std::string server_nonce = "nonce_1";
+ state.add_server_nonce(server_nonce);
+ EXPECT_TRUE(state.has_server_nonce());
+ EXPECT_EQ(server_nonce, state.GetNextServerNonce());
+ EXPECT_FALSE(state.has_server_nonce());
+
+ // Allow the ID to be set multiple times. It's unusual that this would
+ // happen, but not impossible.
+ server_nonce = "nonce_2";
+ state.add_server_nonce(server_nonce);
+ EXPECT_TRUE(state.has_server_nonce());
+ EXPECT_EQ(server_nonce, state.GetNextServerNonce());
+ server_nonce = "nonce_3";
+ state.add_server_nonce(server_nonce);
+ EXPECT_EQ(server_nonce, state.GetNextServerNonce());
+ EXPECT_FALSE(state.has_server_nonce());
+
+ // Test FIFO behavior.
+ const std::string first_nonce = "first_nonce";
+ const std::string second_nonce = "second_nonce";
+ state.add_server_nonce(first_nonce);
+ state.add_server_nonce(second_nonce);
+ EXPECT_TRUE(state.has_server_nonce());
+ EXPECT_EQ(first_nonce, state.GetNextServerNonce());
+ EXPECT_EQ(second_nonce, state.GetNextServerNonce());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_ServerNonceConsumedBeforeSet) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.has_server_nonce());
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+ EXPECT_DEBUG_DEATH(state.GetNextServerNonce(),
+ "Attempting to consume a server nonce "
+ "that was never designated.");
+#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+}
+
+TEST_F(QuicCryptoClientConfigTest, CachedState_InitializeFrom) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig::CachedState other;
+ state.set_source_address_token("TOKEN");
+ // TODO(rch): Populate other fields of |state|.
+ other.InitializeFrom(state);
+ EXPECT_EQ(state.server_config(), other.server_config());
+ EXPECT_EQ(state.source_address_token(), other.source_address_token());
+ EXPECT_EQ(state.certs(), other.certs());
+ EXPECT_EQ(1u, other.generation_counter());
+ EXPECT_FALSE(state.has_server_designated_connection_id());
+ EXPECT_FALSE(state.has_server_nonce());
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChlo) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ config.set_user_agent_id("quic-tester");
+ config.set_alpn("hq");
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ QuicVersionLabel cver;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetVersionLabel(kVER, &cver));
+ EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver);
+ QuicStringPiece proof_nonce;
+ EXPECT_TRUE(msg.GetStringPiece(kNONP, &proof_nonce));
+ EXPECT_EQ(std::string(32, 'r'), proof_nonce);
+ QuicStringPiece user_agent_id;
+ EXPECT_TRUE(msg.GetStringPiece(kUAID, &user_agent_id));
+ EXPECT_EQ("quic-tester", user_agent_id);
+ QuicStringPiece alpn;
+ EXPECT_TRUE(msg.GetStringPiece(kALPN, &alpn));
+ EXPECT_EQ("hq", alpn);
+
+ EXPECT_EQ(msg.minimum_size(), 1024u);
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChloIsNotPadded) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ config.set_pad_inchoate_hello(false);
+ config.set_user_agent_id("quic-tester");
+ config.set_alpn("hq");
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ EXPECT_EQ(msg.minimum_size(), 1u);
+}
+
+// Make sure AES-GCM is the preferred encryption algorithm if it has hardware
+// acceleration.
+TEST_F(QuicCryptoClientConfigTest, PreferAesGcm) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ if (EVP_has_aes_hardware() == 1) {
+ EXPECT_EQ(kAESG, config.aead[0]);
+ } else {
+ EXPECT_EQ(kCC20, config.aead[0]);
+ }
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChloSecure) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ QuicTag pdmd;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd));
+ EXPECT_EQ(kX509, pdmd);
+ QuicStringPiece scid;
+ EXPECT_FALSE(msg.GetStringPiece(kSCID, &scid));
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChloSecureWithSCIDNoEXPY) {
+ // Test that a config with no EXPY is still valid when a non-zero
+ // expiry time is passed in.
+ QuicCryptoClientConfig::CachedState state;
+ CryptoHandshakeMessage scfg;
+ scfg.set_tag(kSCFG);
+ scfg.SetStringPiece(kSCID, "12345678");
+ std::string details;
+ QuicWallTime now = QuicWallTime::FromUNIXSeconds(1);
+ QuicWallTime expiry = QuicWallTime::FromUNIXSeconds(2);
+ state.SetServerConfig(scfg.GetSerialized().AsStringPiece(), now, expiry,
+ &details);
+
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ QuicStringPiece scid;
+ EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid));
+ EXPECT_EQ("12345678", scid);
+}
+
+TEST_F(QuicCryptoClientConfigTest, InchoateChloSecureWithSCID) {
+ QuicCryptoClientConfig::CachedState state;
+ CryptoHandshakeMessage scfg;
+ scfg.set_tag(kSCFG);
+ uint64_t future = 1;
+ scfg.SetValue(kEXPY, future);
+ scfg.SetStringPiece(kSCID, "12345678");
+ std::string details;
+ state.SetServerConfig(scfg.GetSerialized().AsStringPiece(),
+ QuicWallTime::FromUNIXSeconds(1),
+ QuicWallTime::FromUNIXSeconds(0), &details);
+
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, false);
+ MockRandom rand;
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state, &rand,
+ /* demand_x509_proof= */ true, params, &msg);
+
+ QuicStringPiece scid;
+ EXPECT_TRUE(msg.GetStringPiece(kSCID, &scid));
+ EXPECT_EQ("12345678", scid);
+}
+
+TEST_F(QuicCryptoClientConfigTest, FillClientHello) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ QuicConnectionId kConnectionId = TestConnectionId(1234);
+ std::string error_details;
+ MockRandom rand;
+ CryptoHandshakeMessage chlo;
+ QuicServerId server_id("www.google.com", 443, false);
+ config.FillClientHello(server_id, kConnectionId, QuicVersionMax(), &state,
+ QuicWallTime::Zero(), &rand,
+ nullptr, // channel_id_key
+ params, &chlo, &error_details);
+
+ // Verify that the version label has been set correctly in the CHLO.
+ QuicVersionLabel cver;
+ EXPECT_EQ(QUIC_NO_ERROR, chlo.GetVersionLabel(kVER, &cver));
+ EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver);
+}
+
+TEST_F(QuicCryptoClientConfigTest, FillClientHelloNoPadding) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ config.set_pad_full_hello(false);
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params(
+ new QuicCryptoNegotiatedParameters);
+ QuicConnectionId kConnectionId = TestConnectionId(1234);
+ std::string error_details;
+ MockRandom rand;
+ CryptoHandshakeMessage chlo;
+ QuicServerId server_id("www.google.com", 443, false);
+ config.FillClientHello(server_id, kConnectionId, QuicVersionMax(), &state,
+ QuicWallTime::Zero(), &rand,
+ nullptr, // channel_id_key
+ params, &chlo, &error_details);
+
+ // Verify that the version label has been set correctly in the CHLO.
+ QuicVersionLabel cver;
+ EXPECT_EQ(QUIC_NO_ERROR, chlo.GetVersionLabel(kVER, &cver));
+ EXPECT_EQ(CreateQuicVersionLabel(QuicVersionMax()), cver);
+ EXPECT_EQ(chlo.minimum_size(), 1u);
+}
+
+TEST_F(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) {
+ ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ if (supported_versions.size() == 1) {
+ // No downgrade attack is possible if the client only supports one version.
+ return;
+ }
+
+ ParsedQuicVersionVector supported_version_vector;
+ for (size_t i = supported_versions.size(); i > 0; --i) {
+ supported_version_vector.push_back(supported_versions[i - 1]);
+ }
+
+ CryptoHandshakeMessage msg;
+ msg.set_tag(kSHLO);
+ msg.SetVersionVector(kVER, supported_version_vector);
+
+ QuicCryptoClientConfig::CachedState cached;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params(
+ new QuicCryptoNegotiatedParameters);
+ std::string error;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ EXPECT_EQ(QUIC_VERSION_NEGOTIATION_MISMATCH,
+ config.ProcessServerHello(
+ msg, EmptyQuicConnectionId(), supported_versions.front(),
+ supported_versions, &cached, out_params, &error));
+ EXPECT_THAT(error, StartsWith("Downgrade attack detected: ServerVersions"));
+}
+
+TEST_F(QuicCryptoClientConfigTest, InitializeFrom) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ QuicServerId canonical_server_id("www.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_server_id);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicServerId other_server_id("mail.google.com", 443, false);
+ config.InitializeFrom(other_server_id, canonical_server_id, &config);
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(other_server_id);
+
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+}
+
+TEST_F(QuicCryptoClientConfigTest, Canonical) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 443, false);
+ QuicServerId canonical_id2("mail.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(canonical_id2);
+
+ EXPECT_TRUE(state->IsEmpty());
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+
+ QuicServerId different_id("mail.google.org", 443, false);
+ EXPECT_TRUE(config.LookupOrCreate(different_id)->IsEmpty());
+}
+
+TEST_F(QuicCryptoClientConfigTest, CanonicalNotUsedIfNotValid) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 443, false);
+ QuicServerId canonical_id2("mail.google.com", 443, false);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+
+ // Do not set the proof as valid, and check that it is not used
+ // as a canonical entry.
+ EXPECT_TRUE(config.LookupOrCreate(canonical_id2)->IsEmpty());
+}
+
+TEST_F(QuicCryptoClientConfigTest, ClearCachedStates) {
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+
+ // Create two states on different origins.
+ struct TestCase {
+ TestCase(const std::string& host, QuicCryptoClientConfig* config)
+ : server_id(host, 443, false),
+ state(config->LookupOrCreate(server_id)) {
+ // TODO(rch): Populate other fields of |state|.
+ CryptoHandshakeMessage scfg;
+ scfg.set_tag(kSCFG);
+ uint64_t future = 1;
+ scfg.SetValue(kEXPY, future);
+ scfg.SetStringPiece(kSCID, "12345678");
+ std::string details;
+ state->SetServerConfig(scfg.GetSerialized().AsStringPiece(),
+ QuicWallTime::FromUNIXSeconds(0),
+ QuicWallTime::FromUNIXSeconds(future), &details);
+
+ std::vector<std::string> certs(1);
+ certs[0] = "Hello Cert for " + host;
+ state->SetProof(certs, "cert_sct", "chlo_hash", "signature");
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ // The generation counter starts at 2, because proof has been once
+ // invalidated in SetServerConfig().
+ EXPECT_EQ(2u, state->generation_counter());
+ }
+
+ QuicServerId server_id;
+ QuicCryptoClientConfig::CachedState* state;
+ } test_cases[] = {TestCase("www.google.com", &config),
+ TestCase("www.example.com", &config)};
+
+ // Verify LookupOrCreate returns the same data.
+ for (const TestCase& test_case : test_cases) {
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(test_case.server_id);
+ EXPECT_EQ(test_case.state, other);
+ EXPECT_EQ(2u, other->generation_counter());
+ }
+
+ // Clear the cached state for www.google.com.
+ OneServerIdFilter google_com_filter(&test_cases[0].server_id);
+ config.ClearCachedStates(google_com_filter);
+
+ // Verify LookupOrCreate doesn't have any data for google.com.
+ QuicCryptoClientConfig::CachedState* cleared_cache =
+ config.LookupOrCreate(test_cases[0].server_id);
+
+ EXPECT_EQ(test_cases[0].state, cleared_cache);
+ EXPECT_FALSE(cleared_cache->proof_valid());
+ EXPECT_TRUE(cleared_cache->server_config().empty());
+ EXPECT_TRUE(cleared_cache->certs().empty());
+ EXPECT_TRUE(cleared_cache->cert_sct().empty());
+ EXPECT_TRUE(cleared_cache->signature().empty());
+ EXPECT_EQ(3u, cleared_cache->generation_counter());
+
+ // But it still does for www.example.com.
+ QuicCryptoClientConfig::CachedState* existing_cache =
+ config.LookupOrCreate(test_cases[1].server_id);
+
+ EXPECT_EQ(test_cases[1].state, existing_cache);
+ EXPECT_TRUE(existing_cache->proof_valid());
+ EXPECT_FALSE(existing_cache->server_config().empty());
+ EXPECT_FALSE(existing_cache->certs().empty());
+ EXPECT_FALSE(existing_cache->cert_sct().empty());
+ EXPECT_FALSE(existing_cache->signature().empty());
+ EXPECT_EQ(2u, existing_cache->generation_counter());
+
+ // Clear all cached states.
+ AllServerIdsFilter all_server_ids;
+ config.ClearCachedStates(all_server_ids);
+
+ // The data for www.example.com should now be cleared as well.
+ cleared_cache = config.LookupOrCreate(test_cases[1].server_id);
+
+ EXPECT_EQ(test_cases[1].state, cleared_cache);
+ EXPECT_FALSE(cleared_cache->proof_valid());
+ EXPECT_TRUE(cleared_cache->server_config().empty());
+ EXPECT_TRUE(cleared_cache->certs().empty());
+ EXPECT_TRUE(cleared_cache->cert_sct().empty());
+ EXPECT_TRUE(cleared_cache->signature().empty());
+ EXPECT_EQ(3u, cleared_cache->generation_counter());
+}
+
+TEST_F(QuicCryptoClientConfigTest, ProcessReject) {
+ CryptoHandshakeMessage rej;
+ crypto_test_utils::FillInDummyReject(&rej, /* stateless */ false);
+
+ // Now process the rejection.
+ QuicCryptoClientConfig::CachedState cached;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params(
+ new QuicCryptoNegotiatedParameters);
+ std::string error;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ EXPECT_EQ(QUIC_NO_ERROR,
+ config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0),
+ AllSupportedTransportVersions().front(), "",
+ &cached, out_params, &error));
+ EXPECT_FALSE(cached.has_server_designated_connection_id());
+ EXPECT_FALSE(cached.has_server_nonce());
+}
+
+TEST_F(QuicCryptoClientConfigTest, ProcessRejectWithLongTTL) {
+ CryptoHandshakeMessage rej;
+ crypto_test_utils::FillInDummyReject(&rej, /* stateless */ false);
+ QuicTime::Delta one_week = QuicTime::Delta::FromSeconds(kNumSecondsPerWeek);
+ int64_t long_ttl = 3 * one_week.ToSeconds();
+ rej.SetValue(kSTTL, long_ttl);
+
+ // Now process the rejection.
+ QuicCryptoClientConfig::CachedState cached;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params(
+ new QuicCryptoNegotiatedParameters);
+ std::string error;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ EXPECT_EQ(QUIC_NO_ERROR,
+ config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0),
+ AllSupportedTransportVersions().front(), "",
+ &cached, out_params, &error));
+ cached.SetProofValid();
+ EXPECT_FALSE(cached.IsComplete(QuicWallTime::FromUNIXSeconds(long_ttl)));
+ EXPECT_FALSE(
+ cached.IsComplete(QuicWallTime::FromUNIXSeconds(one_week.ToSeconds())));
+ EXPECT_TRUE(cached.IsComplete(
+ QuicWallTime::FromUNIXSeconds(one_week.ToSeconds() - 1)));
+}
+
+TEST_F(QuicCryptoClientConfigTest, ProcessStatelessReject) {
+ // Create a dummy reject message and mark it as stateless.
+ CryptoHandshakeMessage rej;
+ crypto_test_utils::FillInDummyReject(&rej, /* stateless */ true);
+ const QuicConnectionId kConnectionId = TestConnectionId(0xdeadbeef);
+ const std::string server_nonce = "SERVER_NONCE";
+ const uint64_t kConnectionId64 = TestConnectionIdToUInt64(kConnectionId);
+ rej.SetValue(kRCID, kConnectionId64);
+ rej.SetStringPiece(kServerNonceTag, server_nonce);
+
+ // Now process the rejection.
+ QuicCryptoClientConfig::CachedState cached;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params(
+ new QuicCryptoNegotiatedParameters);
+ std::string error;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ EXPECT_EQ(QUIC_NO_ERROR,
+ config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0),
+ AllSupportedTransportVersions().front(), "",
+ &cached, out_params, &error));
+ EXPECT_TRUE(cached.has_server_designated_connection_id());
+ EXPECT_EQ(TestConnectionId(QuicEndian::NetToHost64(
+ TestConnectionIdToUInt64(kConnectionId))),
+ cached.GetNextServerDesignatedConnectionId());
+ EXPECT_EQ(server_nonce, cached.GetNextServerNonce());
+}
+
+TEST_F(QuicCryptoClientConfigTest, BadlyFormattedStatelessReject) {
+ // Create a dummy reject message and mark it as stateless. Do not
+ // add an server-designated connection-id.
+ CryptoHandshakeMessage rej;
+ crypto_test_utils::FillInDummyReject(&rej, /* stateless */ true);
+
+ // Now process the rejection.
+ QuicCryptoClientConfig::CachedState cached;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params(
+ new QuicCryptoNegotiatedParameters);
+ std::string error;
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND,
+ config.ProcessRejection(rej, QuicWallTime::FromUNIXSeconds(0),
+ AllSupportedTransportVersions().front(), "",
+ &cached, out_params, &error));
+ EXPECT_FALSE(cached.has_server_designated_connection_id());
+ EXPECT_EQ("Missing kRCID", error);
+}
+
+TEST_F(QuicCryptoClientConfigTest, ServerNonceinSHLO) {
+ // Test that the server must include a nonce in the SHLO.
+ CryptoHandshakeMessage msg;
+ msg.set_tag(kSHLO);
+ // Choose the latest version.
+ ParsedQuicVersionVector supported_versions;
+ ParsedQuicVersion version = AllSupportedVersions().front();
+ supported_versions.push_back(version);
+ msg.SetVersionVector(kVER, supported_versions);
+
+ QuicCryptoClientConfig config(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ QuicCryptoClientConfig::CachedState cached;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> out_params(
+ new QuicCryptoNegotiatedParameters);
+ std::string error_details;
+ EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ config.ProcessServerHello(msg, EmptyQuicConnectionId(), version,
+ supported_versions, &cached, out_params,
+ &error_details));
+ EXPECT_EQ("server hello missing server nonce", error_details);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.cc
new file mode 100644
index 00000000000..33780b4a556
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.cc
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h"
+
+namespace quic {
+
+QuicCryptoProof::QuicCryptoProof() : send_expect_ct_header(false) {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h
new file mode 100644
index 00000000000..c3350680133
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Contains the crypto-related data provided by ProofSource
+struct QUIC_EXPORT_PRIVATE QuicCryptoProof {
+ QuicCryptoProof();
+
+ // Signature generated by ProofSource
+ std::string signature;
+ // SCTList (RFC6962) to be sent to the client, if it supports receiving it.
+ std::string leaf_cert_scts;
+ // Should the Expect-CT header be sent on the connection where the
+ // certificate is used.
+ bool send_expect_ct_header;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_PROOF_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc
new file mode 100644
index 00000000000..88875beb5a2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.cc
@@ -0,0 +1,1925 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <memory>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/curve25519_key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/p256_key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/proto/source_address_token.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_cert_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+namespace {
+
+// kMultiplier is the multiple of the CHLO message size that a REJ message
+// must stay under when the client doesn't present a valid source-address
+// token. This is used to protect QUIC from amplification attacks.
+// TODO(rch): Reduce this to 2 again once b/25933682 is fixed.
+const size_t kMultiplier = 3;
+
+const int kMaxTokenAddresses = 4;
+
+std::string DeriveSourceAddressTokenKey(
+ QuicStringPiece source_address_token_secret) {
+ QuicHKDF hkdf(source_address_token_secret, QuicStringPiece() /* no salt */,
+ "QUIC source address token key",
+ CryptoSecretBoxer::GetKeySize(), 0 /* no fixed IV needed */,
+ 0 /* no subkey secret */);
+ return std::string(hkdf.server_write_key());
+}
+
+// Default source for creating KeyExchange objects.
+class DefaultKeyExchangeSource : public KeyExchangeSource {
+ public:
+ DefaultKeyExchangeSource() = default;
+ ~DefaultKeyExchangeSource() override = default;
+
+ std::unique_ptr<AsynchronousKeyExchange> Create(
+ std::string server_config_id,
+ bool /* is_fallback */,
+ QuicTag type,
+ QuicStringPiece private_key) override {
+ if (private_key.empty()) {
+ QUIC_LOG(WARNING) << "Server config contains key exchange method without "
+ "corresponding private key of type "
+ << QuicTagToString(type);
+ return nullptr;
+ }
+
+ std::unique_ptr<SynchronousKeyExchange> ka =
+ CreateLocalSynchronousKeyExchange(type, private_key);
+ if (!ka) {
+ QUIC_LOG(WARNING) << "Failed to create key exchange method of type "
+ << QuicTagToString(type);
+ }
+ return ka;
+ }
+};
+
+// Returns true if the PDMD field from the client hello demands an X509
+// certificate.
+bool ClientDemandsX509Proof(const CryptoHandshakeMessage& client_hello) {
+ QuicTagVector their_proof_demands;
+
+ if (client_hello.GetTaglist(kPDMD, &their_proof_demands) != QUIC_NO_ERROR) {
+ return false;
+ }
+
+ for (const QuicTag tag : their_proof_demands) {
+ if (tag == kX509) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+// static
+std::unique_ptr<KeyExchangeSource> KeyExchangeSource::Default() {
+ return QuicMakeUnique<DefaultKeyExchangeSource>();
+}
+
+class ValidateClientHelloHelper {
+ public:
+ // Note: stores a pointer to a unique_ptr, and std::moves the unique_ptr when
+ // ValidationComplete is called.
+ ValidateClientHelloHelper(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result,
+ std::unique_ptr<ValidateClientHelloResultCallback>* done_cb)
+ : result_(std::move(result)), done_cb_(done_cb) {}
+ ValidateClientHelloHelper(const ValidateClientHelloHelper&) = delete;
+ ValidateClientHelloHelper& operator=(const ValidateClientHelloHelper&) =
+ delete;
+
+ ~ValidateClientHelloHelper() {
+ QUIC_BUG_IF(done_cb_ != nullptr)
+ << "Deleting ValidateClientHelloHelper with a pending callback.";
+ }
+
+ void ValidationComplete(
+ QuicErrorCode error_code,
+ const char* error_details,
+ std::unique_ptr<ProofSource::Details> proof_source_details) {
+ result_->error_code = error_code;
+ result_->error_details = error_details;
+ (*done_cb_)->Run(std::move(result_), std::move(proof_source_details));
+ DetachCallback();
+ }
+
+ void DetachCallback() {
+ QUIC_BUG_IF(done_cb_ == nullptr) << "Callback already detached.";
+ done_cb_ = nullptr;
+ }
+
+ private:
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result_;
+ std::unique_ptr<ValidateClientHelloResultCallback>* done_cb_;
+};
+
+// static
+const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
+
+ClientHelloInfo::ClientHelloInfo(const QuicIpAddress& in_client_ip,
+ QuicWallTime in_now)
+ : client_ip(in_client_ip), now(in_now), valid_source_address_token(false) {}
+
+ClientHelloInfo::ClientHelloInfo(const ClientHelloInfo& other) = default;
+
+ClientHelloInfo::~ClientHelloInfo() {}
+
+PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() {}
+
+PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {}
+
+ValidateClientHelloResultCallback::Result::Result(
+ const CryptoHandshakeMessage& in_client_hello,
+ QuicIpAddress in_client_ip,
+ QuicWallTime in_now)
+ : client_hello(in_client_hello),
+ info(in_client_ip, in_now),
+ error_code(QUIC_NO_ERROR) {}
+
+ValidateClientHelloResultCallback::Result::~Result() {}
+
+ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {}
+
+ValidateClientHelloResultCallback::~ValidateClientHelloResultCallback() {}
+
+ProcessClientHelloResultCallback::ProcessClientHelloResultCallback() {}
+
+ProcessClientHelloResultCallback::~ProcessClientHelloResultCallback() {}
+
+QuicCryptoServerConfig::ConfigOptions::ConfigOptions()
+ : expiry_time(QuicWallTime::Zero()),
+ channel_id_enabled(false),
+ p256(false) {}
+
+QuicCryptoServerConfig::ConfigOptions::ConfigOptions(
+ const ConfigOptions& other) = default;
+
+QuicCryptoServerConfig::ConfigOptions::~ConfigOptions() {}
+
+QuicCryptoServerConfig::ProcessClientHelloContext::
+ ~ProcessClientHelloContext() {
+ if (done_cb_ != nullptr) {
+ LOG(WARNING)
+ << "Deleting ProcessClientHelloContext with a pending callback.";
+ }
+}
+
+void QuicCryptoServerConfig::ProcessClientHelloContext::Fail(
+ QuicErrorCode error,
+ const std::string& error_details) {
+ done_cb_->Run(error, error_details, nullptr, nullptr, nullptr);
+ done_cb_ = nullptr;
+}
+
+void QuicCryptoServerConfig::ProcessClientHelloContext::Succeed(
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) {
+ done_cb_->Run(QUIC_NO_ERROR, std::string(), std::move(message),
+ std::move(diversification_nonce),
+ std::move(proof_source_details));
+ done_cb_ = nullptr;
+}
+
+QuicCryptoServerConfig::QuicCryptoServerConfig(
+ QuicStringPiece source_address_token_secret,
+ QuicRandom* server_nonce_entropy,
+ std::unique_ptr<ProofSource> proof_source,
+ std::unique_ptr<KeyExchangeSource> key_exchange_source,
+ bssl::UniquePtr<SSL_CTX> ssl_ctx)
+ : replay_protection_(true),
+ chlo_multiplier_(kMultiplier),
+ configs_lock_(),
+ primary_config_(nullptr),
+ next_config_promotion_time_(QuicWallTime::Zero()),
+ proof_source_(std::move(proof_source)),
+ key_exchange_source_(std::move(key_exchange_source)),
+ ssl_ctx_(std::move(ssl_ctx)),
+ source_address_token_future_secs_(3600),
+ source_address_token_lifetime_secs_(86400),
+ enable_serving_sct_(false),
+ rejection_observer_(nullptr),
+ pad_rej_(true),
+ pad_shlo_(true),
+ validate_chlo_size_(true),
+ validate_source_address_token_(true) {
+ DCHECK(proof_source_.get());
+ source_address_token_boxer_.SetKeys(
+ {DeriveSourceAddressTokenKey(source_address_token_secret)});
+
+ // Generate a random key and orbit for server nonces.
+ server_nonce_entropy->RandBytes(server_nonce_orbit_,
+ sizeof(server_nonce_orbit_));
+ const size_t key_size = server_nonce_boxer_.GetKeySize();
+ std::unique_ptr<uint8_t[]> key_bytes(new uint8_t[key_size]);
+ server_nonce_entropy->RandBytes(key_bytes.get(), key_size);
+
+ server_nonce_boxer_.SetKeys(
+ {std::string(reinterpret_cast<char*>(key_bytes.get()), key_size)});
+}
+
+QuicCryptoServerConfig::~QuicCryptoServerConfig() {}
+
+// static
+QuicServerConfigProtobuf QuicCryptoServerConfig::GenerateConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options) {
+ CryptoHandshakeMessage msg;
+
+ const std::string curve25519_private_key =
+ Curve25519KeyExchange::NewPrivateKey(rand);
+ std::unique_ptr<Curve25519KeyExchange> curve25519 =
+ Curve25519KeyExchange::New(curve25519_private_key);
+ QuicStringPiece curve25519_public_value = curve25519->public_value();
+
+ std::string encoded_public_values;
+ // First three bytes encode the length of the public value.
+ DCHECK_LT(curve25519_public_value.size(), (1U << 24));
+ encoded_public_values.push_back(
+ static_cast<char>(curve25519_public_value.size()));
+ encoded_public_values.push_back(
+ static_cast<char>(curve25519_public_value.size() >> 8));
+ encoded_public_values.push_back(
+ static_cast<char>(curve25519_public_value.size() >> 16));
+ encoded_public_values.append(curve25519_public_value.data(),
+ curve25519_public_value.size());
+
+ std::string p256_private_key;
+ if (options.p256) {
+ p256_private_key = P256KeyExchange::NewPrivateKey();
+ std::unique_ptr<P256KeyExchange> p256(
+ P256KeyExchange::New(p256_private_key));
+ QuicStringPiece p256_public_value = p256->public_value();
+
+ DCHECK_LT(p256_public_value.size(), (1U << 24));
+ encoded_public_values.push_back(
+ static_cast<char>(p256_public_value.size()));
+ encoded_public_values.push_back(
+ static_cast<char>(p256_public_value.size() >> 8));
+ encoded_public_values.push_back(
+ static_cast<char>(p256_public_value.size() >> 16));
+ encoded_public_values.append(p256_public_value.data(),
+ p256_public_value.size());
+ }
+
+ msg.set_tag(kSCFG);
+ if (options.p256) {
+ msg.SetVector(kKEXS, QuicTagVector{kC255, kP256});
+ } else {
+ msg.SetVector(kKEXS, QuicTagVector{kC255});
+ }
+ msg.SetVector(kAEAD, QuicTagVector{kAESG, kCC20});
+ msg.SetStringPiece(kPUBS, encoded_public_values);
+
+ if (options.expiry_time.IsZero()) {
+ const QuicWallTime now = clock->WallNow();
+ const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds(
+ 60 * 60 * 24 * 180 /* 180 days, ~six months */));
+ const uint64_t expiry_seconds = expiry.ToUNIXSeconds();
+ msg.SetValue(kEXPY, expiry_seconds);
+ } else {
+ msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds());
+ }
+
+ char orbit_bytes[kOrbitSize];
+ if (options.orbit.size() == sizeof(orbit_bytes)) {
+ memcpy(orbit_bytes, options.orbit.data(), sizeof(orbit_bytes));
+ } else {
+ DCHECK(options.orbit.empty());
+ rand->RandBytes(orbit_bytes, sizeof(orbit_bytes));
+ }
+ msg.SetStringPiece(kORBT, QuicStringPiece(orbit_bytes, sizeof(orbit_bytes)));
+
+ if (options.channel_id_enabled) {
+ msg.SetVector(kPDMD, QuicTagVector{kCHID});
+ }
+
+ if (!options.token_binding_params.empty()) {
+ msg.SetVector(kTBKP, options.token_binding_params);
+ }
+
+ if (options.id.empty()) {
+ // We need to ensure that the SCID changes whenever the server config does
+ // thus we make it a hash of the rest of the server config.
+ std::unique_ptr<QuicData> serialized =
+ CryptoFramer::ConstructHandshakeMessage(msg);
+
+ uint8_t scid_bytes[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(serialized->data()),
+ serialized->length(), scid_bytes);
+ // The SCID is a truncated SHA-256 digest.
+ static_assert(16 <= SHA256_DIGEST_LENGTH, "SCID length too high.");
+ msg.SetStringPiece(
+ kSCID, QuicStringPiece(reinterpret_cast<const char*>(scid_bytes), 16));
+ } else {
+ msg.SetStringPiece(kSCID, options.id);
+ }
+ // Don't put new tags below this point. The SCID generation should hash over
+ // everything but itself and so extra tags should be added prior to the
+ // preceding if block.
+
+ std::unique_ptr<QuicData> serialized =
+ CryptoFramer::ConstructHandshakeMessage(msg);
+
+ QuicServerConfigProtobuf config;
+ config.set_config(std::string(serialized->AsStringPiece()));
+ QuicServerConfigProtobuf::PrivateKey* curve25519_key = config.add_key();
+ curve25519_key->set_tag(kC255);
+ curve25519_key->set_private_key(curve25519_private_key);
+
+ if (options.p256) {
+ QuicServerConfigProtobuf::PrivateKey* p256_key = config.add_key();
+ p256_key->set_tag(kP256);
+ p256_key->set_private_key(p256_private_key);
+ }
+
+ return config;
+}
+
+std::unique_ptr<CryptoHandshakeMessage> QuicCryptoServerConfig::AddConfig(
+ const QuicServerConfigProtobuf& protobuf,
+ const QuicWallTime now) {
+ std::unique_ptr<CryptoHandshakeMessage> msg =
+ CryptoFramer::ParseMessage(protobuf.config());
+
+ if (!msg.get()) {
+ QUIC_LOG(WARNING) << "Failed to parse server config message";
+ return nullptr;
+ }
+
+ QuicReferenceCountedPointer<Config> config =
+ ParseConfigProtobuf(protobuf, /* is_fallback = */ false);
+ if (!config.get()) {
+ QUIC_LOG(WARNING) << "Failed to parse server config message";
+ return nullptr;
+ }
+
+ {
+ QuicWriterMutexLock locked(&configs_lock_);
+ if (configs_.find(config->id) != configs_.end()) {
+ QUIC_LOG(WARNING) << "Failed to add config because another with the same "
+ "server config id already exists: "
+ << QuicTextUtils::HexEncode(config->id);
+ return nullptr;
+ }
+
+ configs_[config->id] = config;
+ SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
+ primary_config_.get());
+ }
+
+ return msg;
+}
+
+std::unique_ptr<CryptoHandshakeMessage>
+QuicCryptoServerConfig::AddDefaultConfig(QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options) {
+ return AddConfig(GenerateConfig(rand, clock, options), clock->WallNow());
+}
+
+bool QuicCryptoServerConfig::SetConfigs(
+ const std::vector<QuicServerConfigProtobuf>& protobufs,
+ const QuicServerConfigProtobuf* fallback_protobuf,
+ const QuicWallTime now) {
+ std::vector<QuicReferenceCountedPointer<Config>> parsed_configs;
+ for (auto& protobuf : protobufs) {
+ QuicReferenceCountedPointer<Config> config =
+ ParseConfigProtobuf(protobuf, /* is_fallback = */ false);
+ if (!config) {
+ QUIC_LOG(WARNING) << "Rejecting QUIC configs because of above errors";
+ return false;
+ }
+
+ parsed_configs.push_back(config);
+ }
+
+ QuicReferenceCountedPointer<Config> fallback_config;
+ if (fallback_protobuf != nullptr) {
+ fallback_config =
+ ParseConfigProtobuf(*fallback_protobuf, /* is_fallback = */ true);
+ if (!fallback_config) {
+ QUIC_LOG(WARNING) << "Rejecting QUIC configs because of above errors";
+ return false;
+ }
+ QUIC_LOG(INFO) << "Fallback config has scid "
+ << QuicTextUtils::HexEncode(fallback_config->id);
+ parsed_configs.push_back(fallback_config);
+ } else {
+ QUIC_LOG(INFO) << "No fallback config provided";
+ }
+
+ if (parsed_configs.empty()) {
+ QUIC_LOG(WARNING)
+ << "Rejecting QUIC configs because new config list is empty.";
+ return false;
+ }
+
+ QUIC_LOG(INFO) << "Updating configs:";
+
+ QuicWriterMutexLock locked(&configs_lock_);
+ ConfigMap new_configs;
+
+ for (const QuicReferenceCountedPointer<Config>& config : parsed_configs) {
+ auto it = configs_.find(config->id);
+ if (it != configs_.end()) {
+ QUIC_LOG(INFO) << "Keeping scid: " << QuicTextUtils::HexEncode(config->id)
+ << " orbit: "
+ << QuicTextUtils::HexEncode(
+ reinterpret_cast<const char*>(config->orbit),
+ kOrbitSize)
+ << " new primary_time "
+ << config->primary_time.ToUNIXSeconds()
+ << " old primary_time "
+ << it->second->primary_time.ToUNIXSeconds()
+ << " new priority " << config->priority << " old priority "
+ << it->second->priority;
+ // Update primary_time and priority.
+ it->second->primary_time = config->primary_time;
+ it->second->priority = config->priority;
+ new_configs.insert(*it);
+ } else {
+ QUIC_LOG(INFO) << "Adding scid: " << QuicTextUtils::HexEncode(config->id)
+ << " orbit: "
+ << QuicTextUtils::HexEncode(
+ reinterpret_cast<const char*>(config->orbit),
+ kOrbitSize)
+ << " primary_time " << config->primary_time.ToUNIXSeconds()
+ << " priority " << config->priority;
+ new_configs.emplace(config->id, config);
+ }
+ }
+
+ configs_ = std::move(new_configs);
+ fallback_config_ = fallback_config;
+ SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
+ primary_config_.get());
+
+ return true;
+}
+
+void QuicCryptoServerConfig::SetSourceAddressTokenKeys(
+ const std::vector<std::string>& keys) {
+ source_address_token_boxer_.SetKeys(keys);
+}
+
+void QuicCryptoServerConfig::GetConfigIds(
+ std::vector<std::string>* scids) const {
+ QuicReaderMutexLock locked(&configs_lock_);
+ for (auto it = configs_.begin(); it != configs_.end(); ++it) {
+ scids->push_back(it->first);
+ }
+}
+
+void QuicCryptoServerConfig::ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ const QuicIpAddress& client_ip,
+ const QuicSocketAddress& server_address,
+ QuicTransportVersion version,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const {
+ const QuicWallTime now(clock->WallNow());
+
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result> result(
+ new ValidateClientHelloResultCallback::Result(client_hello, client_ip,
+ now));
+
+ QuicStringPiece requested_scid;
+ client_hello.GetStringPiece(kSCID, &requested_scid);
+ Configs configs;
+ if (!GetCurrentConfigs(now, requested_scid,
+ /* old_primary_config = */ nullptr, &configs)) {
+ result->error_code = QUIC_CRYPTO_INTERNAL_ERROR;
+ result->error_details = "No configurations loaded";
+ }
+ signed_config->config = configs.primary;
+
+ if (result->error_code == QUIC_NO_ERROR) {
+ // QUIC requires a new proof for each CHLO so clear any existing proof.
+ signed_config->chain = nullptr;
+ signed_config->proof.signature = "";
+ signed_config->proof.leaf_cert_scts = "";
+ EvaluateClientHello(server_address, version, configs, result,
+ std::move(done_cb));
+ } else {
+ done_cb->Run(result, /* details = */ nullptr);
+ }
+}
+
+class QuicCryptoServerConfig::ProcessClientHelloCallback
+ : public ProofSource::Callback {
+ public:
+ ProcessClientHelloCallback(const QuicCryptoServerConfig* config,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs)
+ : config_(config), context_(std::move(context)), configs_(configs) {}
+
+ void Run(bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<ProofSource::Details> details) override {
+ if (ok) {
+ context_->signed_config()->chain = chain;
+ context_->signed_config()->proof = proof;
+ }
+ config_->ProcessClientHelloAfterGetProof(!ok, std::move(details),
+ std::move(context_), configs_);
+ }
+
+ private:
+ const QuicCryptoServerConfig* config_;
+ std::unique_ptr<ProcessClientHelloContext> context_;
+ const Configs configs_;
+};
+
+class QuicCryptoServerConfig::ProcessClientHelloAfterGetProofCallback
+ : public AsynchronousKeyExchange::Callback {
+ public:
+ ProcessClientHelloAfterGetProofCallback(
+ const QuicCryptoServerConfig* config,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ QuicTag key_exchange_type,
+ std::unique_ptr<CryptoHandshakeMessage> out,
+ QuicStringPiece public_value,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs)
+ : config_(config),
+ proof_source_details_(std::move(proof_source_details)),
+ key_exchange_type_(key_exchange_type),
+ out_(std::move(out)),
+ public_value_(public_value),
+ context_(std::move(context)),
+ configs_(configs) {}
+
+ void Run(bool ok) override {
+ config_->ProcessClientHelloAfterCalculateSharedKeys(
+ !ok, std::move(proof_source_details_), key_exchange_type_,
+ std::move(out_), public_value_, std::move(context_), configs_);
+ }
+
+ private:
+ const QuicCryptoServerConfig* config_;
+ std::unique_ptr<ProofSource::Details> proof_source_details_;
+ const QuicTag key_exchange_type_;
+ std::unique_ptr<CryptoHandshakeMessage> out_;
+ const std::string public_value_;
+ std::unique_ptr<ProcessClientHelloContext> context_;
+ const Configs configs_;
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb_;
+};
+
+class QuicCryptoServerConfig::SendRejectWithFallbackConfigCallback
+ : public ProofSource::Callback {
+ public:
+ SendRejectWithFallbackConfigCallback(
+ const QuicCryptoServerConfig* config,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ QuicReferenceCountedPointer<Config> fallback_config)
+ : config_(config),
+ context_(std::move(context)),
+ fallback_config_(fallback_config) {}
+
+ // Capture |chain| and |proof| into the signed config, and then invoke
+ // SendRejectWithFallbackConfigAfterGetProof.
+ void Run(bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<ProofSource::Details> details) override {
+ if (ok) {
+ context_->signed_config()->chain = chain;
+ context_->signed_config()->proof = proof;
+ }
+ config_->SendRejectWithFallbackConfigAfterGetProof(
+ !ok, std::move(details), std::move(context_), fallback_config_);
+ }
+
+ private:
+ const QuicCryptoServerConfig* config_;
+ std::unique_ptr<ProcessClientHelloContext> context_;
+ QuicReferenceCountedPointer<Config> fallback_config_;
+};
+
+void QuicCryptoServerConfig::ProcessClientHello(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ validate_chlo_result,
+ bool reject_only,
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions,
+ bool use_stateless_rejects,
+ QuicConnectionId server_designated_connection_id,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicByteCount total_framing_overhead,
+ QuicByteCount chlo_packet_size,
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const {
+ DCHECK(done_cb);
+ auto context = QuicMakeUnique<ProcessClientHelloContext>(
+ validate_chlo_result, reject_only, connection_id, server_address,
+ client_address, version, supported_versions, use_stateless_rejects,
+ server_designated_connection_id, clock, rand, compressed_certs_cache,
+ params, signed_config, total_framing_overhead, chlo_packet_size,
+ std::move(done_cb));
+
+ // Verify that various parts of the CHLO are valid
+ std::string error_details;
+ QuicErrorCode valid = CryptoUtils::ValidateClientHello(
+ context->client_hello(), context->version(),
+ context->supported_versions(), &error_details);
+ if (valid != QUIC_NO_ERROR) {
+ context->Fail(valid, error_details);
+ return;
+ }
+
+ QuicStringPiece requested_scid;
+ context->client_hello().GetStringPiece(kSCID, &requested_scid);
+ Configs configs;
+ if (!GetCurrentConfigs(context->clock()->WallNow(), requested_scid,
+ signed_config->config, &configs)) {
+ context->Fail(QUIC_CRYPTO_INTERNAL_ERROR, "No configurations loaded");
+ return;
+ }
+
+ if (context->validate_chlo_result()->error_code != QUIC_NO_ERROR) {
+ context->Fail(context->validate_chlo_result()->error_code,
+ context->validate_chlo_result()->error_details);
+ return;
+ }
+
+ if (!ClientDemandsX509Proof(context->client_hello())) {
+ context->Fail(QUIC_UNSUPPORTED_PROOF_DEMAND, "Missing or invalid PDMD");
+ return;
+ }
+
+ // No need to get a new proof if one was already generated.
+ if (!context->signed_config()->chain) {
+ const std::string chlo_hash = CryptoUtils::HashHandshakeMessage(
+ context->client_hello(), Perspective::IS_SERVER);
+ const QuicSocketAddress server_address = context->server_address();
+ const std::string sni = std::string(context->info().sni);
+ const QuicTransportVersion transport_version = context->transport_version();
+
+ auto cb = QuicMakeUnique<ProcessClientHelloCallback>(
+ this, std::move(context), configs);
+
+ DCHECK(proof_source_.get());
+ proof_source_->GetProof(server_address, sni, configs.primary->serialized,
+ transport_version, chlo_hash, std::move(cb));
+ return;
+ }
+
+ ProcessClientHelloAfterGetProof(
+ /* found_error = */ false, /* proof_source_details = */ nullptr,
+ std::move(context), configs);
+}
+
+void QuicCryptoServerConfig::ProcessClientHelloAfterGetProof(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs) const {
+ QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(
+ context->connection_id(), context->transport_version()))
+ << "ProcessClientHelloAfterGetProof: attempted to use connection ID "
+ << context->connection_id() << " which is invalid with version "
+ << QuicVersionToString(context->transport_version());
+
+ if (found_error) {
+ context->Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof");
+ return;
+ }
+
+ auto out_diversification_nonce = QuicMakeUnique<DiversificationNonce>();
+
+ QuicStringPiece cert_sct;
+ if (context->client_hello().GetStringPiece(kCertificateSCTTag, &cert_sct) &&
+ cert_sct.empty()) {
+ context->params()->sct_supported_by_client = true;
+ }
+
+ auto out = QuicMakeUnique<CryptoHandshakeMessage>();
+ if (!context->info().reject_reasons.empty() || !configs.requested) {
+ BuildRejectionAndRecordStats(*context, *configs.primary,
+ context->info().reject_reasons, out.get());
+ context->Succeed(std::move(out), std::move(out_diversification_nonce),
+ std::move(proof_source_details));
+ return;
+ }
+
+ if (context->reject_only()) {
+ context->Succeed(std::move(out), std::move(out_diversification_nonce),
+ std::move(proof_source_details));
+ return;
+ }
+
+ QuicTagVector their_aeads;
+ QuicTagVector their_key_exchanges;
+ if (context->client_hello().GetTaglist(kAEAD, &their_aeads) !=
+ QUIC_NO_ERROR ||
+ context->client_hello().GetTaglist(kKEXS, &their_key_exchanges) !=
+ QUIC_NO_ERROR ||
+ their_aeads.size() != 1 || their_key_exchanges.size() != 1) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Missing or invalid AEAD or KEXS");
+ return;
+ }
+
+ size_t key_exchange_index;
+ if (!FindMutualQuicTag(configs.requested->aead, their_aeads,
+ &context->params()->aead, nullptr) ||
+ !FindMutualQuicTag(configs.requested->kexs, their_key_exchanges,
+ &context->params()->key_exchange,
+ &key_exchange_index)) {
+ context->Fail(QUIC_CRYPTO_NO_SUPPORT, "Unsupported AEAD or KEXS");
+ return;
+ }
+
+ if (!configs.requested->tb_key_params.empty()) {
+ QuicTagVector their_tbkps;
+ switch (context->client_hello().GetTaglist(kTBKP, &their_tbkps)) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ break;
+ case QUIC_NO_ERROR:
+ if (FindMutualQuicTag(configs.requested->tb_key_params, their_tbkps,
+ &context->params()->token_binding_key_param,
+ nullptr)) {
+ break;
+ }
+ QUIC_FALLTHROUGH_INTENDED;
+ default:
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Invalid Token Binding key parameter");
+ return;
+ }
+ }
+
+ QuicStringPiece public_value;
+ if (!context->client_hello().GetStringPiece(kPUBS, &public_value)) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Missing public value");
+ return;
+ }
+
+ const AsynchronousKeyExchange* key_exchange =
+ configs.requested->key_exchanges[key_exchange_index].get();
+ std::string* initial_premaster_secret =
+ &context->params()->initial_premaster_secret;
+ auto cb = QuicMakeUnique<ProcessClientHelloAfterGetProofCallback>(
+ this, std::move(proof_source_details), key_exchange->type(),
+ std::move(out), public_value, std::move(context), configs);
+ key_exchange->CalculateSharedKeyAsync(public_value, initial_premaster_secret,
+ std::move(cb));
+}
+
+void QuicCryptoServerConfig::ProcessClientHelloAfterCalculateSharedKeys(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ QuicTag key_exchange_type,
+ std::unique_ptr<CryptoHandshakeMessage> out,
+ QuicStringPiece public_value,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs) const {
+ QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(
+ context->connection_id(), context->transport_version()))
+ << "ProcessClientHelloAfterCalculateSharedKeys:"
+ " attempted to use connection ID "
+ << context->connection_id() << " which is invalid with version "
+ << QuicVersionToString(context->transport_version());
+
+ if (found_error) {
+ // If we are already using the fallback config, just bail out of the
+ // handshake.
+ if (context->signed_config()->config == configs.fallback ||
+ !GetQuicReloadableFlag(
+ send_quic_fallback_server_config_on_leto_error)) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Failed to calculate shared key");
+ } else {
+ SendRejectWithFallbackConfig(std::move(context), configs.fallback);
+ }
+ return;
+ }
+
+ if (!context->info().sni.empty()) {
+ context->params()->sni =
+ QuicHostnameUtils::NormalizeHostname(context->info().sni);
+ }
+
+ std::string hkdf_suffix;
+ const QuicData& client_hello_serialized =
+ context->client_hello().GetSerialized();
+ hkdf_suffix.reserve(context->connection_id().length() +
+ client_hello_serialized.length() +
+ configs.requested->serialized.size());
+ hkdf_suffix.append(context->connection_id().data(),
+ context->connection_id().length());
+ hkdf_suffix.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ hkdf_suffix.append(configs.requested->serialized);
+ DCHECK(proof_source_.get());
+ if (context->signed_config()->chain->certs.empty()) {
+ context->Fail(QUIC_CRYPTO_INTERNAL_ERROR, "Failed to get certs");
+ return;
+ }
+ hkdf_suffix.append(context->signed_config()->chain->certs.at(0));
+
+ QuicStringPiece cetv_ciphertext;
+ if (configs.requested->channel_id_enabled &&
+ context->client_hello().GetStringPiece(kCETV, &cetv_ciphertext)) {
+ CryptoHandshakeMessage client_hello_copy(context->client_hello());
+ client_hello_copy.Erase(kCETV);
+ client_hello_copy.Erase(kPAD);
+
+ const QuicData& client_hello_copy_serialized =
+ client_hello_copy.GetSerialized();
+ std::string hkdf_input;
+ hkdf_input.append(QuicCryptoConfig::kCETVLabel,
+ strlen(QuicCryptoConfig::kCETVLabel) + 1);
+ hkdf_input.append(context->connection_id().data(),
+ context->connection_id().length());
+ hkdf_input.append(client_hello_copy_serialized.data(),
+ client_hello_copy_serialized.length());
+ hkdf_input.append(configs.requested->serialized);
+
+ CrypterPair crypters;
+ if (!CryptoUtils::DeriveKeys(
+ context->params()->initial_premaster_secret,
+ context->params()->aead, context->info().client_nonce,
+ context->info().server_nonce, pre_shared_key_, hkdf_input,
+ Perspective::IS_SERVER, CryptoUtils::Diversification::Never(),
+ &crypters, nullptr /* subkey secret */)) {
+ context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
+ "Symmetric key setup failed");
+ return;
+ }
+
+ char plaintext[kMaxOutgoingPacketSize];
+ size_t plaintext_length = 0;
+ const bool success = crypters.decrypter->DecryptPacket(
+ 0 /* packet number */, QuicStringPiece() /* associated data */,
+ cetv_ciphertext, plaintext, &plaintext_length, kMaxOutgoingPacketSize);
+ if (!success) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "CETV decryption failure");
+ return;
+ }
+ std::unique_ptr<CryptoHandshakeMessage> cetv(CryptoFramer::ParseMessage(
+ QuicStringPiece(plaintext, plaintext_length)));
+ if (!cetv.get()) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "CETV parse error");
+ return;
+ }
+
+ QuicStringPiece key, signature;
+ if (cetv->GetStringPiece(kCIDK, &key) &&
+ cetv->GetStringPiece(kCIDS, &signature)) {
+ if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "ChannelID signature failure");
+ return;
+ }
+
+ context->params()->channel_id = std::string(key);
+ }
+ }
+
+ std::string hkdf_input;
+ size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1;
+ hkdf_input.reserve(label_len + hkdf_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len);
+ hkdf_input.append(hkdf_suffix);
+
+ auto out_diversification_nonce = QuicMakeUnique<DiversificationNonce>();
+ context->rand()->RandBytes(out_diversification_nonce->data(),
+ out_diversification_nonce->size());
+ CryptoUtils::Diversification diversification =
+ CryptoUtils::Diversification::Now(out_diversification_nonce.get());
+ if (!CryptoUtils::DeriveKeys(
+ context->params()->initial_premaster_secret, context->params()->aead,
+ context->info().client_nonce, context->info().server_nonce,
+ pre_shared_key_, hkdf_input, Perspective::IS_SERVER, diversification,
+ &context->params()->initial_crypters,
+ &context->params()->initial_subkey_secret)) {
+ context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
+ "Symmetric key setup failed");
+ return;
+ }
+
+ std::string forward_secure_public_value;
+ std::unique_ptr<SynchronousKeyExchange> forward_secure_key_exchange =
+ CreateLocalSynchronousKeyExchange(key_exchange_type, context->rand());
+ if (!forward_secure_key_exchange) {
+ QUIC_DLOG(WARNING) << "Failed to create keypair";
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Failed to create keypair");
+ return;
+ }
+
+ forward_secure_public_value =
+ std::string(forward_secure_key_exchange->public_value());
+ if (!forward_secure_key_exchange->CalculateSharedKeySync(
+ public_value, &context->params()->forward_secure_premaster_secret)) {
+ context->Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Invalid public value");
+ return;
+ }
+
+ std::string forward_secure_hkdf_input;
+ label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1;
+ forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size());
+ forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel,
+ label_len);
+ forward_secure_hkdf_input.append(hkdf_suffix);
+
+ std::string shlo_nonce;
+ shlo_nonce = NewServerNonce(context->rand(), context->info().now);
+ out->SetStringPiece(kServerNonceTag, shlo_nonce);
+
+ if (!CryptoUtils::DeriveKeys(
+ context->params()->forward_secure_premaster_secret,
+ context->params()->aead, context->info().client_nonce,
+ shlo_nonce.empty() ? context->info().server_nonce : shlo_nonce,
+ pre_shared_key_, forward_secure_hkdf_input, Perspective::IS_SERVER,
+ CryptoUtils::Diversification::Never(),
+ &context->params()->forward_secure_crypters,
+ &context->params()->subkey_secret)) {
+ context->Fail(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED,
+ "Symmetric key setup failed");
+ return;
+ }
+
+ out->set_tag(kSHLO);
+ out->SetVersionVector(kVER, context->supported_versions());
+ out->SetStringPiece(
+ kSourceAddressTokenTag,
+ NewSourceAddressToken(*configs.requested,
+ context->info().source_address_tokens,
+ context->client_address().host(), context->rand(),
+ context->info().now, nullptr));
+ QuicSocketAddressCoder address_coder(context->client_address());
+ out->SetStringPiece(kCADR, address_coder.Encode());
+ out->SetStringPiece(kPUBS, forward_secure_public_value);
+
+ context->Succeed(std::move(out), std::move(out_diversification_nonce),
+ std::move(proof_source_details));
+}
+
+void QuicCryptoServerConfig::SendRejectWithFallbackConfig(
+ std::unique_ptr<ProcessClientHelloContext> context,
+ QuicReferenceCountedPointer<Config> fallback_config) const {
+ // We failed to calculate a shared initial key, likely because we tried to use
+ // a remote key-exchange service which could not be reached. We want to send
+ // a REJ which tells the client to use a different ServerConfig which
+ // corresponds to a local keypair. To generate the REJ we need to request a
+ // new proof.
+ const std::string chlo_hash = CryptoUtils::HashHandshakeMessage(
+ context->client_hello(), Perspective::IS_SERVER);
+ const QuicSocketAddress server_address = context->server_address();
+ const std::string sni(context->info().sni);
+ const QuicTransportVersion transport_version = context->transport_version();
+
+ auto cb = QuicMakeUnique<SendRejectWithFallbackConfigCallback>(
+ this, std::move(context), fallback_config);
+ proof_source_->GetProof(server_address, sni, fallback_config->serialized,
+ transport_version, chlo_hash, std::move(cb));
+}
+
+void QuicCryptoServerConfig::SendRejectWithFallbackConfigAfterGetProof(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ QuicReferenceCountedPointer<Config> fallback_config) const {
+ if (found_error) {
+ context->Fail(QUIC_HANDSHAKE_FAILED, "Failed to get proof");
+ return;
+ }
+
+ auto out = QuicMakeUnique<CryptoHandshakeMessage>();
+ BuildRejectionAndRecordStats(*context, *fallback_config,
+ {SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE},
+ out.get());
+
+ context->Succeed(std::move(out), QuicMakeUnique<DiversificationNonce>(),
+ std::move(proof_source_details));
+}
+
+QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfig::GetConfigWithScid(
+ QuicStringPiece requested_scid) const {
+ configs_lock_.AssertReaderHeld();
+
+ if (!requested_scid.empty()) {
+ auto it = configs_.find((std::string(requested_scid)));
+ if (it != configs_.end()) {
+ // We'll use the config that the client requested in order to do
+ // key-agreement.
+ return QuicReferenceCountedPointer<Config>(it->second);
+ }
+ }
+
+ return QuicReferenceCountedPointer<Config>();
+}
+
+bool QuicCryptoServerConfig::GetCurrentConfigs(
+ const QuicWallTime& now,
+ QuicStringPiece requested_scid,
+ QuicReferenceCountedPointer<Config> old_primary_config,
+ Configs* configs) const {
+ QuicReaderMutexLock locked(&configs_lock_);
+
+ if (!primary_config_) {
+ return false;
+ }
+
+ if (IsNextConfigReady(now)) {
+ configs_lock_.ReaderUnlock();
+ configs_lock_.WriterLock();
+ SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second.get(),
+ primary_config_.get());
+ configs_lock_.WriterUnlock();
+ configs_lock_.ReaderLock();
+ }
+
+ if (old_primary_config != nullptr) {
+ configs->primary = old_primary_config;
+ } else {
+ configs->primary = primary_config_;
+ }
+ configs->requested = GetConfigWithScid(requested_scid);
+ configs->fallback = fallback_config_;
+
+ return true;
+}
+
+// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for
+// Config's based on their primary_time.
+// static
+bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan(
+ const QuicReferenceCountedPointer<Config>& a,
+ const QuicReferenceCountedPointer<Config>& b) {
+ if (a->primary_time.IsBefore(b->primary_time) ||
+ b->primary_time.IsBefore(a->primary_time)) {
+ // Primary times differ.
+ return a->primary_time.IsBefore(b->primary_time);
+ } else if (a->priority != b->priority) {
+ // Primary times are equal, sort backwards by priority.
+ return a->priority < b->priority;
+ } else {
+ // Primary times and priorities are equal, sort by config id.
+ return a->id < b->id;
+ }
+}
+
+void QuicCryptoServerConfig::SelectNewPrimaryConfig(
+ const QuicWallTime now) const {
+ std::vector<QuicReferenceCountedPointer<Config>> configs;
+ configs.reserve(configs_.size());
+
+ for (auto it = configs_.begin(); it != configs_.end(); ++it) {
+ // TODO(avd) Exclude expired configs?
+ configs.push_back(it->second);
+ }
+
+ if (configs.empty()) {
+ if (primary_config_ != nullptr) {
+ QUIC_BUG << "No valid QUIC server config. Keeping the current config.";
+ } else {
+ QUIC_BUG << "No valid QUIC server config.";
+ }
+ return;
+ }
+
+ std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);
+
+ QuicReferenceCountedPointer<Config> best_candidate = configs[0];
+
+ for (size_t i = 0; i < configs.size(); ++i) {
+ const QuicReferenceCountedPointer<Config> config(configs[i]);
+ if (!config->primary_time.IsAfter(now)) {
+ if (config->primary_time.IsAfter(best_candidate->primary_time)) {
+ best_candidate = config;
+ }
+ continue;
+ }
+
+ // This is the first config with a primary_time in the future. Thus the
+ // previous Config should be the primary and this one should determine the
+ // next_config_promotion_time_.
+ QuicReferenceCountedPointer<Config> new_primary = best_candidate;
+ if (i == 0) {
+ // We need the primary_time of the next config.
+ if (configs.size() > 1) {
+ next_config_promotion_time_ = configs[1]->primary_time;
+ } else {
+ next_config_promotion_time_ = QuicWallTime::Zero();
+ }
+ } else {
+ next_config_promotion_time_ = config->primary_time;
+ }
+
+ if (primary_config_) {
+ primary_config_->is_primary = false;
+ }
+ primary_config_ = new_primary;
+ new_primary->is_primary = true;
+ QUIC_DLOG(INFO) << "New primary config. orbit: "
+ << QuicTextUtils::HexEncode(reinterpret_cast<const char*>(
+ primary_config_->orbit),
+ kOrbitSize);
+ if (primary_config_changed_cb_ != nullptr) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
+
+ return;
+ }
+
+ // All config's primary times are in the past. We should make the most recent
+ // and highest priority candidate primary.
+ QuicReferenceCountedPointer<Config> new_primary = best_candidate;
+ if (primary_config_) {
+ primary_config_->is_primary = false;
+ }
+ primary_config_ = new_primary;
+ new_primary->is_primary = true;
+ QUIC_DLOG(INFO) << "New primary config. orbit: "
+ << QuicTextUtils::HexEncode(
+ reinterpret_cast<const char*>(primary_config_->orbit),
+ kOrbitSize)
+ << " scid: " << QuicTextUtils::HexEncode(primary_config_->id);
+ next_config_promotion_time_ = QuicWallTime::Zero();
+ if (primary_config_changed_cb_ != nullptr) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
+}
+
+void QuicCryptoServerConfig::EvaluateClientHello(
+ const QuicSocketAddress& server_address,
+ QuicTransportVersion version,
+ const Configs& configs,
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ client_hello_state,
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const {
+ ValidateClientHelloHelper helper(client_hello_state, &done_cb);
+
+ const CryptoHandshakeMessage& client_hello = client_hello_state->client_hello;
+ ClientHelloInfo* info = &(client_hello_state->info);
+
+ if (validate_chlo_size_ && client_hello.size() < kClientHelloMinimumSize) {
+ helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH,
+ "Client hello too small", nullptr);
+ return;
+ }
+
+ if (client_hello.GetStringPiece(kSNI, &info->sni) &&
+ !QuicHostnameUtils::IsValidSNI(info->sni)) {
+ helper.ValidationComplete(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Invalid SNI name", nullptr);
+ return;
+ }
+
+ client_hello.GetStringPiece(kUAID, &info->user_agent_id);
+
+ HandshakeFailureReason source_address_token_error = MAX_FAILURE_REASON;
+ if (validate_source_address_token_) {
+ QuicStringPiece srct;
+ if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) {
+ Config& config =
+ configs.requested != nullptr ? *configs.requested : *configs.primary;
+ source_address_token_error =
+ ParseSourceAddressToken(config, srct, &info->source_address_tokens);
+
+ if (source_address_token_error == HANDSHAKE_OK) {
+ source_address_token_error = ValidateSourceAddressTokens(
+ info->source_address_tokens, info->client_ip, info->now,
+ &client_hello_state->cached_network_params);
+ }
+ info->valid_source_address_token =
+ (source_address_token_error == HANDSHAKE_OK);
+ } else {
+ source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE;
+ }
+ } else {
+ source_address_token_error = HANDSHAKE_OK;
+ info->valid_source_address_token = true;
+ }
+
+ if (!configs.requested) {
+ QuicStringPiece requested_scid;
+ if (client_hello.GetStringPiece(kSCID, &requested_scid)) {
+ info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
+ } else {
+ info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
+ }
+ // No server config with the requested ID.
+ helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr);
+ return;
+ }
+
+ if (!client_hello.GetStringPiece(kNONC, &info->client_nonce)) {
+ info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
+ // Report no client nonce as INCHOATE_HELLO_FAILURE.
+ helper.ValidationComplete(QUIC_NO_ERROR, "", nullptr);
+ return;
+ }
+
+ if (source_address_token_error != HANDSHAKE_OK) {
+ info->reject_reasons.push_back(source_address_token_error);
+ // No valid source address token.
+ }
+
+ QuicReferenceCountedPointer<ProofSource::Chain> chain =
+ proof_source_->GetCertChain(server_address, std::string(info->sni));
+ if (!chain) {
+ info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
+ } else if (!ValidateExpectedLeafCertificate(client_hello, chain->certs)) {
+ info->reject_reasons.push_back(INVALID_EXPECTED_LEAF_CERTIFICATE);
+ }
+
+ if (info->client_nonce.size() != kNonceSize) {
+ info->reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE);
+ // Invalid client nonce.
+ QUIC_LOG_FIRST_N(ERROR, 2)
+ << "Invalid client nonce: " << client_hello.DebugString();
+ QUIC_DLOG(INFO) << "Invalid client nonce.";
+ }
+
+ // Server nonce is optional, and used for key derivation if present.
+ client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce);
+
+ QUIC_DVLOG(1) << "No 0-RTT replay protection in QUIC_VERSION_33 and higher.";
+ // If the server nonce is empty and we're requiring handshake confirmation
+ // for DoS reasons then we must reject the CHLO.
+ if (GetQuicReloadableFlag(quic_require_handshake_confirmation) &&
+ info->server_nonce.empty()) {
+ info->reject_reasons.push_back(SERVER_NONCE_REQUIRED_FAILURE);
+ }
+ helper.ValidationComplete(QUIC_NO_ERROR, "",
+ std::unique_ptr<ProofSource::Details>());
+}
+
+void QuicCryptoServerConfig::BuildServerConfigUpdateMessage(
+ QuicTransportVersion version,
+ QuicStringPiece chlo_hash,
+ const SourceAddressTokens& previous_source_address_tokens,
+ const QuicSocketAddress& server_address,
+ const QuicIpAddress& client_ip,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicCryptoNegotiatedParameters& params,
+ const CachedNetworkParameters* cached_network_params,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const {
+ std::string serialized;
+ std::string source_address_token;
+ const CommonCertSets* common_cert_sets;
+ {
+ QuicReaderMutexLock locked(&configs_lock_);
+ serialized = primary_config_->serialized;
+ common_cert_sets = primary_config_->common_cert_sets;
+ source_address_token = NewSourceAddressToken(
+ *primary_config_, previous_source_address_tokens, client_ip, rand,
+ clock->WallNow(), cached_network_params);
+ }
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kSCUP);
+ message.SetStringPiece(kSCFG, serialized);
+ message.SetStringPiece(kSourceAddressTokenTag, source_address_token);
+
+ auto proof_source_cb =
+ QuicMakeUnique<BuildServerConfigUpdateMessageProofSourceCallback>(
+ this, compressed_certs_cache, common_cert_sets, params,
+ std::move(message), std::move(cb));
+
+ proof_source_->GetProof(server_address, params.sni, serialized, version,
+ chlo_hash, std::move(proof_source_cb));
+}
+
+QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
+ ~BuildServerConfigUpdateMessageProofSourceCallback() {}
+
+QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
+ BuildServerConfigUpdateMessageProofSourceCallback(
+ const QuicCryptoServerConfig* config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const CommonCertSets* common_cert_sets,
+ const QuicCryptoNegotiatedParameters& params,
+ CryptoHandshakeMessage message,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb)
+ : config_(config),
+ compressed_certs_cache_(compressed_certs_cache),
+ common_cert_sets_(common_cert_sets),
+ client_common_set_hashes_(params.client_common_set_hashes),
+ client_cached_cert_hashes_(params.client_cached_cert_hashes),
+ sct_supported_by_client_(params.sct_supported_by_client),
+ sni_(params.sni),
+ message_(std::move(message)),
+ cb_(std::move(cb)) {}
+
+void QuicCryptoServerConfig::BuildServerConfigUpdateMessageProofSourceCallback::
+ Run(bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<ProofSource::Details> details) {
+ config_->FinishBuildServerConfigUpdateMessage(
+ compressed_certs_cache_, common_cert_sets_, client_common_set_hashes_,
+ client_cached_cert_hashes_, sct_supported_by_client_, sni_, ok, chain,
+ proof.signature, proof.leaf_cert_scts, std::move(details),
+ std::move(message_), std::move(cb_));
+}
+
+void QuicCryptoServerConfig::FinishBuildServerConfigUpdateMessage(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const CommonCertSets* common_cert_sets,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes,
+ bool sct_supported_by_client,
+ const std::string& sni,
+ bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& signature,
+ const std::string& leaf_cert_sct,
+ std::unique_ptr<ProofSource::Details> details,
+ CryptoHandshakeMessage message,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const {
+ if (!ok) {
+ cb->Run(false, message);
+ return;
+ }
+
+ const std::string compressed =
+ CompressChain(compressed_certs_cache, chain, client_common_set_hashes,
+ client_cached_cert_hashes, common_cert_sets);
+
+ message.SetStringPiece(kCertificateTag, compressed);
+ message.SetStringPiece(kPROF, signature);
+ if (sct_supported_by_client && enable_serving_sct_) {
+ if (leaf_cert_sct.empty()) {
+ QUIC_LOG_EVERY_N_SEC(WARNING, 60)
+ << "SCT is expected but it is empty. SNI: " << sni;
+ } else {
+ message.SetStringPiece(kCertificateSCTTag, leaf_cert_sct);
+ }
+ }
+
+ cb->Run(true, message);
+}
+
+void QuicCryptoServerConfig::BuildRejectionAndRecordStats(
+ const ProcessClientHelloContext& context,
+ const Config& config,
+ const std::vector<uint32_t>& reject_reasons,
+ CryptoHandshakeMessage* out) const {
+ BuildRejection(context, config, reject_reasons, out);
+ if (rejection_observer_ != nullptr) {
+ rejection_observer_->OnRejectionBuilt(reject_reasons, out);
+ }
+}
+
+void QuicCryptoServerConfig::BuildRejection(
+ const ProcessClientHelloContext& context,
+ const Config& config,
+ const std::vector<uint32_t>& reject_reasons,
+ CryptoHandshakeMessage* out) const {
+ const QuicWallTime now = context.clock()->WallNow();
+ if (GetQuicReloadableFlag(enable_quic_stateless_reject_support) &&
+ context.use_stateless_rejects()) {
+ QUIC_DVLOG(1) << "QUIC Crypto server config returning stateless reject "
+ << "with server-designated connection ID "
+ << context.server_designated_connection_id();
+ out->set_tag(kSREJ);
+ if (!QuicUtils::IsConnectionIdValidForVersion(
+ context.server_designated_connection_id(),
+ context.transport_version())) {
+ QUIC_BUG << "Tried to send server designated connection ID "
+ << context.server_designated_connection_id()
+ << " which is invalid with version "
+ << QuicVersionToString(context.transport_version());
+ return;
+ }
+ out->SetStringPiece(
+ kRCID,
+ QuicStringPiece(context.server_designated_connection_id().data(),
+ context.server_designated_connection_id().length()));
+ } else {
+ out->set_tag(kREJ);
+ }
+ out->SetStringPiece(kSCFG, config.serialized);
+ out->SetStringPiece(
+ kSourceAddressTokenTag,
+ NewSourceAddressToken(
+ config, context.info().source_address_tokens,
+ context.info().client_ip, context.rand(), context.info().now,
+ &context.validate_chlo_result()->cached_network_params));
+ out->SetValue(kSTTL, config.expiry_time.AbsoluteDifference(now).ToSeconds());
+ if (replay_protection_) {
+ out->SetStringPiece(kServerNonceTag,
+ NewServerNonce(context.rand(), context.info().now));
+ }
+
+ // Send client the reject reason for debugging purposes.
+ DCHECK_LT(0u, reject_reasons.size());
+ out->SetVector(kRREJ, reject_reasons);
+
+ // The client may have requested a certificate chain.
+ if (!ClientDemandsX509Proof(context.client_hello())) {
+ QUIC_BUG << "x509 certificates not supported in proof demand";
+ return;
+ }
+
+ QuicStringPiece client_common_set_hashes;
+ if (context.client_hello().GetStringPiece(kCCS, &client_common_set_hashes)) {
+ context.params()->client_common_set_hashes =
+ std::string(client_common_set_hashes);
+ }
+
+ QuicStringPiece client_cached_cert_hashes;
+ if (context.client_hello().GetStringPiece(kCCRT,
+ &client_cached_cert_hashes)) {
+ context.params()->client_cached_cert_hashes =
+ std::string(client_cached_cert_hashes);
+ } else {
+ context.params()->client_cached_cert_hashes.clear();
+ }
+
+ const std::string compressed = CompressChain(
+ context.compressed_certs_cache(), context.signed_config()->chain,
+ context.params()->client_common_set_hashes,
+ context.params()->client_cached_cert_hashes, config.common_cert_sets);
+
+ DCHECK_GT(context.chlo_packet_size(), context.client_hello().size());
+ // kREJOverheadBytes is a very rough estimate of how much of a REJ
+ // message is taken up by things other than the certificates.
+ // STK: 56 bytes
+ // SNO: 56 bytes
+ // SCFG
+ // SCID: 16 bytes
+ // PUBS: 38 bytes
+ const size_t kREJOverheadBytes = 166;
+ // max_unverified_size is the number of bytes that the certificate chain,
+ // signature, and (optionally) signed certificate timestamp can consume before
+ // we will demand a valid source-address token.
+ const size_t max_unverified_size =
+ chlo_multiplier_ *
+ (context.chlo_packet_size() - context.total_framing_overhead()) -
+ kREJOverheadBytes;
+ static_assert(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes,
+ "overhead calculation may underflow");
+ bool should_return_sct =
+ context.params()->sct_supported_by_client && enable_serving_sct_;
+ const std::string& cert_sct = context.signed_config()->proof.leaf_cert_scts;
+ const size_t sct_size = should_return_sct ? cert_sct.size() : 0;
+ const size_t total_size = context.signed_config()->proof.signature.size() +
+ compressed.size() + sct_size;
+ if (context.info().valid_source_address_token ||
+ total_size < max_unverified_size) {
+ out->SetStringPiece(kCertificateTag, compressed);
+ out->SetStringPiece(kPROF, context.signed_config()->proof.signature);
+ if (should_return_sct) {
+ if (cert_sct.empty()) {
+ if (!GetQuicReloadableFlag(quic_log_cert_name_for_empty_sct)) {
+ QUIC_LOG_EVERY_N_SEC(WARNING, 60)
+ << "SCT is expected but it is empty. sni :"
+ << context.params()->sni;
+ } else {
+ // Log SNI and subject name for the leaf cert if its SCT is empty.
+ // This is for debugging b/28342827.
+ const std::vector<std::string>& certs =
+ context.signed_config()->chain->certs;
+ QuicStringPiece ca_subject;
+ if (!certs.empty()) {
+ QuicCertUtils::ExtractSubjectNameFromDERCert(certs[0], &ca_subject);
+ }
+ QUIC_LOG_EVERY_N_SEC(WARNING, 60)
+ << "SCT is expected but it is empty. sni: '"
+ << context.params()->sni << "' cert subject: '" << ca_subject
+ << "'";
+ }
+ } else {
+ out->SetStringPiece(kCertificateSCTTag, cert_sct);
+ }
+ }
+ } else {
+ QUIC_LOG_EVERY_N_SEC(WARNING, 60)
+ << "Sending inchoate REJ for hostname: " << context.info().sni
+ << " signature: " << context.signed_config()->proof.signature.size()
+ << " cert: " << compressed.size() << " sct:" << sct_size
+ << " total: " << total_size << " max: " << max_unverified_size;
+ }
+}
+
+std::string QuicCryptoServerConfig::CompressChain(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes,
+ const CommonCertSets* common_sets) {
+ // Check whether the compressed certs is available in the cache.
+ DCHECK(compressed_certs_cache);
+ const std::string* cached_value = compressed_certs_cache->GetCompressedCert(
+ chain, client_common_set_hashes, client_cached_cert_hashes);
+ if (cached_value) {
+ return *cached_value;
+ }
+ std::string compressed =
+ CertCompressor::CompressChain(chain->certs, client_common_set_hashes,
+ client_cached_cert_hashes, common_sets);
+ // Insert the newly compressed cert to cache.
+ compressed_certs_cache->Insert(chain, client_common_set_hashes,
+ client_cached_cert_hashes, compressed);
+ return compressed;
+}
+
+QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfig::ParseConfigProtobuf(
+ const QuicServerConfigProtobuf& protobuf,
+ bool is_fallback) const {
+ std::unique_ptr<CryptoHandshakeMessage> msg =
+ CryptoFramer::ParseMessage(protobuf.config());
+
+ if (msg->tag() != kSCFG) {
+ QUIC_LOG(WARNING) << "Server config message has tag " << msg->tag()
+ << " expected " << kSCFG;
+ return nullptr;
+ }
+
+ QuicReferenceCountedPointer<Config> config(new Config);
+ config->serialized = protobuf.config();
+ config->source_address_token_boxer = &source_address_token_boxer_;
+
+ if (protobuf.has_primary_time()) {
+ config->primary_time =
+ QuicWallTime::FromUNIXSeconds(protobuf.primary_time());
+ }
+
+ config->priority = protobuf.priority();
+
+ QuicStringPiece scid;
+ if (!msg->GetStringPiece(kSCID, &scid)) {
+ QUIC_LOG(WARNING) << "Server config message is missing SCID";
+ return nullptr;
+ }
+ config->id = std::string(scid);
+
+ if (msg->GetTaglist(kAEAD, &config->aead) != QUIC_NO_ERROR) {
+ QUIC_LOG(WARNING) << "Server config message is missing AEAD";
+ return nullptr;
+ }
+
+ QuicTagVector kexs_tags;
+ if (msg->GetTaglist(kKEXS, &kexs_tags) != QUIC_NO_ERROR) {
+ QUIC_LOG(WARNING) << "Server config message is missing KEXS";
+ return nullptr;
+ }
+
+ QuicErrorCode err;
+ if ((err = msg->GetTaglist(kTBKP, &config->tb_key_params)) !=
+ QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND &&
+ err != QUIC_NO_ERROR) {
+ QUIC_LOG(WARNING) << "Server config message is missing or has invalid TBKP";
+ return nullptr;
+ }
+
+ QuicStringPiece orbit;
+ if (!msg->GetStringPiece(kORBT, &orbit)) {
+ QUIC_LOG(WARNING) << "Server config message is missing ORBT";
+ return nullptr;
+ }
+
+ if (orbit.size() != kOrbitSize) {
+ QUIC_LOG(WARNING) << "Orbit value in server config is the wrong length."
+ " Got "
+ << orbit.size() << " want " << kOrbitSize;
+ return nullptr;
+ }
+ static_assert(sizeof(config->orbit) == kOrbitSize, "incorrect orbit size");
+ memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
+
+ if ((kexs_tags.size() != static_cast<size_t>(protobuf.key_size())) &&
+ (!GetQuicRestartFlag(dont_fetch_quic_private_keys_from_leto) &&
+ protobuf.key_size() == 0)) {
+ QUIC_LOG(WARNING) << "Server config has " << kexs_tags.size()
+ << " key exchange methods configured, but "
+ << protobuf.key_size() << " private keys";
+ return nullptr;
+ }
+
+ QuicTagVector proof_demand_tags;
+ if (msg->GetTaglist(kPDMD, &proof_demand_tags) == QUIC_NO_ERROR) {
+ for (QuicTag tag : proof_demand_tags) {
+ if (tag == kCHID) {
+ config->channel_id_enabled = true;
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < kexs_tags.size(); i++) {
+ const QuicTag tag = kexs_tags[i];
+ std::string private_key;
+
+ config->kexs.push_back(tag);
+
+ for (int j = 0; j < protobuf.key_size(); j++) {
+ const QuicServerConfigProtobuf::PrivateKey& key = protobuf.key(i);
+ if (key.tag() == tag) {
+ private_key = key.private_key();
+ break;
+ }
+ }
+
+ std::unique_ptr<AsynchronousKeyExchange> ka =
+ key_exchange_source_->Create(config->id, is_fallback, tag, private_key);
+ if (!ka) {
+ return nullptr;
+ }
+ for (const auto& key_exchange : config->key_exchanges) {
+ if (key_exchange->type() == tag) {
+ QUIC_LOG(WARNING) << "Duplicate key exchange in config: " << tag;
+ return nullptr;
+ }
+ }
+
+ config->key_exchanges.push_back(std::move(ka));
+ }
+
+ uint64_t expiry_seconds;
+ if (msg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
+ QUIC_LOG(WARNING) << "Server config message is missing EXPY";
+ return nullptr;
+ }
+ config->expiry_time = QuicWallTime::FromUNIXSeconds(expiry_seconds);
+
+ return config;
+}
+
+void QuicCryptoServerConfig::set_replay_protection(bool on) {
+ replay_protection_ = on;
+}
+
+void QuicCryptoServerConfig::set_chlo_multiplier(size_t multiplier) {
+ chlo_multiplier_ = multiplier;
+}
+
+void QuicCryptoServerConfig::set_source_address_token_future_secs(
+ uint32_t future_secs) {
+ source_address_token_future_secs_ = future_secs;
+}
+
+void QuicCryptoServerConfig::set_source_address_token_lifetime_secs(
+ uint32_t lifetime_secs) {
+ source_address_token_lifetime_secs_ = lifetime_secs;
+}
+
+void QuicCryptoServerConfig::set_enable_serving_sct(bool enable_serving_sct) {
+ enable_serving_sct_ = enable_serving_sct;
+}
+
+void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb(
+ std::unique_ptr<PrimaryConfigChangedCallback> cb) {
+ QuicWriterMutexLock locked(&configs_lock_);
+ primary_config_changed_cb_ = std::move(cb);
+}
+
+std::string QuicCryptoServerConfig::NewSourceAddressToken(
+ const Config& config,
+ const SourceAddressTokens& previous_tokens,
+ const QuicIpAddress& ip,
+ QuicRandom* rand,
+ QuicWallTime now,
+ const CachedNetworkParameters* cached_network_params) const {
+ SourceAddressTokens source_address_tokens;
+ SourceAddressToken* source_address_token = source_address_tokens.add_tokens();
+ source_address_token->set_ip(ip.DualStacked().ToPackedString());
+ source_address_token->set_timestamp(now.ToUNIXSeconds());
+ if (cached_network_params != nullptr) {
+ *(source_address_token->mutable_cached_network_parameters()) =
+ *cached_network_params;
+ }
+
+ // Append previous tokens.
+ for (const SourceAddressToken& token : previous_tokens.tokens()) {
+ if (source_address_tokens.tokens_size() > kMaxTokenAddresses) {
+ break;
+ }
+
+ if (token.ip() == source_address_token->ip()) {
+ // It's for the same IP address.
+ continue;
+ }
+
+ if (ValidateSourceAddressTokenTimestamp(token, now) != HANDSHAKE_OK) {
+ continue;
+ }
+
+ *(source_address_tokens.add_tokens()) = token;
+ }
+
+ return config.source_address_token_boxer->Box(
+ rand, source_address_tokens.SerializeAsString());
+}
+
+int QuicCryptoServerConfig::NumberOfConfigs() const {
+ QuicReaderMutexLock locked(&configs_lock_);
+ return configs_.size();
+}
+
+ProofSource* QuicCryptoServerConfig::proof_source() const {
+ return proof_source_.get();
+}
+
+SSL_CTX* QuicCryptoServerConfig::ssl_ctx() const {
+ return ssl_ctx_.get();
+}
+
+HandshakeFailureReason QuicCryptoServerConfig::ParseSourceAddressToken(
+ const Config& config,
+ QuicStringPiece token,
+ SourceAddressTokens* tokens) const {
+ std::string storage;
+ QuicStringPiece plaintext;
+ if (!config.source_address_token_boxer->Unbox(token, &storage, &plaintext)) {
+ return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE;
+ }
+
+ if (!tokens->ParseFromArray(plaintext.data(), plaintext.size())) {
+ // Some clients might still be using the old source token format so
+ // attempt to parse that format.
+ // TODO(rch): remove this code once the new format is ubiquitous.
+ SourceAddressToken token;
+ if (!token.ParseFromArray(plaintext.data(), plaintext.size())) {
+ return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
+ }
+ *tokens->add_tokens() = token;
+ }
+
+ return HANDSHAKE_OK;
+}
+
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens(
+ const SourceAddressTokens& source_address_tokens,
+ const QuicIpAddress& ip,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params) const {
+ HandshakeFailureReason reason =
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
+ for (const SourceAddressToken& token : source_address_tokens.tokens()) {
+ reason = ValidateSingleSourceAddressToken(token, ip, now);
+ if (reason == HANDSHAKE_OK) {
+ if (token.has_cached_network_parameters()) {
+ *cached_network_params = token.cached_network_parameters();
+ }
+ break;
+ }
+ }
+ return reason;
+}
+
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSingleSourceAddressToken(
+ const SourceAddressToken& source_address_token,
+ const QuicIpAddress& ip,
+ QuicWallTime now) const {
+ if (source_address_token.ip() != ip.DualStacked().ToPackedString()) {
+ // It's for a different IP address.
+ return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
+ }
+
+ return ValidateSourceAddressTokenTimestamp(source_address_token, now);
+}
+
+HandshakeFailureReason
+QuicCryptoServerConfig::ValidateSourceAddressTokenTimestamp(
+ const SourceAddressToken& source_address_token,
+ QuicWallTime now) const {
+ const QuicWallTime timestamp(
+ QuicWallTime::FromUNIXSeconds(source_address_token.timestamp()));
+ const QuicTime::Delta delta(now.AbsoluteDifference(timestamp));
+
+ if (now.IsBefore(timestamp) &&
+ delta.ToSeconds() > source_address_token_future_secs_) {
+ return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE;
+ }
+
+ if (now.IsAfter(timestamp) &&
+ delta.ToSeconds() > source_address_token_lifetime_secs_) {
+ return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE;
+ }
+
+ return HANDSHAKE_OK;
+}
+
+// kServerNoncePlaintextSize is the number of bytes in an unencrypted server
+// nonce.
+static const size_t kServerNoncePlaintextSize =
+ 4 /* timestamp */ + 20 /* random bytes */;
+
+std::string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand,
+ QuicWallTime now) const {
+ const uint32_t timestamp = static_cast<uint32_t>(now.ToUNIXSeconds());
+
+ uint8_t server_nonce[kServerNoncePlaintextSize];
+ static_assert(sizeof(server_nonce) > sizeof(timestamp), "nonce too small");
+ server_nonce[0] = static_cast<uint8_t>(timestamp >> 24);
+ server_nonce[1] = static_cast<uint8_t>(timestamp >> 16);
+ server_nonce[2] = static_cast<uint8_t>(timestamp >> 8);
+ server_nonce[3] = static_cast<uint8_t>(timestamp);
+ rand->RandBytes(&server_nonce[sizeof(timestamp)],
+ sizeof(server_nonce) - sizeof(timestamp));
+
+ return server_nonce_boxer_.Box(
+ rand, QuicStringPiece(reinterpret_cast<char*>(server_nonce),
+ sizeof(server_nonce)));
+}
+
+bool QuicCryptoServerConfig::ValidateExpectedLeafCertificate(
+ const CryptoHandshakeMessage& client_hello,
+ const std::vector<std::string>& certs) const {
+ if (certs.empty()) {
+ return false;
+ }
+
+ uint64_t hash_from_client;
+ if (client_hello.GetUint64(kXLCT, &hash_from_client) != QUIC_NO_ERROR) {
+ return false;
+ }
+ return CryptoUtils::ComputeLeafCertHash(certs.at(0)) == hash_from_client;
+}
+
+bool QuicCryptoServerConfig::IsNextConfigReady(QuicWallTime now) const {
+ return !next_config_promotion_time_.IsZero() &&
+ !next_config_promotion_time_.IsAfter(now);
+}
+
+QuicCryptoServerConfig::Config::Config()
+ : channel_id_enabled(false),
+ is_primary(false),
+ primary_time(QuicWallTime::Zero()),
+ expiry_time(QuicWallTime::Zero()),
+ priority(0),
+ source_address_token_boxer(nullptr) {}
+
+QuicCryptoServerConfig::Config::~Config() {}
+
+QuicSignedServerConfig::QuicSignedServerConfig() {}
+QuicSignedServerConfig::~QuicSignedServerConfig() {}
+
+} // 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
new file mode 100644
index 00000000000..7f3f947a313
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h
@@ -0,0 +1,984 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "third_party/boringssl/src/include/openssl/base.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/key_exchange.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_proof.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/proto/source_address_token.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class CryptoHandshakeMessage;
+class ProofSource;
+class QuicClock;
+class QuicRandom;
+class QuicServerConfigProtobuf;
+struct QuicSignedServerConfig;
+
+// ClientHelloInfo contains information about a client hello message that is
+// only kept for as long as it's being processed.
+struct ClientHelloInfo {
+ ClientHelloInfo(const QuicIpAddress& in_client_ip, QuicWallTime in_now);
+ ClientHelloInfo(const ClientHelloInfo& other);
+ ~ClientHelloInfo();
+
+ // Inputs to EvaluateClientHello.
+ const QuicIpAddress client_ip;
+ const QuicWallTime now;
+
+ // Outputs from EvaluateClientHello.
+ bool valid_source_address_token;
+ QuicStringPiece sni;
+ QuicStringPiece client_nonce;
+ QuicStringPiece server_nonce;
+ QuicStringPiece user_agent_id;
+ SourceAddressTokens source_address_tokens;
+
+ // Errors from EvaluateClientHello.
+ std::vector<uint32_t> reject_reasons;
+ static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync");
+};
+
+namespace test {
+class QuicCryptoServerConfigPeer;
+} // namespace test
+
+// Hook that allows application code to subscribe to primary config changes.
+class PrimaryConfigChangedCallback {
+ public:
+ PrimaryConfigChangedCallback();
+ PrimaryConfigChangedCallback(const PrimaryConfigChangedCallback&) = delete;
+ PrimaryConfigChangedCallback& operator=(const PrimaryConfigChangedCallback&) =
+ delete;
+ virtual ~PrimaryConfigChangedCallback();
+ virtual void Run(const std::string& scid) = 0;
+};
+
+// Callback used to accept the result of the |client_hello| validation step.
+class QUIC_EXPORT_PRIVATE ValidateClientHelloResultCallback {
+ public:
+ // Opaque token that holds information about the client_hello and
+ // its validity. Can be interpreted by calling ProcessClientHello.
+ struct QUIC_EXPORT_PRIVATE Result : public QuicReferenceCounted {
+ Result(const CryptoHandshakeMessage& in_client_hello,
+ QuicIpAddress in_client_ip,
+ QuicWallTime in_now);
+
+ CryptoHandshakeMessage client_hello;
+ ClientHelloInfo info;
+ QuicErrorCode error_code;
+ std::string error_details;
+
+ // Populated if the CHLO STK contained a CachedNetworkParameters proto.
+ CachedNetworkParameters cached_network_params;
+
+ protected:
+ ~Result() override;
+ };
+
+ ValidateClientHelloResultCallback();
+ ValidateClientHelloResultCallback(const ValidateClientHelloResultCallback&) =
+ delete;
+ ValidateClientHelloResultCallback& operator=(
+ const ValidateClientHelloResultCallback&) = delete;
+ virtual ~ValidateClientHelloResultCallback();
+ virtual void Run(QuicReferenceCountedPointer<Result> result,
+ std::unique_ptr<ProofSource::Details> details) = 0;
+};
+
+// Callback used to accept the result of the ProcessClientHello method.
+class QUIC_EXPORT_PRIVATE ProcessClientHelloResultCallback {
+ public:
+ ProcessClientHelloResultCallback();
+ ProcessClientHelloResultCallback(const ProcessClientHelloResultCallback&) =
+ delete;
+ ProcessClientHelloResultCallback& operator=(
+ const ProcessClientHelloResultCallback&) = delete;
+ virtual ~ProcessClientHelloResultCallback();
+ virtual void Run(QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> details) = 0;
+};
+
+// Callback used to receive the results of a call to
+// BuildServerConfigUpdateMessage.
+class BuildServerConfigUpdateMessageResultCallback {
+ public:
+ BuildServerConfigUpdateMessageResultCallback() = default;
+ virtual ~BuildServerConfigUpdateMessageResultCallback() {}
+ BuildServerConfigUpdateMessageResultCallback(
+ const BuildServerConfigUpdateMessageResultCallback&) = delete;
+ BuildServerConfigUpdateMessageResultCallback& operator=(
+ const BuildServerConfigUpdateMessageResultCallback&) = delete;
+ virtual void Run(bool ok, const CryptoHandshakeMessage& message) = 0;
+};
+
+// Object that is interested in built rejections (which include REJ, SREJ and
+// cheap SREJ).
+class RejectionObserver {
+ public:
+ RejectionObserver() = default;
+ virtual ~RejectionObserver() {}
+ RejectionObserver(const RejectionObserver&) = delete;
+ RejectionObserver& operator=(const RejectionObserver&) = delete;
+ // Called after a rejection is built.
+ virtual void OnRejectionBuilt(const std::vector<uint32_t>& reasons,
+ CryptoHandshakeMessage* out) const = 0;
+};
+
+// Factory for creating KeyExchange objects.
+class QUIC_EXPORT_PRIVATE KeyExchangeSource {
+ public:
+ virtual ~KeyExchangeSource() = default;
+
+ // Returns the default KeyExchangeSource.
+ static std::unique_ptr<KeyExchangeSource> Default();
+
+ // Create a new KeyExchange using the curve specified by |type| using the
+ // specified private key. |private_key| may be empty for key-exchange
+ // mechanisms which do not hold the private key in-process. If |is_fallback|
+ // is set, |private_key| is required to be set, and a local key-exchange
+ // object should be returned.
+ virtual std::unique_ptr<AsynchronousKeyExchange> Create(
+ std::string server_config_id,
+ bool is_fallback,
+ QuicTag type,
+ QuicStringPiece private_key) = 0;
+};
+
+// QuicCryptoServerConfig contains the crypto configuration of a QUIC server.
+// Unlike a client, a QUIC server can have multiple configurations active in
+// order to support clients resuming with a previous configuration.
+// TODO(agl): when adding configurations at runtime is added, this object will
+// need to consider locking.
+class QUIC_EXPORT_PRIVATE QuicCryptoServerConfig {
+ public:
+ // ConfigOptions contains options for generating server configs.
+ struct QUIC_EXPORT_PRIVATE ConfigOptions {
+ ConfigOptions();
+ ConfigOptions(const ConfigOptions& other);
+ ~ConfigOptions();
+
+ // expiry_time is the time, in UNIX seconds, when the server config will
+ // expire. If unset, it defaults to the current time plus six months.
+ QuicWallTime expiry_time;
+ // channel_id_enabled controls whether the server config will indicate
+ // support for ChannelIDs.
+ bool channel_id_enabled;
+ // token_binding_params contains the list of Token Binding params (e.g.
+ // P256, TB10) that the server config will include.
+ QuicTagVector token_binding_params;
+ // id contains the server config id for the resulting config. If empty, a
+ // random id is generated.
+ std::string id;
+ // orbit contains the kOrbitSize bytes of the orbit value for the server
+ // config. If |orbit| is empty then a random orbit is generated.
+ std::string orbit;
+ // p256 determines whether a P-256 public key will be included in the
+ // server config. Note that this breaks deterministic server-config
+ // generation since P-256 key generation doesn't use the QuicRandom given
+ // to DefaultConfig().
+ bool p256;
+ };
+
+ // |source_address_token_secret|: secret key material used for encrypting and
+ // decrypting source address tokens. It can be of any length as it is fed
+ // into a KDF before use. In tests, use TESTING.
+ // |server_nonce_entropy|: an entropy source used to generate the orbit and
+ // key for server nonces, which are always local to a given instance of a
+ // server. Not owned.
+ // |proof_source|: provides certificate chains and signatures.
+ // |key_exchange_source|: provides key-exchange functionality.
+ // |ssl_ctx|: The SSL_CTX used for doing TLS handshakes.
+ QuicCryptoServerConfig(QuicStringPiece source_address_token_secret,
+ QuicRandom* server_nonce_entropy,
+ std::unique_ptr<ProofSource> proof_source,
+ std::unique_ptr<KeyExchangeSource> key_exchange_source,
+ bssl::UniquePtr<SSL_CTX> ssl_ctx);
+ QuicCryptoServerConfig(const QuicCryptoServerConfig&) = delete;
+ QuicCryptoServerConfig& operator=(const QuicCryptoServerConfig&) = delete;
+ ~QuicCryptoServerConfig();
+
+ // TESTING is a magic parameter for passing to the constructor in tests.
+ static const char TESTING[];
+
+ // Generates a QuicServerConfigProtobuf protobuf suitable for
+ // AddConfig and SetConfigs.
+ static QuicServerConfigProtobuf GenerateConfig(QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options);
+
+ // AddConfig adds a QuicServerConfigProtobuf to the available configurations.
+ // It returns the SCFG message from the config if successful. |now| is used in
+ // conjunction with |protobuf->primary_time()| to determine whether the
+ // config should be made primary.
+ std::unique_ptr<CryptoHandshakeMessage> AddConfig(
+ const QuicServerConfigProtobuf& protobuf,
+ QuicWallTime now);
+
+ // AddDefaultConfig calls DefaultConfig to create a config and then calls
+ // AddConfig to add it. See the comment for |DefaultConfig| for details of
+ // the arguments.
+ std::unique_ptr<CryptoHandshakeMessage> AddDefaultConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options);
+
+ // SetConfigs takes a vector of config protobufs and the current time.
+ // Configs are assumed to be uniquely identified by their server config ID.
+ // Previously unknown configs are added and possibly made the primary config
+ // depending on their |primary_time| and the value of |now|. Configs that are
+ // known, but are missing from the protobufs are deleted, unless they are
+ // currently the primary config. SetConfigs returns false if any errors were
+ // encountered and no changes to the QuicCryptoServerConfig will occur.
+ bool SetConfigs(const std::vector<QuicServerConfigProtobuf>& protobufs,
+ const QuicServerConfigProtobuf* fallback_protobuf,
+ QuicWallTime now);
+
+ // SetSourceAddressTokenKeys sets the keys to be tried, in order, when
+ // decrypting a source address token. Note that these keys are used *without*
+ // passing them through a KDF, in contradistinction to the
+ // |source_address_token_secret| argument to the constructor.
+ void SetSourceAddressTokenKeys(const std::vector<std::string>& keys);
+
+ // Get the server config ids for all known configs.
+ void GetConfigIds(std::vector<std::string>* scids) const;
+
+ // Checks |client_hello| for gross errors and determines whether it can be
+ // shown to be fresh (i.e. not a replay). The result of the validation step
+ // must be interpreted by calling QuicCryptoServerConfig::ProcessClientHello
+ // from the done_cb.
+ //
+ // ValidateClientHello may invoke the done_cb before unrolling the
+ // stack if it is able to assess the validity of the client_nonce
+ // without asynchronous operations.
+ //
+ // client_hello: the incoming client hello message.
+ // client_ip: the IP address of the client, which is used to generate and
+ // validate source-address tokens.
+ // server_address: the IP address and port of the server. The IP address and
+ // port may be used for certificate selection.
+ // version: protocol version used for this connection.
+ // clock: used to validate client nonces and ephemeral keys.
+ // crypto_proof: in/out parameter to which will be written the crypto proof
+ // used in reply to a proof demand. The pointed-to-object must
+ // live until the callback is invoked.
+ // done_cb: single-use callback that accepts an opaque
+ // ValidatedClientHelloMsg token that holds information about
+ // the client hello. The callback will always be called exactly
+ // once, either under the current call stack, or after the
+ // completion of an asynchronous operation.
+ void ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ const QuicIpAddress& client_ip,
+ const QuicSocketAddress& server_address,
+ QuicTransportVersion version,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> crypto_proof,
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const;
+
+ // ProcessClientHello processes |client_hello| and decides whether to accept
+ // or reject the connection. If the connection is to be accepted, |done_cb| is
+ // invoked with the contents of the ServerHello and QUIC_NO_ERROR. Otherwise
+ // |done_cb| is called with a REJ or SREJ message and QUIC_NO_ERROR.
+ //
+ // validate_chlo_result: Output from the asynchronous call to
+ // ValidateClientHello. Contains the client hello message and
+ // information about it.
+ // reject_only: Only generate rejections, not server hello messages.
+ // connection_id: the ConnectionId for the connection, which is used in key
+ // derivation.
+ // server_ip: the IP address of the server. The IP address may be used for
+ // certificate selection.
+ // client_address: the IP address and port of the client. The IP address is
+ // used to generate and validate source-address tokens.
+ // version: version of the QUIC protocol in use for this connection
+ // supported_versions: versions of the QUIC protocol that this server
+ // supports.
+ // clock: used to validate client nonces and ephemeral keys.
+ // rand: an entropy source
+ // compressed_certs_cache: the cache that caches a set of most recently used
+ // certs. Owned by QuicDispatcher.
+ // params: the state of the handshake. This may be updated with a server
+ // nonce when we send a rejection.
+ // crypto_proof: output structure containing the crypto proof used in reply to
+ // a proof demand.
+ // total_framing_overhead: the total per-packet overhead for a stream frame
+ // chlo_packet_size: the size, in bytes, of the CHLO packet
+ // done_cb: the callback invoked on completion
+ void ProcessClientHello(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ validate_chlo_result,
+ bool reject_only,
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions,
+ bool use_stateless_rejects,
+ QuicConnectionId server_designated_connection_id,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> crypto_proof,
+ QuicByteCount total_framing_overhead,
+ QuicByteCount chlo_packet_size,
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb) const;
+
+ // BuildServerConfigUpdateMessage invokes |cb| with a SCUP message containing
+ // the current primary config, an up to date source-address token, and cert
+ // chain and proof in the case of secure QUIC. Passes true to |cb| if the
+ // message was generated successfully, and false otherwise. This method
+ // assumes ownership of |cb|.
+ //
+ // |cached_network_params| is optional, and can be nullptr.
+ void BuildServerConfigUpdateMessage(
+ QuicTransportVersion version,
+ QuicStringPiece chlo_hash,
+ const SourceAddressTokens& previous_source_address_tokens,
+ const QuicSocketAddress& server_address,
+ const QuicIpAddress& client_ip,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicCryptoNegotiatedParameters& params,
+ const CachedNetworkParameters* cached_network_params,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const;
+
+ // set_replay_protection controls whether replay protection is enabled. If
+ // replay protection is disabled then no strike registers are needed and
+ // frontends can share an orbit value without a shared strike-register.
+ // However, an attacker can duplicate a handshake and cause a client's
+ // request to be processed twice.
+ void set_replay_protection(bool on);
+
+ // set_chlo_multiplier specifies the multiple of the CHLO message size
+ // that a REJ message must stay under when the client doesn't present a
+ // valid source-address token.
+ void set_chlo_multiplier(size_t multiplier);
+
+ // When sender is allowed to not pad client hello (not standards compliant),
+ // we need to disable the client hello check.
+ void set_validate_chlo_size(bool new_value) {
+ validate_chlo_size_ = new_value;
+ }
+
+ // Returns whether the sender is allowed to not pad the client hello.
+ bool validate_chlo_size() const { return validate_chlo_size_; }
+
+ // When QUIC is tunneled through some other mechanism, source token validation
+ // may be disabled. Do not disable it if you are not providing other
+ // protection. (|true| protects against UDP amplification attack.).
+ void set_validate_source_address_token(bool new_value) {
+ validate_source_address_token_ = new_value;
+ }
+
+ // set_source_address_token_future_secs sets the number of seconds into the
+ // future that source-address tokens will be accepted from. Since
+ // source-address tokens are authenticated, this should only happen if
+ // another, valid server has clock-skew.
+ void set_source_address_token_future_secs(uint32_t future_secs);
+
+ // set_source_address_token_lifetime_secs sets the number of seconds that a
+ // source-address token will be valid for.
+ void set_source_address_token_lifetime_secs(uint32_t lifetime_secs);
+
+ // set_enable_serving_sct enables or disables serving signed cert timestamp
+ // (RFC6962) in server hello.
+ void set_enable_serving_sct(bool enable_serving_sct);
+
+ // Set and take ownership of the callback to invoke on primary config changes.
+ void AcquirePrimaryConfigChangedCb(
+ std::unique_ptr<PrimaryConfigChangedCallback> cb);
+
+ // Returns the number of configs this object owns.
+ int NumberOfConfigs() const;
+
+ // Callers retain the ownership of |rejection_observer| which must outlive the
+ // config.
+ void set_rejection_observer(RejectionObserver* rejection_observer) {
+ rejection_observer_ = rejection_observer;
+ }
+
+ ProofSource* proof_source() const;
+
+ SSL_CTX* ssl_ctx() const;
+
+ void set_pre_shared_key(QuicStringPiece psk) {
+ pre_shared_key_ = std::string(psk);
+ }
+
+ bool pad_rej() const { return pad_rej_; }
+ void set_pad_rej(bool new_value) { pad_rej_ = new_value; }
+
+ bool pad_shlo() const { return pad_shlo_; }
+ void set_pad_shlo(bool new_value) { pad_shlo_ = new_value; }
+
+ private:
+ friend class test::QuicCryptoServerConfigPeer;
+ friend struct QuicSignedServerConfig;
+
+ // Config represents a server config: a collection of preferences and
+ // Diffie-Hellman public values.
+ class QUIC_EXPORT_PRIVATE Config : public QuicCryptoConfig,
+ public QuicReferenceCounted {
+ public:
+ Config();
+ Config(const Config&) = delete;
+ Config& operator=(const Config&) = delete;
+
+ // TODO(rtenneti): since this is a class, we should probably do
+ // getters/setters here.
+ // |serialized| contains the bytes of this server config, suitable for
+ // sending on the wire.
+ std::string serialized;
+ // id contains the SCID of this server config.
+ std::string id;
+ // orbit contains the orbit value for this config: an opaque identifier
+ // used to identify clusters of server frontends.
+ unsigned char orbit[kOrbitSize];
+
+ // key_exchanges contains key exchange objects. The values correspond,
+ // 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;
+
+ // is_primary is true if this config is the one that we'll give out to
+ // clients as the current one.
+ bool is_primary;
+
+ // primary_time contains the timestamp when this config should become the
+ // primary config. A value of QuicWallTime::Zero() means that this config
+ // will not be promoted at a specific time.
+ QuicWallTime primary_time;
+
+ // expiry_time contains the timestamp when this config expires.
+ QuicWallTime expiry_time;
+
+ // Secondary sort key for use when selecting primary configs and
+ // there are multiple configs with the same primary time.
+ // Smaller numbers mean higher priority.
+ uint64_t priority;
+
+ // source_address_token_boxer_ is used to protect the
+ // source-address tokens that are given to clients.
+ // Points to either source_address_token_boxer_storage or the
+ // default boxer provided by QuicCryptoServerConfig.
+ const CryptoSecretBoxer* source_address_token_boxer;
+
+ // Holds the override source_address_token_boxer instance if the
+ // Config is not using the default source address token boxer
+ // instance provided by QuicCryptoServerConfig.
+ std::unique_ptr<CryptoSecretBoxer> source_address_token_boxer_storage;
+
+ private:
+ ~Config() override;
+ };
+
+ typedef std::map<ServerConfigID, QuicReferenceCountedPointer<Config>>
+ ConfigMap;
+
+ // Get a ref to the config with a given server config id.
+ QuicReferenceCountedPointer<Config> GetConfigWithScid(
+ QuicStringPiece requested_scid) const
+ SHARED_LOCKS_REQUIRED(configs_lock_);
+
+ // A snapshot of the configs associated with an in-progress handshake.
+ struct Configs {
+ QuicReferenceCountedPointer<Config> requested;
+ QuicReferenceCountedPointer<Config> primary;
+ QuicReferenceCountedPointer<Config> fallback;
+ };
+
+ // Get a snapshot of the current configs associated with a handshake. If this
+ // method was called earlier in this handshake |old_primary_config| should be
+ // set to the primary config returned from that invocation, otherwise nullptr.
+ //
+ // Returns true if any configs are loaded. If false is returned, |configs| is
+ // not modified.
+ bool GetCurrentConfigs(const QuicWallTime& now,
+ QuicStringPiece requested_scid,
+ QuicReferenceCountedPointer<Config> old_primary_config,
+ Configs* configs) const;
+
+ // ConfigPrimaryTimeLessThan returns true if a->primary_time <
+ // b->primary_time.
+ static bool ConfigPrimaryTimeLessThan(
+ const QuicReferenceCountedPointer<Config>& a,
+ const QuicReferenceCountedPointer<Config>& b);
+
+ // SelectNewPrimaryConfig reevaluates the primary config based on the
+ // "primary_time" deadlines contained in each.
+ void SelectNewPrimaryConfig(QuicWallTime now) const
+ EXCLUSIVE_LOCKS_REQUIRED(configs_lock_);
+
+ // EvaluateClientHello checks |client_hello_state->client_hello| for gross
+ // errors and determines whether it is fresh (i.e. not a replay). The results
+ // are written to |client_hello_state->info|.
+ void EvaluateClientHello(
+ const QuicSocketAddress& server_address,
+ QuicTransportVersion version,
+ const Configs& configs,
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ client_hello_state,
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb) const;
+
+ // Convenience class which carries the arguments passed to
+ // |ProcessClientHellp| along.
+ class ProcessClientHelloContext {
+ public:
+ ProcessClientHelloContext(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ validate_chlo_result,
+ bool reject_only,
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ ParsedQuicVersion version,
+ const ParsedQuicVersionVector& supported_versions,
+ bool use_stateless_rejects,
+ QuicConnectionId server_designated_connection_id,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicByteCount total_framing_overhead,
+ QuicByteCount chlo_packet_size,
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb)
+ : validate_chlo_result_(validate_chlo_result),
+ reject_only_(reject_only),
+ connection_id_(connection_id),
+ server_address_(server_address),
+ client_address_(client_address),
+ version_(version),
+ supported_versions_(supported_versions),
+ use_stateless_rejects_(use_stateless_rejects),
+ server_designated_connection_id_(server_designated_connection_id),
+ clock_(clock),
+ rand_(rand),
+ compressed_certs_cache_(compressed_certs_cache),
+ params_(params),
+ signed_config_(signed_config),
+ total_framing_overhead_(total_framing_overhead),
+ chlo_packet_size_(chlo_packet_size),
+ done_cb_(std::move(done_cb)) {}
+
+ ~ProcessClientHelloContext();
+
+ // Invoke |done_cb_| with an error status
+ void Fail(QuicErrorCode error, const std::string& error_details);
+
+ // Invoke |done_cb_| with a success status
+ void Succeed(std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details);
+
+ // Member accessors
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ validate_chlo_result() const {
+ return validate_chlo_result_;
+ }
+ bool reject_only() const { return reject_only_; }
+ QuicConnectionId connection_id() const { return connection_id_; }
+ QuicSocketAddress server_address() const { return server_address_; }
+ QuicSocketAddress client_address() const { return client_address_; }
+ ParsedQuicVersion version() const { return version_; }
+ ParsedQuicVersionVector supported_versions() const {
+ return supported_versions_;
+ }
+ bool use_stateless_rejects() const { return use_stateless_rejects_; }
+ QuicConnectionId server_designated_connection_id() const {
+ return server_designated_connection_id_;
+ }
+ const QuicClock* clock() const { return clock_; }
+ QuicRandom* rand() const { return rand_; } // NOLINT
+ QuicCompressedCertsCache* compressed_certs_cache() const {
+ return compressed_certs_cache_;
+ }
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params() const {
+ return params_;
+ }
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config() const {
+ return signed_config_;
+ }
+ QuicByteCount total_framing_overhead() const {
+ return total_framing_overhead_;
+ }
+ QuicByteCount chlo_packet_size() const { return chlo_packet_size_; }
+
+ // Derived value accessors
+ const CryptoHandshakeMessage& client_hello() const {
+ return validate_chlo_result()->client_hello;
+ }
+ const ClientHelloInfo& info() const { return validate_chlo_result()->info; }
+ QuicTransportVersion transport_version() const {
+ return version().transport_version;
+ }
+
+ private:
+ const QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ validate_chlo_result_;
+ const bool reject_only_;
+ const QuicConnectionId connection_id_;
+ const QuicSocketAddress server_address_;
+ const QuicSocketAddress client_address_;
+ const ParsedQuicVersion version_;
+ const ParsedQuicVersionVector supported_versions_;
+ const bool use_stateless_rejects_;
+ const QuicConnectionId server_designated_connection_id_;
+ const QuicClock* const clock_;
+ QuicRandom* const rand_;
+ QuicCompressedCertsCache* const compressed_certs_cache_;
+ const QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ const QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ const QuicByteCount total_framing_overhead_;
+ const QuicByteCount chlo_packet_size_;
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb_;
+ };
+
+ // Callback class for bridging between ProcessClientHello and
+ // ProcessClientHelloAfterGetProof.
+ class ProcessClientHelloCallback;
+ friend class ProcessClientHelloCallback;
+
+ // Portion of ProcessClientHello which executes after GetProof.
+ void ProcessClientHelloAfterGetProof(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs) const;
+
+ // Callback class for bridging between ProcessClientHelloAfterGetProof and
+ // ProcessClientHelloAfterCalculateSharedKeys.
+ class ProcessClientHelloAfterGetProofCallback;
+ friend class ProcessClientHelloAfterGetProofCallback;
+
+ // Portion of ProcessClientHello which executes after CalculateSharedKeys.
+ void ProcessClientHelloAfterCalculateSharedKeys(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ QuicTag key_exchange_type,
+ std::unique_ptr<CryptoHandshakeMessage> out,
+ QuicStringPiece public_value,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ const Configs& configs) const;
+
+ // Send a REJ which contains a different ServerConfig than the one the client
+ // originally used. This is necessary in cases where we discover in the
+ // middle of the handshake that the private key for the ServerConfig the
+ // client used is not accessible.
+ void SendRejectWithFallbackConfig(
+ std::unique_ptr<ProcessClientHelloContext> context,
+ QuicReferenceCountedPointer<Config> fallback_config) const;
+
+ // Callback class for bridging between SendRejectWithFallbackConfig and
+ // SendRejectWithFallbackConfigAfterGetProof.
+ class SendRejectWithFallbackConfigCallback;
+ friend class SendRejectWithFallbackConfigCallback;
+
+ // Portion of ProcessClientHello which executes after GetProof in the case
+ // where we have received a CHLO but need to reject it due to the ServerConfig
+ // private keys being inaccessible.
+ void SendRejectWithFallbackConfigAfterGetProof(
+ bool found_error,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloContext> context,
+ QuicReferenceCountedPointer<Config> fallback_config) const;
+
+ // BuildRejectionAndRecordStats calls |BuildRejection| below and also informs
+ // the RejectionObserver.
+ void BuildRejectionAndRecordStats(const ProcessClientHelloContext& context,
+ const Config& config,
+ const std::vector<uint32_t>& reject_reasons,
+ CryptoHandshakeMessage* out) const;
+
+ // BuildRejection sets |out| to be a REJ message in reply to |client_hello|.
+ void BuildRejection(const ProcessClientHelloContext& context,
+ const Config& config,
+ const std::vector<uint32_t>& reject_reasons,
+ CryptoHandshakeMessage* out) const;
+
+ // CompressChain compresses the certificates in |chain->certs| and returns a
+ // compressed representation. |common_sets| contains the common certificate
+ // sets known locally and |client_common_set_hashes| contains the hashes of
+ // the common sets known to the peer. |client_cached_cert_hashes| contains
+ // 64-bit, FNV-1a hashes of certificates that the peer already possesses.
+ static std::string CompressChain(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes,
+ const CommonCertSets* common_sets);
+
+ // ParseConfigProtobuf parses the given config protobuf and returns a
+ // QuicReferenceCountedPointer<Config> if successful. The caller adopts the
+ // reference to the Config. On error, ParseConfigProtobuf returns nullptr.
+ QuicReferenceCountedPointer<Config> ParseConfigProtobuf(
+ const QuicServerConfigProtobuf& protobuf,
+ bool is_fallback) const;
+
+ // NewSourceAddressToken returns a fresh source address token for the given
+ // IP address. |cached_network_params| is optional, and can be nullptr.
+ std::string NewSourceAddressToken(
+ const Config& config,
+ const SourceAddressTokens& previous_tokens,
+ const QuicIpAddress& ip,
+ QuicRandom* rand,
+ QuicWallTime now,
+ const CachedNetworkParameters* cached_network_params) const;
+
+ // ParseSourceAddressToken parses the source address tokens contained in
+ // the encrypted |token|, and populates |tokens| with the parsed tokens.
+ // Returns HANDSHAKE_OK if |token| could be parsed, or the reason for the
+ // failure.
+ HandshakeFailureReason ParseSourceAddressToken(
+ const Config& config,
+ QuicStringPiece token,
+ SourceAddressTokens* tokens) const;
+
+ // ValidateSourceAddressTokens returns HANDSHAKE_OK if the source address
+ // tokens in |tokens| contain a valid and timely token for the IP address
+ // |ip| given that the current time is |now|. Otherwise it returns the
+ // reason for failure. |cached_network_params| is populated if the valid
+ // token contains a CachedNetworkParameters proto.
+ HandshakeFailureReason ValidateSourceAddressTokens(
+ const SourceAddressTokens& tokens,
+ const QuicIpAddress& ip,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params) const;
+
+ // ValidateSingleSourceAddressToken returns HANDSHAKE_OK if the source
+ // address token in |token| is a timely token for the IP address |ip|
+ // given that the current time is |now|. Otherwise it returns the reason
+ // for failure.
+ HandshakeFailureReason ValidateSingleSourceAddressToken(
+ const SourceAddressToken& token,
+ const QuicIpAddress& ip,
+ QuicWallTime now) const;
+
+ // Returns HANDSHAKE_OK if the source address token in |token| is a timely
+ // token given that the current time is |now|. Otherwise it returns the
+ // reason for failure.
+ HandshakeFailureReason ValidateSourceAddressTokenTimestamp(
+ const SourceAddressToken& token,
+ QuicWallTime now) const;
+
+ // NewServerNonce generates and encrypts a random nonce.
+ std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const;
+
+ // ValidateExpectedLeafCertificate checks the |client_hello| to see if it has
+ // an XLCT tag, and if so, verifies that its value matches the hash of the
+ // server's leaf certificate. |certs| is used to compare against the XLCT
+ // value. This method returns true if the XLCT tag is not present, or if the
+ // XLCT tag is present and valid. It returns false otherwise.
+ bool ValidateExpectedLeafCertificate(
+ const CryptoHandshakeMessage& client_hello,
+ const std::vector<std::string>& certs) const;
+
+ // Callback to receive the results of ProofSource::GetProof. Note: this
+ // callback has no cancellation support, since the lifetime of the ProofSource
+ // is controlled by this object via unique ownership. If that ownership
+ // stricture changes, this decision may need to be revisited.
+ class BuildServerConfigUpdateMessageProofSourceCallback
+ : public ProofSource::Callback {
+ public:
+ BuildServerConfigUpdateMessageProofSourceCallback(
+ const BuildServerConfigUpdateMessageProofSourceCallback&) = delete;
+ ~BuildServerConfigUpdateMessageProofSourceCallback() override;
+ void operator=(const BuildServerConfigUpdateMessageProofSourceCallback&) =
+ delete;
+ BuildServerConfigUpdateMessageProofSourceCallback(
+ const QuicCryptoServerConfig* config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const CommonCertSets* common_cert_sets,
+ const QuicCryptoNegotiatedParameters& params,
+ CryptoHandshakeMessage message,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb);
+
+ void Run(bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& proof,
+ std::unique_ptr<ProofSource::Details> details) override;
+
+ private:
+ const QuicCryptoServerConfig* config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+ const CommonCertSets* common_cert_sets_;
+ const std::string client_common_set_hashes_;
+ const std::string client_cached_cert_hashes_;
+ const bool sct_supported_by_client_;
+ const std::string sni_;
+ CryptoHandshakeMessage message_;
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb_;
+ };
+
+ // Invoked by BuildServerConfigUpdateMessageProofSourceCallback::Run once
+ // the proof has been acquired. Finishes building the server config update
+ // message and invokes |cb|.
+ void FinishBuildServerConfigUpdateMessage(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const CommonCertSets* common_cert_sets,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes,
+ bool sct_supported_by_client,
+ const std::string& sni,
+ bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& signature,
+ const std::string& leaf_cert_sct,
+ std::unique_ptr<ProofSource::Details> details,
+ CryptoHandshakeMessage message,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const;
+
+ // Returns true if the next config promotion should happen now.
+ bool IsNextConfigReady(QuicWallTime now) const
+ SHARED_LOCKS_REQUIRED(configs_lock_);
+
+ // replay_protection_ controls whether the server enforces that handshakes
+ // aren't replays.
+ bool replay_protection_;
+
+ // The multiple of the CHLO message size that a REJ message must stay under
+ // when the client doesn't present a valid source-address token. This is
+ // used to protect QUIC from amplification attacks.
+ size_t chlo_multiplier_;
+
+ // configs_ satisfies the following invariants:
+ // 1) configs_.empty() <-> primary_config_ == nullptr
+ // 2) primary_config_ != nullptr -> primary_config_->is_primary
+ // 3) ∀ c∈configs_, c->is_primary <-> c == primary_config_
+ mutable QuicMutex configs_lock_;
+
+ // configs_ contains all active server configs. It's expected that there are
+ // about half-a-dozen configs active at any one time.
+ ConfigMap configs_ GUARDED_BY(configs_lock_);
+
+ // primary_config_ points to a Config (which is also in |configs_|) which is
+ // the primary config - i.e. the one that we'll give out to new clients.
+ mutable QuicReferenceCountedPointer<Config> primary_config_
+ GUARDED_BY(configs_lock_);
+
+ // fallback_config_ points to a Config (which is also in |configs_|) which is
+ // the fallback config, which will be used if the other configs are unuseable
+ // for some reason.
+ //
+ // TODO(b/112548056): This is currently always nullptr.
+ QuicReferenceCountedPointer<Config> fallback_config_
+ GUARDED_BY(configs_lock_);
+
+ // next_config_promotion_time_ contains the nearest, future time when an
+ // active config will be promoted to primary.
+ mutable QuicWallTime next_config_promotion_time_ GUARDED_BY(configs_lock_);
+
+ // Callback to invoke when the primary config changes.
+ std::unique_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_
+ GUARDED_BY(configs_lock_);
+
+ // Used to protect the source-address tokens that are given to clients.
+ CryptoSecretBoxer source_address_token_boxer_;
+
+ // server_nonce_boxer_ is used to encrypt and validate suggested server
+ // nonces.
+ CryptoSecretBoxer server_nonce_boxer_;
+
+ // server_nonce_orbit_ contains the random, per-server orbit values that this
+ // server will use to generate server nonces (the moral equivalent of a SYN
+ // cookies).
+ uint8_t server_nonce_orbit_[8];
+
+ // proof_source_ contains an object that can provide certificate chains and
+ // signatures.
+ std::unique_ptr<ProofSource> proof_source_;
+
+ // key_exchange_source_ contains an object that can provide key exchange
+ // objects.
+ std::unique_ptr<KeyExchangeSource> key_exchange_source_;
+
+ // ssl_ctx_ contains the server configuration for doing TLS handshakes.
+ bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+
+ // These fields store configuration values. See the comments for their
+ // respective setter functions.
+ uint32_t source_address_token_future_secs_;
+ uint32_t source_address_token_lifetime_secs_;
+
+ // Enable serving SCT or not.
+ bool enable_serving_sct_;
+
+ // Does not own this observer.
+ RejectionObserver* rejection_observer_;
+
+ // If non-empty, the server will operate in the pre-shared key mode by
+ // incorporating |pre_shared_key_| into the key schedule.
+ std::string pre_shared_key_;
+
+ // Whether REJ message should be padded to max packet size.
+ bool pad_rej_;
+
+ // Whether SHLO message should be padded to max packet size.
+ bool pad_shlo_;
+
+ // If client is allowed to send a small client hello (by disabling padding),
+ // server MUST not check for the client hello size.
+ // DO NOT disable this unless you have some other way of validating client.
+ // (e.g. in realtime scenarios, where quic is tunneled through ICE, ICE will
+ // do its own peer validation using STUN pings with ufrag/upass).
+ bool validate_chlo_size_;
+
+ // When source address is validated by some other means (e.g. when using ICE),
+ // source address token validation may be disabled.
+ bool validate_source_address_token_;
+};
+
+struct QUIC_EXPORT_PRIVATE QuicSignedServerConfig
+ : public QuicReferenceCounted {
+ QuicSignedServerConfig();
+
+ QuicCryptoProof proof;
+ QuicReferenceCountedPointer<ProofSource::Chain> chain;
+ // The server config that is used for this proof (and the rest of the
+ // request).
+ QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> config;
+ std::string primary_scid;
+
+ protected:
+ ~QuicSignedServerConfig() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config_test.cc
new file mode 100644
index 00000000000..b3f8f77b51d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config_test.cc
@@ -0,0 +1,508 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+
+#include <stdarg.h>
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/cert_compressor.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_secret_boxer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+
+namespace quic {
+namespace test {
+using ::testing::Not;
+
+// NOTE: This matcher depends on the wire format of serialzied protocol buffers,
+// which may change in the future.
+// Switch to ::testing::EqualsProto once it is available in Chromium.
+MATCHER_P(SerializedProtoEquals, message, "") {
+ std::string expected_serialized, actual_serialized;
+ message.SerializeToString(&expected_serialized);
+ arg.SerializeToString(&actual_serialized);
+ return expected_serialized == actual_serialized;
+}
+
+class QuicCryptoServerConfigTest : public QuicTest {};
+
+TEST_F(QuicCryptoServerConfigTest, ServerConfig) {
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ MockClock clock;
+
+ std::unique_ptr<CryptoHandshakeMessage> message(server.AddDefaultConfig(
+ rand, &clock, QuicCryptoServerConfig::ConfigOptions()));
+
+ // The default configuration should have AES-GCM and at least one ChaCha20
+ // cipher.
+ QuicTagVector aead;
+ ASSERT_EQ(QUIC_NO_ERROR, message->GetTaglist(kAEAD, &aead));
+ EXPECT_THAT(aead, ::testing::Contains(kAESG));
+ EXPECT_LE(1u, aead.size());
+}
+
+TEST_F(QuicCryptoServerConfigTest, CompressCerts) {
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicCryptoServerConfigPeer peer(&server);
+
+ std::vector<std::string> certs = {"testcert"};
+ QuicReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+
+ std::string compressed = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, "", "", nullptr);
+
+ EXPECT_EQ(compressed_certs_cache.Size(), 1u);
+}
+
+TEST_F(QuicCryptoServerConfigTest, CompressSameCertsTwice) {
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicCryptoServerConfigPeer peer(&server);
+
+ // Compress the certs for the first time.
+ std::vector<std::string> certs = {"testcert"};
+ QuicReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+ std::string common_certs = "";
+ std::string cached_certs = "";
+
+ std::string compressed = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, common_certs, cached_certs, nullptr);
+ EXPECT_EQ(compressed_certs_cache.Size(), 1u);
+
+ // Compress the same certs, should use cache if available.
+ std::string compressed2 = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, common_certs, cached_certs, nullptr);
+ EXPECT_EQ(compressed, compressed2);
+ EXPECT_EQ(compressed_certs_cache.Size(), 1u);
+}
+
+TEST_F(QuicCryptoServerConfigTest, CompressDifferentCerts) {
+ // This test compresses a set of similar but not identical certs. Cache if
+ // used should return cache miss and add all the compressed certs.
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicCryptoServerConfigPeer peer(&server);
+
+ std::vector<std::string> certs = {"testcert"};
+ QuicReferenceCountedPointer<ProofSource::Chain> chain(
+ new ProofSource::Chain(certs));
+ std::string common_certs = "";
+ std::string cached_certs = "";
+
+ std::string compressed = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, common_certs, cached_certs, nullptr);
+ EXPECT_EQ(compressed_certs_cache.Size(), 1u);
+
+ // Compress a similar certs which only differs in the chain.
+ QuicReferenceCountedPointer<ProofSource::Chain> chain2(
+ new ProofSource::Chain(certs));
+
+ std::string compressed2 = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain2, common_certs, cached_certs, nullptr);
+ EXPECT_EQ(compressed_certs_cache.Size(), 2u);
+
+ // Compress a similar certs which only differs in common certs field.
+ static const uint64_t set_hash = 42;
+ std::unique_ptr<CommonCertSets> common_sets(
+ crypto_test_utils::MockCommonCertSets(certs[0], set_hash, 1));
+ QuicStringPiece different_common_certs(
+ reinterpret_cast<const char*>(&set_hash), sizeof(set_hash));
+ std::string compressed3 = QuicCryptoServerConfigPeer::CompressChain(
+ &compressed_certs_cache, chain, std::string(different_common_certs),
+ cached_certs, common_sets.get());
+ EXPECT_EQ(compressed_certs_cache.Size(), 3u);
+}
+
+class SourceAddressTokenTest : public QuicTest {
+ public:
+ SourceAddressTokenTest()
+ : ip4_(QuicIpAddress::Loopback4()),
+ ip4_dual_(ip4_.DualStacked()),
+ ip6_(QuicIpAddress::Loopback6()),
+ original_time_(QuicWallTime::Zero()),
+ rand_(QuicRandom::GetInstance()),
+ server_(QuicCryptoServerConfig::TESTING,
+ rand_,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ peer_(&server_) {
+ // Advance the clock to some non-zero time.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000000));
+ original_time_ = clock_.WallNow();
+
+ primary_config_ = server_.AddDefaultConfig(
+ rand_, &clock_, QuicCryptoServerConfig::ConfigOptions());
+ }
+
+ std::string NewSourceAddressToken(std::string config_id,
+ const QuicIpAddress& ip) {
+ return NewSourceAddressToken(config_id, ip, nullptr);
+ }
+
+ std::string NewSourceAddressToken(
+ std::string config_id,
+ const QuicIpAddress& ip,
+ const SourceAddressTokens& previous_tokens) {
+ return peer_.NewSourceAddressToken(config_id, previous_tokens, ip, rand_,
+ clock_.WallNow(), nullptr);
+ }
+
+ std::string NewSourceAddressToken(
+ std::string config_id,
+ const QuicIpAddress& ip,
+ CachedNetworkParameters* cached_network_params) {
+ SourceAddressTokens previous_tokens;
+ return peer_.NewSourceAddressToken(config_id, previous_tokens, ip, rand_,
+ clock_.WallNow(), cached_network_params);
+ }
+
+ HandshakeFailureReason ValidateSourceAddressTokens(std::string config_id,
+ QuicStringPiece srct,
+ const QuicIpAddress& ip) {
+ return ValidateSourceAddressTokens(config_id, srct, ip, nullptr);
+ }
+
+ HandshakeFailureReason ValidateSourceAddressTokens(
+ std::string config_id,
+ QuicStringPiece srct,
+ const QuicIpAddress& ip,
+ CachedNetworkParameters* cached_network_params) {
+ return peer_.ValidateSourceAddressTokens(
+ config_id, srct, ip, clock_.WallNow(), cached_network_params);
+ }
+
+ const std::string kPrimary = "<primary>";
+ const std::string kOverride = "Config with custom source address token key";
+
+ QuicIpAddress ip4_;
+ QuicIpAddress ip4_dual_;
+ QuicIpAddress ip6_;
+
+ MockClock clock_;
+ QuicWallTime original_time_;
+ QuicRandom* rand_ = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server_;
+ QuicCryptoServerConfigPeer peer_;
+ // Stores the primary config.
+ std::unique_ptr<CryptoHandshakeMessage> primary_config_;
+ std::unique_ptr<QuicServerConfigProtobuf> override_config_protobuf_;
+};
+
+// Test basic behavior of source address tokens including being specific
+// to a single IP address and server config.
+TEST_F(SourceAddressTokenTest, SourceAddressToken) {
+ // Primary config generates configs that validate successfully.
+ const std::string token4 = NewSourceAddressToken(kPrimary, ip4_);
+ const std::string token4d = NewSourceAddressToken(kPrimary, ip4_dual_);
+ const std::string token6 = NewSourceAddressToken(kPrimary, ip6_);
+ EXPECT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token4, ip4_));
+ ASSERT_EQ(HANDSHAKE_OK,
+ ValidateSourceAddressTokens(kPrimary, token4, ip4_dual_));
+ ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ ValidateSourceAddressTokens(kPrimary, token4, ip6_));
+ ASSERT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token4d, ip4_));
+ ASSERT_EQ(HANDSHAKE_OK,
+ ValidateSourceAddressTokens(kPrimary, token4d, ip4_dual_));
+ ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ ValidateSourceAddressTokens(kPrimary, token4d, ip6_));
+ ASSERT_EQ(HANDSHAKE_OK, ValidateSourceAddressTokens(kPrimary, token6, ip6_));
+}
+
+TEST_F(SourceAddressTokenTest, SourceAddressTokenExpiration) {
+ const std::string token = NewSourceAddressToken(kPrimary, ip4_);
+
+ // Validation fails if the token is from the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(-3600 * 2));
+ ASSERT_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE,
+ ValidateSourceAddressTokens(kPrimary, token, ip4_));
+
+ // Validation fails after tokens expire.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(86400 * 7));
+ ASSERT_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE,
+ ValidateSourceAddressTokens(kPrimary, token, ip4_));
+}
+
+TEST_F(SourceAddressTokenTest, SourceAddressTokenWithNetworkParams) {
+ // Make sure that if the source address token contains CachedNetworkParameters
+ // that this gets written to ValidateSourceAddressToken output argument.
+ CachedNetworkParameters cached_network_params_input;
+ cached_network_params_input.set_bandwidth_estimate_bytes_per_second(1234);
+ const std::string token4_with_cached_network_params =
+ NewSourceAddressToken(kPrimary, ip4_, &cached_network_params_input);
+
+ CachedNetworkParameters cached_network_params_output;
+ EXPECT_THAT(cached_network_params_output,
+ Not(SerializedProtoEquals(cached_network_params_input)));
+ ValidateSourceAddressTokens(kPrimary, token4_with_cached_network_params, ip4_,
+ &cached_network_params_output);
+ EXPECT_THAT(cached_network_params_output,
+ SerializedProtoEquals(cached_network_params_input));
+}
+
+// Test the ability for a source address token to be valid for multiple
+// addresses.
+TEST_F(SourceAddressTokenTest, SourceAddressTokenMultipleAddresses) {
+ QuicWallTime now = clock_.WallNow();
+
+ // Now create a token which is usable for both addresses.
+ SourceAddressToken previous_token;
+ previous_token.set_ip(ip6_.DualStacked().ToPackedString());
+ previous_token.set_timestamp(now.ToUNIXSeconds());
+ SourceAddressTokens previous_tokens;
+ (*previous_tokens.add_tokens()) = previous_token;
+ const std::string token4or6 =
+ NewSourceAddressToken(kPrimary, ip4_, previous_tokens);
+
+ EXPECT_EQ(HANDSHAKE_OK,
+ ValidateSourceAddressTokens(kPrimary, token4or6, ip4_));
+ ASSERT_EQ(HANDSHAKE_OK,
+ ValidateSourceAddressTokens(kPrimary, token4or6, ip6_));
+}
+
+class CryptoServerConfigsTest : public QuicTest {
+ public:
+ CryptoServerConfigsTest()
+ : rand_(QuicRandom::GetInstance()),
+ config_(QuicCryptoServerConfig::TESTING,
+ rand_,
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ test_peer_(&config_) {}
+
+ void SetUp() override {
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000));
+ }
+
+ // SetConfigs constructs suitable config protobufs and calls SetConfigs on
+ // |config_|.
+ // Each struct in the input vector contains 3 elements.
+ // The first is the server config ID of a Config. The second is
+ // the |primary_time| of that Config, given in epoch seconds. (Although note
+ // that, in these tests, time is set to 1000 seconds since the epoch.).
+ // The third is the priority.
+ //
+ // For example:
+ // SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority>()); // calls
+ // |config_.SetConfigs| with no protobufs.
+ //
+ // // Calls |config_.SetConfigs| with two protobufs: one for a Config with
+ // // a |primary_time| of 900 and priority 1, and another with
+ // // a |primary_time| of 1000 and priority 2.
+
+ // CheckConfigs(
+ // {{"id1", 900, 1},
+ // {"id2", 1000, 2}});
+ //
+ // If the server config id starts with "INVALID" then the generated protobuf
+ // will be invalid.
+ struct ServerConfigIDWithTimeAndPriority {
+ ServerConfigID server_config_id;
+ int primary_time;
+ int priority;
+ };
+ void SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority> configs) {
+ const char kOrbit[] = "12345678";
+
+ bool has_invalid = false;
+
+ std::vector<QuicServerConfigProtobuf> protobufs;
+ for (const auto& config : configs) {
+ const ServerConfigID& server_config_id = config.server_config_id;
+ const int primary_time = config.primary_time;
+ const int priority = config.priority;
+
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.id = server_config_id;
+ options.orbit = kOrbit;
+ QuicServerConfigProtobuf protobuf =
+ QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options);
+ protobuf.set_primary_time(primary_time);
+ protobuf.set_priority(priority);
+ if (std::string(server_config_id).find("INVALID") == 0) {
+ protobuf.clear_key();
+ has_invalid = true;
+ }
+ protobufs.push_back(std::move(protobuf));
+ }
+
+ ASSERT_EQ(!has_invalid && !configs.empty(),
+ config_.SetConfigs(protobufs, /* fallback_protobuf = */ nullptr,
+ clock_.WallNow()));
+ }
+
+ protected:
+ QuicRandom* const rand_;
+ MockClock clock_;
+ QuicCryptoServerConfig config_;
+ QuicCryptoServerConfigPeer test_peer_;
+};
+
+TEST_F(CryptoServerConfigsTest, NoConfigs) {
+ test_peer_.CheckConfigs(std::vector<std::pair<std::string, bool>>());
+}
+
+TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) {
+ // Make sure that "b" is primary even though "a" comes first.
+ SetConfigs({{"a", 1100, 1}, {"b", 900, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, MakePrimarySecond) {
+ // Make sure that a remains primary after b is added.
+ SetConfigs({{"a", 900, 1}, {"b", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}});
+}
+
+TEST_F(CryptoServerConfigsTest, Delete) {
+ // Ensure that configs get deleted when removed.
+ SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}});
+ SetConfigs({{"b", 900, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"b", true}, {"c", false}});
+}
+
+TEST_F(CryptoServerConfigsTest, DeletePrimary) {
+ // Ensure that deleting the primary config works.
+ SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}});
+ SetConfigs({{"a", 800, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", true}, {"c", false}});
+}
+
+TEST_F(CryptoServerConfigsTest, FailIfDeletingAllConfigs) {
+ // Ensure that configs get deleted when removed.
+ SetConfigs({{"a", 800, 1}, {"b", 900, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+ SetConfigs(std::vector<ServerConfigIDWithTimeAndPriority>());
+ // Config change is rejected, still using old configs.
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, ChangePrimaryTime) {
+ // Check that updates to primary time get picked up.
+ SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}});
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+ SetConfigs({{"a", 1200, 1}, {"b", 800, 1}, {"c", 400, 1}});
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInThePast) {
+ // Check that the most recent config is selected.
+ SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}});
+ test_peer_.SelectNewPrimaryConfig(1500);
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInTheFuture) {
+ // Check that the first config is selected.
+ SetConfigs({{"a", 400, 1}, {"b", 800, 1}, {"c", 1200, 1}});
+ test_peer_.SelectNewPrimaryConfig(100);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+}
+
+TEST_F(CryptoServerConfigsTest, SortByPriority) {
+ // Check that priority is used to decide on a primary config when
+ // configs have the same primary time.
+ SetConfigs({{"a", 900, 1}, {"b", 900, 2}, {"c", 900, 3}});
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}, {"c", false}});
+
+ // Change priorities and expect sort order to change.
+ SetConfigs({{"a", 900, 2}, {"b", 900, 1}, {"c", 900, 0}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs({{"a", false}, {"b", false}, {"c", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, AdvancePrimary) {
+ // Check that a new primary config is enabled at the right time.
+ SetConfigs({{"a", 900, 1}, {"b", 1100, 1}});
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}});
+ test_peer_.SelectNewPrimaryConfig(1101);
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
+class ValidateCallback : public ValidateClientHelloResultCallback {
+ public:
+ void Run(QuicReferenceCountedPointer<Result> result,
+ std::unique_ptr<ProofSource::Details> /* details */) override {}
+};
+
+TEST_F(CryptoServerConfigsTest, AdvancePrimaryViaValidate) {
+ // Check that a new primary config is enabled at the right time.
+ SetConfigs({{"a", 900, 1}, {"b", 1100, 1}});
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs({{"a", true}, {"b", false}});
+ CryptoHandshakeMessage client_hello;
+ QuicIpAddress client_ip;
+ QuicSocketAddress server_address;
+ QuicTransportVersion version = QUIC_VERSION_99;
+ MockClock clock;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config(
+ new QuicSignedServerConfig);
+ std::unique_ptr<ValidateClientHelloResultCallback> done_cb(
+ new ValidateCallback);
+ clock.AdvanceTime(QuicTime::Delta::FromSeconds(1100));
+ config_.ValidateClientHello(client_hello, client_ip, server_address, version,
+ &clock, signed_config, std::move(done_cb));
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
+TEST_F(CryptoServerConfigsTest, InvalidConfigs) {
+ // Ensure that invalid configs don't change anything.
+ SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}});
+ SetConfigs({{"a", 800, 1}, {"c", 1100, 1}, {"INVALID1", 1000, 1}});
+ test_peer_.CheckConfigs({{"a", false}, {"b", true}, {"c", false}});
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.cc
new file mode 100644
index 00000000000..79369f0f064
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/tls1.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+// static
+std::unique_ptr<QuicDecrypter> QuicDecrypter::Create(QuicTag algorithm) {
+ switch (algorithm) {
+ case kAESG:
+ return QuicMakeUnique<Aes128Gcm12Decrypter>();
+ case kCC20:
+ return QuicMakeUnique<ChaCha20Poly1305Decrypter>();
+ default:
+ QUIC_LOG(FATAL) << "Unsupported algorithm: " << algorithm;
+ return nullptr;
+ }
+}
+
+// static
+std::unique_ptr<QuicDecrypter> QuicDecrypter::CreateFromCipherSuite(
+ uint32_t cipher_suite) {
+ switch (cipher_suite) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ return QuicMakeUnique<Aes128GcmDecrypter>();
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return QuicMakeUnique<Aes256GcmDecrypter>();
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return QuicMakeUnique<ChaCha20Poly1305TlsDecrypter>();
+ default:
+ QUIC_BUG << "TLS cipher suite is unknown to QUIC";
+ return nullptr;
+ }
+}
+
+// static
+void QuicDecrypter::DiversifyPreliminaryKey(QuicStringPiece preliminary_key,
+ QuicStringPiece nonce_prefix,
+ const DiversificationNonce& nonce,
+ size_t key_size,
+ size_t nonce_prefix_size,
+ std::string* out_key,
+ std::string* out_nonce_prefix) {
+ QuicHKDF hkdf((std::string(preliminary_key)) + (std::string(nonce_prefix)),
+ QuicStringPiece(nonce.data(), nonce.size()),
+ "QUIC key diversification", 0, key_size, 0, nonce_prefix_size,
+ 0);
+ *out_key = std::string(hkdf.server_write_key());
+ *out_nonce_prefix = std::string(hkdf.server_write_iv());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h
new file mode 100644
index 00000000000..559420cf423
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_DECRYPTER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicDecrypter : public QuicCrypter {
+ public:
+ virtual ~QuicDecrypter() {}
+
+ static std::unique_ptr<QuicDecrypter> Create(QuicTag algorithm);
+
+ // Creates an IETF QuicDecrypter based on |cipher_suite| which must be an id
+ // returned by SSL_CIPHER_get_id. The caller is responsible for taking
+ // ownership of the new QuicDecrypter.
+ static std::unique_ptr<QuicDecrypter> CreateFromCipherSuite(
+ uint32_t cipher_suite);
+
+ // Sets the encryption key. Returns true on success, false on failure.
+ // |DecryptPacket| may not be called until |SetDiversificationNonce| is
+ // called and the preliminary keying material will be combined with that
+ // nonce in order to create the actual key and nonce-prefix.
+ //
+ // If this function is called, neither |SetKey| nor |SetNoncePrefix| may be
+ // called.
+ virtual bool SetPreliminaryKey(QuicStringPiece key) = 0;
+
+ // SetDiversificationNonce uses |nonce| to derive final keys based on the
+ // input keying material given by calling |SetPreliminaryKey|.
+ //
+ // Calling this function is a no-op if |SetPreliminaryKey| hasn't been
+ // called.
+ virtual bool SetDiversificationNonce(const DiversificationNonce& nonce) = 0;
+
+ // Populates |output| with the decrypted |ciphertext| and populates
+ // |output_length| with the length. Returns 0 if there is an error.
+ // |output| size is specified by |max_output_length| and must be
+ // at least as large as the ciphertext. |packet_number| is
+ // appended to the |nonce_prefix| value provided in SetNoncePrefix()
+ // to form the nonce.
+ // TODO(wtc): add a way for DecryptPacket to report decryption failure due
+ // to non-authentic inputs, as opposed to other reasons for failure.
+ virtual bool DecryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) = 0;
+
+ // Reads a sample of ciphertext from |sample_reader| and uses the header
+ // protection key to generate a mask to use for header protection. If
+ // successful, this function returns this mask, which is at least 5 bytes
+ // long. Callers can detect failure by checking if the output string is empty.
+ virtual std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) = 0;
+
+ // The ID of the cipher. Return 0x03000000 ORed with the 'cryptographic suite
+ // selector'.
+ virtual uint32_t cipher_id() const = 0;
+
+ // For use by unit tests only.
+ virtual QuicStringPiece GetKey() const = 0;
+ virtual QuicStringPiece GetNoncePrefix() const = 0;
+
+ static void DiversifyPreliminaryKey(QuicStringPiece preliminary_key,
+ QuicStringPiece nonce_prefix,
+ const DiversificationNonce& nonce,
+ size_t key_size,
+ size_t nonce_prefix_size,
+ std::string* out_key,
+ std::string* out_nonce_prefix);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_DECRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.cc
new file mode 100644
index 00000000000..f264bfdf2ba
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/tls1.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_256_gcm_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha20_poly1305_tls_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+// static
+std::unique_ptr<QuicEncrypter> QuicEncrypter::Create(QuicTag algorithm) {
+ switch (algorithm) {
+ case kAESG:
+ return QuicMakeUnique<Aes128Gcm12Encrypter>();
+ case kCC20:
+ return QuicMakeUnique<ChaCha20Poly1305Encrypter>();
+ default:
+ QUIC_LOG(FATAL) << "Unsupported algorithm: " << algorithm;
+ return nullptr;
+ }
+}
+
+// static
+std::unique_ptr<QuicEncrypter> QuicEncrypter::CreateFromCipherSuite(
+ uint32_t cipher_suite) {
+ switch (cipher_suite) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ return QuicMakeUnique<Aes128GcmEncrypter>();
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return QuicMakeUnique<Aes256GcmEncrypter>();
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return QuicMakeUnique<ChaCha20Poly1305TlsEncrypter>();
+ default:
+ QUIC_BUG << "TLS cipher suite is unknown to QUIC";
+ return nullptr;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h
new file mode 100644
index 00000000000..6a69fccdc5a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_ENCRYPTER_H_
+
+#include <cstddef>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicEncrypter : public QuicCrypter {
+ public:
+ virtual ~QuicEncrypter() {}
+
+ static std::unique_ptr<QuicEncrypter> Create(QuicTag algorithm);
+
+ // Creates an IETF QuicEncrypter based on |cipher_suite| which must be an id
+ // returned by SSL_CIPHER_get_id. The caller is responsible for taking
+ // ownership of the new QuicEncrypter.
+ static std::unique_ptr<QuicEncrypter> CreateFromCipherSuite(
+ uint32_t cipher_suite);
+
+ // Writes encrypted |plaintext| and a MAC over |plaintext| and
+ // |associated_data| into output. Sets |output_length| to the number of
+ // bytes written. Returns true on success or false if there was an error.
+ // |packet_number| is appended to the |nonce_prefix| value provided in
+ // SetNoncePrefix() to form the nonce. |output| must not overlap with
+ // |associated_data|. If |output| overlaps with |plaintext| then
+ // |plaintext| must be <= |output|.
+ virtual bool EncryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) = 0;
+
+ // Takes a |sample| of ciphertext and uses the header protection key to
+ // generate a mask to use for header protection, and returns that mask. On
+ // success, the mask will be at least 5 bytes long; on failure the string will
+ // be empty.
+ virtual std::string GenerateHeaderProtectionMask(QuicStringPiece sample) = 0;
+
+ // GetKeySize() and GetNoncePrefixSize() tell the HKDF class how many bytes
+ // of key material needs to be derived from the master secret.
+ // NOTE: the sizes returned by GetKeySize() and GetNoncePrefixSize() are
+ // also correct for the QuicDecrypter of the same algorithm.
+
+ // Returns the size in bytes of the fixed initial part of the nonce.
+ virtual size_t GetNoncePrefixSize() const = 0;
+
+ // Returns the maximum length of plaintext that can be encrypted
+ // to ciphertext no larger than |ciphertext_size|.
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const = 0;
+
+ // Returns the length of the ciphertext that would be generated by encrypting
+ // to plaintext of size |plaintext_size|.
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const = 0;
+
+ // For use by unit tests only.
+ virtual QuicStringPiece GetKey() const = 0;
+ virtual QuicStringPiece GetNoncePrefix() const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_ENCRYPTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf.cc
new file mode 100644
index 00000000000..3754cab89c2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf.cc
@@ -0,0 +1,93 @@
+// 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 "net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h"
+
+#include <memory>
+
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/hkdf.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+const size_t kSHA256HashLength = 32;
+const size_t kMaxKeyMaterialSize = kSHA256HashLength * 256;
+
+QuicHKDF::QuicHKDF(QuicStringPiece secret,
+ QuicStringPiece salt,
+ QuicStringPiece info,
+ size_t key_bytes_to_generate,
+ size_t iv_bytes_to_generate,
+ size_t subkey_secret_bytes_to_generate)
+ : QuicHKDF(secret,
+ salt,
+ info,
+ key_bytes_to_generate,
+ key_bytes_to_generate,
+ iv_bytes_to_generate,
+ iv_bytes_to_generate,
+ subkey_secret_bytes_to_generate) {}
+
+QuicHKDF::QuicHKDF(QuicStringPiece secret,
+ QuicStringPiece salt,
+ QuicStringPiece info,
+ size_t client_key_bytes_to_generate,
+ size_t server_key_bytes_to_generate,
+ size_t client_iv_bytes_to_generate,
+ size_t server_iv_bytes_to_generate,
+ size_t subkey_secret_bytes_to_generate) {
+ const size_t material_length =
+ client_key_bytes_to_generate + client_iv_bytes_to_generate +
+ server_key_bytes_to_generate + server_iv_bytes_to_generate +
+ subkey_secret_bytes_to_generate;
+ DCHECK_LT(material_length, kMaxKeyMaterialSize);
+
+ output_.resize(material_length);
+ // On Windows, when the size of output_ is zero, dereference of 0'th element
+ // results in a crash. C++11 solves this problem by adding a data() getter
+ // method to std::vector.
+ if (output_.empty()) {
+ return;
+ }
+
+ ::HKDF(&output_[0], output_.size(), ::EVP_sha256(),
+ reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+ reinterpret_cast<const uint8_t*>(info.data()), info.size());
+
+ size_t j = 0;
+ if (client_key_bytes_to_generate) {
+ client_write_key_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]),
+ client_key_bytes_to_generate);
+ j += client_key_bytes_to_generate;
+ }
+
+ if (server_key_bytes_to_generate) {
+ server_write_key_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]),
+ server_key_bytes_to_generate);
+ j += server_key_bytes_to_generate;
+ }
+
+ if (client_iv_bytes_to_generate) {
+ client_write_iv_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]),
+ client_iv_bytes_to_generate);
+ j += client_iv_bytes_to_generate;
+ }
+
+ if (server_iv_bytes_to_generate) {
+ server_write_iv_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]),
+ server_iv_bytes_to_generate);
+ j += server_iv_bytes_to_generate;
+ }
+
+ if (subkey_secret_bytes_to_generate) {
+ subkey_secret_ = QuicStringPiece(reinterpret_cast<char*>(&output_[j]),
+ subkey_secret_bytes_to_generate);
+ }
+}
+
+QuicHKDF::~QuicHKDF() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h
new file mode 100644
index 00000000000..fb80f7bd1ad
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h
@@ -0,0 +1,70 @@
+// Copyright 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_CORE_CRYPTO_QUIC_HKDF_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_HKDF_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// QuicHKDF implements the key derivation function specified in RFC 5869
+// (using SHA-256) and outputs key material, as needed by QUIC.
+// See https://tools.ietf.org/html/rfc5869 for details.
+class QUIC_EXPORT QuicHKDF {
+ public:
+ // |secret|: the input shared secret (or, from RFC 5869, the IKM).
+ // |salt|: an (optional) public salt / non-secret random value. While
+ // optional, callers are strongly recommended to provide a salt. There is no
+ // added security value in making this larger than the SHA-256 block size of
+ // 64 bytes.
+ // |info|: an (optional) label to distinguish different uses of HKDF. It is
+ // optional context and application specific information (can be a zero-length
+ // string).
+ // |key_bytes_to_generate|: the number of bytes of key material to generate
+ // for both client and server.
+ // |iv_bytes_to_generate|: the number of bytes of IV to generate for both
+ // client and server.
+ // |subkey_secret_bytes_to_generate|: the number of bytes of subkey secret to
+ // generate, shared between client and server.
+ QuicHKDF(QuicStringPiece secret,
+ QuicStringPiece salt,
+ QuicStringPiece info,
+ size_t key_bytes_to_generate,
+ size_t iv_bytes_to_generate,
+ size_t subkey_secret_bytes_to_generate);
+
+ // An alternative constructor that allows the client and server key/IV
+ // lengths to be different.
+ QuicHKDF(QuicStringPiece secret,
+ QuicStringPiece salt,
+ QuicStringPiece info,
+ size_t client_key_bytes_to_generate,
+ size_t server_key_bytes_to_generate,
+ size_t client_iv_bytes_to_generate,
+ size_t server_iv_bytes_to_generate,
+ size_t subkey_secret_bytes_to_generate);
+
+ ~QuicHKDF();
+
+ QuicStringPiece client_write_key() const { return client_write_key_; }
+ QuicStringPiece client_write_iv() const { return client_write_iv_; }
+ QuicStringPiece server_write_key() const { return server_write_key_; }
+ QuicStringPiece server_write_iv() const { return server_write_iv_; }
+ QuicStringPiece subkey_secret() const { return subkey_secret_; }
+
+ private:
+ std::vector<uint8_t> output_;
+
+ QuicStringPiece client_write_key_;
+ QuicStringPiece server_write_key_;
+ QuicStringPiece client_write_iv_;
+ QuicStringPiece server_write_iv_;
+ QuicStringPiece subkey_secret_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_HKDF_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf_test.cc
new file mode 100644
index 00000000000..e40bebf62e6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_hkdf_test.cc
@@ -0,0 +1,91 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/crypto/quic_hkdf.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+struct HKDFInput {
+ const char* key_hex;
+ const char* salt_hex;
+ const char* info_hex;
+ const char* output_hex;
+};
+
+// These test cases are taken from
+// https://tools.ietf.org/html/rfc5869#appendix-A.
+static const HKDFInput kHKDFInputs[] = {
+ {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "000102030405060708090a0b0c",
+ "f0f1f2f3f4f5f6f7f8f9",
+ "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf340072"
+ "08d5"
+ "b887185865",
+ },
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122"
+ "2324"
+ "25262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041424344454647"
+ "4849"
+ "4a4b4c4d4e4f",
+ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182"
+ "8384"
+ "85868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7"
+ "a8a9"
+ "aaabacadaeaf",
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2"
+ "d3d4"
+ "d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7"
+ "f8f9"
+ "fafbfcfdfeff",
+ "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a"
+ "99ca"
+ "c7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87"
+ "c14c"
+ "01d5c1f3434f1d87",
+ },
+ {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "",
+ "",
+ "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d2013"
+ "95fa"
+ "a4b61a96c8",
+ },
+};
+
+class QuicHKDFTest : public QuicTest {};
+
+TEST_F(QuicHKDFTest, HKDF) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kHKDFInputs); i++) {
+ const HKDFInput& test(kHKDFInputs[i]);
+ SCOPED_TRACE(i);
+
+ const std::string key = QuicTextUtils::HexDecode(test.key_hex);
+ const std::string salt = QuicTextUtils::HexDecode(test.salt_hex);
+ const std::string info = QuicTextUtils::HexDecode(test.info_hex);
+ const std::string expected = QuicTextUtils::HexDecode(test.output_hex);
+
+ // We set the key_length to the length of the expected output and then take
+ // the result from the first key, which is the client write key.
+ QuicHKDF hkdf(key, salt, info, expected.size(), 0, 0);
+
+ ASSERT_EQ(expected.size(), hkdf.client_write_key().size());
+ EXPECT_EQ(0, memcmp(expected.data(), hkdf.client_write_key().data(),
+ expected.size()));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..230264c00c2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+
+#include "third_party/boringssl/src/include/openssl/rand.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+namespace {
+
+class DefaultRandom : public QuicRandom {
+ public:
+ DefaultRandom() {}
+ DefaultRandom(const DefaultRandom&) = delete;
+ DefaultRandom& operator=(const DefaultRandom&) = delete;
+ ~DefaultRandom() override {}
+
+ // QuicRandom implementation
+ void RandBytes(void* data, size_t len) override;
+ uint64_t RandUint64() override;
+};
+
+void DefaultRandom::RandBytes(void* data, size_t len) {
+ RAND_bytes(reinterpret_cast<uint8_t*>(data), len);
+}
+
+uint64_t DefaultRandom::RandUint64() {
+ uint64_t value;
+ RandBytes(&value, sizeof(value));
+ return value;
+}
+
+} // namespace
+
+// static
+QuicRandom* QuicRandom::GetInstance() {
+ static DefaultRandom* random = new DefaultRandom();
+ return random;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.h b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.h
new file mode 100644
index 00000000000..79e09534be7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_
+#define QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// The interface for a random number generator.
+class QUIC_EXPORT_PRIVATE QuicRandom {
+ public:
+ virtual ~QuicRandom() {}
+
+ // Returns the default random number generator, which is cryptographically
+ // secure and thread-safe.
+ static QuicRandom* GetInstance();
+
+ // Generates |len| random bytes in the |data| buffer.
+ virtual void RandBytes(void* data, size_t len) = 0;
+
+ // Returns a random number in the range [0, kuint64max].
+ virtual uint64_t RandUint64() = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random_test.cc
new file mode 100644
index 00000000000..d38bcf408c2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/quic_random_test.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class QuicRandomTest : public QuicTest {};
+
+TEST_F(QuicRandomTest, RandBytes) {
+ unsigned char buf1[16];
+ unsigned char buf2[16];
+ memset(buf1, 0xaf, sizeof(buf1));
+ memset(buf2, 0xaf, sizeof(buf2));
+ ASSERT_EQ(0, memcmp(buf1, buf2, sizeof(buf1)));
+
+ QuicRandom* rng = QuicRandom::GetInstance();
+ rng->RandBytes(buf1, sizeof(buf1));
+ EXPECT_NE(0, memcmp(buf1, buf2, sizeof(buf1)));
+}
+
+TEST_F(QuicRandomTest, RandUint64) {
+ QuicRandom* rng = QuicRandom::GetInstance();
+ uint64_t value1 = rng->RandUint64();
+ uint64_t value2 = rng->RandUint64();
+ EXPECT_NE(value1, value2);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc
new file mode 100644
index 00000000000..60fb22c09f7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.cc
@@ -0,0 +1,302 @@
+// 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 "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
+
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+
+namespace quic {
+
+namespace {
+
+// Values of the TransportParameterId enum as defined in
+// draft-ietf-quic-transport-08 section 7.4. When parameters are encoded, one of
+// these enum values is used to indicate which parameter is encoded.
+enum TransportParameterId : uint16_t {
+ kInitialMaxStreamDataId = 0,
+ kInitialMaxDataId = 1,
+ kInitialMaxBidiStreamsId = 2,
+ kIdleTimeoutId = 3,
+ kMaxOutgoingPacketSizeId = 5,
+ kStatelessResetTokenId = 6,
+ kAckDelayExponentId = 7,
+ kInitialMaxUniStreamsId = 8,
+
+ kMaxKnownParameterId = 9,
+};
+
+// Value for the TransportParameterId to use for non-standard Google QUIC params
+// in Transport Parameters.
+const uint16_t kGoogleQuicParamId = 18257;
+
+// The following constants define minimum and maximum allowed values for some of
+// the parameters. These come from draft-ietf-quic-transport-08 section 7.4.1.
+const uint16_t kMaxAllowedIdleTimeout = 600;
+const uint16_t kMinAllowedMaxPacketSize = 1200;
+const uint16_t kMaxAllowedMaxPacketSize = 65527;
+const uint8_t kMaxAllowedAckDelayExponent = 20;
+
+static_assert(kMaxKnownParameterId <= 32, "too many parameters to bit pack");
+
+// The initial_max_stream_data, initial_max_data, and idle_timeout parameters
+// are always required to be present. When parsing the extension, a bitmask is
+// used to keep track of which parameter have been seen so far, and that bitmask
+// will be compared to this mask to check that all of the required parameters
+// were present.
+static constexpr uint16_t kRequiredParamsMask = (1 << kInitialMaxStreamDataId) |
+ (1 << kInitialMaxDataId) |
+ (1 << kIdleTimeoutId);
+
+} // namespace
+
+TransportParameters::TransportParameters() = default;
+
+TransportParameters::~TransportParameters() = default;
+
+bool TransportParameters::is_valid() const {
+ if (perspective == Perspective::IS_CLIENT && !stateless_reset_token.empty()) {
+ return false;
+ }
+ if (perspective == Perspective::IS_SERVER &&
+ stateless_reset_token.size() != 16) {
+ return false;
+ }
+ if (idle_timeout > kMaxAllowedIdleTimeout ||
+ (max_packet_size.present &&
+ (max_packet_size.value > kMaxAllowedMaxPacketSize ||
+ max_packet_size.value < kMinAllowedMaxPacketSize)) ||
+ (ack_delay_exponent.present &&
+ ack_delay_exponent.value > kMaxAllowedAckDelayExponent)) {
+ return false;
+ }
+ return true;
+}
+
+bool SerializeTransportParameters(const TransportParameters& in,
+ std::vector<uint8_t>* out) {
+ if (!in.is_valid()) {
+ return false;
+ }
+ bssl::ScopedCBB cbb;
+ // 28 is the minimum size that the serialized TransportParameters can be,
+ // which is when it is for a client and only the required parameters are
+ // present. The CBB will grow to fit larger serializations.
+ if (!CBB_init(cbb.get(), 28) || !CBB_add_u32(cbb.get(), in.version)) {
+ return false;
+ }
+ CBB versions;
+ if (in.perspective == Perspective::IS_SERVER) {
+ if (!CBB_add_u8_length_prefixed(cbb.get(), &versions)) {
+ return false;
+ }
+ for (QuicVersionLabel version : in.supported_versions) {
+ if (!CBB_add_u32(&versions, version)) {
+ return false;
+ }
+ }
+ }
+
+ CBB params, initial_max_stream_data_param, initial_max_data_param,
+ idle_timeout_param;
+ // required parameters
+ if (!CBB_add_u16_length_prefixed(cbb.get(), &params) ||
+ // initial_max_stream_data
+ !CBB_add_u16(&params, kInitialMaxStreamDataId) ||
+ !CBB_add_u16_length_prefixed(&params, &initial_max_stream_data_param) ||
+ !CBB_add_u32(&initial_max_stream_data_param,
+ in.initial_max_stream_data) ||
+ // initial_max_data
+ !CBB_add_u16(&params, kInitialMaxDataId) ||
+ !CBB_add_u16_length_prefixed(&params, &initial_max_data_param) ||
+ !CBB_add_u32(&initial_max_data_param, in.initial_max_data) ||
+ // idle_timeout
+ !CBB_add_u16(&params, kIdleTimeoutId) ||
+ !CBB_add_u16_length_prefixed(&params, &idle_timeout_param) ||
+ !CBB_add_u16(&idle_timeout_param, in.idle_timeout)) {
+ return false;
+ }
+
+ CBB stateless_reset_token_param;
+ if (!in.stateless_reset_token.empty()) {
+ if (!CBB_add_u16(&params, kStatelessResetTokenId) ||
+ !CBB_add_u16_length_prefixed(&params, &stateless_reset_token_param) ||
+ !CBB_add_bytes(&stateless_reset_token_param,
+ in.stateless_reset_token.data(),
+ in.stateless_reset_token.size())) {
+ return false;
+ }
+ }
+
+ CBB initial_max_bidi_streams_param;
+ if (in.initial_max_bidi_streams.present) {
+ if (!CBB_add_u16(&params, kInitialMaxBidiStreamsId) ||
+ !CBB_add_u16_length_prefixed(&params,
+ &initial_max_bidi_streams_param) ||
+ !CBB_add_u16(&initial_max_bidi_streams_param,
+ in.initial_max_bidi_streams.value)) {
+ return false;
+ }
+ }
+ CBB initial_max_uni_streams_param;
+ if (in.initial_max_uni_streams.present) {
+ if (!CBB_add_u16(&params, kInitialMaxUniStreamsId) ||
+ !CBB_add_u16_length_prefixed(&params, &initial_max_uni_streams_param) ||
+ !CBB_add_u16(&initial_max_uni_streams_param,
+ in.initial_max_uni_streams.value)) {
+ return false;
+ }
+ }
+ CBB max_packet_size_param;
+ if (in.max_packet_size.present) {
+ if (!CBB_add_u16(&params, kMaxOutgoingPacketSizeId) ||
+ !CBB_add_u16_length_prefixed(&params, &max_packet_size_param) ||
+ !CBB_add_u16(&max_packet_size_param, in.max_packet_size.value)) {
+ return false;
+ }
+ }
+ CBB ack_delay_exponent_param;
+ if (in.ack_delay_exponent.present) {
+ if (!CBB_add_u16(&params, kAckDelayExponentId) ||
+ !CBB_add_u16_length_prefixed(&params, &ack_delay_exponent_param) ||
+ !CBB_add_u8(&ack_delay_exponent_param, in.ack_delay_exponent.value)) {
+ return false;
+ }
+ }
+ CBB google_quic_params;
+ if (in.google_quic_params) {
+ const QuicData& serialized_google_quic_params =
+ in.google_quic_params->GetSerialized();
+ if (!CBB_add_u16(&params, kGoogleQuicParamId) ||
+ !CBB_add_u16_length_prefixed(&params, &google_quic_params) ||
+ !CBB_add_bytes(&google_quic_params,
+ reinterpret_cast<const uint8_t*>(
+ serialized_google_quic_params.data()),
+ serialized_google_quic_params.length())) {
+ return false;
+ }
+ }
+ if (!CBB_flush(cbb.get())) {
+ return false;
+ }
+ out->resize(CBB_len(cbb.get()));
+ memcpy(out->data(), CBB_data(cbb.get()), CBB_len(cbb.get()));
+ return true;
+}
+
+bool ParseTransportParameters(const uint8_t* in,
+ size_t in_len,
+ Perspective perspective,
+ TransportParameters* out) {
+ CBS cbs;
+ CBS_init(&cbs, in, in_len);
+ if (!CBS_get_u32(&cbs, &out->version)) {
+ return false;
+ }
+ if (perspective == Perspective::IS_SERVER) {
+ CBS versions;
+ if (!CBS_get_u8_length_prefixed(&cbs, &versions) ||
+ CBS_len(&versions) % 4 != 0) {
+ return false;
+ }
+ while (CBS_len(&versions) > 0) {
+ QuicVersionLabel version;
+ if (!CBS_get_u32(&versions, &version)) {
+ return false;
+ }
+ out->supported_versions.push_back(version);
+ }
+ }
+ out->perspective = perspective;
+
+ uint32_t present_params = 0;
+ bool has_google_quic_params = false;
+ CBS params;
+ if (!CBS_get_u16_length_prefixed(&cbs, &params)) {
+ return false;
+ }
+ while (CBS_len(&params) > 0) {
+ uint16_t param_id;
+ CBS value;
+ if (!CBS_get_u16(&params, &param_id) ||
+ !CBS_get_u16_length_prefixed(&params, &value)) {
+ return false;
+ }
+ if (param_id < kMaxKnownParameterId) {
+ uint16_t mask = 1 << param_id;
+ if (present_params & mask) {
+ return false;
+ }
+ present_params |= mask;
+ }
+ switch (param_id) {
+ case kInitialMaxStreamDataId:
+ if (!CBS_get_u32(&value, &out->initial_max_stream_data) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ break;
+ case kInitialMaxDataId:
+ if (!CBS_get_u32(&value, &out->initial_max_data) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ break;
+ case kInitialMaxBidiStreamsId:
+ if (!CBS_get_u16(&value, &out->initial_max_bidi_streams.value) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ out->initial_max_bidi_streams.present = true;
+ break;
+ case kIdleTimeoutId:
+ if (!CBS_get_u16(&value, &out->idle_timeout) || CBS_len(&value) != 0) {
+ return false;
+ }
+ break;
+ case kMaxOutgoingPacketSizeId:
+ if (!CBS_get_u16(&value, &out->max_packet_size.value) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ out->max_packet_size.present = true;
+ break;
+ case kStatelessResetTokenId:
+ if (CBS_len(&value) == 0) {
+ return false;
+ }
+ out->stateless_reset_token.assign(CBS_data(&value),
+ CBS_data(&value) + CBS_len(&value));
+ break;
+ case kAckDelayExponentId:
+ if (!CBS_get_u8(&value, &out->ack_delay_exponent.value) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ out->ack_delay_exponent.present = true;
+ break;
+ case kInitialMaxUniStreamsId:
+ if (!CBS_get_u16(&value, &out->initial_max_uni_streams.value) ||
+ CBS_len(&value) != 0) {
+ return false;
+ }
+ out->initial_max_uni_streams.present = true;
+ break;
+ case kGoogleQuicParamId:
+ if (has_google_quic_params) {
+ return false;
+ }
+ has_google_quic_params = true;
+ QuicStringPiece serialized_params(
+ reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value));
+ out->google_quic_params = CryptoFramer::ParseMessage(serialized_params);
+ }
+ }
+ if ((present_params & kRequiredParamsMask) != kRequiredParamsMask) {
+ return false;
+ }
+ return out->is_valid();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h
new file mode 100644
index 00000000000..b8abc0ba7f6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters.h
@@ -0,0 +1,93 @@
+// 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_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
+#define QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
+
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+
+namespace quic {
+
+// TransportParameters contains parameters for QUIC's transport layer that are
+// indicated during the TLS handshake. This struct is a mirror of the struct in
+// section 6.4 of draft-ietf-quic-transport-11.
+struct QUIC_EXPORT_PRIVATE TransportParameters {
+ TransportParameters();
+ ~TransportParameters();
+
+ // When |perspective| is Perspective::IS_CLIENT, this struct is being used in
+ // the client_hello handshake message; when it is Perspective::IS_SERVER, it
+ // is being used in the encrypted_extensions handshake message.
+ Perspective perspective;
+
+ // When Perspective::IS_CLIENT, |version| is the initial version offered by
+ // the client (before any version negotiation packets) for this connection.
+ // When Perspective::IS_SERVER, |version| is the version that is in use.
+ QuicVersionLabel version = 0;
+
+ // Server-only parameters:
+
+ // |supported_versions| contains a list of all versions that the server would
+ // send in a version negotiation packet. It is not used if |perspective ==
+ // Perspective::IS_CLIENT|.
+ QuicVersionLabelVector supported_versions;
+
+ // See section 6.4.1 of draft-ietf-quic-transport-11 for definition.
+ std::vector<uint8_t> stateless_reset_token;
+
+ // Required parameters. See section 6.4.1 of draft-ietf-quic-transport-11 for
+ // definitions.
+ uint32_t initial_max_stream_data = 0;
+ uint32_t initial_max_data = 0;
+ uint16_t idle_timeout = 0;
+
+ template <typename T>
+ struct OptionalParam {
+ bool present = false;
+ T value;
+ };
+
+ // Optional parameters. See section 6.4.1 of draft-ietf-quic-transport-11 for
+ // definitions.
+ OptionalParam<uint16_t> initial_max_bidi_streams;
+ OptionalParam<uint16_t> initial_max_uni_streams;
+ OptionalParam<uint16_t> max_packet_size;
+ OptionalParam<uint8_t> ack_delay_exponent;
+
+ // Transport parameters used by Google QUIC but not IETF QUIC. This is
+ // serialized into a TransportParameter struct with a TransportParameterId of
+ // 18257.
+ std::unique_ptr<CryptoHandshakeMessage> google_quic_params;
+
+ // Returns true if the contents of this struct are valid.
+ bool is_valid() const;
+};
+
+// Serializes a TransportParameters struct into the format for sending it in a
+// TLS extension. The serialized bytes are put in |*out|, and this function
+// returns true on success or false if |TransportParameters::is_valid| returns
+// false.
+QUIC_EXPORT_PRIVATE bool SerializeTransportParameters(
+ const TransportParameters& in,
+ std::vector<uint8_t>* out);
+
+// Parses bytes from the quic_transport_parameters TLS extension and writes the
+// parsed parameters into |*out|. Input is read from |in| for |in_len| bytes.
+// |perspective| indicates whether the input came from a client or a server.
+// This method returns true if the input was successfully parsed, and false if
+// it could not be parsed.
+// TODO(nharper): Write fuzz tests for this method.
+QUIC_EXPORT_PRIVATE bool ParseTransportParameters(const uint8_t* in,
+ size_t in_len,
+ Perspective perspective,
+ TransportParameters* out);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_CRYPTO_TRANSPORT_PARAMETERS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc
new file mode 100644
index 00000000000..23b18e54bbc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/crypto/transport_parameters_test.cc
@@ -0,0 +1,440 @@
+// 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 "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
+
+#include "third_party/boringssl/src/include/openssl/mem.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class TransportParametersTest : public QuicTest {};
+
+TEST_F(TransportParametersTest, RoundTripClient) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.initial_max_stream_data = 12;
+ orig_params.initial_max_data = 34;
+ orig_params.idle_timeout = 56;
+ orig_params.initial_max_bidi_streams.present = true;
+ orig_params.initial_max_bidi_streams.value = 2000;
+ orig_params.initial_max_uni_streams.present = true;
+ orig_params.initial_max_uni_streams.value = 3000;
+ orig_params.max_packet_size.present = true;
+ orig_params.max_packet_size.value = 9001;
+ orig_params.ack_delay_exponent.present = true;
+ orig_params.ack_delay_exponent.value = 10;
+ orig_params.version = 0xff000005;
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized));
+
+ TransportParameters new_params;
+ ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(),
+ Perspective::IS_CLIENT, &new_params));
+
+ EXPECT_EQ(new_params.initial_max_stream_data,
+ orig_params.initial_max_stream_data);
+ EXPECT_EQ(new_params.initial_max_data, orig_params.initial_max_data);
+ EXPECT_EQ(new_params.idle_timeout, orig_params.idle_timeout);
+ EXPECT_EQ(new_params.version, orig_params.version);
+ EXPECT_TRUE(new_params.initial_max_bidi_streams.present);
+ EXPECT_EQ(new_params.initial_max_bidi_streams.value,
+ orig_params.initial_max_bidi_streams.value);
+ EXPECT_TRUE(new_params.initial_max_uni_streams.present);
+ EXPECT_EQ(new_params.initial_max_uni_streams.value,
+ orig_params.initial_max_uni_streams.value);
+ EXPECT_TRUE(new_params.max_packet_size.present);
+ EXPECT_EQ(new_params.max_packet_size.value,
+ orig_params.max_packet_size.value);
+ EXPECT_TRUE(new_params.ack_delay_exponent.present);
+ EXPECT_EQ(new_params.ack_delay_exponent.value,
+ orig_params.ack_delay_exponent.value);
+}
+
+TEST_F(TransportParametersTest, RoundTripServer) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_SERVER;
+ orig_params.initial_max_stream_data = 12;
+ orig_params.initial_max_data = 34;
+ orig_params.idle_timeout = 56;
+ orig_params.stateless_reset_token.resize(16);
+ orig_params.version = 0xff000005;
+ orig_params.supported_versions.push_back(0xff000005);
+ orig_params.supported_versions.push_back(0xff000004);
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized));
+
+ TransportParameters new_params;
+ ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(),
+ Perspective::IS_SERVER, &new_params));
+
+ EXPECT_EQ(new_params.initial_max_stream_data,
+ orig_params.initial_max_stream_data);
+ EXPECT_EQ(new_params.initial_max_data, orig_params.initial_max_data);
+ EXPECT_EQ(new_params.idle_timeout, orig_params.idle_timeout);
+ EXPECT_EQ(new_params.stateless_reset_token,
+ orig_params.stateless_reset_token);
+ EXPECT_EQ(new_params.version, orig_params.version);
+ ASSERT_EQ(new_params.supported_versions, orig_params.supported_versions);
+}
+
+TEST_F(TransportParametersTest, IsValid) {
+ TransportParameters empty_params;
+ empty_params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(empty_params.is_valid());
+
+ {
+ TransportParameters params;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.is_valid());
+ params.idle_timeout = 600;
+ EXPECT_TRUE(params.is_valid());
+ params.idle_timeout = 601;
+ EXPECT_FALSE(params.is_valid());
+ }
+ {
+ TransportParameters params;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.is_valid());
+ params.max_packet_size.present = true;
+ params.max_packet_size.value = 0;
+ EXPECT_FALSE(params.is_valid());
+ params.max_packet_size.value = 1200;
+ EXPECT_TRUE(params.is_valid());
+ params.max_packet_size.value = 65527;
+ EXPECT_TRUE(params.is_valid());
+ params.max_packet_size.value = 65535;
+ EXPECT_FALSE(params.is_valid());
+ }
+ {
+ TransportParameters params;
+ params.perspective = Perspective::IS_CLIENT;
+ EXPECT_TRUE(params.is_valid());
+ params.ack_delay_exponent.present = true;
+ params.ack_delay_exponent.value = 0;
+ EXPECT_TRUE(params.is_valid());
+ params.ack_delay_exponent.value = 20;
+ EXPECT_TRUE(params.is_valid());
+ params.ack_delay_exponent.value = 21;
+ EXPECT_FALSE(params.is_valid());
+ }
+}
+
+TEST_F(TransportParametersTest, NoServerParamsWithoutStatelessResetToken) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_SERVER;
+ orig_params.initial_max_stream_data = 12;
+ orig_params.initial_max_data = 34;
+ orig_params.idle_timeout = 56;
+ orig_params.version = 0xff000005;
+ orig_params.supported_versions.push_back(0xff000005);
+ orig_params.supported_versions.push_back(0xff000004);
+
+ std::vector<uint8_t> out;
+ ASSERT_FALSE(SerializeTransportParameters(orig_params, &out));
+}
+
+TEST_F(TransportParametersTest, NoClientParamsWithStatelessResetToken) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.initial_max_stream_data = 12;
+ orig_params.initial_max_data = 34;
+ orig_params.idle_timeout = 56;
+ orig_params.stateless_reset_token.resize(16);
+ orig_params.version = 0xff000005;
+
+ std::vector<uint8_t> out;
+ ASSERT_FALSE(SerializeTransportParameters(orig_params, &out));
+}
+
+TEST_F(TransportParametersTest, ParseClientParams) {
+ const uint8_t kClientParams[] = {
+ 0xff, 0x00, 0x00, 0x05, // initial version
+ 0x00, 0x16, // length parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x0c, // value
+ // initial_max_data
+ 0x00, 0x01, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x22, // value
+ // idle_timeout
+ 0x00, 0x03, // parameter id
+ 0x00, 0x02, // length
+ 0x00, 0x38, // value
+ };
+
+ TransportParameters out_params;
+ ASSERT_TRUE(ParseTransportParameters(kClientParams,
+ QUIC_ARRAYSIZE(kClientParams),
+ Perspective::IS_CLIENT, &out_params));
+}
+
+TEST_F(TransportParametersTest, ParseClientParamsFailsWithStatelessResetToken) {
+ TransportParameters out_params;
+
+ // clang-format off
+ const uint8_t kClientParamsWithFullToken[] = {
+ 0xff, 0x00, 0x00, 0x05, // initial version
+ 0x00, 0x2a, // length parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x0c, // value
+ // initial_max_data
+ 0x00, 0x01, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x22, // value
+ // idle_timeout
+ 0x00, 0x03, // parameter id
+ 0x00, 0x02, // length
+ 0x00, 0x38, // value
+ // stateless_reset_token
+ 0x00, 0x06, // parameter id
+ 0x00, 0x10, // length
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ };
+ // clang-format on
+
+ ASSERT_FALSE(ParseTransportParameters(
+ kClientParamsWithFullToken, QUIC_ARRAYSIZE(kClientParamsWithFullToken),
+ Perspective::IS_CLIENT, &out_params));
+
+ const uint8_t kClientParamsWithEmptyToken[] = {
+ 0xff, 0x00, 0x00, 0x05, // initial version
+ 0x00, 0x1a, // length parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x0c, // value
+ // initial_max_data
+ 0x00, 0x01, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x22, // value
+ // idle_timeout
+ 0x00, 0x03, // parameter id
+ 0x00, 0x02, // length
+ 0x00, 0x38, // value
+ // stateless_reset_token
+ 0x00, 0x06, // parameter id
+ 0x00, 0x00, // length
+ };
+
+ ASSERT_FALSE(ParseTransportParameters(
+ kClientParamsWithEmptyToken, QUIC_ARRAYSIZE(kClientParamsWithEmptyToken),
+ Perspective::IS_CLIENT, &out_params));
+}
+
+TEST_F(TransportParametersTest, ParseClientParametersWithInvalidParams) {
+ TransportParameters out_params;
+
+ const uint8_t kClientParamsRepeated[] = {
+ 0xff, 0x00, 0x00, 0x05, // initial version
+ 0x00, 0x1c, // length parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x0c, // value
+ // initial_max_data
+ 0x00, 0x01, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x22, // value
+ // idle_timeout
+ 0x00, 0x03, // parameter id
+ 0x00, 0x02, // length
+ 0x00, 0x38, // value
+ // idle_timeout (repeat)
+ 0x00, 0x03, // parameter id
+ 0x00, 0x02, // length
+ 0x00, 0x38, // value
+ };
+ ASSERT_FALSE(ParseTransportParameters(kClientParamsRepeated,
+ QUIC_ARRAYSIZE(kClientParamsRepeated),
+ Perspective::IS_CLIENT, &out_params));
+
+ const uint8_t kClientParamsMissing[] = {
+ 0xff, 0x00, 0x00, 0x05, // initial version
+ 0x00, 0x10, // length parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x0c, // value
+ // initial_max_data
+ 0x00, 0x01, // parameter id
+ 0x00, 0x04, // length
+ 0x00, 0x00, 0x00, 0x22, // value
+ };
+ ASSERT_FALSE(ParseTransportParameters(kClientParamsMissing,
+ QUIC_ARRAYSIZE(kClientParamsMissing),
+ Perspective::IS_CLIENT, &out_params));
+}
+
+TEST_F(TransportParametersTest, ParseServerParams) {
+ // clang-format off
+ const uint8_t kServerParams[] = {
+ 0xff, 0x00, 0x00, 0x05, // negotiated_version
+ 0x08, // length of supported versions array
+ 0xff, 0x00, 0x00, 0x05,
+ 0xff, 0x00, 0x00, 0x04,
+ 0x00, 0x2a, // length of parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00,
+ 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0c,
+ // initial_max_data
+ 0x00, 0x01,
+ 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x22,
+ // idle_timeout
+ 0x00, 0x03,
+ 0x00, 0x02,
+ 0x00, 0x38,
+ // stateless_reset_token
+ 0x00, 0x06,
+ 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+
+ TransportParameters out_params;
+ ASSERT_TRUE(ParseTransportParameters(kServerParams,
+ QUIC_ARRAYSIZE(kServerParams),
+ Perspective::IS_SERVER, &out_params));
+}
+
+TEST_F(TransportParametersTest, ParseServerParamsWithoutToken) {
+ // clang-format off
+ const uint8_t kServerParams[] = {
+ 0xff, 0x00, 0x00, 0x05, // negotiated_version
+ 0x08, // length of supported versions array
+ 0xff, 0x00, 0x00, 0x05,
+ 0xff, 0x00, 0x00, 0x04,
+ 0x00, 0x16, // length of parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00,
+ 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0c,
+ // initial_max_data
+ 0x00, 0x01,
+ 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x22,
+ // idle_timeout
+ 0x00, 0x03,
+ 0x00, 0x02,
+ 0x00, 0x38,
+ };
+ // clang-format on
+
+ TransportParameters out_params;
+ ASSERT_FALSE(ParseTransportParameters(kServerParams,
+ QUIC_ARRAYSIZE(kServerParams),
+ Perspective::IS_SERVER, &out_params));
+}
+
+TEST_F(TransportParametersTest, ParseServerParametersWithInvalidParams) {
+ TransportParameters out_params;
+
+ // clang-format off
+ const uint8_t kServerParamsRepeated[] = {
+ 0xff, 0x00, 0x00, 0x05, // negotiated_version
+ 0x08, // length of supported versions array
+ 0xff, 0x00, 0x00, 0x05,
+ 0xff, 0x00, 0x00, 0x04,
+ 0x00, 0x30, // length of parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00,
+ 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0c,
+ // initial_max_data
+ 0x00, 0x01,
+ 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x22,
+ // idle_timeout
+ 0x00, 0x03,
+ 0x00, 0x02,
+ 0x00, 0x38,
+ // idle_timeout (repeat)
+ 0x00, 0x03,
+ 0x00, 0x02,
+ 0x00, 0x38,
+ // stateless_reset_token
+ 0x00, 0x06,
+ 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+ ASSERT_FALSE(ParseTransportParameters(kServerParamsRepeated,
+ QUIC_ARRAYSIZE(kServerParamsRepeated),
+ Perspective::IS_SERVER, &out_params));
+
+ // clang-format off
+ const uint8_t kServerParamsMissing[] = {
+ 0xff, 0x00, 0x00, 0x05, // negotiated_version
+ 0x08, // length of supported versions array
+ 0xff, 0x00, 0x00, 0x05,
+ 0xff, 0x00, 0x00, 0x04,
+ 0x00, 0x24, // length of parameters array that follows
+ // initial_max_stream_data
+ 0x00, 0x00,
+ 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0c,
+ // initial_max_data
+ 0x00, 0x01,
+ 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x22,
+ // stateless_reset_token
+ 0x00, 0x06,
+ 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+ ASSERT_FALSE(ParseTransportParameters(kServerParamsMissing,
+ QUIC_ARRAYSIZE(kServerParamsMissing),
+ Perspective::IS_SERVER, &out_params));
+}
+
+TEST_F(TransportParametersTest, CryptoHandshakeMessageRoundtrip) {
+ TransportParameters orig_params;
+ orig_params.perspective = Perspective::IS_CLIENT;
+ orig_params.initial_max_stream_data = 12;
+ orig_params.initial_max_data = 34;
+ orig_params.idle_timeout = 56;
+
+ orig_params.google_quic_params = QuicMakeUnique<CryptoHandshakeMessage>();
+ const std::string kTestString = "test string";
+ orig_params.google_quic_params->SetStringPiece(42, kTestString);
+ const uint32_t kTestValue = 12;
+ orig_params.google_quic_params->SetValue(1337, kTestValue);
+
+ std::vector<uint8_t> serialized;
+ ASSERT_TRUE(SerializeTransportParameters(orig_params, &serialized));
+
+ TransportParameters new_params;
+ ASSERT_TRUE(ParseTransportParameters(serialized.data(), serialized.size(),
+ Perspective::IS_CLIENT, &new_params));
+
+ ASSERT_NE(new_params.google_quic_params.get(), nullptr);
+ EXPECT_EQ(new_params.google_quic_params->tag(),
+ orig_params.google_quic_params->tag());
+ QuicStringPiece test_string;
+ EXPECT_TRUE(new_params.google_quic_params->GetStringPiece(42, &test_string));
+ EXPECT_EQ(test_string, kTestString);
+ uint32_t test_value;
+ EXPECT_EQ(new_params.google_quic_params->GetUint32(1337, &test_value),
+ QUIC_NO_ERROR);
+ EXPECT_EQ(test_value, kTestValue);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.cc
new file mode 100644
index 00000000000..389f1c04a0a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+
+namespace quic {
+
+namespace {
+
+const QuicPacketCount kMaxPrintRange = 128;
+
+uint64_t PacketNumberIntervalLength(
+ const QuicInterval<QuicPacketNumber>& interval) {
+ if (interval.Empty()) {
+ return 0u;
+ }
+ return interval.max() - interval.min();
+}
+} // namespace
+
+bool IsAwaitingPacket(const QuicAckFrame& ack_frame,
+ QuicPacketNumber packet_number,
+ QuicPacketNumber peer_least_packet_awaiting_ack) {
+ DCHECK(packet_number.IsInitialized());
+ return (!peer_least_packet_awaiting_ack.IsInitialized() ||
+ packet_number >= peer_least_packet_awaiting_ack) &&
+ !ack_frame.packets.Contains(packet_number);
+}
+
+QuicAckFrame::QuicAckFrame()
+ : ack_delay_time(QuicTime::Delta::Infinite()),
+ ecn_counters_populated(false),
+ ect_0_count(0),
+ ect_1_count(0),
+ ecn_ce_count(0) {}
+
+QuicAckFrame::QuicAckFrame(const QuicAckFrame& other) = default;
+
+QuicAckFrame::~QuicAckFrame() {}
+
+std::ostream& operator<<(std::ostream& os, const QuicAckFrame& ack_frame) {
+ os << "{ largest_acked: " << LargestAcked(ack_frame)
+ << ", ack_delay_time: " << ack_frame.ack_delay_time.ToMicroseconds()
+ << ", packets: [ " << ack_frame.packets << " ]"
+ << ", received_packets: [ ";
+ for (const std::pair<QuicPacketNumber, QuicTime>& p :
+ ack_frame.received_packet_times) {
+ os << p.first << " at " << p.second.ToDebuggingValue() << " ";
+ }
+ os << " ]";
+ os << ", ecn_counters_populated: " << ack_frame.ecn_counters_populated;
+ if (ack_frame.ecn_counters_populated) {
+ os << ", ect_0_count: " << ack_frame.ect_0_count
+ << ", ect_1_count: " << ack_frame.ect_1_count
+ << ", ecn_ce_count: " << ack_frame.ecn_ce_count;
+ }
+
+ os << " }\n";
+ return os;
+}
+
+void QuicAckFrame::Clear() {
+ largest_acked.Clear();
+ ack_delay_time = QuicTime::Delta::Infinite();
+ received_packet_times.clear();
+ packets.Clear();
+}
+
+PacketNumberQueue::PacketNumberQueue() {}
+PacketNumberQueue::PacketNumberQueue(const PacketNumberQueue& other) = default;
+PacketNumberQueue::PacketNumberQueue(PacketNumberQueue&& other) = default;
+PacketNumberQueue::~PacketNumberQueue() {}
+
+PacketNumberQueue& PacketNumberQueue::operator=(
+ const PacketNumberQueue& other) = default;
+PacketNumberQueue& PacketNumberQueue::operator=(PacketNumberQueue&& other) =
+ default;
+
+void PacketNumberQueue::Add(QuicPacketNumber packet_number) {
+ if (!packet_number.IsInitialized()) {
+ return;
+ }
+ // Check if the deque is empty
+ if (packet_number_deque_.empty()) {
+ packet_number_deque_.push_front(
+ QuicInterval<QuicPacketNumber>(packet_number, packet_number + 1));
+ return;
+ }
+ QuicInterval<QuicPacketNumber> back = packet_number_deque_.back();
+
+ // Check for the typical case,
+ // when the next packet in order is acked
+ if (back.max() == packet_number) {
+ packet_number_deque_.back().SetMax(packet_number + 1);
+ return;
+ }
+ // Check if the next packet in order is skipped
+ if (back.max() < packet_number) {
+ packet_number_deque_.push_back(
+ QuicInterval<QuicPacketNumber>(packet_number, packet_number + 1));
+ return;
+ }
+
+ QuicInterval<QuicPacketNumber> front = packet_number_deque_.front();
+ // Check if the packet can be popped on the front
+ if (front.min() > packet_number + 1) {
+ packet_number_deque_.push_front(
+ QuicInterval<QuicPacketNumber>(packet_number, packet_number + 1));
+ return;
+ }
+ if (front.min() == packet_number + 1) {
+ packet_number_deque_.front().SetMin(packet_number);
+ return;
+ }
+
+ int i = packet_number_deque_.size() - 1;
+ // Iterating through the queue backwards
+ // to find a proper place for the packet
+ while (i >= 0) {
+ QuicInterval<QuicPacketNumber> packet_interval = packet_number_deque_[i];
+ DCHECK(packet_interval.min() < packet_interval.max());
+ // Check if the packet is contained in an interval already
+ if (packet_interval.Contains(packet_number)) {
+ return;
+ }
+
+ // Check if the packet can extend an interval.
+ if (packet_interval.max() == packet_number) {
+ packet_number_deque_[i].SetMax(packet_number + 1);
+ return;
+ }
+ // Check if the packet can extend an interval
+ // and merge two intervals if needed.
+ // There is no need to merge an interval in the previous
+ // if statement, as all merges will happen here.
+ if (packet_interval.min() == packet_number + 1) {
+ packet_number_deque_[i].SetMin(packet_number);
+ if (i > 0 && packet_number == packet_number_deque_[i - 1].max()) {
+ packet_number_deque_[i - 1].SetMax(packet_interval.max());
+ packet_number_deque_.erase(packet_number_deque_.begin() + i);
+ }
+ return;
+ }
+
+ // Check if we need to make a new interval for the packet
+ if (packet_interval.max() < packet_number + 1) {
+ packet_number_deque_.insert(
+ packet_number_deque_.begin() + i + 1,
+ QuicInterval<QuicPacketNumber>(packet_number, packet_number + 1));
+ return;
+ }
+ i--;
+ }
+}
+
+void PacketNumberQueue::AddRange(QuicPacketNumber lower,
+ QuicPacketNumber higher) {
+ if (!lower.IsInitialized() || !higher.IsInitialized() || lower >= higher) {
+ return;
+ }
+ if (packet_number_deque_.empty()) {
+ packet_number_deque_.push_front(
+ QuicInterval<QuicPacketNumber>(lower, higher));
+ return;
+ }
+ QuicInterval<QuicPacketNumber> back = packet_number_deque_.back();
+
+ if (back.max() == lower) {
+ // Check for the typical case,
+ // when the next packet in order is acked
+ packet_number_deque_.back().SetMax(higher);
+ return;
+ }
+ if (back.max() < lower) {
+ // Check if the next packet in order is skipped
+ packet_number_deque_.push_back(
+ QuicInterval<QuicPacketNumber>(lower, higher));
+ return;
+ }
+ QuicInterval<QuicPacketNumber> front = packet_number_deque_.front();
+ // Check if the packets are being added in reverse order
+ if (front.min() == higher) {
+ packet_number_deque_.front().SetMin(lower);
+ } else if (front.min() > higher) {
+ packet_number_deque_.push_front(
+ QuicInterval<QuicPacketNumber>(lower, higher));
+
+ } else {
+ // Ranges must be above or below all existing ranges.
+ QUIC_BUG << "AddRange only supports adding packets above or below the "
+ << "current min:" << Min() << " and max:" << Max()
+ << ", but adding [" << lower << "," << higher << ")";
+ }
+}
+
+bool PacketNumberQueue::RemoveUpTo(QuicPacketNumber higher) {
+ if (!higher.IsInitialized() || Empty()) {
+ return false;
+ }
+ const QuicPacketNumber old_min = Min();
+ while (!packet_number_deque_.empty()) {
+ QuicInterval<QuicPacketNumber> front = packet_number_deque_.front();
+ if (front.max() < higher) {
+ packet_number_deque_.pop_front();
+ } else if (front.min() < higher && front.max() >= higher) {
+ packet_number_deque_.front().SetMin(higher);
+ if (front.max() == higher) {
+ packet_number_deque_.pop_front();
+ }
+ break;
+ } else {
+ break;
+ }
+ }
+
+ return Empty() || old_min != Min();
+}
+
+void PacketNumberQueue::RemoveSmallestInterval() {
+ QUIC_BUG_IF(packet_number_deque_.size() < 2)
+ << (Empty() ? "No intervals to remove."
+ : "Can't remove the last interval.");
+ packet_number_deque_.pop_front();
+}
+
+void PacketNumberQueue::Clear() {
+ packet_number_deque_.clear();
+}
+
+bool PacketNumberQueue::Contains(QuicPacketNumber packet_number) const {
+ if (!packet_number.IsInitialized() || packet_number_deque_.empty()) {
+ return false;
+ }
+ if (packet_number_deque_.front().min() > packet_number ||
+ packet_number_deque_.back().max() <= packet_number) {
+ return false;
+ }
+ for (QuicInterval<QuicPacketNumber> interval : packet_number_deque_) {
+ if (interval.Contains(packet_number)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PacketNumberQueue::Empty() const {
+ return packet_number_deque_.empty();
+}
+
+QuicPacketNumber PacketNumberQueue::Min() const {
+ DCHECK(!Empty());
+ return packet_number_deque_.front().min();
+}
+
+QuicPacketNumber PacketNumberQueue::Max() const {
+ DCHECK(!Empty());
+ return packet_number_deque_.back().max() - 1;
+}
+
+QuicPacketCount PacketNumberQueue::NumPacketsSlow() const {
+ QuicPacketCount n_packets = 0;
+ for (QuicInterval<QuicPacketNumber> interval : packet_number_deque_) {
+ n_packets += PacketNumberIntervalLength(interval);
+ }
+ return n_packets;
+}
+
+size_t PacketNumberQueue::NumIntervals() const {
+ return packet_number_deque_.size();
+}
+
+PacketNumberQueue::const_iterator PacketNumberQueue::begin() const {
+ return packet_number_deque_.begin();
+}
+
+PacketNumberQueue::const_iterator PacketNumberQueue::end() const {
+ return packet_number_deque_.end();
+}
+
+PacketNumberQueue::const_reverse_iterator PacketNumberQueue::rbegin() const {
+ return packet_number_deque_.rbegin();
+}
+
+PacketNumberQueue::const_reverse_iterator PacketNumberQueue::rend() const {
+ return packet_number_deque_.rend();
+}
+
+QuicPacketCount PacketNumberQueue::LastIntervalLength() const {
+ DCHECK(!Empty());
+ return PacketNumberIntervalLength(packet_number_deque_.back());
+}
+
+// Largest min...max range for packet numbers where we print the numbers
+// explicitly. If bigger than this, we print as a range [a,d] rather
+// than [a b c d]
+
+std::ostream& operator<<(std::ostream& os, const PacketNumberQueue& q) {
+ for (const QuicInterval<QuicPacketNumber>& interval : q) {
+ // Print as a range if there is a pathological condition.
+ if ((interval.min() >= interval.max()) ||
+ (interval.max() - interval.min() > kMaxPrintRange)) {
+ // If min>max, it's really a bug, so QUIC_BUG it to
+ // catch it in development.
+ QUIC_BUG_IF(interval.min() >= interval.max())
+ << "Ack Range minimum (" << interval.min() << "Not less than max ("
+ << interval.max() << ")";
+ // print range as min...max rather than full list.
+ // in the event of a bug, the list could be very big.
+ os << interval.min() << "..." << (interval.max() - 1) << " ";
+ } else {
+ for (QuicPacketNumber packet_number = interval.min();
+ packet_number < interval.max(); ++packet_number) {
+ os << packet_number << " ";
+ }
+ }
+ }
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h
new file mode 100644
index 00000000000..771d93e3285
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h
@@ -0,0 +1,144 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+// A sequence of packet numbers where each number is unique. Intended to be used
+// in a sliding window fashion, where smaller old packet numbers are removed and
+// larger new packet numbers are added, with the occasional random access.
+class QUIC_EXPORT_PRIVATE PacketNumberQueue {
+ public:
+ PacketNumberQueue();
+ PacketNumberQueue(const PacketNumberQueue& other);
+ PacketNumberQueue(PacketNumberQueue&& other);
+ ~PacketNumberQueue();
+
+ PacketNumberQueue& operator=(const PacketNumberQueue& other);
+ PacketNumberQueue& operator=(PacketNumberQueue&& other);
+
+ typedef QuicDeque<QuicInterval<QuicPacketNumber>>::const_iterator
+ const_iterator;
+ typedef QuicDeque<QuicInterval<QuicPacketNumber>>::const_reverse_iterator
+ const_reverse_iterator;
+
+ // Adds |packet_number| to the set of packets in the queue.
+ void Add(QuicPacketNumber packet_number);
+
+ // Adds packets between [lower, higher) to the set of packets in the queue. It
+ // is undefined behavior to call this with |higher| < |lower|.
+ void AddRange(QuicPacketNumber lower, QuicPacketNumber higher);
+
+ // Removes packets with values less than |higher| from the set of packets in
+ // the queue. Returns true if packets were removed.
+ bool RemoveUpTo(QuicPacketNumber higher);
+
+ // Removes the smallest interval in the queue.
+ void RemoveSmallestInterval();
+
+ // Clear this packet number queue.
+ void Clear();
+
+ // Returns true if the queue contains |packet_number|.
+ bool Contains(QuicPacketNumber packet_number) const;
+
+ // Returns true if the queue is empty.
+ bool Empty() const;
+
+ // Returns the minimum packet number stored in the queue. It is undefined
+ // behavior to call this if the queue is empty.
+ QuicPacketNumber Min() const;
+
+ // Returns the maximum packet number stored in the queue. It is undefined
+ // behavior to call this if the queue is empty.
+ QuicPacketNumber Max() const;
+
+ // Returns the number of unique packets stored in the queue. Inefficient; only
+ // exposed for testing.
+ QuicPacketCount NumPacketsSlow() const;
+
+ // Returns the number of disjoint packet number intervals contained in the
+ // queue.
+ size_t NumIntervals() const;
+
+ // Returns the length of last interval.
+ QuicPacketCount LastIntervalLength() const;
+
+ // Returns iterators over the packet number intervals.
+ const_iterator begin() const;
+ const_iterator end() const;
+ const_reverse_iterator rbegin() const;
+ const_reverse_iterator rend() const;
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const PacketNumberQueue& q);
+
+ private:
+ QuicDeque<QuicInterval<QuicPacketNumber>> packet_number_deque_;
+};
+
+struct QUIC_EXPORT_PRIVATE QuicAckFrame {
+ QuicAckFrame();
+ QuicAckFrame(const QuicAckFrame& other);
+ ~QuicAckFrame();
+
+ void Clear();
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicAckFrame& ack_frame);
+
+ // The highest packet number we've observed from the peer. When |packets| is
+ // not empty, it should always be equal to packets.Max(). The |LargestAcked|
+ // function ensures this invariant in debug mode.
+ QuicPacketNumber largest_acked;
+
+ // Time elapsed since largest_observed() was received until this Ack frame was
+ // sent.
+ QuicTime::Delta ack_delay_time;
+
+ // Vector of <packet_number, time> for when packets arrived.
+ PacketTimeVector received_packet_times;
+
+ // Set of packets.
+ PacketNumberQueue packets;
+
+ // ECN counters, used only in version 99's ACK frame and valid only when
+ // |ecn_counters_populated| is true.
+ bool ecn_counters_populated;
+ QuicPacketCount ect_0_count;
+ QuicPacketCount ect_1_count;
+ QuicPacketCount ecn_ce_count;
+};
+
+// The highest acked packet number we've observed from the peer. If no packets
+// have been observed, return 0.
+inline QUIC_EXPORT_PRIVATE QuicPacketNumber
+LargestAcked(const QuicAckFrame& frame) {
+ DCHECK(frame.packets.Empty() || frame.packets.Max() == frame.largest_acked);
+ return frame.largest_acked;
+}
+
+// True if the packet number is greater than largest_observed or is listed
+// as missing.
+// Always returns false for packet numbers less than least_unacked.
+QUIC_EXPORT_PRIVATE bool IsAwaitingPacket(
+ const QuicAckFrame& ack_frame,
+ QuicPacketNumber packet_number,
+ QuicPacketNumber peer_least_packet_awaiting_ack);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.cc
new file mode 100644
index 00000000000..41ba1443ce9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicBlockedFrame::QuicBlockedFrame()
+ : control_frame_id(kInvalidControlFrameId), stream_id(0), offset(0) {}
+
+QuicBlockedFrame::QuicBlockedFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id)
+ : control_frame_id(control_frame_id), stream_id(stream_id), offset(0) {}
+
+QuicBlockedFrame::QuicBlockedFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ QuicStreamOffset offset)
+ : control_frame_id(control_frame_id),
+ stream_id(stream_id),
+ offset(offset) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicBlockedFrame& blocked_frame) {
+ os << "{ control_frame_id: " << blocked_frame.control_frame_id
+ << ", stream_id: " << blocked_frame.stream_id << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h
new file mode 100644
index 00000000000..a4be66411d3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h
@@ -0,0 +1,51 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_BLOCKED_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_BLOCKED_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+// The BLOCKED frame is used to indicate to the remote endpoint that this
+// endpoint believes itself to be flow-control blocked but otherwise ready to
+// send data. The BLOCKED frame is purely advisory and optional.
+// Based on SPDY's BLOCKED frame (undocumented as of 2014-01-28).
+struct QUIC_EXPORT_PRIVATE QuicBlockedFrame {
+ QuicBlockedFrame();
+ QuicBlockedFrame(QuicControlFrameId control_frame_id, QuicStreamId stream_id);
+ QuicBlockedFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ QuicStreamOffset offset);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicBlockedFrame& b);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+
+ // The stream this frame applies to. 0 is a special case meaning the overall
+ // connection rather than a specific stream.
+ //
+ // For IETF QUIC, the stream_id controls whether an IETF QUIC
+ // BLOCKED or STREAM_BLOCKED frame is generated.
+ // If stream_id is 0 then a BLOCKED frame is generated and transmitted,
+ // if non-0, a STREAM_BLOCKED.
+ // TODO(fkastenholz): This should be converted to use
+ // QuicUtils::GetInvalidStreamId to get the correct invalid stream id value
+ // and not rely on 0.
+ QuicStreamId stream_id;
+
+ // For Google QUIC, the offset is ignored.
+ QuicStreamOffset offset;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_BLOCKED_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc
new file mode 100644
index 00000000000..baffa9d941b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h"
+
+namespace quic {
+
+QuicConnectionCloseFrame::QuicConnectionCloseFrame()
+ // Default close type ensures that existing, pre-V99 code works as expected.
+ : close_type(GOOGLE_QUIC_CONNECTION_CLOSE),
+ quic_error_code(QUIC_NO_ERROR),
+ extracted_error_code(QUIC_IETF_GQUIC_ERROR_MISSING),
+ transport_close_frame_type(0) {}
+
+QuicConnectionCloseFrame::QuicConnectionCloseFrame(QuicErrorCode error_code)
+ // Default close type ensures that existing, pre-V99 code works as expected.
+ : close_type(GOOGLE_QUIC_CONNECTION_CLOSE),
+ quic_error_code(error_code),
+ extracted_error_code(QUIC_IETF_GQUIC_ERROR_MISSING),
+ transport_close_frame_type(0) {}
+
+QuicConnectionCloseFrame::QuicConnectionCloseFrame(QuicErrorCode error_code,
+ std::string error_details)
+ // Default close type ensures that existing, pre-V99 code works as expected.
+ : close_type(GOOGLE_QUIC_CONNECTION_CLOSE),
+ quic_error_code(error_code),
+ extracted_error_code(QUIC_IETF_GQUIC_ERROR_MISSING),
+ error_details(std::move(error_details)),
+ transport_close_frame_type(0) {}
+
+QuicConnectionCloseFrame::QuicConnectionCloseFrame(
+ QuicIetfTransportErrorCodes transport_error_code,
+ QuicErrorCode extracted_error_code,
+ std::string error_details,
+ uint64_t transport_close_frame_type)
+ // Default close type ensures that existing, pre-V99 code works as expected.
+ : close_type(GOOGLE_QUIC_CONNECTION_CLOSE),
+ transport_error_code(transport_error_code),
+ extracted_error_code(extracted_error_code),
+ error_details(std::move(error_details)),
+ transport_close_frame_type(transport_close_frame_type) {}
+
+std::ostream& operator<<(
+ std::ostream& os,
+ const QuicConnectionCloseFrame& connection_close_frame) {
+ os << "{ Close type: " << connection_close_frame.close_type
+ << ", error_code: "
+ << ((connection_close_frame.close_type ==
+ IETF_QUIC_TRANSPORT_CONNECTION_CLOSE)
+ ? connection_close_frame.transport_error_code
+ : ((connection_close_frame.close_type ==
+ IETF_QUIC_APPLICATION_CONNECTION_CLOSE)
+ ? connection_close_frame.application_error_code
+ : connection_close_frame.quic_error_code))
+ << ", extracted_error_code: "
+ << connection_close_frame.extracted_error_code << ", error_details: '"
+ << connection_close_frame.error_details
+ << "', frame_type: " << connection_close_frame.transport_close_frame_type
+ << "}\n";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const QuicConnectionCloseType type) {
+ switch (type) {
+ case GOOGLE_QUIC_CONNECTION_CLOSE:
+ os << "GOOGLE_QUIC_CONNECTION_CLOSE";
+ break;
+ case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE:
+ os << "IETF_QUIC_TRANSPORT_CONNECTION_CLOSE";
+ break;
+ case IETF_QUIC_APPLICATION_CONNECTION_CLOSE:
+ os << "IETF_QUIC_APPLICATION_CONNECTION_CLOSE";
+ break;
+ default:
+ os << "Unknown: " << static_cast<int>(type);
+ break;
+ }
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h
new file mode 100644
index 00000000000..7997fbcc329
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_CONNECTION_CLOSE_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_CONNECTION_CLOSE_FRAME_H_
+
+#include <ostream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// There are three different forms of CONNECTION_CLOSE.
+typedef enum QuicConnectionCloseType {
+ GOOGLE_QUIC_CONNECTION_CLOSE = 0,
+ IETF_QUIC_TRANSPORT_CONNECTION_CLOSE = 1,
+ IETF_QUIC_APPLICATION_CONNECTION_CLOSE = 2
+} QuicConnectionCloseType;
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicConnectionCloseType type);
+
+struct QUIC_EXPORT_PRIVATE QuicConnectionCloseFrame {
+ QuicConnectionCloseFrame();
+ QuicConnectionCloseFrame(QuicErrorCode error_code);
+ QuicConnectionCloseFrame(QuicErrorCode error_code, std::string error_details);
+ QuicConnectionCloseFrame(QuicIetfTransportErrorCodes transport_error_code,
+ QuicErrorCode extracted_error_code,
+ std::string error_details,
+ uint64_t frame_type);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicConnectionCloseFrame& c);
+
+ // Indicates whether the received CONNECTION_CLOSE frame is a Google QUIC
+ // CONNECTION_CLOSE, IETF QUIC CONNECTION_CLOSE.
+ QuicConnectionCloseType close_type;
+
+ // This is the error field in the frame.
+ // The CONNECTION_CLOSE frame reports a 16-bit error code:
+ // - The transport error code as reported in a CONNECTION_CLOSE/Transport
+ // frame,
+ // - An opaque 16-bit code as reported in CONNECTION_CLOSE/Application frames,
+ // - A QuicErrorCode, which is used in Google QUIC.
+ union {
+ QuicIetfTransportErrorCodes transport_error_code;
+ uint16_t application_error_code;
+ QuicErrorCode quic_error_code;
+ };
+
+ // This error code is extracted from, or added to, the "QuicErrorCode:
+ // QUIC_...(123)" text in the error_details. It provides fine-grained
+ // information as to the source of the error.
+ QuicErrorCode extracted_error_code;
+
+ // String with additional error details. "QuicErrorCode: 123" will be appended
+ // to the error details when sending IETF QUIC Connection Close and
+ // Application Close frames and parsed into extracted_error_code upon receipt,
+ // when present.
+ std::string error_details;
+
+ // The frame type present in the IETF transport connection close frame.
+ // Not populated for the Google QUIC or application connection close frames.
+ // Contains the type of frame that triggered the connection close. Made a
+ // uint64, as opposed to the QuicIetfFrameType, to support possible
+ // extensions as well as reporting invalid frame types received from the peer.
+ uint64_t transport_close_frame_type;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_CONNECTION_CLOSE_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.cc
new file mode 100644
index 00000000000..fafbba85b6b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.cc
@@ -0,0 +1,42 @@
+// 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 "net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicCryptoFrame::QuicCryptoFrame()
+ : QuicCryptoFrame(ENCRYPTION_INITIAL, 0, nullptr, 0) {}
+
+QuicCryptoFrame::QuicCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicPacketLength data_length)
+ : QuicCryptoFrame(level, offset, nullptr, data_length) {}
+
+QuicCryptoFrame::QuicCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicStringPiece data)
+ : QuicCryptoFrame(level, offset, data.data(), data.length()) {}
+
+QuicCryptoFrame::QuicCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ const char* data_buffer,
+ QuicPacketLength data_length)
+ : level(level),
+ data_length(data_length),
+ data_buffer(data_buffer),
+ offset(offset) {}
+
+QuicCryptoFrame::~QuicCryptoFrame() {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicCryptoFrame& stream_frame) {
+ os << "{ offset: " << stream_frame.offset
+ << ", length: " << stream_frame.data_length << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h
new file mode 100644
index 00000000000..a3968fe7f27
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h
@@ -0,0 +1,51 @@
+// 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_CORE_FRAMES_QUIC_CRYPTO_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_CRYPTO_FRAME_H_
+
+#include <memory>
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicCryptoFrame {
+ QuicCryptoFrame();
+ QuicCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicPacketLength data_length);
+ QuicCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicStringPiece data);
+ ~QuicCryptoFrame();
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+ const QuicCryptoFrame& s);
+
+ // When writing a crypto frame to a packet, the packet must be encrypted at
+ // |level|. When a crypto frame is read, the encryption level of the packet it
+ // was received in is put in |level|.
+ EncryptionLevel level;
+ QuicPacketLength data_length;
+ // When reading, |data_buffer| points to the data that was received in the
+ // frame. |data_buffer| is not used when writing.
+ const char* data_buffer;
+ QuicStreamOffset offset; // Location of this data in the stream.
+
+ QuicCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ const char* data_buffer,
+ QuicPacketLength data_length);
+};
+static_assert(sizeof(QuicCryptoFrame) <= 64,
+ "Keep the QuicCryptoFrame size to a cacheline.");
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_CRYPTO_FRAME_H_
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
new file mode 100644
index 00000000000..95fee41f6bf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicFrame::QuicFrame() {}
+
+QuicFrame::QuicFrame(QuicPaddingFrame padding_frame)
+ : padding_frame(padding_frame) {}
+
+QuicFrame::QuicFrame(QuicStreamFrame stream_frame)
+ : stream_frame(stream_frame) {}
+
+QuicFrame::QuicFrame(QuicCryptoFrame* crypto_frame)
+ : type(CRYPTO_FRAME), crypto_frame(crypto_frame) {}
+
+QuicFrame::QuicFrame(QuicAckFrame* frame) : type(ACK_FRAME), ack_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicMtuDiscoveryFrame frame)
+ : mtu_discovery_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicStopWaitingFrame frame) : stop_waiting_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicPingFrame frame) : ping_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicRstStreamFrame* frame)
+ : type(RST_STREAM_FRAME), rst_stream_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicConnectionCloseFrame* frame)
+ : type(CONNECTION_CLOSE_FRAME), connection_close_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicGoAwayFrame* frame)
+ : type(GOAWAY_FRAME), goaway_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicWindowUpdateFrame* frame)
+ : type(WINDOW_UPDATE_FRAME), window_update_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicBlockedFrame* frame)
+ : type(BLOCKED_FRAME), blocked_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicNewConnectionIdFrame* frame)
+ : type(NEW_CONNECTION_ID_FRAME), new_connection_id_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicRetireConnectionIdFrame* frame)
+ : type(RETIRE_CONNECTION_ID_FRAME), retire_connection_id_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicMaxStreamIdFrame frame) : max_stream_id_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicStreamIdBlockedFrame frame)
+ : stream_id_blocked_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicPathResponseFrame* frame)
+ : type(PATH_RESPONSE_FRAME), path_response_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicPathChallengeFrame* frame)
+ : type(PATH_CHALLENGE_FRAME), path_challenge_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicStopSendingFrame* frame)
+ : type(STOP_SENDING_FRAME), stop_sending_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicMessageFrame* frame)
+ : type(MESSAGE_FRAME), message_frame(frame) {}
+
+QuicFrame::QuicFrame(QuicNewTokenFrame* frame)
+ : type(NEW_TOKEN_FRAME), new_token_frame(frame) {}
+
+void DeleteFrames(QuicFrames* frames) {
+ for (QuicFrame& frame : *frames) {
+ DeleteFrame(&frame);
+ }
+ frames->clear();
+}
+
+void DeleteFrame(QuicFrame* frame) {
+ switch (frame->type) {
+ // Frames smaller than a pointer are inlined, so don't need to be deleted.
+ case PADDING_FRAME:
+ case MTU_DISCOVERY_FRAME:
+ case PING_FRAME:
+ case MAX_STREAM_ID_FRAME:
+ case STOP_WAITING_FRAME:
+ case STREAM_ID_BLOCKED_FRAME:
+ case STREAM_FRAME:
+ break;
+ case ACK_FRAME:
+ delete frame->ack_frame;
+ break;
+ case RST_STREAM_FRAME:
+ delete frame->rst_stream_frame;
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ delete frame->connection_close_frame;
+ break;
+ case GOAWAY_FRAME:
+ delete frame->goaway_frame;
+ break;
+ case BLOCKED_FRAME:
+ delete frame->blocked_frame;
+ break;
+ case WINDOW_UPDATE_FRAME:
+ delete frame->window_update_frame;
+ break;
+ case PATH_CHALLENGE_FRAME:
+ delete frame->path_challenge_frame;
+ break;
+ case STOP_SENDING_FRAME:
+ delete frame->stop_sending_frame;
+ break;
+ case NEW_CONNECTION_ID_FRAME:
+ delete frame->new_connection_id_frame;
+ break;
+ case RETIRE_CONNECTION_ID_FRAME:
+ delete frame->retire_connection_id_frame;
+ break;
+ case PATH_RESPONSE_FRAME:
+ delete frame->path_response_frame;
+ break;
+ case MESSAGE_FRAME:
+ delete frame->message_frame;
+ break;
+ case CRYPTO_FRAME:
+ delete frame->crypto_frame;
+ break;
+ case NEW_TOKEN_FRAME:
+ delete frame->new_token_frame;
+ break;
+
+ case NUM_FRAME_TYPES:
+ DCHECK(false) << "Cannot delete type: " << frame->type;
+ }
+}
+
+void RemoveFramesForStream(QuicFrames* frames, QuicStreamId stream_id) {
+ auto it = frames->begin();
+ while (it != frames->end()) {
+ if (it->type != STREAM_FRAME || it->stream_frame.stream_id != stream_id) {
+ ++it;
+ continue;
+ }
+ it = frames->erase(it);
+ }
+}
+
+bool IsControlFrame(QuicFrameType type) {
+ switch (type) {
+ case RST_STREAM_FRAME:
+ case GOAWAY_FRAME:
+ case WINDOW_UPDATE_FRAME:
+ case BLOCKED_FRAME:
+ case STREAM_ID_BLOCKED_FRAME:
+ case MAX_STREAM_ID_FRAME:
+ case PING_FRAME:
+ case STOP_SENDING_FRAME:
+ return true;
+ default:
+ return false;
+ }
+}
+
+QuicControlFrameId GetControlFrameId(const QuicFrame& frame) {
+ switch (frame.type) {
+ case RST_STREAM_FRAME:
+ return frame.rst_stream_frame->control_frame_id;
+ case GOAWAY_FRAME:
+ return frame.goaway_frame->control_frame_id;
+ case WINDOW_UPDATE_FRAME:
+ return frame.window_update_frame->control_frame_id;
+ case BLOCKED_FRAME:
+ return frame.blocked_frame->control_frame_id;
+ case STREAM_ID_BLOCKED_FRAME:
+ return frame.stream_id_blocked_frame.control_frame_id;
+ case MAX_STREAM_ID_FRAME:
+ return frame.max_stream_id_frame.control_frame_id;
+ case PING_FRAME:
+ return frame.ping_frame.control_frame_id;
+ case STOP_SENDING_FRAME:
+ return frame.stop_sending_frame->control_frame_id;
+ default:
+ return kInvalidControlFrameId;
+ }
+}
+
+void SetControlFrameId(QuicControlFrameId control_frame_id, QuicFrame* frame) {
+ switch (frame->type) {
+ case RST_STREAM_FRAME:
+ frame->rst_stream_frame->control_frame_id = control_frame_id;
+ return;
+ case GOAWAY_FRAME:
+ frame->goaway_frame->control_frame_id = control_frame_id;
+ return;
+ case WINDOW_UPDATE_FRAME:
+ frame->window_update_frame->control_frame_id = control_frame_id;
+ return;
+ case BLOCKED_FRAME:
+ frame->blocked_frame->control_frame_id = control_frame_id;
+ return;
+ case PING_FRAME:
+ frame->ping_frame.control_frame_id = control_frame_id;
+ return;
+ case STREAM_ID_BLOCKED_FRAME:
+ frame->stream_id_blocked_frame.control_frame_id = control_frame_id;
+ return;
+ case MAX_STREAM_ID_FRAME:
+ frame->max_stream_id_frame.control_frame_id = control_frame_id;
+ return;
+ case STOP_SENDING_FRAME:
+ frame->stop_sending_frame->control_frame_id = control_frame_id;
+ return;
+ default:
+ QUIC_BUG
+ << "Try to set control frame id of a frame without control frame id";
+ }
+}
+
+QuicFrame CopyRetransmittableControlFrame(const QuicFrame& frame) {
+ QuicFrame copy;
+ switch (frame.type) {
+ case RST_STREAM_FRAME:
+ copy = QuicFrame(new QuicRstStreamFrame(*frame.rst_stream_frame));
+ break;
+ case GOAWAY_FRAME:
+ copy = QuicFrame(new QuicGoAwayFrame(*frame.goaway_frame));
+ break;
+ case WINDOW_UPDATE_FRAME:
+ copy = QuicFrame(new QuicWindowUpdateFrame(*frame.window_update_frame));
+ break;
+ case BLOCKED_FRAME:
+ copy = QuicFrame(new QuicBlockedFrame(*frame.blocked_frame));
+ break;
+ case PING_FRAME:
+ copy = QuicFrame(QuicPingFrame(frame.ping_frame.control_frame_id));
+ break;
+ case STOP_SENDING_FRAME:
+ copy = QuicFrame(new QuicStopSendingFrame(*frame.stop_sending_frame));
+ break;
+ case STREAM_ID_BLOCKED_FRAME:
+ copy = QuicFrame(QuicStreamIdBlockedFrame(frame.stream_id_blocked_frame));
+ break;
+ case MAX_STREAM_ID_FRAME:
+ copy = QuicFrame(QuicMaxStreamIdFrame(frame.max_stream_id_frame));
+ break;
+ default:
+ QUIC_BUG << "Try to copy a non-retransmittable control frame: " << frame;
+ copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId));
+ break;
+ }
+ return copy;
+}
+
+std::ostream& operator<<(std::ostream& os, const QuicFrame& frame) {
+ switch (frame.type) {
+ case PADDING_FRAME: {
+ os << "type { PADDING_FRAME } " << frame.padding_frame;
+ break;
+ }
+ case RST_STREAM_FRAME: {
+ os << "type { RST_STREAM_FRAME } " << *(frame.rst_stream_frame);
+ break;
+ }
+ case CONNECTION_CLOSE_FRAME: {
+ os << "type { CONNECTION_CLOSE_FRAME } "
+ << *(frame.connection_close_frame);
+ break;
+ }
+ case GOAWAY_FRAME: {
+ os << "type { GOAWAY_FRAME } " << *(frame.goaway_frame);
+ break;
+ }
+ case WINDOW_UPDATE_FRAME: {
+ os << "type { WINDOW_UPDATE_FRAME } " << *(frame.window_update_frame);
+ break;
+ }
+ case BLOCKED_FRAME: {
+ os << "type { BLOCKED_FRAME } " << *(frame.blocked_frame);
+ break;
+ }
+ case STREAM_FRAME: {
+ os << "type { STREAM_FRAME } " << frame.stream_frame;
+ break;
+ }
+ case ACK_FRAME: {
+ os << "type { ACK_FRAME } " << *(frame.ack_frame);
+ break;
+ }
+ case STOP_WAITING_FRAME: {
+ os << "type { STOP_WAITING_FRAME } " << frame.stop_waiting_frame;
+ break;
+ }
+ case PING_FRAME: {
+ os << "type { PING_FRAME } " << frame.ping_frame;
+ break;
+ }
+ case MTU_DISCOVERY_FRAME: {
+ os << "type { MTU_DISCOVERY_FRAME } ";
+ break;
+ }
+ case NEW_CONNECTION_ID_FRAME:
+ os << "type { NEW_CONNECTION_ID } " << *(frame.new_connection_id_frame);
+ break;
+ case RETIRE_CONNECTION_ID_FRAME:
+ os << "type { RETIRE_CONNECTION_ID } "
+ << *(frame.retire_connection_id_frame);
+ break;
+ case MAX_STREAM_ID_FRAME:
+ os << "type { MAX_STREAM_ID } " << frame.max_stream_id_frame;
+ break;
+ case STREAM_ID_BLOCKED_FRAME:
+ os << "type { STREAM_ID_BLOCKED } " << frame.stream_id_blocked_frame;
+ break;
+ case PATH_RESPONSE_FRAME:
+ os << "type { PATH_RESPONSE } " << *(frame.path_response_frame);
+ break;
+ case PATH_CHALLENGE_FRAME:
+ os << "type { PATH_CHALLENGE } " << *(frame.path_challenge_frame);
+ break;
+ case STOP_SENDING_FRAME:
+ os << "type { STOP_SENDING } " << *(frame.stop_sending_frame);
+ break;
+ case MESSAGE_FRAME:
+ os << "type { MESSAGE_FRAME }" << *(frame.message_frame);
+ break;
+ case NEW_TOKEN_FRAME:
+ os << "type { NEW_TOKEN_FRAME }" << *(frame.new_token_frame);
+ break;
+ default: {
+ QUIC_LOG(ERROR) << "Unknown frame type: " << frame.type;
+ break;
+ }
+ }
+ return os;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..c452bf41303
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frame.h
@@ -0,0 +1,145 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_FRAME_H_
+
+#include <ostream>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_message_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicFrame {
+ QuicFrame();
+ // Please keep the constructors in the same order as the union below.
+ explicit QuicFrame(QuicPaddingFrame padding_frame);
+ explicit QuicFrame(QuicMtuDiscoveryFrame frame);
+ explicit QuicFrame(QuicPingFrame frame);
+ explicit QuicFrame(QuicMaxStreamIdFrame frame);
+ explicit QuicFrame(QuicStopWaitingFrame frame);
+ explicit QuicFrame(QuicStreamIdBlockedFrame frame);
+ explicit QuicFrame(QuicStreamFrame stream_frame);
+
+ explicit QuicFrame(QuicAckFrame* frame);
+ explicit QuicFrame(QuicRstStreamFrame* frame);
+ explicit QuicFrame(QuicConnectionCloseFrame* frame);
+ explicit QuicFrame(QuicGoAwayFrame* frame);
+ explicit QuicFrame(QuicWindowUpdateFrame* frame);
+ explicit QuicFrame(QuicBlockedFrame* frame);
+ explicit QuicFrame(QuicNewConnectionIdFrame* frame);
+ explicit QuicFrame(QuicRetireConnectionIdFrame* frame);
+ explicit QuicFrame(QuicNewTokenFrame* frame);
+ explicit QuicFrame(QuicPathResponseFrame* frame);
+ explicit QuicFrame(QuicPathChallengeFrame* frame);
+ explicit QuicFrame(QuicStopSendingFrame* frame);
+ explicit QuicFrame(QuicMessageFrame* message_frame);
+ explicit QuicFrame(QuicCryptoFrame* crypto_frame);
+
+ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os,
+ const QuicFrame& frame);
+
+ union {
+ // Inlined frames.
+ // Overlapping inlined frames have a |type| field at the same 0 offset as
+ // QuicFrame does for out of line frames below, allowing use of the
+ // remaining 7 bytes after offset for frame-type specific fields.
+ QuicPaddingFrame padding_frame;
+ QuicMtuDiscoveryFrame mtu_discovery_frame;
+ QuicPingFrame ping_frame;
+ QuicMaxStreamIdFrame max_stream_id_frame;
+ QuicStopWaitingFrame stop_waiting_frame;
+ QuicStreamIdBlockedFrame stream_id_blocked_frame;
+ QuicStreamFrame stream_frame;
+
+ // Out of line frames.
+ struct {
+ QuicFrameType type;
+
+ // TODO(wub): These frames can also be inlined without increasing the size
+ // of QuicFrame: QuicRstStreamFrame, QuicWindowUpdateFrame,
+ // QuicBlockedFrame, QuicPathResponseFrame, QuicPathChallengeFrame and
+ // QuicStopSendingFrame.
+ union {
+ QuicAckFrame* ack_frame;
+ QuicRstStreamFrame* rst_stream_frame;
+ QuicConnectionCloseFrame* connection_close_frame;
+ QuicGoAwayFrame* goaway_frame;
+ QuicWindowUpdateFrame* window_update_frame;
+ QuicBlockedFrame* blocked_frame;
+ QuicNewConnectionIdFrame* new_connection_id_frame;
+ QuicRetireConnectionIdFrame* retire_connection_id_frame;
+ QuicPathResponseFrame* path_response_frame;
+ QuicPathChallengeFrame* path_challenge_frame;
+ QuicStopSendingFrame* stop_sending_frame;
+ QuicMessageFrame* message_frame;
+ QuicCryptoFrame* crypto_frame;
+ QuicNewTokenFrame* new_token_frame;
+ };
+ };
+ };
+};
+
+static_assert(sizeof(QuicFrame) <= 24,
+ "Frames larger than 24 bytes should be referenced by pointer.");
+static_assert(offsetof(QuicStreamFrame, type) == offsetof(QuicFrame, type),
+ "Offset of |type| must match in QuicFrame and QuicStreamFrame");
+
+// A inline size of 1 is chosen to optimize the typical use case of
+// 1-stream-frame in QuicTransmissionInfo.retransmittable_frames.
+typedef QuicInlinedVector<QuicFrame, 1> QuicFrames;
+
+// Deletes all the sub-frames contained in |frames|.
+QUIC_EXPORT_PRIVATE void DeleteFrames(QuicFrames* frames);
+
+// Delete the sub-frame contained in |frame|.
+QUIC_EXPORT_PRIVATE void DeleteFrame(QuicFrame* frame);
+
+// Deletes all the QuicStreamFrames for the specified |stream_id|.
+QUIC_EXPORT_PRIVATE void RemoveFramesForStream(QuicFrames* frames,
+ QuicStreamId stream_id);
+
+// Returns true if |type| is a retransmittable control frame.
+QUIC_EXPORT_PRIVATE bool IsControlFrame(QuicFrameType type);
+
+// Returns control_frame_id of |frame|. Returns kInvalidControlFrameId if
+// |frame| does not have a valid control_frame_id.
+QUIC_EXPORT_PRIVATE QuicControlFrameId
+GetControlFrameId(const QuicFrame& frame);
+
+// Sets control_frame_id of |frame| to |control_frame_id|.
+QUIC_EXPORT_PRIVATE void SetControlFrameId(QuicControlFrameId control_frame_id,
+ QuicFrame* frame);
+
+// Returns a copy of |frame|.
+QUIC_EXPORT_PRIVATE QuicFrame
+CopyRetransmittableControlFrame(const QuicFrame& frame);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_FRAME_H_
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
new file mode 100644
index 00000000000..c432657a442
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_frames_test.cc
@@ -0,0 +1,644 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicFramesTest : public QuicTest {};
+
+TEST_F(QuicFramesTest, AckFrameToString) {
+ QuicAckFrame frame;
+ frame.largest_acked = QuicPacketNumber(5);
+ frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(3);
+ frame.packets.Add(QuicPacketNumber(4));
+ frame.packets.Add(QuicPacketNumber(5));
+ frame.received_packet_times = {
+ {QuicPacketNumber(6),
+ QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(7)}};
+ std::ostringstream stream;
+ stream << frame;
+ EXPECT_EQ(
+ "{ largest_acked: 5, ack_delay_time: 3, packets: [ 4 5 ], "
+ "received_packets: [ 6 at 7 ], ecn_counters_populated: 0 }\n",
+ stream.str());
+ QuicFrame quic_frame(&frame);
+ EXPECT_FALSE(IsControlFrame(quic_frame.type));
+}
+
+TEST_F(QuicFramesTest, BigAckFrameToString) {
+ QuicAckFrame frame;
+ frame.largest_acked = QuicPacketNumber(500);
+ frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(3);
+ frame.packets.AddRange(QuicPacketNumber(4), QuicPacketNumber(501));
+ frame.received_packet_times = {
+ {QuicPacketNumber(500),
+ QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(7)}};
+ std::ostringstream stream;
+ stream << frame;
+ EXPECT_EQ(
+ "{ largest_acked: 500, ack_delay_time: 3, packets: [ 4...500 ], "
+ "received_packets: [ 500 at 7 ], ecn_counters_populated: 0 }\n",
+ stream.str());
+ QuicFrame quic_frame(&frame);
+ EXPECT_FALSE(IsControlFrame(quic_frame.type));
+}
+
+TEST_F(QuicFramesTest, PaddingFrameToString) {
+ QuicPaddingFrame frame;
+ frame.num_padding_bytes = 1;
+ std::ostringstream stream;
+ stream << frame;
+ EXPECT_EQ("{ num_padding_bytes: 1 }\n", stream.str());
+ QuicFrame quic_frame(frame);
+ EXPECT_FALSE(IsControlFrame(quic_frame.type));
+}
+
+TEST_F(QuicFramesTest, RstStreamFrameToString) {
+ QuicRstStreamFrame rst_stream;
+ QuicFrame frame(&rst_stream);
+ SetControlFrameId(1, &frame);
+ EXPECT_EQ(1u, GetControlFrameId(frame));
+ rst_stream.stream_id = 1;
+ rst_stream.error_code = QUIC_STREAM_CANCELLED;
+ std::ostringstream stream;
+ stream << rst_stream;
+ EXPECT_EQ("{ control_frame_id: 1, stream_id: 1, error_code: 6 }\n",
+ stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, StopSendingFrameToString) {
+ QuicStopSendingFrame stop_sending;
+ QuicFrame frame(&stop_sending);
+ SetControlFrameId(1, &frame);
+ EXPECT_EQ(1u, GetControlFrameId(frame));
+ stop_sending.stream_id = 321;
+ stop_sending.application_error_code = QUIC_STREAM_CANCELLED;
+ std::ostringstream stream;
+ stream << stop_sending;
+ EXPECT_EQ(
+ "{ control_frame_id: 1, stream_id: 321, application_error_code: 6 }\n",
+ stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, StreamIdBlockedFrameToString) {
+ QuicStreamIdBlockedFrame stream_id_blocked;
+ QuicFrame frame(stream_id_blocked);
+ SetControlFrameId(1, &frame);
+ EXPECT_EQ(1u, GetControlFrameId(frame));
+ // QuicStreamIdBlocked is copied into a QuicFrame (as opposed to putting a
+ // pointer to it into QuicFrame) so need to work with the copy in |frame| and
+ // not the original one, stream_id_blocked.
+ frame.stream_id_blocked_frame.stream_id = 321;
+ std::ostringstream stream;
+ stream << frame.stream_id_blocked_frame;
+ EXPECT_EQ("{ control_frame_id: 1, stream id: 321 }\n", stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, MaxStreamIdFrameToString) {
+ QuicMaxStreamIdFrame max_stream_id;
+ QuicFrame frame(max_stream_id);
+ SetControlFrameId(1, &frame);
+ EXPECT_EQ(1u, GetControlFrameId(frame));
+ // QuicMaxStreamId is copied into a QuicFrame (as opposed to putting a
+ // pointer to it into QuicFrame) so need to work with the copy in |frame| and
+ // not the original one, max_stream_id.
+ frame.max_stream_id_frame.max_stream_id = 321;
+ std::ostringstream stream;
+ stream << frame.max_stream_id_frame;
+ EXPECT_EQ("{ control_frame_id: 1, stream_id: 321 }\n", stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, ConnectionCloseFrameToString) {
+ QuicConnectionCloseFrame frame;
+ frame.quic_error_code = QUIC_NETWORK_IDLE_TIMEOUT;
+ frame.error_details = "No recent network activity.";
+ std::ostringstream stream;
+ stream << frame;
+ // Note that "extracted_error_code: 122" is QUIC_IETF_GQUIC_ERROR_MISSING,
+ // indicating that, in fact, no extended error code was available from the
+ // underlying frame.
+ EXPECT_EQ(
+ "{ Close type: GOOGLE_QUIC_CONNECTION_CLOSE, error_code: 25, "
+ "extracted_error_code: 122, "
+ "error_details: 'No recent "
+ "network activity.', "
+ "frame_type: 0"
+ "}\n",
+ stream.str());
+ QuicFrame quic_frame(&frame);
+ EXPECT_FALSE(IsControlFrame(quic_frame.type));
+}
+
+TEST_F(QuicFramesTest, GoAwayFrameToString) {
+ QuicGoAwayFrame goaway_frame;
+ QuicFrame frame(&goaway_frame);
+ SetControlFrameId(2, &frame);
+ EXPECT_EQ(2u, GetControlFrameId(frame));
+ goaway_frame.error_code = QUIC_NETWORK_IDLE_TIMEOUT;
+ goaway_frame.last_good_stream_id = 2;
+ goaway_frame.reason_phrase = "Reason";
+ std::ostringstream stream;
+ stream << goaway_frame;
+ EXPECT_EQ(
+ "{ control_frame_id: 2, error_code: 25, last_good_stream_id: 2, "
+ "reason_phrase: "
+ "'Reason' }\n",
+ stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, WindowUpdateFrameToString) {
+ QuicWindowUpdateFrame window_update;
+ QuicFrame frame(&window_update);
+ SetControlFrameId(3, &frame);
+ EXPECT_EQ(3u, GetControlFrameId(frame));
+ std::ostringstream stream;
+ window_update.stream_id = 1;
+ window_update.byte_offset = 2;
+ stream << window_update;
+ EXPECT_EQ("{ control_frame_id: 3, stream_id: 1, byte_offset: 2 }\n",
+ stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, BlockedFrameToString) {
+ QuicBlockedFrame blocked;
+ QuicFrame frame(&blocked);
+ SetControlFrameId(4, &frame);
+ EXPECT_EQ(4u, GetControlFrameId(frame));
+ blocked.stream_id = 1;
+ std::ostringstream stream;
+ stream << blocked;
+ EXPECT_EQ("{ control_frame_id: 4, stream_id: 1 }\n", stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, PingFrameToString) {
+ QuicPingFrame ping;
+ QuicFrame frame(ping);
+ SetControlFrameId(5, &frame);
+ EXPECT_EQ(5u, GetControlFrameId(frame));
+ std::ostringstream stream;
+ stream << frame.ping_frame;
+ EXPECT_EQ("{ control_frame_id: 5 }\n", stream.str());
+ EXPECT_TRUE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, StreamFrameToString) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.fin = false;
+ frame.offset = 2;
+ frame.data_length = 3;
+ std::ostringstream stream;
+ stream << frame;
+ EXPECT_EQ("{ stream_id: 1, fin: 0, offset: 2, length: 3 }\n", stream.str());
+ EXPECT_FALSE(IsControlFrame(frame.type));
+}
+
+TEST_F(QuicFramesTest, StopWaitingFrameToString) {
+ QuicStopWaitingFrame frame;
+ frame.least_unacked = QuicPacketNumber(2);
+ std::ostringstream stream;
+ stream << frame;
+ EXPECT_EQ("{ least_unacked: 2 }\n", stream.str());
+ QuicFrame quic_frame(frame);
+ EXPECT_FALSE(IsControlFrame(quic_frame.type));
+}
+
+TEST_F(QuicFramesTest, IsAwaitingPacket) {
+ QuicAckFrame ack_frame1;
+ ack_frame1.largest_acked = QuicPacketNumber(10u);
+ ack_frame1.packets.AddRange(QuicPacketNumber(1), QuicPacketNumber(11));
+ EXPECT_TRUE(
+ IsAwaitingPacket(ack_frame1, QuicPacketNumber(11u), QuicPacketNumber()));
+ EXPECT_FALSE(
+ IsAwaitingPacket(ack_frame1, QuicPacketNumber(1u), QuicPacketNumber()));
+
+ ack_frame1.packets.Add(QuicPacketNumber(12));
+ EXPECT_TRUE(
+ IsAwaitingPacket(ack_frame1, QuicPacketNumber(11u), QuicPacketNumber()));
+
+ QuicAckFrame ack_frame2;
+ ack_frame2.largest_acked = QuicPacketNumber(100u);
+ ack_frame2.packets.AddRange(QuicPacketNumber(21), QuicPacketNumber(100));
+ EXPECT_FALSE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(11u),
+ QuicPacketNumber(20u)));
+ EXPECT_FALSE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(80u),
+ QuicPacketNumber(20u)));
+ EXPECT_TRUE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(101u),
+ QuicPacketNumber(20u)));
+
+ ack_frame2.packets.AddRange(QuicPacketNumber(102), QuicPacketNumber(200));
+ EXPECT_TRUE(IsAwaitingPacket(ack_frame2, QuicPacketNumber(101u),
+ QuicPacketNumber(20u)));
+}
+
+TEST_F(QuicFramesTest, AddPacket) {
+ QuicAckFrame ack_frame1;
+ ack_frame1.packets.Add(QuicPacketNumber(1));
+ ack_frame1.packets.Add(QuicPacketNumber(99));
+
+ EXPECT_EQ(2u, ack_frame1.packets.NumIntervals());
+ EXPECT_EQ(QuicPacketNumber(1u), ack_frame1.packets.Min());
+ EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
+ expected_intervals.emplace_back(
+ QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2)));
+ expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(99), QuicPacketNumber(100)));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
+ ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+ EXPECT_EQ(expected_intervals, actual_intervals);
+
+ ack_frame1.packets.Add(QuicPacketNumber(20));
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2(
+ ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals2;
+ expected_intervals2.emplace_back(
+ QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2)));
+ expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(20), QuicPacketNumber(21)));
+ expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(99), QuicPacketNumber(100)));
+
+ EXPECT_EQ(3u, ack_frame1.packets.NumIntervals());
+ EXPECT_EQ(expected_intervals2, actual_intervals2);
+
+ ack_frame1.packets.Add(QuicPacketNumber(19));
+ ack_frame1.packets.Add(QuicPacketNumber(21));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals3(
+ ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals3;
+ expected_intervals3.emplace_back(
+ QuicInterval<QuicPacketNumber>(QuicPacketNumber(1), QuicPacketNumber(2)));
+ expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(19), QuicPacketNumber(22)));
+ expected_intervals3.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(99), QuicPacketNumber(100)));
+
+ EXPECT_EQ(expected_intervals3, actual_intervals3);
+
+ ack_frame1.packets.Add(QuicPacketNumber(20));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals4(
+ ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+ EXPECT_EQ(expected_intervals3, actual_intervals4);
+
+ QuicAckFrame ack_frame2;
+ ack_frame2.packets.Add(QuicPacketNumber(20));
+ ack_frame2.packets.Add(QuicPacketNumber(40));
+ ack_frame2.packets.Add(QuicPacketNumber(60));
+ ack_frame2.packets.Add(QuicPacketNumber(10));
+ ack_frame2.packets.Add(QuicPacketNumber(80));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals5(
+ ack_frame2.packets.begin(), ack_frame2.packets.end());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals5;
+ expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(10), QuicPacketNumber(11)));
+ expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(20), QuicPacketNumber(21)));
+ expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(40), QuicPacketNumber(41)));
+ expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(60), QuicPacketNumber(61)));
+ expected_intervals5.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(80), QuicPacketNumber(81)));
+
+ EXPECT_EQ(expected_intervals5, actual_intervals5);
+}
+
+TEST_F(QuicFramesTest, AddInterval) {
+ QuicAckFrame ack_frame1;
+ ack_frame1.packets.AddRange(QuicPacketNumber(1), QuicPacketNumber(10));
+ ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(100));
+
+ EXPECT_EQ(2u, ack_frame1.packets.NumIntervals());
+ EXPECT_EQ(QuicPacketNumber(1u), ack_frame1.packets.Min());
+ EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
+ expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(1), QuicPacketNumber(10)));
+ expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(50), QuicPacketNumber(100)));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
+ ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+ EXPECT_EQ(expected_intervals, actual_intervals);
+
+ // Ensure adding a range within the existing ranges fails.
+ EXPECT_QUIC_BUG(
+ ack_frame1.packets.AddRange(QuicPacketNumber(20), QuicPacketNumber(30)),
+ "");
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2(
+ ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals2;
+ expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(1), QuicPacketNumber(10)));
+ expected_intervals2.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(50), QuicPacketNumber(100)));
+
+ EXPECT_EQ(expected_intervals2.size(), ack_frame1.packets.NumIntervals());
+ EXPECT_EQ(expected_intervals2, actual_intervals2);
+
+ // Add ranges at both ends.
+ QuicAckFrame ack_frame2;
+ ack_frame2.packets.AddRange(QuicPacketNumber(20), QuicPacketNumber(25));
+ ack_frame2.packets.AddRange(QuicPacketNumber(40), QuicPacketNumber(45));
+ ack_frame2.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(65));
+ ack_frame2.packets.AddRange(QuicPacketNumber(10), QuicPacketNumber(15));
+ ack_frame2.packets.AddRange(QuicPacketNumber(80), QuicPacketNumber(85));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals8(
+ ack_frame2.packets.begin(), ack_frame2.packets.end());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals8;
+ expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(10), QuicPacketNumber(15)));
+ expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(20), QuicPacketNumber(25)));
+ expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(40), QuicPacketNumber(45)));
+ expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(60), QuicPacketNumber(65)));
+ expected_intervals8.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(80), QuicPacketNumber(85)));
+
+ EXPECT_EQ(expected_intervals8, actual_intervals8);
+}
+
+TEST_F(QuicFramesTest, AddAdjacentForward) {
+ QuicAckFrame ack_frame1;
+ ack_frame1.packets.Add(QuicPacketNumber(49));
+ ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(60));
+ ack_frame1.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(70));
+ ack_frame1.packets.AddRange(QuicPacketNumber(70), QuicPacketNumber(100));
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
+ expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(49), QuicPacketNumber(100)));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
+ ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+ EXPECT_EQ(expected_intervals, actual_intervals);
+}
+
+TEST_F(QuicFramesTest, AddAdjacentReverse) {
+ QuicAckFrame ack_frame1;
+ ack_frame1.packets.AddRange(QuicPacketNumber(70), QuicPacketNumber(100));
+ ack_frame1.packets.AddRange(QuicPacketNumber(60), QuicPacketNumber(70));
+ ack_frame1.packets.AddRange(QuicPacketNumber(50), QuicPacketNumber(60));
+ ack_frame1.packets.Add(QuicPacketNumber(49));
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
+ expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(49), QuicPacketNumber(100)));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
+ ack_frame1.packets.begin(), ack_frame1.packets.end());
+
+ EXPECT_EQ(expected_intervals, actual_intervals);
+}
+
+TEST_F(QuicFramesTest, RemoveSmallestInterval) {
+ QuicAckFrame ack_frame1;
+ ack_frame1.largest_acked = QuicPacketNumber(100u);
+ ack_frame1.packets.AddRange(QuicPacketNumber(51), QuicPacketNumber(60));
+ ack_frame1.packets.AddRange(QuicPacketNumber(71), QuicPacketNumber(80));
+ ack_frame1.packets.AddRange(QuicPacketNumber(91), QuicPacketNumber(100));
+ ack_frame1.packets.RemoveSmallestInterval();
+ EXPECT_EQ(2u, ack_frame1.packets.NumIntervals());
+ EXPECT_EQ(QuicPacketNumber(71u), ack_frame1.packets.Min());
+ EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max());
+
+ ack_frame1.packets.RemoveSmallestInterval();
+ EXPECT_EQ(1u, ack_frame1.packets.NumIntervals());
+ EXPECT_EQ(QuicPacketNumber(91u), ack_frame1.packets.Min());
+ EXPECT_EQ(QuicPacketNumber(99u), ack_frame1.packets.Max());
+}
+
+class PacketNumberQueueTest : public QuicTest {};
+
+// Tests that a queue contains the expected data after calls to Add().
+TEST_F(PacketNumberQueueTest, AddRange) {
+ PacketNumberQueue queue;
+ queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(51));
+ queue.Add(QuicPacketNumber(53));
+
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber()));
+ for (int i = 1; i < 51; ++i) {
+ EXPECT_TRUE(queue.Contains(QuicPacketNumber(i)));
+ }
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber(51)));
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber(52)));
+ EXPECT_TRUE(queue.Contains(QuicPacketNumber(53)));
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber(54)));
+ EXPECT_EQ(51u, queue.NumPacketsSlow());
+ EXPECT_EQ(QuicPacketNumber(1u), queue.Min());
+ EXPECT_EQ(QuicPacketNumber(53u), queue.Max());
+
+ queue.Add(QuicPacketNumber(70));
+ EXPECT_EQ(QuicPacketNumber(70u), queue.Max());
+}
+
+// Tests Contains function
+TEST_F(PacketNumberQueueTest, Contains) {
+ PacketNumberQueue queue;
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber()));
+ queue.AddRange(QuicPacketNumber(5), QuicPacketNumber(10));
+ queue.Add(QuicPacketNumber(20));
+
+ for (int i = 1; i < 5; ++i) {
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber(i)));
+ }
+
+ for (int i = 5; i < 10; ++i) {
+ EXPECT_TRUE(queue.Contains(QuicPacketNumber(i)));
+ }
+ for (int i = 10; i < 20; ++i) {
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber(i)));
+ }
+ EXPECT_TRUE(queue.Contains(QuicPacketNumber(20)));
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber(21)));
+
+ PacketNumberQueue queue2;
+ EXPECT_FALSE(queue2.Contains(QuicPacketNumber(1)));
+ for (int i = 1; i < 51; ++i) {
+ queue2.Add(QuicPacketNumber(2 * i));
+ }
+ EXPECT_FALSE(queue2.Contains(QuicPacketNumber()));
+ for (int i = 1; i < 51; ++i) {
+ if (i % 2 == 0) {
+ EXPECT_TRUE(queue2.Contains(QuicPacketNumber(i)));
+ } else {
+ EXPECT_FALSE(queue2.Contains(QuicPacketNumber(i)));
+ }
+ }
+ EXPECT_FALSE(queue2.Contains(QuicPacketNumber(101)));
+}
+
+// Tests that a queue contains the expected data after calls to RemoveUpTo().
+TEST_F(PacketNumberQueueTest, Removal) {
+ PacketNumberQueue queue;
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber(51)));
+ queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100));
+
+ EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(51)));
+ EXPECT_FALSE(queue.RemoveUpTo(QuicPacketNumber(51)));
+
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber()));
+ for (int i = 1; i < 51; ++i) {
+ EXPECT_FALSE(queue.Contains(QuicPacketNumber(i)));
+ }
+ for (int i = 51; i < 100; ++i) {
+ EXPECT_TRUE(queue.Contains(QuicPacketNumber(i)));
+ }
+ EXPECT_EQ(49u, queue.NumPacketsSlow());
+ EXPECT_EQ(QuicPacketNumber(51u), queue.Min());
+ EXPECT_EQ(QuicPacketNumber(99u), queue.Max());
+
+ PacketNumberQueue queue2;
+ queue2.AddRange(QuicPacketNumber(1), QuicPacketNumber(5));
+ EXPECT_TRUE(queue2.RemoveUpTo(QuicPacketNumber(3)));
+ EXPECT_TRUE(queue2.RemoveUpTo(QuicPacketNumber(50)));
+ EXPECT_TRUE(queue2.Empty());
+}
+
+// Tests that a queue is empty when all of its elements are removed.
+TEST_F(PacketNumberQueueTest, Empty) {
+ PacketNumberQueue queue;
+ EXPECT_TRUE(queue.Empty());
+ EXPECT_EQ(0u, queue.NumPacketsSlow());
+
+ queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100));
+ EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(100)));
+ EXPECT_TRUE(queue.Empty());
+ EXPECT_EQ(0u, queue.NumPacketsSlow());
+}
+
+// Tests that logging the state of a PacketNumberQueue does not crash.
+TEST_F(PacketNumberQueueTest, LogDoesNotCrash) {
+ std::ostringstream oss;
+ PacketNumberQueue queue;
+ oss << queue;
+
+ queue.Add(QuicPacketNumber(1));
+ queue.AddRange(QuicPacketNumber(50), QuicPacketNumber(100));
+ oss << queue;
+}
+
+// Tests that the iterators returned from a packet queue iterate over the queue.
+TEST_F(PacketNumberQueueTest, Iterators) {
+ PacketNumberQueue queue;
+ queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100));
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
+ queue.begin(), queue.end());
+
+ PacketNumberQueue queue2;
+ for (int i = 1; i < 100; i++) {
+ queue2.AddRange(QuicPacketNumber(i), QuicPacketNumber(i + 1));
+ }
+
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2(
+ queue2.begin(), queue2.end());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
+ expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(1), QuicPacketNumber(100)));
+ EXPECT_EQ(expected_intervals, actual_intervals);
+ EXPECT_EQ(expected_intervals, actual_intervals2);
+ EXPECT_EQ(actual_intervals, actual_intervals2);
+}
+
+TEST_F(PacketNumberQueueTest, ReversedIterators) {
+ PacketNumberQueue queue;
+ queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(100));
+ PacketNumberQueue queue2;
+ for (int i = 1; i < 100; i++) {
+ queue2.AddRange(QuicPacketNumber(i), QuicPacketNumber(i + 1));
+ }
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals(
+ queue.rbegin(), queue.rend());
+ const std::vector<QuicInterval<QuicPacketNumber>> actual_intervals2(
+ queue2.rbegin(), queue2.rend());
+
+ std::vector<QuicInterval<QuicPacketNumber>> expected_intervals;
+ expected_intervals.emplace_back(QuicInterval<QuicPacketNumber>(
+ QuicPacketNumber(1), QuicPacketNumber(100)));
+
+ EXPECT_EQ(expected_intervals, actual_intervals);
+ EXPECT_EQ(expected_intervals, actual_intervals2);
+ EXPECT_EQ(actual_intervals, actual_intervals2);
+
+ PacketNumberQueue queue3;
+ for (int i = 1; i < 20; i++) {
+ queue3.Add(QuicPacketNumber(2 * i));
+ }
+
+ auto begin = queue3.begin();
+ auto end = queue3.end();
+ --end;
+ auto rbegin = queue3.rbegin();
+ auto rend = queue3.rend();
+ --rend;
+
+ EXPECT_EQ(*begin, *rend);
+ EXPECT_EQ(*rbegin, *end);
+}
+
+TEST_F(PacketNumberQueueTest, IntervalLengthAndRemoveInterval) {
+ PacketNumberQueue queue;
+ queue.AddRange(QuicPacketNumber(1), QuicPacketNumber(10));
+ queue.AddRange(QuicPacketNumber(20), QuicPacketNumber(30));
+ queue.AddRange(QuicPacketNumber(40), QuicPacketNumber(50));
+ EXPECT_EQ(3u, queue.NumIntervals());
+ EXPECT_EQ(10u, queue.LastIntervalLength());
+
+ EXPECT_TRUE(queue.RemoveUpTo(QuicPacketNumber(25)));
+ EXPECT_EQ(2u, queue.NumIntervals());
+ EXPECT_EQ(10u, queue.LastIntervalLength());
+ EXPECT_EQ(QuicPacketNumber(25u), queue.Min());
+ EXPECT_EQ(QuicPacketNumber(49u), queue.Max());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.cc
new file mode 100644
index 00000000000..3842f92690e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicGoAwayFrame::QuicGoAwayFrame()
+ : control_frame_id(kInvalidControlFrameId),
+ error_code(QUIC_NO_ERROR),
+ last_good_stream_id(0) {}
+
+QuicGoAwayFrame::QuicGoAwayFrame(QuicControlFrameId control_frame_id,
+ QuicErrorCode error_code,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason)
+ : control_frame_id(control_frame_id),
+ error_code(error_code),
+ last_good_stream_id(last_good_stream_id),
+ reason_phrase(reason) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicGoAwayFrame& goaway_frame) {
+ os << "{ control_frame_id: " << goaway_frame.control_frame_id
+ << ", error_code: " << goaway_frame.error_code
+ << ", last_good_stream_id: " << goaway_frame.last_good_stream_id
+ << ", reason_phrase: '" << goaway_frame.reason_phrase << "' }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h
new file mode 100644
index 00000000000..ecceee6fc0e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_goaway_frame.h
@@ -0,0 +1,36 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_GOAWAY_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_GOAWAY_FRAME_H_
+
+#include <ostream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicGoAwayFrame {
+ QuicGoAwayFrame();
+ QuicGoAwayFrame(QuicControlFrameId control_frame_id,
+ QuicErrorCode error_code,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+ const QuicGoAwayFrame& g);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+ QuicErrorCode error_code;
+ QuicStreamId last_good_stream_id;
+ std::string reason_phrase;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_GOAWAY_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h
new file mode 100644
index 00000000000..08c4869ab7e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h
@@ -0,0 +1,30 @@
+// 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_CORE_FRAMES_QUIC_INLINED_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_INLINED_FRAME_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// QuicInlinedFrame is the base class of all frame types that is inlined in the
+// QuicFrame class. It gurantees all inlined frame types contain a 'type' field
+// at offset 0, such that QuicFrame.type can get the correct frame type for both
+// inline and out-of-line frame types.
+template <typename DerivedT>
+struct QUIC_EXPORT_PRIVATE QuicInlinedFrame {
+ QuicInlinedFrame(QuicFrameType type) : type(type) {
+ static_assert(offsetof(DerivedT, type) == 0,
+ "type must be the first field.");
+ static_assert(sizeof(DerivedT) <= 24,
+ "Frames larger than 24 bytes should not be inlined.");
+ }
+ QuicFrameType type;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_INLINED_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.cc
new file mode 100644
index 00000000000..19270e8e23e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.h"
+
+namespace quic {
+
+QuicMaxStreamIdFrame::QuicMaxStreamIdFrame()
+ : QuicInlinedFrame(MAX_STREAM_ID_FRAME),
+ control_frame_id(kInvalidControlFrameId) {}
+
+QuicMaxStreamIdFrame::QuicMaxStreamIdFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId max_stream_id)
+ : QuicInlinedFrame(MAX_STREAM_ID_FRAME),
+ control_frame_id(control_frame_id),
+ max_stream_id(max_stream_id) {}
+
+std::ostream& operator<<(std::ostream& os, const QuicMaxStreamIdFrame& frame) {
+ os << "{ control_frame_id: " << frame.control_frame_id
+ << ", stream_id: " << frame.max_stream_id << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.h
new file mode 100644
index 00000000000..6177687d44e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_max_stream_id_frame.h
@@ -0,0 +1,40 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_MAX_STREAM_ID_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_MAX_STREAM_ID_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// IETF format MAX_STREAM_ID frame.
+// This frame is used by the sender to inform the peer of the largest
+// stream id that the peer may open and that the sender will accept.
+struct QUIC_EXPORT_PRIVATE QuicMaxStreamIdFrame
+ : public QuicInlinedFrame<QuicMaxStreamIdFrame> {
+ QuicMaxStreamIdFrame();
+ QuicMaxStreamIdFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId max_stream_id);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicMaxStreamIdFrame& frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+
+ // The maximum stream id to support.
+ QuicStreamId max_stream_id;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_MAX_STREAM_ID_FRAME_H_
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
new file mode 100644
index 00000000000..196f6e90f95
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.cc
@@ -0,0 +1,39 @@
+// 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 "net/third_party/quiche/src/quic/core/frames/quic_message_frame.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h"
+
+namespace quic {
+
+QuicMessageFrame::QuicMessageFrame()
+ : message_id(0), data(nullptr), message_length(0) {}
+
+QuicMessageFrame::QuicMessageFrame(QuicMessageId message_id)
+ : message_id(message_id), data(nullptr), message_length(0) {}
+
+QuicMessageFrame::QuicMessageFrame(QuicMessageId message_id,
+ QuicMemSliceSpan span)
+ : message_id(message_id), data(nullptr), message_length(0) {
+ span.ConsumeAll([&](QuicMemSlice slice) {
+ message_length += slice.length();
+ message_data.push_back(std::move(slice));
+ });
+}
+
+QuicMessageFrame::QuicMessageFrame(const char* data, QuicPacketLength length)
+ : message_id(0), data(data), message_length(length) {}
+
+QuicMessageFrame::~QuicMessageFrame() {}
+
+std::ostream& operator<<(std::ostream& os, const QuicMessageFrame& s) {
+ os << " message_id: " << s.message_id
+ << ", message_length: " << s.message_length << " }\n";
+ return os;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..78dafe39243
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_message_frame.h
@@ -0,0 +1,51 @@
+// 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_CORE_FRAMES_QUIC_MESSAGE_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_MESSAGE_FRAME_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+typedef QuicInlinedVector<QuicMemSlice, 1> QuicMessageData;
+
+struct QUIC_EXPORT_PRIVATE QuicMessageFrame {
+ QuicMessageFrame();
+ explicit QuicMessageFrame(QuicMessageId message_id);
+ QuicMessageFrame(QuicMessageId message_id, QuicMemSliceSpan span);
+ QuicMessageFrame(const char* data, QuicPacketLength length);
+
+ QuicMessageFrame(const QuicMessageFrame& other) = delete;
+ QuicMessageFrame& operator=(const QuicMessageFrame& other) = delete;
+
+ QuicMessageFrame(QuicMessageFrame&& other) = default;
+ QuicMessageFrame& operator=(QuicMessageFrame&& other) = default;
+
+ ~QuicMessageFrame();
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicMessageFrame& s);
+
+ // message_id is only used on the sender side and does not get serialized on
+ // wire.
+ QuicMessageId message_id;
+ // Not owned, only used on read path.
+ const char* data;
+ // Total length of message_data, must be fit into one packet.
+ QuicPacketLength message_length;
+
+ // The actual message data which is reference counted, used on write path.
+ QuicMessageData message_data;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_MESSAGE_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h
new file mode 100644
index 00000000000..330df2123e2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_mtu_discovery_frame.h
@@ -0,0 +1,23 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_MTU_DISCOVERY_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_MTU_DISCOVERY_FRAME_H_
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A path MTU discovery frame contains no payload and is serialized as a ping
+// frame.
+struct QUIC_EXPORT_PRIVATE QuicMtuDiscoveryFrame
+ : public QuicInlinedFrame<QuicMtuDiscoveryFrame> {
+ QuicMtuDiscoveryFrame() : QuicInlinedFrame(MTU_DISCOVERY_FRAME) {}
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_MTU_DISCOVERY_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.cc
new file mode 100644
index 00000000000..b7f63e22000
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicNewConnectionIdFrame::QuicNewConnectionIdFrame()
+ : control_frame_id(kInvalidControlFrameId),
+ connection_id(EmptyQuicConnectionId()),
+ sequence_number(0) {}
+
+QuicNewConnectionIdFrame::QuicNewConnectionIdFrame(
+ QuicControlFrameId control_frame_id,
+ QuicConnectionId connection_id,
+ QuicConnectionIdSequenceNumber sequence_number,
+ const QuicUint128 stateless_reset_token)
+ : control_frame_id(control_frame_id),
+ connection_id(connection_id),
+ sequence_number(sequence_number),
+ stateless_reset_token(stateless_reset_token) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicNewConnectionIdFrame& frame) {
+ os << "{ control_frame_id: " << frame.control_frame_id
+ << ", connection_id: " << frame.connection_id
+ << ", sequence_number: " << frame.sequence_number << " }\n";
+ return os;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..1948d224cb8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_connection_id_frame.h
@@ -0,0 +1,37 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_CONNECTION_ID_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_CONNECTION_ID_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicNewConnectionIdFrame {
+ QuicNewConnectionIdFrame();
+ QuicNewConnectionIdFrame(QuicControlFrameId control_frame_id,
+ QuicConnectionId connection_id,
+ QuicConnectionIdSequenceNumber sequence_number,
+ const QuicUint128 stateless_reset_token);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicNewConnectionIdFrame& frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+ QuicConnectionId connection_id;
+ QuicConnectionIdSequenceNumber sequence_number;
+ QuicUint128 stateless_reset_token;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_CONNECTION_ID_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.cc
new file mode 100644
index 00000000000..6e368666002
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.cc
@@ -0,0 +1,24 @@
+// 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 "net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicNewTokenFrame::QuicNewTokenFrame()
+ : control_frame_id(kInvalidControlFrameId) {}
+
+QuicNewTokenFrame::QuicNewTokenFrame(QuicControlFrameId control_frame_id,
+ std::string token)
+ : control_frame_id(control_frame_id), token(token) {}
+
+std::ostream& operator<<(std::ostream& os, const QuicNewTokenFrame& s) {
+ os << "{ control_frame_id: " << s.control_frame_id << ", token: " << s.token
+ << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.h
new file mode 100644
index 00000000000..5a063a3d7cf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_new_token_frame.h
@@ -0,0 +1,37 @@
+// 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_CORE_FRAMES_QUIC_NEW_TOKEN_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_TOKEN_FRAME_H_
+
+#include <memory>
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicNewTokenFrame {
+ QuicNewTokenFrame();
+ QuicNewTokenFrame(QuicControlFrameId control_frame_id, std::string token);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicNewTokenFrame& s);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+
+ std::string token;
+};
+static_assert(sizeof(QuicNewTokenFrame) <= 64,
+ "Keep the QuicNewTokenFrame size to a cacheline.");
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_NEW_TOKEN_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_padding_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_padding_frame.cc
new file mode 100644
index 00000000000..098a6647dec
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_padding_frame.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h"
+
+namespace quic {
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicPaddingFrame& padding_frame) {
+ os << "{ num_padding_bytes: " << padding_frame.num_padding_bytes << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h
new file mode 100644
index 00000000000..03e0a4041fe
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_padding_frame.h
@@ -0,0 +1,35 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_PADDING_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_PADDING_FRAME_H_
+
+#include <cstdint>
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A padding frame contains no payload.
+struct QUIC_EXPORT_PRIVATE QuicPaddingFrame
+ : public QuicInlinedFrame<QuicPaddingFrame> {
+ QuicPaddingFrame() : QuicInlinedFrame(PADDING_FRAME), num_padding_bytes(-1) {}
+ explicit QuicPaddingFrame(int num_padding_bytes)
+ : QuicInlinedFrame(PADDING_FRAME), num_padding_bytes(num_padding_bytes) {}
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicPaddingFrame& s);
+
+ // -1: full padding to the end of a max-sized packet
+ // otherwise: only pad up to num_padding_bytes bytes
+ int num_padding_bytes;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_PADDING_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.cc
new file mode 100644
index 00000000000..c1ca6a85dac
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.cc
@@ -0,0 +1,37 @@
+// 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 "net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+QuicPathChallengeFrame::QuicPathChallengeFrame()
+ : control_frame_id(kInvalidControlFrameId) {}
+
+QuicPathChallengeFrame::QuicPathChallengeFrame(
+ QuicControlFrameId control_frame_id,
+ const QuicPathFrameBuffer& data_buff)
+ : control_frame_id(control_frame_id) {
+ memcpy(data_buffer.data(), data_buff.data(), data_buffer.size());
+}
+
+QuicPathChallengeFrame::~QuicPathChallengeFrame() {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicPathChallengeFrame& frame) {
+ os << "{ control_frame_id: " << frame.control_frame_id
+ << ", data: " << static_cast<unsigned>(frame.data_buffer[0]) << " "
+ << static_cast<unsigned>(frame.data_buffer[1]) << " "
+ << static_cast<unsigned>(frame.data_buffer[2]) << " "
+ << static_cast<unsigned>(frame.data_buffer[3]) << " "
+ << static_cast<unsigned>(frame.data_buffer[4]) << " "
+ << static_cast<unsigned>(frame.data_buffer[5]) << " "
+ << static_cast<unsigned>(frame.data_buffer[6]) << " "
+ << static_cast<unsigned>(frame.data_buffer[7]) << " }\n";
+ return os;
+}
+
+} // 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
new file mode 100644
index 00000000000..46a010ae92b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_challenge_frame.h
@@ -0,0 +1,36 @@
+// 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_CORE_FRAMES_QUIC_PATH_CHALLENGE_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_PATH_CHALLENGE_FRAME_H_
+
+#include <memory>
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+// Size of the entire IETF Quic Path Challenge frame.
+const size_t kQuicPathChallengeFrameSize = kQuicPathFrameBufferSize;
+
+struct QUIC_EXPORT_PRIVATE QuicPathChallengeFrame {
+ QuicPathChallengeFrame();
+ QuicPathChallengeFrame(QuicControlFrameId control_frame_id,
+ const QuicPathFrameBuffer& data_buff);
+ ~QuicPathChallengeFrame();
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicPathChallengeFrame& frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+
+ QuicPathFrameBuffer data_buffer;
+};
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_PATH_CHALLENGE_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.cc
new file mode 100644
index 00000000000..85e9712b583
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.cc
@@ -0,0 +1,36 @@
+// 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 "net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+QuicPathResponseFrame::QuicPathResponseFrame()
+ : control_frame_id(kInvalidControlFrameId) {}
+
+QuicPathResponseFrame::QuicPathResponseFrame(
+ QuicControlFrameId control_frame_id,
+ const QuicPathFrameBuffer& data_buff)
+ : control_frame_id(control_frame_id) {
+ memcpy(data_buffer.data(), data_buff.data(), data_buffer.size());
+}
+
+QuicPathResponseFrame::~QuicPathResponseFrame() {}
+
+std::ostream& operator<<(std::ostream& os, const QuicPathResponseFrame& frame) {
+ os << "{ control_frame_id: " << frame.control_frame_id
+ << ", data: " << static_cast<unsigned>(frame.data_buffer[0]) << " "
+ << static_cast<unsigned>(frame.data_buffer[1]) << " "
+ << static_cast<unsigned>(frame.data_buffer[2]) << " "
+ << static_cast<unsigned>(frame.data_buffer[3]) << " "
+ << static_cast<unsigned>(frame.data_buffer[4]) << " "
+ << static_cast<unsigned>(frame.data_buffer[5]) << " "
+ << static_cast<unsigned>(frame.data_buffer[6]) << " "
+ << static_cast<unsigned>(frame.data_buffer[7]) << " }\n";
+ return os;
+}
+
+} // 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
new file mode 100644
index 00000000000..e953ad8aa52
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_path_response_frame.h
@@ -0,0 +1,36 @@
+// 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_CORE_FRAMES_QUIC_PATH_RESPONSE_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_PATH_RESPONSE_FRAME_H_
+
+#include <memory>
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+// Size of the entire IETF Quic Path Response frame.
+const size_t kQuicPathResponseFrameSize = kQuicPathFrameBufferSize;
+
+struct QUIC_EXPORT_PRIVATE QuicPathResponseFrame {
+ QuicPathResponseFrame();
+ QuicPathResponseFrame(QuicControlFrameId control_frame_id,
+ const QuicPathFrameBuffer& data_buff);
+ ~QuicPathResponseFrame();
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicPathResponseFrame& frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+
+ QuicPathFrameBuffer data_buffer;
+};
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_PATH_RESPONSE_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_ping_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ping_frame.cc
new file mode 100644
index 00000000000..d31efb0aae9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ping_frame.cc
@@ -0,0 +1,20 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h"
+
+namespace quic {
+
+QuicPingFrame::QuicPingFrame()
+ : QuicInlinedFrame(PING_FRAME), control_frame_id(kInvalidControlFrameId) {}
+
+QuicPingFrame::QuicPingFrame(QuicControlFrameId control_frame_id)
+ : QuicInlinedFrame(PING_FRAME), control_frame_id(control_frame_id) {}
+
+std::ostream& operator<<(std::ostream& os, const QuicPingFrame& ping_frame) {
+ os << "{ control_frame_id: " << ping_frame.control_frame_id << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h
new file mode 100644
index 00000000000..352d079951c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_ping_frame.h
@@ -0,0 +1,33 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_PING_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_PING_FRAME_H_
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A ping frame contains no payload, though it is retransmittable,
+// and ACK'd just like other normal frames.
+struct QUIC_EXPORT_PRIVATE QuicPingFrame
+ : public QuicInlinedFrame<QuicPingFrame> {
+ QuicPingFrame();
+ explicit QuicPingFrame(QuicControlFrameId control_frame_id);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicPingFrame& ping_frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_PING_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.cc
new file mode 100644
index 00000000000..6828ce41227
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.cc
@@ -0,0 +1,25 @@
+// 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 "net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicRetireConnectionIdFrame::QuicRetireConnectionIdFrame()
+ : control_frame_id(kInvalidControlFrameId), sequence_number(0) {}
+
+QuicRetireConnectionIdFrame::QuicRetireConnectionIdFrame(
+ QuicControlFrameId control_frame_id,
+ QuicConnectionIdSequenceNumber sequence_number)
+ : control_frame_id(control_frame_id), sequence_number(sequence_number) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicRetireConnectionIdFrame& frame) {
+ os << "{ control_frame_id: " << frame.control_frame_id
+ << ", sequence_number: " << frame.sequence_number << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h
new file mode 100644
index 00000000000..79521f647a0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_retire_connection_id_frame.h
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_RETIRE_CONNECTION_ID_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_RETIRE_CONNECTION_ID_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicRetireConnectionIdFrame {
+ QuicRetireConnectionIdFrame();
+ QuicRetireConnectionIdFrame(QuicControlFrameId control_frame_id,
+ QuicConnectionIdSequenceNumber sequence_number);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicRetireConnectionIdFrame& frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+ QuicConnectionIdSequenceNumber sequence_number;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_RETIRE_CONNECTION_ID_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.cc
new file mode 100644
index 00000000000..7fbf365692f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicRstStreamFrame::QuicRstStreamFrame()
+ : control_frame_id(kInvalidControlFrameId),
+ stream_id(0),
+ error_code(QUIC_STREAM_NO_ERROR),
+ byte_offset(0) {}
+
+QuicRstStreamFrame::QuicRstStreamFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicStreamOffset bytes_written)
+ : control_frame_id(control_frame_id),
+ stream_id(stream_id),
+ error_code(error_code),
+ byte_offset(bytes_written) {}
+
+QuicRstStreamFrame::QuicRstStreamFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ uint16_t ietf_error_code,
+ QuicStreamOffset bytes_written)
+ : control_frame_id(control_frame_id),
+ stream_id(stream_id),
+ ietf_error_code(ietf_error_code),
+ byte_offset(bytes_written) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicRstStreamFrame& rst_frame) {
+ os << "{ control_frame_id: " << rst_frame.control_frame_id
+ << ", stream_id: " << rst_frame.stream_id
+ << ", error_code: " << rst_frame.error_code << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h
new file mode 100644
index 00000000000..0957614153e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h
@@ -0,0 +1,54 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_RST_STREAM_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_RST_STREAM_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicRstStreamFrame {
+ QuicRstStreamFrame();
+ QuicRstStreamFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicStreamOffset bytes_written);
+ QuicRstStreamFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ uint16_t ietf_error_code,
+ QuicStreamOffset bytes_written);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicRstStreamFrame& r);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+
+ QuicStreamId stream_id;
+
+ // Caller must know whether IETF- or Google- QUIC is in use and
+ // set the appropriate error code.
+ union {
+ QuicRstStreamErrorCode error_code;
+ // In IETF QUIC the code is up to the app on top of quic, so is
+ // more general than QuicRstStreamErrorCode allows.
+ uint16_t ietf_error_code;
+ };
+
+ // Used to update flow control windows. On termination of a stream, both
+ // endpoints must inform the peer of the number of bytes they have sent on
+ // that stream. This can be done through normal termination (data packet with
+ // FIN) or through a RST.
+ QuicStreamOffset byte_offset;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_RST_STREAM_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.cc
new file mode 100644
index 00000000000..1afd512150a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.cc
@@ -0,0 +1,30 @@
+// 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 "net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicStopSendingFrame::QuicStopSendingFrame()
+ : control_frame_id(kInvalidControlFrameId),
+ stream_id(0),
+ application_error_code(0) {}
+
+QuicStopSendingFrame::QuicStopSendingFrame(
+ QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ QuicApplicationErrorCode application_error_code)
+ : control_frame_id(control_frame_id),
+ stream_id(stream_id),
+ application_error_code(application_error_code) {}
+
+std::ostream& operator<<(std::ostream& os, const QuicStopSendingFrame& frame) {
+ os << "{ control_frame_id: " << frame.control_frame_id
+ << ", stream_id: " << frame.stream_id
+ << ", application_error_code: " << frame.application_error_code << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h
new file mode 100644
index 00000000000..7ca639d2eb3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_sending_frame.h
@@ -0,0 +1,34 @@
+// 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_CORE_FRAMES_QUIC_STOP_SENDING_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_SENDING_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicStopSendingFrame {
+ QuicStopSendingFrame();
+ QuicStopSendingFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ QuicApplicationErrorCode application_error_code);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicStopSendingFrame& frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+ QuicStreamId stream_id;
+ QuicApplicationErrorCode application_error_code;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_SENDING_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.cc
new file mode 100644
index 00000000000..02e0a62eac9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.cc
@@ -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.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicStopWaitingFrame::QuicStopWaitingFrame()
+ : QuicInlinedFrame(STOP_WAITING_FRAME) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicStopWaitingFrame& sent_info) {
+ os << "{ least_unacked: " << sent_info.least_unacked << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h
new file mode 100644
index 00000000000..84bfc2a706e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stop_waiting_frame.h
@@ -0,0 +1,30 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_WAITING_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_WAITING_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicStopWaitingFrame
+ : public QuicInlinedFrame<QuicStopWaitingFrame> {
+ QuicStopWaitingFrame();
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicStopWaitingFrame& s);
+
+ // The lowest packet we've sent which is unacked, and we expect an ack for.
+ QuicPacketNumber least_unacked;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_STOP_WAITING_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_frame.cc
new file mode 100644
index 00000000000..b9413c7e271
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_frame.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicStreamFrame::QuicStreamFrame()
+ : QuicStreamFrame(-1, false, 0, nullptr, 0) {}
+
+QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id,
+ bool fin,
+ QuicStreamOffset offset,
+ QuicStringPiece data)
+ : QuicStreamFrame(stream_id, fin, offset, data.data(), data.length()) {}
+
+QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id,
+ bool fin,
+ QuicStreamOffset offset,
+ QuicPacketLength data_length)
+ : QuicStreamFrame(stream_id, fin, offset, nullptr, data_length) {}
+
+QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id,
+ bool fin,
+ QuicStreamOffset offset,
+ const char* data_buffer,
+ QuicPacketLength data_length)
+ : QuicInlinedFrame(STREAM_FRAME),
+ fin(fin),
+ data_length(data_length),
+ stream_id(stream_id),
+ data_buffer(data_buffer),
+ offset(offset) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicStreamFrame& stream_frame) {
+ os << "{ stream_id: " << stream_frame.stream_id
+ << ", fin: " << stream_frame.fin << ", offset: " << stream_frame.offset
+ << ", length: " << stream_frame.data_length << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h
new file mode 100644
index 00000000000..6cd510d41f5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h
@@ -0,0 +1,51 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_FRAME_H_
+
+#include <memory>
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicStreamFrame
+ : public QuicInlinedFrame<QuicStreamFrame> {
+ QuicStreamFrame();
+ QuicStreamFrame(QuicStreamId stream_id,
+ bool fin,
+ QuicStreamOffset offset,
+ QuicStringPiece data);
+ QuicStreamFrame(QuicStreamId stream_id,
+ bool fin,
+ QuicStreamOffset offset,
+ QuicPacketLength data_length);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+ const QuicStreamFrame& s);
+
+ bool fin;
+ QuicPacketLength data_length;
+ QuicStreamId stream_id;
+ const char* data_buffer; // Not owned.
+ QuicStreamOffset offset; // Location of this data in the stream.
+
+ QuicStreamFrame(QuicStreamId stream_id,
+ bool fin,
+ QuicStreamOffset offset,
+ const char* data_buffer,
+ QuicPacketLength data_length);
+};
+static_assert(sizeof(QuicStreamFrame) <= 64,
+ "Keep the QuicStreamFrame size to a cacheline.");
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.cc
new file mode 100644
index 00000000000..627ad2a93ce
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.h"
+
+namespace quic {
+
+QuicStreamIdBlockedFrame::QuicStreamIdBlockedFrame()
+ : QuicInlinedFrame(STREAM_ID_BLOCKED_FRAME),
+ control_frame_id(kInvalidControlFrameId) {}
+
+QuicStreamIdBlockedFrame::QuicStreamIdBlockedFrame(
+ QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id)
+ : QuicInlinedFrame(STREAM_ID_BLOCKED_FRAME),
+ control_frame_id(control_frame_id),
+ stream_id(stream_id) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicStreamIdBlockedFrame& frame) {
+ os << "{ control_frame_id: " << frame.control_frame_id
+ << ", stream id: " << frame.stream_id << " }\n";
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.h b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.h
new file mode 100644
index 00000000000..f9ccca2d8b1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_stream_id_blocked_frame.h
@@ -0,0 +1,40 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_ID_BLOCKED_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_ID_BLOCKED_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_inlined_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// IETF format STREAM_ID_BLOCKED frame.
+// The sender uses this to inform the peer that the sender wished to
+// open a new stream but was blocked from doing so due due to the
+// maximum stream ID limit set by the peer (via a MAX_STREAM_ID frame)
+struct QUIC_EXPORT_PRIVATE QuicStreamIdBlockedFrame
+ : public QuicInlinedFrame<QuicStreamIdBlockedFrame> {
+ QuicStreamIdBlockedFrame();
+ QuicStreamIdBlockedFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicStreamIdBlockedFrame& frame);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+
+ QuicStreamId stream_id;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_STREAM_ID_BLOCKED_FRAME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.cc b/chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.cc
new file mode 100644
index 00000000000..07e31687fbd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicWindowUpdateFrame::QuicWindowUpdateFrame()
+ : control_frame_id(kInvalidControlFrameId) {}
+
+QuicWindowUpdateFrame::QuicWindowUpdateFrame(
+ QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ QuicStreamOffset byte_offset)
+ : control_frame_id(control_frame_id),
+ stream_id(stream_id),
+ byte_offset(byte_offset) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicWindowUpdateFrame& window_update_frame) {
+ os << "{ control_frame_id: " << window_update_frame.control_frame_id
+ << ", stream_id: " << window_update_frame.stream_id
+ << ", byte_offset: " << window_update_frame.byte_offset << " }\n";
+ return os;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..73163cecb19
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h
@@ -0,0 +1,48 @@
+// 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 QUICHE_QUIC_CORE_FRAMES_QUIC_WINDOW_UPDATE_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_WINDOW_UPDATE_FRAME_H_
+
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+// Flow control updates per-stream and at the connection level.
+// Based on SPDY's WINDOW_UPDATE frame, but uses an absolute byte offset rather
+// than a window delta.
+// TODO(rjshade): A possible future optimization is to make stream_id and
+// byte_offset variable length, similar to stream frames.
+struct QUIC_EXPORT_PRIVATE QuicWindowUpdateFrame {
+ QuicWindowUpdateFrame();
+ QuicWindowUpdateFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id,
+ QuicStreamOffset byte_offset);
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicWindowUpdateFrame& w);
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id;
+
+ // The stream this frame applies to. 0 is a special case meaning the overall
+ // connection rather than a specific stream.
+ QuicStreamId stream_id;
+
+ // Byte offset in the stream or connection. The receiver of this frame must
+ // not send data which would result in this offset being exceeded.
+ //
+ // TODO(fkastenholz): Rename this to max_data and change the type to
+ // QuicByteCount because the IETF defines this as the "maximum
+ // amount of data that can be sent".
+ QuicStreamOffset byte_offset;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_WINDOW_UPDATE_FRAME_H_
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
new file mode 100644
index 00000000000..e537c735377
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/end_to_end_test.cc
@@ -0,0 +1,3915 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstddef>
+#include <cstdint>
+#include <list>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_sleep.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h"
+#include "net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_client.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_server.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/server_thread.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+using spdy::kV3LowestPriority;
+using spdy::SETTINGS_MAX_HEADER_LIST_SIZE;
+using spdy::SpdyFramer;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdySerializedFrame;
+using spdy::SpdySettingsIR;
+
+namespace quic {
+namespace test {
+namespace {
+
+const char kFooResponseBody[] = "Artichoke hearts make me happy.";
+const char kBarResponseBody[] = "Palm hearts are pretty delicious, also.";
+const float kSessionToStreamRatio = 1.5;
+
+// Run all tests with the cross products of all versions.
+struct TestParams {
+ TestParams(const ParsedQuicVersionVector& client_supported_versions,
+ const ParsedQuicVersionVector& server_supported_versions,
+ ParsedQuicVersion negotiated_version,
+ bool client_supports_stateless_rejects,
+ bool server_uses_stateless_rejects_if_peer_supported,
+ QuicTag congestion_control_tag,
+ bool use_cheap_stateless_reject)
+ : client_supported_versions(client_supported_versions),
+ server_supported_versions(server_supported_versions),
+ negotiated_version(negotiated_version),
+ client_supports_stateless_rejects(client_supports_stateless_rejects),
+ server_uses_stateless_rejects_if_peer_supported(
+ server_uses_stateless_rejects_if_peer_supported),
+ congestion_control_tag(congestion_control_tag),
+ use_cheap_stateless_reject(use_cheap_stateless_reject) {}
+
+ friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+ os << "{ server_supported_versions: "
+ << ParsedQuicVersionVectorToString(p.server_supported_versions);
+ os << " client_supported_versions: "
+ << ParsedQuicVersionVectorToString(p.client_supported_versions);
+ os << " negotiated_version: "
+ << ParsedQuicVersionToString(p.negotiated_version);
+ os << " client_supports_stateless_rejects: "
+ << p.client_supports_stateless_rejects;
+ os << " server_uses_stateless_rejects_if_peer_supported: "
+ << p.server_uses_stateless_rejects_if_peer_supported;
+ os << " congestion_control_tag: "
+ << QuicTagToString(p.congestion_control_tag);
+ os << " use_cheap_stateless_reject: " << p.use_cheap_stateless_reject
+ << " }";
+ return os;
+ }
+
+ ParsedQuicVersionVector client_supported_versions;
+ ParsedQuicVersionVector server_supported_versions;
+ ParsedQuicVersion negotiated_version;
+ bool client_supports_stateless_rejects;
+ bool server_uses_stateless_rejects_if_peer_supported;
+ QuicTag congestion_control_tag;
+ bool use_cheap_stateless_reject;
+};
+
+// Constructs various test permutations.
+std::vector<TestParams> GetTestParams(bool use_tls_handshake,
+ bool test_stateless_rejects) {
+ QuicFlagSaver flags;
+ // Divide the versions into buckets in which the intra-frame format
+ // is compatible. When clients encounter QUIC version negotiation
+ // they simply retransmit all packets using the new version's
+ // QUIC framing. However, they are unable to change the intra-frame
+ // layout (for example to change HTTP/2 headers to SPDY/3, or a change in the
+ // handshake protocol). So these tests need to ensure that clients are never
+ // attempting to do 0-RTT across incompatible versions. Chromium only
+ // supports a single version at a time anyway. :)
+ FLAGS_quic_supports_tls_handshake = use_tls_handshake;
+ ParsedQuicVersionVector all_supported_versions =
+ FilterSupportedVersions(AllSupportedVersions());
+
+ // Buckets are separated by versions: versions prior to QUIC_VERSION_47 use
+ // STREAM frames for the handshake, and only have QUIC crypto as the handshake
+ // protocol. Version 47 and greater use CRYPTO frames for the handshake, and
+ // must also be split based on the handshake protocol. If the handshake
+ // protocol (QUIC crypto or TLS) changes, the ClientHello/CHLO must be
+ // reconstructed for the correct protocol.
+ ParsedQuicVersionVector version_buckets[3];
+
+ for (const ParsedQuicVersion& version : all_supported_versions) {
+ if (!QuicVersionUsesCryptoFrames(version.transport_version)) {
+ version_buckets[0].push_back(version);
+ } else if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
+ version_buckets[1].push_back(version);
+ } else {
+ version_buckets[2].push_back(version);
+ }
+ }
+
+ // This must be kept in sync with the number of nested for-loops below as it
+ // is used to prune the number of tests that are run.
+ const int kMaxEnabledOptions = 4;
+ int max_enabled_options = 0;
+ std::vector<TestParams> params;
+ for (const QuicTag congestion_control_tag : {kRENO, kTBBR, kQBIC, kTPCC}) {
+ for (bool server_uses_stateless_rejects_if_peer_supported : {true, false}) {
+ for (bool client_supports_stateless_rejects : {true, false}) {
+ for (bool use_cheap_stateless_reject : {true, false}) {
+ int enabled_options = 0;
+ if (congestion_control_tag != kQBIC) {
+ ++enabled_options;
+ }
+ if (client_supports_stateless_rejects) {
+ ++enabled_options;
+ }
+ if (server_uses_stateless_rejects_if_peer_supported) {
+ ++enabled_options;
+ }
+ if (use_cheap_stateless_reject) {
+ ++enabled_options;
+ }
+ CHECK_GE(kMaxEnabledOptions, enabled_options);
+ if (enabled_options > max_enabled_options) {
+ max_enabled_options = enabled_options;
+ }
+
+ // Run tests with no options, a single option, or all the
+ // options enabled to avoid a combinatorial explosion.
+ if (enabled_options > 1 && enabled_options < kMaxEnabledOptions) {
+ continue;
+ }
+
+ // There are many stateless reject combinations, so don't test them
+ // unless requested.
+ if ((server_uses_stateless_rejects_if_peer_supported ||
+ client_supports_stateless_rejects ||
+ use_cheap_stateless_reject) &&
+ !test_stateless_rejects) {
+ continue;
+ }
+
+ for (const ParsedQuicVersionVector& client_versions :
+ version_buckets) {
+ if (FilterSupportedVersions(client_versions).empty()) {
+ continue;
+ }
+ // Add an entry for server and client supporting all
+ // versions.
+ params.push_back(TestParams(
+ client_versions, all_supported_versions,
+ client_versions.front(), client_supports_stateless_rejects,
+ server_uses_stateless_rejects_if_peer_supported,
+ congestion_control_tag, use_cheap_stateless_reject));
+
+ // Run version negotiation tests tests with no options, or
+ // all the options enabled to avoid a combinatorial
+ // explosion.
+ if (enabled_options > 1 && enabled_options < kMaxEnabledOptions) {
+ continue;
+ }
+
+ // Test client supporting all versions and server supporting
+ // 1 version. Simulate an old server and exercise version
+ // downgrade in the client. Protocol negotiation should
+ // occur. Skip the i = 0 case because it is essentially the
+ // same as the default case.
+ for (size_t i = 1; i < client_versions.size(); ++i) {
+ ParsedQuicVersionVector server_supported_versions;
+ server_supported_versions.push_back(client_versions[i]);
+ if (FilterSupportedVersions(server_supported_versions).empty()) {
+ continue;
+ }
+ params.push_back(TestParams(
+ client_versions, server_supported_versions,
+ server_supported_versions.front(),
+ client_supports_stateless_rejects,
+ server_uses_stateless_rejects_if_peer_supported,
+ congestion_control_tag, use_cheap_stateless_reject));
+ } // End of inner version loop.
+ } // End of outer version loop.
+ } // End of use_cheap_stateless_reject loop.
+ } // End of client_supports_stateless_rejects loop.
+ } // End of server_uses_stateless_rejects_if_peer_supported loop.
+ } // End of congestion_control_tag loop.
+ CHECK_EQ(kMaxEnabledOptions, max_enabled_options);
+ return params;
+}
+
+class ServerDelegate : public PacketDroppingTestWriter::Delegate {
+ public:
+ explicit ServerDelegate(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {}
+ ~ServerDelegate() override = default;
+ void OnCanWrite() override { dispatcher_->OnCanWrite(); }
+
+ private:
+ QuicDispatcher* dispatcher_;
+};
+
+class ClientDelegate : public PacketDroppingTestWriter::Delegate {
+ public:
+ explicit ClientDelegate(QuicClient* client) : client_(client) {}
+ ~ClientDelegate() override = default;
+ void OnCanWrite() override {
+ QuicEpollEvent event(EPOLLOUT);
+ client_->epoll_network_helper()->OnEvent(client_->GetLatestFD(), &event);
+ }
+
+ private:
+ QuicClient* client_;
+};
+
+class EndToEndTest : public QuicTestWithParam<TestParams> {
+ protected:
+ EndToEndTest()
+ : initialized_(false),
+ connect_to_server_on_initialize_(true),
+ server_address_(
+ QuicSocketAddress(TestLoopback(), QuicPickUnusedPortOrDie())),
+ server_hostname_("test.example.com"),
+ client_writer_(nullptr),
+ server_writer_(nullptr),
+ negotiated_version_(UnsupportedQuicVersion()),
+ chlo_multiplier_(0),
+ stream_factory_(nullptr),
+ support_server_push_(false),
+ override_connection_id_(nullptr),
+ expected_connection_id_length_(kQuicDefaultConnectionIdLength) {
+ FLAGS_quic_supports_tls_handshake = true;
+ SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, true);
+ SetQuicReloadableFlag(quic_no_client_conn_ver_negotiation, true);
+ client_supported_versions_ = GetParam().client_supported_versions;
+ server_supported_versions_ = GetParam().server_supported_versions;
+ negotiated_version_ = GetParam().negotiated_version;
+
+ QUIC_LOG(INFO) << "Using Configuration: " << GetParam();
+
+ // Use different flow control windows for client/server.
+ client_config_.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ client_config_.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ server_config_.SetInitialStreamFlowControlWindowToSend(
+ 3 * kInitialStreamFlowControlWindowForTest);
+ server_config_.SetInitialSessionFlowControlWindowToSend(
+ 3 * kInitialSessionFlowControlWindowForTest);
+
+ // The default idle timeouts can be too strict when running on a busy
+ // machine.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(30);
+ client_config_.set_max_time_before_crypto_handshake(timeout);
+ client_config_.set_max_idle_time_before_crypto_handshake(timeout);
+ server_config_.set_max_time_before_crypto_handshake(timeout);
+ server_config_.set_max_idle_time_before_crypto_handshake(timeout);
+
+ AddToCache("/foo", 200, kFooResponseBody);
+ AddToCache("/bar", 200, kBarResponseBody);
+ }
+
+ ~EndToEndTest() override { QuicRecyclePort(server_address_.port()); }
+
+ virtual void CreateClientWithWriter() {
+ client_.reset(CreateQuicClient(client_writer_));
+ }
+
+ QuicTestClient* CreateQuicClient(QuicPacketWriterWrapper* writer) {
+ QuicTestClient* client =
+ new QuicTestClient(server_address_, server_hostname_, client_config_,
+ client_supported_versions_,
+ crypto_test_utils::ProofVerifierForTesting());
+ client->UseWriter(writer);
+ if (!pre_shared_key_client_.empty()) {
+ client->client()->SetPreSharedKey(pre_shared_key_client_);
+ }
+ if (override_connection_id_ != nullptr) {
+ client->UseConnectionId(*override_connection_id_);
+ }
+ client->Connect();
+ return client;
+ }
+
+ void set_smaller_flow_control_receive_window() {
+ const uint32_t kClientIFCW = 64 * 1024;
+ const uint32_t kServerIFCW = 1024 * 1024;
+ set_client_initial_stream_flow_control_receive_window(kClientIFCW);
+ set_client_initial_session_flow_control_receive_window(
+ kSessionToStreamRatio * kClientIFCW);
+ set_server_initial_stream_flow_control_receive_window(kServerIFCW);
+ set_server_initial_session_flow_control_receive_window(
+ kSessionToStreamRatio * kServerIFCW);
+ }
+
+ void set_client_initial_stream_flow_control_receive_window(uint32_t window) {
+ CHECK(client_ == nullptr);
+ QUIC_DLOG(INFO) << "Setting client initial stream flow control window: "
+ << window;
+ client_config_.SetInitialStreamFlowControlWindowToSend(window);
+ }
+
+ void set_client_initial_session_flow_control_receive_window(uint32_t window) {
+ CHECK(client_ == nullptr);
+ QUIC_DLOG(INFO) << "Setting client initial session flow control window: "
+ << window;
+ client_config_.SetInitialSessionFlowControlWindowToSend(window);
+ }
+
+ void set_server_initial_stream_flow_control_receive_window(uint32_t window) {
+ CHECK(server_thread_ == nullptr);
+ QUIC_DLOG(INFO) << "Setting server initial stream flow control window: "
+ << window;
+ server_config_.SetInitialStreamFlowControlWindowToSend(window);
+ }
+
+ void set_server_initial_session_flow_control_receive_window(uint32_t window) {
+ CHECK(server_thread_ == nullptr);
+ QUIC_DLOG(INFO) << "Setting server initial session flow control window: "
+ << window;
+ server_config_.SetInitialSessionFlowControlWindowToSend(window);
+ }
+
+ const QuicSentPacketManager* GetSentPacketManagerFromFirstServerSession() {
+ return &GetServerConnection()->sent_packet_manager();
+ }
+
+ QuicConnection* GetServerConnection() {
+ return GetServerSession()->connection();
+ }
+
+ QuicSession* GetServerSession() {
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ EXPECT_EQ(1u, dispatcher->session_map().size());
+ return dispatcher->session_map().begin()->second.get();
+ }
+
+ bool Initialize() {
+ QuicTagVector copt;
+ server_config_.SetConnectionOptionsToSend(copt);
+ copt = client_extra_copts_;
+
+ // TODO(nimia): Consider setting the congestion control algorithm for the
+ // client as well according to the test parameter.
+ copt.push_back(GetParam().congestion_control_tag);
+ if (GetParam().congestion_control_tag == kTPCC &&
+ GetQuicReloadableFlag(quic_enable_pcc3)) {
+ copt.push_back(kTPCC);
+ }
+
+ if (GetParam().client_supports_stateless_rejects) {
+ copt.push_back(kSREJ);
+ }
+ client_config_.SetConnectionOptionsToSend(copt);
+
+ // Start the server first, because CreateQuicClient() attempts
+ // to connect to the server.
+ StartServer();
+
+ if (!connect_to_server_on_initialize_) {
+ initialized_ = true;
+ return true;
+ }
+
+ CreateClientWithWriter();
+ static QuicEpollEvent event(EPOLLOUT);
+ if (client_writer_ != nullptr) {
+ client_writer_->Initialize(
+ QuicConnectionPeer::GetHelper(
+ client_->client()->client_session()->connection()),
+ QuicConnectionPeer::GetAlarmFactory(
+ client_->client()->client_session()->connection()),
+ QuicMakeUnique<ClientDelegate>(client_->client()));
+ }
+ initialized_ = true;
+ return client_->client()->connected();
+ }
+
+ void SetUp() override {
+ // The ownership of these gets transferred to the QuicPacketWriterWrapper
+ // when Initialize() is executed.
+ client_writer_ = new PacketDroppingTestWriter();
+ server_writer_ = new PacketDroppingTestWriter();
+ }
+
+ void TearDown() override {
+ ASSERT_TRUE(initialized_) << "You must call Initialize() in every test "
+ << "case. Otherwise, your test will leak memory.";
+ StopServer();
+ }
+
+ void StartServer() {
+ SetQuicReloadableFlag(quic_use_cheap_stateless_rejects,
+ GetParam().use_cheap_stateless_reject);
+
+ auto* test_server = new QuicTestServer(
+ crypto_test_utils::ProofSourceForTesting(), server_config_,
+ server_supported_versions_, &memory_cache_backend_,
+ expected_connection_id_length_);
+ server_thread_ = QuicMakeUnique<ServerThread>(test_server, server_address_);
+ if (chlo_multiplier_ != 0) {
+ server_thread_->server()->SetChloMultiplier(chlo_multiplier_);
+ }
+ if (!pre_shared_key_server_.empty()) {
+ server_thread_->server()->SetPreSharedKey(pre_shared_key_server_);
+ }
+ server_thread_->Initialize();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ QuicDispatcherPeer::UseWriter(dispatcher, server_writer_);
+
+ SetQuicReloadableFlag(
+ enable_quic_stateless_reject_support,
+ GetParam().server_uses_stateless_rejects_if_peer_supported);
+
+ server_writer_->Initialize(QuicDispatcherPeer::GetHelper(dispatcher),
+ QuicDispatcherPeer::GetAlarmFactory(dispatcher),
+ QuicMakeUnique<ServerDelegate>(dispatcher));
+ if (stream_factory_ != nullptr) {
+ static_cast<QuicTestServer*>(server_thread_->server())
+ ->SetSpdyStreamFactory(stream_factory_);
+ }
+
+ server_thread_->Start();
+ }
+
+ void StopServer() {
+ if (server_thread_) {
+ server_thread_->Quit();
+ server_thread_->Join();
+ }
+ }
+
+ void AddToCache(QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body) {
+ memory_cache_backend_.AddSimpleResponse(server_hostname_, path,
+ response_code, body);
+ }
+
+ void SetPacketLossPercentage(int32_t loss) {
+ client_writer_->set_fake_packet_loss_percentage(loss);
+ server_writer_->set_fake_packet_loss_percentage(loss);
+ }
+
+ void SetPacketSendDelay(QuicTime::Delta delay) {
+ client_writer_->set_fake_packet_delay(delay);
+ server_writer_->set_fake_packet_delay(delay);
+ }
+
+ void SetReorderPercentage(int32_t reorder) {
+ client_writer_->set_fake_reorder_percentage(reorder);
+ server_writer_->set_fake_reorder_percentage(reorder);
+ }
+
+ // Verifies that the client and server connections were both free of packets
+ // being discarded, based on connection stats.
+ // Calls server_thread_ Pause() and Resume(), which may only be called once
+ // per test.
+ void VerifyCleanConnection(bool had_packet_loss) {
+ QuicConnectionStats client_stats =
+ client_->client()->client_session()->connection()->GetStats();
+ // TODO(ianswett): Determine why this becomes even more flaky with BBR
+ // enabled. b/62141144
+ if (!had_packet_loss && !GetQuicReloadableFlag(quic_default_to_bbr)) {
+ EXPECT_EQ(0u, client_stats.packets_lost);
+ }
+ EXPECT_EQ(0u, client_stats.packets_discarded);
+ // When doing 0-RTT with stateless rejects, the encrypted requests cause
+ // a retranmission of the SREJ packets which are dropped by the client.
+ // When client starts with an unsupported version, the version negotiation
+ // packet sent by server for the old connection (respond for the connection
+ // close packet) will be dropped by the client.
+ if (!BothSidesSupportStatelessRejects() &&
+ !ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(0u, client_stats.packets_dropped);
+ }
+ if (!ClientSupportsIetfQuicNotSupportedByServer()) {
+ // In this case, if client sends 0-RTT POST with v99, receives IETF
+ // version negotiation packet and speaks a GQUIC version. Server processes
+ // this connection in time wait list and keeps sending IETF version
+ // negotiation packet for incoming packets. But these version negotiation
+ // packets cannot be processed by the client speaking GQUIC.
+ EXPECT_EQ(client_stats.packets_received, client_stats.packets_processed);
+ }
+
+ const int num_expected_stateless_rejects =
+ (BothSidesSupportStatelessRejects() &&
+ client_->client()->client_session()->GetNumSentClientHellos() > 0)
+ ? 1
+ : 0;
+ EXPECT_EQ(num_expected_stateless_rejects,
+ client_->client()->num_stateless_rejects_received());
+
+ server_thread_->Pause();
+ QuicConnectionStats server_stats = GetServerConnection()->GetStats();
+ if (!had_packet_loss) {
+ EXPECT_EQ(0u, server_stats.packets_lost);
+ }
+ EXPECT_EQ(0u, server_stats.packets_discarded);
+ // TODO(ianswett): Restore the check for packets_dropped equals 0.
+ // The expect for packets received is equal to packets processed fails
+ // due to version negotiation packets.
+ server_thread_->Resume();
+ }
+
+ bool BothSidesSupportStatelessRejects() {
+ return (GetParam().server_uses_stateless_rejects_if_peer_supported &&
+ GetParam().client_supports_stateless_rejects);
+ }
+
+ // Client supports IETF QUIC, while it is not supported by server.
+ bool ClientSupportsIetfQuicNotSupportedByServer() {
+ return GetParam().client_supported_versions[0].transport_version >
+ QUIC_VERSION_43 &&
+ FilterSupportedVersions(GetParam().server_supported_versions)[0]
+ .transport_version <= QUIC_VERSION_43;
+ }
+
+ // Returns true when client starts with an unsupported version, and client
+ // closes connection when version negotiation is received.
+ bool ServerSendsVersionNegotiation() {
+ return GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation) &&
+ GetParam().client_supported_versions[0] !=
+ GetParam().negotiated_version;
+ }
+
+ bool SupportsIetfQuicWithTls(ParsedQuicVersion version) {
+ return version.transport_version > QUIC_VERSION_43 &&
+ version.handshake_protocol == PROTOCOL_TLS1_3;
+ }
+
+ void ExpectFlowControlsSynced(QuicFlowController* client,
+ QuicFlowController* server) {
+ EXPECT_EQ(QuicFlowControllerPeer::SendWindowSize(client),
+ QuicFlowControllerPeer::ReceiveWindowSize(server));
+ EXPECT_EQ(QuicFlowControllerPeer::ReceiveWindowSize(client),
+ QuicFlowControllerPeer::SendWindowSize(server));
+ }
+
+ // Must be called before Initialize to have effect.
+ void SetSpdyStreamFactory(QuicTestServer::StreamFactory* factory) {
+ stream_factory_ = factory;
+ }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return GetNthClientInitiatedBidirectionalStreamId(
+ client_->client()->client_session()->connection()->transport_version(),
+ n);
+ }
+
+ QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
+ return GetNthServerInitiatedBidirectionalStreamId(
+ client_->client()->client_session()->connection()->transport_version(),
+ n);
+ }
+
+ ScopedEnvironmentForThreads environment_;
+ bool initialized_;
+ // If true, the Initialize() function will create |client_| and starts to
+ // connect to the server.
+ // Default is true.
+ bool connect_to_server_on_initialize_;
+ QuicSocketAddress server_address_;
+ std::string server_hostname_;
+ QuicMemoryCacheBackend memory_cache_backend_;
+ std::unique_ptr<ServerThread> server_thread_;
+ std::unique_ptr<QuicTestClient> client_;
+ PacketDroppingTestWriter* client_writer_;
+ PacketDroppingTestWriter* server_writer_;
+ QuicConfig client_config_;
+ QuicConfig server_config_;
+ ParsedQuicVersionVector client_supported_versions_;
+ ParsedQuicVersionVector server_supported_versions_;
+ QuicTagVector client_extra_copts_;
+ ParsedQuicVersion negotiated_version_;
+ size_t chlo_multiplier_;
+ QuicTestServer::StreamFactory* stream_factory_;
+ bool support_server_push_;
+ std::string pre_shared_key_client_;
+ std::string pre_shared_key_server_;
+ QuicConnectionId* override_connection_id_;
+ uint8_t expected_connection_id_length_;
+};
+
+// Run all end to end tests with all supported versions.
+INSTANTIATE_TEST_SUITE_P(EndToEndTests,
+ EndToEndTest,
+ ::testing::ValuesIn(GetTestParams(false, false)));
+
+class EndToEndTestWithTls : public EndToEndTest {};
+
+INSTANTIATE_TEST_SUITE_P(EndToEndTestsWithTls,
+ EndToEndTestWithTls,
+ ::testing::ValuesIn(GetTestParams(true, false)));
+
+class EndToEndTestWithStatelessReject : public EndToEndTest {};
+
+INSTANTIATE_TEST_SUITE_P(WithStatelessReject,
+ EndToEndTestWithStatelessReject,
+ ::testing::ValuesIn(GetTestParams(false, true)));
+
+TEST_P(EndToEndTestWithTls, HandshakeSuccessful) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+ // There have been occasions where it seemed that negotiated_version_ and the
+ // version in the connection are not in sync. If it is happening, it has not
+ // been recreatable; this assert is here just to check and raise a flag if it
+ // happens.
+ ASSERT_EQ(
+ client_->client()->client_session()->connection()->transport_version(),
+ negotiated_version_.transport_version);
+
+ QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream(
+ client_->client()->client_session());
+ QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(crypto_stream);
+ EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+ server_thread_->Pause();
+ crypto_stream = QuicSessionPeer::GetMutableCryptoStream(GetServerSession());
+ sequencer = QuicStreamPeer::sequencer(crypto_stream);
+ EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+}
+
+TEST_P(EndToEndTestWithStatelessReject, SimpleRequestResponseStatless) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ int expected_num_client_hellos = 2;
+ if (ServerSendsVersionNegotiation()) {
+ ++expected_num_client_hellos;
+ if (BothSidesSupportStatelessRejects()) {
+ ++expected_num_client_hellos;
+ }
+ }
+ EXPECT_EQ(expected_num_client_hellos,
+ client_->client()->GetNumSentClientHellos());
+}
+
+TEST_P(EndToEndTest, SimpleRequestResponse) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ int expected_num_client_hellos = 2;
+ if (ServerSendsVersionNegotiation()) {
+ ++expected_num_client_hellos;
+ if (BothSidesSupportStatelessRejects()) {
+ ++expected_num_client_hellos;
+ }
+ }
+ EXPECT_EQ(expected_num_client_hellos,
+ client_->client()->GetNumSentClientHellos());
+}
+
+TEST_P(EndToEndTest, SimpleRequestResponseZeroConnectionID) {
+ QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId(
+ GetParam().negotiated_version.transport_version);
+ override_connection_id_ = &connection_id;
+ expected_connection_id_length_ = connection_id.length();
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ int expected_num_client_hellos = 2;
+ if (ServerSendsVersionNegotiation()) {
+ ++expected_num_client_hellos;
+ if (BothSidesSupportStatelessRejects()) {
+ ++expected_num_client_hellos;
+ }
+ }
+ EXPECT_EQ(expected_num_client_hellos,
+ client_->client()->GetNumSentClientHellos());
+ EXPECT_EQ(client_->client()->client_session()->connection()->connection_id(),
+ QuicUtils::CreateZeroConnectionId(
+ GetParam().negotiated_version.transport_version));
+}
+
+TEST_P(EndToEndTest, BadConnectionIdLength) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ GetParam().negotiated_version.transport_version)) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+ QuicConnectionId connection_id =
+ TestConnectionIdNineBytesLong(UINT64_C(0xBADbadBADbad));
+ override_connection_id_ = &connection_id;
+ ASSERT_TRUE(Initialize());
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ EXPECT_EQ(kQuicDefaultConnectionIdLength, client_->client()
+ ->client_session()
+ ->connection()
+ ->connection_id()
+ .length());
+}
+
+TEST_P(EndToEndTest, MixGoodAndBadConnectionIdLengths) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ GetParam().negotiated_version.transport_version)) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+
+ // Start client_ which will use a bad connection ID length.
+ QuicConnectionId connection_id =
+ TestConnectionIdNineBytesLong(UINT64_C(0xBADbadBADbad));
+ override_connection_id_ = &connection_id;
+ ASSERT_TRUE(Initialize());
+ override_connection_id_ = nullptr;
+
+ // Start client2 which will use a good connection ID length.
+ std::unique_ptr<QuicTestClient> client2(CreateQuicClient(nullptr));
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["content-length"] = "3";
+ client2->SendMessage(headers, "", /*fin=*/false);
+ client2->SendData("eep", true);
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ EXPECT_EQ(kQuicDefaultConnectionIdLength, client_->client()
+ ->client_session()
+ ->connection()
+ ->connection_id()
+ .length());
+
+ client2->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client2->response_body());
+ EXPECT_EQ("200", client2->response_headers()->find(":status")->second);
+ EXPECT_EQ(kQuicDefaultConnectionIdLength, client2->client()
+ ->client_session()
+ ->connection()
+ ->connection_id()
+ .length());
+}
+
+TEST_P(EndToEndTest, SimpleRequestResponseWithLargeReject) {
+ chlo_multiplier_ = 1;
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(4, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
+ }
+}
+
+TEST_P(EndToEndTestWithTls, SimpleRequestResponsev6) {
+ server_address_ =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), server_address_.port());
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTestWithTls, SeparateFinPacket) {
+ ASSERT_TRUE(Initialize());
+
+ // Send a request in two parts: the request and then an empty packet with FIN.
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ client_->SendMessage(headers, "", /*fin=*/false);
+ client_->SendData("", true);
+ client_->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client_->response_body());
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Now do the same thing but with a content length.
+ headers["content-length"] = "3";
+ client_->SendMessage(headers, "", /*fin=*/false);
+ client_->SendData("foo", true);
+ client_->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client_->response_body());
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTestWithTls, MultipleRequestResponse) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTest, MultipleRequestResponseZeroConnectionID) {
+ QuicConnectionId connection_id = QuicUtils::CreateZeroConnectionId(
+ GetParam().negotiated_version.transport_version);
+ override_connection_id_ = &connection_id;
+ expected_connection_id_length_ = connection_id.length();
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTestWithTls, MultipleStreams) {
+ // Verifies quic_test_client can track responses of all active streams.
+ ASSERT_TRUE(Initialize());
+
+ const int kNumRequests = 10;
+
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["content-length"] = "3";
+
+ for (int i = 0; i < kNumRequests; ++i) {
+ client_->SendMessage(headers, "bar", /*fin=*/true);
+ }
+
+ while (kNumRequests > client_->num_responses()) {
+ client_->ClearPerRequestState();
+ client_->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client_->response_body());
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ }
+}
+
+TEST_P(EndToEndTestWithTls, MultipleClients) {
+ ASSERT_TRUE(Initialize());
+ std::unique_ptr<QuicTestClient> client2(CreateQuicClient(nullptr));
+
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["content-length"] = "3";
+
+ client_->SendMessage(headers, "", /*fin=*/false);
+ client2->SendMessage(headers, "", /*fin=*/false);
+
+ client_->SendData("bar", true);
+ client_->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client_->response_body());
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ client2->SendData("eep", true);
+ client2->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client2->response_body());
+ EXPECT_EQ("200", client2->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTestWithTls, RequestOverMultiplePackets) {
+ // Send a large enough request to guarantee fragmentation.
+ std::string huge_request =
+ "/some/path?query=" + std::string(kMaxOutgoingPacketSize, '.');
+ AddToCache(huge_request, 200, kBarResponseBody);
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest(huge_request));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTestWithTls, MultiplePacketsRandomOrder) {
+ // Send a large enough request to guarantee fragmentation.
+ std::string huge_request =
+ "/some/path?query=" + std::string(kMaxOutgoingPacketSize, '.');
+ AddToCache(huge_request, 200, kBarResponseBody);
+
+ ASSERT_TRUE(Initialize());
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
+ SetReorderPercentage(50);
+
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest(huge_request));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTestWithTls, PostMissingBytes) {
+ ASSERT_TRUE(Initialize());
+
+ // Add a content length header with no body.
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["content-length"] = "3";
+
+ // This should be detected as stream fin without complete request,
+ // triggering an error response.
+ client_->SendCustomSynchronousRequest(headers, "");
+ EXPECT_EQ(QuicSimpleServerStream::kErrorResponseBody,
+ client_->response_body());
+ EXPECT_EQ("500", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTest, LargePostNoPacketLoss) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // 1 MB body.
+ std::string body(1024 * 1024, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ // TODO(ianswett): There should not be packet loss in this test, but on some
+ // platforms the receive buffer overflows.
+ VerifyCleanConnection(true);
+}
+
+TEST_P(EndToEndTest, LargePostNoPacketLoss1sRTT) {
+ ASSERT_TRUE(Initialize());
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(1000));
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // 100 KB body.
+ std::string body(100 * 1024, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ VerifyCleanConnection(false);
+}
+
+TEST_P(EndToEndTest, LargePostWithPacketLoss) {
+ if (!BothSidesSupportStatelessRejects()) {
+ // Connect with lower fake packet loss than we'd like to test.
+ // Until b/10126687 is fixed, losing handshake packets is pretty
+ // brutal.
+ // TODO(jokulik): Until we support redundant SREJ packets, don't
+ // drop handshake packets for stateless rejects.
+ SetPacketLossPercentage(5);
+ }
+ ASSERT_TRUE(Initialize());
+
+ // Wait for the server SHLO before upping the packet loss.
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ SetPacketLossPercentage(30);
+
+ // 10 KB body.
+ std::string body(1024 * 10, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ VerifyCleanConnection(true);
+}
+
+// Regression test for b/80090281.
+TEST_P(EndToEndTest, LargePostWithPacketLossAndAlwaysBundleWindowUpdates) {
+ ASSERT_TRUE(Initialize());
+
+ // Wait for the server SHLO before upping the packet loss.
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Normally server only bundles a retransmittable frame once every other
+ // kMaxConsecutiveNonRetransmittablePackets ack-only packets. Setting the max
+ // to 0 to reliably reproduce b/80090281.
+ server_thread_->Schedule([this]() {
+ QuicConnectionPeer::SetMaxConsecutiveNumPacketsWithNoRetransmittableFrames(
+ GetServerConnection(), 0);
+ });
+
+ SetPacketLossPercentage(30);
+
+ // 10 KB body.
+ std::string body(1024 * 10, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ VerifyCleanConnection(true);
+}
+
+TEST_P(EndToEndTest, LargePostWithPacketLossAndBlockedSocket) {
+ if (!BothSidesSupportStatelessRejects()) {
+ // Connect with lower fake packet loss than we'd like to test. Until
+ // b/10126687 is fixed, losing handshake packets is pretty brutal.
+ // TODO(jokulik): Until we support redundant SREJ packets, don't
+ // drop handshake packets for stateless rejects.
+ SetPacketLossPercentage(5);
+ }
+ ASSERT_TRUE(Initialize());
+
+ // Wait for the server SHLO before upping the packet loss.
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ SetPacketLossPercentage(10);
+ client_writer_->set_fake_blocked_socket_percentage(10);
+
+ // 10 KB body.
+ std::string body(1024 * 10, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+}
+
+TEST_P(EndToEndTest, LargePostNoPacketLossWithDelayAndReordering) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ // Both of these must be called when the writer is not actively used.
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
+ SetReorderPercentage(30);
+
+ // 1 MB body.
+ std::string body(1024 * 1024, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+}
+
+TEST_P(EndToEndTest, LargePostZeroRTTFailure) {
+ // Send a request and then disconnect. This prepares the client to attempt
+ // a 0-RTT handshake for the next request.
+ ASSERT_TRUE(Initialize());
+
+ std::string body(20480, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ // In the non-stateless case, the same session is used for both
+ // hellos, so the number of hellos sent on that session is 2. In
+ // the stateless case, the first client session will be completely
+ // torn down after the reject. The number of hellos on the latest
+ // session is 1.
+ const int expected_num_hellos_latest_session =
+ (BothSidesSupportStatelessRejects() && !ServerSendsVersionNegotiation())
+ ? 1
+ : 2;
+ EXPECT_EQ(expected_num_hellos_latest_session,
+ client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ }
+
+ client_->Disconnect();
+
+ // The 0-RTT handshake should succeed.
+ client_->Connect();
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_->client()->connected());
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+
+ EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
+ }
+
+ client_->Disconnect();
+
+ // Restart the server so that the 0-RTT handshake will take 1 RTT.
+ StopServer();
+ server_writer_ = new PacketDroppingTestWriter();
+ StartServer();
+
+ client_->Connect();
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_->client()->connected());
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ // In the non-stateless case, the same session is used for both
+ // hellos, so the number of hellos sent on that session is 2. In
+ // the stateless case, the first client session will be completely
+ // torn down after the reject. The number of hellos sent on the
+ // latest session is 1.
+ EXPECT_EQ(expected_num_hellos_latest_session,
+ client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ }
+
+ VerifyCleanConnection(false);
+}
+
+TEST_P(EndToEndTest, SynchronousRequestZeroRTTFailure) {
+ // Send a request and then disconnect. This prepares the client to attempt
+ // a 0-RTT handshake for the next request.
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ // In the non-stateless case, the same session is used for both
+ // hellos, so the number of hellos sent on that session is 2. In
+ // the stateless case, the first client session will be completely
+ // torn down after the reject. The number of hellos on that second
+ // latest session is 1.
+ const int expected_num_hellos_latest_session =
+ (BothSidesSupportStatelessRejects() && !ServerSendsVersionNegotiation())
+ ? 1
+ : 2;
+ EXPECT_EQ(expected_num_hellos_latest_session,
+ client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ }
+
+ client_->Disconnect();
+
+ // The 0-RTT handshake should succeed.
+ client_->Connect();
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_->client()->connected());
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+
+ EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
+ }
+
+ client_->Disconnect();
+
+ // Restart the server so that the 0-RTT handshake will take 1 RTT.
+ StopServer();
+ server_writer_ = new PacketDroppingTestWriter();
+ StartServer();
+
+ client_->Connect();
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_->client()->connected());
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ // In the non-stateless case, the same session is used for both
+ // hellos, so the number of hellos sent on that session is 2. In
+ // the stateless case, the first client session will be completely
+ // torn down after the reject. The number of hellos sent on the
+ // latest session is 1.
+ EXPECT_EQ(expected_num_hellos_latest_session,
+ client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ }
+
+ VerifyCleanConnection(false);
+}
+
+TEST_P(EndToEndTest, LargePostSynchronousRequest) {
+ // Send a request and then disconnect. This prepares the client to attempt
+ // a 0-RTT handshake for the next request.
+ ASSERT_TRUE(Initialize());
+
+ std::string body(20480, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ // In the non-stateless case, the same session is used for both
+ // hellos, so the number of hellos sent on that session is 2. In
+ // the stateless case, the first client session will be completely
+ // torn down after the reject. The number of hellos on the latest
+ // session is 1.
+ const int expected_num_hellos_latest_session =
+ (BothSidesSupportStatelessRejects() && !ServerSendsVersionNegotiation())
+ ? 1
+ : 2;
+ EXPECT_EQ(expected_num_hellos_latest_session,
+ client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ }
+
+ client_->Disconnect();
+
+ // The 0-RTT handshake should succeed.
+ client_->Connect();
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_->client()->connected());
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+
+ EXPECT_EQ(1, client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
+ }
+
+ client_->Disconnect();
+
+ // Restart the server so that the 0-RTT handshake will take 1 RTT.
+ StopServer();
+ server_writer_ = new PacketDroppingTestWriter();
+ StartServer();
+
+ client_->Connect();
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_->client()->connected());
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ // In the non-stateless case, the same session is used for both
+ // hellos, so the number of hellos sent on that session is 2. In
+ // the stateless case, the first client session will be completely
+ // torn down after the reject. The number of hellos sent on the
+ // latest session is 1.
+ EXPECT_EQ(expected_num_hellos_latest_session,
+ client_->client()->client_session()->GetNumSentClientHellos());
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(3, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ }
+
+ VerifyCleanConnection(false);
+}
+
+TEST_P(EndToEndTest, StatelessRejectWithPacketLoss) {
+ // In this test, we intentionally drop the first packet from the
+ // server, which corresponds with the initial REJ/SREJ response from
+ // the server.
+ server_writer_->set_fake_drop_first_n_packets(1);
+ ASSERT_TRUE(Initialize());
+}
+
+TEST_P(EndToEndTest, SetInitialReceivedConnectionOptions) {
+ QuicTagVector initial_received_options;
+ initial_received_options.push_back(kTBBR);
+ initial_received_options.push_back(kIW10);
+ initial_received_options.push_back(kPRST);
+ EXPECT_TRUE(server_config_.SetInitialReceivedConnectionOptions(
+ initial_received_options));
+
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ EXPECT_FALSE(server_config_.SetInitialReceivedConnectionOptions(
+ initial_received_options));
+
+ // Verify that server's configuration is correct.
+ server_thread_->Pause();
+ EXPECT_TRUE(server_config_.HasReceivedConnectionOptions());
+ EXPECT_TRUE(
+ ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kTBBR));
+ EXPECT_TRUE(
+ ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kIW10));
+ EXPECT_TRUE(
+ ContainsQuicTag(server_config_.ReceivedConnectionOptions(), kPRST));
+}
+
+TEST_P(EndToEndTest, LargePostSmallBandwidthLargeBuffer) {
+ ASSERT_TRUE(Initialize());
+ SetPacketSendDelay(QuicTime::Delta::FromMicroseconds(1));
+ // 256KB per second with a 256KB buffer from server to client. Wireless
+ // clients commonly have larger buffers, but our max CWND is 200.
+ server_writer_->set_max_bandwidth_and_buffer_size(
+ QuicBandwidth::FromBytesPerSecond(256 * 1024), 256 * 1024);
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // 1 MB body.
+ std::string body(1024 * 1024, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ // This connection may drop packets, because the buffer is smaller than the
+ // max CWND.
+ VerifyCleanConnection(true);
+}
+
+TEST_P(EndToEndTestWithTls, DoNotSetSendAlarmIfConnectionFlowControlBlocked) {
+ // Regression test for b/14677858.
+ // Test that the resume write alarm is not set in QuicConnection::OnCanWrite
+ // if currently connection level flow control blocked. If set, this results in
+ // an infinite loop in the EpollServer, as the alarm fires and is immediately
+ // rescheduled.
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // Ensure both stream and connection level are flow control blocked by setting
+ // the send window offset to 0.
+ const uint64_t flow_control_window =
+ server_config_.GetInitialStreamFlowControlWindowToSend();
+ QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+ QuicSession* session = client_->client()->client_session();
+ QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0);
+ QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0);
+ EXPECT_TRUE(stream->flow_controller()->IsBlocked());
+ EXPECT_TRUE(session->flow_controller()->IsBlocked());
+
+ // Make sure that the stream has data pending so that it will be marked as
+ // write blocked when it receives a stream level WINDOW_UPDATE.
+ stream->WriteOrBufferBody("hello", false);
+
+ // The stream now attempts to write, fails because it is still connection
+ // level flow control blocked, and is added to the write blocked list.
+ QuicWindowUpdateFrame window_update(kInvalidControlFrameId, stream->id(),
+ 2 * flow_control_window);
+ stream->OnWindowUpdateFrame(window_update);
+
+ // Prior to fixing b/14677858 this call would result in an infinite loop in
+ // Chromium. As a proxy for detecting this, we now check whether the
+ // send alarm is set after OnCanWrite. It should not be, as the
+ // connection is still flow control blocked.
+ session->connection()->OnCanWrite();
+
+ QuicAlarm* send_alarm =
+ QuicConnectionPeer::GetSendAlarm(session->connection());
+ EXPECT_FALSE(send_alarm->IsSet());
+}
+
+// TODO(nharper): Needs to get turned back to EndToEndTestWithTls
+// when we figure out why the test doesn't work on chrome.
+TEST_P(EndToEndTest, InvalidStream) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ std::string body(kMaxOutgoingPacketSize, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ // Force the client to write with a stream ID belonging to a nonexistent
+ // server-side stream.
+ QuicSpdySession* session = client_->client()->client_session();
+ QuicSessionPeer::SetNextOutgoingBidirectionalStreamId(
+ session, GetNthServerInitiatedBidirectionalId(0));
+
+ client_->SendCustomSynchronousRequest(headers, body);
+ EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+ EXPECT_EQ(QUIC_INVALID_STREAM_ID, client_->connection_error());
+}
+
+// Test that if the server will close the connection if the client attempts
+// to send a request with overly large headers.
+TEST_P(EndToEndTest, LargeHeaders) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ std::string body(kMaxOutgoingPacketSize, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["key1"] = std::string(15 * 1024, 'a');
+ headers["key2"] = std::string(15 * 1024, 'a');
+ headers["key3"] = std::string(15 * 1024, 'a');
+
+ client_->SendCustomSynchronousRequest(headers, body);
+ EXPECT_EQ(QUIC_HEADERS_TOO_LARGE, client_->stream_error());
+ EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
+}
+
+TEST_P(EndToEndTest, EarlyResponseWithQuicStreamNoError) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ std::string large_body(1024 * 1024, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ // Insert an invalid content_length field in request to trigger an early
+ // response from server.
+ headers["content-length"] = "-3";
+
+ client_->SendCustomSynchronousRequest(headers, large_body);
+ EXPECT_EQ("bad", client_->response_body());
+ EXPECT_EQ("500", client_->response_headers()->find(":status")->second);
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR, client_->stream_error());
+ EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
+}
+
+// TODO(rch): this test seems to cause net_unittests timeouts :|
+TEST_P(EndToEndTestWithTls, QUIC_TEST_DISABLED_IN_CHROME(MultipleTermination)) {
+ ASSERT_TRUE(Initialize());
+
+ // Set the offset so we won't frame. Otherwise when we pick up termination
+ // before HTTP framing is complete, we send an error and close the stream,
+ // and the second write is picked up as writing on a closed stream.
+ QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+ ASSERT_TRUE(stream != nullptr);
+ QuicStreamPeer::SetStreamBytesWritten(3, stream);
+
+ client_->SendData("bar", true);
+ client_->WaitForWriteToFlush();
+
+ // By default the stream protects itself from writes after terminte is set.
+ // Override this to test the server handling buggy clients.
+ QuicStreamPeer::SetWriteSideClosed(false, client_->GetOrCreateStream());
+
+ EXPECT_QUIC_BUG(client_->SendData("eep", true), "Fin already buffered");
+}
+
+// TODO(nharper): Needs to get turned back to EndToEndTestWithTls
+// when we figure out why the test doesn't work on chrome.
+TEST_P(EndToEndTest, Timeout) {
+ client_config_.SetIdleNetworkTimeout(QuicTime::Delta::FromMicroseconds(500),
+ QuicTime::Delta::FromMicroseconds(500));
+ // Note: we do NOT ASSERT_TRUE: we may time out during initial handshake:
+ // that's enough to validate timeout in this case.
+ Initialize();
+ while (client_->client()->connected()) {
+ client_->client()->WaitForEvents();
+ }
+}
+
+TEST_P(EndToEndTestWithTls, MaxIncomingDynamicStreamsLimitRespected) {
+ // Set a limit on maximum number of incoming dynamic streams.
+ // Make sure the limit is respected.
+ const uint32_t kServerMaxIncomingDynamicStreams = 1;
+ server_config_.SetMaxIncomingDynamicStreamsToSend(
+ kServerMaxIncomingDynamicStreams);
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+
+ // Make the client misbehave after negotiation.
+ const int kServerMaxStreams = kMaxStreamsMinimumIncrement + 1;
+ QuicSessionPeer::SetMaxOpenOutgoingStreams(
+ client_->client()->client_session(), kServerMaxStreams + 1);
+
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["content-length"] = "3";
+
+ // The server supports a small number of additional streams beyond the
+ // negotiated limit. Open enough streams to go beyond that limit.
+ for (int i = 0; i < kServerMaxStreams + 1; ++i) {
+ client_->SendMessage(headers, "", /*fin=*/false);
+ }
+ client_->WaitForResponse();
+ if (client_connection->transport_version() != QUIC_VERSION_99) {
+ EXPECT_TRUE(client_->connected());
+ EXPECT_EQ(QUIC_REFUSED_STREAM, client_->stream_error());
+ EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
+ } else {
+ // Version 99 disconnects the connection if we exceed the stream limit.
+ EXPECT_FALSE(client_->connected());
+ EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+ EXPECT_EQ(QUIC_INVALID_STREAM_ID, client_->connection_error());
+ }
+}
+
+TEST_P(EndToEndTest, SetIndependentMaxIncomingDynamicStreamsLimits) {
+ // Each endpoint can set max incoming dynamic streams independently.
+ const uint32_t kClientMaxIncomingDynamicStreams = 2;
+ const uint32_t kServerMaxIncomingDynamicStreams = 1;
+ client_config_.SetMaxIncomingDynamicStreamsToSend(
+ kClientMaxIncomingDynamicStreams);
+ server_config_.SetMaxIncomingDynamicStreamsToSend(
+ kServerMaxIncomingDynamicStreams);
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // The client has received the server's limit and vice versa.
+ QuicSpdyClientSession* client_session = client_->client()->client_session();
+ size_t client_max_open_outgoing_bidirectional_streams =
+ client_session->connection()->transport_version() == QUIC_VERSION_99
+ ? QuicSessionPeer::v99_streamid_manager(client_session)
+ ->max_allowed_outgoing_bidirectional_streams()
+ : QuicSessionPeer::GetStreamIdManager(client_session)
+ ->max_open_outgoing_streams();
+ size_t client_max_open_outgoing_unidirectional_streams =
+ client_session->connection()->transport_version() == QUIC_VERSION_99
+ ? QuicSessionPeer::v99_streamid_manager(client_session)
+ ->max_allowed_outgoing_unidirectional_streams()
+ : QuicSessionPeer::GetStreamIdManager(client_session)
+ ->max_open_outgoing_streams();
+ EXPECT_EQ(kServerMaxIncomingDynamicStreams,
+ client_max_open_outgoing_bidirectional_streams);
+ EXPECT_EQ(kServerMaxIncomingDynamicStreams,
+ client_max_open_outgoing_unidirectional_streams);
+ server_thread_->Pause();
+ QuicSession* server_session = GetServerSession();
+ size_t server_max_open_outgoing_bidirectional_streams =
+ server_session->connection()->transport_version() == QUIC_VERSION_99
+ ? QuicSessionPeer::v99_streamid_manager(server_session)
+ ->max_allowed_outgoing_bidirectional_streams()
+ : QuicSessionPeer::GetStreamIdManager(server_session)
+ ->max_open_outgoing_streams();
+ size_t server_max_open_outgoing_unidirectional_streams =
+ server_session->connection()->transport_version() == QUIC_VERSION_99
+ ? QuicSessionPeer::v99_streamid_manager(server_session)
+ ->max_allowed_outgoing_unidirectional_streams()
+ : QuicSessionPeer::GetStreamIdManager(server_session)
+ ->max_open_outgoing_streams();
+ EXPECT_EQ(kClientMaxIncomingDynamicStreams,
+ server_max_open_outgoing_bidirectional_streams);
+ EXPECT_EQ(kClientMaxIncomingDynamicStreams,
+ server_max_open_outgoing_unidirectional_streams);
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, NegotiateCongestionControl) {
+ ASSERT_TRUE(Initialize());
+
+ // For PCC, the underlying implementation may be a stub with a
+ // different name-tag. Skip the rest of this test.
+ if (GetParam().congestion_control_tag == kTPCC) {
+ return;
+ }
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ CongestionControlType expected_congestion_control_type = kRenoBytes;
+ switch (GetParam().congestion_control_tag) {
+ case kRENO:
+ expected_congestion_control_type = kRenoBytes;
+ break;
+ case kTBBR:
+ expected_congestion_control_type = kBBR;
+ break;
+ case kQBIC:
+ expected_congestion_control_type = kCubicBytes;
+ break;
+ default:
+ QUIC_DLOG(FATAL) << "Unexpected congestion control tag";
+ }
+
+ server_thread_->Pause();
+ EXPECT_EQ(expected_congestion_control_type,
+ QuicSentPacketManagerPeer::GetSendAlgorithm(
+ *GetSentPacketManagerFromFirstServerSession())
+ ->GetCongestionControlType());
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, ClientSuggestsRTT) {
+ // Client suggests initial RTT, verify it is used.
+ const QuicTime::Delta kInitialRTT = QuicTime::Delta::FromMicroseconds(20000);
+ client_config_.SetInitialRoundTripTimeUsToSend(kInitialRTT.ToMicroseconds());
+
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ ASSERT_EQ(1u, dispatcher->session_map().size());
+ const QuicSentPacketManager& client_sent_packet_manager =
+ client_->client()->client_session()->connection()->sent_packet_manager();
+ const QuicSentPacketManager* server_sent_packet_manager =
+ GetSentPacketManagerFromFirstServerSession();
+
+ EXPECT_EQ(kInitialRTT,
+ client_sent_packet_manager.GetRttStats()->initial_rtt());
+ EXPECT_EQ(kInitialRTT,
+ server_sent_packet_manager->GetRttStats()->initial_rtt());
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, ClientSuggestsIgnoredRTT) {
+ // Client suggests initial RTT, but also specifies NRTT, so it's not used.
+ const QuicTime::Delta kInitialRTT = QuicTime::Delta::FromMicroseconds(20000);
+ client_config_.SetInitialRoundTripTimeUsToSend(kInitialRTT.ToMicroseconds());
+ QuicTagVector options;
+ options.push_back(kNRTT);
+ client_config_.SetConnectionOptionsToSend(options);
+
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ ASSERT_EQ(1u, dispatcher->session_map().size());
+ const QuicSentPacketManager& client_sent_packet_manager =
+ client_->client()->client_session()->connection()->sent_packet_manager();
+ const QuicSentPacketManager* server_sent_packet_manager =
+ GetSentPacketManagerFromFirstServerSession();
+
+ EXPECT_EQ(kInitialRTT,
+ client_sent_packet_manager.GetRttStats()->initial_rtt());
+ EXPECT_EQ(kInitialRTT,
+ server_sent_packet_manager->GetRttStats()->initial_rtt());
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, MaxInitialRTT) {
+ // Client tries to suggest twice the server's max initial rtt and the server
+ // uses the max.
+ client_config_.SetInitialRoundTripTimeUsToSend(2 *
+ kMaxInitialRoundTripTimeUs);
+
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+ const QuicSentPacketManager& client_sent_packet_manager =
+ client_->client()->client_session()->connection()->sent_packet_manager();
+
+ // Now that acks have been exchanged, the RTT estimate has decreased on the
+ // server and is not infinite on the client.
+ EXPECT_FALSE(
+ client_sent_packet_manager.GetRttStats()->smoothed_rtt().IsInfinite());
+ const RttStats& server_rtt_stats =
+ *GetServerConnection()->sent_packet_manager().GetRttStats();
+ EXPECT_EQ(static_cast<int64_t>(kMaxInitialRoundTripTimeUs),
+ server_rtt_stats.initial_rtt().ToMicroseconds());
+ EXPECT_GE(static_cast<int64_t>(kMaxInitialRoundTripTimeUs),
+ server_rtt_stats.smoothed_rtt().ToMicroseconds());
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, MinInitialRTT) {
+ // Client tries to suggest 0 and the server uses the default.
+ client_config_.SetInitialRoundTripTimeUsToSend(0);
+
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+ const QuicSentPacketManager& client_sent_packet_manager =
+ client_->client()->client_session()->connection()->sent_packet_manager();
+ const QuicSentPacketManager& server_sent_packet_manager =
+ GetServerConnection()->sent_packet_manager();
+
+ // Now that acks have been exchanged, the RTT estimate has decreased on the
+ // server and is not infinite on the client.
+ EXPECT_FALSE(
+ client_sent_packet_manager.GetRttStats()->smoothed_rtt().IsInfinite());
+ // Expect the default rtt of 100ms.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100),
+ server_sent_packet_manager.GetRttStats()->initial_rtt());
+ // Ensure the bandwidth is valid.
+ client_sent_packet_manager.BandwidthEstimate();
+ server_sent_packet_manager.BandwidthEstimate();
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, 0ByteConnectionId) {
+ client_config_.SetBytesForConnectionIdToSend(0);
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ QuicPacketHeader* header =
+ QuicConnectionPeer::GetLastHeader(client_connection);
+ EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included);
+}
+
+TEST_P(EndToEndTestWithTls, 8ByteConnectionId) {
+ client_config_.SetBytesForConnectionIdToSend(8);
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ QuicPacketHeader* header =
+ QuicConnectionPeer::GetLastHeader(client_connection);
+ if (client_connection->transport_version() > QUIC_VERSION_43) {
+ EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included);
+ } else {
+ EXPECT_EQ(CONNECTION_ID_PRESENT,
+ header->destination_connection_id_included);
+ }
+}
+
+TEST_P(EndToEndTestWithTls, 15ByteConnectionId) {
+ client_config_.SetBytesForConnectionIdToSend(15);
+ ASSERT_TRUE(Initialize());
+
+ // Our server is permissive and allows for out of bounds values.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ QuicPacketHeader* header =
+ QuicConnectionPeer::GetLastHeader(client_connection);
+ if (client_connection->transport_version() > QUIC_VERSION_43) {
+ EXPECT_EQ(CONNECTION_ID_ABSENT, header->destination_connection_id_included);
+ } else {
+ EXPECT_EQ(CONNECTION_ID_PRESENT,
+ header->destination_connection_id_included);
+ }
+}
+
+TEST_P(EndToEndTestWithTls, ResetConnection) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ client_->ResetConnection();
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+// TODO(nharper): Needs to get turned back to EndToEndTestWithTls
+// when we figure out why the test doesn't work on chrome.
+TEST_P(EndToEndTest, MaxStreamsUberTest) {
+ if (!BothSidesSupportStatelessRejects()) {
+ // Connect with lower fake packet loss than we'd like to test. Until
+ // b/10126687 is fixed, losing handshake packets is pretty brutal.
+ // TODO(jokulik): Until we support redundant SREJ packets, don't
+ // drop handshake packets for stateless rejects.
+ SetPacketLossPercentage(1);
+ }
+ ASSERT_TRUE(Initialize());
+ std::string large_body(10240, 'a');
+ int max_streams = 100;
+
+ AddToCache("/large_response", 200, large_body);
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ SetPacketLossPercentage(10);
+
+ for (int i = 0; i < max_streams; ++i) {
+ EXPECT_LT(0, client_->SendRequest("/large_response"));
+ }
+
+ // WaitForEvents waits 50ms and returns true if there are outstanding
+ // requests.
+ while (client_->client()->WaitForEvents() == true) {
+ }
+}
+
+TEST_P(EndToEndTestWithTls, StreamCancelErrorTest) {
+ ASSERT_TRUE(Initialize());
+ std::string small_body(256, 'a');
+
+ AddToCache("/small_response", 200, small_body);
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ QuicSession* session = client_->client()->client_session();
+ // Lose the request.
+ SetPacketLossPercentage(100);
+ EXPECT_LT(0, client_->SendRequest("/small_response"));
+ client_->client()->WaitForEvents();
+ // Transmit the cancel, and ensure the connection is torn down properly.
+ SetPacketLossPercentage(0);
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
+ session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0);
+
+ // WaitForEvents waits 50ms and returns true if there are outstanding
+ // requests.
+ while (client_->client()->WaitForEvents() == true) {
+ }
+ // It should be completely fine to RST a stream before any data has been
+ // received for that stream.
+ EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
+}
+
+TEST_P(EndToEndTest, ConnectionMigrationClientIPChanged) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Store the client IP address which was used to send the first request.
+ QuicIpAddress old_host =
+ client_->client()->network_helper()->GetLatestClientAddress().host();
+
+ // Migrate socket to the new IP address.
+ QuicIpAddress new_host = TestLoopback(2);
+ EXPECT_NE(old_host, new_host);
+ ASSERT_TRUE(client_->client()->MigrateSocket(new_host));
+
+ // Send a request using the new socket.
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) {
+ // Tests that the client's port can change during an established QUIC
+ // connection, and that doing so does not result in the connection being
+ // closed by the server.
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Store the client address which was used to send the first request.
+ QuicSocketAddress old_address =
+ client_->client()->network_helper()->GetLatestClientAddress();
+ int old_fd = client_->client()->GetLatestFD();
+
+ // Create a new socket before closing the old one, which will result in a new
+ // ephemeral port.
+ QuicClientPeer::CreateUDPSocketAndBind(client_->client());
+
+ // Stop listening and close the old FD.
+ QuicClientPeer::CleanUpUDPSocket(client_->client(), old_fd);
+
+ // The packet writer needs to be updated to use the new FD.
+ client_->client()->network_helper()->CreateQuicPacketWriter();
+
+ // Change the internal state of the client and connection to use the new port,
+ // this is done because in a real NAT rebinding the client wouldn't see any
+ // port change, and so expects no change to incoming port.
+ // This is kind of ugly, but needed as we are simply swapping out the client
+ // FD rather than any more complex NAT rebinding simulation.
+ int new_port =
+ client_->client()->network_helper()->GetLatestClientAddress().port();
+ QuicClientPeer::SetClientPort(client_->client(), new_port);
+ QuicConnectionPeer::SetSelfAddress(
+ client_->client()->client_session()->connection(),
+ QuicSocketAddress(client_->client()
+ ->client_session()
+ ->connection()
+ ->self_address()
+ .host(),
+ new_port));
+
+ // Register the new FD for epoll events.
+ int new_fd = client_->client()->GetLatestFD();
+ QuicEpollServer* eps = client_->epoll_server();
+ eps->RegisterFD(new_fd, client_->client()->epoll_network_helper(),
+ EPOLLIN | EPOLLOUT | EPOLLET);
+
+ // Send a second request, using the new FD.
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Verify that the client's ephemeral port is different.
+ QuicSocketAddress new_address =
+ client_->client()->network_helper()->GetLatestClientAddress();
+ EXPECT_EQ(old_address.host(), new_address.host());
+ EXPECT_NE(old_address.port(), new_address.port());
+}
+
+TEST_P(EndToEndTest, NegotiatedInitialCongestionWindow) {
+ SetQuicReloadableFlag(quic_unified_iw_options, true);
+ client_extra_copts_.push_back(kIW03);
+
+ ASSERT_TRUE(Initialize());
+
+ // Values are exchanged during crypto handshake, so wait for that to finish.
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+ server_thread_->Pause();
+
+ QuicPacketCount cwnd =
+ GetServerConnection()->sent_packet_manager().initial_congestion_window();
+ EXPECT_EQ(3u, cwnd);
+}
+
+TEST_P(EndToEndTest, DifferentFlowControlWindows) {
+ // Client and server can set different initial flow control receive windows.
+ // These are sent in CHLO/SHLO. Tests that these values are exchanged properly
+ // in the crypto handshake.
+ const uint32_t kClientStreamIFCW = 123456;
+ const uint32_t kClientSessionIFCW = 234567;
+ set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW);
+ set_client_initial_session_flow_control_receive_window(kClientSessionIFCW);
+
+ uint32_t kServerStreamIFCW = 32 * 1024;
+ uint32_t kServerSessionIFCW = 48 * 1024;
+ set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW);
+ set_server_initial_session_flow_control_receive_window(kServerSessionIFCW);
+
+ ASSERT_TRUE(Initialize());
+
+ // Values are exchanged during crypto handshake, so wait for that to finish.
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Open a data stream to make sure the stream level flow control is updated.
+ QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+ stream->WriteOrBufferBody("hello", false);
+
+ // Client should have the right values for server's receive window.
+ EXPECT_EQ(kServerStreamIFCW,
+ client_->client()
+ ->client_session()
+ ->config()
+ ->ReceivedInitialStreamFlowControlWindowBytes());
+ EXPECT_EQ(kServerSessionIFCW,
+ client_->client()
+ ->client_session()
+ ->config()
+ ->ReceivedInitialSessionFlowControlWindowBytes());
+ EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset(
+ stream->flow_controller()));
+ EXPECT_EQ(kServerSessionIFCW,
+ QuicFlowControllerPeer::SendWindowOffset(
+ client_->client()->client_session()->flow_controller()));
+
+ // Server should have the right values for client's receive window.
+ server_thread_->Pause();
+ QuicSession* session = GetServerSession();
+ EXPECT_EQ(kClientStreamIFCW,
+ session->config()->ReceivedInitialStreamFlowControlWindowBytes());
+ EXPECT_EQ(kClientSessionIFCW,
+ session->config()->ReceivedInitialSessionFlowControlWindowBytes());
+ EXPECT_EQ(kClientSessionIFCW, QuicFlowControllerPeer::SendWindowOffset(
+ session->flow_controller()));
+ server_thread_->Resume();
+}
+
+// Test negotiation of IFWA connection option.
+TEST_P(EndToEndTest, NegotiatedServerInitialFlowControlWindow) {
+ const uint32_t kClientStreamIFCW = 123456;
+ const uint32_t kClientSessionIFCW = 234567;
+ set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW);
+ set_client_initial_session_flow_control_receive_window(kClientSessionIFCW);
+
+ uint32_t kServerStreamIFCW = 32 * 1024;
+ uint32_t kServerSessionIFCW = 48 * 1024;
+ set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW);
+ set_server_initial_session_flow_control_receive_window(kServerSessionIFCW);
+
+ // Bump the window.
+ const uint32_t kExpectedStreamIFCW = 1024 * 1024;
+ const uint32_t kExpectedSessionIFCW = 1.5 * 1024 * 1024;
+ client_extra_copts_.push_back(kIFWA);
+
+ ASSERT_TRUE(Initialize());
+
+ // Values are exchanged during crypto handshake, so wait for that to finish.
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ // Open a data stream to make sure the stream level flow control is updated.
+ QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+ stream->WriteOrBufferBody("hello", false);
+
+ // Client should have the right values for server's receive window.
+ EXPECT_EQ(kExpectedStreamIFCW,
+ client_->client()
+ ->client_session()
+ ->config()
+ ->ReceivedInitialStreamFlowControlWindowBytes());
+ EXPECT_EQ(kExpectedSessionIFCW,
+ client_->client()
+ ->client_session()
+ ->config()
+ ->ReceivedInitialSessionFlowControlWindowBytes());
+ EXPECT_EQ(kExpectedStreamIFCW, QuicFlowControllerPeer::SendWindowOffset(
+ stream->flow_controller()));
+ EXPECT_EQ(kExpectedSessionIFCW,
+ QuicFlowControllerPeer::SendWindowOffset(
+ client_->client()->client_session()->flow_controller()));
+}
+
+TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) {
+ // The special headers and crypto streams should be subject to per-stream flow
+ // control limits, but should not be subject to connection level flow control
+ const uint32_t kStreamIFCW = 32 * 1024;
+ const uint32_t kSessionIFCW = 48 * 1024;
+ set_client_initial_stream_flow_control_receive_window(kStreamIFCW);
+ set_client_initial_session_flow_control_receive_window(kSessionIFCW);
+ set_server_initial_stream_flow_control_receive_window(kStreamIFCW);
+ set_server_initial_session_flow_control_receive_window(kSessionIFCW);
+
+ ASSERT_TRUE(Initialize());
+
+ // Wait for crypto handshake to finish. This should have contributed to the
+ // crypto stream flow control window, but not affected the session flow
+ // control window.
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ QuicCryptoStream* crypto_stream = QuicSessionPeer::GetMutableCryptoStream(
+ client_->client()->client_session());
+ // In v47 and later, the crypto handshake (sent in CRYPTO frames) is not
+ // subject to flow control.
+ if (!QuicVersionUsesCryptoFrames(client_->client()
+ ->client_session()
+ ->connection()
+ ->transport_version())) {
+ EXPECT_LT(QuicFlowControllerPeer::SendWindowSize(
+ crypto_stream->flow_controller()),
+ kStreamIFCW);
+ }
+ EXPECT_EQ(kSessionIFCW,
+ QuicFlowControllerPeer::SendWindowSize(
+ client_->client()->client_session()->flow_controller()));
+
+ // Send a request with no body, and verify that the connection level window
+ // has not been affected.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+
+ QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
+ client_->client()->client_session());
+ EXPECT_LT(
+ QuicFlowControllerPeer::SendWindowSize(headers_stream->flow_controller()),
+ kStreamIFCW);
+ EXPECT_EQ(kSessionIFCW,
+ QuicFlowControllerPeer::SendWindowSize(
+ client_->client()->client_session()->flow_controller()));
+
+ // Server should be in a similar state: connection flow control window should
+ // not have any bytes marked as received.
+ server_thread_->Pause();
+ QuicSession* session = GetServerSession();
+ QuicFlowController* server_connection_flow_controller =
+ session->flow_controller();
+ EXPECT_EQ(kSessionIFCW, QuicFlowControllerPeer::ReceiveWindowSize(
+ server_connection_flow_controller));
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, FlowControlsSynced) {
+ set_smaller_flow_control_receive_window();
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ server_thread_->WaitForCryptoHandshakeConfirmed();
+
+ server_thread_->Pause();
+ QuicSpdySession* const client_session = client_->client()->client_session();
+ auto* server_session = static_cast<QuicSpdySession*>(GetServerSession());
+ ExpectFlowControlsSynced(client_session->flow_controller(),
+ server_session->flow_controller());
+ ExpectFlowControlsSynced(
+ QuicSessionPeer::GetMutableCryptoStream(client_session)
+ ->flow_controller(),
+ QuicSessionPeer::GetMutableCryptoStream(server_session)
+ ->flow_controller());
+ SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION);
+ SpdySettingsIR settings_frame;
+ settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE,
+ kDefaultMaxUncompressedHeaderSize);
+ SpdySerializedFrame frame(spdy_framer.SerializeFrame(settings_frame));
+ QuicFlowController* client_header_stream_flow_controller =
+ QuicSpdySessionPeer::GetHeadersStream(client_session)->flow_controller();
+ QuicFlowController* server_header_stream_flow_controller =
+ QuicSpdySessionPeer::GetHeadersStream(server_session)->flow_controller();
+ // Both client and server are sending this SETTINGS frame, and the send
+ // window is consumed. But because of timing issue, the server may send or
+ // not send the frame, and the client may send/ not send / receive / not
+ // receive the frame.
+ // TODO(fayang): Rewrite this part because it is hacky.
+ QuicByteCount win_difference1 = QuicFlowControllerPeer::ReceiveWindowSize(
+ server_header_stream_flow_controller) -
+ QuicFlowControllerPeer::SendWindowSize(
+ client_header_stream_flow_controller);
+ QuicByteCount win_difference2 = QuicFlowControllerPeer::ReceiveWindowSize(
+ client_header_stream_flow_controller) -
+ QuicFlowControllerPeer::SendWindowSize(
+ server_header_stream_flow_controller);
+ EXPECT_TRUE(win_difference1 == 0 || win_difference1 == frame.size());
+ EXPECT_TRUE(win_difference2 == 0 || win_difference2 == frame.size());
+
+ // Client *may* have received the SETTINGs frame.
+ // TODO(fayang): Rewrite this part because it is hacky.
+ float ratio1 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize(
+ client_session->flow_controller())) /
+ QuicFlowControllerPeer::ReceiveWindowSize(
+ QuicSpdySessionPeer::GetHeadersStream(client_session)
+ ->flow_controller());
+ float ratio2 = static_cast<float>(QuicFlowControllerPeer::ReceiveWindowSize(
+ client_session->flow_controller())) /
+ (QuicFlowControllerPeer::ReceiveWindowSize(
+ QuicSpdySessionPeer::GetHeadersStream(client_session)
+ ->flow_controller()) +
+ frame.size());
+ EXPECT_TRUE(ratio1 == kSessionToStreamRatio ||
+ ratio2 == kSessionToStreamRatio);
+
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTestWithTls, RequestWithNoBodyWillNeverSendStreamFrameWithFIN) {
+ // A stream created on receipt of a simple request with no body will never get
+ // a stream frame with a FIN. Verify that we don't keep track of the stream in
+ // the locally closed streams map: it will never be removed if so.
+ ASSERT_TRUE(Initialize());
+
+ // Send a simple headers only request, and receive response.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Now verify that the server is not waiting for a final FIN or RST.
+ server_thread_->Pause();
+ QuicSession* session = GetServerSession();
+ EXPECT_EQ(
+ 0u,
+ QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(session).size());
+ server_thread_->Resume();
+}
+
+// A TestAckListener verifies that its OnAckNotification method has been
+// called exactly once on destruction.
+class TestAckListener : public QuicAckListenerInterface {
+ public:
+ explicit TestAckListener(int bytes_to_ack) : bytes_to_ack_(bytes_to_ack) {}
+
+ void OnPacketAcked(int acked_bytes,
+ QuicTime::Delta /*delta_largest_observed*/) override {
+ ASSERT_LE(acked_bytes, bytes_to_ack_);
+ bytes_to_ack_ -= acked_bytes;
+ }
+
+ void OnPacketRetransmitted(int /*retransmitted_bytes*/) override {}
+
+ bool has_been_notified() const { return bytes_to_ack_ == 0; }
+
+ protected:
+ // Object is ref counted.
+ ~TestAckListener() override { EXPECT_EQ(0, bytes_to_ack_); }
+
+ private:
+ int bytes_to_ack_;
+};
+
+class TestResponseListener : public QuicSpdyClientBase::ResponseListener {
+ public:
+ void OnCompleteResponse(QuicStreamId id,
+ const SpdyHeaderBlock& response_headers,
+ const std::string& response_body) override {
+ QUIC_DVLOG(1) << "response for stream " << id << " "
+ << response_headers.DebugString() << "\n"
+ << response_body;
+ }
+};
+
+TEST_P(EndToEndTest, AckNotifierWithPacketLossAndBlockedSocket) {
+ // Verify that even in the presence of packet loss and occasionally blocked
+ // socket, an AckNotifierDelegate will get informed that the data it is
+ // interested in has been ACKed. This tests end-to-end ACK notification, and
+ // demonstrates that retransmissions do not break this functionality.
+ if (!BothSidesSupportStatelessRejects()) {
+ // TODO(jokulik): Until we support redundant SREJ packets, don't
+ // drop handshake packets for stateless rejects.
+ SetPacketLossPercentage(5);
+ }
+ ASSERT_TRUE(Initialize());
+
+ // Wait for the server SHLO before upping the packet loss.
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ SetPacketLossPercentage(30);
+ client_writer_->set_fake_blocked_socket_percentage(10);
+
+ // Create a POST request and send the headers only.
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ client_->SendMessage(headers, "", /*fin=*/false);
+
+ // Test the AckNotifier's ability to track multiple packets by making the
+ // request body exceed the size of a single packet.
+ std::string request_string = "a request body bigger than one packet" +
+ std::string(kMaxOutgoingPacketSize, '.');
+
+ // The TestAckListener will cause a failure if not notified.
+ QuicReferenceCountedPointer<TestAckListener> ack_listener(
+ new TestAckListener(request_string.length()));
+
+ // Send the request, and register the delegate for ACKs.
+ client_->SendData(request_string, true, ack_listener);
+ client_->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client_->response_body());
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Send another request to flush out any pending ACKs on the server.
+ client_->SendSynchronousRequest("/bar");
+
+ // Make sure the delegate does get the notification it expects.
+ while (!ack_listener->has_been_notified()) {
+ // Waits for up to 50 ms.
+ client_->client()->WaitForEvents();
+ }
+}
+
+// Send a public reset from the server.
+TEST_P(EndToEndTestWithTls, ServerSendPublicReset) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ if (SupportsIetfQuicWithTls(client_connection->version())) {
+ // TLS handshake does not support stateless reset token yet.
+ return;
+ }
+ QuicUint128 stateless_reset_token = 0;
+ if (client_connection->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
+ QuicConfig* config = client_->client()->session()->config();
+ EXPECT_TRUE(config->HasReceivedStatelessResetToken());
+ stateless_reset_token = config->ReceivedStatelessResetToken();
+ }
+
+ // Send the public reset.
+ QuicConnectionId connection_id = client_connection->connection_id();
+ QuicPublicResetPacket header;
+ header.connection_id = connection_id;
+ QuicFramer framer(server_supported_versions_, QuicTime::Zero(),
+ Perspective::IS_SERVER, kQuicDefaultConnectionIdLength);
+ std::unique_ptr<QuicEncryptedPacket> packet;
+ if (client_connection->transport_version() > QUIC_VERSION_43) {
+ packet = framer.BuildIetfStatelessResetPacket(connection_id,
+ stateless_reset_token);
+ } else {
+ packet = framer.BuildPublicResetPacket(header);
+ }
+ // We must pause the server's thread in order to call WritePacket without
+ // race conditions.
+ server_thread_->Pause();
+ server_writer_->WritePacket(
+ packet->data(), packet->length(), server_address_.host(),
+ client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
+ server_thread_->Resume();
+
+ // The request should fail.
+ EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
+ EXPECT_TRUE(client_->response_headers()->empty());
+ EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error());
+}
+
+// Send a public reset from the server for a different connection ID.
+// It should be ignored.
+TEST_P(EndToEndTestWithTls, ServerSendPublicResetWithDifferentConnectionId) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ if (SupportsIetfQuicWithTls(client_connection->version())) {
+ // TLS handshake does not support stateless reset token yet.
+ return;
+ }
+ QuicUint128 stateless_reset_token = 0;
+ if (client_connection->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
+ QuicConfig* config = client_->client()->session()->config();
+ EXPECT_TRUE(config->HasReceivedStatelessResetToken());
+ stateless_reset_token = config->ReceivedStatelessResetToken();
+ }
+ // Send the public reset.
+ QuicConnectionId incorrect_connection_id = TestConnectionId(
+ TestConnectionIdToUInt64(client_connection->connection_id()) + 1);
+ QuicPublicResetPacket header;
+ header.connection_id = incorrect_connection_id;
+ QuicFramer framer(server_supported_versions_, QuicTime::Zero(),
+ Perspective::IS_SERVER, kQuicDefaultConnectionIdLength);
+ std::unique_ptr<QuicEncryptedPacket> packet;
+ testing::NiceMock<MockQuicConnectionDebugVisitor> visitor;
+ client_->client()->client_session()->connection()->set_debug_visitor(
+ &visitor);
+ if (client_connection->transport_version() > QUIC_VERSION_43) {
+ packet = framer.BuildIetfStatelessResetPacket(incorrect_connection_id,
+ stateless_reset_token);
+ EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
+ .Times(0);
+ } else {
+ packet = framer.BuildPublicResetPacket(header);
+ EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
+ .Times(1);
+ }
+ // We must pause the server's thread in order to call WritePacket without
+ // race conditions.
+ server_thread_->Pause();
+ server_writer_->WritePacket(
+ packet->data(), packet->length(), server_address_.host(),
+ client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
+ server_thread_->Resume();
+
+ if (client_connection->transport_version() > QUIC_VERSION_43) {
+ // The request should fail. IETF stateless reset does not include connection
+ // ID.
+ EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
+ EXPECT_TRUE(client_->response_headers()->empty());
+ EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error());
+ return;
+ }
+ // The connection should be unaffected.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ client_->client()->client_session()->connection()->set_debug_visitor(nullptr);
+}
+
+// Send a public reset from the client for a different connection ID.
+// It should be ignored.
+TEST_P(EndToEndTestWithTls, ClientSendPublicResetWithDifferentConnectionId) {
+ ASSERT_TRUE(Initialize());
+
+ // Send the public reset.
+ QuicConnectionId incorrect_connection_id = TestConnectionId(
+ TestConnectionIdToUInt64(
+ client_->client()->client_session()->connection()->connection_id()) +
+ 1);
+ QuicPublicResetPacket header;
+ header.connection_id = incorrect_connection_id;
+ QuicFramer framer(server_supported_versions_, QuicTime::Zero(),
+ Perspective::IS_CLIENT, kQuicDefaultConnectionIdLength);
+ std::unique_ptr<QuicEncryptedPacket> packet(
+ framer.BuildPublicResetPacket(header));
+ client_writer_->WritePacket(
+ packet->data(), packet->length(),
+ client_->client()->network_helper()->GetLatestClientAddress().host(),
+ server_address_, nullptr);
+
+ // The connection should be unaffected.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+// Send a version negotiation packet from the server for a different
+// connection ID. It should be ignored.
+TEST_P(EndToEndTestWithTls,
+ ServerSendVersionNegotiationWithDifferentConnectionId) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // Send the version negotiation packet.
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ QuicConnectionId incorrect_connection_id = TestConnectionId(
+ TestConnectionIdToUInt64(client_connection->connection_id()) + 1);
+ std::unique_ptr<QuicEncryptedPacket> packet(
+ QuicFramer::BuildVersionNegotiationPacket(
+ incorrect_connection_id,
+ client_connection->transport_version() > QUIC_VERSION_43,
+ server_supported_versions_));
+ testing::NiceMock<MockQuicConnectionDebugVisitor> visitor;
+ client_connection->set_debug_visitor(&visitor);
+ EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
+ .Times(1);
+ // We must pause the server's thread in order to call WritePacket without
+ // race conditions.
+ server_thread_->Pause();
+ server_writer_->WritePacket(
+ packet->data(), packet->length(), server_address_.host(),
+ client_->client()->network_helper()->GetLatestClientAddress(), nullptr);
+ server_thread_->Resume();
+
+ // The connection should be unaffected.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ client_connection->set_debug_visitor(nullptr);
+}
+
+// A bad header shouldn't tear down the connection, because the receiver can't
+// tell the connection ID.
+TEST_P(EndToEndTestWithTls, BadPacketHeaderTruncated) {
+ ASSERT_TRUE(Initialize());
+
+ // Start the connection.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Packet with invalid public flags.
+ char packet[] = {// public flags (8 byte connection_id)
+ 0x3C,
+ // truncated connection ID
+ 0x11};
+ client_writer_->WritePacket(
+ &packet[0], sizeof(packet),
+ client_->client()->network_helper()->GetLatestClientAddress().host(),
+ server_address_, nullptr);
+ // Give the server time to process the packet.
+ QuicSleep(QuicTime::Delta::FromMilliseconds(100));
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER,
+ QuicDispatcherPeer::GetAndClearLastError(dispatcher));
+ server_thread_->Resume();
+
+ // The connection should not be terminated.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+// A bad header shouldn't tear down the connection, because the receiver can't
+// tell the connection ID.
+TEST_P(EndToEndTestWithTls, BadPacketHeaderFlags) {
+ ASSERT_TRUE(Initialize());
+
+ // Start the connection.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Packet with invalid public flags.
+ char packet[] = {
+ // invalid public flags
+ 0xFF,
+ // connection_id
+ 0x10,
+ 0x32,
+ 0x54,
+ 0x76,
+ 0x98,
+ 0xBA,
+ 0xDC,
+ 0xFE,
+ // packet sequence number
+ 0xBC,
+ 0x9A,
+ 0x78,
+ 0x56,
+ 0x34,
+ 0x12,
+ // private flags
+ 0x00,
+ };
+ client_writer_->WritePacket(
+ &packet[0], sizeof(packet),
+ client_->client()->network_helper()->GetLatestClientAddress().host(),
+ server_address_, nullptr);
+ // Give the server time to process the packet.
+ QuicSleep(QuicTime::Delta::FromMilliseconds(100));
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER,
+ QuicDispatcherPeer::GetAndClearLastError(dispatcher));
+ server_thread_->Resume();
+
+ // The connection should not be terminated.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+// Send a packet from the client with bad encrypted data. The server should not
+// tear down the connection.
+TEST_P(EndToEndTestWithTls, BadEncryptedData) {
+ ASSERT_TRUE(Initialize());
+
+ // Start the connection.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
+ client_->client()->client_session()->connection()->connection_id(),
+ EmptyQuicConnectionId(), false, false, 1, "At least 20 characters.",
+ CONNECTION_ID_PRESENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER));
+ // Damage the encrypted data.
+ std::string damaged_packet(packet->data(), packet->length());
+ damaged_packet[30] ^= 0x01;
+ QUIC_DLOG(INFO) << "Sending bad packet.";
+ client_writer_->WritePacket(
+ damaged_packet.data(), damaged_packet.length(),
+ client_->client()->network_helper()->GetLatestClientAddress().host(),
+ server_address_, nullptr);
+ // Give the server time to process the packet.
+ QuicSleep(QuicTime::Delta::FromMilliseconds(100));
+ // This error is sent to the connection's OnError (which ignores it), so the
+ // dispatcher doesn't see it.
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ EXPECT_EQ(QUIC_NO_ERROR,
+ QuicDispatcherPeer::GetAndClearLastError(dispatcher));
+ server_thread_->Resume();
+
+ // The connection should not be terminated.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+TEST_P(EndToEndTestWithTls, CanceledStreamDoesNotBecomeZombie) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ // Lose the request.
+ SetPacketLossPercentage(100);
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ client_->SendMessage(headers, "test_body", /*fin=*/false);
+ QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+
+ // Cancel the stream.
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ QuicSession* session = client_->client()->client_session();
+ // Verify canceled stream does not become zombie.
+ EXPECT_TRUE(QuicSessionPeer::zombie_streams(session).empty());
+ EXPECT_EQ(1u, QuicSessionPeer::closed_streams(session).size());
+}
+
+// A test stream that gives |response_body_| as an error response body.
+class ServerStreamWithErrorResponseBody : public QuicSimpleServerStream {
+ public:
+ ServerStreamWithErrorResponseBody(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ std::string response_body)
+ : QuicSimpleServerStream(id,
+ session,
+ BIDIRECTIONAL,
+ quic_simple_server_backend),
+ response_body_(std::move(response_body)) {}
+
+ ~ServerStreamWithErrorResponseBody() override = default;
+
+ protected:
+ void SendErrorResponse() override {
+ QUIC_DLOG(INFO) << "Sending error response for stream " << id();
+ SpdyHeaderBlock headers;
+ headers[":status"] = "500";
+ headers["content-length"] =
+ QuicTextUtils::Uint64ToString(response_body_.size());
+ // This method must call CloseReadSide to cause the test case, StopReading
+ // is not sufficient.
+ QuicStreamPeer::CloseReadSide(this);
+ SendHeadersAndBody(std::move(headers), response_body_);
+ }
+
+ std::string response_body_;
+};
+
+class StreamWithErrorFactory : public QuicTestServer::StreamFactory {
+ public:
+ explicit StreamWithErrorFactory(std::string response_body)
+ : response_body_(std::move(response_body)) {}
+
+ ~StreamWithErrorFactory() override = default;
+
+ QuicSimpleServerStream* CreateStream(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ QuicSimpleServerBackend* quic_simple_server_backend) override {
+ return new ServerStreamWithErrorResponseBody(
+ id, session, quic_simple_server_backend, response_body_);
+ }
+
+ private:
+ std::string response_body_;
+};
+
+// A test server stream that drops all received body.
+class ServerStreamThatDropsBody : public QuicSimpleServerStream {
+ public:
+ ServerStreamThatDropsBody(QuicStreamId id,
+ QuicSpdySession* session,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerStream(id,
+ session,
+ BIDIRECTIONAL,
+ quic_simple_server_backend) {}
+
+ ~ServerStreamThatDropsBody() override = default;
+
+ protected:
+ void OnBodyAvailable() override {
+ while (HasBytesToRead()) {
+ struct iovec iov;
+ if (GetReadableRegions(&iov, 1) == 0) {
+ // No more data to read.
+ break;
+ }
+ QUIC_DVLOG(1) << "Processed " << iov.iov_len << " bytes for stream "
+ << id();
+ MarkConsumed(iov.iov_len);
+ }
+
+ if (!sequencer()->IsClosed()) {
+ sequencer()->SetUnblocked();
+ return;
+ }
+
+ // If the sequencer is closed, then all the body, including the fin, has
+ // been consumed.
+ OnFinRead();
+
+ if (write_side_closed() || fin_buffered()) {
+ return;
+ }
+
+ SendResponse();
+ }
+};
+
+class ServerStreamThatDropsBodyFactory : public QuicTestServer::StreamFactory {
+ public:
+ ServerStreamThatDropsBodyFactory() = default;
+
+ ~ServerStreamThatDropsBodyFactory() override = default;
+
+ QuicSimpleServerStream* CreateStream(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ QuicSimpleServerBackend* quic_simple_server_backend) override {
+ return new ServerStreamThatDropsBody(id, session,
+ quic_simple_server_backend);
+ }
+};
+
+// A test server stream that sends response with body size greater than 4GB.
+class ServerStreamThatSendsHugeResponse : public QuicSimpleServerStream {
+ public:
+ ServerStreamThatSendsHugeResponse(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ int64_t body_bytes)
+ : QuicSimpleServerStream(id,
+ session,
+ BIDIRECTIONAL,
+ quic_simple_server_backend),
+ body_bytes_(body_bytes) {}
+
+ ~ServerStreamThatSendsHugeResponse() override = default;
+
+ protected:
+ void SendResponse() override {
+ QuicBackendResponse response;
+ std::string body(body_bytes_, 'a');
+ response.set_body(body);
+ SendHeadersAndBodyAndTrailers(response.headers().Clone(), response.body(),
+ response.trailers().Clone());
+ }
+
+ private:
+ // Use a explicit int64_t rather than size_t to simulate a 64-bit server
+ // talking to a 32-bit client.
+ int64_t body_bytes_;
+};
+
+class ServerStreamThatSendsHugeResponseFactory
+ : public QuicTestServer::StreamFactory {
+ public:
+ explicit ServerStreamThatSendsHugeResponseFactory(int64_t body_bytes)
+ : body_bytes_(body_bytes) {}
+
+ ~ServerStreamThatSendsHugeResponseFactory() override = default;
+
+ QuicSimpleServerStream* CreateStream(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ QuicSimpleServerBackend* quic_simple_server_backend) override {
+ return new ServerStreamThatSendsHugeResponse(
+ id, session, quic_simple_server_backend, body_bytes_);
+ }
+
+ int64_t body_bytes_;
+};
+
+TEST_P(EndToEndTest, EarlyResponseFinRecording) {
+ set_smaller_flow_control_receive_window();
+
+ // Verify that an incoming FIN is recorded in a stream object even if the read
+ // side has been closed. This prevents an entry from being made in
+ // locally_close_streams_highest_offset_ (which will never be deleted).
+ // To set up the test condition, the server must do the following in order:
+ // start sending the response and call CloseReadSide
+ // receive the FIN of the request
+ // send the FIN of the response
+
+ // The response body must be larger than the flow control window so the server
+ // must receive a window update from the client before it can finish sending
+ // it.
+ uint32_t response_body_size =
+ 2 * client_config_.GetInitialStreamFlowControlWindowToSend();
+ std::string response_body(response_body_size, 'a');
+
+ StreamWithErrorFactory stream_factory(response_body);
+ SetSpdyStreamFactory(&stream_factory);
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // A POST that gets an early error response, after the headers are received
+ // and before the body is received, due to invalid content-length.
+ // Set an invalid content-length, so the request will receive an early 500
+ // response.
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/garbage";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["content-length"] = "-1";
+
+ // The body must be large enough that the FIN will be in a different packet
+ // than the end of the headers, but short enough to not require a flow control
+ // update. This allows headers processing to trigger the error response
+ // before the request FIN is processed but receive the request FIN before the
+ // response is sent completely.
+ const uint32_t kRequestBodySize = kMaxOutgoingPacketSize + 10;
+ std::string request_body(kRequestBodySize, 'a');
+
+ // Send the request.
+ client_->SendMessage(headers, request_body);
+ client_->WaitForResponse();
+ EXPECT_EQ("500", client_->response_headers()->find(":status")->second);
+
+ // Pause the server so we can access the server's internals without races.
+ server_thread_->Pause();
+
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ QuicDispatcher::SessionMap const& map =
+ QuicDispatcherPeer::session_map(dispatcher);
+ auto it = map.begin();
+ EXPECT_TRUE(it != map.end());
+ QuicSession* server_session = it->second.get();
+
+ // The stream is not waiting for the arrival of the peer's final offset.
+ EXPECT_EQ(
+ 0u, QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(server_session)
+ .size());
+
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTestWithTls, Trailers) {
+ // Test sending and receiving HTTP/2 Trailers (trailing HEADERS frames).
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // Set reordering to ensure that Trailers arriving before body is ok.
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
+ SetReorderPercentage(30);
+
+ // Add a response with headers, body, and trailers.
+ const std::string kBody = "body content";
+
+ SpdyHeaderBlock headers;
+ headers[":status"] = "200";
+ headers[":version"] = "HTTP/1.1";
+ headers["content-length"] = QuicTextUtils::Uint64ToString(kBody.size());
+
+ SpdyHeaderBlock trailers;
+ trailers["some-trailing-header"] = "trailing-header-value";
+
+ memory_cache_backend_.AddResponse(server_hostname_, "/trailer_url",
+ std::move(headers), kBody,
+ trailers.Clone());
+
+ EXPECT_EQ(kBody, client_->SendSynchronousRequest("/trailer_url"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ EXPECT_EQ(trailers, client_->response_trailers());
+}
+
+class EndToEndTestServerPush : public EndToEndTest {
+ protected:
+ const size_t kNumMaxStreams = 10;
+
+ EndToEndTestServerPush() : EndToEndTest() {
+ client_config_.SetMaxIncomingDynamicStreamsToSend(kNumMaxStreams);
+ server_config_.SetMaxIncomingDynamicStreamsToSend(kNumMaxStreams);
+ support_server_push_ = true;
+ }
+
+ // Add a request with its response and |num_resources| push resources into
+ // cache.
+ // If |resource_size| == 0, response body of push resources use default string
+ // concatenating with resource url. Otherwise, generate a string of
+ // |resource_size| as body.
+ void AddRequestAndResponseWithServerPush(std::string host,
+ std::string path,
+ std::string response_body,
+ std::string* push_urls,
+ const size_t num_resources,
+ const size_t resource_size) {
+ bool use_large_response = resource_size != 0;
+ std::string large_resource;
+ if (use_large_response) {
+ // Generate a response common body larger than flow control window for
+ // push response.
+ large_resource = std::string(resource_size, 'a');
+ }
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources;
+ for (size_t i = 0; i < num_resources; ++i) {
+ std::string url = push_urls[i];
+ QuicUrl resource_url(url);
+ std::string body =
+ use_large_response
+ ? large_resource
+ : QuicStrCat("This is server push response body for ", url);
+ SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = "200";
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(body.size());
+ push_resources.push_back(QuicBackendResponse::ServerPushInfo(
+ resource_url, std::move(response_headers), kV3LowestPriority, body));
+ }
+
+ memory_cache_backend_.AddSimpleResponseWithServerPushResources(
+ host, path, 200, response_body, push_resources);
+ }
+};
+
+// Run all server push end to end tests with all supported versions.
+INSTANTIATE_TEST_SUITE_P(EndToEndTestsServerPush,
+ EndToEndTestServerPush,
+ ::testing::ValuesIn(GetTestParams(false, false)));
+
+TEST_P(EndToEndTestServerPush, ServerPush) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // Set reordering to ensure that body arriving before PUSH_PROMISE is ok.
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
+ SetReorderPercentage(30);
+
+ // Add a response with headers, body, and push resources.
+ const std::string kBody = "body content";
+ size_t kNumResources = 4;
+ std::string push_urls[] = {"https://example.com/font.woff",
+ "https://example.com/script.js",
+ "https://fonts.example.com/font.woff",
+ "https://example.com/logo-hires.jpg"};
+ AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody,
+ push_urls, kNumResources, 0);
+
+ client_->client()->set_response_listener(
+ std::unique_ptr<QuicSpdyClientBase::ResponseListener>(
+ new TestResponseListener));
+
+ QUIC_DVLOG(1) << "send request for /push_example";
+ EXPECT_EQ(kBody, client_->SendSynchronousRequest(
+ "https://example.com/push_example"));
+ QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
+ client_->client()->client_session());
+ QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream);
+ // Headers stream's sequencer buffer shouldn't be released because server push
+ // hasn't finished yet.
+ EXPECT_TRUE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+
+ for (const std::string& url : push_urls) {
+ QUIC_DVLOG(1) << "send request for pushed stream on url " << url;
+ std::string expected_body =
+ QuicStrCat("This is server push response body for ", url);
+ std::string response_body = client_->SendSynchronousRequest(url);
+ QUIC_DVLOG(1) << "response body " << response_body;
+ EXPECT_EQ(expected_body, response_body);
+ }
+ EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+}
+
+TEST_P(EndToEndTestServerPush, ServerPushUnderLimit) {
+ // Tests that sending a request which has 4 push resources will trigger server
+ // to push those 4 resources and client can handle pushed resources and match
+ // them with requests later.
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // Set reordering to ensure that body arriving before PUSH_PROMISE is ok.
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
+ SetReorderPercentage(30);
+
+ // Add a response with headers, body, and push resources.
+ const std::string kBody = "body content";
+ size_t const kNumResources = 4;
+ std::string push_urls[] = {
+ "https://example.com/font.woff",
+ "https://example.com/script.js",
+ "https://fonts.example.com/font.woff",
+ "https://example.com/logo-hires.jpg",
+ };
+ AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody,
+ push_urls, kNumResources, 0);
+ client_->client()->set_response_listener(
+ std::unique_ptr<QuicSpdyClientBase::ResponseListener>(
+ new TestResponseListener));
+
+ // Send the first request: this will trigger the server to send all the push
+ // resources associated with this request, and these will be cached by the
+ // client.
+ EXPECT_EQ(kBody, client_->SendSynchronousRequest(
+ "https://example.com/push_example"));
+
+ for (const std::string& url : push_urls) {
+ // Sending subsequent requesets will not actually send anything on the wire,
+ // as the responses are already in the client's cache.
+ QUIC_DVLOG(1) << "send request for pushed stream on url " << url;
+ std::string expected_body =
+ QuicStrCat("This is server push response body for ", url);
+ std::string response_body = client_->SendSynchronousRequest(url);
+ QUIC_DVLOG(1) << "response body " << response_body;
+ EXPECT_EQ(expected_body, response_body);
+ }
+ // Expect only original request has been sent and push responses have been
+ // received as normal response.
+ EXPECT_EQ(1u, client_->num_requests());
+ EXPECT_EQ(1u + kNumResources, client_->num_responses());
+}
+
+TEST_P(EndToEndTestServerPush, ServerPushOverLimitNonBlocking) {
+ // Tests that when streams are not blocked by flow control or congestion
+ // control, pushing even more resources than max number of open outgoing
+ // streams should still work because all response streams get closed
+ // immediately after pushing resources.
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // Set reordering to ensure that body arriving before PUSH_PROMISE is ok.
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
+ SetReorderPercentage(30);
+
+ // Add a response with headers, body, and push resources.
+ const std::string kBody = "body content";
+
+ // One more resource than max number of outgoing stream of this session.
+ const size_t kNumResources = 1 + kNumMaxStreams; // 11.
+ std::string push_urls[11];
+ for (size_t i = 0; i < kNumResources; ++i) {
+ push_urls[i] = QuicStrCat("https://example.com/push_resources", i);
+ }
+ AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody,
+ push_urls, kNumResources, 0);
+ client_->client()->set_response_listener(
+ std::unique_ptr<QuicSpdyClientBase::ResponseListener>(
+ new TestResponseListener));
+
+ // Send the first request: this will trigger the server to send all the push
+ // resources associated with this request, and these will be cached by the
+ // client.
+ EXPECT_EQ(kBody, client_->SendSynchronousRequest(
+ "https://example.com/push_example"));
+
+ for (const std::string& url : push_urls) {
+ // Sending subsequent requesets will not actually send anything on the wire,
+ // as the responses are already in the client's cache.
+ EXPECT_EQ(QuicStrCat("This is server push response body for ", url),
+ client_->SendSynchronousRequest(url));
+ }
+
+ // Only 1 request should have been sent.
+ EXPECT_EQ(1u, client_->num_requests());
+ // The responses to the original request and all the promised resources
+ // should have been received.
+ EXPECT_EQ(12u, client_->num_responses());
+}
+
+TEST_P(EndToEndTestServerPush, ServerPushOverLimitWithBlocking) {
+ // Tests that when server tries to send more large resources(large enough to
+ // be blocked by flow control window or congestion control window) than max
+ // open outgoing streams , server can open upto max number of outgoing
+ // streams for them, and the rest will be queued up.
+
+ // Reset flow control windows.
+ size_t kFlowControlWnd = 20 * 1024; // 20KB.
+ // Response body is larger than 1 flow controlblock window.
+ size_t kBodySize = kFlowControlWnd * 2;
+ set_client_initial_stream_flow_control_receive_window(kFlowControlWnd);
+ // Make sure conntection level flow control window is large enough not to
+ // block data being sent out though they will be blocked by stream level one.
+ set_client_initial_session_flow_control_receive_window(
+ kBodySize * kNumMaxStreams + 1024);
+
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ // Set reordering to ensure that body arriving before PUSH_PROMISE is ok.
+ SetPacketSendDelay(QuicTime::Delta::FromMilliseconds(2));
+ SetReorderPercentage(30);
+
+ // Add a response with headers, body, and push resources.
+ const std::string kBody = "body content";
+
+ const size_t kNumResources = kNumMaxStreams + 1;
+ std::string push_urls[11];
+ for (size_t i = 0; i < kNumResources; ++i) {
+ push_urls[i] = QuicStrCat("http://example.com/push_resources", i);
+ }
+ AddRequestAndResponseWithServerPush("example.com", "/push_example", kBody,
+ push_urls, kNumResources, kBodySize);
+
+ client_->client()->set_response_listener(
+ std::unique_ptr<QuicSpdyClientBase::ResponseListener>(
+ new TestResponseListener));
+
+ client_->SendRequest("https://example.com/push_example");
+
+ // Pause after the first response arrives.
+ while (!client_->response_complete()) {
+ // Because of priority, the first response arrived should be to original
+ // request.
+ client_->WaitForResponse();
+ }
+
+ // Check server session to see if it has max number of outgoing streams opened
+ // though more resources need to be pushed.
+ server_thread_->Pause();
+ EXPECT_EQ(kNumMaxStreams, GetServerSession()->GetNumOpenOutgoingStreams());
+ server_thread_->Resume();
+
+ EXPECT_EQ(1u, client_->num_requests());
+ EXPECT_EQ(1u, client_->num_responses());
+ EXPECT_EQ(kBody, client_->response_body());
+
+ // "Send" request for a promised resources will not really send out it because
+ // its response is being pushed(but blocked). And the following ack and
+ // flow control behavior of SendSynchronousRequests()
+ // will unblock the stream to finish receiving response.
+ client_->SendSynchronousRequest(push_urls[0]);
+ EXPECT_EQ(1u, client_->num_requests());
+ EXPECT_EQ(2u, client_->num_responses());
+
+ // Do same thing for the rest 10 resources.
+ for (size_t i = 1; i < kNumResources; ++i) {
+ client_->SendSynchronousRequest(push_urls[i]);
+ }
+
+ // Because of server push, client gets all pushed resources without actually
+ // sending requests for them.
+ EXPECT_EQ(1u, client_->num_requests());
+ // Including response to original request, 12 responses in total were
+ // received.
+ EXPECT_EQ(12u, client_->num_responses());
+}
+
+// TODO(fayang): this test seems to cause net_unittests timeouts :|
+TEST_P(EndToEndTest, DISABLED_TestHugePostWithPacketLoss) {
+ // This test tests a huge post with introduced packet loss from client to
+ // server and body size greater than 4GB, making sure QUIC code does not break
+ // for 32-bit builds.
+ ServerStreamThatDropsBodyFactory stream_factory;
+ SetSpdyStreamFactory(&stream_factory);
+ ASSERT_TRUE(Initialize());
+ // Set client's epoll server's time out to 0 to make this test be finished
+ // within a short time.
+ client_->epoll_server()->set_timeout_in_us(0);
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ SetPacketLossPercentage(1);
+ // To avoid storing the whole request body in memory, use a loop to repeatedly
+ // send body size of kSizeBytes until the whole request body size is reached.
+ const int kSizeBytes = 128 * 1024;
+ // Request body size is 4G plus one more kSizeBytes.
+ int64_t request_body_size_bytes = pow(2, 32) + kSizeBytes;
+ ASSERT_LT(INT64_C(4294967296), request_body_size_bytes);
+ std::string body(kSizeBytes, 'a');
+
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["content-length"] =
+ QuicTextUtils::Uint64ToString(request_body_size_bytes);
+
+ client_->SendMessage(headers, "", /*fin=*/false);
+
+ for (int i = 0; i < request_body_size_bytes / kSizeBytes; ++i) {
+ bool fin = (i == request_body_size_bytes - 1);
+ client_->SendData(std::string(body.data(), kSizeBytes), fin);
+ client_->client()->WaitForEvents();
+ }
+ VerifyCleanConnection(true);
+}
+
+// TODO(fayang): this test seems to cause net_unittests timeouts :|
+TEST_P(EndToEndTest, DISABLED_TestHugeResponseWithPacketLoss) {
+ // This test tests a huge response with introduced loss from server to client
+ // and body size greater than 4GB, making sure QUIC code does not break for
+ // 32-bit builds.
+ const int kSizeBytes = 128 * 1024;
+ int64_t response_body_size_bytes = pow(2, 32) + kSizeBytes;
+ ASSERT_LT(4294967296, response_body_size_bytes);
+ ServerStreamThatSendsHugeResponseFactory stream_factory(
+ response_body_size_bytes);
+ SetSpdyStreamFactory(&stream_factory);
+
+ StartServer();
+
+ // Use a quic client that drops received body.
+ QuicTestClient* client =
+ new QuicTestClient(server_address_, server_hostname_, client_config_,
+ client_supported_versions_);
+ client->client()->set_drop_response_body(true);
+ client->UseWriter(client_writer_);
+ client->Connect();
+ client_.reset(client);
+ static QuicEpollEvent event(EPOLLOUT);
+ client_writer_->Initialize(
+ QuicConnectionPeer::GetHelper(
+ client_->client()->client_session()->connection()),
+ QuicConnectionPeer::GetAlarmFactory(
+ client_->client()->client_session()->connection()),
+ QuicMakeUnique<ClientDelegate>(client_->client()));
+ initialized_ = true;
+ ASSERT_TRUE(client_->client()->connected());
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ SetPacketLossPercentage(1);
+ client_->SendRequest("/huge_response");
+ client_->WaitForResponse();
+ // TODO(fayang): Fix this test to work with stateless rejects.
+ if (!BothSidesSupportStatelessRejects()) {
+ VerifyCleanConnection(true);
+ }
+}
+
+// Regression test for b/111515567
+TEST_P(EndToEndTest, AgreeOnStopWaiting) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ server_thread_->Pause();
+ QuicConnection* server_connection = GetServerConnection();
+ // Verify client and server connections agree on the value of
+ // no_stop_waiting_frames.
+ EXPECT_EQ(QuicConnectionPeer::GetNoStopWaitingFrames(client_connection),
+ QuicConnectionPeer::GetNoStopWaitingFrames(server_connection));
+ server_thread_->Resume();
+}
+
+// Regression test for b/111515567
+TEST_P(EndToEndTest, AgreeOnStopWaitingWithNoStopWaitingOption) {
+ QuicTagVector options;
+ options.push_back(kNSTP);
+ client_config_.SetConnectionOptionsToSend(options);
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ server_thread_->Pause();
+ QuicConnection* server_connection = GetServerConnection();
+ // Verify client and server connections agree on the value of
+ // no_stop_waiting_frames.
+ EXPECT_EQ(QuicConnectionPeer::GetNoStopWaitingFrames(client_connection),
+ QuicConnectionPeer::GetNoStopWaitingFrames(server_connection));
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest, ReleaseHeadersStreamBufferWhenIdle) {
+ // Tests that when client side has no active request and no waiting
+ // PUSH_PROMISE, its headers stream's sequencer buffer should be released.
+ ASSERT_TRUE(Initialize());
+ client_->SendSynchronousRequest("/foo");
+ QuicHeadersStream* headers_stream = QuicSpdySessionPeer::GetHeadersStream(
+ client_->client()->client_session());
+ QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(headers_stream);
+ EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+}
+
+TEST_P(EndToEndTest, WayTooLongRequestHeaders) {
+ ASSERT_TRUE(Initialize());
+ SpdyHeaderBlock headers;
+ headers[":method"] = "GET";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ headers["key"] = std::string(64 * 1024, 'a');
+
+ client_->SendMessage(headers, "");
+ client_->WaitForResponse();
+ EXPECT_EQ(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE,
+ client_->connection_error());
+}
+
+class WindowUpdateObserver : public QuicConnectionDebugVisitor {
+ public:
+ WindowUpdateObserver() : num_window_update_frames_(0), num_ping_frames_(0) {}
+
+ size_t num_window_update_frames() const { return num_window_update_frames_; }
+
+ size_t num_ping_frames() const { return num_ping_frames_; }
+
+ void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ const QuicTime& receive_time) override {
+ ++num_window_update_frames_;
+ }
+
+ void OnPingFrame(const QuicPingFrame& frame) override { ++num_ping_frames_; }
+
+ private:
+ size_t num_window_update_frames_;
+ size_t num_ping_frames_;
+};
+
+TEST_P(EndToEndTest, WindowUpdateInAck) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ WindowUpdateObserver observer;
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ client_connection->set_debug_visitor(&observer);
+ // 100KB body.
+ std::string body(100 * 1024, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ EXPECT_EQ(kFooResponseBody,
+ client_->SendCustomSynchronousRequest(headers, body));
+ client_->Disconnect();
+ EXPECT_LT(0u, observer.num_window_update_frames());
+ EXPECT_EQ(0u, observer.num_ping_frames());
+}
+
+TEST_P(EndToEndTest, SendStatelessResetTokenInShlo) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ QuicConfig* config = client_->client()->session()->config();
+ EXPECT_TRUE(config->HasReceivedStatelessResetToken());
+ EXPECT_EQ(QuicUtils::GenerateStatelessResetToken(
+ client_->client()->session()->connection()->connection_id()),
+ config->ReceivedStatelessResetToken());
+ client_->Disconnect();
+}
+
+// Regression test for b/116200989.
+TEST_P(EndToEndTest,
+ SendStatelessResetIfServerConnectionClosedLocallyDuringHandshake) {
+ connect_to_server_on_initialize_ = false;
+ ASSERT_TRUE(Initialize());
+
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ ASSERT_EQ(0u, dispatcher->session_map().size());
+ // Note: this writer will only used by the server connection, not the time
+ // wait list.
+ QuicDispatcherPeer::UseWriter(
+ dispatcher,
+ // This cause the first server-sent packet, a.k.a REJ, to fail.
+ new BadPacketWriter(/*packet_causing_write_error=*/0, EPERM));
+ server_thread_->Resume();
+
+ client_.reset(CreateQuicClient(client_writer_));
+ EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
+
+ if (client_->client()->client_session()->connection()->transport_version() >
+ QUIC_VERSION_43) {
+ EXPECT_EQ(QUIC_HANDSHAKE_FAILED, client_->connection_error());
+ } else {
+ EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error());
+ }
+}
+
+// Regression test for b/116200989.
+TEST_P(EndToEndTest,
+ SendStatelessResetIfServerConnectionClosedLocallyAfterHandshake) {
+ // Prevent the connection from expiring in the time wait list.
+ FLAGS_quic_time_wait_list_seconds = 10000;
+ connect_to_server_on_initialize_ = false;
+ ASSERT_TRUE(Initialize());
+
+ // big_response_body is 64K, which is about 48 full-sized packets.
+ const size_t kBigResponseBodySize = 65536;
+ QuicData big_response_body(new char[kBigResponseBodySize](),
+ kBigResponseBodySize, /*owns_buffer=*/true);
+ AddToCache("/big_response", 200, big_response_body.AsStringPiece());
+
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ ASSERT_EQ(0u, dispatcher->session_map().size());
+ QuicDispatcherPeer::UseWriter(
+ dispatcher,
+ // This will cause an server write error with EPERM, while sending the
+ // response for /big_response.
+ new BadPacketWriter(/*packet_causing_write_error=*/20, EPERM));
+ server_thread_->Resume();
+
+ client_.reset(CreateQuicClient(client_writer_));
+
+ // First, a /foo request with small response should succeed.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Second, a /big_response request with big response should fail.
+ EXPECT_LT(client_->SendSynchronousRequest("/big_response").length(),
+ kBigResponseBodySize);
+ EXPECT_EQ(QUIC_PUBLIC_RESET, client_->connection_error());
+}
+
+// Regression test of b/70782529.
+TEST_P(EndToEndTest, DoNotCrashOnPacketWriteError) {
+ ASSERT_TRUE(Initialize());
+ BadPacketWriter* bad_writer =
+ new BadPacketWriter(/*packet_causing_write_error=*/5,
+ /*error_code=*/90);
+ std::unique_ptr<QuicTestClient> client(CreateQuicClient(bad_writer));
+
+ // 1 MB body.
+ std::string body(1024 * 1024, 'a');
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ client->SendCustomSynchronousRequest(headers, body);
+}
+
+// Regression test for b/71711996. This test sends a connectivity probing packet
+// as its last sent packet, and makes sure the server's ACK of that packet does
+// not cause the client to fail.
+TEST_P(EndToEndTest, LastPacketSentIsConnectivityProbing) {
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+
+ // Wait for the client's ACK (of the response) to be received by the server.
+ client_->WaitForDelayedAcks();
+
+ // We are sending a connectivity probing packet from an unchanged client
+ // address, so the server will not respond to us with a connectivity probing
+ // packet, however the server should send an ack-only packet to us.
+ client_->SendConnectivityProbing();
+
+ // Wait for the server's last ACK to be received by the client.
+ client_->WaitForDelayedAcks();
+}
+
+TEST_P(EndToEndTest, PreSharedKey) {
+ client_config_.set_max_time_before_crypto_handshake(
+ QuicTime::Delta::FromSeconds(1));
+ client_config_.set_max_idle_time_before_crypto_handshake(
+ QuicTime::Delta::FromSeconds(1));
+ pre_shared_key_client_ = "foobar";
+ pre_shared_key_server_ = "foobar";
+ ASSERT_TRUE(Initialize());
+
+ ASSERT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+}
+
+// TODO: reenable once we have a way to make this run faster.
+TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyMismatch)) {
+ client_config_.set_max_time_before_crypto_handshake(
+ QuicTime::Delta::FromSeconds(1));
+ client_config_.set_max_idle_time_before_crypto_handshake(
+ QuicTime::Delta::FromSeconds(1));
+ pre_shared_key_client_ = "foo";
+ pre_shared_key_server_ = "bar";
+ // One of two things happens when Initialize() returns:
+ // 1. Crypto handshake has completed, and it is unsuccessful. Initialize()
+ // returns false.
+ // 2. Crypto handshake has not completed, Initialize() returns true. The call
+ // to WaitForCryptoHandshakeConfirmed() will wait for the handshake and
+ // return whether it is successful.
+ ASSERT_FALSE(Initialize() &&
+ client_->client()->WaitForCryptoHandshakeConfirmed());
+ EXPECT_EQ(QUIC_HANDSHAKE_TIMEOUT, client_->connection_error());
+}
+
+// TODO: reenable once we have a way to make this run faster.
+TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoClient)) {
+ client_config_.set_max_time_before_crypto_handshake(
+ QuicTime::Delta::FromSeconds(1));
+ client_config_.set_max_idle_time_before_crypto_handshake(
+ QuicTime::Delta::FromSeconds(1));
+ pre_shared_key_server_ = "foobar";
+ ASSERT_FALSE(Initialize() &&
+ client_->client()->WaitForCryptoHandshakeConfirmed());
+ EXPECT_EQ(QUIC_HANDSHAKE_TIMEOUT, client_->connection_error());
+}
+
+// TODO: reenable once we have a way to make this run faster.
+TEST_P(EndToEndTest, QUIC_TEST_DISABLED_IN_CHROME(PreSharedKeyNoServer)) {
+ client_config_.set_max_time_before_crypto_handshake(
+ QuicTime::Delta::FromSeconds(1));
+ client_config_.set_max_idle_time_before_crypto_handshake(
+ QuicTime::Delta::FromSeconds(1));
+ pre_shared_key_client_ = "foobar";
+ ASSERT_FALSE(Initialize() &&
+ client_->client()->WaitForCryptoHandshakeConfirmed());
+ EXPECT_EQ(QUIC_HANDSHAKE_TIMEOUT, client_->connection_error());
+}
+
+TEST_P(EndToEndTest, RequestAndStreamRstInOnePacket) {
+ // Regression test for b/80234898.
+ ASSERT_TRUE(Initialize());
+
+ // INCOMPLETE_RESPONSE will cause the server to not to send the trailer
+ // (and the FIN) after the response body.
+ std::string response_body(1305, 'a');
+ SpdyHeaderBlock response_headers;
+ response_headers[":status"] = QuicTextUtils::Uint64ToString(200);
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(response_body.length());
+ memory_cache_backend_.AddSpecialResponse(
+ server_hostname_, "/test_url", std::move(response_headers), response_body,
+ QuicBackendResponse::INCOMPLETE_RESPONSE);
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ client_->WaitForDelayedAcks();
+
+ QuicSession* session = client_->client()->client_session();
+ const QuicPacketCount packets_sent_before =
+ session->connection()->GetStats().packets_sent;
+
+ client_->SendRequestAndRstTogether("/test_url");
+
+ // Expect exactly one packet is sent from the block above.
+ ASSERT_EQ(packets_sent_before + 1,
+ session->connection()->GetStats().packets_sent);
+
+ // Wait for the connection to become idle.
+ client_->WaitForDelayedAcks();
+
+ // The real expectation is the test does not crash or timeout.
+ EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
+}
+
+TEST_P(EndToEndTest, ResetStreamOnTtlExpires) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ if (!client_->client()->client_session()->session_decides_what_to_write()) {
+ return;
+ }
+ SetPacketLossPercentage(30);
+
+ QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+ // Set a TTL which expires immediately.
+ stream->MaybeSetTtl(QuicTime::Delta::FromMicroseconds(1));
+
+ // 1 MB body.
+ std::string body(1024 * 1024, 'a');
+ stream->WriteOrBufferBody(body, true);
+ client_->WaitForResponse();
+ EXPECT_EQ(QUIC_STREAM_TTL_EXPIRED, client_->stream_error());
+}
+
+TEST_P(EndToEndTest, SendMessages) {
+ ASSERT_TRUE(Initialize());
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ QuicSession* client_session = client_->client()->client_session();
+ QuicConnection* client_connection = client_session->connection();
+ if (client_connection->transport_version() <= QUIC_VERSION_44) {
+ return;
+ }
+
+ SetPacketLossPercentage(30);
+ ASSERT_GT(kMaxOutgoingPacketSize,
+ client_session->GetCurrentLargestMessagePayload());
+ ASSERT_LT(0, client_session->GetCurrentLargestMessagePayload());
+
+ std::string message_string(kMaxOutgoingPacketSize, 'a');
+ QuicStringPiece message_buffer(message_string);
+ QuicRandom* random =
+ QuicConnectionPeer::GetHelper(client_connection)->GetRandomGenerator();
+ QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
+ {
+ QuicConnection::ScopedPacketFlusher flusher(
+ client_session->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+ // Verify the largest message gets successfully sent.
+ EXPECT_EQ(
+ MessageResult(MESSAGE_STATUS_SUCCESS, 1),
+ client_session->SendMessage(MakeSpan(
+ client_session->connection()
+ ->helper()
+ ->GetStreamSendBufferAllocator(),
+ QuicStringPiece(message_buffer.data(),
+ client_session->GetCurrentLargestMessagePayload()),
+ &storage)));
+ // Send more messages with size (0, largest_payload] until connection is
+ // write blocked.
+ const int kTestMaxNumberOfMessages = 100;
+ for (size_t i = 2; i <= kTestMaxNumberOfMessages; ++i) {
+ size_t message_length =
+ random->RandUint64() %
+ client_session->GetCurrentLargestMessagePayload() +
+ 1;
+ MessageResult result = client_session->SendMessage(MakeSpan(
+ client_session->connection()
+ ->helper()
+ ->GetStreamSendBufferAllocator(),
+ QuicStringPiece(message_buffer.data(), message_length), &storage));
+ if (result.status == MESSAGE_STATUS_BLOCKED) {
+ // Connection is write blocked.
+ break;
+ }
+ EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, i), result);
+ }
+ }
+
+ client_->WaitForDelayedAcks();
+ EXPECT_EQ(MESSAGE_STATUS_TOO_LARGE,
+ client_session
+ ->SendMessage(MakeSpan(
+ client_session->connection()
+ ->helper()
+ ->GetStreamSendBufferAllocator(),
+ QuicStringPiece(
+ message_buffer.data(),
+ client_session->GetCurrentLargestMessagePayload() + 1),
+ &storage))
+ .status);
+ EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
+}
+
+class EndToEndPacketReorderingTest : public EndToEndTest {
+ public:
+ void CreateClientWithWriter() override {
+ QUIC_LOG(ERROR) << "create client with reorder_writer_";
+ reorder_writer_ = new PacketReorderingWriter();
+ client_.reset(EndToEndTest::CreateQuicClient(reorder_writer_));
+ }
+
+ void SetUp() override {
+ // Don't initialize client writer in base class.
+ server_writer_ = new PacketDroppingTestWriter();
+ }
+
+ protected:
+ PacketReorderingWriter* reorder_writer_;
+};
+
+INSTANTIATE_TEST_SUITE_P(EndToEndPacketReorderingTests,
+ EndToEndPacketReorderingTest,
+ testing::ValuesIn(GetTestParams(false, false)));
+
+TEST_P(EndToEndPacketReorderingTest, ReorderedConnectivityProbing) {
+ ASSERT_TRUE(Initialize());
+
+ // Finish one request to make sure handshake established.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+
+ // Wait for the connection to become idle, to make sure the packet gets
+ // delayed is the connectivity probing packet.
+ client_->WaitForDelayedAcks();
+
+ QuicSocketAddress old_addr =
+ client_->client()->network_helper()->GetLatestClientAddress();
+
+ // Migrate socket to the new IP address.
+ QuicIpAddress new_host = TestLoopback(2);
+ EXPECT_NE(old_addr.host(), new_host);
+ ASSERT_TRUE(client_->client()->MigrateSocket(new_host));
+
+ // Write a connectivity probing after the next /foo request.
+ reorder_writer_->SetDelay(1);
+ client_->SendConnectivityProbing();
+
+ ASSERT_TRUE(client_->MigrateSocketWithSpecifiedPort(old_addr.host(),
+ old_addr.port()));
+
+ // The (delayed) connectivity probing will be sent after this request.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+
+ // Send yet another request after the connectivity probing, when this request
+ // returns, the probing is guaranteed to have been received by the server, and
+ // the server's response to probing is guaranteed to have been received by the
+ // client.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+
+ server_thread_->Pause();
+ QuicConnection* server_connection = GetServerConnection();
+ EXPECT_EQ(1u,
+ server_connection->GetStats().num_connectivity_probing_received);
+ server_thread_->Resume();
+
+ QuicConnection* client_connection =
+ client_->client()->client_session()->connection();
+ EXPECT_EQ(1u,
+ client_connection->GetStats().num_connectivity_probing_received);
+}
+
+TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) {
+ ASSERT_TRUE(Initialize());
+ // Finish one request to make sure handshake established.
+ client_->SendSynchronousRequest("/foo");
+ // Disconnect for next 0-rtt request.
+ client_->Disconnect();
+
+ // Client get valid STK now. Do a 0-rtt request.
+ // Buffer a CHLO till another packets sent out.
+ reorder_writer_->SetDelay(1);
+ // Only send out a CHLO.
+ client_->client()->Initialize();
+ client_->client()->StartConnect();
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_->client()->connected());
+
+ // Send a request before handshake finishes.
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/bar";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+
+ client_->SendMessage(headers, "");
+ client_->WaitForResponse();
+ EXPECT_EQ(kBarResponseBody, client_->response_body());
+ QuicConnectionStats client_stats =
+ client_->client()->client_session()->connection()->GetStats();
+ EXPECT_EQ(0u, client_stats.packets_lost);
+ if (ServerSendsVersionNegotiation()) {
+ EXPECT_EQ(2, client_->client()->GetNumSentClientHellos());
+ } else {
+ EXPECT_EQ(1, client_->client()->GetNumSentClientHellos());
+ }
+}
+
+// Test that STOP_SENDING makes it to the other side. Set up a client & server,
+// create a stream (do not close it), and then send a STOP_SENDING from one
+// side. The other side should get a call to QuicStream::OnStopSending.
+// (aside, test cribbed from RequestAndStreamRstInOnePacket)
+TEST_P(EndToEndTest, SimpleStopSendingTest) {
+ const uint16_t kStopSendingTestCode = 123;
+ ASSERT_TRUE(Initialize());
+ if (negotiated_version_.transport_version != QUIC_VERSION_99) {
+ return;
+ }
+ QuicSession* client_session = client_->client()->client_session();
+ ASSERT_NE(nullptr, client_session);
+ QuicConnection* client_connection = client_session->connection();
+ ASSERT_NE(nullptr, client_connection);
+
+ // STOP_SENDING will cause the server to not to send the trailer
+ // (and the FIN) after the response body. Instead, it sends a STOP_SENDING
+ // frame for the stream.
+ std::string response_body(1305, 'a');
+ SpdyHeaderBlock response_headers;
+ response_headers[":status"] = QuicTextUtils::Uint64ToString(200);
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(response_body.length());
+ memory_cache_backend_.AddStopSendingResponse(
+ server_hostname_, "/test_url", std::move(response_headers), response_body,
+ kStopSendingTestCode);
+
+ EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
+ client_->WaitForDelayedAcks();
+
+ QuicSession* session = client_->client()->client_session();
+ const QuicPacketCount packets_sent_before =
+ session->connection()->GetStats().packets_sent;
+
+ QuicStreamId stream_id = session->next_outgoing_bidirectional_stream_id();
+ client_->SendRequest("/test_url");
+
+ // Expect exactly one packet is sent from the block above.
+ ASSERT_EQ(packets_sent_before + 1,
+ session->connection()->GetStats().packets_sent);
+
+ // Wait for the connection to become idle.
+ client_->WaitForDelayedAcks();
+
+ // The real expectation is the test does not crash or timeout.
+ EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
+ // And that the stop-sending code is received.
+ QuicSimpleClientStream* client_stream =
+ static_cast<QuicSimpleClientStream*>(client_->latest_created_stream());
+ ASSERT_NE(nullptr, client_stream);
+ // Make sure we have the correct stream
+ EXPECT_EQ(stream_id, client_stream->id());
+ EXPECT_EQ(kStopSendingTestCode, client_stream->last_stop_sending_code());
+}
+
+TEST_P(EndToEndTest, SimpleStopSendingRstStreamTest) {
+ ASSERT_TRUE(Initialize());
+
+ // Send a request without a fin, to keep the stream open
+ SpdyHeaderBlock headers;
+ headers[":method"] = "POST";
+ headers[":path"] = "/foo";
+ headers[":scheme"] = "https";
+ headers[":authority"] = server_hostname_;
+ client_->SendMessage(headers, "", /*fin=*/false);
+ // Stream should be open
+ ASSERT_NE(nullptr, client_->latest_created_stream());
+ EXPECT_FALSE(client_->latest_created_stream()->write_side_closed());
+ EXPECT_FALSE(
+ QuicStreamPeer::read_side_closed(client_->latest_created_stream()));
+
+ // Send a RST_STREAM+STOP_SENDING on the stream
+ // Code is not important.
+ client_->latest_created_stream()->Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ client_->WaitForResponse();
+
+ // Stream should be gone.
+ ASSERT_EQ(nullptr, client_->latest_created_stream());
+}
+
+class BadShloPacketWriter : public QuicPacketWriterWrapper {
+ public:
+ BadShloPacketWriter() : error_returned_(false) {}
+ ~BadShloPacketWriter() override {}
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ quic::PerPacketOptions* options) override {
+ const WriteResult result = QuicPacketWriterWrapper::WritePacket(
+ buffer, buf_len, self_address, peer_address, options);
+ const uint8_t type_byte = buffer[0];
+ if (!error_returned_ && (type_byte & FLAGS_LONG_HEADER) &&
+ (((type_byte & 0x30) >> 4) == 1 || (type_byte & 0x7F) == 0x7C)) {
+ QUIC_DVLOG(1) << "Return write error for ZERO_RTT_PACKET";
+ error_returned_ = true;
+ return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE);
+ }
+ return result;
+ }
+
+ private:
+ bool error_returned_;
+};
+
+TEST_P(EndToEndTest, ZeroRttProtectedConnectionClose) {
+ // This test ensures ZERO_RTT_PROTECTED connection close could close a client
+ // which has switched to forward secure.
+ connect_to_server_on_initialize_ =
+ negotiated_version_.transport_version <= QUIC_VERSION_43;
+ ASSERT_TRUE(Initialize());
+ if (negotiated_version_.transport_version <= QUIC_VERSION_43) {
+ // Only runs for IETF QUIC header.
+ return;
+ }
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ ASSERT_EQ(0u, dispatcher->session_map().size());
+ // Note: this writer will only used by the server connection, not the time
+ // wait list.
+ QuicDispatcherPeer::UseWriter(
+ dispatcher,
+ // This causes the first server sent ZERO_RTT_PROTECTED packet (i.e.,
+ // SHLO) to be sent, but WRITE_ERROR is returned. Such that a
+ // ZERO_RTT_PROTECTED connection close would be sent to a client with
+ // encryption level FORWARD_SECURE.
+ new BadShloPacketWriter());
+ server_thread_->Resume();
+
+ client_.reset(CreateQuicClient(client_writer_));
+ EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
+ // Verify ZERO_RTT_PROTECTED connection close is successfully processed by
+ // client which switches to FORWARD_SECURE.
+ EXPECT_EQ(QUIC_PACKET_WRITE_ERROR, client_->connection_error());
+}
+
+class BadShloPacketWriter2 : public QuicPacketWriterWrapper {
+ public:
+ BadShloPacketWriter2() : error_returned_(false) {}
+ ~BadShloPacketWriter2() override {}
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ quic::PerPacketOptions* options) override {
+ const uint8_t type_byte = buffer[0];
+ if ((type_byte & FLAGS_LONG_HEADER) &&
+ (((type_byte & 0x30) >> 4) == 1 || (type_byte & 0x7F) == 0x7C)) {
+ QUIC_DVLOG(1) << "Dropping ZERO_RTT_PACKET packet";
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+ if (!error_returned_ && !(type_byte & FLAGS_LONG_HEADER)) {
+ QUIC_DVLOG(1) << "Return write error for short header packet";
+ error_returned_ = true;
+ return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE);
+ }
+ return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+ peer_address, options);
+ }
+
+ private:
+ bool error_returned_;
+};
+
+TEST_P(EndToEndTest, ForwardSecureConnectionClose) {
+ // This test ensures ZERO_RTT_PROTECTED connection close is sent to a client
+ // which has ZERO_RTT_PROTECTED encryption level.
+ SetQuicReloadableFlag(quic_fix_termination_packets, true);
+ connect_to_server_on_initialize_ =
+ negotiated_version_.transport_version <= QUIC_VERSION_43;
+ ASSERT_TRUE(Initialize());
+ if (negotiated_version_.transport_version <= QUIC_VERSION_43) {
+ // Only runs for IETF QUIC header.
+ return;
+ }
+ server_thread_->Pause();
+ QuicDispatcher* dispatcher =
+ QuicServerPeer::GetDispatcher(server_thread_->server());
+ ASSERT_EQ(0u, dispatcher->session_map().size());
+ // Note: this writer will only used by the server connection, not the time
+ // wait list.
+ QuicDispatcherPeer::UseWriter(
+ dispatcher,
+ // This causes the all server sent ZERO_RTT_PROTECTED packets to be
+ // dropped, and first short header packet causes write error.
+ new BadShloPacketWriter2());
+ server_thread_->Resume();
+ client_.reset(CreateQuicClient(client_writer_));
+ EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
+ // Verify ZERO_RTT_PROTECTED connection close is successfully processed by
+ // client.
+ EXPECT_EQ(QUIC_PACKET_WRITE_ERROR, client_->connection_error());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..ec0062a0dd0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.cc
@@ -0,0 +1,469 @@
+// 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 "net/third_party/quiche/src/quic/core/http/http_decoder.h"
+
+#include <type_traits>
+
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+
+namespace quic {
+
+namespace {
+
+// Create a mask that sets the last |num_bits| to 1 and the rest to 0.
+inline uint8_t GetMaskFromNumBits(uint8_t num_bits) {
+ return (1u << num_bits) - 1;
+}
+
+// Extract |num_bits| from |flags| offset by |offset|.
+uint8_t ExtractBits(uint8_t flags, uint8_t num_bits, uint8_t offset) {
+ return (flags >> offset) & GetMaskFromNumBits(num_bits);
+}
+
+// Length of the type field of HTTP/3 frames.
+static const QuicByteCount kFrameTypeLength = 1;
+// Length of the weight field of a priority frame.
+static const size_t kPriorityWeightLength = 1;
+// Length of a priority frame's first byte.
+static const size_t kPriorityFirstByteLength = 1;
+
+} // namespace
+
+HttpDecoder::HttpDecoder()
+ : visitor_(nullptr),
+ state_(STATE_READING_FRAME_LENGTH),
+ current_frame_type_(0),
+ current_length_field_size_(0),
+ remaining_length_field_length_(0),
+ current_frame_length_(0),
+ remaining_frame_length_(0),
+ error_(QUIC_NO_ERROR),
+ error_detail_("") {}
+
+HttpDecoder::~HttpDecoder() {}
+
+QuicByteCount HttpDecoder::ProcessInput(const char* data, QuicByteCount len) {
+ QuicDataReader reader(data, len);
+ while (error_ == QUIC_NO_ERROR &&
+ (reader.BytesRemaining() != 0 || state_ == STATE_FINISH_PARSING)) {
+ switch (state_) {
+ case STATE_READING_FRAME_LENGTH:
+ ReadFrameLength(&reader);
+ break;
+ case STATE_READING_FRAME_TYPE:
+ ReadFrameType(&reader);
+ break;
+ case STATE_READING_FRAME_PAYLOAD:
+ ReadFramePayload(&reader);
+ break;
+ case STATE_FINISH_PARSING:
+ FinishParsing();
+ break;
+ case STATE_ERROR:
+ break;
+ default:
+ QUIC_BUG << "Invalid state: " << state_;
+ }
+ }
+
+ if (error_ != QUIC_NO_ERROR) {
+ return 0;
+ }
+
+ return len - reader.BytesRemaining();
+}
+
+void HttpDecoder::ReadFrameLength(QuicDataReader* reader) {
+ DCHECK_NE(0u, reader->BytesRemaining());
+ BufferFrameLength(reader);
+ if (remaining_length_field_length_ != 0) {
+ return;
+ }
+ QuicDataReader length_reader(length_buffer_.data(),
+ current_length_field_size_);
+ if (!length_reader.ReadVarInt62(&current_frame_length_)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame length");
+ visitor_->OnError(this);
+ return;
+ }
+
+ state_ = STATE_READING_FRAME_TYPE;
+ remaining_frame_length_ = current_frame_length_;
+}
+
+void HttpDecoder::ReadFrameType(QuicDataReader* reader) {
+ DCHECK_NE(0u, reader->BytesRemaining());
+ if (!reader->ReadUInt8(&current_frame_type_)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame type");
+ return;
+ }
+
+ if (current_frame_length_ > MaxFrameLength(current_frame_type_)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Frame is too large");
+ visitor_->OnError(this);
+ return;
+ }
+
+ // Calling the following two visitor methods does not require parsing of any
+ // frame payload.
+ if (current_frame_type_ == 0x0) {
+ visitor_->OnDataFrameStart(Http3FrameLengths(
+ current_length_field_size_ + kFrameTypeLength, current_frame_length_));
+ } else if (current_frame_type_ == 0x1) {
+ visitor_->OnHeadersFrameStart(Http3FrameLengths(
+ current_length_field_size_ + kFrameTypeLength, current_frame_length_));
+ } else if (current_frame_type_ == 0x4) {
+ visitor_->OnSettingsFrameStart(Http3FrameLengths(
+ current_length_field_size_ + kFrameTypeLength, current_frame_length_));
+ }
+
+ state_ = (remaining_frame_length_ == 0) ? STATE_FINISH_PARSING
+ : STATE_READING_FRAME_PAYLOAD;
+}
+
+void HttpDecoder::ReadFramePayload(QuicDataReader* reader) {
+ DCHECK_NE(0u, reader->BytesRemaining());
+ DCHECK_NE(0u, remaining_frame_length_);
+ switch (current_frame_type_) {
+ case 0x0: { // DATA
+ QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+ remaining_frame_length_, reader->BytesRemaining());
+ QuicStringPiece payload;
+ if (!reader->ReadStringPiece(&payload, bytes_to_read)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read data");
+ return;
+ }
+ DCHECK(!payload.empty());
+ visitor_->OnDataFramePayload(payload);
+ remaining_frame_length_ -= payload.length();
+ break;
+ }
+ case 0x1: { // HEADERS
+ QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+ remaining_frame_length_, reader->BytesRemaining());
+ QuicStringPiece payload;
+ if (!reader->ReadStringPiece(&payload, bytes_to_read)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read data");
+ return;
+ }
+ DCHECK(!payload.empty());
+ visitor_->OnHeadersFramePayload(payload);
+ remaining_frame_length_ -= payload.length();
+ break;
+ }
+ case 0x2: { // PRIORITY
+ // TODO(rch): avoid buffering if the entire frame is present, and
+ // instead parse directly out of |reader|.
+ BufferFramePayload(reader);
+ break;
+ }
+ case 0x3: { // CANCEL_PUSH
+ BufferFramePayload(reader);
+ break;
+ }
+ case 0x4: { // SETTINGS
+ BufferFramePayload(reader);
+ break;
+ }
+ case 0x5: { // PUSH_PROMISE
+ if (current_frame_length_ == remaining_frame_length_) {
+ QuicByteCount bytes_remaining = reader->BytesRemaining();
+ PushId push_id;
+ // TODO(rch): Handle partial delivery of this field.
+ if (!reader->ReadVarInt62(&push_id)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
+ return;
+ }
+ remaining_frame_length_ -= bytes_remaining - reader->BytesRemaining();
+ visitor_->OnPushPromiseFrameStart(push_id);
+ }
+ 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;
+ }
+ QuicStringPiece payload;
+ if (!reader->ReadStringPiece(&payload, bytes_to_read)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read data");
+ return;
+ }
+ DCHECK(!payload.empty());
+ visitor_->OnPushPromiseFramePayload(payload);
+ remaining_frame_length_ -= payload.length();
+ break;
+ }
+ case 0x7: { // GOAWAY
+ BufferFramePayload(reader);
+ break;
+ }
+
+ case 0xD: { // MAX_PUSH_ID
+ // TODO(rch): Handle partial delivery.
+ BufferFramePayload(reader);
+ break;
+ }
+
+ case 0xE: { // DUPLICATE_PUSH
+ BufferFramePayload(reader);
+ break;
+ }
+ // Reserved frame types.
+ // TODO(rch): Since these are actually the same behavior as the
+ // default, we probably don't need to special case them here?
+ case 0xB:
+ QUIC_FALLTHROUGH_INTENDED;
+ case 0xB + 0x1F:
+ QUIC_FALLTHROUGH_INTENDED;
+ case 0xB + 0x1F * 2:
+ QUIC_FALLTHROUGH_INTENDED;
+ case 0xB + 0x1F * 3:
+ QUIC_FALLTHROUGH_INTENDED;
+ case 0xB + 0x1F * 4:
+ QUIC_FALLTHROUGH_INTENDED;
+ case 0xB + 0x1F * 5:
+ QUIC_FALLTHROUGH_INTENDED;
+ case 0xB + 0x1F * 6:
+ QUIC_FALLTHROUGH_INTENDED;
+ case 0xB + 0x1F * 7:
+ QUIC_FALLTHROUGH_INTENDED;
+ default:
+ DiscardFramePayload(reader);
+ }
+
+ if (remaining_frame_length_ == 0) {
+ state_ = STATE_FINISH_PARSING;
+ }
+}
+
+void HttpDecoder::FinishParsing() {
+ DCHECK_EQ(0u, remaining_frame_length_);
+ switch (current_frame_type_) {
+ case 0x0: { // DATA
+ visitor_->OnDataFrameEnd();
+ break;
+ }
+ case 0x1: { // HEADERS
+ visitor_->OnHeadersFrameEnd();
+ break;
+ }
+ case 0x2: { // PRIORITY
+ // TODO(rch): avoid buffering if the entire frame is present, and
+ // instead parse directly out of |reader|.
+ PriorityFrame frame;
+ QuicDataReader reader(buffer_.data(), current_frame_length_);
+ if (!ParsePriorityFrame(&reader, &frame)) {
+ return;
+ }
+ visitor_->OnPriorityFrame(frame);
+ break;
+ }
+ case 0x3: { // CANCEL_PUSH
+ // TODO(rch): Handle partial delivery.
+ CancelPushFrame frame;
+ QuicDataReader reader(buffer_.data(), current_frame_length_);
+ if (!reader.ReadVarInt62(&frame.push_id)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
+ return;
+ }
+ visitor_->OnCancelPushFrame(frame);
+ break;
+ }
+ case 0x4: { // SETTINGS
+ // TODO(rch): Handle overly large SETTINGS frames. Either:
+ // 1. Impose a limit on SETTINGS frame size, and close the connection if
+ // exceeded
+ // 2. Implement a streaming parsing mode.
+ SettingsFrame frame;
+ QuicDataReader reader(buffer_.data(), current_frame_length_);
+ if (!ParseSettingsFrame(&reader, &frame)) {
+ return;
+ }
+ visitor_->OnSettingsFrame(frame);
+ break;
+ }
+ case 0x5: { // PUSH_PROMISE
+ visitor_->OnPushPromiseFrameEnd();
+ break;
+ }
+ case 0x7: { // GOAWAY
+ QuicDataReader reader(buffer_.data(), current_frame_length_);
+ GoAwayFrame frame;
+ static_assert(!std::is_same<decltype(frame.stream_id), uint64_t>::value,
+ "Please remove local |stream_id| variable and pass "
+ "&frame.stream_id directly to ReadVarInt62() when changing "
+ "QuicStreamId from uint32_t to uint64_t.");
+ uint64_t stream_id;
+ if (!reader.ReadVarInt62(&stream_id)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read GOAWAY stream_id");
+ return;
+ }
+ frame.stream_id = static_cast<QuicStreamId>(stream_id);
+ visitor_->OnGoAwayFrame(frame);
+ break;
+ }
+
+ case 0xD: { // MAX_PUSH_ID
+ QuicDataReader reader(buffer_.data(), current_frame_length_);
+ MaxPushIdFrame frame;
+ if (!reader.ReadVarInt62(&frame.push_id)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
+ return;
+ }
+ visitor_->OnMaxPushIdFrame(frame);
+ break;
+ }
+
+ case 0xE: { // DUPLICATE_PUSH
+ QuicDataReader reader(buffer_.data(), current_frame_length_);
+ DuplicatePushFrame frame;
+ if (!reader.ReadVarInt62(&frame.push_id)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read push_id");
+ return;
+ }
+ visitor_->OnDuplicatePushFrame(frame);
+ break;
+ }
+ }
+
+ current_length_field_size_ = 0;
+ state_ = STATE_READING_FRAME_LENGTH;
+}
+
+void HttpDecoder::DiscardFramePayload(QuicDataReader* reader) {
+ QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+ remaining_frame_length_, reader->BytesRemaining());
+ QuicStringPiece payload;
+ if (!reader->ReadStringPiece(&payload, bytes_to_read)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame payload");
+ return;
+ }
+ remaining_frame_length_ -= payload.length();
+ if (remaining_frame_length_ == 0) {
+ state_ = STATE_READING_FRAME_LENGTH;
+ current_length_field_size_ = 0;
+ }
+}
+
+void HttpDecoder::BufferFramePayload(QuicDataReader* reader) {
+ if (current_frame_length_ == remaining_frame_length_) {
+ buffer_.erase(buffer_.size());
+ buffer_.reserve(current_frame_length_);
+ }
+ QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+ remaining_frame_length_, reader->BytesRemaining());
+ if (!reader->ReadBytes(
+ &(buffer_[0]) + current_frame_length_ - remaining_frame_length_,
+ bytes_to_read)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame payload");
+ return;
+ }
+ remaining_frame_length_ -= bytes_to_read;
+}
+
+void HttpDecoder::BufferFrameLength(QuicDataReader* reader) {
+ if (current_length_field_size_ == 0) {
+ current_length_field_size_ = reader->PeekVarInt62Length();
+ if (current_length_field_size_ == 0) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame length");
+ visitor_->OnError(this);
+ return;
+ }
+ remaining_length_field_length_ = current_length_field_size_;
+ }
+ if (current_length_field_size_ == remaining_length_field_length_) {
+ length_buffer_.erase(length_buffer_.size());
+ length_buffer_.reserve(current_length_field_size_);
+ }
+ QuicByteCount bytes_to_read = std::min<QuicByteCount>(
+ remaining_length_field_length_, reader->BytesRemaining());
+ if (!reader->ReadBytes(&(length_buffer_[0]) + current_length_field_size_ -
+ remaining_length_field_length_,
+ bytes_to_read)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read frame length");
+ visitor_->OnError(this);
+ return;
+ }
+ remaining_length_field_length_ -= bytes_to_read;
+}
+
+void HttpDecoder::RaiseError(QuicErrorCode error, std::string error_detail) {
+ state_ = STATE_ERROR;
+ error_ = error;
+ error_detail_ = std::move(error_detail);
+}
+
+bool HttpDecoder::ParsePriorityFrame(QuicDataReader* reader,
+ PriorityFrame* frame) {
+ uint8_t flags;
+ if (!reader->ReadUInt8(&flags)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read priority frame flags");
+ return false;
+ }
+
+ frame->prioritized_type =
+ static_cast<PriorityElementType>(ExtractBits(flags, 2, 6));
+ frame->dependency_type =
+ static_cast<PriorityElementType>(ExtractBits(flags, 2, 4));
+ frame->exclusive = flags % 2 == 1;
+ if (!reader->ReadVarInt62(&frame->prioritized_element_id)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read prioritized_element_id");
+ return false;
+ }
+ if (!reader->ReadVarInt62(&frame->element_dependency_id)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read element_dependency_id");
+ return false;
+ }
+ if (!reader->ReadUInt8(&frame->weight)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read priority frame weight");
+ return false;
+ }
+ return true;
+}
+
+bool HttpDecoder::ParseSettingsFrame(QuicDataReader* reader,
+ SettingsFrame* frame) {
+ while (!reader->IsDoneReading()) {
+ uint16_t id;
+ if (!reader->ReadUInt16(&id)) {
+ RaiseError(QUIC_INTERNAL_ERROR,
+ "Unable to read settings frame identifier");
+ return false;
+ }
+ uint64_t content;
+ if (!reader->ReadVarInt62(&content)) {
+ RaiseError(QUIC_INTERNAL_ERROR, "Unable to read settings frame content");
+ return false;
+ }
+ frame->values[id] = content;
+ }
+ return true;
+}
+
+QuicByteCount HttpDecoder::MaxFrameLength(uint8_t frame_type) {
+ switch (frame_type) {
+ case 0x2: // PRIORITY
+ return kPriorityFirstByteLength + VARIABLE_LENGTH_INTEGER_LENGTH_8 * 2 +
+ kPriorityWeightLength;
+ case 0x3: // CANCEL_PUSH
+ return sizeof(PushId);
+ case 0x4: // SETTINGS
+ // This limit is arbitrary.
+ return 1024 * 1024;
+ case 0x7: // GOAWAY
+ return sizeof(QuicStreamId);
+ case 0xD: // MAX_PUSH_ID
+ return sizeof(PushId);
+ case 0xE: // DUPLICATE_PUSH
+ return sizeof(PushId);
+ default:
+ // Other frames require no data buffering, so it's safe to have no limit.
+ return std::numeric_limits<QuicByteCount>::max();
+ }
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..55de8adf3ac
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder.h
@@ -0,0 +1,196 @@
+// 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_CORE_HTTP_HTTP_DECODER_H_
+#define QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/http/http_frames.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicDataReader;
+
+// Struct that stores meta data of an HTTP/3 frame.
+// |header_length| is frame header length in bytes.
+// |payload_length| is frame payload length in bytes.
+struct QUIC_EXPORT_PRIVATE Http3FrameLengths {
+ Http3FrameLengths(QuicByteCount header, QuicByteCount payload)
+ : header_length(header), payload_length(payload) {}
+
+ bool operator==(const Http3FrameLengths& other) const {
+ return (header_length == other.header_length) &&
+ (payload_length == other.payload_length);
+ }
+
+ QuicByteCount header_length;
+ QuicByteCount payload_length;
+};
+
+// A class for decoding the HTTP frames that are exchanged in an HTTP over QUIC
+// session.
+class QUIC_EXPORT_PRIVATE HttpDecoder {
+ public:
+ class QUIC_EXPORT_PRIVATE Visitor {
+ public:
+ virtual ~Visitor() {}
+
+ // Called if an error is detected.
+ virtual void OnError(HttpDecoder* decoder) = 0;
+
+ // Called when a PRIORITY frame has been successfully parsed.
+ virtual void OnPriorityFrame(const PriorityFrame& frame) = 0;
+
+ // Called when a CANCEL_PUSH frame has been successfully parsed.
+ virtual void OnCancelPushFrame(const CancelPushFrame& frame) = 0;
+
+ // Called when a MAX_PUSH_ID frame has been successfully parsed.
+ virtual void OnMaxPushIdFrame(const MaxPushIdFrame& frame) = 0;
+
+ // Called when a GOAWAY frame has been successfully parsed.
+ virtual void OnGoAwayFrame(const GoAwayFrame& frame) = 0;
+
+ // Called when a SETTINGS frame has been received.
+ virtual void OnSettingsFrameStart(Http3FrameLengths frame_length) = 0;
+
+ // Called when a SETTINGS frame has been successfully parsed.
+ virtual void OnSettingsFrame(const SettingsFrame& frame) = 0;
+
+ // Called when a DUPLICATE_PUSH frame has been successfully parsed.
+ virtual void OnDuplicatePushFrame(const DuplicatePushFrame& frame) = 0;
+
+ // Called when a DATA frame has been received.
+ // |frame_length| contains DATA frame length and payload length.
+ virtual void OnDataFrameStart(Http3FrameLengths frame_length) = 0;
+ // Called when part of the payload of a DATA frame has been read. May be
+ // called multiple times for a single frame. |payload| is guaranteed to be
+ // non-empty.
+ virtual void OnDataFramePayload(QuicStringPiece payload) = 0;
+ // Called when a DATA frame has been completely processed.
+ virtual void OnDataFrameEnd() = 0;
+
+ // Called when a HEADERS frame has been received.
+ // |frame_length| contains HEADERS frame length and payload length.
+ virtual void OnHeadersFrameStart(Http3FrameLengths frame_length) = 0;
+ // Called when part of the payload of a HEADERS frame has been read. May be
+ // called multiple times for a single frame. |payload| is guaranteed to be
+ // non-empty.
+ virtual void OnHeadersFramePayload(QuicStringPiece payload) = 0;
+ // Called when a HEADERS frame has been completely processed.
+ // |frame_len| is the length of the HEADERS frame payload.
+ virtual void OnHeadersFrameEnd() = 0;
+
+ // Called when a PUSH_PROMISE frame has been received for |push_id|.
+ virtual void OnPushPromiseFrameStart(PushId push_id) = 0;
+ // Called when part of the payload 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 void OnPushPromiseFramePayload(QuicStringPiece payload) = 0;
+ // Called when a PUSH_PROMISE frame has been completely processed.
+ virtual void OnPushPromiseFrameEnd() = 0;
+
+ // TODO(rch): Consider adding methods like:
+ // OnUnknownFrame{Start,Payload,End}()
+ // to allow callers to handle unknown frames.
+ };
+
+ HttpDecoder();
+
+ ~HttpDecoder();
+
+ // Set callbacks to be called from the decoder. A visitor must be set, or
+ // else the decoder will crash. It is acceptable for the visitor to do
+ // nothing. If this is called multiple times, only the last visitor
+ // will be used. |visitor| will be owned by the caller.
+ void set_visitor(Visitor* visitor) { visitor_ = visitor; }
+
+ // Processes the input and invokes the visitor for any frames.
+ // Returns the number of bytes consumed, or 0 if there was an error, in which
+ // case error() should be consulted.
+ QuicByteCount ProcessInput(const char* data, QuicByteCount len);
+
+ QuicErrorCode error() const { return error_; }
+ const std::string& error_detail() const { return error_detail_; }
+
+ private:
+ // Represents the current state of the parsing state machine.
+ enum HttpDecoderState {
+ STATE_READING_FRAME_LENGTH,
+ STATE_READING_FRAME_TYPE,
+ STATE_READING_FRAME_PAYLOAD,
+ STATE_FINISH_PARSING,
+ STATE_ERROR
+ };
+
+ // Reads the length of a frame from |reader|. Sets error_ and error_detail_
+ // if there are any errors.
+ void ReadFrameLength(QuicDataReader* reader);
+
+ // Reads the type of a frame from |reader|. Sets error_ and error_detail_
+ // if there are any errors. Also calls OnDataFrameStart() or
+ // OnHeadersFrameStart() for appropriate frame types.
+ void ReadFrameType(QuicDataReader* reader);
+
+ // Reads the payload of the current frame from |reader| and processes it,
+ // possibly buffering the data or invoking the visitor.
+ void ReadFramePayload(QuicDataReader* reader);
+
+ // Optionally parses buffered data; calls visitor method to signal that frame
+ // had been parsed completely.
+ void FinishParsing();
+
+ // Discards any remaining frame payload from |reader|.
+ void DiscardFramePayload(QuicDataReader* reader);
+
+ // Buffers any remaining frame payload from |reader| into |buffer_|.
+ void BufferFramePayload(QuicDataReader* reader);
+
+ // Buffers any remaining frame length field from |reader| into
+ // |length_buffer_|
+ void BufferFrameLength(QuicDataReader* reader);
+
+ // Sets |error_| and |error_detail_| accordingly.
+ void RaiseError(QuicErrorCode error, std::string error_detail);
+
+ // Parses the payload of a PRIORITY frame from |reader| into |frame|.
+ bool ParsePriorityFrame(QuicDataReader* reader, PriorityFrame* frame);
+
+ // Parses the payload of a SETTINGS frame from |reader| into |frame|.
+ bool ParseSettingsFrame(QuicDataReader* reader, SettingsFrame* frame);
+
+ // Returns the max frame size of a given |frame_type|.
+ QuicByteCount MaxFrameLength(uint8_t frame_type);
+
+ // Visitor to invoke when messages are parsed.
+ Visitor* visitor_; // Unowned.
+ // Current state of the parsing.
+ HttpDecoderState state_;
+ // Type of the frame currently being parsed.
+ uint8_t current_frame_type_;
+ // Size of the frame's length field.
+ QuicByteCount current_length_field_size_;
+ // Remaining length that's needed for the frame's length field.
+ QuicByteCount remaining_length_field_length_;
+ // Length of the payload of the frame currently being parsed.
+ QuicByteCount current_frame_length_;
+ // Remaining payload bytes to be parsed.
+ QuicByteCount remaining_frame_length_;
+ // Last error.
+ QuicErrorCode error_;
+ // The issue which caused |error_|
+ std::string error_detail_;
+ // Remaining unparsed data.
+ std::string buffer_;
+ // Remaining unparsed length field data.
+ std::string length_buffer_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_HTTP_DECODER_H_
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
new file mode 100644
index 00000000000..7f0dcd44048
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_decoder_test.cc
@@ -0,0 +1,510 @@
+// 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 "net/third_party/quiche/src/quic/core/http/http_decoder.h"
+
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+using testing::InSequence;
+
+namespace quic {
+
+class MockVisitor : public HttpDecoder::Visitor {
+ public:
+ virtual ~MockVisitor() = default;
+
+ // Called if an error is detected.
+ MOCK_METHOD1(OnError, void(HttpDecoder* decoder));
+
+ MOCK_METHOD1(OnPriorityFrame, void(const PriorityFrame& frame));
+ MOCK_METHOD1(OnCancelPushFrame, void(const CancelPushFrame& frame));
+ MOCK_METHOD1(OnMaxPushIdFrame, void(const MaxPushIdFrame& frame));
+ MOCK_METHOD1(OnGoAwayFrame, void(const GoAwayFrame& frame));
+ MOCK_METHOD1(OnSettingsFrameStart, void(Http3FrameLengths frame_lengths));
+ MOCK_METHOD1(OnSettingsFrame, void(const SettingsFrame& frame));
+ MOCK_METHOD1(OnDuplicatePushFrame, void(const DuplicatePushFrame& frame));
+
+ MOCK_METHOD1(OnDataFrameStart, void(Http3FrameLengths frame_lengths));
+ MOCK_METHOD1(OnDataFramePayload, void(QuicStringPiece payload));
+ MOCK_METHOD0(OnDataFrameEnd, void());
+
+ MOCK_METHOD1(OnHeadersFrameStart, void(Http3FrameLengths frame_lengths));
+ MOCK_METHOD1(OnHeadersFramePayload, void(QuicStringPiece payload));
+ MOCK_METHOD0(OnHeadersFrameEnd, void());
+
+ MOCK_METHOD1(OnPushPromiseFrameStart, void(PushId push_id));
+ MOCK_METHOD1(OnPushPromiseFramePayload, void(QuicStringPiece payload));
+ MOCK_METHOD0(OnPushPromiseFrameEnd, void());
+};
+
+class HttpDecoderTest : public QuicTest {
+ public:
+ HttpDecoderTest() { decoder_.set_visitor(&visitor_); }
+ HttpDecoder decoder_;
+ testing::StrictMock<MockVisitor> visitor_;
+};
+
+TEST_F(HttpDecoderTest, InitialState) {
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, ReservedFramesNoPayload) {
+ for (int n = 0; n < 8; ++n) {
+ const uint8_t type = 0xB + 0x1F * n;
+ char input[] = {// length
+ 0x00,
+ // type
+ type};
+
+ EXPECT_EQ(2u, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))) << n;
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ ASSERT_EQ("", decoder_.error_detail());
+ }
+}
+
+TEST_F(HttpDecoderTest, ReservedFramesSmallPayload) {
+ for (int n = 0; n < 8; ++n) {
+ const uint8_t type = 0xB + 0x1F * n;
+ const uint8_t payload_size = 50;
+ char input[payload_size + 2] = {// length
+ payload_size,
+ // type
+ type};
+
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)))
+ << n;
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ ASSERT_EQ("", decoder_.error_detail());
+ }
+}
+
+TEST_F(HttpDecoderTest, ReservedFramesLargePayload) {
+ for (int n = 0; n < 8; ++n) {
+ const uint8_t type = 0xB + 0x1F * n;
+ const QuicByteCount payload_size = 256;
+ char input[payload_size + 3] = {// length
+ 0x40 + 0x01, 0x00,
+ // type
+ type};
+
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)))
+ << n;
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ ASSERT_EQ("", decoder_.error_detail());
+ }
+}
+
+TEST_F(HttpDecoderTest, CancelPush) {
+ char input[] = {// length
+ 0x1,
+ // type (CANCEL_PUSH)
+ 0x03,
+ // Push Id
+ 0x01};
+
+ // Process the full frame.
+ EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1})));
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnCancelPushFrame(CancelPushFrame({1})));
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, PushPromiseFrame) {
+ char input[] = {// length
+ 0x8,
+ // type (PUSH_PROMISE)
+ 0x05,
+ // Push Id
+ 0x01,
+ // Header Block
+ 'H', 'e', 'a', 'd', 'e', 'r', 's'};
+
+ // Process the full frame.
+ InSequence s;
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("Headers")));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("H")));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e")));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("a")));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("d")));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("e")));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("r")));
+ EXPECT_CALL(visitor_, OnPushPromiseFramePayload(QuicStringPiece("s")));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, MaxPushId) {
+ char input[] = {// length
+ 0x1,
+ // type (MAX_PUSH_ID)
+ 0x0D,
+ // Push Id
+ 0x01};
+
+ // Process the full frame.
+ EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1})));
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnMaxPushIdFrame(MaxPushIdFrame({1})));
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, DuplicatePush) {
+ char input[] = {// length
+ 0x1,
+ // type (DUPLICATE_PUSH)
+ 0x0E,
+ // Push Id
+ 0x01};
+ // Process the full frame.
+ EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({1})));
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnDuplicatePushFrame(DuplicatePushFrame({1})));
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, PriorityFrame) {
+ char input[] = {// length
+ 0x4,
+ // type (PRIORITY)
+ 0x2,
+ // request stream, request stream, exclusive
+ 0x01,
+ // prioritized_element_id
+ 0x03,
+ // element_dependency_id
+ 0x04,
+ // weight
+ 0xFF};
+
+ PriorityFrame frame;
+ frame.prioritized_type = REQUEST_STREAM;
+ frame.dependency_type = REQUEST_STREAM;
+ frame.exclusive = true;
+ frame.prioritized_element_id = 0x03;
+ frame.element_dependency_id = 0x04;
+ frame.weight = 0xFF;
+
+ // Process the full frame.
+ EXPECT_CALL(visitor_, OnPriorityFrame(frame));
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ /*
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnPriorityFrame(frame));
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+ */
+}
+
+TEST_F(HttpDecoderTest, SettingsFrame) {
+ // clang-format off
+ char input[] = {
+ // length
+ 0x06,
+ // type (SETTINGS)
+ 0x04,
+ // identifier (SETTINGS_NUM_PLACEHOLDERS)
+ 0x00,
+ 0x03,
+ // content
+ 0x02,
+ // identifier (SETTINGS_MAX_HEADER_LIST_SIZE)
+ 0x00,
+ 0x06,
+ // content
+ 0x05,
+ };
+ // clang-format on
+
+ SettingsFrame frame;
+ frame.values[3] = 2;
+ frame.values[6] = 5;
+
+ // Process the full frame.
+ EXPECT_CALL(visitor_, OnSettingsFrameStart(Http3FrameLengths(2, 6)));
+ EXPECT_CALL(visitor_, OnSettingsFrame(frame));
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnSettingsFrameStart(Http3FrameLengths(2, 6)));
+ EXPECT_CALL(visitor_, OnSettingsFrame(frame));
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, DataFrame) {
+ char input[] = {// length
+ 0x05,
+ // type (DATA)
+ 0x00,
+ // data
+ 'D', 'a', 't', 'a', '!'};
+
+ // Process the full frame.
+ InSequence s;
+ EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5)));
+ EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("Data!")));
+ EXPECT_CALL(visitor_, OnDataFrameEnd());
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 5)));
+ EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("D")));
+ EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("a")));
+ EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("t")));
+ EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("a")));
+ EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece("!")));
+ EXPECT_CALL(visitor_, OnDataFrameEnd());
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, FrameHeaderPartialDelivery) {
+ // A large input that will occupy more than 1 byte in the length field.
+ std::string input(2048, 'x');
+ HttpEncoder encoder;
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder.SerializeDataFrameHeader(input.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ // Partially send only 1 byte of the header to process.
+ EXPECT_EQ(1u, decoder_.ProcessInput(header.data(), 1));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Send the rest of the header.
+ EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(3, 2048)));
+ EXPECT_EQ(header_length - 1,
+ decoder_.ProcessInput(header.data() + 1, header_length - 1));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Send data.
+ EXPECT_CALL(visitor_, OnDataFramePayload(QuicStringPiece(input)));
+ EXPECT_CALL(visitor_, OnDataFrameEnd());
+ EXPECT_EQ(2048u, decoder_.ProcessInput(input.data(), 2048));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, GoAway) {
+ char input[] = {// length
+ 0x1,
+ // type (GOAWAY)
+ 0x07,
+ // StreamId
+ 0x01};
+
+ // Process the full frame.
+ EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1})));
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnGoAwayFrame(GoAwayFrame({1})));
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, HeadersFrame) {
+ char input[] = {// length
+ 0x07,
+ // type (HEADERS)
+ 0x01,
+ // headers
+ 'H', 'e', 'a', 'd', 'e', 'r', 's'};
+
+ // Process the full frame.
+ InSequence s;
+ EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7)));
+ EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("Headers")));
+ EXPECT_CALL(visitor_, OnHeadersFrameEnd());
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 7)));
+ EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("H")));
+ EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("e")));
+ EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("a")));
+ EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("d")));
+ EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("e")));
+ EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("r")));
+ EXPECT_CALL(visitor_, OnHeadersFramePayload(QuicStringPiece("s")));
+ EXPECT_CALL(visitor_, OnHeadersFrameEnd());
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, EmptyDataFrame) {
+ char input[] = {0x00, // length
+ 0x00}; // type (DATA)
+
+ // Process the full frame.
+ InSequence s;
+ EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 0)));
+ EXPECT_CALL(visitor_, OnDataFrameEnd());
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnDataFrameStart(Http3FrameLengths(2, 0)));
+ EXPECT_CALL(visitor_, OnDataFrameEnd());
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, EmptyHeadersFrame) {
+ char input[] = {0x00, // length
+ 0x01}; // type (HEADERS)
+
+ // Process the full frame.
+ InSequence s;
+ EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 0)));
+ EXPECT_CALL(visitor_, OnHeadersFrameEnd());
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnHeadersFrameStart(Http3FrameLengths(2, 0)));
+ EXPECT_CALL(visitor_, OnHeadersFrameEnd());
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, PushPromiseFrameNoHeaders) {
+ char input[] = {0x01, // length
+ 0x05, // type (PUSH_PROMISE)
+ 0x01}; // Push Id
+
+ // Process the full frame.
+ InSequence s;
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
+ EXPECT_EQ(QUIC_ARRAYSIZE(input),
+ decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+
+ // Process the frame incremently.
+ EXPECT_CALL(visitor_, OnPushPromiseFrameStart(1));
+ EXPECT_CALL(visitor_, OnPushPromiseFrameEnd());
+ for (char c : input) {
+ EXPECT_EQ(1u, decoder_.ProcessInput(&c, 1));
+ }
+ EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+ EXPECT_EQ("", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, MalformedFrameWithOverlyLargePayload) {
+ char input[] = {0x10, // length
+ 0x03, // type (CANCEL_PUSH)
+ 0x15}; // malformed payload
+ // Process the full frame.
+ EXPECT_CALL(visitor_, OnError(&decoder_));
+ EXPECT_EQ(0u, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
+ EXPECT_EQ("Frame is too large", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, MalformedSettingsFrame) {
+ char input[30];
+ QuicDataWriter writer(30, input);
+ // Write length.
+ writer.WriteVarInt62(2048 * 1024);
+ // Write type SETTINGS.
+ writer.WriteUInt8(0x04);
+
+ writer.WriteStringPiece("Malformed payload");
+ EXPECT_CALL(visitor_, OnError(&decoder_));
+ EXPECT_EQ(0u, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input)));
+ EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error());
+ EXPECT_EQ("Frame is too large", decoder_.error_detail());
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..beeef9b6abb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.cc
@@ -0,0 +1,258 @@
+// 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 <string>
+
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Set the first byte of a PRIORITY frame according to its fields.
+uint8_t SetPriorityFields(uint8_t num,
+ PriorityElementType type,
+ bool prioritized) {
+ switch (type) {
+ case REQUEST_STREAM:
+ return num;
+ case PUSH_STREAM:
+ if (prioritized) {
+ return num | (1 << 6);
+ }
+ return num | (1 << 4);
+ case PLACEHOLDER:
+ if (prioritized) {
+ return num | (1 << 7);
+ }
+ return num | (1 << 5);
+ case ROOT_OF_TREE:
+ if (prioritized) {
+ num = num | (1 << 6);
+ return num | (1 << 7);
+ }
+ num = num | (1 << 4);
+ return num | (1 << 5);
+ default:
+ QUIC_NOTREACHED();
+ return num;
+ }
+}
+
+// Length of the type field of a frame.
+static const size_t kFrameTypeLength = 1;
+// Length of the weight field of a priority frame.
+static const size_t kPriorityWeightLength = 1;
+// Length of a priority frame's first byte.
+static const size_t kPriorityFirstByteLength = 1;
+// Length of a key in the map of a settings frame.
+static const size_t kSettingsMapKeyLength = 2;
+
+} // namespace
+
+HttpEncoder::HttpEncoder() {}
+
+HttpEncoder::~HttpEncoder() {}
+
+QuicByteCount HttpEncoder::SerializeDataFrameHeader(
+ QuicByteCount payload_length,
+ std::unique_ptr<char[]>* output) {
+ DCHECK_NE(0u, payload_length);
+ QuicByteCount header_length =
+ QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength;
+
+ output->reset(new char[header_length]);
+ QuicDataWriter writer(header_length, output->get());
+
+ if (WriteFrameHeader(payload_length, HttpFrameType::DATA, &writer)) {
+ return header_length;
+ }
+ return 0;
+}
+
+QuicByteCount HttpEncoder::SerializeHeadersFrameHeader(
+ QuicByteCount payload_length,
+ std::unique_ptr<char[]>* output) {
+ DCHECK_NE(0u, payload_length);
+ QuicByteCount header_length =
+ QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength;
+
+ output->reset(new char[header_length]);
+ QuicDataWriter writer(header_length, output->get());
+
+ if (WriteFrameHeader(payload_length, HttpFrameType::HEADERS, &writer)) {
+ return header_length;
+ }
+ return 0;
+}
+
+QuicByteCount HttpEncoder::SerializePriorityFrame(
+ const PriorityFrame& priority,
+ std::unique_ptr<char[]>* output) {
+ QuicByteCount payload_length =
+ kPriorityFirstByteLength +
+ QuicDataWriter::GetVarInt62Len(priority.prioritized_element_id) +
+ QuicDataWriter::GetVarInt62Len(priority.element_dependency_id) +
+ kPriorityWeightLength;
+ QuicByteCount total_length = GetTotalLength(payload_length);
+
+ output->reset(new char[total_length]);
+ QuicDataWriter writer(total_length, output->get());
+
+ if (!WriteFrameHeader(payload_length, HttpFrameType::PRIORITY, &writer)) {
+ return 0;
+ }
+
+ // Set the first byte of the payload.
+ uint8_t bits = 0;
+ bits = SetPriorityFields(bits, priority.prioritized_type, true);
+ bits = SetPriorityFields(bits, priority.dependency_type, false);
+ if (priority.exclusive) {
+ bits |= 1;
+ }
+
+ if (writer.WriteUInt8(bits) &&
+ writer.WriteVarInt62(priority.prioritized_element_id) &&
+ writer.WriteVarInt62(priority.element_dependency_id) &&
+ writer.WriteUInt8(priority.weight)) {
+ return total_length;
+ }
+ return 0;
+}
+
+QuicByteCount HttpEncoder::SerializeCancelPushFrame(
+ const CancelPushFrame& cancel_push,
+ std::unique_ptr<char[]>* output) {
+ QuicByteCount payload_length =
+ QuicDataWriter::GetVarInt62Len(cancel_push.push_id);
+ QuicByteCount total_length = GetTotalLength(payload_length);
+
+ output->reset(new char[total_length]);
+ QuicDataWriter writer(total_length, output->get());
+
+ if (WriteFrameHeader(payload_length, HttpFrameType::CANCEL_PUSH, &writer) &&
+ writer.WriteVarInt62(cancel_push.push_id)) {
+ return total_length;
+ }
+ return 0;
+}
+
+QuicByteCount HttpEncoder::SerializeSettingsFrame(
+ const SettingsFrame& settings,
+ std::unique_ptr<char[]>* output) {
+ // Calculate the key sizes.
+ QuicByteCount payload_length = settings.values.size() * kSettingsMapKeyLength;
+ // Calculate the value sizes.
+ for (auto it = settings.values.begin(); it != settings.values.end(); ++it) {
+ payload_length += QuicDataWriter::GetVarInt62Len(it->second);
+ }
+
+ QuicByteCount total_length = GetTotalLength(payload_length);
+
+ output->reset(new char[total_length]);
+ QuicDataWriter writer(total_length, output->get());
+
+ if (!WriteFrameHeader(payload_length, HttpFrameType::SETTINGS, &writer)) {
+ return 0;
+ }
+
+ for (auto it = settings.values.begin(); it != settings.values.end(); ++it) {
+ if (!writer.WriteUInt16(it->first) || !writer.WriteVarInt62(it->second)) {
+ return 0;
+ }
+ }
+
+ return total_length;
+}
+
+QuicByteCount HttpEncoder::SerializePushPromiseFrameWithOnlyPushId(
+ const PushPromiseFrame& push_promise,
+ std::unique_ptr<char[]>* output) {
+ QuicByteCount payload_length =
+ QuicDataWriter::GetVarInt62Len(push_promise.push_id) +
+ push_promise.headers.length();
+ // GetTotalLength() is not used because headers will not be serialized.
+ QuicByteCount total_length =
+ QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength +
+ QuicDataWriter::GetVarInt62Len(push_promise.push_id);
+
+ output->reset(new char[total_length]);
+ QuicDataWriter writer(total_length, output->get());
+
+ if (WriteFrameHeader(payload_length, HttpFrameType::PUSH_PROMISE, &writer) &&
+ writer.WriteVarInt62(push_promise.push_id)) {
+ return total_length;
+ }
+ return 0;
+}
+
+QuicByteCount HttpEncoder::SerializeGoAwayFrame(
+ const GoAwayFrame& goaway,
+ std::unique_ptr<char[]>* output) {
+ QuicByteCount payload_length =
+ QuicDataWriter::GetVarInt62Len(goaway.stream_id);
+ QuicByteCount total_length = GetTotalLength(payload_length);
+
+ output->reset(new char[total_length]);
+ QuicDataWriter writer(total_length, output->get());
+
+ if (WriteFrameHeader(payload_length, HttpFrameType::GOAWAY, &writer) &&
+ writer.WriteVarInt62(goaway.stream_id)) {
+ return total_length;
+ }
+ return 0;
+}
+
+QuicByteCount HttpEncoder::SerializeMaxPushIdFrame(
+ const MaxPushIdFrame& max_push_id,
+ std::unique_ptr<char[]>* output) {
+ QuicByteCount payload_length =
+ QuicDataWriter::GetVarInt62Len(max_push_id.push_id);
+ QuicByteCount total_length = GetTotalLength(payload_length);
+
+ output->reset(new char[total_length]);
+ QuicDataWriter writer(total_length, output->get());
+
+ if (WriteFrameHeader(payload_length, HttpFrameType::MAX_PUSH_ID, &writer) &&
+ writer.WriteVarInt62(max_push_id.push_id)) {
+ return total_length;
+ }
+ return 0;
+}
+
+QuicByteCount HttpEncoder::SerializeDuplicatePushFrame(
+ const DuplicatePushFrame& duplicate_push,
+ std::unique_ptr<char[]>* output) {
+ QuicByteCount payload_length =
+ QuicDataWriter::GetVarInt62Len(duplicate_push.push_id);
+ QuicByteCount total_length = GetTotalLength(payload_length);
+
+ output->reset(new char[total_length]);
+ QuicDataWriter writer(total_length, output->get());
+
+ if (WriteFrameHeader(payload_length, HttpFrameType::DUPLICATE_PUSH,
+ &writer) &&
+ writer.WriteVarInt62(duplicate_push.push_id)) {
+ return total_length;
+ }
+ return 0;
+}
+
+bool HttpEncoder::WriteFrameHeader(QuicByteCount length,
+ HttpFrameType type,
+ QuicDataWriter* writer) {
+ return writer->WriteVarInt62(length) &&
+ writer->WriteUInt8(static_cast<uint8_t>(type));
+}
+
+QuicByteCount HttpEncoder::GetTotalLength(QuicByteCount payload_length) {
+ return QuicDataWriter::GetVarInt62Len(payload_length) + kFrameTypeLength +
+ payload_length;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..f04e6e40700
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder.h
@@ -0,0 +1,85 @@
+// 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_CORE_HTTP_HTTP_ENCODER_H_
+#define QUICHE_QUIC_CORE_HTTP_HTTP_ENCODER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/http/http_frames.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicDataWriter;
+
+// A class for encoding the HTTP frames that are exchanged in an HTTP over QUIC
+// session.
+class QUIC_EXPORT_PRIVATE HttpEncoder {
+ public:
+ HttpEncoder();
+
+ ~HttpEncoder();
+
+ // Serializes a DATA frame header into a new buffer stored in |output|.
+ // Returns the length of the buffer on success, or 0 otherwise.
+ QuicByteCount SerializeDataFrameHeader(QuicByteCount payload_length,
+ std::unique_ptr<char[]>* output);
+
+ // Serializes a HEADERS frame header into a new buffer stored in |output|.
+ // Returns the length of the buffer on success, or 0 otherwise.
+ QuicByteCount SerializeHeadersFrameHeader(QuicByteCount payload_length,
+ std::unique_ptr<char[]>* output);
+
+ // Serializes a PRIORITY frame into a new buffer stored in |output|.
+ // Returns the length of the buffer on success, or 0 otherwise.
+ QuicByteCount SerializePriorityFrame(const PriorityFrame& priority,
+ std::unique_ptr<char[]>* output);
+
+ // Serializes a CANCEL_PUSH frame into a new buffer stored in |output|.
+ // Returns the length of the buffer on success, or 0 otherwise.
+ QuicByteCount SerializeCancelPushFrame(const CancelPushFrame& cancel_push,
+ std::unique_ptr<char[]>* output);
+
+ // Serializes a SETTINGS frame into a new buffer stored in |output|.
+ // Returns the length of the buffer on success, or 0 otherwise.
+ QuicByteCount SerializeSettingsFrame(const SettingsFrame& settings,
+ std::unique_ptr<char[]>* output);
+
+ // Serializes the header and push_id of a PUSH_PROMISE frame into a new buffer
+ // stored in |output|. Returns the length of the buffer on success, or 0
+ // otherwise.
+ QuicByteCount SerializePushPromiseFrameWithOnlyPushId(
+ const PushPromiseFrame& push_promise,
+ std::unique_ptr<char[]>* output);
+
+ // Serializes a GOAWAY frame into a new buffer stored in |output|.
+ // Returns the length of the buffer on success, or 0 otherwise.
+ QuicByteCount SerializeGoAwayFrame(const GoAwayFrame& goaway,
+ std::unique_ptr<char[]>* output);
+
+ // Serializes a MAX_PUSH frame into a new buffer stored in |output|.
+ // Returns the length of the buffer on success, or 0 otherwise.
+ QuicByteCount SerializeMaxPushIdFrame(const MaxPushIdFrame& max_push_id,
+ std::unique_ptr<char[]>* output);
+
+ // Serialize a DUPLICATE_PUSH frame into a new buffer stored in |output|.
+ // Returns the length of the buffer on success, or 0 otherwise.
+ QuicByteCount SerializeDuplicatePushFrame(
+ const DuplicatePushFrame& duplicate_push,
+ std::unique_ptr<char[]>* output);
+
+ private:
+ bool WriteFrameHeader(QuicByteCount length,
+ HttpFrameType type,
+ QuicDataWriter* writer);
+
+ QuicByteCount GetTotalLength(QuicByteCount payload_length);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_HTTP_ENCODER_H_
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
new file mode 100644
index 00000000000..154d7688e8d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_encoder_test.cc
@@ -0,0 +1,186 @@
+// 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 <string>
+
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+class HttpEncoderTest : public QuicTest {
+ public:
+ HttpEncoderTest() {}
+ HttpEncoder encoder_;
+};
+
+TEST_F(HttpEncoderTest, SerializeDataFrameHeader) {
+ std::unique_ptr<char[]> buffer;
+ uint64_t length =
+ encoder_.SerializeDataFrameHeader(/* payload_length = */ 5, &buffer);
+ char output[] = {// length
+ 0x05,
+ // type (DATA)
+ 0x00};
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("DATA", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+TEST_F(HttpEncoderTest, SerializeHeadersFrameHeader) {
+ std::unique_ptr<char[]> buffer;
+ uint64_t length =
+ encoder_.SerializeHeadersFrameHeader(/* payload_length = */ 7, &buffer);
+ char output[] = {// length
+ 0x07,
+ // type (HEADERS)
+ 0x01};
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("HEADERS", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+TEST_F(HttpEncoderTest, SerializePriorityFrame) {
+ PriorityFrame priority;
+ priority.prioritized_type = REQUEST_STREAM;
+ priority.dependency_type = REQUEST_STREAM;
+ priority.exclusive = true;
+ priority.prioritized_element_id = 0x03;
+ priority.element_dependency_id = 0x04;
+ priority.weight = 0xFF;
+ char output[] = {// length
+ 0x4,
+ // type (PRIORITY)
+ 0x2,
+ // request stream, request stream, exclusive
+ 0x01,
+ // prioritized_element_id
+ 0x03,
+ // element_dependency_id
+ 0x04,
+ // weight
+ 0xFF};
+
+ std::unique_ptr<char[]> buffer;
+ uint64_t length = encoder_.SerializePriorityFrame(priority, &buffer);
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("PRIORITY", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+TEST_F(HttpEncoderTest, SerializeCancelPushFrame) {
+ CancelPushFrame cancel_push;
+ cancel_push.push_id = 0x01;
+ char output[] = {// length
+ 0x1,
+ // type (CANCEL_PUSH)
+ 0x03,
+ // Push Id
+ 0x01};
+ std::unique_ptr<char[]> buffer;
+ uint64_t length = encoder_.SerializeCancelPushFrame(cancel_push, &buffer);
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("CANCEL_PUSH", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+TEST_F(HttpEncoderTest, SerializeSettingsFrame) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ char output[] = {
+ // length
+ 0x06,
+ // type (SETTINGS)
+ 0x04,
+ // identifier (SETTINGS_NUM_PLACEHOLDERS)
+ 0x00,
+ 0x03,
+ // content
+ 0x02,
+ // identifier (SETTINGS_MAX_HEADER_LIST_SIZE)
+ 0x00,
+ 0x06,
+ // content
+ 0x05,
+ };
+ std::unique_ptr<char[]> buffer;
+ uint64_t length = encoder_.SerializeSettingsFrame(settings, &buffer);
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("SETTINGS", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+TEST_F(HttpEncoderTest, SerializePushPromiseFrameWithOnlyPushId) {
+ PushPromiseFrame push_promise;
+ push_promise.push_id = 0x01;
+ push_promise.headers = "Headers";
+ char output[] = {// length
+ 0x8,
+ // type (PUSH_PROMISE)
+ 0x05,
+ // Push Id
+ 0x01};
+ std::unique_ptr<char[]> buffer;
+ uint64_t length =
+ encoder_.SerializePushPromiseFrameWithOnlyPushId(push_promise, &buffer);
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("PUSH_PROMISE", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+TEST_F(HttpEncoderTest, SerializeGoAwayFrame) {
+ GoAwayFrame goaway;
+ goaway.stream_id = 0x1;
+ char output[] = {// length
+ 0x1,
+ // type (GOAWAY)
+ 0x07,
+ // StreamId
+ 0x01};
+ std::unique_ptr<char[]> buffer;
+ uint64_t length = encoder_.SerializeGoAwayFrame(goaway, &buffer);
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("GOAWAY", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+TEST_F(HttpEncoderTest, SerializeMaxPushIdFrame) {
+ MaxPushIdFrame max_push_id;
+ max_push_id.push_id = 0x1;
+ char output[] = {// length
+ 0x1,
+ // type (MAX_PUSH_ID)
+ 0x0D,
+ // Push Id
+ 0x01};
+ std::unique_ptr<char[]> buffer;
+ uint64_t length = encoder_.SerializeMaxPushIdFrame(max_push_id, &buffer);
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("MAX_PUSH_ID", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+TEST_F(HttpEncoderTest, SerializeDuplicatePushFrame) {
+ DuplicatePushFrame duplicate_push;
+ duplicate_push.push_id = 0x1;
+ char output[] = {// length
+ 0x1,
+ // type (DUPLICATE_PUSH)
+ 0x0E,
+ // Push Id
+ 0x01};
+ std::unique_ptr<char[]> buffer;
+ uint64_t length =
+ encoder_.SerializeDuplicatePushFrame(duplicate_push, &buffer);
+ EXPECT_EQ(QUIC_ARRAYSIZE(output), length);
+ CompareCharArraysWithHexError("DUPLICATE_PUSH", buffer.get(), length, output,
+ QUIC_ARRAYSIZE(output));
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..ea115571fc2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/http_frames.h
@@ -0,0 +1,156 @@
+// 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_CORE_HTTP_HTTP_FRAMES_H_
+#define QUICHE_QUIC_CORE_HTTP_HTTP_FRAMES_H_
+
+#include <map>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+enum class HttpFrameType : uint8_t {
+ DATA = 0x0,
+ HEADERS = 0x1,
+ PRIORITY = 0X2,
+ CANCEL_PUSH = 0X3,
+ SETTINGS = 0x4,
+ PUSH_PROMISE = 0x5,
+ GOAWAY = 0x7,
+ MAX_PUSH_ID = 0xD,
+ DUPLICATE_PUSH = 0xE
+};
+
+// 4.2.1. DATA
+//
+// DATA frames (type=0x0) convey arbitrary, variable-length sequences of
+// octets associated with an HTTP request or response payload.
+struct DataFrame {
+ QuicStringPiece data;
+};
+
+// 4.2.2. HEADERS
+//
+// The HEADERS frame (type=0x1) is used to carry a header block,
+// compressed using QPACK.
+struct HeadersFrame {
+ QuicStringPiece headers;
+};
+
+// 4.2.3. PRIORITY
+//
+// The PRIORITY (type=0x02) frame specifies the sender-advised priority
+// of a stream
+enum PriorityElementType {
+ REQUEST_STREAM = 0,
+ PUSH_STREAM = 1,
+ PLACEHOLDER = 2,
+ ROOT_OF_TREE = 3
+};
+
+struct PriorityFrame {
+ PriorityElementType prioritized_type;
+ PriorityElementType dependency_type;
+ bool exclusive;
+ uint64_t prioritized_element_id;
+ uint64_t element_dependency_id;
+ uint8_t weight;
+
+ bool operator==(const PriorityFrame& rhs) const {
+ return prioritized_type == rhs.prioritized_type &&
+ dependency_type == rhs.dependency_type &&
+ exclusive == rhs.exclusive &&
+ prioritized_element_id == rhs.prioritized_element_id &&
+ element_dependency_id == rhs.element_dependency_id &&
+ weight == rhs.weight;
+ }
+};
+
+// 4.2.4. 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 CancelPushFrame {
+ PushId push_id;
+
+ bool operator==(const CancelPushFrame& rhs) const {
+ return push_id == rhs.push_id;
+ }
+};
+
+// 4.2.5. SETTINGS
+//
+// The SETTINGS frame (type=0x4) conveys configuration parameters that
+// affect how endpoints communicate, such as preferences and constraints
+// on peer behavior
+
+using SettingsId = uint16_t;
+using SettingsMap = std::map<SettingsId, uint64_t>;
+
+struct SettingsFrame {
+ SettingsMap values;
+
+ bool operator==(const SettingsFrame& rhs) const {
+ return values == rhs.values;
+ }
+};
+
+// 4.2.6. PUSH_PROMISE
+//
+// The PUSH_PROMISE frame (type=0x05) is used to carry a request header
+// set from server to client, as in HTTP/2.
+struct PushPromiseFrame {
+ PushId push_id;
+ QuicStringPiece headers;
+
+ bool operator==(const PushPromiseFrame& rhs) const {
+ return push_id == rhs.push_id && headers == rhs.headers;
+ }
+};
+
+// 4.2.7. GOAWAY
+//
+// The GOAWAY frame (type=0x7) is used to initiate graceful shutdown of
+// a connection by a server.
+struct GoAwayFrame {
+ QuicStreamId stream_id;
+
+ bool operator==(const GoAwayFrame& rhs) const {
+ return stream_id == rhs.stream_id;
+ }
+};
+
+// 4.2.8. MAX_PUSH_ID
+//
+// The MAX_PUSH_ID frame (type=0xD) is used by clients to control the
+// number of server pushes that the server can initiate.
+struct MaxPushIdFrame {
+ PushId push_id;
+
+ bool operator==(const MaxPushIdFrame& rhs) const {
+ return push_id == rhs.push_id;
+ }
+};
+
+// 4.2.9. DUPLICATE_PUSH
+//
+// The DUPLICATE_PUSH frame (type=0xE) is used by servers to indicate
+// that an existing pushed resource is related to multiple client
+// requests.
+struct DuplicatePushFrame {
+ PushId push_id;
+
+ bool operator==(const DuplicatePushFrame& rhs) const {
+ return push_id == rhs.push_id;
+ }
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_HTTP_FRAMES_H_
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
new file mode 100644
index 00000000000..8b549692e4c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+QuicClientPromisedInfo::QuicClientPromisedInfo(
+ QuicSpdyClientSessionBase* session,
+ QuicStreamId id,
+ std::string url)
+ : session_(session),
+ id_(id),
+ url_(std::move(url)),
+ client_request_delegate_(nullptr) {}
+
+QuicClientPromisedInfo::~QuicClientPromisedInfo() {}
+
+void QuicClientPromisedInfo::CleanupAlarm::OnAlarm() {
+ QUIC_DVLOG(1) << "self GC alarm for stream " << promised_->id_;
+ promised_->session()->OnPushStreamTimedOut(promised_->id_);
+ promised_->Reset(QUIC_PUSH_STREAM_TIMED_OUT);
+}
+
+void QuicClientPromisedInfo::Init() {
+ cleanup_alarm_.reset(session_->connection()->alarm_factory()->CreateAlarm(
+ new QuicClientPromisedInfo::CleanupAlarm(this)));
+ cleanup_alarm_->Set(
+ session_->connection()->helper()->GetClock()->ApproximateNow() +
+ QuicTime::Delta::FromSeconds(kPushPromiseTimeoutSecs));
+}
+
+bool QuicClientPromisedInfo::OnPromiseHeaders(const SpdyHeaderBlock& headers) {
+ // RFC7540, Section 8.2, requests MUST be safe [RFC7231], Section
+ // 4.2.1. GET and HEAD are the methods that are safe and required.
+ SpdyHeaderBlock::const_iterator it = headers.find(spdy::kHttp2MethodHeader);
+ if (it == headers.end()) {
+ QUIC_DVLOG(1) << "Promise for stream " << id_ << " has no method";
+ Reset(QUIC_INVALID_PROMISE_METHOD);
+ return false;
+ }
+ if (!(it->second == "GET" || it->second == "HEAD")) {
+ QUIC_DVLOG(1) << "Promise for stream " << id_ << " has invalid method "
+ << it->second;
+ Reset(QUIC_INVALID_PROMISE_METHOD);
+ return false;
+ }
+ if (!SpdyUtils::PromisedUrlIsValid(headers)) {
+ QUIC_DVLOG(1) << "Promise for stream " << id_ << " has invalid URL "
+ << url_;
+ Reset(QUIC_INVALID_PROMISE_URL);
+ return false;
+ }
+ if (!session_->IsAuthorized(
+ SpdyUtils::GetPromisedHostNameFromHeaders(headers))) {
+ Reset(QUIC_UNAUTHORIZED_PROMISE_URL);
+ return false;
+ }
+ request_headers_ = headers.Clone();
+ return true;
+}
+
+void QuicClientPromisedInfo::OnResponseHeaders(const SpdyHeaderBlock& headers) {
+ response_headers_ = QuicMakeUnique<SpdyHeaderBlock>(headers.Clone());
+ if (client_request_delegate_) {
+ // We already have a client request waiting.
+ FinalValidation();
+ }
+}
+
+void QuicClientPromisedInfo::Reset(QuicRstStreamErrorCode error_code) {
+ QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_;
+ session_->ResetPromised(id_, error_code);
+ session_->DeletePromised(this);
+ if (delegate) {
+ delegate->OnRendezvousResult(nullptr);
+ }
+}
+
+QuicAsyncStatus QuicClientPromisedInfo::FinalValidation() {
+ if (!client_request_delegate_->CheckVary(
+ client_request_headers_, request_headers_, *response_headers_)) {
+ Reset(QUIC_PROMISE_VARY_MISMATCH);
+ return QUIC_FAILURE;
+ }
+ QuicSpdyStream* stream = session_->GetPromisedStream(id_);
+ if (!stream) {
+ // This shouldn't be possible, as |ClientRequest| guards against
+ // closed stream for the synchronous case. And in the
+ // asynchronous case, a RST can only be caught by |OnAlarm()|.
+ QUIC_BUG << "missing promised stream" << id_;
+ }
+ QuicClientPushPromiseIndex::Delegate* delegate = client_request_delegate_;
+ session_->DeletePromised(this);
+ // Stream can start draining now
+ if (delegate) {
+ delegate->OnRendezvousResult(stream);
+ }
+ return QUIC_SUCCESS;
+}
+
+QuicAsyncStatus QuicClientPromisedInfo::HandleClientRequest(
+ const SpdyHeaderBlock& request_headers,
+ QuicClientPushPromiseIndex::Delegate* delegate) {
+ if (session_->IsClosedStream(id_)) {
+ // There was a RST on the response stream.
+ session_->DeletePromised(this);
+ return QUIC_FAILURE;
+ }
+
+ if (is_validating()) {
+ // The push promise has already been matched to another request though
+ // pending for validation. Returns QUIC_FAILURE to the caller as it couldn't
+ // match a new request any more. This will not affect the validation of the
+ // other request.
+ return QUIC_FAILURE;
+ }
+
+ client_request_delegate_ = delegate;
+ client_request_headers_ = request_headers.Clone();
+ if (response_headers_ == nullptr) {
+ return QUIC_PENDING;
+ }
+ return FinalValidation();
+}
+
+void QuicClientPromisedInfo::Cancel() {
+ // Don't fire OnRendezvousResult() for client initiated cancel.
+ client_request_delegate_ = nullptr;
+ Reset(QUIC_STREAM_CANCELLED);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h
new file mode 100644
index 00000000000..bf614051fc1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h
@@ -0,0 +1,114 @@
+// 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 QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PROMISED_INFO_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PROMISED_INFO_H_
+
+#include <cstddef>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+namespace test {
+class QuicClientPromisedInfoPeer;
+} // namespace test
+
+// QuicClientPromisedInfo tracks the client state of a server push
+// stream from the time a PUSH_PROMISE is received until rendezvous
+// between the promised response and the corresponding client request
+// is complete.
+class QUIC_EXPORT_PRIVATE QuicClientPromisedInfo
+ : public QuicClientPushPromiseIndex::TryHandle {
+ public:
+ // Interface to QuicSpdyClientStream
+ QuicClientPromisedInfo(QuicSpdyClientSessionBase* session,
+ QuicStreamId id,
+ std::string url);
+ QuicClientPromisedInfo(const QuicClientPromisedInfo&) = delete;
+ QuicClientPromisedInfo& operator=(const QuicClientPromisedInfo&) = delete;
+ virtual ~QuicClientPromisedInfo();
+
+ void Init();
+
+ // Validate promise headers etc. Returns true if headers are valid.
+ bool OnPromiseHeaders(const spdy::SpdyHeaderBlock& headers);
+
+ // Store response, possibly proceed with final validation.
+ void OnResponseHeaders(const spdy::SpdyHeaderBlock& headers);
+
+ // Rendezvous between this promised stream and a client request that
+ // has a matching URL.
+ virtual QuicAsyncStatus HandleClientRequest(
+ const spdy::SpdyHeaderBlock& headers,
+ QuicClientPushPromiseIndex::Delegate* delegate);
+
+ void Cancel() override;
+
+ void Reset(QuicRstStreamErrorCode error_code);
+
+ // Client requests are initially associated to promises by matching
+ // URL in the client request against the URL in the promise headers,
+ // uing the |promised_by_url| map. The push can be cross-origin, so
+ // the client should validate that the session is authoritative for
+ // the promised URL. If not, it should call |RejectUnauthorized|.
+ QuicSpdyClientSessionBase* session() { return session_; }
+
+ // If the promised response contains Vary header, then the fields
+ // specified by Vary must match between the client request header
+ // and the promise headers (see https://crbug.com//554220). Vary
+ // validation requires the response headers (for the actual Vary
+ // field list), the promise headers (taking the role of the "cached"
+ // request), and the client request headers.
+ spdy::SpdyHeaderBlock* request_headers() { return &request_headers_; }
+
+ spdy::SpdyHeaderBlock* response_headers() { return response_headers_.get(); }
+
+ // After validation, client will use this to access the pushed stream.
+
+ QuicStreamId id() const { return id_; }
+
+ const std::string url() const { return url_; }
+
+ // Return true if there's a request pending matching this push promise.
+ bool is_validating() const { return client_request_delegate_ != nullptr; }
+
+ private:
+ friend class test::QuicClientPromisedInfoPeer;
+
+ class CleanupAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit CleanupAlarm(QuicClientPromisedInfo* promised)
+ : promised_(promised) {}
+
+ void OnAlarm() override;
+
+ QuicClientPromisedInfo* promised_;
+ };
+
+ QuicAsyncStatus FinalValidation();
+
+ QuicSpdyClientSessionBase* session_;
+ QuicStreamId id_;
+ std::string url_;
+ spdy::SpdyHeaderBlock request_headers_;
+ std::unique_ptr<spdy::SpdyHeaderBlock> response_headers_;
+ spdy::SpdyHeaderBlock client_request_headers_;
+ QuicClientPushPromiseIndex::Delegate* client_request_delegate_;
+
+ // The promise will commit suicide eventually if it is not claimed by a GET
+ // first.
+ std::unique_ptr<QuicAlarm> cleanup_alarm_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PROMISED_INFO_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info_test.cc
new file mode 100644
index 00000000000..104c049e87e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_promised_info_test.cc
@@ -0,0 +1,354 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using spdy::SpdyHeaderBlock;
+using testing::_;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockQuicSpdyClientSession : public QuicSpdyClientSession {
+ public:
+ explicit MockQuicSpdyClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicClientPushPromiseIndex* push_promise_index)
+ : QuicSpdyClientSession(DefaultQuicConfig(),
+ supported_versions,
+ connection,
+ QuicServerId("example.com", 443, false),
+ &crypto_config_,
+ push_promise_index),
+ crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx()),
+ authorized_(true) {}
+ MockQuicSpdyClientSession(const MockQuicSpdyClientSession&) = delete;
+ MockQuicSpdyClientSession& operator=(const MockQuicSpdyClientSession&) =
+ delete;
+ ~MockQuicSpdyClientSession() override {}
+
+ bool IsAuthorized(const std::string& authority) override {
+ return authorized_;
+ }
+
+ void set_authorized(bool authorized) { authorized_ = authorized; }
+
+ MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id));
+
+ private:
+ QuicCryptoClientConfig crypto_config_;
+
+ bool authorized_;
+};
+
+class QuicClientPromisedInfoTest : public QuicTest {
+ public:
+ class StreamVisitor;
+
+ QuicClientPromisedInfoTest()
+ : connection_(new StrictMock<MockQuicConnection>(&helper_,
+ &alarm_factory_,
+ Perspective::IS_CLIENT)),
+ session_(connection_->supported_versions(),
+ connection_,
+ &push_promise_index_),
+ body_("hello world"),
+ promise_id_(
+ QuicUtils::GetInvalidStreamId(connection_->transport_version())) {
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ session_.Initialize();
+
+ headers_[":status"] = "200";
+ headers_["content-length"] = "11";
+
+ stream_ = QuicMakeUnique<QuicSpdyClientStream>(
+ GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0),
+ &session_, BIDIRECTIONAL);
+ stream_visitor_ = QuicMakeUnique<StreamVisitor>();
+ stream_->set_visitor(stream_visitor_.get());
+
+ push_promise_[":path"] = "/bar";
+ push_promise_[":authority"] = "www.google.com";
+ push_promise_[":version"] = "HTTP/1.1";
+ push_promise_[":method"] = "GET";
+ push_promise_[":scheme"] = "https";
+
+ promise_url_ = SpdyUtils::GetPromisedUrlFromHeaders(push_promise_);
+
+ client_request_ = push_promise_.Clone();
+ promise_id_ = GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), 0);
+ }
+
+ class StreamVisitor : public QuicSpdyClientStream::Visitor {
+ void OnClose(QuicSpdyStream* stream) override {
+ QUIC_DVLOG(1) << "stream " << stream->id();
+ }
+ };
+
+ void ReceivePromise(QuicStreamId id) {
+ auto headers = AsHeaderList(push_promise_);
+ stream_->OnPromiseHeaderList(id, headers.uncompressed_header_bytes(),
+ headers);
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ QuicClientPushPromiseIndex push_promise_index_;
+
+ MockQuicSpdyClientSession session_;
+ std::unique_ptr<QuicSpdyClientStream> stream_;
+ std::unique_ptr<StreamVisitor> stream_visitor_;
+ std::unique_ptr<QuicSpdyClientStream> promised_stream_;
+ SpdyHeaderBlock headers_;
+ std::string body_;
+ SpdyHeaderBlock push_promise_;
+ QuicStreamId promise_id_;
+ std::string promise_url_;
+ SpdyHeaderBlock client_request_;
+};
+
+TEST_F(QuicClientPromisedInfoTest, PushPromise) {
+ ReceivePromise(promise_id_);
+
+ // Verify that the promise is in the unclaimed streams map.
+ EXPECT_NE(session_.GetPromisedById(promise_id_), nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseCleanupAlarm) {
+ ReceivePromise(promise_id_);
+
+ // Verify that the promise is in the unclaimed streams map.
+ QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_);
+ ASSERT_NE(promised, nullptr);
+
+ // Fire the alarm that will cancel the promised stream.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promise_id_, QUIC_PUSH_STREAM_TIMED_OUT));
+ alarm_factory_.FireAlarm(QuicClientPromisedInfoPeer::GetAlarm(promised));
+
+ // Verify that the promise is gone after the alarm fires.
+ EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr);
+ EXPECT_EQ(session_.GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseInvalidMethod) {
+ // Promise with an unsafe method
+ push_promise_[":method"] = "PUT";
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promise_id_, QUIC_INVALID_PROMISE_METHOD));
+ ReceivePromise(promise_id_);
+
+ // Verify that the promise headers were ignored
+ EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr);
+ EXPECT_EQ(session_.GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseMissingMethod) {
+ // Promise with a missing method
+ push_promise_.erase(":method");
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promise_id_, QUIC_INVALID_PROMISE_METHOD));
+ ReceivePromise(promise_id_);
+
+ // Verify that the promise headers were ignored
+ EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr);
+ EXPECT_EQ(session_.GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseInvalidUrl) {
+ // Remove required header field to make URL invalid
+ push_promise_.erase(":authority");
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promise_id_, QUIC_INVALID_PROMISE_URL));
+ ReceivePromise(promise_id_);
+
+ // Verify that the promise headers were ignored
+ EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr);
+ EXPECT_EQ(session_.GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseUnauthorizedUrl) {
+ session_.set_authorized(false);
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promise_id_, QUIC_UNAUTHORIZED_PROMISE_URL));
+
+ ReceivePromise(promise_id_);
+
+ QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_);
+ ASSERT_EQ(promised, nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseMismatch) {
+ ReceivePromise(promise_id_);
+
+ QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_);
+ ASSERT_NE(promised, nullptr);
+
+ // Need to send the promised response headers and initiate the
+ // rendezvous for secondary validation to proceed.
+ QuicSpdyClientStream* promise_stream = static_cast<QuicSpdyClientStream*>(
+ session_.GetOrCreateStream(promise_id_));
+ auto headers = AsHeaderList(headers_);
+ promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+
+ TestPushPromiseDelegate delegate(/*match=*/false);
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promise_id_, QUIC_PROMISE_VARY_MISMATCH));
+ EXPECT_CALL(session_, CloseStream(promise_id_));
+
+ promised->HandleClientRequest(client_request_, &delegate);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseVaryWaits) {
+ ReceivePromise(promise_id_);
+
+ QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_);
+ EXPECT_FALSE(promised->is_validating());
+ ASSERT_NE(promised, nullptr);
+
+ // Now initiate rendezvous.
+ TestPushPromiseDelegate delegate(/*match=*/true);
+ promised->HandleClientRequest(client_request_, &delegate);
+ EXPECT_TRUE(promised->is_validating());
+
+ // Promise is still there, waiting for response.
+ EXPECT_NE(session_.GetPromisedById(promise_id_), nullptr);
+
+ // Send Response, should trigger promise validation and complete rendezvous
+ QuicSpdyClientStream* promise_stream = static_cast<QuicSpdyClientStream*>(
+ session_.GetOrCreateStream(promise_id_));
+ ASSERT_NE(promise_stream, nullptr);
+ auto headers = AsHeaderList(headers_);
+ promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+
+ // Promise is gone
+ EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseVaryNoWait) {
+ ReceivePromise(promise_id_);
+
+ QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_);
+ ASSERT_NE(promised, nullptr);
+
+ QuicSpdyClientStream* promise_stream = static_cast<QuicSpdyClientStream*>(
+ session_.GetOrCreateStream(promise_id_));
+ ASSERT_NE(promise_stream, nullptr);
+
+ // Send Response, should trigger promise validation and complete rendezvous
+ auto headers = AsHeaderList(headers_);
+ promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+
+ // Now initiate rendezvous.
+ TestPushPromiseDelegate delegate(/*match=*/true);
+ promised->HandleClientRequest(client_request_, &delegate);
+
+ // Promise is gone
+ EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr);
+ // Have a push stream
+ EXPECT_TRUE(delegate.rendezvous_fired());
+
+ EXPECT_NE(delegate.rendezvous_stream(), nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseWaitCancels) {
+ ReceivePromise(promise_id_);
+
+ QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_);
+ ASSERT_NE(promised, nullptr);
+
+ // Now initiate rendezvous.
+ TestPushPromiseDelegate delegate(/*match=*/true);
+ promised->HandleClientRequest(client_request_, &delegate);
+
+ // Promise is still there, waiting for response.
+ EXPECT_NE(session_.GetPromisedById(promise_id_), nullptr);
+
+ // Create response stream, but no data yet.
+ session_.GetOrCreateStream(promise_id_);
+
+ // Cancel the promised stream.
+ EXPECT_CALL(session_, CloseStream(promise_id_));
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(promise_id_, QUIC_STREAM_CANCELLED));
+ promised->Cancel();
+
+ // Promise is gone
+ EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr);
+}
+
+TEST_F(QuicClientPromisedInfoTest, PushPromiseDataClosed) {
+ ReceivePromise(promise_id_);
+
+ QuicClientPromisedInfo* promised = session_.GetPromisedById(promise_id_);
+ ASSERT_NE(promised, nullptr);
+
+ QuicSpdyClientStream* promise_stream = static_cast<QuicSpdyClientStream*>(
+ session_.GetOrCreateStream(promise_id_));
+ ASSERT_NE(promise_stream, nullptr);
+
+ // Send response, rendezvous will be able to finish synchronously.
+ auto headers = AsHeaderList(headers_);
+ promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+
+ EXPECT_CALL(session_, CloseStream(promise_id_));
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promise_id_, QUIC_STREAM_PEER_GOING_AWAY));
+ session_.SendRstStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0);
+
+ // Now initiate rendezvous.
+ TestPushPromiseDelegate delegate(/*match=*/true);
+ EXPECT_EQ(promised->HandleClientRequest(client_request_, &delegate),
+ QUIC_FAILURE);
+
+ // Got an indication of the stream failure, client should retry
+ // request.
+ EXPECT_FALSE(delegate.rendezvous_fired());
+ EXPECT_EQ(delegate.rendezvous_stream(), nullptr);
+
+ // Promise is gone
+ EXPECT_EQ(session_.GetPromisedById(promise_id_), nullptr);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.cc
new file mode 100644
index 00000000000..0f11af40ba3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.cc
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+QuicClientPushPromiseIndex::QuicClientPushPromiseIndex() {}
+
+QuicClientPushPromiseIndex::~QuicClientPushPromiseIndex() {}
+
+QuicClientPushPromiseIndex::TryHandle::~TryHandle() {}
+
+QuicClientPromisedInfo* QuicClientPushPromiseIndex::GetPromised(
+ const std::string& url) {
+ auto it = promised_by_url_.find(url);
+ if (it == promised_by_url_.end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+
+QuicAsyncStatus QuicClientPushPromiseIndex::Try(
+ const spdy::SpdyHeaderBlock& request,
+ QuicClientPushPromiseIndex::Delegate* delegate,
+ TryHandle** handle) {
+ std::string url(SpdyUtils::GetPromisedUrlFromHeaders(request));
+ auto it = promised_by_url_.find(url);
+ if (it != promised_by_url_.end()) {
+ QuicClientPromisedInfo* promised = it->second;
+ QuicAsyncStatus rv = promised->HandleClientRequest(request, delegate);
+ if (rv == QUIC_PENDING) {
+ *handle = promised;
+ }
+ return rv;
+ }
+ return QUIC_FAILURE;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h
new file mode 100644
index 00000000000..10e83c81eae
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PUSH_PROMISE_INDEX_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PUSH_PROMISE_INDEX_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// QuicClientPushPromiseIndex is the interface to support rendezvous
+// between client requests and resources delivered via server push.
+// The same index can be shared across multiple sessions (e.g. for the
+// same browser users profile), since cross-origin pushes are allowed
+// (subject to authority constraints).
+
+class QUIC_EXPORT_PRIVATE QuicClientPushPromiseIndex {
+ public:
+ // Delegate is used to complete the rendezvous that began with
+ // |Try()|.
+ class QUIC_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // The primary lookup matched request with push promise by URL. A
+ // secondary match is necessary to ensure Vary (RFC 2616, 14.14)
+ // is honored. If Vary is not present, return true. If Vary is
+ // present, return whether designated header fields of
+ // |promise_request| and |client_request| match.
+ virtual bool CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) = 0;
+
+ // On rendezvous success, provides the promised |stream|. Callee
+ // does not inherit ownership of |stream|. On rendezvous failure,
+ // |stream| is |nullptr| and the client should retry the request.
+ // Rendezvous can fail due to promise validation failure or RST on
+ // promised stream. |url| will have been removed from the index
+ // before |OnRendezvousResult()| is invoked, so a recursive call to
+ // |Try()| will return |QUIC_FAILURE|, which may be convenient for
+ // retry purposes.
+ virtual void OnRendezvousResult(QuicSpdyStream* stream) = 0;
+ };
+
+ class QUIC_EXPORT_PRIVATE TryHandle {
+ public:
+ // Cancel the request.
+ virtual void Cancel() = 0;
+
+ protected:
+ TryHandle() {}
+ TryHandle(const TryHandle&) = delete;
+ TryHandle& operator=(const TryHandle&) = delete;
+ ~TryHandle();
+ };
+
+ QuicClientPushPromiseIndex();
+ QuicClientPushPromiseIndex(const QuicClientPushPromiseIndex&) = delete;
+ QuicClientPushPromiseIndex& operator=(const QuicClientPushPromiseIndex&) =
+ delete;
+ virtual ~QuicClientPushPromiseIndex();
+
+ // Called by client code, used to enforce affinity between requests
+ // for promised streams and the session the promise came from.
+ QuicClientPromisedInfo* GetPromised(const std::string& url);
+
+ // Called by client code, to initiate rendezvous between a request
+ // and a server push stream. If |request|'s url is in the index,
+ // rendezvous will be attempted and may complete immediately or
+ // asynchronously. If the matching promise and response headers
+ // have already arrived, the delegate's methods will fire
+ // recursively from within |Try()|. Returns |QUIC_SUCCESS| if the
+ // rendezvous was a success. Returns |QUIC_FAILURE| if there was no
+ // matching promise, or if there was but the rendezvous has failed.
+ // Returns QUIC_PENDING if a matching promise was found, but the
+ // rendezvous needs to complete asynchronously because the promised
+ // response headers are not yet available. If result is
+ // QUIC_PENDING, then |*handle| will set so that the caller may
+ // cancel the request if need be. The caller does not inherit
+ // ownership of |*handle|, and it ceases to be valid if the caller
+ // invokes |handle->Cancel()| or if |delegate->OnReponse()| fires.
+ QuicAsyncStatus Try(const spdy::SpdyHeaderBlock& request,
+ Delegate* delegate,
+ TryHandle** handle);
+
+ QuicPromisedByUrlMap* promised_by_url() { return &promised_by_url_; }
+
+ private:
+ QuicPromisedByUrlMap promised_by_url_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_CLIENT_PUSH_PROMISE_INDEX_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index_test.cc
new file mode 100644
index 00000000000..c12a3285af8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index_test.cc
@@ -0,0 +1,118 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockQuicSpdyClientSession : public QuicSpdyClientSession {
+ public:
+ explicit MockQuicSpdyClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicClientPushPromiseIndex* push_promise_index)
+ : QuicSpdyClientSession(DefaultQuicConfig(),
+ supported_versions,
+ connection,
+ QuicServerId("example.com", 443, false),
+ &crypto_config_,
+ push_promise_index),
+ crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx()) {}
+ MockQuicSpdyClientSession(const MockQuicSpdyClientSession&) = delete;
+ MockQuicSpdyClientSession& operator=(const MockQuicSpdyClientSession&) =
+ delete;
+ ~MockQuicSpdyClientSession() override {}
+
+ MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id));
+
+ private:
+ QuicCryptoClientConfig crypto_config_;
+};
+
+class QuicClientPushPromiseIndexTest : public QuicTest {
+ public:
+ QuicClientPushPromiseIndexTest()
+ : connection_(new StrictMock<MockQuicConnection>(&helper_,
+ &alarm_factory_,
+ Perspective::IS_CLIENT)),
+ session_(connection_->supported_versions(), connection_, &index_),
+ promised_(&session_,
+ GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(),
+ 0),
+ url_) {
+ request_[":path"] = "/bar";
+ request_[":authority"] = "www.google.com";
+ request_[":version"] = "HTTP/1.1";
+ request_[":method"] = "GET";
+ request_[":scheme"] = "https";
+ url_ = SpdyUtils::GetPromisedUrlFromHeaders(request_);
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ MockQuicSpdyClientSession session_;
+ QuicClientPushPromiseIndex index_;
+ spdy::SpdyHeaderBlock request_;
+ std::string url_;
+ MockQuicClientPromisedInfo promised_;
+ QuicClientPushPromiseIndex::TryHandle* handle_;
+};
+
+TEST_F(QuicClientPushPromiseIndexTest, TryRequestSuccess) {
+ (*index_.promised_by_url())[url_] = &promised_;
+ EXPECT_CALL(promised_, HandleClientRequest(_, _))
+ .WillOnce(Return(QUIC_SUCCESS));
+ EXPECT_EQ(index_.Try(request_, nullptr, &handle_), QUIC_SUCCESS);
+}
+
+TEST_F(QuicClientPushPromiseIndexTest, TryRequestPending) {
+ (*index_.promised_by_url())[url_] = &promised_;
+ EXPECT_CALL(promised_, HandleClientRequest(_, _))
+ .WillOnce(Return(QUIC_PENDING));
+ EXPECT_EQ(index_.Try(request_, nullptr, &handle_), QUIC_PENDING);
+}
+
+TEST_F(QuicClientPushPromiseIndexTest, TryRequestFailure) {
+ (*index_.promised_by_url())[url_] = &promised_;
+ EXPECT_CALL(promised_, HandleClientRequest(_, _))
+ .WillOnce(Return(QUIC_FAILURE));
+ EXPECT_EQ(index_.Try(request_, nullptr, &handle_), QUIC_FAILURE);
+}
+
+TEST_F(QuicClientPushPromiseIndexTest, TryNoPromise) {
+ EXPECT_EQ(index_.Try(request_, nullptr, &handle_), QUIC_FAILURE);
+}
+
+TEST_F(QuicClientPushPromiseIndexTest, GetNoPromise) {
+ EXPECT_EQ(index_.GetPromised(url_), nullptr);
+}
+
+TEST_F(QuicClientPushPromiseIndexTest, GetPromise) {
+ (*index_.promised_by_url())[url_] = &promised_;
+ EXPECT_EQ(index_.GetPromised(url_), &promised_);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.cc
new file mode 100644
index 00000000000..f9b730bcb4c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+namespace quic {
+
+QuicHeaderList::QuicHeaderList()
+ : max_header_list_size_(kDefaultMaxUncompressedHeaderSize),
+ current_header_list_size_(0),
+ uncompressed_header_bytes_(0),
+ compressed_header_bytes_(0) {}
+
+QuicHeaderList::QuicHeaderList(QuicHeaderList&& other) = default;
+
+QuicHeaderList::QuicHeaderList(const QuicHeaderList& other) = default;
+
+QuicHeaderList& QuicHeaderList::operator=(const QuicHeaderList& other) =
+ default;
+
+QuicHeaderList& QuicHeaderList::operator=(QuicHeaderList&& other) = default;
+
+QuicHeaderList::~QuicHeaderList() {}
+
+void QuicHeaderList::OnHeaderBlockStart() {
+ QUIC_BUG_IF(current_header_list_size_ != 0)
+ << "OnHeaderBlockStart called more than once!";
+}
+
+void QuicHeaderList::OnHeader(QuicStringPiece name, QuicStringPiece value) {
+ // Avoid infinite buffering of headers. No longer store headers
+ // once the current headers are over the limit.
+ if (current_header_list_size_ < max_header_list_size_) {
+ current_header_list_size_ += name.size();
+ current_header_list_size_ += value.size();
+ current_header_list_size_ += spdy::kPerHeaderOverhead;
+ header_list_.emplace_back(std::string(name), std::string(value));
+ }
+}
+
+void QuicHeaderList::OnHeaderBlockEnd(size_t uncompressed_header_bytes,
+ size_t compressed_header_bytes) {
+ uncompressed_header_bytes_ = uncompressed_header_bytes;
+ compressed_header_bytes_ = compressed_header_bytes;
+ if (current_header_list_size_ > max_header_list_size_) {
+ Clear();
+ }
+}
+
+void QuicHeaderList::Clear() {
+ header_list_.clear();
+ current_header_list_size_ = 0;
+ uncompressed_header_bytes_ = 0;
+ compressed_header_bytes_ = 0;
+}
+
+std::string QuicHeaderList::DebugString() const {
+ std::string s = "{ ";
+ for (const auto& p : *this) {
+ s.append(p.first + "=" + p.second + ", ");
+ }
+ s.append("}");
+ return s;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.h
new file mode 100644
index 00000000000..cc6e7bc210d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list.h
@@ -0,0 +1,89 @@
+// 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 QUICHE_QUIC_CORE_HTTP_QUIC_HEADER_LIST_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_HEADER_LIST_H_
+
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_headers_handler_interface.h"
+
+namespace quic {
+
+// A simple class that accumulates header pairs
+class QUIC_EXPORT_PRIVATE QuicHeaderList
+ : public spdy::SpdyHeadersHandlerInterface {
+ public:
+ using ListType = QuicDeque<std::pair<std::string, std::string>>;
+ using value_type = ListType::value_type;
+ using const_iterator = ListType::const_iterator;
+
+ QuicHeaderList();
+ QuicHeaderList(QuicHeaderList&& other);
+ QuicHeaderList(const QuicHeaderList& other);
+ QuicHeaderList& operator=(QuicHeaderList&& other);
+ QuicHeaderList& operator=(const QuicHeaderList& other);
+ ~QuicHeaderList() override;
+
+ // From SpdyHeadersHandlerInteface.
+ void OnHeaderBlockStart() override;
+ void OnHeader(QuicStringPiece name, QuicStringPiece value) override;
+ void OnHeaderBlockEnd(size_t uncompressed_header_bytes,
+ size_t compressed_header_bytes) override;
+
+ void Clear();
+
+ const_iterator begin() const { return header_list_.begin(); }
+ const_iterator end() const { return header_list_.end(); }
+
+ bool empty() const { return header_list_.empty(); }
+ size_t uncompressed_header_bytes() const {
+ return uncompressed_header_bytes_;
+ }
+ size_t compressed_header_bytes() const { return compressed_header_bytes_; }
+
+ void set_max_header_list_size(size_t max_header_list_size) {
+ max_header_list_size_ = max_header_list_size;
+ }
+
+ size_t max_header_list_size() const { return max_header_list_size_; }
+
+ std::string DebugString() const;
+
+ private:
+ QuicDeque<std::pair<std::string, std::string>> header_list_;
+
+ // The limit on the size of the header list (defined by spec as name + value +
+ // overhead for each header field). Headers over this limit will not be
+ // buffered, and the list will be cleared upon OnHeaderBlockEnd.
+ size_t max_header_list_size_;
+
+ // Defined per the spec as the size of all header fields with an additional
+ // overhead for each field.
+ size_t current_header_list_size_;
+
+ // TODO(dahollings) Are these fields necessary?
+ size_t uncompressed_header_bytes_;
+ size_t compressed_header_bytes_;
+};
+
+inline bool operator==(const QuicHeaderList& l1, const QuicHeaderList& l2) {
+ auto pred = [](const std::pair<std::string, std::string>& p1,
+ const std::pair<std::string, std::string>& p2) {
+ return p1.first == p2.first && p1.second == p2.second;
+ };
+ return std::equal(l1.begin(), l1.end(), l2.begin(), pred);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_HEADER_LIST_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list_test.cc
new file mode 100644
index 00000000000..67bd35f2644
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_header_list_test.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+using ::testing::ElementsAre;
+using ::testing::Pair;
+
+namespace quic {
+
+class QuicHeaderListTest : public QuicTest {};
+
+// This test verifies that QuicHeaderList accumulates header pairs in order.
+TEST_F(QuicHeaderListTest, OnHeader) {
+ QuicHeaderList headers;
+ headers.OnHeader("foo", "bar");
+ headers.OnHeader("april", "fools");
+ headers.OnHeader("beep", "");
+
+ EXPECT_THAT(headers, ElementsAre(Pair("foo", "bar"), Pair("april", "fools"),
+ Pair("beep", "")));
+}
+
+TEST_F(QuicHeaderListTest, DebugString) {
+ QuicHeaderList headers;
+ headers.OnHeader("foo", "bar");
+ headers.OnHeader("april", "fools");
+ headers.OnHeader("beep", "");
+
+ EXPECT_EQ("{ foo=bar, april=fools, beep=, }", headers.DebugString());
+}
+
+TEST_F(QuicHeaderListTest, TooLarge) {
+ QuicHeaderList headers;
+ std::string key = "key";
+ std::string value(1 << 18, '1');
+ // Send a header that exceeds max_header_list_size.
+ headers.OnHeader(key, value);
+ // Send a second header exceeding max_header_list_size.
+ headers.OnHeader(key + "2", value);
+ // We should not allocate more memory after exceeding max_header_list_size.
+ EXPECT_LT(headers.DebugString().size(), 2 * value.size());
+ size_t total_bytes = 2 * (key.size() + value.size()) + 1;
+ headers.OnHeaderBlockEnd(total_bytes, total_bytes);
+ EXPECT_TRUE(headers.empty());
+
+ EXPECT_EQ("{ }", headers.DebugString());
+}
+
+TEST_F(QuicHeaderListTest, NotTooLarge) {
+ QuicHeaderList headers;
+ headers.set_max_header_list_size(1 << 20);
+ std::string key = "key";
+ std::string value(1 << 18, '1');
+ headers.OnHeader(key, value);
+ size_t total_bytes = key.size() + value.size();
+ headers.OnHeaderBlockEnd(total_bytes, total_bytes);
+ EXPECT_FALSE(headers.empty());
+}
+
+// This test verifies that QuicHeaderList is copyable and assignable.
+TEST_F(QuicHeaderListTest, IsCopyableAndAssignable) {
+ QuicHeaderList headers;
+ headers.OnHeader("foo", "bar");
+ headers.OnHeader("april", "fools");
+ headers.OnHeader("beep", "");
+
+ QuicHeaderList headers2(headers);
+ QuicHeaderList headers3 = headers;
+
+ EXPECT_THAT(headers2, ElementsAre(Pair("foo", "bar"), Pair("april", "fools"),
+ Pair("beep", "")));
+ EXPECT_THAT(headers3, ElementsAre(Pair("foo", "bar"), Pair("april", "fools"),
+ Pair("beep", "")));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.cc
new file mode 100644
index 00000000000..9c99ab7e41e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.cc
@@ -0,0 +1,157 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+QuicHeadersStream::CompressedHeaderInfo::CompressedHeaderInfo(
+ QuicStreamOffset headers_stream_offset,
+ QuicStreamOffset full_length,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener)
+ : headers_stream_offset(headers_stream_offset),
+ full_length(full_length),
+ unacked_length(full_length),
+ ack_listener(std::move(ack_listener)) {}
+
+QuicHeadersStream::CompressedHeaderInfo::CompressedHeaderInfo(
+ const CompressedHeaderInfo& other) = default;
+
+QuicHeadersStream::CompressedHeaderInfo::~CompressedHeaderInfo() {}
+
+QuicHeadersStream::QuicHeadersStream(QuicSpdySession* session)
+ : QuicStream(QuicUtils::GetHeadersStreamId(
+ session->connection()->transport_version()),
+ session,
+ /*is_static=*/true,
+ BIDIRECTIONAL),
+ spdy_session_(session) {
+ // The headers stream is exempt from connection level flow control.
+ DisableConnectionFlowControlForThisStream();
+}
+
+QuicHeadersStream::~QuicHeadersStream() {}
+
+void QuicHeadersStream::OnDataAvailable() {
+ struct iovec iov;
+ while (sequencer()->GetReadableRegion(&iov)) {
+ if (spdy_session_->ProcessHeaderData(iov) != iov.iov_len) {
+ // Error processing data.
+ return;
+ }
+ sequencer()->MarkConsumed(iov.iov_len);
+ MaybeReleaseSequencerBuffer();
+ }
+}
+
+void QuicHeadersStream::MaybeReleaseSequencerBuffer() {
+ if (spdy_session_->ShouldReleaseHeadersStreamSequencerBuffer()) {
+ sequencer()->ReleaseBufferIfEmpty();
+ }
+}
+
+bool QuicHeadersStream::OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) {
+ QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+ newly_acked.Difference(bytes_acked());
+ for (const auto& acked : newly_acked) {
+ QuicStreamOffset acked_offset = acked.min();
+ QuicByteCount acked_length = acked.max() - acked.min();
+ for (CompressedHeaderInfo& header : unacked_headers_) {
+ if (acked_offset < header.headers_stream_offset) {
+ // This header frame offset belongs to headers with smaller offset, stop
+ // processing.
+ break;
+ }
+
+ if (acked_offset >= header.headers_stream_offset + header.full_length) {
+ // This header frame belongs to headers with larger offset.
+ continue;
+ }
+
+ QuicByteCount header_offset = acked_offset - header.headers_stream_offset;
+ QuicByteCount header_length =
+ std::min(acked_length, header.full_length - header_offset);
+
+ if (header.unacked_length < header_length) {
+ QUIC_BUG << "Unsent stream data is acked. unacked_length: "
+ << header.unacked_length << " acked_length: " << header_length;
+ CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+ "Unsent stream data is acked");
+ return false;
+ }
+ if (header.ack_listener != nullptr && header_length > 0) {
+ header.ack_listener->OnPacketAcked(header_length, ack_delay_time);
+ }
+ header.unacked_length -= header_length;
+ acked_offset += header_length;
+ acked_length -= header_length;
+ }
+ }
+ // Remove headers which are fully acked. Please note, header frames can be
+ // acked out of order, but unacked_headers_ is cleaned up in order.
+ while (!unacked_headers_.empty() &&
+ unacked_headers_.front().unacked_length == 0) {
+ unacked_headers_.pop_front();
+ }
+ return QuicStream::OnStreamFrameAcked(offset, data_length, fin_acked,
+ ack_delay_time, newly_acked_length);
+}
+
+void QuicHeadersStream::OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool /*fin_retransmitted*/) {
+ QuicStream::OnStreamFrameRetransmitted(offset, data_length, false);
+ for (CompressedHeaderInfo& header : unacked_headers_) {
+ if (offset < header.headers_stream_offset) {
+ // This header frame offset belongs to headers with smaller offset, stop
+ // processing.
+ break;
+ }
+
+ if (offset >= header.headers_stream_offset + header.full_length) {
+ // This header frame belongs to headers with larger offset.
+ continue;
+ }
+
+ QuicByteCount header_offset = offset - header.headers_stream_offset;
+ QuicByteCount retransmitted_length =
+ std::min(data_length, header.full_length - header_offset);
+ if (header.ack_listener != nullptr && retransmitted_length > 0) {
+ header.ack_listener->OnPacketRetransmitted(retransmitted_length);
+ }
+ offset += retransmitted_length;
+ data_length -= retransmitted_length;
+ }
+}
+
+void QuicHeadersStream::OnDataBuffered(
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) {
+ // Populate unacked_headers_.
+ if (!unacked_headers_.empty() &&
+ (offset == unacked_headers_.back().headers_stream_offset +
+ unacked_headers_.back().full_length) &&
+ ack_listener == unacked_headers_.back().ack_listener) {
+ // Try to combine with latest inserted entry if they belong to the same
+ // header (i.e., having contiguous offset and the same ack listener).
+ unacked_headers_.back().full_length += data_length;
+ unacked_headers_.back().unacked_length += data_length;
+ } else {
+ unacked_headers_.push_back(
+ CompressedHeaderInfo(offset, data_length, ack_listener));
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.h
new file mode 100644
index 00000000000..d07c85f17e8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream.h
@@ -0,0 +1,96 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_HEADERS_STREAM_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_HEADERS_STREAM_H_
+
+#include <cstddef>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+class QuicSpdySession;
+
+namespace test {
+class QuicHeadersStreamPeer;
+} // namespace test
+
+// Headers in QUIC are sent as HTTP/2 HEADERS or PUSH_PROMISE frames over a
+// reserved stream with the id 3. Each endpoint (client and server) will
+// allocate an instance of QuicHeadersStream to send and receive headers.
+class QUIC_EXPORT_PRIVATE QuicHeadersStream : public QuicStream {
+ public:
+ explicit QuicHeadersStream(QuicSpdySession* session);
+ QuicHeadersStream(const QuicHeadersStream&) = delete;
+ QuicHeadersStream& operator=(const QuicHeadersStream&) = delete;
+ ~QuicHeadersStream() override;
+
+ // QuicStream implementation
+ void OnDataAvailable() override;
+
+ // Release underlying buffer if allowed.
+ void MaybeReleaseSequencerBuffer();
+
+ bool OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) override;
+
+ void OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted) override;
+
+ private:
+ friend class test::QuicHeadersStreamPeer;
+
+ // CompressedHeaderInfo includes simple information of a header, including
+ // offset in headers stream, unacked length and ack listener of this header.
+ struct QUIC_EXPORT_PRIVATE CompressedHeaderInfo {
+ CompressedHeaderInfo(
+ QuicStreamOffset headers_stream_offset,
+ QuicStreamOffset full_length,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+ CompressedHeaderInfo(const CompressedHeaderInfo& other);
+ ~CompressedHeaderInfo();
+
+ // Offset the header was sent on the headers stream.
+ QuicStreamOffset headers_stream_offset;
+ // The full length of the header.
+ QuicByteCount full_length;
+ // The remaining bytes to be acked.
+ QuicByteCount unacked_length;
+ // Ack listener of this header, and it is notified once any of the bytes has
+ // been acked or retransmitted.
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener;
+ };
+
+ // Returns true if the session is still connected.
+ bool IsConnected();
+
+ // Override to store mapping from offset, length to ack_listener. This
+ // ack_listener is notified once data within [offset, offset + length] is
+ // acked or retransmitted.
+ void OnDataBuffered(
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener)
+ override;
+
+ QuicSpdySession* spdy_session_;
+
+ // Headers that have not been fully acked.
+ QuicDeque<CompressedHeaderInfo> unacked_headers_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_HEADERS_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc
new file mode 100644
index 00000000000..2f669df22c3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc
@@ -0,0 +1,969 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
+
+#include <cstdint>
+#include <ostream>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
+
+using spdy::ERROR_CODE_PROTOCOL_ERROR;
+using spdy::SETTINGS_ENABLE_PUSH;
+using spdy::SETTINGS_HEADER_TABLE_SIZE;
+using spdy::SETTINGS_INITIAL_WINDOW_SIZE;
+using spdy::SETTINGS_MAX_CONCURRENT_STREAMS;
+using spdy::SETTINGS_MAX_FRAME_SIZE;
+using spdy::SETTINGS_MAX_HEADER_LIST_SIZE;
+using spdy::Spdy3PriorityToHttp2Weight;
+using spdy::SpdyAltSvcWireFormat;
+using spdy::SpdyDataIR;
+using spdy::SpdyErrorCode;
+using spdy::SpdyFramer;
+using spdy::SpdyFramerVisitorInterface;
+using spdy::SpdyGoAwayIR;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyHeadersHandlerInterface;
+using spdy::SpdyHeadersIR;
+using spdy::SpdyKnownSettingsId;
+using spdy::SpdyPingId;
+using spdy::SpdyPingIR;
+using spdy::SpdyPriority;
+using spdy::SpdyPriorityIR;
+using spdy::SpdyPushPromiseIR;
+using spdy::SpdyRstStreamIR;
+using spdy::SpdySerializedFrame;
+using spdy::SpdySettingsId;
+using spdy::SpdySettingsIR;
+using spdy::SpdyStreamId;
+using spdy::SpdyWindowUpdateIR;
+using spdy::test::TestHeadersHandler;
+using testing::_;
+using testing::AtLeast;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace quic {
+namespace test {
+
+class MockQuicHpackDebugVisitor : public QuicHpackDebugVisitor {
+ public:
+ MockQuicHpackDebugVisitor() : QuicHpackDebugVisitor() {}
+ MockQuicHpackDebugVisitor(const MockQuicHpackDebugVisitor&) = delete;
+ MockQuicHpackDebugVisitor& operator=(const MockQuicHpackDebugVisitor&) =
+ delete;
+
+ MOCK_METHOD1(OnUseEntry, void(QuicTime::Delta elapsed));
+};
+
+namespace {
+
+class MockVisitor : public SpdyFramerVisitorInterface {
+ public:
+ MOCK_METHOD1(OnError,
+ void(http2::Http2DecoderAdapter::SpdyFramerError error));
+ MOCK_METHOD3(OnDataFrameHeader,
+ void(SpdyStreamId stream_id, size_t length, bool fin));
+ MOCK_METHOD3(OnStreamFrameData,
+ void(SpdyStreamId stream_id, const char* data, size_t len));
+ MOCK_METHOD1(OnStreamEnd, void(SpdyStreamId stream_id));
+ MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId stream_id, size_t len));
+ MOCK_METHOD1(OnHeaderFrameStart,
+ SpdyHeadersHandlerInterface*(SpdyStreamId stream_id));
+ MOCK_METHOD1(OnHeaderFrameEnd, void(SpdyStreamId stream_id));
+ MOCK_METHOD3(OnControlFrameHeaderData,
+ bool(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len));
+ MOCK_METHOD2(OnRstStream,
+ void(SpdyStreamId stream_id, SpdyErrorCode error_code));
+ MOCK_METHOD0(OnSettings, void());
+ MOCK_METHOD2(OnSetting, void(SpdySettingsId id, uint32_t value));
+ MOCK_METHOD0(OnSettingsAck, void());
+ MOCK_METHOD0(OnSettingsEnd, void());
+ MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
+ MOCK_METHOD2(OnGoAway,
+ void(SpdyStreamId last_accepted_stream_id,
+ SpdyErrorCode error_code));
+ MOCK_METHOD7(OnHeaders,
+ void(SpdyStreamId stream_id,
+ bool has_priority,
+ int weight,
+ SpdyStreamId parent_stream_id,
+ bool exclusive,
+ bool fin,
+ bool end));
+ MOCK_METHOD2(OnWindowUpdate,
+ void(SpdyStreamId stream_id, int delta_window_size));
+ MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
+ MOCK_METHOD3(OnPushPromise,
+ void(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end));
+ MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end));
+ MOCK_METHOD3(OnAltSvc,
+ void(SpdyStreamId stream_id,
+ QuicStringPiece origin,
+ const SpdyAltSvcWireFormat::AlternativeServiceVector&
+ altsvc_vector));
+ MOCK_METHOD4(OnPriority,
+ void(SpdyStreamId stream_id,
+ SpdyStreamId parent_stream_id,
+ int weight,
+ bool exclusive));
+ MOCK_METHOD2(OnUnknownFrame,
+ bool(SpdyStreamId stream_id, uint8_t frame_type));
+};
+
+struct TestParams {
+ TestParams(const ParsedQuicVersion& version, Perspective perspective)
+ : version(version), perspective(perspective) {
+ QUIC_LOG(INFO) << "TestParams: version: "
+ << ParsedQuicVersionToString(version)
+ << ", perspective: " << perspective;
+ }
+
+ TestParams(const TestParams& other)
+ : version(other.version), perspective(other.perspective) {}
+
+ ParsedQuicVersion version;
+ Perspective perspective;
+};
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
+ for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+ for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) {
+ params.emplace_back(all_supported_versions[i], p);
+ }
+ }
+ return params;
+}
+
+class QuicHeadersStreamTest : public QuicTestWithParam<TestParams> {
+ public:
+ QuicHeadersStreamTest()
+ : connection_(new StrictMock<MockQuicConnection>(&helper_,
+ &alarm_factory_,
+ perspective(),
+ GetVersion())),
+ session_(connection_),
+ body_("hello world"),
+ stream_frame_(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*fin=*/false,
+ /*offset=*/0,
+ ""),
+ next_promised_stream_id_(2) {
+ session_.Initialize();
+ headers_stream_ = QuicSpdySessionPeer::GetHeadersStream(&session_);
+ headers_[":version"] = "HTTP/1.1";
+ headers_[":status"] = "200 Ok";
+ headers_["content-length"] = "11";
+ framer_ = std::unique_ptr<SpdyFramer>(
+ new SpdyFramer(SpdyFramer::ENABLE_COMPRESSION));
+ deframer_ = std::unique_ptr<http2::Http2DecoderAdapter>(
+ new http2::Http2DecoderAdapter());
+ deframer_->set_visitor(&visitor_);
+ EXPECT_EQ(transport_version(), session_.connection()->transport_version());
+ EXPECT_TRUE(headers_stream_ != nullptr);
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ client_id_1_ = GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0);
+ client_id_2_ = GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 1);
+ client_id_3_ = GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 2);
+ next_stream_id_ =
+ QuicUtils::StreamIdDelta(connection_->transport_version());
+ }
+
+ QuicStreamId GetNthClientInitiatedId(int n) {
+ return GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ QuicConsumedData SaveIov(size_t write_length) {
+ char* buf = new char[write_length];
+ QuicDataWriter writer(write_length, buf, NETWORK_BYTE_ORDER);
+ headers_stream_->WriteStreamData(headers_stream_->stream_bytes_written(),
+ write_length, &writer);
+ saved_data_.append(buf, write_length);
+ delete[] buf;
+ return QuicConsumedData(write_length, false);
+ }
+
+ void SavePayload(const char* data, size_t len) {
+ saved_payloads_.append(data, len);
+ }
+
+ bool SaveHeaderData(const char* data, int len) {
+ saved_header_data_.append(data, len);
+ return true;
+ }
+
+ void SaveHeaderDataStringPiece(QuicStringPiece data) {
+ saved_header_data_.append(data.data(), data.length());
+ }
+
+ void SavePromiseHeaderList(QuicStreamId /* stream_id */,
+ QuicStreamId /* promised_stream_id */,
+ size_t size,
+ const QuicHeaderList& header_list) {
+ SaveToHandler(size, header_list);
+ }
+
+ void SaveHeaderList(QuicStreamId /* stream_id */,
+ bool /* fin */,
+ size_t size,
+ const QuicHeaderList& header_list) {
+ SaveToHandler(size, header_list);
+ }
+
+ void SaveToHandler(size_t size, const QuicHeaderList& header_list) {
+ headers_handler_ = QuicMakeUnique<TestHeadersHandler>();
+ headers_handler_->OnHeaderBlockStart();
+ for (const auto& p : header_list) {
+ headers_handler_->OnHeader(p.first, p.second);
+ }
+ headers_handler_->OnHeaderBlockEnd(size, size);
+ }
+
+ void WriteAndExpectRequestHeaders(QuicStreamId stream_id,
+ bool fin,
+ SpdyPriority priority) {
+ WriteHeadersAndCheckData(stream_id, fin, priority, true /*is_request*/);
+ }
+
+ void WriteAndExpectResponseHeaders(QuicStreamId stream_id, bool fin) {
+ WriteHeadersAndCheckData(stream_id, fin, 0, false /*is_request*/);
+ }
+
+ void WriteHeadersAndCheckData(QuicStreamId stream_id,
+ bool fin,
+ SpdyPriority priority,
+ bool is_request) {
+ // Write the headers and capture the outgoing data
+ EXPECT_CALL(session_, WritevData(headers_stream_,
+ QuicUtils::GetHeadersStreamId(
+ connection_->transport_version()),
+ _, _, NO_FIN))
+ .WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
+ QuicSpdySessionPeer::WriteHeadersOnHeadersStream(
+ &session_, stream_id, headers_.Clone(), fin, priority, nullptr);
+
+ // Parse the outgoing data and check that it matches was was written.
+ if (is_request) {
+ EXPECT_CALL(visitor_,
+ OnHeaders(stream_id, kHasPriority,
+ Spdy3PriorityToHttp2Weight(priority),
+ /*parent_stream_id=*/0,
+ /*exclusive=*/false, fin, kFrameComplete));
+ } else {
+ EXPECT_CALL(visitor_,
+ OnHeaders(stream_id, !kHasPriority,
+ /*priority=*/0,
+ /*parent_stream_id=*/0,
+ /*exclusive=*/false, fin, kFrameComplete));
+ }
+ headers_handler_ = QuicMakeUnique<TestHeadersHandler>();
+ EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id))
+ .WillOnce(Return(headers_handler_.get()));
+ EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id)).Times(1);
+ if (fin) {
+ EXPECT_CALL(visitor_, OnStreamEnd(stream_id));
+ }
+ deframer_->ProcessInput(saved_data_.data(), saved_data_.length());
+ EXPECT_FALSE(deframer_->HasError())
+ << http2::Http2DecoderAdapter::SpdyFramerErrorToString(
+ deframer_->spdy_framer_error());
+
+ CheckHeaders();
+ saved_data_.clear();
+ }
+
+ void CheckHeaders() {
+ EXPECT_EQ(headers_, headers_handler_->decoded_block());
+ headers_handler_.reset();
+ }
+
+ Perspective perspective() const { return GetParam().perspective; }
+
+ QuicTransportVersion transport_version() const {
+ return GetParam().version.transport_version;
+ }
+
+ ParsedQuicVersionVector GetVersion() {
+ ParsedQuicVersionVector versions;
+ versions.push_back(GetParam().version);
+ return versions;
+ }
+
+ void TearDownLocalConnectionState() {
+ QuicConnectionPeer::TearDownLocalConnectionState(connection_);
+ }
+
+ QuicStreamId NextPromisedStreamId() {
+ return next_promised_stream_id_ += next_stream_id_;
+ }
+
+ static const bool kFrameComplete = true;
+ static const bool kHasPriority = true;
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ StrictMock<MockQuicSpdySession> session_;
+ QuicHeadersStream* headers_stream_;
+ SpdyHeaderBlock headers_;
+ std::unique_ptr<TestHeadersHandler> headers_handler_;
+ std::string body_;
+ std::string saved_data_;
+ std::string saved_header_data_;
+ std::string saved_payloads_;
+ std::unique_ptr<SpdyFramer> framer_;
+ std::unique_ptr<http2::Http2DecoderAdapter> deframer_;
+ StrictMock<MockVisitor> visitor_;
+ QuicStreamFrame stream_frame_;
+ QuicStreamId next_promised_stream_id_;
+ QuicStreamId client_id_1_;
+ QuicStreamId client_id_2_;
+ QuicStreamId client_id_3_;
+ QuicStreamId next_stream_id_;
+};
+
+// Run all tests with each version and perspective (client or server).
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicHeadersStreamTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicHeadersStreamTest, StreamId) {
+ EXPECT_EQ(QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ headers_stream_->id());
+}
+
+TEST_P(QuicHeadersStreamTest, WriteHeaders) {
+ for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
+ stream_id += next_stream_id_) {
+ for (bool fin : {false, true}) {
+ if (perspective() == Perspective::IS_SERVER) {
+ WriteAndExpectResponseHeaders(stream_id, fin);
+ } else {
+ for (SpdyPriority priority = 0; priority < 7; ++priority) {
+ // TODO(rch): implement priorities correctly.
+ WriteAndExpectRequestHeaders(stream_id, fin, 0);
+ }
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, WritePushPromises) {
+ for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
+ stream_id += next_stream_id_) {
+ QuicStreamId promised_stream_id = NextPromisedStreamId();
+ if (perspective() == Perspective::IS_SERVER) {
+ // Write the headers and capture the outgoing data
+ EXPECT_CALL(session_, WritevData(headers_stream_,
+ QuicUtils::GetHeadersStreamId(
+ connection_->transport_version()),
+ _, _, NO_FIN))
+ .WillOnce(WithArgs<2>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
+ session_.WritePushPromise(stream_id, promised_stream_id,
+ headers_.Clone());
+
+ // Parse the outgoing data and check that it matches was was written.
+ EXPECT_CALL(visitor_,
+ OnPushPromise(stream_id, promised_stream_id, kFrameComplete));
+ headers_handler_ = QuicMakeUnique<TestHeadersHandler>();
+ EXPECT_CALL(visitor_, OnHeaderFrameStart(stream_id))
+ .WillOnce(Return(headers_handler_.get()));
+ EXPECT_CALL(visitor_, OnHeaderFrameEnd(stream_id)).Times(1);
+ deframer_->ProcessInput(saved_data_.data(), saved_data_.length());
+ EXPECT_FALSE(deframer_->HasError())
+ << http2::Http2DecoderAdapter::SpdyFramerErrorToString(
+ deframer_->spdy_framer_error());
+ CheckHeaders();
+ saved_data_.clear();
+ } else {
+ EXPECT_QUIC_BUG(session_.WritePushPromise(stream_id, promised_stream_id,
+ headers_.Clone()),
+ "Client shouldn't send PUSH_PROMISE");
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessRawData) {
+ for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
+ stream_id += next_stream_id_) {
+ for (bool fin : {false, true}) {
+ for (SpdyPriority priority = 0; priority < 7; ++priority) {
+ // Replace with "WriteHeadersAndSaveData"
+ SpdySerializedFrame frame;
+ if (perspective() == Perspective::IS_SERVER) {
+ SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
+ headers_frame.set_fin(fin);
+ headers_frame.set_has_priority(true);
+ headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0));
+ frame = framer_->SerializeFrame(headers_frame);
+ EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
+ } else {
+ SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
+ headers_frame.set_fin(fin);
+ frame = framer_->SerializeFrame(headers_frame);
+ }
+ EXPECT_CALL(session_,
+ OnStreamHeaderList(stream_id, fin, frame.size(), _))
+ .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+ stream_frame_.offset += frame.size();
+ CheckHeaders();
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessPushPromise) {
+ if (perspective() == Perspective::IS_SERVER) {
+ return;
+ }
+ for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
+ stream_id += next_stream_id_) {
+ QuicStreamId promised_stream_id = NextPromisedStreamId();
+ SpdyPushPromiseIR push_promise(stream_id, promised_stream_id,
+ headers_.Clone());
+ SpdySerializedFrame frame(framer_->SerializeFrame(push_promise));
+ if (perspective() == Perspective::IS_SERVER) {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "PUSH_PROMISE not supported.", _))
+ .WillRepeatedly(InvokeWithoutArgs(
+ this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
+ } else {
+ EXPECT_CALL(session_, OnPromiseHeaderList(stream_id, promised_stream_id,
+ frame.size(), _))
+ .WillOnce(
+ Invoke(this, &QuicHeadersStreamTest::SavePromiseHeaderList));
+ }
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+ if (perspective() == Perspective::IS_CLIENT) {
+ stream_frame_.offset += frame.size();
+ CheckHeaders();
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessPriorityFrame) {
+ QuicStreamId parent_stream_id = 0;
+ for (SpdyPriority priority = 0; priority < 7; ++priority) {
+ for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
+ stream_id += next_stream_id_) {
+ int weight = Spdy3PriorityToHttp2Weight(priority);
+ SpdyPriorityIR priority_frame(stream_id, parent_stream_id, weight, true);
+ SpdySerializedFrame frame(framer_->SerializeFrame(priority_frame));
+ parent_stream_id = stream_id;
+ if (transport_version() <= QUIC_VERSION_39) {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY PRIORITY frame received.", _))
+ .WillRepeatedly(InvokeWithoutArgs(
+ this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
+ } else if (perspective() == Perspective::IS_CLIENT) {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "Server must not send PRIORITY frames.", _))
+ .WillRepeatedly(InvokeWithoutArgs(
+ this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
+ } else {
+ EXPECT_CALL(session_, OnPriorityFrame(stream_id, priority)).Times(1);
+ }
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+ stream_frame_.offset += frame.size();
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessPushPromiseDisabledSetting) {
+ session_.OnConfigNegotiated();
+ SpdySettingsIR data;
+ // Respect supported settings frames SETTINGS_ENABLE_PUSH.
+ data.AddSetting(SETTINGS_ENABLE_PUSH, 0);
+ SpdySerializedFrame frame(framer_->SerializeFrame(data));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ if (perspective() == Perspective::IS_CLIENT) {
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "Unsupported field of HTTP/2 SETTINGS frame: 2", _));
+ }
+ headers_stream_->OnStreamFrame(stream_frame_);
+ EXPECT_EQ(session_.server_push_enabled(),
+ perspective() == Perspective::IS_CLIENT);
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessLargeRawData) {
+ QuicSpdySessionPeer::SetMaxUncompressedHeaderBytes(&session_, 256 * 1024);
+ // We want to create a frame that is more than the SPDY Framer's max control
+ // frame size, which is 16K, but less than the HPACK decoders max decode
+ // buffer size, which is 32K.
+ headers_["key0"] = std::string(1 << 13, '.');
+ headers_["key1"] = std::string(1 << 13, '.');
+ headers_["key2"] = std::string(1 << 13, '.');
+ for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
+ stream_id += next_stream_id_) {
+ for (bool fin : {false, true}) {
+ for (SpdyPriority priority = 0; priority < 7; ++priority) {
+ // Replace with "WriteHeadersAndSaveData"
+ SpdySerializedFrame frame;
+ if (perspective() == Perspective::IS_SERVER) {
+ SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
+ headers_frame.set_fin(fin);
+ headers_frame.set_has_priority(true);
+ headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0));
+ frame = framer_->SerializeFrame(headers_frame);
+ EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
+ } else {
+ SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
+ headers_frame.set_fin(fin);
+ frame = framer_->SerializeFrame(headers_frame);
+ }
+ EXPECT_CALL(session_,
+ OnStreamHeaderList(stream_id, fin, frame.size(), _))
+ .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+ stream_frame_.offset += frame.size();
+ CheckHeaders();
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessBadData) {
+ const char kBadData[] = "blah blah blah";
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _))
+ .Times(::testing::AnyNumber());
+ stream_frame_.data_buffer = kBadData;
+ stream_frame_.data_length = strlen(kBadData);
+ headers_stream_->OnStreamFrame(stream_frame_);
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) {
+ SpdyDataIR data(/* stream_id = */ 2, "ping");
+ SpdySerializedFrame frame(framer_->SerializeFrame(data));
+
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY DATA frame received.", _))
+ .WillOnce(InvokeWithoutArgs(
+ this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) {
+ SpdyRstStreamIR data(/* stream_id = */ 2, ERROR_CODE_PROTOCOL_ERROR);
+ SpdySerializedFrame frame(framer_->SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY RST_STREAM frame received.", _))
+ .WillOnce(InvokeWithoutArgs(
+ this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+}
+
+TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameSupportedFields) {
+ const uint32_t kTestHeaderTableSize = 1000;
+ SpdySettingsIR data;
+ // Respect supported settings frames SETTINGS_HEADER_TABLE_SIZE,
+ // SETTINGS_MAX_HEADER_LIST_SIZE.
+ data.AddSetting(SETTINGS_HEADER_TABLE_SIZE, kTestHeaderTableSize);
+ data.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, 2000);
+ SpdySerializedFrame frame(framer_->SerializeFrame(data));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+ EXPECT_EQ(kTestHeaderTableSize, QuicSpdySessionPeer::GetSpdyFramer(&session_)
+ .header_encoder_table_size());
+}
+
+TEST_P(QuicHeadersStreamTest, RespectHttp2SettingsFrameUnsupportedFields) {
+ SpdySettingsIR data;
+ // Does not support SETTINGS_MAX_CONCURRENT_STREAMS,
+ // SETTINGS_INITIAL_WINDOW_SIZE, SETTINGS_ENABLE_PUSH and
+ // SETTINGS_MAX_FRAME_SIZE.
+ data.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 100);
+ data.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 100);
+ data.AddSetting(SETTINGS_ENABLE_PUSH, 1);
+ data.AddSetting(SETTINGS_MAX_FRAME_SIZE, 1250);
+ SpdySerializedFrame frame(framer_->SerializeFrame(data));
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
+ SETTINGS_MAX_CONCURRENT_STREAMS),
+ _));
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
+ SETTINGS_INITIAL_WINDOW_SIZE),
+ _));
+ if (session_.perspective() == Perspective::IS_CLIENT) {
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
+ SETTINGS_ENABLE_PUSH),
+ _));
+ }
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ",
+ SETTINGS_MAX_FRAME_SIZE),
+ _));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) {
+ SpdyPingIR data(1);
+ SpdySerializedFrame frame(framer_->SerializeFrame(data));
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY PING frame received.", _))
+ .WillOnce(InvokeWithoutArgs(
+ this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) {
+ SpdyGoAwayIR data(/* last_good_stream_id = */ 1, ERROR_CODE_PROTOCOL_ERROR,
+ "go away");
+ SpdySerializedFrame frame(framer_->SerializeFrame(data));
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY GOAWAY frame received.", _))
+ .WillOnce(InvokeWithoutArgs(
+ this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) {
+ SpdyWindowUpdateIR data(/* stream_id = */ 1, /* delta = */ 1);
+ SpdySerializedFrame frame(framer_->SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY WINDOW_UPDATE frame received.", _))
+ .WillOnce(InvokeWithoutArgs(
+ this, &QuicHeadersStreamTest::TearDownLocalConnectionState));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ headers_stream_->OnStreamFrame(stream_frame_);
+}
+
+TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) {
+ EXPECT_FALSE(QuicStreamPeer::StreamContributesToConnectionFlowControl(
+ headers_stream_));
+}
+
+TEST_P(QuicHeadersStreamTest, HpackDecoderDebugVisitor) {
+ auto hpack_decoder_visitor =
+ QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>();
+ {
+ InSequence seq;
+ // Number of indexed representations generated in headers below.
+ for (int i = 1; i < 28; i++) {
+ EXPECT_CALL(*hpack_decoder_visitor,
+ OnUseEntry(QuicTime::Delta::FromMilliseconds(i)))
+ .Times(4);
+ }
+ }
+ QuicSpdySessionPeer::SetHpackDecoderDebugVisitor(
+ &session_, std::move(hpack_decoder_visitor));
+
+ // Create some headers we expect to generate entries in HPACK's
+ // dynamic table, in addition to content-length.
+ headers_["key0"] = std::string(1 << 1, '.');
+ headers_["key1"] = std::string(1 << 2, '.');
+ headers_["key2"] = std::string(1 << 3, '.');
+ for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
+ stream_id += next_stream_id_) {
+ for (bool fin : {false, true}) {
+ for (SpdyPriority priority = 0; priority < 7; ++priority) {
+ // Replace with "WriteHeadersAndSaveData"
+ SpdySerializedFrame frame;
+ if (perspective() == Perspective::IS_SERVER) {
+ SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
+ headers_frame.set_fin(fin);
+ headers_frame.set_has_priority(true);
+ headers_frame.set_weight(Spdy3PriorityToHttp2Weight(0));
+ frame = framer_->SerializeFrame(headers_frame);
+ EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
+ } else {
+ SpdyHeadersIR headers_frame(stream_id, headers_.Clone());
+ headers_frame.set_fin(fin);
+ frame = framer_->SerializeFrame(headers_frame);
+ }
+ EXPECT_CALL(session_,
+ OnStreamHeaderList(stream_id, fin, frame.size(), _))
+ .WillOnce(Invoke(this, &QuicHeadersStreamTest::SaveHeaderList));
+ stream_frame_.data_buffer = frame.data();
+ stream_frame_.data_length = frame.size();
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ headers_stream_->OnStreamFrame(stream_frame_);
+ stream_frame_.offset += frame.size();
+ CheckHeaders();
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, HpackEncoderDebugVisitor) {
+ auto hpack_encoder_visitor =
+ QuicMakeUnique<StrictMock<MockQuicHpackDebugVisitor>>();
+ if (perspective() == Perspective::IS_SERVER) {
+ InSequence seq;
+ for (int i = 1; i < 4; i++) {
+ EXPECT_CALL(*hpack_encoder_visitor,
+ OnUseEntry(QuicTime::Delta::FromMilliseconds(i)));
+ }
+ } else {
+ InSequence seq;
+ for (int i = 1; i < 28; i++) {
+ EXPECT_CALL(*hpack_encoder_visitor,
+ OnUseEntry(QuicTime::Delta::FromMilliseconds(i)));
+ }
+ }
+ QuicSpdySessionPeer::SetHpackEncoderDebugVisitor(
+ &session_, std::move(hpack_encoder_visitor));
+
+ for (QuicStreamId stream_id = client_id_1_; stream_id < client_id_3_;
+ stream_id += next_stream_id_) {
+ for (bool fin : {false, true}) {
+ if (perspective() == Perspective::IS_SERVER) {
+ WriteAndExpectResponseHeaders(stream_id, fin);
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ } else {
+ for (SpdyPriority priority = 0; priority < 7; ++priority) {
+ // TODO(rch): implement priorities correctly.
+ WriteAndExpectRequestHeaders(stream_id, fin, 0);
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ }
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, AckSentData) {
+ EXPECT_CALL(session_, WritevData(headers_stream_,
+ QuicUtils::GetHeadersStreamId(
+ connection_->transport_version()),
+ _, _, NO_FIN))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ InSequence s;
+ QuicReferenceCountedPointer<MockAckListener> ack_listener1(
+ new MockAckListener());
+ QuicReferenceCountedPointer<MockAckListener> ack_listener2(
+ new MockAckListener());
+ QuicReferenceCountedPointer<MockAckListener> ack_listener3(
+ new MockAckListener());
+
+ // Packet 1.
+ headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
+ headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
+ headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
+
+ // Packet 2.
+ headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
+ headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
+
+ // Packet 3.
+ headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
+
+ // Packet 2 gets retransmitted.
+ EXPECT_CALL(*ack_listener3, OnPacketRetransmitted(7)).Times(1);
+ EXPECT_CALL(*ack_listener2, OnPacketRetransmitted(7)).Times(1);
+ headers_stream_->OnStreamFrameRetransmitted(21, 7, false);
+ headers_stream_->OnStreamFrameRetransmitted(28, 7, false);
+
+ // Packets are acked in order: 2, 3, 1.
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 21, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(7u, newly_acked_length);
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 28, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(7u, newly_acked_length);
+
+ EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 35, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(7u, newly_acked_length);
+
+ EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
+ EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 0, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(7u, newly_acked_length);
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 7, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(7u, newly_acked_length);
+ // Unsent data is acked.
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 14, 10, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(7u, newly_acked_length);
+}
+
+TEST_P(QuicHeadersStreamTest, FrameContainsMultipleHeaders) {
+ // In this test, a stream frame can contain multiple headers.
+ EXPECT_CALL(session_, WritevData(headers_stream_,
+ QuicUtils::GetHeadersStreamId(
+ connection_->transport_version()),
+ _, _, NO_FIN))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ InSequence s;
+ QuicReferenceCountedPointer<MockAckListener> ack_listener1(
+ new MockAckListener());
+ QuicReferenceCountedPointer<MockAckListener> ack_listener2(
+ new MockAckListener());
+ QuicReferenceCountedPointer<MockAckListener> ack_listener3(
+ new MockAckListener());
+
+ headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
+ headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
+ headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
+ headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
+ headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
+ headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
+
+ // Frame 1 is retransmitted.
+ EXPECT_CALL(*ack_listener1, OnPacketRetransmitted(14));
+ EXPECT_CALL(*ack_listener2, OnPacketRetransmitted(3));
+ headers_stream_->OnStreamFrameRetransmitted(0, 17, false);
+
+ // Frames are acked in order: 2, 3, 1.
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(4, _));
+ EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(2, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 17, 13, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(13u, newly_acked_length);
+
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _));
+ EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 30, 12, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(12u, newly_acked_length);
+
+ EXPECT_CALL(*ack_listener1, OnPacketAcked(14, _));
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(3, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 0, 17, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(17u, newly_acked_length);
+}
+
+TEST_P(QuicHeadersStreamTest, HeadersGetAckedMultipleTimes) {
+ EXPECT_CALL(session_, WritevData(headers_stream_,
+ QuicUtils::GetHeadersStreamId(
+ connection_->transport_version()),
+ _, _, NO_FIN))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ InSequence s;
+ QuicReferenceCountedPointer<MockAckListener> ack_listener1(
+ new MockAckListener());
+ QuicReferenceCountedPointer<MockAckListener> ack_listener2(
+ new MockAckListener());
+ QuicReferenceCountedPointer<MockAckListener> ack_listener3(
+ new MockAckListener());
+
+ // Send [0, 42).
+ headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
+ headers_stream_->WriteOrBufferData("Header5", false, ack_listener1);
+ headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
+ headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
+ headers_stream_->WriteOrBufferData("Header7", false, ack_listener2);
+ headers_stream_->WriteOrBufferData("Header9", false, ack_listener3);
+
+ // Ack [15, 20), [5, 25), [10, 17), [0, 12) and [22, 42).
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 15, 5, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(5u, newly_acked_length);
+
+ EXPECT_CALL(*ack_listener1, OnPacketAcked(9, _));
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(1, _));
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(1, _));
+ EXPECT_CALL(*ack_listener3, OnPacketAcked(4, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 5, 20, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(15u, newly_acked_length);
+
+ // Duplicate ack.
+ EXPECT_FALSE(headers_stream_->OnStreamFrameAcked(
+ 10, 7, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(0u, newly_acked_length);
+
+ EXPECT_CALL(*ack_listener1, OnPacketAcked(5, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 0, 12, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(5u, newly_acked_length);
+
+ EXPECT_CALL(*ack_listener3, OnPacketAcked(3, _));
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
+ EXPECT_CALL(*ack_listener3, OnPacketAcked(7, _));
+ EXPECT_TRUE(headers_stream_->OnStreamFrameAcked(
+ 22, 20, false, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(17u, newly_acked_length);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..4c625c0df04
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc
@@ -0,0 +1,163 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+const uint16_t kSettingsMaxHeaderListSize = 6;
+const uint16_t kSettingsNumPlaceholders = 8;
+
+// Visitor of HttpDecoder that passes data frame to QuicSpdyStream and closes
+// the connection on unexpected frames.
+class QuicReceiveControlStream::HttpDecoderVisitor
+ : public HttpDecoder::Visitor {
+ public:
+ explicit HttpDecoderVisitor(QuicReceiveControlStream* stream)
+ : stream_(stream) {}
+ HttpDecoderVisitor(const HttpDecoderVisitor&) = delete;
+ HttpDecoderVisitor& operator=(const HttpDecoderVisitor&) = delete;
+
+ void OnError(HttpDecoder* decoder) override {
+ stream_->session()->connection()->CloseConnection(
+ QUIC_HTTP_DECODER_ERROR, "Http decoder internal error",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ void OnPriorityFrame(const PriorityFrame& frame) override {
+ CloseConnectionOnWrongFrame("Priority");
+ }
+
+ void OnCancelPushFrame(const CancelPushFrame& frame) override {
+ CloseConnectionOnWrongFrame("Cancel Push");
+ }
+
+ void OnMaxPushIdFrame(const MaxPushIdFrame& frame) override {
+ CloseConnectionOnWrongFrame("Max Push Id");
+ }
+
+ void OnGoAwayFrame(const GoAwayFrame& frame) override {
+ CloseConnectionOnWrongFrame("Goaway");
+ }
+
+ void OnSettingsFrameStart(Http3FrameLengths frame_lengths) override {
+ stream_->OnSettingsFrameStart(frame_lengths);
+ }
+
+ void OnSettingsFrame(const SettingsFrame& frame) override {
+ stream_->OnSettingsFrame(frame);
+ }
+
+ void OnDuplicatePushFrame(const DuplicatePushFrame& frame) override {
+ CloseConnectionOnWrongFrame("Duplicate Push");
+ }
+
+ void OnDataFrameStart(Http3FrameLengths frame_lengths) override {
+ CloseConnectionOnWrongFrame("Data");
+ }
+
+ void OnDataFramePayload(QuicStringPiece payload) override {
+ CloseConnectionOnWrongFrame("Data");
+ }
+
+ void OnDataFrameEnd() override { CloseConnectionOnWrongFrame("Data"); }
+
+ void OnHeadersFrameStart(Http3FrameLengths frame_length) override {
+ CloseConnectionOnWrongFrame("Headers");
+ }
+
+ void OnHeadersFramePayload(QuicStringPiece payload) override {
+ CloseConnectionOnWrongFrame("Headers");
+ }
+
+ void OnHeadersFrameEnd() override { CloseConnectionOnWrongFrame("Headers"); }
+
+ void OnPushPromiseFrameStart(PushId push_id) override {
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ void OnPushPromiseFramePayload(QuicStringPiece payload) override {
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ void OnPushPromiseFrameEnd() override {
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ private:
+ void CloseConnectionOnWrongFrame(std::string frame_type) {
+ // TODO(renjietang): Change to HTTP/3 error type.
+ stream_->session()->connection()->CloseConnection(
+ QUIC_HTTP_DECODER_ERROR,
+ frame_type + " frame received on control stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ QuicReceiveControlStream* stream_;
+};
+
+QuicReceiveControlStream::QuicReceiveControlStream(QuicStreamId id,
+ QuicSpdySession* session)
+ : QuicStream(id, session, /*is_static = */ true, READ_UNIDIRECTIONAL),
+ received_settings_length_(0),
+ http_decoder_visitor_(new HttpDecoderVisitor(this)) {
+ decoder_.set_visitor(http_decoder_visitor_.get());
+ sequencer()->set_level_triggered(true);
+}
+
+QuicReceiveControlStream::~QuicReceiveControlStream() {}
+
+void QuicReceiveControlStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ // TODO(renjietang) Change the error code to H/3 specific
+ // HTTP_CLOSED_CRITICAL_STREAM.
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to reset receive control stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+void QuicReceiveControlStream::OnDataAvailable() {
+ iovec iov;
+ while (!reading_stopped() && sequencer()->PrefetchNextRegion(&iov)) {
+ decoder_.ProcessInput(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len);
+ }
+}
+
+void QuicReceiveControlStream::OnSettingsFrameStart(
+ Http3FrameLengths frame_lengths) {
+ if (received_settings_length_ != 0) {
+ // TODO(renjietang): Change error code to HTTP_UNEXPECTED_FRAME.
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Settings frames are received twice.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ received_settings_length_ +=
+ frame_lengths.header_length + frame_lengths.payload_length;
+}
+
+void QuicReceiveControlStream::OnSettingsFrame(const SettingsFrame& settings) {
+ QuicSpdySession* spdy_session = static_cast<QuicSpdySession*>(session());
+ for (auto& it : settings.values) {
+ uint16_t setting_id = it.first;
+ switch (setting_id) {
+ case kSettingsMaxHeaderListSize:
+ spdy_session->set_max_inbound_header_list_size(it.second);
+ break;
+ case kSettingsNumPlaceholders:
+ // TODO: Support placeholder setting
+ break;
+ default:
+ break;
+ }
+ }
+ sequencer()->MarkConsumed(received_settings_length_);
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..1805d6ccc08
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h
@@ -0,0 +1,53 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_RECEIVE_CONTROL_STREAM_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_RECEIVE_CONTROL_STREAM_H_
+
+#include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicSpdySession;
+
+// 3.2.1 Control Stream.
+// The receive control stream is peer initiated and is read only.
+class QUIC_EXPORT_PRIVATE QuicReceiveControlStream : public QuicStream {
+ public:
+ // |session| can't be nullptr, and the ownership is not passed. The stream can
+ // only be accessed through the session.
+ explicit QuicReceiveControlStream(QuicStreamId id, QuicSpdySession* session);
+ QuicReceiveControlStream(const QuicReceiveControlStream&) = delete;
+ QuicReceiveControlStream& operator=(const QuicReceiveControlStream&) = delete;
+ ~QuicReceiveControlStream() override;
+
+ // Overriding QuicStream::OnStreamReset to make sure control stream is never
+ // closed before connection.
+ void OnStreamReset(const QuicRstStreamFrame& frame) override;
+
+ // Implementation of QuicStream.
+ void OnDataAvailable() override;
+
+ protected:
+ // Called from HttpDecoderVisitor.
+ void OnSettingsFrameStart(Http3FrameLengths frame_lengths);
+ void OnSettingsFrame(const SettingsFrame& settings);
+
+ private:
+ class HttpDecoderVisitor;
+
+ HttpDecoder decoder_;
+
+ // Track the number of settings bytes received.
+ size_t received_settings_length_;
+
+ // HttpDecoder's visitor.
+ std::unique_ptr<HttpDecoderVisitor> http_decoder_visitor_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_RECEIVE_CONTROL_STREAM_H_
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
new file mode 100644
index 00000000000..1861d17495c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc
@@ -0,0 +1,172 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_receive_control_stream.h"
+
+#include <cstdint>
+#include <ostream>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+using testing::_;
+using testing::StrictMock;
+
+struct TestParams {
+ TestParams(const ParsedQuicVersion& version, Perspective perspective)
+ : version(version), perspective(perspective) {
+ QUIC_LOG(INFO) << "TestParams: version: "
+ << ParsedQuicVersionToString(version)
+ << ", perspective: " << perspective;
+ }
+
+ TestParams(const TestParams& other)
+ : version(other.version), perspective(other.perspective) {}
+
+ ParsedQuicVersion version;
+ Perspective perspective;
+};
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
+ for (const auto& version : AllSupportedVersions()) {
+ if (!VersionHasControlStreams(version.transport_version)) {
+ continue;
+ }
+ for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) {
+ params.emplace_back(version, p);
+ }
+ }
+ return params;
+}
+
+class QuicReceiveControlStreamTest : public QuicTestWithParam<TestParams> {
+ public:
+ QuicReceiveControlStreamTest()
+ : connection_(new StrictMock<MockQuicConnection>(
+ &helper_,
+ &alarm_factory_,
+ perspective(),
+ SupportedVersions(GetParam().version))),
+ session_(connection_) {
+ session_.Initialize();
+ receive_control_stream_ = QuicMakeUnique<QuicReceiveControlStream>(
+ QuicUtils::GetFirstUnidirectionalStreamId(
+ GetParam().version.transport_version,
+ perspective() == Perspective::IS_CLIENT ? Perspective::IS_SERVER
+ : Perspective::IS_CLIENT),
+ &session_);
+ }
+
+ Perspective perspective() const { return GetParam().perspective; }
+
+ std::string EncodeSettings(const SettingsFrame& settings) {
+ HttpEncoder encoder;
+ std::unique_ptr<char[]> buffer;
+ auto header_length = encoder.SerializeSettingsFrame(settings, &buffer);
+ return std::string(buffer.get(), header_length);
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ StrictMock<MockQuicSpdySession> session_;
+ HttpDecoder decoder_;
+ std::unique_ptr<QuicReceiveControlStream> receive_control_stream_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicReceiveControlStreamTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicReceiveControlStreamTest, ResetControlStream) {
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId,
+ receive_control_stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ receive_control_stream_->OnStreamReset(rst_frame);
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveSettings) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ std::string data = EncodeSettings(settings);
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 0,
+ QuicStringPiece(data));
+ EXPECT_NE(5u, session_.max_inbound_header_list_size());
+ receive_control_stream_->OnStreamFrame(frame);
+ EXPECT_EQ(5u, session_.max_inbound_header_list_size());
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsTwice) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ std::string data = EncodeSettings(settings);
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 0,
+ QuicStringPiece(data));
+ QuicStreamFrame frame2(receive_control_stream_->id(), false, data.length(),
+ QuicStringPiece(data));
+ receive_control_stream_->OnStreamFrame(frame);
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Settings frames are received twice.", _));
+ receive_control_stream_->OnStreamFrame(frame2);
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsFragments) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ std::string data = EncodeSettings(settings);
+ std::string data1 = data.substr(0, 1);
+ std::string data2 = data.substr(1, data.length() - 1);
+
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 0,
+ QuicStringPiece(data.data(), 1));
+ QuicStreamFrame frame2(receive_control_stream_->id(), false, 1,
+ QuicStringPiece(data.data() + 1, data.length() - 1));
+ EXPECT_NE(5u, session_.max_inbound_header_list_size());
+ receive_control_stream_->OnStreamFrame(frame);
+ receive_control_stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(5u, session_.max_inbound_header_list_size());
+}
+
+TEST_P(QuicReceiveControlStreamTest, ReceiveWrongFrame) {
+ GoAwayFrame goaway;
+ goaway.stream_id = 0x1;
+ HttpEncoder encoder;
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length = encoder.SerializeGoAwayFrame(goaway, &buffer);
+ std::string data = std::string(buffer.get(), header_length);
+
+ QuicStreamFrame frame(receive_control_stream_->id(), false, 0,
+ QuicStringPiece(data));
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_DECODER_ERROR, _, _));
+ receive_control_stream_->OnStreamFrame(frame);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc
new file mode 100644
index 00000000000..40b6111133b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.cc
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+QuicSendControlStream::QuicSendControlStream(QuicStreamId id,
+ QuicSpdySession* session)
+ : QuicStream(id, session, /*is_static = */ true, WRITE_UNIDIRECTIONAL),
+ settings_sent_(false) {}
+
+void QuicSendControlStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ // TODO(renjietang) Change the error code to H/3 specific
+ // HTTP_CLOSED_CRITICAL_STREAM.
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to reset send control stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+void QuicSendControlStream::SendSettingsFrame(const SettingsFrame& settings) {
+ DCHECK(!settings_sent_);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount frame_length =
+ encoder_.SerializeSettingsFrame(settings, &buffer);
+ WriteOrBufferData(QuicStringPiece(buffer.get(), frame_length),
+ /*fin = */ false, nullptr);
+ settings_sent_ = true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h
new file mode 100644
index 00000000000..09bdafb26a6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h
@@ -0,0 +1,47 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SEND_CONTROL_STREAM_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SEND_CONTROL_STREAM_H_
+
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicSpdySession;
+
+// 3.2.1 Control Stream.
+// The send control stream is self initiated and is write only.
+class QUIC_EXPORT_PRIVATE QuicSendControlStream : public QuicStream {
+ public:
+ // |session| can't be nullptr, and the ownership is not passed. The stream can
+ // only be accessed through the session.
+ explicit QuicSendControlStream(QuicStreamId id, QuicSpdySession* session);
+ QuicSendControlStream(const QuicSendControlStream&) = delete;
+ QuicSendControlStream& operator=(const QuicSendControlStream&) = delete;
+ ~QuicSendControlStream() override = default;
+
+ // Overriding QuicStream::OnStreamReset to make sure control stream is never
+ // closed before connection.
+ void OnStreamReset(const QuicRstStreamFrame& frame) override;
+
+ // Send |settings| on this stream.
+ // Settings frame must be the first frame sent on this stream.
+ void SendSettingsFrame(const SettingsFrame& settings);
+
+ // The send control stream is write unidirectional, so this method should
+ // never be called.
+ void OnDataAvailable() override { QUIC_NOTREACHED(); }
+
+ private:
+ HttpEncoder encoder_;
+ // Track if a settings frame is already sent.
+ bool settings_sent_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SEND_CONTROL_STREAM_H_
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
new file mode 100644
index 00000000000..980cdf54133
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc
@@ -0,0 +1,124 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_send_control_stream.h"
+
+#include <cstdint>
+#include <ostream>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+using testing::_;
+using testing::Invoke;
+using testing::StrictMock;
+
+struct TestParams {
+ TestParams(const ParsedQuicVersion& version, Perspective perspective)
+ : version(version), perspective(perspective) {
+ QUIC_LOG(INFO) << "TestParams: version: "
+ << ParsedQuicVersionToString(version)
+ << ", perspective: " << perspective;
+ }
+
+ TestParams(const TestParams& other)
+ : version(other.version), perspective(other.perspective) {}
+
+ ParsedQuicVersion version;
+ Perspective perspective;
+};
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
+ for (const auto& version : AllSupportedVersions()) {
+ if (!VersionHasControlStreams(version.transport_version)) {
+ continue;
+ }
+ for (Perspective p : {Perspective::IS_SERVER, Perspective::IS_CLIENT}) {
+ params.emplace_back(version, p);
+ }
+ }
+ return params;
+}
+
+class QuicSendControlStreamTest : public QuicTestWithParam<TestParams> {
+ public:
+ QuicSendControlStreamTest()
+ : connection_(new StrictMock<MockQuicConnection>(
+ &helper_,
+ &alarm_factory_,
+ perspective(),
+ SupportedVersions(GetParam().version))),
+ session_(connection_) {
+ session_.Initialize();
+ send_control_stream_ = QuicMakeUnique<QuicSendControlStream>(
+ QuicSpdySessionPeer::GetNextOutgoingUnidirectionalStreamId(&session_),
+ &session_);
+ ON_CALL(session_, WritevData(_, _, _, _, _))
+ .WillByDefault(Invoke(MockQuicSession::ConsumeData));
+ }
+
+ Perspective perspective() const { return GetParam().perspective; }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ StrictMock<MockQuicSpdySession> session_;
+ HttpEncoder encoder_;
+ std::unique_ptr<QuicSendControlStream> send_control_stream_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSendControlStreamTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicSendControlStreamTest, WriteSettingsOnStartUp) {
+ SettingsFrame settings;
+ settings.values[3] = 2;
+ settings.values[6] = 5;
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount frame_length =
+ encoder_.SerializeSettingsFrame(settings, &buffer);
+
+ EXPECT_CALL(session_, WritevData(_, _, frame_length, _, _));
+ send_control_stream_->SendSettingsFrame(settings);
+}
+
+TEST_P(QuicSendControlStreamTest, ResetControlStream) {
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId,
+ send_control_stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ send_control_stream_->OnStreamReset(rst_frame);
+}
+
+TEST_P(QuicSendControlStreamTest, ReceiveDataOnSendControlStream) {
+ QuicStreamFrame frame(send_control_stream_->id(), false, 0, "test");
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM, _, _));
+ send_control_stream_->OnStreamFrame(frame);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..5cdb2c46451
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicServerSessionBase::QuicServerSessionBase(
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache)
+ : QuicSpdySession(connection, visitor, config, supported_versions),
+ crypto_config_(crypto_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ helper_(helper),
+ bandwidth_resumption_enabled_(false),
+ bandwidth_estimate_sent_to_client_(QuicBandwidth::Zero()),
+ last_scup_time_(QuicTime::Zero()) {}
+
+QuicServerSessionBase::~QuicServerSessionBase() {}
+
+void QuicServerSessionBase::Initialize() {
+ crypto_stream_.reset(
+ CreateQuicCryptoServerStream(crypto_config_, compressed_certs_cache_));
+ QuicSpdySession::Initialize();
+}
+
+void QuicServerSessionBase::OnConfigNegotiated() {
+ QuicSpdySession::OnConfigNegotiated();
+
+ if (!config()->HasReceivedConnectionOptions()) {
+ return;
+ }
+
+ // Enable bandwidth resumption if peer sent correct connection options.
+ const bool last_bandwidth_resumption =
+ ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE);
+ const bool max_bandwidth_resumption =
+ ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWMX);
+ bandwidth_resumption_enabled_ =
+ last_bandwidth_resumption || max_bandwidth_resumption;
+
+ // If the client has provided a bandwidth estimate from the same serving
+ // region as this server, then decide whether to use the data for bandwidth
+ // resumption.
+ const CachedNetworkParameters* cached_network_params =
+ crypto_stream_->PreviousCachedNetworkParams();
+ if (cached_network_params != nullptr &&
+ cached_network_params->serving_region() == serving_region_) {
+ // Log the received connection parameters, regardless of how they
+ // get used for bandwidth resumption.
+ connection()->OnReceiveConnectionState(*cached_network_params);
+
+ if (bandwidth_resumption_enabled_) {
+ // Only do bandwidth resumption if estimate is recent enough.
+ const uint64_t seconds_since_estimate =
+ connection()->clock()->WallNow().ToUNIXSeconds() -
+ cached_network_params->timestamp();
+ if (seconds_since_estimate <= kNumSecondsPerHour) {
+ connection()->ResumeConnectionState(*cached_network_params,
+ max_bandwidth_resumption);
+ }
+ }
+ }
+}
+
+void QuicServerSessionBase::OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {
+ QuicSession::OnConnectionClosed(error, error_details, source);
+ // In the unlikely event we get a connection close while doing an asynchronous
+ // crypto event, make sure we cancel the callback.
+ if (crypto_stream_ != nullptr) {
+ crypto_stream_->CancelOutstandingCallbacks();
+ }
+}
+
+void QuicServerSessionBase::OnCongestionWindowChange(QuicTime now) {
+ if (!bandwidth_resumption_enabled_) {
+ return;
+ }
+ // Only send updates when the application has no data to write.
+ if (HasDataToWrite()) {
+ return;
+ }
+
+ // If not enough time has passed since the last time we sent an update to the
+ // client, or not enough packets have been sent, then return early.
+ const QuicSentPacketManager& sent_packet_manager =
+ connection()->sent_packet_manager();
+ int64_t srtt_ms =
+ sent_packet_manager.GetRttStats()->smoothed_rtt().ToMilliseconds();
+ int64_t now_ms = (now - last_scup_time_).ToMilliseconds();
+ int64_t packets_since_last_scup = 0;
+ const QuicPacketNumber largest_sent_packet =
+ connection()->sent_packet_manager().GetLargestSentPacket();
+ if (largest_sent_packet.IsInitialized()) {
+ packets_since_last_scup =
+ last_scup_packet_number_.IsInitialized()
+ ? largest_sent_packet - last_scup_packet_number_
+ : largest_sent_packet.ToUint64();
+ }
+ if (now_ms < (kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms) ||
+ now_ms < kMinIntervalBetweenServerConfigUpdatesMs ||
+ packets_since_last_scup < kMinPacketsBetweenServerConfigUpdates) {
+ return;
+ }
+
+ // If the bandwidth recorder does not have a valid estimate, return early.
+ const QuicSustainedBandwidthRecorder* bandwidth_recorder =
+ sent_packet_manager.SustainedBandwidthRecorder();
+ if (bandwidth_recorder == nullptr || !bandwidth_recorder->HasEstimate()) {
+ return;
+ }
+
+ // The bandwidth recorder has recorded at least one sustained bandwidth
+ // estimate. Check that it's substantially different from the last one that
+ // we sent to the client, and if so, send the new one.
+ QuicBandwidth new_bandwidth_estimate =
+ bandwidth_recorder->BandwidthEstimate();
+
+ int64_t bandwidth_delta =
+ std::abs(new_bandwidth_estimate.ToBitsPerSecond() -
+ bandwidth_estimate_sent_to_client_.ToBitsPerSecond());
+
+ // Define "substantial" difference as a 50% increase or decrease from the
+ // last estimate.
+ bool substantial_difference =
+ bandwidth_delta >
+ 0.5 * bandwidth_estimate_sent_to_client_.ToBitsPerSecond();
+ if (!substantial_difference) {
+ return;
+ }
+
+ bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate;
+ QUIC_DVLOG(1) << "Server: sending new bandwidth estimate (KBytes/s): "
+ << bandwidth_estimate_sent_to_client_.ToKBytesPerSecond();
+
+ // Include max bandwidth in the update.
+ QuicBandwidth max_bandwidth_estimate =
+ bandwidth_recorder->MaxBandwidthEstimate();
+ int32_t max_bandwidth_timestamp = bandwidth_recorder->MaxBandwidthTimestamp();
+
+ // Fill the proto before passing it to the crypto stream to send.
+ const int32_t bw_estimate_bytes_per_second =
+ BandwidthToCachedParameterBytesPerSecond(
+ bandwidth_estimate_sent_to_client_);
+ const int32_t max_bw_estimate_bytes_per_second =
+ BandwidthToCachedParameterBytesPerSecond(max_bandwidth_estimate);
+ QUIC_BUG_IF(max_bw_estimate_bytes_per_second < 0)
+ << max_bw_estimate_bytes_per_second;
+ QUIC_BUG_IF(bw_estimate_bytes_per_second < 0) << bw_estimate_bytes_per_second;
+
+ CachedNetworkParameters cached_network_params;
+ cached_network_params.set_bandwidth_estimate_bytes_per_second(
+ bw_estimate_bytes_per_second);
+ cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
+ max_bw_estimate_bytes_per_second);
+ cached_network_params.set_max_bandwidth_timestamp_seconds(
+ max_bandwidth_timestamp);
+ cached_network_params.set_min_rtt_ms(
+ sent_packet_manager.GetRttStats()->min_rtt().ToMilliseconds());
+ cached_network_params.set_previous_connection_state(
+ bandwidth_recorder->EstimateRecordedDuringSlowStart()
+ ? CachedNetworkParameters::SLOW_START
+ : CachedNetworkParameters::CONGESTION_AVOIDANCE);
+ cached_network_params.set_timestamp(
+ connection()->clock()->WallNow().ToUNIXSeconds());
+ if (!serving_region_.empty()) {
+ cached_network_params.set_serving_region(serving_region_);
+ }
+
+ crypto_stream_->SendServerConfigUpdate(&cached_network_params);
+
+ connection()->OnSendConnectionState(cached_network_params);
+
+ last_scup_time_ = now;
+ last_scup_packet_number_ =
+ connection()->sent_packet_manager().GetLargestSentPacket();
+}
+
+bool QuicServerSessionBase::ShouldCreateIncomingStream(QuicStreamId id) {
+ if (!connection()->connected()) {
+ QUIC_BUG << "ShouldCreateIncomingStream called when disconnected";
+ return false;
+ }
+
+ if (QuicUtils::IsServerInitiatedStreamId(connection()->transport_version(),
+ id)) {
+ QUIC_DLOG(INFO) << "Invalid incoming even stream_id:" << id;
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Client created even numbered stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ return true;
+}
+
+bool QuicServerSessionBase::ShouldCreateOutgoingBidirectionalStream() {
+ if (!connection()->connected()) {
+ QUIC_BUG
+ << "ShouldCreateOutgoingBidirectionalStream called when disconnected";
+ return false;
+ }
+ if (!crypto_stream_->encryption_established()) {
+ QUIC_BUG << "Encryption not established so no outgoing stream created.";
+ return false;
+ }
+
+ if (!GetQuicReloadableFlag(quic_use_common_stream_check) &&
+ connection()->transport_version() != QUIC_VERSION_99) {
+ if (GetNumOpenOutgoingStreams() >=
+ stream_id_manager().max_open_outgoing_streams()) {
+ VLOG(1) << "No more streams should be created. "
+ << "Already " << GetNumOpenOutgoingStreams() << " open.";
+ return false;
+ }
+ }
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_common_stream_check, 2, 2);
+ return CanOpenNextOutgoingBidirectionalStream();
+}
+
+bool QuicServerSessionBase::ShouldCreateOutgoingUnidirectionalStream() {
+ if (!connection()->connected()) {
+ QUIC_BUG
+ << "ShouldCreateOutgoingUnidirectionalStream called when disconnected";
+ return false;
+ }
+ if (!crypto_stream_->encryption_established()) {
+ QUIC_BUG << "Encryption not established so no outgoing stream created.";
+ return false;
+ }
+
+ if (!GetQuicReloadableFlag(quic_use_common_stream_check) &&
+ connection()->transport_version() != QUIC_VERSION_99) {
+ if (GetNumOpenOutgoingStreams() >=
+ stream_id_manager().max_open_outgoing_streams()) {
+ VLOG(1) << "No more streams should be created. "
+ << "Already " << GetNumOpenOutgoingStreams() << " open.";
+ return false;
+ }
+ }
+
+ return CanOpenNextOutgoingUnidirectionalStream();
+}
+
+QuicCryptoServerStreamBase* QuicServerSessionBase::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+const QuicCryptoServerStreamBase* QuicServerSessionBase::GetCryptoStream()
+ const {
+ return crypto_stream_.get();
+}
+
+int32_t QuicServerSessionBase::BandwidthToCachedParameterBytesPerSecond(
+ const QuicBandwidth& bandwidth) {
+ return static_cast<int32_t>(std::min<int64_t>(
+ bandwidth.ToBytesPerSecond(), std::numeric_limits<uint32_t>::max()));
+}
+
+} // 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
new file mode 100644
index 00000000000..8f071f3fed4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base.h
@@ -0,0 +1,139 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A server specific QuicSession subclass.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SERVER_SESSION_BASE_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SERVER_SESSION_BASE_H_
+
+#include <cstdint>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicConfig;
+class QuicConnection;
+class QuicCryptoServerConfig;
+
+namespace test {
+class QuicServerSessionBasePeer;
+class QuicSimpleServerSessionPeer;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE QuicServerSessionBase : public QuicSpdySession {
+ public:
+ // Does not take ownership of |connection|. |crypto_config| must outlive the
+ // session. |helper| must outlive any created crypto streams.
+ QuicServerSessionBase(const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache);
+ QuicServerSessionBase(const QuicServerSessionBase&) = delete;
+ QuicServerSessionBase& operator=(const QuicServerSessionBase&) = delete;
+
+ // Override the base class to cancel any ongoing asychronous crypto.
+ void OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) override;
+
+ // Sends a server config update to the client, containing new bandwidth
+ // estimate.
+ void OnCongestionWindowChange(QuicTime now) override;
+
+ ~QuicServerSessionBase() override;
+
+ void Initialize() override;
+
+ const QuicCryptoServerStreamBase* crypto_stream() const {
+ return crypto_stream_.get();
+ }
+
+ // Override base class to process bandwidth related config received from
+ // client.
+ void OnConfigNegotiated() override;
+
+ void set_serving_region(const std::string& serving_region) {
+ serving_region_ = serving_region;
+ }
+
+ protected:
+ // QuicSession methods(override them with return type of QuicSpdyStream*):
+ QuicCryptoServerStreamBase* GetMutableCryptoStream() override;
+
+ const QuicCryptoServerStreamBase* GetCryptoStream() const override;
+
+ // If an outgoing stream can be created, return true.
+ // Return false when connection is closed or forward secure encryption hasn't
+ // established yet or number of server initiated streams already reaches the
+ // upper limit.
+ bool ShouldCreateOutgoingBidirectionalStream() override;
+ bool ShouldCreateOutgoingUnidirectionalStream() override;
+
+ // If we should create an incoming stream, returns true. Otherwise
+ // does error handling, including communicating the error to the client and
+ // possibly closing the connection, and returns false.
+ bool ShouldCreateIncomingStream(QuicStreamId id) override;
+
+ virtual QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) = 0;
+
+ const QuicCryptoServerConfig* crypto_config() { return crypto_config_; }
+
+ QuicCryptoServerStream::Helper* stream_helper() { return helper_; }
+
+ private:
+ friend class test::QuicServerSessionBasePeer;
+ friend class test::QuicSimpleServerSessionPeer;
+
+ const QuicCryptoServerConfig* crypto_config_;
+
+ // The cache which contains most recently compressed certs.
+ // Owned by QuicDispatcher.
+ QuicCompressedCertsCache* compressed_certs_cache_;
+
+ std::unique_ptr<QuicCryptoServerStreamBase> crypto_stream_;
+
+ // Pointer to the helper used to create crypto server streams. Must outlive
+ // streams created via CreateQuicCryptoServerStream.
+ QuicCryptoServerStream::Helper* helper_;
+
+ // Whether bandwidth resumption is enabled for this connection.
+ bool bandwidth_resumption_enabled_;
+
+ // The most recent bandwidth estimate sent to the client.
+ QuicBandwidth bandwidth_estimate_sent_to_client_;
+
+ // Text describing server location. Sent to the client as part of the bandwith
+ // estimate in the source-address token. Optional, can be left empty.
+ std::string serving_region_;
+
+ // Time at which we send the last SCUP to the client.
+ QuicTime last_scup_time_;
+
+ // Number of packets sent to the peer, at the time we last sent a SCUP.
+ QuicPacketNumber last_scup_packet_number_;
+
+ // Converts QuicBandwidth to an int32 bytes/second that can be
+ // stored in CachedNetworkParameters. TODO(jokulik): This function
+ // should go away once we fix http://b//27897982
+ int32_t BandwidthToCachedParameterBytesPerSecond(
+ const QuicBandwidth& bandwidth);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SERVER_SESSION_BASE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc
new file mode 100644
index 00000000000..e5945e1c9d6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc
@@ -0,0 +1,740 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_server_session_base_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+using testing::_;
+using testing::StrictMock;
+
+using testing::AtLeast;
+using testing::Return;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestServerSession : public QuicServerSessionBase {
+ public:
+ TestServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicServerSessionBase(config,
+ CurrentSupportedVersions(),
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+ ~TestServerSession() override { delete connection(); }
+
+ protected:
+ QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override {
+ if (!ShouldCreateIncomingStream(id)) {
+ return nullptr;
+ }
+ QuicSpdyStream* stream = new QuicSimpleServerStream(
+ id, this, BIDIRECTIONAL, quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ QuicSpdyStream* CreateIncomingStream(PendingStream pending) override {
+ QuicSpdyStream* stream = new QuicSimpleServerStream(
+ std::move(pending), this, BIDIRECTIONAL, quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ QuicSpdyStream* CreateOutgoingBidirectionalStream() override {
+ DCHECK(false);
+ return nullptr;
+ }
+
+ QuicSpdyStream* CreateOutgoingUnidirectionalStream() override {
+ if (!ShouldCreateOutgoingUnidirectionalStream()) {
+ return nullptr;
+ }
+
+ QuicSpdyStream* stream = new QuicSimpleServerStream(
+ GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL,
+ quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) override {
+ return new QuicCryptoServerStream(
+ crypto_config, compressed_certs_cache,
+ GetQuicReloadableFlag(enable_quic_stateless_reject_support), this,
+ stream_helper());
+ }
+
+ private:
+ QuicSimpleServerBackend*
+ quic_simple_server_backend_; // Owned by QuicServerSessionBaseTest
+};
+
+const size_t kMaxStreamsForTest = 10;
+
+class QuicServerSessionBaseTest : public QuicTestWithParam<ParsedQuicVersion> {
+ protected:
+ QuicServerSessionBaseTest()
+ : QuicServerSessionBaseTest(crypto_test_utils::ProofSourceForTesting()) {}
+
+ explicit QuicServerSessionBaseTest(std::unique_ptr<ProofSource> proof_source)
+ : crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ std::move(proof_source),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {
+ config_.SetMaxIncomingDynamicStreamsToSend(kMaxStreamsForTest);
+ QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(&config_,
+ kMaxStreamsForTest);
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+
+ ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam());
+ connection_ = new StrictMock<MockQuicConnection>(
+ &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions);
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ session_ = QuicMakeUnique<TestServerSession>(
+ config_, connection_, &owner_, &stream_helper_, &crypto_config_,
+ &compressed_certs_cache_, &memory_cache_backend_);
+ MockClock clock;
+ handshake_message_ = crypto_config_.AddDefaultConfig(
+ QuicRandom::GetInstance(), &clock,
+ QuicCryptoServerConfig::ConfigOptions());
+ session_->Initialize();
+ QuicSessionPeer::GetMutableCryptoStream(session_.get())
+ ->OnSuccessfulVersionNegotiation(supported_versions.front());
+ visitor_ = QuicConnectionPeer::GetVisitor(connection_);
+ }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
+ return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ QuicTransportVersion transport_version() const {
+ return connection_->transport_version();
+ }
+
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a
+ // one-way close. This method can be used to inject a STOP_SENDING, which
+ // would cause a close in the opposite direction. This allows tests to do the
+ // extra work to get a two-way (full) close where desired. Also sets up
+ // expects needed to ensure that the STOP_SENDING worked as expected.
+ void InjectStopSendingFrame(QuicStreamId stream_id,
+ QuicRstStreamErrorCode rst_stream_code) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Only needed for version 99/IETF QUIC. Noop otherwise.
+ return;
+ }
+ QuicStopSendingFrame stop_sending(
+ kInvalidControlFrameId, stream_id,
+ static_cast<QuicApplicationErrorCode>(rst_stream_code));
+ EXPECT_CALL(owner_, OnStopSendingReceived(_)).Times(1);
+ // Expect the RESET_STREAM that is generated in response to receiving a
+ // STOP_SENDING.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream_id, rst_stream_code));
+ session_->OnStopSendingFrame(stop_sending);
+ }
+
+ StrictMock<MockQuicSessionVisitor> owner_;
+ StrictMock<MockQuicCryptoServerStreamHelper> stream_helper_;
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicMemoryCacheBackend memory_cache_backend_;
+ std::unique_ptr<TestServerSession> session_;
+ std::unique_ptr<CryptoHandshakeMessage> handshake_message_;
+ QuicConnectionVisitorInterface* visitor_;
+};
+
+// Compares CachedNetworkParameters.
+MATCHER_P(EqualsProto, network_params, "") {
+ CachedNetworkParameters reference(network_params);
+ return (arg->bandwidth_estimate_bytes_per_second() ==
+ reference.bandwidth_estimate_bytes_per_second() &&
+ arg->bandwidth_estimate_bytes_per_second() ==
+ reference.bandwidth_estimate_bytes_per_second() &&
+ arg->max_bandwidth_estimate_bytes_per_second() ==
+ reference.max_bandwidth_estimate_bytes_per_second() &&
+ arg->max_bandwidth_timestamp_seconds() ==
+ reference.max_bandwidth_timestamp_seconds() &&
+ arg->min_rtt_ms() == reference.min_rtt_ms() &&
+ arg->previous_connection_state() ==
+ reference.previous_connection_state());
+}
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicServerSessionBaseTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+TEST_P(QuicServerSessionBaseTest, CloseStreamDueToReset) {
+ // Open a stream, then reset it.
+ // Send two bytes of payload to open it.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ session_->OnStreamFrame(data1);
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst1(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ if (transport_version() != QUIC_VERSION_99) {
+ // For non-version 99, the RESET_STREAM will do the full close.
+ // Set up expects accordingly.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst1);
+
+ // For version-99 will create and receive a stop-sending, completing
+ // the full-close expected by this test.
+ InjectStopSendingFrame(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+
+ // Send the same two bytes of payload in a new packet.
+ visitor_->OnStreamFrame(data1);
+
+ // The stream should not be re-opened.
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicServerSessionBaseTest, NeverOpenStreamDueToReset) {
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst1(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ if (transport_version() != QUIC_VERSION_99) {
+ // For non-version 99, the RESET_STREAM will do the full close.
+ // Set up expects accordingly.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst1);
+
+ // For version-99 will create and receive a stop-sending, completing
+ // the full-close expected by this test.
+ InjectStopSendingFrame(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+
+ // Send two bytes of payload.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ visitor_->OnStreamFrame(data1);
+
+ // The stream should never be opened, now that the reset is received.
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicServerSessionBaseTest, AcceptClosedStream) {
+ // Send (empty) compressed headers followed by two bytes of data.
+ QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("\1\0\0\0\0\0\0\0HT"));
+ QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(1), false, 0,
+ QuicStringPiece("\2\0\0\0\0\0\0\0HT"));
+ visitor_->OnStreamFrame(frame1);
+ visitor_->OnStreamFrame(frame2);
+ EXPECT_EQ(2u, session_->GetNumOpenIncomingStreams());
+
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ if (transport_version() != QUIC_VERSION_99) {
+ // For non-version 99, the RESET_STREAM will do the full close.
+ // Set up expects accordingly.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst);
+
+ // For version-99 will create and receive a stop-sending, completing
+ // the full-close expected by this test.
+ InjectStopSendingFrame(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+
+ // If we were tracking, we'd probably want to reject this because it's data
+ // past the reset point of stream 3. As it's a closed stream we just drop the
+ // data on the floor, but accept the packet because it has data for stream 5.
+ QuicStreamFrame frame3(GetNthClientInitiatedBidirectionalId(0), false, 2,
+ QuicStringPiece("TP"));
+ QuicStreamFrame frame4(GetNthClientInitiatedBidirectionalId(1), false, 2,
+ QuicStringPiece("TP"));
+ visitor_->OnStreamFrame(frame3);
+ visitor_->OnStreamFrame(frame4);
+ // The stream should never be opened, now that the reset is received.
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicServerSessionBaseTest, MaxOpenStreams) {
+ // Test that the server refuses if a client attempts to open too many data
+ // streams. For versions other than version 99, the server accepts slightly
+ // more than the negotiated stream limit to deal with rare cases where a
+ // client FIN/RST is lost.
+
+ session_->OnConfigNegotiated();
+ if (transport_version() != QUIC_VERSION_99) {
+ // The slightly increased stream limit is set during config negotiation. It
+ // is either an increase of 10 over negotiated limit, or a fixed percentage
+ // scaling, whichever is larger. Test both before continuing.
+ EXPECT_LT(kMaxStreamsMultiplier * kMaxStreamsForTest,
+ kMaxStreamsForTest + kMaxStreamsMinimumIncrement);
+ EXPECT_EQ(kMaxStreamsForTest + kMaxStreamsMinimumIncrement,
+ session_->max_open_incoming_bidirectional_streams());
+ }
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
+ // Open the max configured number of streams, should be no problem.
+ for (size_t i = 0; i < kMaxStreamsForTest; ++i) {
+ EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
+ session_.get(), stream_id));
+ stream_id += QuicUtils::StreamIdDelta(connection_->transport_version());
+ }
+
+ if (transport_version() != QUIC_VERSION_99) {
+ // Open more streams: server should accept slightly more than the limit.
+ // Excess streams are for non-version-99 only.
+ for (size_t i = 0; i < kMaxStreamsMinimumIncrement; ++i) {
+ EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
+ session_.get(), stream_id));
+ stream_id += QuicUtils::StreamIdDelta(connection_->transport_version());
+ }
+ }
+ // Now violate the server's internal stream limit.
+ stream_id += QuicUtils::StreamIdDelta(connection_->transport_version());
+
+ if (transport_version() != QUIC_VERSION_99) {
+ // For non-version 99, QUIC responds to an attempt to exceed the stream
+ // limit by resetting the stream.
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream_id, QUIC_REFUSED_STREAM));
+ } else {
+ // In version 99 QUIC responds to an attempt to exceed the stream limit by
+ // closing the connection.
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1);
+ }
+ // Even if the connection remains open, the stream creation should fail.
+ EXPECT_FALSE(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
+ session_.get(), stream_id));
+}
+
+TEST_P(QuicServerSessionBaseTest, MaxAvailableBidirectionalStreams) {
+ // Test that the server closes the connection if a client makes too many data
+ // streams available. The server accepts slightly more than the negotiated
+ // stream limit to deal with rare cases where a client FIN/RST is lost.
+
+ session_->OnConfigNegotiated();
+ const size_t kAvailableStreamLimit =
+ session_->MaxAvailableBidirectionalStreams();
+
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
+ session_.get(), GetNthClientInitiatedBidirectionalId(0)));
+
+ // Establish available streams up to the server's limit.
+ QuicStreamId next_id =
+ QuicUtils::StreamIdDelta(connection_->transport_version());
+ const int kLimitingStreamId =
+ GetNthClientInitiatedBidirectionalId(kAvailableStreamLimit + 1);
+ if (transport_version() != QUIC_VERSION_99) {
+ // This exceeds the stream limit. In versions other than 99
+ // this is allowed. Version 99 hews to the IETF spec and does
+ // not allow it.
+ EXPECT_TRUE(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
+ session_.get(), kLimitingStreamId));
+ // A further available stream will result in connection close.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _));
+ } else {
+ // A further available stream will result in connection close.
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ }
+
+ // This forces stream kLimitingStreamId + 2 to become available, which
+ // violates the quota.
+ EXPECT_FALSE(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
+ session_.get(), kLimitingStreamId + 2 * next_id));
+}
+
+TEST_P(QuicServerSessionBaseTest, GetEvenIncomingError) {
+ // Incoming streams on the server session must be odd.
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ EXPECT_EQ(nullptr,
+ QuicServerSessionBasePeer::GetOrCreateDynamicStream(
+ session_.get(), GetNthServerInitiatedUnidirectionalId(0)));
+}
+
+TEST_P(QuicServerSessionBaseTest, GetStreamDisconnected) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Don't create new streams if the connection is disconnected.
+ QuicConnectionPeer::TearDownLocalConnectionState(connection_);
+ EXPECT_QUIC_BUG(QuicServerSessionBasePeer::GetOrCreateDynamicStream(
+ session_.get(), GetNthClientInitiatedBidirectionalId(0)),
+ "ShouldCreateIncomingStream called when disconnected");
+}
+
+class MockQuicCryptoServerStream : public QuicCryptoServerStream {
+ public:
+ explicit MockQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicServerSessionBase* session,
+ QuicCryptoServerStream::Helper* helper)
+ : QuicCryptoServerStream(
+ crypto_config,
+ compressed_certs_cache,
+ GetQuicReloadableFlag(enable_quic_stateless_reject_support),
+ session,
+ helper) {}
+ MockQuicCryptoServerStream(const MockQuicCryptoServerStream&) = delete;
+ MockQuicCryptoServerStream& operator=(const MockQuicCryptoServerStream&) =
+ delete;
+ ~MockQuicCryptoServerStream() override {}
+
+ MOCK_METHOD1(SendServerConfigUpdate,
+ void(const CachedNetworkParameters* cached_network_parameters));
+};
+
+TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) {
+ // Test that bandwidth estimate updates are sent to the client, only when
+ // bandwidth resumption is enabled, the bandwidth estimate has changed
+ // sufficiently, enough time has passed,
+ // and we don't have any other data to write.
+
+ // Client has sent kBWRE connection option to trigger bandwidth resumption.
+ QuicTagVector copt;
+ copt.push_back(kBWRE);
+ QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt);
+ session_->OnConfigNegotiated();
+ EXPECT_TRUE(
+ QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
+
+ int32_t bandwidth_estimate_kbytes_per_second = 123;
+ int32_t max_bandwidth_estimate_kbytes_per_second = 134;
+ int32_t max_bandwidth_estimate_timestamp = 1122334455;
+ const std::string serving_region = "not a real region";
+ session_->set_serving_region(serving_region);
+
+ session_->UnregisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true);
+ QuicServerSessionBasePeer::SetCryptoStream(session_.get(), nullptr);
+ MockQuicCryptoServerStream* crypto_stream =
+ new MockQuicCryptoServerStream(&crypto_config_, &compressed_certs_cache_,
+ session_.get(), &stream_helper_);
+ QuicServerSessionBasePeer::SetCryptoStream(session_.get(), crypto_stream);
+ session_->RegisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true, QuicStream::kDefaultPriority);
+
+ // Set some initial bandwidth values.
+ QuicSentPacketManager* sent_packet_manager =
+ QuicConnectionPeer::GetSentPacketManager(session_->connection());
+ QuicSustainedBandwidthRecorder& bandwidth_recorder =
+ QuicSentPacketManagerPeer::GetBandwidthRecorder(sent_packet_manager);
+ // Seed an rtt measurement equal to the initial default rtt.
+ RttStats* rtt_stats =
+ const_cast<RttStats*>(sent_packet_manager->GetRttStats());
+ rtt_stats->UpdateRtt(rtt_stats->initial_rtt(), QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ QuicSustainedBandwidthRecorderPeer::SetBandwidthEstimate(
+ &bandwidth_recorder, bandwidth_estimate_kbytes_per_second);
+ QuicSustainedBandwidthRecorderPeer::SetMaxBandwidthEstimate(
+ &bandwidth_recorder, max_bandwidth_estimate_kbytes_per_second,
+ max_bandwidth_estimate_timestamp);
+ // Queue up some pending data.
+ session_->MarkConnectionLevelWriteBlocked(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()));
+ EXPECT_TRUE(session_->HasDataToWrite());
+
+ // There will be no update sent yet - not enough time has passed.
+ QuicTime now = QuicTime::Zero();
+ session_->OnCongestionWindowChange(now);
+
+ // Bandwidth estimate has now changed sufficiently but not enough time has
+ // passed to send a Server Config Update.
+ bandwidth_estimate_kbytes_per_second =
+ bandwidth_estimate_kbytes_per_second * 1.6;
+ session_->OnCongestionWindowChange(now);
+
+ // Bandwidth estimate has now changed sufficiently and enough time has passed,
+ // but not enough packets have been sent.
+ int64_t srtt_ms =
+ sent_packet_manager->GetRttStats()->smoothed_rtt().ToMilliseconds();
+ now = now + QuicTime::Delta::FromMilliseconds(
+ kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms);
+ session_->OnCongestionWindowChange(now);
+
+ // The connection no longer has pending data to be written.
+ session_->OnCanWrite();
+ EXPECT_FALSE(session_->HasDataToWrite());
+ session_->OnCongestionWindowChange(now);
+
+ // Bandwidth estimate has now changed sufficiently, enough time has passed,
+ // and enough packets have been sent.
+ SerializedPacket packet(
+ QuicPacketNumber(1) + kMinPacketsBetweenServerConfigUpdates,
+ PACKET_4BYTE_PACKET_NUMBER, nullptr, 1000, false, false);
+ sent_packet_manager->OnPacketSent(&packet, QuicPacketNumber(), now,
+ NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+
+ // Verify that the proto has exactly the values we expect.
+ CachedNetworkParameters expected_network_params;
+ expected_network_params.set_bandwidth_estimate_bytes_per_second(
+ bandwidth_recorder.BandwidthEstimate().ToBytesPerSecond());
+ expected_network_params.set_max_bandwidth_estimate_bytes_per_second(
+ bandwidth_recorder.MaxBandwidthEstimate().ToBytesPerSecond());
+ expected_network_params.set_max_bandwidth_timestamp_seconds(
+ bandwidth_recorder.MaxBandwidthTimestamp());
+ expected_network_params.set_min_rtt_ms(session_->connection()
+ ->sent_packet_manager()
+ .GetRttStats()
+ ->min_rtt()
+ .ToMilliseconds());
+ expected_network_params.set_previous_connection_state(
+ CachedNetworkParameters::CONGESTION_AVOIDANCE);
+ expected_network_params.set_timestamp(
+ session_->connection()->clock()->WallNow().ToUNIXSeconds());
+ expected_network_params.set_serving_region(serving_region);
+
+ EXPECT_CALL(*crypto_stream,
+ SendServerConfigUpdate(EqualsProto(expected_network_params)))
+ .Times(1);
+ EXPECT_CALL(*connection_, OnSendConnectionState(_)).Times(1);
+ session_->OnCongestionWindowChange(now);
+}
+
+TEST_P(QuicServerSessionBaseTest, BandwidthResumptionExperiment) {
+ if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) {
+ // This test relies on resumption, which is not currently supported by the
+ // TLS handshake.
+ // TODO(nharper): Add support for resumption to the TLS handshake.
+ return;
+ }
+ // Test that if a client provides a CachedNetworkParameters with the same
+ // serving region as the current server, and which was made within an hour of
+ // now, that this data is passed down to the send algorithm.
+
+ // Client has sent kBWRE connection option to trigger bandwidth resumption.
+ QuicTagVector copt;
+ copt.push_back(kBWRE);
+ QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt);
+
+ const std::string kTestServingRegion = "a serving region";
+ session_->set_serving_region(kTestServingRegion);
+
+ // Set the time to be one hour + one second from the 0 baseline.
+ connection_->AdvanceTime(
+ QuicTime::Delta::FromSeconds(kNumSecondsPerHour + 1));
+
+ QuicCryptoServerStream* crypto_stream = static_cast<QuicCryptoServerStream*>(
+ QuicSessionPeer::GetMutableCryptoStream(session_.get()));
+
+ // No effect if no CachedNetworkParameters provided.
+ EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(0);
+ session_->OnConfigNegotiated();
+
+ // No effect if CachedNetworkParameters provided, but different serving
+ // regions.
+ CachedNetworkParameters cached_network_params;
+ cached_network_params.set_bandwidth_estimate_bytes_per_second(1);
+ cached_network_params.set_serving_region("different serving region");
+ crypto_stream->SetPreviousCachedNetworkParams(cached_network_params);
+ EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(0);
+ session_->OnConfigNegotiated();
+
+ // Same serving region, but timestamp is too old, should have no effect.
+ cached_network_params.set_serving_region(kTestServingRegion);
+ cached_network_params.set_timestamp(0);
+ crypto_stream->SetPreviousCachedNetworkParams(cached_network_params);
+ EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(0);
+ session_->OnConfigNegotiated();
+
+ // Same serving region, and timestamp is recent: estimate is stored.
+ cached_network_params.set_timestamp(
+ connection_->clock()->WallNow().ToUNIXSeconds());
+ crypto_stream->SetPreviousCachedNetworkParams(cached_network_params);
+ EXPECT_CALL(*connection_, ResumeConnectionState(_, _)).Times(1);
+ session_->OnConfigNegotiated();
+}
+
+TEST_P(QuicServerSessionBaseTest, BandwidthMaxEnablesResumption) {
+ EXPECT_FALSE(
+ QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
+
+ // Client has sent kBWMX connection option to trigger bandwidth resumption.
+ QuicTagVector copt;
+ copt.push_back(kBWMX);
+ QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt);
+ session_->OnConfigNegotiated();
+ EXPECT_TRUE(
+ QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
+}
+
+TEST_P(QuicServerSessionBaseTest, NoBandwidthResumptionByDefault) {
+ EXPECT_FALSE(
+ QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
+ session_->OnConfigNegotiated();
+ EXPECT_FALSE(
+ QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
+}
+
+// Tests which check the lifetime management of data members of
+// QuicCryptoServerStream objects when async GetProof is in use.
+class StreamMemberLifetimeTest : public QuicServerSessionBaseTest {
+ public:
+ StreamMemberLifetimeTest()
+ : QuicServerSessionBaseTest(
+ std::unique_ptr<FakeProofSource>(new FakeProofSource())),
+ crypto_config_peer_(&crypto_config_) {
+ GetFakeProofSource()->Activate();
+ }
+
+ FakeProofSource* GetFakeProofSource() const {
+ return static_cast<FakeProofSource*>(crypto_config_peer_.GetProofSource());
+ }
+
+ private:
+ QuicCryptoServerConfigPeer crypto_config_peer_;
+};
+
+INSTANTIATE_TEST_SUITE_P(StreamMemberLifetimeTests,
+ StreamMemberLifetimeTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+// Trigger an operation which causes an async invocation of
+// ProofSource::GetProof. Delay the completion of the operation until after the
+// stream has been destroyed, and verify that there are no memory bugs.
+TEST_P(StreamMemberLifetimeTest, Basic) {
+ if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) {
+ // This test depends on the QUIC crypto protocol, so it is disabled for the
+ // TLS handshake.
+ // TODO(nharper): Fix this test so it doesn't rely on QUIC crypto.
+ return;
+ }
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true);
+
+ const QuicClock* clock = helper_.GetClock();
+ CryptoHandshakeMessage chlo = crypto_test_utils::GenerateDefaultInchoateCHLO(
+ clock, GetParam().transport_version, &crypto_config_);
+ chlo.SetVector(kCOPT, QuicTagVector{kSREJ});
+ std::vector<ParsedQuicVersion> packet_version_list = {GetParam()};
+ std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
+ TestConnectionId(1), EmptyQuicConnectionId(), true, false, 1,
+ std::string(chlo.GetSerialized().AsStringPiece()), CONNECTION_ID_PRESENT,
+ CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &packet_version_list));
+
+ EXPECT_CALL(stream_helper_, CanAcceptClientHello(_, _, _, _, _))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(stream_helper_, GenerateConnectionIdForReject(_, _))
+ .WillOnce(testing::Return(TestConnectionId(12345)));
+
+ // Set the current packet
+ QuicConnectionPeer::SetCurrentPacket(session_->connection(),
+ packet->AsStringPiece());
+
+ // Yes, this is horrible. But it's the easiest way to trigger the behavior we
+ // need to exercise.
+ QuicCryptoServerStreamBase* crypto_stream =
+ const_cast<QuicCryptoServerStreamBase*>(session_->crypto_stream());
+
+ // Feed the CHLO into the crypto stream, which will trigger a call to
+ // ProofSource::GetProof
+ crypto_test_utils::SendHandshakeMessageToStream(crypto_stream, chlo,
+ Perspective::IS_CLIENT);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Destroy the stream
+ session_.reset();
+
+ // Allow the async ProofSource::GetProof call to complete. Verify (under
+ // memory access checkers) that this does not result in accesses to any
+ // freed memory from the session or its subobjects.
+ GetFakeProofSource()->InvokePendingCallback(0);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..458b8a05e81
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+QuicSpdyClientSession::QuicSpdyClientSession(
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config,
+ QuicClientPushPromiseIndex* push_promise_index)
+ : QuicSpdyClientSessionBase(connection,
+ push_promise_index,
+ config,
+ supported_versions),
+ server_id_(server_id),
+ crypto_config_(crypto_config),
+ respect_goaway_(true) {}
+
+QuicSpdyClientSession::~QuicSpdyClientSession() = default;
+
+void QuicSpdyClientSession::Initialize() {
+ crypto_stream_ = CreateQuicCryptoStream();
+ QuicSpdyClientSessionBase::Initialize();
+}
+
+void QuicSpdyClientSession::OnProofValid(
+ const QuicCryptoClientConfig::CachedState& /*cached*/) {}
+
+void QuicSpdyClientSession::OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& /*verify_details*/) {}
+
+bool QuicSpdyClientSession::ShouldCreateOutgoingBidirectionalStream() {
+ if (!crypto_stream_->encryption_established()) {
+ QUIC_DLOG(INFO) << "Encryption not active so no outgoing stream created.";
+ return false;
+ }
+ if (!GetQuicReloadableFlag(quic_use_common_stream_check) &&
+ connection()->transport_version() != QUIC_VERSION_99) {
+ if (GetNumOpenOutgoingStreams() >=
+ stream_id_manager().max_open_outgoing_streams()) {
+ QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
+ << "Already " << GetNumOpenOutgoingStreams() << " open.";
+ return false;
+ }
+ if (goaway_received() && respect_goaway_) {
+ QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
+ << "Already received goaway.";
+ return false;
+ }
+ return true;
+ }
+ if (goaway_received() && respect_goaway_) {
+ QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
+ << "Already received goaway.";
+ return false;
+ }
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_common_stream_check, 1, 2);
+ return CanOpenNextOutgoingBidirectionalStream();
+}
+
+bool QuicSpdyClientSession::ShouldCreateOutgoingUnidirectionalStream() {
+ QUIC_BUG << "Try to create outgoing unidirectional client data streams";
+ return false;
+}
+
+QuicSpdyClientStream*
+QuicSpdyClientSession::CreateOutgoingBidirectionalStream() {
+ if (!ShouldCreateOutgoingBidirectionalStream()) {
+ return nullptr;
+ }
+ std::unique_ptr<QuicSpdyClientStream> stream = CreateClientStream();
+ QuicSpdyClientStream* stream_ptr = stream.get();
+ ActivateStream(std::move(stream));
+ return stream_ptr;
+}
+
+QuicSpdyClientStream*
+QuicSpdyClientSession::CreateOutgoingUnidirectionalStream() {
+ QUIC_BUG << "Try to create outgoing unidirectional client data streams";
+ return nullptr;
+}
+
+std::unique_ptr<QuicSpdyClientStream>
+QuicSpdyClientSession::CreateClientStream() {
+ return QuicMakeUnique<QuicSpdyClientStream>(
+ GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL);
+}
+
+QuicCryptoClientStreamBase* QuicSpdyClientSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+const QuicCryptoClientStreamBase* QuicSpdyClientSession::GetCryptoStream()
+ const {
+ return crypto_stream_.get();
+}
+
+void QuicSpdyClientSession::CryptoConnect() {
+ DCHECK(flow_controller());
+ crypto_stream_->CryptoConnect();
+}
+
+int QuicSpdyClientSession::GetNumSentClientHellos() const {
+ return crypto_stream_->num_sent_client_hellos();
+}
+
+int QuicSpdyClientSession::GetNumReceivedServerConfigUpdates() const {
+ return crypto_stream_->num_scup_messages_received();
+}
+
+bool QuicSpdyClientSession::ShouldCreateIncomingStream(QuicStreamId id) {
+ if (!connection()->connected()) {
+ QUIC_BUG << "ShouldCreateIncomingStream called when disconnected";
+ return false;
+ }
+ if (goaway_received() && respect_goaway_) {
+ QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
+ << "Already received goaway.";
+ return false;
+ }
+ if (QuicUtils::IsClientInitiatedStreamId(connection()->transport_version(),
+ id) ||
+ (connection()->transport_version() == QUIC_VERSION_99 &&
+ QuicUtils::IsBidirectionalStreamId(id))) {
+ QUIC_LOG(WARNING) << "Received invalid push stream id " << id;
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID,
+ "Server created non write unidirectional stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ return true;
+}
+
+QuicSpdyStream* QuicSpdyClientSession::CreateIncomingStream(
+ PendingStream pending) {
+ QuicSpdyStream* stream =
+ new QuicSpdyClientStream(std::move(pending), this, READ_UNIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+}
+
+QuicSpdyStream* QuicSpdyClientSession::CreateIncomingStream(QuicStreamId id) {
+ if (!ShouldCreateIncomingStream(id)) {
+ return nullptr;
+ }
+ QuicSpdyStream* stream =
+ new QuicSpdyClientStream(id, this, READ_UNIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+}
+
+std::unique_ptr<QuicCryptoClientStreamBase>
+QuicSpdyClientSession::CreateQuicCryptoStream() {
+ return QuicMakeUnique<QuicCryptoClientStream>(
+ server_id_, this,
+ crypto_config_->proof_verifier()->CreateDefaultContext(), crypto_config_,
+ this);
+}
+
+bool QuicSpdyClientSession::IsAuthorized(const std::string& authority) {
+ return true;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..747033a2fe8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A client specific QuicSession subclass.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_H_
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicConnection;
+class QuicServerId;
+
+class QuicSpdyClientSession : public QuicSpdyClientSessionBase {
+ public:
+ // Takes ownership of |connection|. Caller retains ownership of
+ // |promised_by_url|.
+ QuicSpdyClientSession(const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config,
+ QuicClientPushPromiseIndex* push_promise_index);
+ QuicSpdyClientSession(const QuicSpdyClientSession&) = delete;
+ QuicSpdyClientSession& operator=(const QuicSpdyClientSession&) = delete;
+ ~QuicSpdyClientSession() override;
+ // Set up the QuicSpdyClientSession. Must be called prior to use.
+ void Initialize() override;
+
+ // QuicSession methods:
+ QuicSpdyClientStream* CreateOutgoingBidirectionalStream() override;
+ QuicSpdyClientStream* CreateOutgoingUnidirectionalStream() override;
+ QuicCryptoClientStreamBase* GetMutableCryptoStream() override;
+ const QuicCryptoClientStreamBase* GetCryptoStream() const override;
+
+ bool IsAuthorized(const std::string& authority) override;
+
+ // QuicSpdyClientSessionBase methods:
+ void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
+ void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) override;
+
+ // Performs a crypto handshake with the server.
+ virtual void CryptoConnect();
+
+ // Returns the number of client hello messages that have been sent on the
+ // crypto stream. If the handshake has completed then this is one greater
+ // than the number of round-trips needed for the handshake.
+ int GetNumSentClientHellos() const;
+
+ int GetNumReceivedServerConfigUpdates() const;
+
+ void set_respect_goaway(bool respect_goaway) {
+ respect_goaway_ = respect_goaway;
+ }
+
+ protected:
+ // QuicSession methods:
+ QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override;
+ QuicSpdyStream* CreateIncomingStream(PendingStream pending) override;
+ // If an outgoing stream can be created, return true.
+ bool ShouldCreateOutgoingBidirectionalStream() override;
+ bool ShouldCreateOutgoingUnidirectionalStream() override;
+
+ // If an incoming stream can be created, return true.
+ // TODO(fayang): move this up to QuicSpdyClientSessionBase.
+ bool ShouldCreateIncomingStream(QuicStreamId id) override;
+
+ // Create the crypto stream. Called by Initialize().
+ virtual std::unique_ptr<QuicCryptoClientStreamBase> CreateQuicCryptoStream();
+
+ // Unlike CreateOutgoingBidirectionalStream, which applies a bunch of
+ // sanity checks, this simply returns a new QuicSpdyClientStream. This may be
+ // used by subclasses which want to use a subclass of QuicSpdyClientStream for
+ // streams but wish to use the sanity checks in
+ // CreateOutgoingBidirectionalStream.
+ virtual std::unique_ptr<QuicSpdyClientStream> CreateClientStream();
+
+ const QuicServerId& server_id() { return server_id_; }
+ QuicCryptoClientConfig* crypto_config() { return crypto_config_; }
+
+ private:
+ std::unique_ptr<QuicCryptoClientStreamBase> crypto_stream_;
+ QuicServerId server_id_;
+ QuicCryptoClientConfig* crypto_config_;
+
+ // If this is set to false, the client will ignore server GOAWAYs and allow
+ // the creation of streams regardless of the high chance they will fail.
+ bool respect_goaway_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc
new file mode 100644
index 00000000000..d388d2e1931
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc
@@ -0,0 +1,210 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+QuicSpdyClientSessionBase::QuicSpdyClientSessionBase(
+ QuicConnection* connection,
+ QuicClientPushPromiseIndex* push_promise_index,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions)
+ : QuicSpdySession(connection, nullptr, config, supported_versions),
+ push_promise_index_(push_promise_index),
+ largest_promised_stream_id_(
+ QuicUtils::GetInvalidStreamId(connection->transport_version())) {}
+
+QuicSpdyClientSessionBase::~QuicSpdyClientSessionBase() {
+ // all promised streams for this session
+ for (auto& it : promised_by_id_) {
+ QUIC_DVLOG(1) << "erase stream " << it.first << " url " << it.second->url();
+ push_promise_index_->promised_by_url()->erase(it.second->url());
+ }
+ delete connection();
+}
+
+void QuicSpdyClientSessionBase::OnConfigNegotiated() {
+ QuicSpdySession::OnConfigNegotiated();
+}
+
+void QuicSpdyClientSessionBase::OnCryptoHandshakeEvent(
+ CryptoHandshakeEvent event) {
+ QuicSpdySession::OnCryptoHandshakeEvent(event);
+}
+
+void QuicSpdyClientSessionBase::OnInitialHeadersComplete(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock& response_headers) {
+ // Note that the strong ordering of the headers stream means that
+ // QuicSpdyClientStream::OnPromiseHeadersComplete must have already
+ // been called (on the associated stream) if this is a promised
+ // stream. However, this stream may not have existed at this time,
+ // hence the need to query the session.
+ QuicClientPromisedInfo* promised = GetPromisedById(stream_id);
+ if (!promised)
+ return;
+
+ promised->OnResponseHeaders(response_headers);
+}
+
+void QuicSpdyClientSessionBase::OnPromiseHeaderList(
+ QuicStreamId stream_id,
+ QuicStreamId promised_stream_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ if (QuicContainsKey(static_streams(), stream_id)) {
+ connection()->CloseConnection(
+ QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ if (promised_stream_id !=
+ QuicUtils::GetInvalidStreamId(connection()->transport_version()) &&
+ largest_promised_stream_id_ !=
+ QuicUtils::GetInvalidStreamId(connection()->transport_version()) &&
+ promised_stream_id <= largest_promised_stream_id_) {
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID,
+ "Received push stream id lesser or equal to the"
+ " last accepted before",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ if (!IsIncomingStream(promised_stream_id)) {
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received push stream id for outgoing stream.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ largest_promised_stream_id_ = promised_stream_id;
+
+ QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
+ }
+ stream->OnPromiseHeaderList(promised_stream_id, frame_len, header_list);
+}
+
+bool QuicSpdyClientSessionBase::HandlePromised(QuicStreamId /* associated_id */,
+ QuicStreamId promised_id,
+ const SpdyHeaderBlock& headers) {
+ // Due to pathalogical packet re-ordering, it is possible that
+ // frames for the promised stream have already arrived, and the
+ // promised stream could be active or closed.
+ if (IsClosedStream(promised_id)) {
+ // There was a RST on the data stream already, perhaps
+ // QUIC_REFUSED_STREAM?
+ QUIC_DVLOG(1) << "Promise ignored for stream " << promised_id
+ << " that is already closed";
+ return false;
+ }
+
+ if (push_promise_index_->promised_by_url()->size() >= get_max_promises()) {
+ QUIC_DVLOG(1) << "Too many promises, rejecting promise for stream "
+ << promised_id;
+ ResetPromised(promised_id, QUIC_REFUSED_STREAM);
+ return false;
+ }
+
+ const std::string url = SpdyUtils::GetPromisedUrlFromHeaders(headers);
+ QuicClientPromisedInfo* old_promised = GetPromisedByUrl(url);
+ if (old_promised) {
+ QUIC_DVLOG(1) << "Promise for stream " << promised_id
+ << " is duplicate URL " << url
+ << " of previous promise for stream " << old_promised->id();
+ ResetPromised(promised_id, QUIC_DUPLICATE_PROMISE_URL);
+ return false;
+ }
+
+ if (GetPromisedById(promised_id)) {
+ // OnPromiseHeadersComplete() would have closed the connection if
+ // promised id is a duplicate.
+ QUIC_BUG << "Duplicate promise for id " << promised_id;
+ return false;
+ }
+
+ QuicClientPromisedInfo* promised =
+ new QuicClientPromisedInfo(this, promised_id, url);
+ std::unique_ptr<QuicClientPromisedInfo> promised_owner(promised);
+ promised->Init();
+ QUIC_DVLOG(1) << "stream " << promised_id << " emplace url " << url;
+ (*push_promise_index_->promised_by_url())[url] = promised;
+ promised_by_id_[promised_id] = std::move(promised_owner);
+ bool result = promised->OnPromiseHeaders(headers);
+ if (result) {
+ DCHECK(promised_by_id_.find(promised_id) != promised_by_id_.end());
+ }
+ return result;
+}
+
+QuicClientPromisedInfo* QuicSpdyClientSessionBase::GetPromisedByUrl(
+ const std::string& url) {
+ auto it = push_promise_index_->promised_by_url()->find(url);
+ if (it != push_promise_index_->promised_by_url()->end()) {
+ return it->second;
+ }
+ return nullptr;
+}
+
+QuicClientPromisedInfo* QuicSpdyClientSessionBase::GetPromisedById(
+ const QuicStreamId id) {
+ auto it = promised_by_id_.find(id);
+ if (it != promised_by_id_.end()) {
+ return it->second.get();
+ }
+ return nullptr;
+}
+
+QuicSpdyStream* QuicSpdyClientSessionBase::GetPromisedStream(
+ const QuicStreamId id) {
+ DynamicStreamMap::iterator it = dynamic_streams().find(id);
+ if (it != dynamic_streams().end()) {
+ return static_cast<QuicSpdyStream*>(it->second.get());
+ }
+ return nullptr;
+}
+
+void QuicSpdyClientSessionBase::DeletePromised(
+ QuicClientPromisedInfo* promised) {
+ push_promise_index_->promised_by_url()->erase(promised->url());
+ // Since promised_by_id_ contains the unique_ptr, this will destroy
+ // promised.
+ promised_by_id_.erase(promised->id());
+ headers_stream()->MaybeReleaseSequencerBuffer();
+}
+
+void QuicSpdyClientSessionBase::OnPushStreamTimedOut(QuicStreamId stream_id) {}
+
+void QuicSpdyClientSessionBase::ResetPromised(
+ QuicStreamId id,
+ QuicRstStreamErrorCode error_code) {
+ SendRstStream(id, error_code, 0);
+ if (!IsOpenStream(id)) {
+ MaybeIncreaseLargestPeerStreamId(id);
+ }
+}
+
+void QuicSpdyClientSessionBase::CloseStreamInner(QuicStreamId stream_id,
+ bool locally_reset) {
+ QuicSpdySession::CloseStreamInner(stream_id, locally_reset);
+ headers_stream()->MaybeReleaseSequencerBuffer();
+}
+
+bool QuicSpdyClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() {
+ return !HasActiveRequestStreams() && promised_by_id_.empty();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h
new file mode 100644
index 00000000000..aec5e75947f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h
@@ -0,0 +1,141 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_BASE_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_BASE_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicClientPromisedInfo;
+class QuicClientPushPromiseIndex;
+class QuicSpdyClientStream;
+
+// For client/http layer code. Lookup promised streams based on
+// matching promised request url. The same map can be shared across
+// multiple sessions, since cross-origin pushes are allowed (subject
+// to authority constraints). Clients should use this map to enforce
+// session affinity for requests corresponding to cross-origin push
+// promised streams.
+using QuicPromisedByUrlMap =
+ QuicUnorderedMap<std::string, QuicClientPromisedInfo*>;
+
+// The maximum time a promises stream can be reserved without being
+// claimed by a client request.
+const int64_t kPushPromiseTimeoutSecs = 60;
+
+// Base class for all client-specific QuicSession subclasses.
+class QUIC_EXPORT_PRIVATE QuicSpdyClientSessionBase
+ : public QuicSpdySession,
+ public QuicCryptoClientStream::ProofHandler {
+ public:
+ // Takes ownership of |connection|. Caller retains ownership of
+ // |promised_by_url|.
+ QuicSpdyClientSessionBase(QuicConnection* connection,
+ QuicClientPushPromiseIndex* push_promise_index,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions);
+ QuicSpdyClientSessionBase(const QuicSpdyClientSessionBase&) = delete;
+ QuicSpdyClientSessionBase& operator=(const QuicSpdyClientSessionBase&) =
+ delete;
+
+ ~QuicSpdyClientSessionBase() override;
+
+ void OnConfigNegotiated() override;
+
+ // Override base class to set FEC policy before any data is sent by client.
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
+ // Called by |headers_stream_| when push promise headers have been
+ // completely received.
+ void OnPromiseHeaderList(QuicStreamId stream_id,
+ QuicStreamId promised_stream_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list) override;
+
+ // Called by |QuicSpdyClientStream| on receipt of response headers,
+ // needed to detect promised server push streams, as part of
+ // client-request to push-stream rendezvous.
+ void OnInitialHeadersComplete(QuicStreamId stream_id,
+ const spdy::SpdyHeaderBlock& response_headers);
+
+ // Called by |QuicSpdyClientStream| on receipt of PUSH_PROMISE, does
+ // some session level validation and creates the
+ // |QuicClientPromisedInfo| inserting into maps by (promised) id and
+ // url. Returns true if a new push promise is accepted. Resets the promised
+ // stream and returns false otherwise.
+ virtual bool HandlePromised(QuicStreamId associated_id,
+ QuicStreamId promised_id,
+ const spdy::SpdyHeaderBlock& headers);
+
+ // For cross-origin server push, this should verify the server is
+ // authoritative per [RFC2818], Section 3. Roughly, subjectAltName
+ // list in the certificate should contain a matching DNS name, or IP
+ // address. |hostname| is derived from the ":authority" header field of
+ // the PUSH_PROMISE frame, port if present there will be dropped.
+ virtual bool IsAuthorized(const std::string& hostname) = 0;
+
+ // Session retains ownership.
+ QuicClientPromisedInfo* GetPromisedByUrl(const std::string& url);
+ // Session retains ownership.
+ QuicClientPromisedInfo* GetPromisedById(const QuicStreamId id);
+
+ //
+ QuicSpdyStream* GetPromisedStream(const QuicStreamId id);
+
+ // Removes |promised| from the maps by url.
+ void ErasePromisedByUrl(QuicClientPromisedInfo* promised);
+
+ // Removes |promised| from the maps by url and id and destroys
+ // promised.
+ virtual void DeletePromised(QuicClientPromisedInfo* promised);
+
+ virtual void OnPushStreamTimedOut(QuicStreamId stream_id);
+
+ // Sends Rst for the stream, and makes sure that future calls to
+ // IsClosedStream(id) return true, which ensures that any subsequent
+ // frames related to this stream will be ignored (modulo flow
+ // control accounting).
+ void ResetPromised(QuicStreamId id, QuicRstStreamErrorCode error_code);
+
+ // Release headers stream's sequencer buffer if it's empty.
+ void CloseStreamInner(QuicStreamId stream_id, bool locally_reset) override;
+
+ // Returns true if there are no active requests and no promised streams.
+ bool ShouldReleaseHeadersStreamSequencerBuffer() override;
+
+ size_t get_max_promises() const {
+ return max_open_incoming_unidirectional_streams() *
+ kMaxPromisedStreamsMultiplier;
+ }
+
+ QuicClientPushPromiseIndex* push_promise_index() {
+ return push_promise_index_;
+ }
+
+ private:
+ // For QuicSpdyClientStream to detect that a response corresponds to a
+ // promise.
+ using QuicPromisedByIdMap =
+ QuicUnorderedMap<QuicStreamId, std::unique_ptr<QuicClientPromisedInfo>>;
+
+ // As per rfc7540, section 10.5: track promise streams in "reserved
+ // (remote)". The primary key is URL from the promise request
+ // headers. The promised stream id is a secondary key used to get
+ // promise info when the response headers of the promised stream
+ // arrive.
+ QuicClientPushPromiseIndex* push_promise_index_;
+ QuicPromisedByIdMap promised_by_id_;
+ QuicStreamId largest_promised_stream_id_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_SESSION_BASE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc
new file mode 100644
index 00000000000..9a99ca7bfc8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_test.cc
@@ -0,0 +1,803 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using spdy::SpdyHeaderBlock;
+using testing::_;
+using testing::AnyNumber;
+using testing::Invoke;
+using testing::Truly;
+
+namespace quic {
+namespace test {
+namespace {
+
+const char kServerHostname[] = "test.example.com";
+const uint16_t kPort = 443;
+
+class TestQuicSpdyClientSession : public QuicSpdyClientSession {
+ public:
+ explicit TestQuicSpdyClientSession(
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config,
+ QuicClientPushPromiseIndex* push_promise_index)
+ : QuicSpdyClientSession(config,
+ supported_versions,
+ connection,
+ server_id,
+ crypto_config,
+ push_promise_index) {}
+
+ std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override {
+ return QuicMakeUnique<MockQuicSpdyClientStream>(
+ GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL);
+ }
+
+ MockQuicSpdyClientStream* CreateIncomingStream(QuicStreamId id) override {
+ if (!ShouldCreateIncomingStream(id)) {
+ return nullptr;
+ }
+ MockQuicSpdyClientStream* stream =
+ new MockQuicSpdyClientStream(id, this, READ_UNIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+};
+
+class QuicSpdyClientSessionTest : public QuicTestWithParam<ParsedQuicVersion> {
+ protected:
+ QuicSpdyClientSessionTest()
+ : crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx()),
+ promised_stream_id_(
+ QuicUtils::GetInvalidStreamId(GetParam().transport_version)),
+ associated_stream_id_(
+ QuicUtils::GetInvalidStreamId(GetParam().transport_version)) {
+ Initialize();
+ // Advance the time, because timers do not like uninitialized times.
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ ~QuicSpdyClientSessionTest() override {
+ // Session must be destroyed before promised_by_url_
+ session_.reset(nullptr);
+ }
+
+ void Initialize() {
+ session_.reset();
+ connection_ = new PacketSavingConnection(&helper_, &alarm_factory_,
+ Perspective::IS_CLIENT,
+ SupportedVersions(GetParam()));
+ session_ = QuicMakeUnique<TestQuicSpdyClientSession>(
+ DefaultQuicConfig(), SupportedVersions(GetParam()), connection_,
+ QuicServerId(kServerHostname, kPort, false), &crypto_config_,
+ &push_promise_index_);
+ session_->Initialize();
+ push_promise_[":path"] = "/bar";
+ push_promise_[":authority"] = "www.google.com";
+ push_promise_[":version"] = "HTTP/1.1";
+ push_promise_[":method"] = "GET";
+ push_promise_[":scheme"] = "https";
+ promise_url_ = SpdyUtils::GetPromisedUrlFromHeaders(push_promise_);
+ promised_stream_id_ = GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), 0);
+ associated_stream_id_ = GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0);
+ }
+
+ // The function ensures that A) the max stream id frames get properly deleted
+ // (since the test uses a 'did we leak memory' check ... if we just lose the
+ // frame, the test fails) and B) returns true (instead of the default, false)
+ // which ensures that the rest of the system thinks that the frame actually
+ // was transmitted.
+ bool ClearMaxStreamIdControlFrame(const QuicFrame& frame) {
+ if (frame.type == MAX_STREAM_ID_FRAME) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+ return false;
+ }
+
+ public:
+ bool ClearStreamIdBlockedControlFrame(const QuicFrame& frame) {
+ if (frame.type == STREAM_ID_BLOCKED_FRAME) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+ return false;
+ }
+
+ protected:
+ void CompleteCryptoHandshake() {
+ CompleteCryptoHandshake(kDefaultMaxStreamsPerConnection);
+ }
+
+ void CompleteCryptoHandshake(uint32_t server_max_incoming_streams) {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(testing::AnyNumber())
+ .WillRepeatedly(Invoke(
+ this, &QuicSpdyClientSessionTest::ClearMaxStreamIdControlFrame));
+ }
+ session_->CryptoConnect();
+ QuicCryptoClientStream* stream = static_cast<QuicCryptoClientStream*>(
+ session_->GetMutableCryptoStream());
+ crypto_test_utils::FakeServerOptions options;
+ QuicConfig config = DefaultQuicConfig();
+ config.SetMaxIncomingDynamicStreamsToSend(server_max_incoming_streams);
+ crypto_test_utils::HandshakeWithFakeServer(
+ &config, &helper_, &alarm_factory_, connection_, stream, options);
+ }
+
+ QuicCryptoClientConfig crypto_config_;
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ PacketSavingConnection* connection_;
+ std::unique_ptr<TestQuicSpdyClientSession> session_;
+ QuicClientPushPromiseIndex push_promise_index_;
+ SpdyHeaderBlock push_promise_;
+ std::string promise_url_;
+ QuicStreamId promised_stream_id_;
+ QuicStreamId associated_stream_id_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSpdyClientSessionTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSpdyClientSessionTest, CryptoConnect) {
+ CompleteCryptoHandshake();
+}
+
+TEST_P(QuicSpdyClientSessionTest, NoEncryptionAfterInitialEncryption) {
+ if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) {
+ // This test relies on resumption and is QUIC crypto specific, so it is
+ // disabled for TLS.
+ // TODO(nharper): Add support for resumption to the TLS handshake, and fix
+ // this test to not rely on QUIC crypto.
+ return;
+ }
+ // Complete a handshake in order to prime the crypto config for 0-RTT.
+ CompleteCryptoHandshake();
+
+ // Now create a second session using the same crypto config.
+ Initialize();
+
+ EXPECT_CALL(*connection_, OnCanWrite());
+ // Starting the handshake should move immediately to encryption
+ // established and will allow streams to be created.
+ session_->CryptoConnect();
+ EXPECT_TRUE(session_->IsEncryptionEstablished());
+ QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream();
+ ASSERT_TRUE(stream != nullptr);
+ if (!QuicVersionUsesCryptoFrames(GetParam().transport_version)) {
+ EXPECT_NE(QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ stream->id());
+ }
+
+ // Process an "inchoate" REJ from the server which will cause
+ // an inchoate CHLO to be sent and will leave the encryption level
+ // at NONE.
+ CryptoHandshakeMessage rej;
+ crypto_test_utils::FillInDummyReject(&rej, /* stateless */ false);
+ EXPECT_TRUE(session_->IsEncryptionEstablished());
+ crypto_test_utils::SendHandshakeMessageToStream(
+ session_->GetMutableCryptoStream(), rej, Perspective::IS_CLIENT);
+ EXPECT_FALSE(session_->IsEncryptionEstablished());
+ EXPECT_EQ(ENCRYPTION_INITIAL,
+ QuicPacketCreatorPeer::GetEncryptionLevel(
+ QuicConnectionPeer::GetPacketCreator(connection_)));
+ // Verify that no new streams may be created.
+ EXPECT_TRUE(session_->CreateOutgoingBidirectionalStream() == nullptr);
+ // Verify that no data may be send on existing streams.
+ char data[] = "hello world";
+ QuicConsumedData consumed = session_->WritevData(
+ stream, stream->id(), QUIC_ARRAYSIZE(data), 0, NO_FIN);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_EQ(0u, consumed.bytes_consumed);
+}
+
+TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithNoFinOrRst) {
+ if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) {
+ // This test relies on the MIDS transport parameter, which is not yet
+ // supported in TLS 1.3.
+ // TODO(nharper): Add support for Transport Parameters in the TLS handshake.
+ return;
+ }
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AnyNumber());
+ EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(AnyNumber());
+
+ const uint32_t kServerMaxIncomingStreams = 1;
+ CompleteCryptoHandshake(kServerMaxIncomingStreams);
+
+ QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream();
+ ASSERT_TRUE(stream);
+ EXPECT_FALSE(session_->CreateOutgoingBidirectionalStream());
+
+ // Close the stream, but without having received a FIN or a RST_STREAM
+ // or MAX_STREAM_ID (V99) and check that a new one can not be created.
+ session_->CloseStream(stream->id());
+ EXPECT_EQ(1u, session_->GetNumOpenOutgoingStreams());
+
+ stream = session_->CreateOutgoingBidirectionalStream();
+ EXPECT_FALSE(stream);
+}
+
+TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithRst) {
+ if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) {
+ // This test relies on the MIDS transport parameter, which is not yet
+ // supported in TLS 1.3.
+ // TODO(nharper): Add support for Transport Parameters in the TLS handshake.
+ return;
+ }
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AnyNumber());
+ EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(AnyNumber());
+
+ const uint32_t kServerMaxIncomingStreams = 1;
+ CompleteCryptoHandshake(kServerMaxIncomingStreams);
+
+ QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream();
+ ASSERT_NE(nullptr, stream);
+ EXPECT_EQ(nullptr, session_->CreateOutgoingBidirectionalStream());
+
+ // Close the stream and receive an RST frame to remove the unfinished stream
+ session_->CloseStream(stream->id());
+ session_->OnRstStream(QuicRstStreamFrame(kInvalidControlFrameId, stream->id(),
+ QUIC_RST_ACKNOWLEDGEMENT, 0));
+ // Check that a new one can be created.
+ EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams());
+ if (GetParam().transport_version == QUIC_VERSION_99) {
+ // In V99 the stream limit increases only if we get a MAX_STREAM_ID
+ // frame; pretend we got one.
+
+ // Note that this is to be the second stream created, but GetNth... starts
+ // numbering at 0 (the first stream is 0, second is 1...)
+ QuicMaxStreamIdFrame frame(0, GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 1));
+ session_->OnMaxStreamIdFrame(frame);
+ }
+ stream = session_->CreateOutgoingBidirectionalStream();
+ EXPECT_NE(nullptr, stream);
+}
+
+TEST_P(QuicSpdyClientSessionTest, ResetAndTrailers) {
+ if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) {
+ // This test relies on the MIDS transport parameter, which is not yet
+ // supported in TLS 1.3.
+ // TODO(nharper): Add support for Transport Parameters in the TLS handshake.
+ return;
+ }
+ // Tests the situation in which the client sends a RST at the same time that
+ // the server sends trailing headers (trailers). Receipt of the trailers by
+ // the client should result in all outstanding stream state being tidied up
+ // (including flow control, and number of available outgoing streams).
+ const uint32_t kServerMaxIncomingStreams = 1;
+ CompleteCryptoHandshake(kServerMaxIncomingStreams);
+
+ QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream();
+ ASSERT_NE(nullptr, stream);
+
+ if (GetParam().transport_version == QUIC_VERSION_99) {
+ // For v99, trying to open a stream and failing due to lack
+ // of stream ids will result in a STREAM_ID_BLOCKED. Make
+ // sure we get one. Also clear out the frame because if it's
+ // left sitting, the later SendRstStream will not actually
+ // transmit the RST_STREAM because the connection will be in write-blocked
+ // state. This means that the SendControlFrame that is expected w.r.t. the
+ // RST_STREAM, below, will not be satisfied.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(
+ this,
+ &QuicSpdyClientSessionTest::ClearStreamIdBlockedControlFrame));
+ }
+
+ EXPECT_EQ(nullptr, session_->CreateOutgoingBidirectionalStream());
+
+ QuicStreamId stream_id = stream->id();
+
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1);
+ session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0);
+
+ // A new stream cannot be created as the reset stream still counts as an open
+ // outgoing stream until closed by the server.
+ EXPECT_EQ(1u, session_->GetNumOpenOutgoingStreams());
+ stream = session_->CreateOutgoingBidirectionalStream();
+ EXPECT_EQ(nullptr, stream);
+
+ // The stream receives trailers with final byte offset: this is one of three
+ // ways that a peer can signal the end of a stream (the others being RST,
+ // stream data + FIN).
+ QuicHeaderList trailers;
+ trailers.OnHeaderBlockStart();
+ trailers.OnHeader(kFinalOffsetHeaderKey, "0");
+ trailers.OnHeaderBlockEnd(0, 0);
+ session_->OnStreamHeaderList(stream_id, /*fin=*/false, 0, trailers);
+
+ // The stream is now complete from the client's perspective, and it should
+ // be able to create a new outgoing stream.
+ EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams());
+ if (GetParam().transport_version == QUIC_VERSION_99) {
+ // Note that this is to be the second stream created, but GetNth... starts
+ // numbering at 0 (the first stream is 0, second is 1...)
+ QuicMaxStreamIdFrame frame(0, GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 1));
+ session_->OnMaxStreamIdFrame(frame);
+ }
+ stream = session_->CreateOutgoingBidirectionalStream();
+ EXPECT_NE(nullptr, stream);
+}
+
+TEST_P(QuicSpdyClientSessionTest, ReceivedMalformedTrailersAfterSendingRst) {
+ // Tests the situation where the client has sent a RST to the server, and has
+ // received trailing headers with a malformed final byte offset value.
+ CompleteCryptoHandshake();
+
+ QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream();
+ ASSERT_NE(nullptr, stream);
+
+ // Send the RST, which results in the stream being closed locally (but some
+ // state remains while the client waits for a response from the server).
+ QuicStreamId stream_id = stream->id();
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1);
+ session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0);
+
+ // The stream receives trailers with final byte offset, but the header value
+ // is non-numeric and should be treated as malformed.
+ QuicHeaderList trailers;
+ trailers.OnHeaderBlockStart();
+ trailers.OnHeader(kFinalOffsetHeaderKey, "invalid non-numeric value");
+ trailers.OnHeaderBlockEnd(0, 0);
+
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1);
+ session_->OnStreamHeaderList(stream_id, /*fin=*/false, 0, trailers);
+}
+
+TEST_P(QuicSpdyClientSessionTest, OnStreamHeaderListWithStaticStream) {
+ // Test situation where OnStreamHeaderList is called by stream with static id.
+ CompleteCryptoHandshake();
+
+ QuicHeaderList trailers;
+ trailers.OnHeaderBlockStart();
+ trailers.OnHeader(kFinalOffsetHeaderKey, "0");
+ trailers.OnHeaderBlockEnd(0, 0);
+
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1);
+ session_->OnStreamHeaderList(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ /*fin=*/false, 0, trailers);
+}
+
+TEST_P(QuicSpdyClientSessionTest, OnPromiseHeaderListWithStaticStream) {
+ // Test situation where OnPromiseHeaderList is called by stream with static
+ // id.
+ CompleteCryptoHandshake();
+
+ QuicHeaderList trailers;
+ trailers.OnHeaderBlockStart();
+ trailers.OnHeader(kFinalOffsetHeaderKey, "0");
+ trailers.OnHeaderBlockEnd(0, 0);
+
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1);
+ session_->OnPromiseHeaderList(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ promised_stream_id_, 0, trailers);
+}
+
+TEST_P(QuicSpdyClientSessionTest, GoAwayReceived) {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return;
+ }
+ CompleteCryptoHandshake();
+
+ // After receiving a GoAway, I should no longer be able to create outgoing
+ // streams.
+ session_->connection()->OnGoAwayFrame(QuicGoAwayFrame(
+ kInvalidControlFrameId, QUIC_PEER_GOING_AWAY, 1u, "Going away."));
+ EXPECT_EQ(nullptr, session_->CreateOutgoingBidirectionalStream());
+}
+
+static bool CheckForDecryptionError(QuicFramer* framer) {
+ return framer->error() == QUIC_DECRYPTION_FAILURE;
+}
+
+// Various sorts of invalid packets that should not cause a connection
+// to be closed.
+TEST_P(QuicSpdyClientSessionTest, InvalidPacketReceived) {
+ QuicSocketAddress server_address(TestPeerIPAddress(), kTestPort);
+ QuicSocketAddress client_address(TestPeerIPAddress(), kTestPort);
+
+ EXPECT_CALL(*connection_, ProcessUdpPacket(server_address, client_address, _))
+ .WillRepeatedly(Invoke(static_cast<MockQuicConnection*>(connection_),
+ &MockQuicConnection::ReallyProcessUdpPacket));
+ EXPECT_CALL(*connection_, OnCanWrite()).Times(AnyNumber());
+ EXPECT_CALL(*connection_, OnError(_)).Times(1);
+
+ // Verify that empty packets don't close the connection.
+ QuicReceivedPacket zero_length_packet(nullptr, 0, QuicTime::Zero(), false);
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ session_->ProcessUdpPacket(client_address, server_address,
+ zero_length_packet);
+
+ // Verifiy that small, invalid packets don't close the connection.
+ char buf[2] = {0x00, 0x01};
+ QuicConnectionId connection_id = session_->connection()->connection_id();
+ QuicReceivedPacket valid_packet(buf, 2, QuicTime::Zero(), false);
+ // Close connection shouldn't be called.
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ if (connection_->transport_version() > QUIC_VERSION_44) {
+ // Illegal fixed bit value.
+ EXPECT_CALL(*connection_, OnError(_)).Times(1);
+ }
+ session_->ProcessUdpPacket(client_address, server_address, valid_packet);
+
+ // Verify that a non-decryptable packet doesn't close the connection.
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ QuicConnectionPeer::GetFramer(connection_), connection_id);
+ ParsedQuicVersionVector versions = SupportedVersions(GetParam());
+ std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
+ connection_id, EmptyQuicConnectionId(), false, false, 100, "data",
+ CONNECTION_ID_ABSENT, CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER,
+ &versions, Perspective::IS_SERVER));
+ std::unique_ptr<QuicReceivedPacket> received(
+ ConstructReceivedPacket(*packet, QuicTime::Zero()));
+ // Change the last byte of the encrypted data.
+ *(const_cast<char*>(received->data() + received->length() - 1)) += 1;
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_CALL(*connection_, OnError(Truly(CheckForDecryptionError))).Times(1);
+ session_->ProcessUdpPacket(client_address, server_address, *received);
+}
+
+// A packet with invalid framing should cause a connection to be closed.
+TEST_P(QuicSpdyClientSessionTest, InvalidFramedPacketReceived) {
+ QuicSocketAddress server_address(TestPeerIPAddress(), kTestPort);
+ QuicSocketAddress client_address(TestPeerIPAddress(), kTestPort);
+
+ EXPECT_CALL(*connection_, ProcessUdpPacket(server_address, client_address, _))
+ .WillRepeatedly(Invoke(static_cast<MockQuicConnection*>(connection_),
+ &MockQuicConnection::ReallyProcessUdpPacket));
+ EXPECT_CALL(*connection_, OnError(_)).Times(1);
+
+ // Verify that a decryptable packet with bad frames does close the connection.
+ QuicConnectionId destination_connection_id =
+ session_->connection()->connection_id();
+ QuicConnectionId source_connection_id = EmptyQuicConnectionId();
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ QuicConnectionPeer::GetFramer(connection_), destination_connection_id);
+ ParsedQuicVersionVector versions = {GetParam()};
+ bool version_flag = false;
+ QuicConnectionIdIncluded scid_included = CONNECTION_ID_ABSENT;
+ if (GetParam().transport_version > QUIC_VERSION_43) {
+ version_flag = true;
+ source_connection_id = destination_connection_id;
+ scid_included = CONNECTION_ID_PRESENT;
+ }
+ std::unique_ptr<QuicEncryptedPacket> packet(ConstructMisFramedEncryptedPacket(
+ destination_connection_id, source_connection_id, version_flag, false, 100,
+ "data", CONNECTION_ID_ABSENT, scid_included, PACKET_4BYTE_PACKET_NUMBER,
+ &versions, Perspective::IS_SERVER));
+ std::unique_ptr<QuicReceivedPacket> received(
+ ConstructReceivedPacket(*packet, QuicTime::Zero()));
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1);
+ session_->ProcessUdpPacket(client_address, server_address, *received);
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeaders) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>(
+ session_->CreateOutgoingBidirectionalStream());
+
+ EXPECT_CALL(*stream, OnPromiseHeaderList(_, _, _));
+ session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0,
+ QuicHeaderList());
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseOnPromiseHeadersAlreadyClosed) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ session_->CreateOutgoingBidirectionalStream();
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(associated_stream_id_, QUIC_REFUSED_STREAM));
+ session_->ResetPromised(associated_stream_id_, QUIC_REFUSED_STREAM);
+
+ session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0,
+ QuicHeaderList());
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseOutOfOrder) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>(
+ session_->CreateOutgoingBidirectionalStream());
+
+ EXPECT_CALL(*stream, OnPromiseHeaderList(promised_stream_id_, _, _));
+ session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0,
+ QuicHeaderList());
+ associated_stream_id_ +=
+ QuicUtils::StreamIdDelta(connection_->transport_version());
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Received push stream id lesser or equal to the"
+ " last accepted before",
+ _));
+ session_->OnPromiseHeaderList(associated_stream_id_, promised_stream_id_, 0,
+ QuicHeaderList());
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseOutgoingStreamId) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ MockQuicSpdyClientStream* stream = static_cast<MockQuicSpdyClientStream*>(
+ session_->CreateOutgoingBidirectionalStream());
+
+ // Promise an illegal (outgoing) stream id.
+ promised_stream_id_ = GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0);
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Received push stream id for outgoing stream.", _));
+
+ session_->OnPromiseHeaderList(stream->id(), promised_stream_id_, 0,
+ QuicHeaderList());
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseHandlePromise) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ session_->CreateOutgoingBidirectionalStream();
+
+ EXPECT_TRUE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, push_promise_));
+
+ EXPECT_NE(session_->GetPromisedById(promised_stream_id_), nullptr);
+ EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseAlreadyClosed) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ session_->CreateOutgoingBidirectionalStream();
+ session_->GetOrCreateStream(promised_stream_id_);
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promised_stream_id_, QUIC_REFUSED_STREAM));
+
+ session_->ResetPromised(promised_stream_id_, QUIC_REFUSED_STREAM);
+ SpdyHeaderBlock promise_headers;
+ EXPECT_FALSE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, promise_headers));
+
+ // Verify that the promise was not created.
+ EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr);
+ EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseDuplicateUrl) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ session_->CreateOutgoingBidirectionalStream();
+
+ EXPECT_TRUE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, push_promise_));
+
+ EXPECT_NE(session_->GetPromisedById(promised_stream_id_), nullptr);
+ EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
+
+ promised_stream_id_ +=
+ QuicUtils::StreamIdDelta(connection_->transport_version());
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promised_stream_id_, QUIC_DUPLICATE_PROMISE_URL));
+
+ EXPECT_FALSE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, push_promise_));
+
+ // Verify that the promise was not created.
+ EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr);
+}
+
+TEST_P(QuicSpdyClientSessionTest, ReceivingPromiseEnhanceYourCalm) {
+ for (size_t i = 0u; i < session_->get_max_promises(); i++) {
+ push_promise_[":path"] = QuicStringPrintf("/bar%zu", i);
+
+ QuicStreamId id =
+ promised_stream_id_ +
+ i * QuicUtils::StreamIdDelta(connection_->transport_version());
+
+ EXPECT_TRUE(
+ session_->HandlePromised(associated_stream_id_, id, push_promise_));
+
+ // Verify that the promise is in the unclaimed streams map.
+ std::string promise_url(
+ SpdyUtils::GetPromisedUrlFromHeaders(push_promise_));
+ EXPECT_NE(session_->GetPromisedByUrl(promise_url), nullptr);
+ EXPECT_NE(session_->GetPromisedById(id), nullptr);
+ }
+
+ // One more promise, this should be refused.
+ int i = session_->get_max_promises();
+ push_promise_[":path"] = QuicStringPrintf("/bar%d", i);
+
+ QuicStreamId id =
+ promised_stream_id_ +
+ i * QuicUtils::StreamIdDelta(connection_->transport_version());
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(id, QUIC_REFUSED_STREAM));
+ EXPECT_FALSE(
+ session_->HandlePromised(associated_stream_id_, id, push_promise_));
+
+ // Verify that the promise was not created.
+ std::string promise_url(SpdyUtils::GetPromisedUrlFromHeaders(push_promise_));
+ EXPECT_EQ(session_->GetPromisedById(id), nullptr);
+ EXPECT_EQ(session_->GetPromisedByUrl(promise_url), nullptr);
+}
+
+TEST_P(QuicSpdyClientSessionTest, IsClosedTrueAfterResetPromisedAlreadyOpen) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ session_->GetOrCreateStream(promised_stream_id_);
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promised_stream_id_, QUIC_REFUSED_STREAM));
+ session_->ResetPromised(promised_stream_id_, QUIC_REFUSED_STREAM);
+ EXPECT_TRUE(session_->IsClosedStream(promised_stream_id_));
+}
+
+TEST_P(QuicSpdyClientSessionTest, IsClosedTrueAfterResetPromisedNonexistant) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promised_stream_id_, QUIC_REFUSED_STREAM));
+ session_->ResetPromised(promised_stream_id_, QUIC_REFUSED_STREAM);
+ EXPECT_TRUE(session_->IsClosedStream(promised_stream_id_));
+}
+
+TEST_P(QuicSpdyClientSessionTest, OnInitialHeadersCompleteIsPush) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+ session_->GetOrCreateStream(promised_stream_id_);
+ EXPECT_TRUE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, push_promise_));
+ EXPECT_NE(session_->GetPromisedById(promised_stream_id_), nullptr);
+ EXPECT_NE(session_->GetPromisedStream(promised_stream_id_), nullptr);
+ EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
+
+ session_->OnInitialHeadersComplete(promised_stream_id_, SpdyHeaderBlock());
+}
+
+TEST_P(QuicSpdyClientSessionTest, OnInitialHeadersCompleteIsNotPush) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+ session_->CreateOutgoingBidirectionalStream();
+ session_->OnInitialHeadersComplete(promised_stream_id_, SpdyHeaderBlock());
+}
+
+TEST_P(QuicSpdyClientSessionTest, DeletePromised) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+ session_->GetOrCreateStream(promised_stream_id_);
+ EXPECT_TRUE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, push_promise_));
+ QuicClientPromisedInfo* promised =
+ session_->GetPromisedById(promised_stream_id_);
+ EXPECT_NE(promised, nullptr);
+ EXPECT_NE(session_->GetPromisedStream(promised_stream_id_), nullptr);
+ EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
+
+ session_->DeletePromised(promised);
+ EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr);
+ EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_P(QuicSpdyClientSessionTest, ResetPromised) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+ session_->GetOrCreateStream(promised_stream_id_);
+ EXPECT_TRUE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, push_promise_));
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY));
+ session_->SendRstStream(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY, 0);
+ QuicClientPromisedInfo* promised =
+ session_->GetPromisedById(promised_stream_id_);
+ EXPECT_NE(promised, nullptr);
+ EXPECT_NE(session_->GetPromisedByUrl(promise_url_), nullptr);
+ EXPECT_EQ(session_->GetPromisedStream(promised_stream_id_), nullptr);
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseInvalidMethod) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ session_->CreateOutgoingBidirectionalStream();
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promised_stream_id_, QUIC_INVALID_PROMISE_METHOD));
+
+ push_promise_[":method"] = "POST";
+ EXPECT_FALSE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, push_promise_));
+
+ EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr);
+ EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_P(QuicSpdyClientSessionTest, PushPromiseInvalidHost) {
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ session_->CreateOutgoingBidirectionalStream();
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(promised_stream_id_, QUIC_INVALID_PROMISE_URL));
+
+ push_promise_[":authority"] = "";
+ EXPECT_FALSE(session_->HandlePromised(associated_stream_id_,
+ promised_stream_id_, push_promise_));
+
+ EXPECT_EQ(session_->GetPromisedById(promised_stream_id_), nullptr);
+ EXPECT_EQ(session_->GetPromisedByUrl(promise_url_), nullptr);
+}
+
+TEST_P(QuicSpdyClientSessionTest,
+ TryToCreateServerInitiatedBidirectionalStream) {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ } else {
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ }
+ session_->GetOrCreateStream(GetNthServerInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.cc
new file mode 100644
index 00000000000..d4e8a69bb17
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id,
+ QuicSpdyClientSession* session,
+ StreamType type)
+ : QuicSpdyStream(id, session, type),
+ content_length_(-1),
+ response_code_(0),
+ header_bytes_read_(0),
+ header_bytes_written_(0),
+ session_(session),
+ has_preliminary_headers_(false) {}
+
+QuicSpdyClientStream::QuicSpdyClientStream(PendingStream pending,
+ QuicSpdyClientSession* session,
+ StreamType type)
+ : QuicSpdyStream(std::move(pending), session, type),
+ content_length_(-1),
+ response_code_(0),
+ header_bytes_read_(0),
+ header_bytes_written_(0),
+ session_(session),
+ has_preliminary_headers_(false) {}
+
+QuicSpdyClientStream::~QuicSpdyClientStream() = default;
+
+void QuicSpdyClientStream::OnInitialHeadersComplete(
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
+
+ DCHECK(headers_decompressed());
+ header_bytes_read_ += frame_len;
+ if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
+ &response_headers_)) {
+ QUIC_DLOG(ERROR) << "Failed to parse header list: "
+ << header_list.DebugString();
+ Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ return;
+ }
+
+ if (!ParseHeaderStatusCode(response_headers_, &response_code_)) {
+ QUIC_DLOG(ERROR) << "Received invalid response code: "
+ << response_headers_[":status"].as_string();
+ Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ return;
+ }
+
+ if (response_code_ == 100 && !has_preliminary_headers_) {
+ // These are preliminary 100 Continue headers, not the actual response
+ // headers.
+ set_headers_decompressed(false);
+ has_preliminary_headers_ = true;
+ preliminary_headers_ = std::move(response_headers_);
+ }
+
+ ConsumeHeaderList();
+ QUIC_DVLOG(1) << "headers complete for stream " << id();
+
+ session_->OnInitialHeadersComplete(id(), response_headers_);
+}
+
+void QuicSpdyClientStream::OnTrailingHeadersComplete(
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ QuicSpdyStream::OnTrailingHeadersComplete(fin, frame_len, header_list);
+ MarkTrailersConsumed();
+}
+
+void QuicSpdyClientStream::OnPromiseHeaderList(
+ QuicStreamId promised_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ header_bytes_read_ += frame_len;
+ int64_t content_length = -1;
+ SpdyHeaderBlock promise_headers;
+ if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length,
+ &promise_headers)) {
+ QUIC_DLOG(ERROR) << "Failed to parse promise headers: "
+ << header_list.DebugString();
+ Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ return;
+ }
+
+ session_->HandlePromised(id(), promised_id, promise_headers);
+ if (visitor() != nullptr) {
+ visitor()->OnPromiseHeadersComplete(promised_id, frame_len);
+ }
+}
+
+void QuicSpdyClientStream::OnBodyAvailable() {
+ // For push streams, visitor will not be set until the rendezvous
+ // between server promise and client request is complete.
+ if (visitor() == nullptr)
+ return;
+
+ while (HasBytesToRead()) {
+ struct iovec iov;
+ if (GetReadableRegions(&iov, 1) == 0) {
+ // No more data to read.
+ break;
+ }
+ QUIC_DVLOG(1) << "Client processed " << iov.iov_len << " bytes for stream "
+ << id();
+ data_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
+
+ if (content_length_ >= 0 &&
+ data_.size() > static_cast<uint64_t>(content_length_)) {
+ QUIC_DLOG(ERROR) << "Invalid content length (" << content_length_
+ << ") with data of size " << data_.size();
+ Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ return;
+ }
+ MarkConsumed(iov.iov_len);
+ }
+ if (sequencer()->IsClosed()) {
+ OnFinRead();
+ } else {
+ sequencer()->SetUnblocked();
+ }
+}
+
+size_t QuicSpdyClientStream::SendRequest(SpdyHeaderBlock headers,
+ QuicStringPiece body,
+ bool fin) {
+ QuicConnection::ScopedPacketFlusher flusher(
+ session_->connection(), QuicConnection::SEND_ACK_IF_QUEUED);
+ bool send_fin_with_headers = fin && body.empty();
+ size_t bytes_sent = body.size();
+ header_bytes_written_ =
+ WriteHeaders(std::move(headers), send_fin_with_headers, nullptr);
+ bytes_sent += header_bytes_written_;
+
+ if (!body.empty()) {
+ WriteOrBufferBody(body, fin);
+ }
+
+ return bytes_sent;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h
new file mode 100644
index 00000000000..ba59ca622ef
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_STREAM_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_STREAM_H_
+
+#include <cstddef>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+class QuicSpdyClientSession;
+
+// All this does right now is send an SPDY request, and aggregate the
+// SPDY response.
+class QuicSpdyClientStream : public QuicSpdyStream {
+ public:
+ QuicSpdyClientStream(QuicStreamId id,
+ QuicSpdyClientSession* session,
+ StreamType type);
+ QuicSpdyClientStream(PendingStream pending,
+ QuicSpdyClientSession* spdy_session,
+ StreamType type);
+ QuicSpdyClientStream(const QuicSpdyClientStream&) = delete;
+ QuicSpdyClientStream& operator=(const QuicSpdyClientStream&) = delete;
+ ~QuicSpdyClientStream() override;
+
+ // Override the base class to parse and store headers.
+ void OnInitialHeadersComplete(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) override;
+
+ // Override the base class to parse and store trailers.
+ void OnTrailingHeadersComplete(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) override;
+
+ // Override the base class to handle creation of the push stream.
+ void OnPromiseHeaderList(QuicStreamId promised_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list) override;
+
+ // QuicStream implementation called by the session when there's data for us.
+ void OnBodyAvailable() override;
+
+ // Serializes the headers and body, sends it to the server, and
+ // returns the number of bytes sent.
+ size_t SendRequest(spdy::SpdyHeaderBlock headers,
+ QuicStringPiece body,
+ bool fin);
+
+ // Returns the response data.
+ const std::string& data() { return data_; }
+
+ // Returns whatever headers have been received for this stream.
+ const spdy::SpdyHeaderBlock& response_headers() { return response_headers_; }
+
+ const spdy::SpdyHeaderBlock& preliminary_headers() {
+ return preliminary_headers_;
+ }
+
+ size_t header_bytes_read() const { return header_bytes_read_; }
+
+ size_t header_bytes_written() const { return header_bytes_written_; }
+
+ int response_code() const { return response_code_; }
+
+ // While the server's SetPriority shouldn't be called externally, the creator
+ // of client-side streams should be able to set the priority.
+ using QuicSpdyStream::SetPriority;
+
+ private:
+ // The parsed headers received from the server.
+ spdy::SpdyHeaderBlock response_headers_;
+
+ // The parsed content-length, or -1 if none is specified.
+ int64_t content_length_;
+ int response_code_;
+ std::string data_;
+ size_t header_bytes_read_;
+ size_t header_bytes_written_;
+
+ QuicSpdyClientSession* session_;
+
+ // These preliminary headers are used for the 100 Continue headers
+ // that may arrive before the response headers when the request has
+ // Expect: 100-continue.
+ bool has_preliminary_headers_;
+ spdy::SpdyHeaderBlock preliminary_headers_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_CLIENT_STREAM_H_
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
new file mode 100644
index 00000000000..007bba08e48
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream_test.cc
@@ -0,0 +1,232 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using spdy::SpdyHeaderBlock;
+using testing::_;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+
+namespace {
+
+class MockQuicSpdyClientSession : public QuicSpdyClientSession {
+ public:
+ explicit MockQuicSpdyClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicClientPushPromiseIndex* push_promise_index)
+ : QuicSpdyClientSession(DefaultQuicConfig(),
+ supported_versions,
+ connection,
+ QuicServerId("example.com", 443, false),
+ &crypto_config_,
+ push_promise_index),
+ crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx()) {}
+ MockQuicSpdyClientSession(const MockQuicSpdyClientSession&) = delete;
+ MockQuicSpdyClientSession& operator=(const MockQuicSpdyClientSession&) =
+ delete;
+ ~MockQuicSpdyClientSession() override = default;
+
+ MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id));
+
+ private:
+ QuicCryptoClientConfig crypto_config_;
+};
+
+class QuicSpdyClientStreamTest : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ class StreamVisitor;
+
+ QuicSpdyClientStreamTest()
+ : connection_(
+ new StrictMock<MockQuicConnection>(&helper_,
+ &alarm_factory_,
+ Perspective::IS_CLIENT,
+ SupportedVersions(GetParam()))),
+ session_(connection_->supported_versions(),
+ connection_,
+ &push_promise_index_),
+ body_("hello world") {
+ session_.Initialize();
+
+ headers_[":status"] = "200";
+ headers_["content-length"] = "11";
+
+ stream_ = QuicMakeUnique<QuicSpdyClientStream>(
+ GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0),
+ &session_, BIDIRECTIONAL);
+ stream_visitor_ = QuicMakeUnique<StreamVisitor>();
+ stream_->set_visitor(stream_visitor_.get());
+ }
+
+ class StreamVisitor : public QuicSpdyClientStream::Visitor {
+ void OnClose(QuicSpdyStream* stream) override {
+ QUIC_DVLOG(1) << "stream " << stream->id();
+ }
+ };
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ QuicClientPushPromiseIndex push_promise_index_;
+
+ MockQuicSpdyClientSession session_;
+ std::unique_ptr<QuicSpdyClientStream> stream_;
+ std::unique_ptr<StreamVisitor> stream_visitor_;
+ SpdyHeaderBlock headers_;
+ std::string body_;
+ HttpEncoder encoder_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSpdyClientStreamTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSpdyClientStreamTest, TestReceivingIllegalResponseStatusCode) {
+ headers_[":status"] = "200 ok";
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD));
+ auto headers = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+ EXPECT_EQ(QUIC_BAD_APPLICATION_PAYLOAD, stream_->stream_error());
+}
+
+TEST_P(QuicSpdyClientStreamTest, TestFraming) {
+ auto headers = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = VersionHasDataFrameHeader(connection_->transport_version())
+ ? header + body_
+ : body_;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+ EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
+ EXPECT_EQ(200, stream_->response_code());
+ EXPECT_EQ(body_, stream_->data());
+}
+
+TEST_P(QuicSpdyClientStreamTest, TestFraming100Continue) {
+ headers_[":status"] = "100";
+ auto headers = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_));
+ EXPECT_EQ("100", stream_->preliminary_headers().find(":status")->second);
+ EXPECT_EQ(0u, stream_->response_headers().size());
+ EXPECT_EQ(100, stream_->response_code());
+ EXPECT_EQ("", stream_->data());
+}
+
+TEST_P(QuicSpdyClientStreamTest, TestFramingOnePacket) {
+ auto headers = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = VersionHasDataFrameHeader(connection_->transport_version())
+ ? header + body_
+ : body_;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+ EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
+ EXPECT_EQ(200, stream_->response_code());
+ EXPECT_EQ(body_, stream_->data());
+}
+
+TEST_P(QuicSpdyClientStreamTest,
+ QUIC_TEST_DISABLED_IN_CHROME(TestFramingExtraData)) {
+ std::string large_body = "hello world!!!!!!";
+
+ auto headers = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+ // The headers should parse successfully.
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
+ EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
+ EXPECT_EQ(200, stream_->response_code());
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(large_body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = VersionHasDataFrameHeader(connection_->transport_version())
+ ? header + large_body
+ : large_body;
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD));
+
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+
+ EXPECT_NE(QUIC_STREAM_NO_ERROR, stream_->stream_error());
+}
+
+TEST_P(QuicSpdyClientStreamTest, ReceivingTrailers) {
+ // Test that receiving trailing headers, containing a final offset, results in
+ // the stream being closed at that byte offset.
+
+ // Send headers as usual.
+ auto headers = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+
+ // Send trailers before sending the body. Even though a FIN has been received
+ // the stream should not be closed, as it does not yet have all the data bytes
+ // promised by the final offset field.
+ SpdyHeaderBlock trailer_block;
+ trailer_block["trailer key"] = "trailer value";
+ trailer_block[kFinalOffsetHeaderKey] =
+ QuicTextUtils::Uint64ToString(body_.size());
+ auto trailers = AsHeaderList(trailer_block);
+ stream_->OnStreamHeaderList(true, trailers.uncompressed_header_bytes(),
+ trailers);
+
+ // 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 =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = VersionHasDataFrameHeader(connection_->transport_version())
+ ? header + body_
+ : body_;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+ EXPECT_TRUE(stream_->reading_stopped());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.cc
new file mode 100644
index 00000000000..cbe479efab7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(QuicStreamId id,
+ QuicSpdySession* session,
+ StreamType type)
+ : QuicSpdyStream(id, session, type) {}
+
+QuicSpdyServerStreamBase::QuicSpdyServerStreamBase(PendingStream pending,
+ QuicSpdySession* session,
+ StreamType type)
+ : QuicSpdyStream(std::move(pending), session, type) {}
+
+void QuicSpdyServerStreamBase::CloseWriteSide() {
+ if (!fin_received() && !rst_received() && sequencer()->ignore_read_data() &&
+ !rst_sent()) {
+ // Early cancel the stream if it has stopped reading before receiving FIN
+ // or RST.
+ DCHECK(fin_sent() || !session()->connection()->connected());
+ // Tell the peer to stop sending further data.
+ QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id();
+ Reset(QUIC_STREAM_NO_ERROR);
+ }
+
+ QuicSpdyStream::CloseWriteSide();
+}
+
+void QuicSpdyServerStreamBase::StopReading() {
+ if (!fin_received() && !rst_received() && write_side_closed() &&
+ !rst_sent()) {
+ DCHECK(fin_sent());
+ // Tell the peer to stop sending further data.
+ QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id();
+ Reset(QUIC_STREAM_NO_ERROR);
+ }
+ QuicSpdyStream::StopReading();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h
new file mode 100644
index 00000000000..438d152b941
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h
@@ -0,0 +1,31 @@
+// 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 QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SERVER_STREAM_BASE_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SERVER_STREAM_BASE_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+
+namespace quic {
+
+class QuicSpdyServerStreamBase : public QuicSpdyStream {
+ public:
+ QuicSpdyServerStreamBase(QuicStreamId id,
+ QuicSpdySession* session,
+ StreamType type);
+ QuicSpdyServerStreamBase(PendingStream pending,
+ QuicSpdySession* session,
+ StreamType type);
+ QuicSpdyServerStreamBase(const QuicSpdyServerStreamBase&) = delete;
+ QuicSpdyServerStreamBase& operator=(const QuicSpdyServerStreamBase&) = delete;
+
+ // Override the base class to send QUIC_STREAM_NO_ERROR to the peer
+ // when the stream has not received all the data.
+ void CloseWriteSide() override;
+ void StopReading() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SERVER_STREAM_BASE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base_test.cc
new file mode 100644
index 00000000000..4e1aa1bd3e9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base_test.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestQuicSpdyServerStream : public QuicSpdyServerStreamBase {
+ public:
+ TestQuicSpdyServerStream(QuicStreamId id,
+ QuicSpdySession* session,
+ StreamType type)
+ : QuicSpdyServerStreamBase(id, session, type) {}
+
+ void OnBodyAvailable() override {}
+};
+
+class QuicSpdyServerStreamBaseTest : public QuicTest {
+ protected:
+ QuicSpdyServerStreamBaseTest()
+ : session_(new MockQuicConnection(&helper_,
+ &alarm_factory_,
+ Perspective::IS_SERVER)) {
+ stream_ = new TestQuicSpdyServerStream(
+ GetNthClientInitiatedBidirectionalStreamId(
+ session_.connection()->transport_version(), 0),
+ &session_, BIDIRECTIONAL);
+ session_.ActivateStream(QuicWrapUnique(stream_));
+ helper_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ QuicSpdyServerStreamBase* stream_ = nullptr;
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicSpdySession session_;
+};
+
+TEST_F(QuicSpdyServerStreamBaseTest,
+ SendQuicRstStreamNoErrorWithEarlyResponse) {
+ stream_->StopReading();
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1);
+ stream_->set_fin_sent(true);
+ stream_->CloseWriteSide();
+}
+
+TEST_F(QuicSpdyServerStreamBaseTest,
+ DoNotSendQuicRstStreamNoErrorWithRstReceived) {
+ EXPECT_FALSE(stream_->reading_stopped());
+
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ if (session_.connection()->transport_version() != QUIC_VERSION_99) {
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _))
+ .Times(1);
+ } else {
+ // Intercept & check that the call to the QuicConnection's OnStreamReast
+ // has correct stream ID and error code -- for V99/IETF Quic, it should
+ // have the STREAM_CANCELLED error code, not RST_ACK... Capture
+ // OnStreamReset (rather than SendRstStream) because the V99 path bypasses
+ // SendRstStream, calling SendRstStreamInner directly. Mocking
+ // SendRstStreamInner is problematic since the test relies on it to perform
+ // the closing operations and getting the stream in the correct state.
+ EXPECT_CALL(*(static_cast<MockQuicConnection*>(session_.connection())),
+ OnStreamReset(stream_->id(), QUIC_STREAM_CANCELLED));
+ }
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ if (session_.connection()->transport_version() == QUIC_VERSION_99) {
+ // Create and inject a STOP SENDING frame to complete the close
+ // of the stream. This is only needed for version 99/IETF QUIC.
+ QuicStopSendingFrame stop_sending(
+ kInvalidControlFrameId, stream_->id(),
+ static_cast<QuicApplicationErrorCode>(QUIC_STREAM_CANCELLED));
+ session_.OnStopSendingFrame(stop_sending);
+ }
+
+ EXPECT_TRUE(stream_->reading_stopped());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..bda2e172d01
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.cc
@@ -0,0 +1,702 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
+
+using http2::Http2DecoderAdapter;
+using spdy::HpackEntry;
+using spdy::HpackHeaderTable;
+using spdy::Http2WeightToSpdy3Priority;
+using spdy::SETTINGS_ENABLE_PUSH;
+using spdy::SETTINGS_HEADER_TABLE_SIZE;
+using spdy::SETTINGS_MAX_HEADER_LIST_SIZE;
+using spdy::Spdy3PriorityToHttp2Weight;
+using spdy::SpdyErrorCode;
+using spdy::SpdyFramer;
+using spdy::SpdyFramerDebugVisitorInterface;
+using spdy::SpdyFramerVisitorInterface;
+using spdy::SpdyFrameType;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyHeadersHandlerInterface;
+using spdy::SpdyHeadersIR;
+using spdy::SpdyKnownSettingsId;
+using spdy::SpdyPingId;
+using spdy::SpdyPriority;
+using spdy::SpdyPriorityIR;
+using spdy::SpdyPushPromiseIR;
+using spdy::SpdySerializedFrame;
+using spdy::SpdySettingsId;
+using spdy::SpdySettingsIR;
+using spdy::SpdyStreamId;
+
+namespace quic {
+
+namespace {
+
+class HeaderTableDebugVisitor : public HpackHeaderTable::DebugVisitorInterface {
+ public:
+ HeaderTableDebugVisitor(const QuicClock* clock,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor)
+ : clock_(clock), headers_stream_hpack_visitor_(std::move(visitor)) {}
+ HeaderTableDebugVisitor(const HeaderTableDebugVisitor&) = delete;
+ HeaderTableDebugVisitor& operator=(const HeaderTableDebugVisitor&) = delete;
+
+ int64_t OnNewEntry(const HpackEntry& entry) override {
+ QUIC_DVLOG(1) << entry.GetDebugString();
+ return (clock_->ApproximateNow() - QuicTime::Zero()).ToMicroseconds();
+ }
+
+ void OnUseEntry(const HpackEntry& entry) override {
+ const QuicTime::Delta elapsed(
+ clock_->ApproximateNow() -
+ QuicTime::Delta::FromMicroseconds(entry.time_added()) -
+ QuicTime::Zero());
+ QUIC_DVLOG(1) << entry.GetDebugString() << " " << elapsed.ToMilliseconds()
+ << " ms";
+ headers_stream_hpack_visitor_->OnUseEntry(elapsed);
+ }
+
+ private:
+ const QuicClock* clock_;
+ std::unique_ptr<QuicHpackDebugVisitor> headers_stream_hpack_visitor_;
+};
+
+} // namespace
+
+// A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and
+// closes the connection if any unexpected frames are received.
+class QuicSpdySession::SpdyFramerVisitor
+ : public SpdyFramerVisitorInterface,
+ public SpdyFramerDebugVisitorInterface {
+ public:
+ explicit SpdyFramerVisitor(QuicSpdySession* session) : session_(session) {}
+ SpdyFramerVisitor(const SpdyFramerVisitor&) = delete;
+ SpdyFramerVisitor& operator=(const SpdyFramerVisitor&) = delete;
+
+ SpdyHeadersHandlerInterface* OnHeaderFrameStart(
+ SpdyStreamId /* stream_id */) override {
+ return &header_list_;
+ }
+
+ void OnHeaderFrameEnd(SpdyStreamId /* stream_id */) override {
+ if (session_->IsConnected()) {
+ session_->OnHeaderList(header_list_);
+ }
+ header_list_.Clear();
+ }
+
+ void OnStreamFrameData(SpdyStreamId stream_id,
+ const char* data,
+ size_t len) override {
+ CloseConnection("SPDY DATA frame received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+
+ void OnStreamEnd(SpdyStreamId stream_id) override {
+ // The framer invokes OnStreamEnd after processing a frame that had the fin
+ // bit set.
+ }
+
+ void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {
+ CloseConnection("SPDY frame padding received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+
+ void OnError(Http2DecoderAdapter::SpdyFramerError error) override {
+ QuicErrorCode code = QUIC_INVALID_HEADERS_STREAM_DATA;
+ switch (error) {
+ case Http2DecoderAdapter::SpdyFramerError::SPDY_DECOMPRESS_FAILURE:
+ code = QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE;
+ break;
+ default:
+ break;
+ }
+ CloseConnection(
+ QuicStrCat("SPDY framing error: ",
+ Http2DecoderAdapter::SpdyFramerErrorToString(error)),
+ code);
+ }
+
+ void OnDataFrameHeader(SpdyStreamId stream_id,
+ size_t length,
+ bool fin) override {
+ CloseConnection("SPDY DATA frame received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+
+ void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override {
+ CloseConnection("SPDY RST_STREAM frame received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+
+ void OnSetting(SpdySettingsId id, uint32_t value) override {
+ switch (id) {
+ case SETTINGS_HEADER_TABLE_SIZE:
+ session_->UpdateHeaderEncoderTableSize(value);
+ break;
+ case SETTINGS_ENABLE_PUSH:
+ if (session_->perspective() == Perspective::IS_SERVER) {
+ // See rfc7540, Section 6.5.2.
+ if (value > 1) {
+ CloseConnection(
+ QuicStrCat("Invalid value for SETTINGS_ENABLE_PUSH: ", value),
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ return;
+ }
+ session_->UpdateEnableServerPush(value > 0);
+ break;
+ } else {
+ CloseConnection(
+ QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id),
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+ break;
+ // TODO(fayang): Need to support SETTINGS_MAX_HEADER_LIST_SIZE when
+ // clients are actually sending it.
+ case SETTINGS_MAX_HEADER_LIST_SIZE:
+ break;
+ default:
+ CloseConnection(
+ QuicStrCat("Unsupported field of HTTP/2 SETTINGS frame: ", id),
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+ }
+
+ void OnSettingsEnd() override {}
+
+ void OnPing(SpdyPingId unique_id, bool is_ack) override {
+ CloseConnection("SPDY PING frame received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+
+ void OnGoAway(SpdyStreamId last_accepted_stream_id,
+ SpdyErrorCode error_code) override {
+ CloseConnection("SPDY GOAWAY frame received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+
+ void OnHeaders(SpdyStreamId stream_id,
+ bool has_priority,
+ int weight,
+ SpdyStreamId /*parent_stream_id*/,
+ bool /*exclusive*/,
+ bool fin,
+ bool end) override {
+ if (!session_->IsConnected()) {
+ return;
+ }
+
+ // TODO(mpw): avoid down-conversion and plumb SpdyStreamPrecedence through
+ // QuicHeadersStream.
+ SpdyPriority priority =
+ has_priority ? Http2WeightToSpdy3Priority(weight) : 0;
+ session_->OnHeaders(stream_id, has_priority, priority, fin);
+ }
+
+ void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override {
+ CloseConnection("SPDY WINDOW_UPDATE frame received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ }
+
+ void OnPushPromise(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end) override {
+ if (!session_->supports_push_promise()) {
+ CloseConnection("PUSH_PROMISE not supported.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ return;
+ }
+ if (!session_->IsConnected()) {
+ return;
+ }
+ session_->OnPushPromise(stream_id, promised_stream_id, end);
+ }
+
+ void OnContinuation(SpdyStreamId stream_id, bool end) override {}
+
+ void OnPriority(SpdyStreamId stream_id,
+ SpdyStreamId parent_id,
+ int weight,
+ bool exclusive) override {
+ if (session_->connection()->transport_version() <= QUIC_VERSION_39) {
+ CloseConnection("SPDY PRIORITY frame received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ return;
+ }
+ if (!session_->IsConnected()) {
+ return;
+ }
+ // TODO (wangyix): implement real HTTP/2 weights and dependencies instead of
+ // converting to SpdyPriority.
+ SpdyPriority priority = Http2WeightToSpdy3Priority(weight);
+ session_->OnPriority(stream_id, priority);
+ }
+
+ bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override {
+ CloseConnection("Unknown frame type received.",
+ QUIC_INVALID_HEADERS_STREAM_DATA);
+ return false;
+ }
+
+ // SpdyFramerDebugVisitorInterface implementation
+ void OnSendCompressedFrame(SpdyStreamId stream_id,
+ SpdyFrameType type,
+ size_t payload_len,
+ size_t frame_len) override {
+ if (payload_len == 0) {
+ QUIC_BUG << "Zero payload length.";
+ return;
+ }
+ int compression_pct = 100 - (100 * frame_len) / payload_len;
+ QUIC_DVLOG(1) << "Net.QuicHpackCompressionPercentage: " << compression_pct;
+ }
+
+ void OnReceiveCompressedFrame(SpdyStreamId stream_id,
+ SpdyFrameType type,
+ size_t frame_len) override {
+ if (session_->IsConnected()) {
+ session_->OnCompressedFrameSize(frame_len);
+ }
+ }
+
+ void set_max_uncompressed_header_bytes(
+ size_t set_max_uncompressed_header_bytes) {
+ header_list_.set_max_header_list_size(set_max_uncompressed_header_bytes);
+ }
+
+ private:
+ void CloseConnection(const std::string& details, QuicErrorCode code) {
+ if (session_->IsConnected()) {
+ session_->CloseConnectionWithDetails(code, details);
+ }
+ }
+
+ private:
+ QuicSpdySession* session_;
+ QuicHeaderList header_list_;
+};
+
+QuicHpackDebugVisitor::QuicHpackDebugVisitor() {}
+
+QuicHpackDebugVisitor::~QuicHpackDebugVisitor() {}
+
+QuicSpdySession::QuicSpdySession(
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions)
+ : QuicSession(connection, visitor, config, supported_versions),
+ max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize),
+ server_push_enabled_(true),
+ stream_id_(
+ QuicUtils::GetInvalidStreamId(connection->transport_version())),
+ promised_stream_id_(
+ QuicUtils::GetInvalidStreamId(connection->transport_version())),
+ fin_(false),
+ frame_len_(0),
+ uncompressed_frame_len_(0),
+ supports_push_promise_(perspective() == Perspective::IS_CLIENT),
+ spdy_framer_(SpdyFramer::ENABLE_COMPRESSION),
+ spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
+ 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());
+}
+
+QuicSpdySession::~QuicSpdySession() {
+ // Set the streams' session pointers in closed and dynamic stream lists
+ // to null to avoid subsequent use of this session.
+ for (auto& stream : *closed_streams()) {
+ static_cast<QuicSpdyStream*>(stream.get())->ClearSession();
+ }
+ for (auto const& kv : zombie_streams()) {
+ static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
+ }
+ for (auto const& kv : dynamic_streams()) {
+ static_cast<QuicSpdyStream*>(kv.second.get())->ClearSession();
+ }
+}
+
+void QuicSpdySession::Initialize() {
+ QuicSession::Initialize();
+
+ if (perspective() == Perspective::IS_SERVER) {
+ set_largest_peer_created_stream_id(
+ QuicUtils::GetHeadersStreamId(connection()->transport_version()));
+ } else {
+ QuicStreamId headers_stream_id = GetNextOutgoingBidirectionalStreamId();
+ DCHECK_EQ(headers_stream_id,
+ QuicUtils::GetHeadersStreamId(connection()->transport_version()));
+ }
+
+ if (VersionUsesQpack(connection()->transport_version())) {
+ qpack_encoder_ = QuicMakeUnique<QpackEncoder>(this, this);
+ qpack_decoder_ = QuicMakeUnique<QpackDecoder>(this, this);
+ }
+
+ headers_stream_ = QuicMakeUnique<QuicHeadersStream>((this));
+ DCHECK_EQ(QuicUtils::GetHeadersStreamId(connection()->transport_version()),
+ headers_stream_->id());
+ RegisterStaticStream(
+ QuicUtils::GetHeadersStreamId(connection()->transport_version()),
+ headers_stream_.get());
+
+ set_max_uncompressed_header_bytes(max_inbound_header_list_size_);
+
+ // Limit HPACK buffering to 2x header list size limit.
+ set_max_decode_buffer_size_bytes(2 * max_inbound_header_list_size_);
+}
+
+void QuicSpdySession::OnDecoderStreamError(QuicStringPiece error_message) {
+ DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+ // TODO(112770235): Signal connection error on decoder stream errors.
+ QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::WriteEncoderStreamData(QuicStringPiece data) {
+ DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+ // TODO(112770235): Send encoder stream data on encoder stream.
+ QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::OnEncoderStreamError(QuicStringPiece error_message) {
+ DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+ // TODO(112770235): Signal connection error on encoder stream errors.
+ QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::WriteDecoderStreamData(QuicStringPiece data) {
+ DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+ // TODO(112770235): Send decoder stream data on decoder stream.
+ QUIC_NOTREACHED();
+}
+
+void QuicSpdySession::OnStreamHeadersPriority(QuicStreamId stream_id,
+ SpdyPriority priority) {
+ QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
+ }
+ stream->OnStreamHeadersPriority(priority);
+}
+
+void QuicSpdySession::OnStreamHeaderList(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ if (QuicContainsKey(static_streams(), stream_id)) {
+ connection()->CloseConnection(
+ QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
+ if (stream == nullptr) {
+ // The stream no longer exists, but trailing headers may contain the final
+ // byte offset necessary for flow control and open stream accounting.
+ size_t final_byte_offset = 0;
+ for (const auto& header : header_list) {
+ const std::string& header_key = header.first;
+ const std::string& header_value = header.second;
+ if (header_key == kFinalOffsetHeaderKey) {
+ if (!QuicTextUtils::StringToSizeT(header_value, &final_byte_offset)) {
+ connection()->CloseConnection(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "Trailers are malformed (no final offset)",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ DVLOG(1) << "Received final byte offset in trailers for stream "
+ << stream_id << ", which no longer exists.";
+ OnFinalByteOffsetReceived(stream_id, final_byte_offset);
+ }
+ }
+
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
+ }
+ stream->OnStreamHeaderList(fin, frame_len, header_list);
+}
+
+void QuicSpdySession::OnPriorityFrame(QuicStreamId stream_id,
+ SpdyPriority priority) {
+ QuicSpdyStream* stream = GetSpdyDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive a PRIORITY frame after a stream has been
+ // reset.
+ return;
+ }
+ stream->OnPriorityFrame(priority);
+}
+
+size_t QuicSpdySession::ProcessHeaderData(const struct iovec& iov) {
+ return h2_deframer_.ProcessInput(static_cast<char*>(iov.iov_base),
+ iov.iov_len);
+}
+
+size_t QuicSpdySession::WriteHeadersOnHeadersStream(
+ QuicStreamId id,
+ SpdyHeaderBlock headers,
+ bool fin,
+ SpdyPriority priority,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ return WriteHeadersOnHeadersStreamImpl(
+ id, std::move(headers), fin,
+ /* parent_stream_id = */ 0, Spdy3PriorityToHttp2Weight(priority),
+ /* exclusive = */ false, std::move(ack_listener));
+}
+
+size_t QuicSpdySession::WritePriority(QuicStreamId id,
+ QuicStreamId parent_stream_id,
+ int weight,
+ bool exclusive) {
+ if (connection()->transport_version() <= QUIC_VERSION_39) {
+ return 0;
+ }
+ SpdyPriorityIR priority_frame(id, parent_stream_id, weight, exclusive);
+ SpdySerializedFrame frame(spdy_framer_.SerializeFrame(priority_frame));
+ headers_stream_->WriteOrBufferData(
+ QuicStringPiece(frame.data(), frame.size()), false, nullptr);
+ return frame.size();
+}
+
+size_t QuicSpdySession::WritePushPromise(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ SpdyHeaderBlock headers) {
+ if (perspective() == Perspective::IS_CLIENT) {
+ QUIC_BUG << "Client shouldn't send PUSH_PROMISE";
+ return 0;
+ }
+
+ SpdyPushPromiseIR push_promise(original_stream_id, promised_stream_id,
+ std::move(headers));
+ // PUSH_PROMISE must not be the last frame sent out, at least followed by
+ // response headers.
+ push_promise.set_fin(false);
+
+ SpdySerializedFrame frame(spdy_framer_.SerializeFrame(push_promise));
+ headers_stream_->WriteOrBufferData(
+ QuicStringPiece(frame.data(), frame.size()), false, nullptr);
+ return frame.size();
+}
+
+size_t QuicSpdySession::SendMaxHeaderListSize(size_t value) {
+ SpdySettingsIR settings_frame;
+ settings_frame.AddSetting(SETTINGS_MAX_HEADER_LIST_SIZE, value);
+
+ SpdySerializedFrame frame(spdy_framer_.SerializeFrame(settings_frame));
+ headers_stream_->WriteOrBufferData(
+ QuicStringPiece(frame.data(), frame.size()), false, nullptr);
+ return frame.size();
+}
+
+QpackEncoder* QuicSpdySession::qpack_encoder() {
+ DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+ return qpack_encoder_.get();
+}
+
+QpackDecoder* QuicSpdySession::qpack_decoder() {
+ DCHECK(VersionUsesQpack(connection()->transport_version()));
+
+ return qpack_decoder_.get();
+}
+
+QuicSpdyStream* QuicSpdySession::GetSpdyDataStream(
+ const QuicStreamId stream_id) {
+ return static_cast<QuicSpdyStream*>(GetOrCreateDynamicStream(stream_id));
+}
+
+void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+ if (event == HANDSHAKE_CONFIRMED && config()->SupportMaxHeaderListSize()) {
+ SendMaxHeaderListSize(max_inbound_header_list_size_);
+ }
+}
+
+// True if there are open HTTP requests.
+bool QuicSpdySession::ShouldKeepConnectionAlive() const {
+ // Change to check if there are open HTTP requests.
+ // When IETF QUIC control and QPACK streams are used, those will need to be
+ // subtracted from this count to ensure only request streams are counted.
+ return GetNumOpenDynamicStreams() > 0;
+}
+
+bool QuicSpdySession::ShouldBufferIncomingStream(QuicStreamId id) const {
+ DCHECK_EQ(QUIC_VERSION_99, connection()->transport_version());
+ return !QuicUtils::IsBidirectionalStreamId(id);
+}
+
+size_t QuicSpdySession::WriteHeadersOnHeadersStreamImpl(
+ QuicStreamId id,
+ spdy::SpdyHeaderBlock headers,
+ bool fin,
+ QuicStreamId parent_stream_id,
+ int weight,
+ bool exclusive,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ SpdyHeadersIR headers_frame(id, std::move(headers));
+ headers_frame.set_fin(fin);
+ if (perspective() == Perspective::IS_CLIENT) {
+ headers_frame.set_has_priority(true);
+ headers_frame.set_parent_stream_id(parent_stream_id);
+ headers_frame.set_weight(weight);
+ headers_frame.set_exclusive(exclusive);
+ }
+ SpdySerializedFrame frame(spdy_framer_.SerializeFrame(headers_frame));
+ headers_stream_->WriteOrBufferData(
+ QuicStringPiece(frame.data(), frame.size()), false,
+ std::move(ack_listener));
+ return frame.size();
+}
+
+void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id,
+ QuicStreamId promised_stream_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ std::string error =
+ "OnPromiseHeaderList should be overridden in client code.";
+ QUIC_BUG << error;
+ connection()->CloseConnection(QUIC_INTERNAL_ERROR, error,
+ ConnectionCloseBehavior::SILENT_CLOSE);
+}
+
+bool QuicSpdySession::ShouldReleaseHeadersStreamSequencerBuffer() {
+ return false;
+}
+
+void QuicSpdySession::OnHeaders(SpdyStreamId stream_id,
+ bool has_priority,
+ SpdyPriority priority,
+ bool fin) {
+ if (has_priority) {
+ if (perspective() == Perspective::IS_CLIENT) {
+ CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "Server must not send priorities.");
+ return;
+ }
+ OnStreamHeadersPriority(stream_id, priority);
+ } else {
+ if (perspective() == Perspective::IS_SERVER) {
+ CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "Client must send priorities.");
+ return;
+ }
+ }
+ DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
+ stream_id_);
+ DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
+ promised_stream_id_);
+ stream_id_ = stream_id;
+ fin_ = fin;
+}
+
+void QuicSpdySession::OnPushPromise(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end) {
+ DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
+ stream_id_);
+ DCHECK_EQ(QuicUtils::GetInvalidStreamId(connection()->transport_version()),
+ promised_stream_id_);
+ stream_id_ = stream_id;
+ promised_stream_id_ = promised_stream_id;
+}
+
+// TODO (wangyix): Why is SpdyStreamId used instead of QuicStreamId?
+// This occurs in many places in this file.
+void QuicSpdySession::OnPriority(SpdyStreamId stream_id,
+ SpdyPriority priority) {
+ if (perspective() == Perspective::IS_CLIENT) {
+ CloseConnectionWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "Server must not send PRIORITY frames.");
+ return;
+ }
+ OnPriorityFrame(stream_id, priority);
+}
+
+void QuicSpdySession::OnHeaderList(const QuicHeaderList& header_list) {
+ QUIC_DVLOG(1) << "Received header list for stream " << stream_id_ << ": "
+ << header_list.DebugString();
+ if (promised_stream_id_ ==
+ QuicUtils::GetInvalidStreamId(connection()->transport_version())) {
+ OnStreamHeaderList(stream_id_, fin_, frame_len_, header_list);
+ } else {
+ OnPromiseHeaderList(stream_id_, promised_stream_id_, frame_len_,
+ header_list);
+ }
+ // Reset state for the next frame.
+ promised_stream_id_ =
+ QuicUtils::GetInvalidStreamId(connection()->transport_version());
+ stream_id_ = QuicUtils::GetInvalidStreamId(connection()->transport_version());
+ fin_ = false;
+ frame_len_ = 0;
+ uncompressed_frame_len_ = 0;
+}
+
+void QuicSpdySession::OnCompressedFrameSize(size_t frame_len) {
+ frame_len_ += frame_len;
+}
+
+void QuicSpdySession::SetHpackEncoderDebugVisitor(
+ std::unique_ptr<QuicHpackDebugVisitor> visitor) {
+ spdy_framer_.SetEncoderHeaderTableDebugVisitor(
+ std::unique_ptr<HeaderTableDebugVisitor>(new HeaderTableDebugVisitor(
+ connection()->helper()->GetClock(), std::move(visitor))));
+}
+
+void QuicSpdySession::SetHpackDecoderDebugVisitor(
+ std::unique_ptr<QuicHpackDebugVisitor> visitor) {
+ h2_deframer_.SetDecoderHeaderTableDebugVisitor(
+ QuicMakeUnique<HeaderTableDebugVisitor>(
+ connection()->helper()->GetClock(), std::move(visitor)));
+}
+
+void QuicSpdySession::UpdateHeaderEncoderTableSize(uint32_t value) {
+ spdy_framer_.UpdateHeaderEncoderTableSize(value);
+}
+
+void QuicSpdySession::UpdateEnableServerPush(bool value) {
+ set_server_push_enabled(value);
+}
+
+void QuicSpdySession::set_max_uncompressed_header_bytes(
+ size_t set_max_uncompressed_header_bytes) {
+ spdy_framer_visitor_->set_max_uncompressed_header_bytes(
+ set_max_uncompressed_header_bytes);
+}
+
+void QuicSpdySession::CloseConnectionWithDetails(QuicErrorCode error,
+ const std::string& details) {
+ connection()->CloseConnection(
+ error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+bool QuicSpdySession::HasActiveRequestStreams() const {
+ // TODO(renjietang): Exclude static streams.
+ return !dynamic_streams().empty();
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..0ff1150293e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session.h
@@ -0,0 +1,289 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SESSION_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SESSION_H_
+
+#include <cstddef>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_headers_stream.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
+
+namespace quic {
+
+namespace test {
+class QuicSpdySessionPeer;
+} // namespace test
+
+// QuicHpackDebugVisitor gathers data used for understanding HPACK HoL
+// dynamics. Specifically, it is to help predict the compression
+// penalty of avoiding HoL by chagning how the dynamic table is used.
+// In chromium, the concrete instance populates an UMA
+// histogram with the data.
+class QUIC_EXPORT_PRIVATE QuicHpackDebugVisitor {
+ public:
+ QuicHpackDebugVisitor();
+ QuicHpackDebugVisitor(const QuicHpackDebugVisitor&) = delete;
+ QuicHpackDebugVisitor& operator=(const QuicHpackDebugVisitor&) = delete;
+
+ virtual ~QuicHpackDebugVisitor();
+
+ // For each HPACK indexed representation processed, |elapsed| is
+ // the time since the corresponding entry was added to the dynamic
+ // table.
+ virtual void OnUseEntry(QuicTime::Delta elapsed) = 0;
+};
+
+// A QUIC session with a headers stream.
+class QUIC_EXPORT_PRIVATE QuicSpdySession
+ : public QuicSession,
+ public QpackEncoder::DecoderStreamErrorDelegate,
+ public QpackEncoderStreamSender::Delegate,
+ public QpackDecoder::EncoderStreamErrorDelegate,
+ public QpackDecoderStreamSender::Delegate {
+ public:
+ // Does not take ownership of |connection| or |visitor|.
+ QuicSpdySession(QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions);
+ QuicSpdySession(const QuicSpdySession&) = delete;
+ QuicSpdySession& operator=(const QuicSpdySession&) = delete;
+
+ ~QuicSpdySession() override;
+
+ void Initialize() override;
+
+ // QpackEncoder::DecoderStreamErrorDelegate implementation.
+ void OnDecoderStreamError(QuicStringPiece error_message) override;
+
+ // QpackEncoderStreamSender::Delegate implemenation.
+ void WriteEncoderStreamData(QuicStringPiece data) override;
+
+ // QpackDecoder::EncoderStreamErrorDelegate implementation.
+ void OnEncoderStreamError(QuicStringPiece error_message) override;
+
+ // QpackDecoderStreamSender::Delegate implementation.
+ void WriteDecoderStreamData(QuicStringPiece data) override;
+
+ // Called by |headers_stream_| when headers with a priority have been
+ // received for a stream. This method will only be called for server streams.
+ virtual void OnStreamHeadersPriority(QuicStreamId stream_id,
+ spdy::SpdyPriority priority);
+
+ // Called by |headers_stream_| when headers have been completely received
+ // for a stream. |fin| will be true if the fin flag was set in the headers
+ // frame.
+ virtual void OnStreamHeaderList(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list);
+
+ // Called by |headers_stream_| when push promise headers have been
+ // completely received. |fin| will be true if the fin flag was set
+ // in the headers.
+ virtual void OnPromiseHeaderList(QuicStreamId stream_id,
+ QuicStreamId promised_stream_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list);
+
+ // Called by |headers_stream_| when a PRIORITY frame has been received for a
+ // stream. This method will only be called for server streams.
+ virtual void OnPriorityFrame(QuicStreamId stream_id,
+ spdy::SpdyPriority priority);
+
+ // Sends contents of |iov| to h2_deframer_, returns number of bytes processed.
+ size_t ProcessHeaderData(const struct iovec& iov);
+
+ // Writes |headers| for the stream |id| to the dedicated headers stream.
+ // If |fin| is true, then no more data will be sent for the stream |id|.
+ // If provided, |ack_notifier_delegate| will be registered to be notified when
+ // we have seen ACKs for all packets resulting from this call.
+ virtual size_t WriteHeadersOnHeadersStream(
+ QuicStreamId id,
+ spdy::SpdyHeaderBlock headers,
+ bool fin,
+ spdy::SpdyPriority priority,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ // Writes a PRIORITY frame the to peer. Returns the size in bytes of the
+ // resulting PRIORITY frame for QUIC_VERSION_43 and above. Otherwise, does
+ // nothing and returns 0.
+ size_t WritePriority(QuicStreamId id,
+ QuicStreamId parent_stream_id,
+ int weight,
+ bool exclusive);
+
+ // Write |headers| for |promised_stream_id| on |original_stream_id| in a
+ // PUSH_PROMISE frame to peer.
+ // Return the size, in bytes, of the resulting PUSH_PROMISE frame.
+ virtual size_t WritePushPromise(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ spdy::SpdyHeaderBlock headers);
+
+ // Sends SETTINGS_MAX_HEADER_LIST_SIZE SETTINGS frame.
+ size_t SendMaxHeaderListSize(size_t value);
+
+ QpackEncoder* qpack_encoder();
+ QpackDecoder* qpack_decoder();
+ QuicHeadersStream* headers_stream() { return headers_stream_.get(); }
+
+ bool server_push_enabled() const { return server_push_enabled_; }
+
+ // Called by |QuicHeadersStream::UpdateEnableServerPush()| with
+ // value from SETTINGS_ENABLE_PUSH.
+ void set_server_push_enabled(bool enable) { server_push_enabled_ = enable; }
+
+ // Return true if this session wants to release headers stream's buffer
+ // aggressively.
+ virtual bool ShouldReleaseHeadersStreamSequencerBuffer();
+
+ void CloseConnectionWithDetails(QuicErrorCode error,
+ const std::string& details);
+
+ void set_max_inbound_header_list_size(size_t max_inbound_header_list_size) {
+ max_inbound_header_list_size_ = max_inbound_header_list_size;
+ }
+
+ size_t max_inbound_header_list_size() const {
+ return max_inbound_header_list_size_;
+ }
+
+ // Returns true if the session has active request streams.
+ bool HasActiveRequestStreams() const;
+
+ protected:
+ // Override CreateIncomingStream(), CreateOutgoingBidirectionalStream() and
+ // CreateOutgoingUnidirectionalStream() with QuicSpdyStream return type to
+ // make sure that all data streams are QuicSpdyStreams.
+ QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override = 0;
+ QuicSpdyStream* CreateIncomingStream(PendingStream pending) override = 0;
+ virtual QuicSpdyStream* CreateOutgoingBidirectionalStream() = 0;
+ virtual QuicSpdyStream* CreateOutgoingUnidirectionalStream() = 0;
+
+ QuicSpdyStream* GetSpdyDataStream(const QuicStreamId stream_id);
+
+ // If an incoming stream can be created, return true.
+ virtual bool ShouldCreateIncomingStream(QuicStreamId id) = 0;
+
+ // If an outgoing bidirectional/unidirectional stream can be created, return
+ // true.
+ virtual bool ShouldCreateOutgoingBidirectionalStream() = 0;
+ virtual bool ShouldCreateOutgoingUnidirectionalStream() = 0;
+
+ // Returns true if there are open HTTP requests.
+ bool ShouldKeepConnectionAlive() const override;
+
+ // Overridden to buffer incoming streams for version 99.
+ bool ShouldBufferIncomingStream(QuicStreamId id) const override;
+
+ size_t WriteHeadersOnHeadersStreamImpl(
+ QuicStreamId id,
+ spdy::SpdyHeaderBlock headers,
+ bool fin,
+ QuicStreamId parent_stream_id,
+ int weight,
+ bool exclusive,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
+ bool supports_push_promise() { return supports_push_promise_; }
+
+ // Optional, enables instrumentation related to go/quic-hpack.
+ void SetHpackEncoderDebugVisitor(
+ std::unique_ptr<QuicHpackDebugVisitor> visitor);
+ void SetHpackDecoderDebugVisitor(
+ std::unique_ptr<QuicHpackDebugVisitor> visitor);
+
+ // Sets the maximum size of the header compression table spdy_framer_ is
+ // willing to use to encode header blocks.
+ void UpdateHeaderEncoderTableSize(uint32_t value);
+
+ // Called when SETTINGS_ENABLE_PUSH is received, only supported on
+ // server side.
+ void UpdateEnableServerPush(bool value);
+
+ bool IsConnected() { return connection()->connected(); }
+
+ // Sets how much encoded data the hpack decoder of h2_deframer_ is willing to
+ // buffer.
+ void set_max_decode_buffer_size_bytes(size_t max_decode_buffer_size_bytes) {
+ h2_deframer_.GetHpackDecoder()->set_max_decode_buffer_size_bytes(
+ max_decode_buffer_size_bytes);
+ }
+
+ void set_max_uncompressed_header_bytes(
+ size_t set_max_uncompressed_header_bytes);
+
+ private:
+ friend class test::QuicSpdySessionPeer;
+
+ class SpdyFramerVisitor;
+
+ // The following methods are called by the SimpleVisitor.
+
+ // Called when a HEADERS frame has been received.
+ void OnHeaders(spdy::SpdyStreamId stream_id,
+ bool has_priority,
+ spdy::SpdyPriority priority,
+ bool fin);
+
+ // Called when a PUSH_PROMISE frame has been received.
+ void OnPushPromise(spdy::SpdyStreamId stream_id,
+ spdy::SpdyStreamId promised_stream_id,
+ bool end);
+
+ // Called when a PRIORITY frame has been received.
+ void OnPriority(spdy::SpdyStreamId stream_id, spdy::SpdyPriority priority);
+
+ // Called when the complete list of headers is available.
+ void OnHeaderList(const QuicHeaderList& header_list);
+
+ // Called when the size of the compressed frame payload is available.
+ void OnCompressedFrameSize(size_t frame_len);
+
+ std::unique_ptr<QpackEncoder> qpack_encoder_;
+ std::unique_ptr<QpackDecoder> qpack_decoder_;
+
+ // TODO(123528590): Remove this member.
+ std::unique_ptr<QuicHeadersStream> headers_stream_;
+
+ // The maximum size of a header block that will be accepted from the peer,
+ // defined per spec as key + value + overhead per field (uncompressed).
+ size_t max_inbound_header_list_size_;
+
+ // Set during handshake. If true, resources in x-associated-content and link
+ // headers will be pushed.
+ bool server_push_enabled_;
+
+ // Data about the stream whose headers are being processed.
+ QuicStreamId stream_id_;
+ QuicStreamId promised_stream_id_;
+ bool fin_;
+ size_t frame_len_;
+ size_t uncompressed_frame_len_;
+
+ bool supports_push_promise_;
+
+ spdy::SpdyFramer spdy_framer_;
+ http2::Http2DecoderAdapter h2_deframer_;
+ std::unique_ptr<SpdyFramerVisitor> spdy_framer_visitor_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_SESSION_H_
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
new file mode 100644
index 00000000000..04605e1faf7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc
@@ -0,0 +1,1808 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+
+#include <cstdint>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+using spdy::kV3HighestPriority;
+using spdy::Spdy3PriorityToHttp2Weight;
+using spdy::SpdyFramer;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyPriority;
+using spdy::SpdyPriorityIR;
+using spdy::SpdySerializedFrame;
+using testing::_;
+using testing::AtLeast;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker {
+ public:
+ explicit TestCryptoStream(QuicSession* session)
+ : QuicCryptoStream(session),
+ QuicCryptoHandshaker(this, session),
+ encryption_established_(false),
+ handshake_confirmed_(false),
+ params_(new QuicCryptoNegotiatedParameters) {}
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& /*message*/) override {
+ encryption_established_ = true;
+ handshake_confirmed_ = true;
+ CryptoHandshakeMessage msg;
+ std::string error_details;
+ session()->config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session()->config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session()->config()->ToHandshakeMessage(&msg);
+ const QuicErrorCode error =
+ session()->config()->ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ session()->OnConfigNegotiated();
+ session()->connection()->SetDefaultEncryptionLevel(
+ ENCRYPTION_FORWARD_SECURE);
+ session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+ }
+
+ // QuicCryptoStream implementation
+ bool encryption_established() const override {
+ return encryption_established_;
+ }
+ bool handshake_confirmed() const override { return handshake_confirmed_; }
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override {
+ return *params_;
+ }
+ CryptoMessageParser* crypto_message_parser() override {
+ return QuicCryptoHandshaker::crypto_message_parser();
+ }
+
+ MOCK_METHOD0(OnCanWrite, void());
+
+ bool HasPendingCryptoRetransmission() override { return false; }
+
+ MOCK_CONST_METHOD0(HasPendingRetransmission, bool());
+
+ private:
+ using QuicCryptoStream::session;
+
+ bool encryption_established_;
+ bool handshake_confirmed_;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+};
+
+class TestHeadersStream : public QuicHeadersStream {
+ public:
+ explicit TestHeadersStream(QuicSpdySession* session)
+ : QuicHeadersStream(session) {}
+
+ MOCK_METHOD0(OnCanWrite, void());
+};
+
+class TestStream : public QuicSpdyStream {
+ public:
+ TestStream(QuicStreamId id, QuicSpdySession* session, StreamType type)
+ : QuicSpdyStream(id, session, type) {}
+
+ TestStream(PendingStream pending, QuicSpdySession* session, StreamType type)
+ : QuicSpdyStream(std::move(pending), session, type) {}
+
+ using QuicStream::CloseWriteSide;
+
+ void OnBodyAvailable() override {}
+
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD3(RetransmitStreamData,
+ bool(QuicStreamOffset, QuicByteCount, bool));
+
+ MOCK_CONST_METHOD0(HasPendingRetransmission, bool());
+};
+
+class TestSession : public QuicSpdySession {
+ public:
+ explicit TestSession(QuicConnection* connection)
+ : QuicSpdySession(connection,
+ nullptr,
+ DefaultQuicConfig(),
+ CurrentSupportedVersions()),
+ crypto_stream_(this),
+ writev_consumes_all_data_(false) {
+ Initialize();
+ this->connection()->SetEncrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullEncrypter>(connection->perspective()));
+ }
+
+ ~TestSession() override { delete connection(); }
+
+ TestCryptoStream* GetMutableCryptoStream() override {
+ return &crypto_stream_;
+ }
+
+ const TestCryptoStream* GetCryptoStream() const override {
+ return &crypto_stream_;
+ }
+
+ TestStream* CreateOutgoingBidirectionalStream() override {
+ TestStream* stream = new TestStream(GetNextOutgoingBidirectionalStreamId(),
+ this, BIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ TestStream* CreateOutgoingUnidirectionalStream() override {
+ TestStream* stream = new TestStream(GetNextOutgoingUnidirectionalStreamId(),
+ this, WRITE_UNIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ TestStream* CreateIncomingStream(QuicStreamId id) override {
+ // Enforce the limit on the number of open streams.
+ if (GetNumOpenIncomingStreams() + 1 >
+ max_open_incoming_bidirectional_streams() &&
+ connection()->transport_version() != QUIC_VERSION_99) {
+ connection()->CloseConnection(
+ QUIC_TOO_MANY_OPEN_STREAMS, "Too many streams!",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return nullptr;
+ } else {
+ TestStream* stream = new TestStream(
+ id, this,
+ DetermineStreamType(id, connection()->transport_version(),
+ perspective(), /*is_incoming=*/true,
+ BIDIRECTIONAL));
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+ }
+
+ TestStream* CreateIncomingStream(PendingStream pending) override {
+ QuicStreamId id = pending.id();
+ TestStream* stream =
+ new TestStream(std::move(pending), this,
+ DetermineStreamType(
+ id, connection()->transport_version(), perspective(),
+ /*is_incoming=*/true, BIDIRECTIONAL));
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ bool ShouldCreateIncomingStream(QuicStreamId /*id*/) override { return true; }
+
+ bool ShouldCreateOutgoingBidirectionalStream() override { return true; }
+ bool ShouldCreateOutgoingUnidirectionalStream() override { return true; }
+
+ bool IsClosedStream(QuicStreamId id) {
+ return QuicSession::IsClosedStream(id);
+ }
+
+ QuicStream* GetOrCreateDynamicStream(QuicStreamId stream_id) {
+ return QuicSpdySession::GetOrCreateDynamicStream(stream_id);
+ }
+
+ QuicConsumedData WritevData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) override {
+ bool fin = state != NO_FIN;
+ QuicConsumedData consumed(write_length, fin);
+ if (!writev_consumes_all_data_) {
+ consumed =
+ QuicSession::WritevData(stream, id, write_length, offset, state);
+ }
+ if (fin && consumed.fin_consumed) {
+ stream->set_fin_sent(true);
+ }
+ QuicSessionPeer::GetWriteBlockedStreams(this)->UpdateBytesForStream(
+ id, consumed.bytes_consumed);
+ return consumed;
+ }
+
+ void set_writev_consumes_all_data(bool val) {
+ writev_consumes_all_data_ = val;
+ }
+
+ QuicConsumedData SendStreamData(QuicStream* stream) {
+ struct iovec iov;
+ if ((QuicVersionUsesCryptoFrames(connection()->transport_version()) ||
+ stream->id() !=
+ QuicUtils::GetCryptoStreamId(connection()->transport_version())) &&
+ connection()->encryption_level() != ENCRYPTION_FORWARD_SECURE) {
+ this->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ }
+ MakeIOVector("not empty", &iov);
+ QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9);
+ QuicConsumedData consumed = WritevData(stream, stream->id(), 9, 0, FIN);
+ QuicStreamPeer::SendBuffer(stream).OnStreamDataConsumed(
+ consumed.bytes_consumed);
+ return consumed;
+ }
+
+ bool ClearControlFrame(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ QuicConsumedData SendLargeFakeData(QuicStream* stream, int bytes) {
+ DCHECK(writev_consumes_all_data_);
+ return WritevData(stream, stream->id(), bytes, 0, FIN);
+ }
+
+ using QuicSession::closed_streams;
+ using QuicSession::zombie_streams;
+ using QuicSpdySession::ShouldBufferIncomingStream;
+
+ private:
+ StrictMock<TestCryptoStream> crypto_stream_;
+
+ bool writev_consumes_all_data_;
+};
+
+class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ bool ClearMaxStreamIdControlFrame(const QuicFrame& frame) {
+ if (frame.type == MAX_STREAM_ID_FRAME) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+ return false;
+ }
+
+ protected:
+ explicit QuicSpdySessionTestBase(Perspective perspective)
+ : connection_(
+ new StrictMock<MockQuicConnection>(&helper_,
+ &alarm_factory_,
+ perspective,
+ SupportedVersions(GetParam()))),
+ session_(connection_) {
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ headers_[":host"] = "www.google.com";
+ headers_[":path"] = "/index.hml";
+ headers_[":scheme"] = "http";
+ headers_["cookie"] =
+ "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
+ "__utmc=160408618; "
+ "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
+ "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
+ "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
+ "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
+ "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
+ "1zFMi5vzcns38-8_Sns; "
+ "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
+ "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
+ "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
+ "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
+ "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
+ "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
+ "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
+ "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
+ "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
+ "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
+ "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
+ "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
+ "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
+ "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
+ "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+ .Times(testing::AnyNumber());
+ }
+
+ void CheckClosedStreams() {
+ QuicStreamId first_stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT);
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ first_stream_id =
+ QuicUtils::GetCryptoStreamId(connection_->transport_version());
+ }
+ for (QuicStreamId i = first_stream_id; i < 100; i++) {
+ if (!QuicContainsKey(closed_streams_, i)) {
+ EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i;
+ } else {
+ EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i;
+ }
+ }
+ }
+
+ void CloseStream(QuicStreamId id) {
+ if (!IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ } else {
+ // V99 has two frames, RST_STREAM and STOP_SENDING
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ }
+ EXPECT_CALL(*connection_, OnStreamReset(id, _));
+ session_.CloseStream(id);
+ closed_streams_.insert(id);
+ }
+
+ QuicTransportVersion transport_version() const {
+ return connection_->transport_version();
+ }
+
+ bool IsVersion99() const { return transport_version() == QUIC_VERSION_99; }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return GetNthClientInitiatedBidirectionalStreamId(transport_version(), n);
+ }
+
+ QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
+ return GetNthServerInitiatedBidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ QuicStreamId IdDelta() {
+ return QuicUtils::StreamIdDelta(connection_->transport_version());
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ TestSession session_;
+ std::set<QuicStreamId> closed_streams_;
+ SpdyHeaderBlock headers_;
+};
+
+class QuicSpdySessionTestServer : public QuicSpdySessionTestBase {
+ protected:
+ QuicSpdySessionTestServer()
+ : QuicSpdySessionTestBase(Perspective::IS_SERVER) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSpdySessionTestServer,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSpdySessionTestServer, ShouldBufferIncomingStreamUnidirectional) {
+ if (!IsVersion99()) {
+ return;
+ }
+ EXPECT_TRUE(session_.ShouldBufferIncomingStream(
+ QuicUtils::GetFirstUnidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT)));
+}
+
+TEST_P(QuicSpdySessionTestServer, ShouldBufferIncomingStreamBidirectional) {
+ if (!IsVersion99()) {
+ return;
+ }
+ EXPECT_FALSE(session_.ShouldBufferIncomingStream(
+ QuicUtils::GetFirstBidirectionalStreamId(connection_->transport_version(),
+ Perspective::IS_CLIENT)));
+}
+
+TEST_P(QuicSpdySessionTestServer, PeerAddress) {
+ EXPECT_EQ(QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort),
+ session_.peer_address());
+}
+
+TEST_P(QuicSpdySessionTestServer, SelfAddress) {
+ EXPECT_TRUE(session_.self_address().IsInitialized());
+}
+
+TEST_P(QuicSpdySessionTestServer, IsCryptoHandshakeConfirmed) {
+ EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
+ EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
+}
+
+TEST_P(QuicSpdySessionTestServer, IsClosedStreamDefault) {
+ // Ensure that no streams are initially closed.
+ QuicStreamId first_stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT);
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ first_stream_id =
+ QuicUtils::GetCryptoStreamId(connection_->transport_version());
+ }
+ for (QuicStreamId i = first_stream_id; i < 100; i++) {
+ EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i;
+ }
+}
+
+TEST_P(QuicSpdySessionTestServer, AvailableStreams) {
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedBidirectionalId(2)) != nullptr);
+ // Both client initiated streams with smaller stream IDs are available.
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedBidirectionalId(0)));
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedBidirectionalId(1)));
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedBidirectionalId(1)) != nullptr);
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedBidirectionalId(0)) != nullptr);
+}
+
+TEST_P(QuicSpdySessionTestServer, IsClosedStreamLocallyCreated) {
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0), stream2->id());
+ QuicSpdyStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_EQ(GetNthServerInitiatedBidirectionalId(1), stream4->id());
+
+ CheckClosedStreams();
+ CloseStream(GetNthServerInitiatedBidirectionalId(0));
+ CheckClosedStreams();
+ CloseStream(GetNthServerInitiatedBidirectionalId(1));
+ CheckClosedStreams();
+}
+
+TEST_P(QuicSpdySessionTestServer, IsClosedStreamPeerCreated) {
+ QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0);
+ QuicStreamId stream_id2 = GetNthClientInitiatedBidirectionalId(1);
+ session_.GetOrCreateDynamicStream(stream_id1);
+ session_.GetOrCreateDynamicStream(stream_id2);
+
+ CheckClosedStreams();
+ CloseStream(stream_id1);
+ CheckClosedStreams();
+ CloseStream(stream_id2);
+ // Create a stream, and make another available.
+ QuicStream* stream3 = session_.GetOrCreateDynamicStream(stream_id2 + 4);
+ CheckClosedStreams();
+ // Close one, but make sure the other is still not closed
+ CloseStream(stream3->id());
+ CheckClosedStreams();
+}
+
+TEST_P(QuicSpdySessionTestServer, MaximumAvailableOpenedStreams) {
+ if (IsVersion99()) {
+ // For IETF QUIC, we should be able to obtain the max allowed
+ // stream ID, the next ID should fail. Since the actual limit
+ // is not the number of open streams, we allocate the max and the max+2.
+ // Get the max allowed stream ID, this should succeed.
+ EXPECT_NE(nullptr,
+ session_.GetOrCreateDynamicStream(
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->actual_max_allowed_incoming_bidirectional_stream_id()));
+ EXPECT_NE(
+ nullptr,
+ session_.GetOrCreateDynamicStream(
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->actual_max_allowed_incoming_unidirectional_stream_id()));
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1);
+ // Get the (max allowed stream ID)++, this should fail.
+ EXPECT_EQ(nullptr,
+ session_.GetOrCreateDynamicStream(
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->actual_max_allowed_incoming_bidirectional_stream_id() +
+ IdDelta()));
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(1);
+ EXPECT_EQ(nullptr,
+ session_.GetOrCreateDynamicStream(
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->actual_max_allowed_incoming_unidirectional_stream_id() +
+ IdDelta()));
+ } else {
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
+ session_.GetOrCreateDynamicStream(stream_id);
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_NE(
+ nullptr,
+ session_.GetOrCreateDynamicStream(
+ stream_id +
+ IdDelta() *
+ (session_.max_open_incoming_bidirectional_streams() - 1)));
+ }
+}
+
+TEST_P(QuicSpdySessionTestServer, TooManyAvailableStreams) {
+ QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0);
+ QuicStreamId stream_id2;
+ EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id1));
+ // A stream ID which is too large to create.
+ stream_id2 = GetNthClientInitiatedBidirectionalId(
+ 2 * session_.MaxAvailableBidirectionalStreams() + 4);
+ if (IsVersion99()) {
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ } else {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _));
+ }
+ EXPECT_EQ(nullptr, session_.GetOrCreateDynamicStream(stream_id2));
+}
+
+TEST_P(QuicSpdySessionTestServer, ManyAvailableStreams) {
+ // When max_open_streams_ is 200, should be able to create 200 streams
+ // out-of-order, that is, creating the one with the largest stream ID first.
+ QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
+ // Create one stream.
+ session_.GetOrCreateDynamicStream(stream_id);
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ // Stream count is 200, GetNth... starts counting at 0, so the 200'th stream
+ // is 199.
+ EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedBidirectionalId(199)));
+}
+
+TEST_P(QuicSpdySessionTestServer,
+ DebugDFatalIfMarkingClosedStreamWriteBlocked) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamId closed_stream_id = stream2->id();
+ // Close the stream.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(closed_stream_id, _));
+ stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ std::string msg =
+ QuicStrCat("Marking unknown stream ", closed_stream_id, " blocked.");
+ EXPECT_QUIC_BUG(session_.MarkConnectionLevelWriteBlocked(closed_stream_id),
+ msg);
+}
+
+TEST_P(QuicSpdySessionTestServer, OnCanWrite) {
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ InSequence s;
+
+ // Reregister, to test the loop limit.
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ // 2 will get called a second time as it didn't finish its block
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() {
+ session_.SendStreamData(stream6);
+ }));
+ // 4 will not get called, as we exceeded the loop limit.
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSpdySessionTestServer, TestBatchedWrites) {
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.set_writev_consumes_all_data(true);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ // With two sessions blocked, we should get two write calls. They should both
+ // go to the first stream as it will only write 6k and mark itself blocked
+ // again.
+ InSequence s;
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendLargeFakeData(stream2, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendLargeFakeData(stream2, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ session_.OnCanWrite();
+
+ // We should get one more call for stream2, at which point it has used its
+ // write quota and we move over to stream 4.
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendLargeFakeData(stream2, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendLargeFakeData(stream4, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ }));
+ session_.OnCanWrite();
+
+ // Now let stream 4 do the 2nd of its 3 writes, but add a block for a high
+ // priority stream 6. 4 should be preempted. 6 will write but *not* block so
+ // will cede back to 4.
+ stream6->SetPriority(kV3HighestPriority);
+ EXPECT_CALL(*stream4, OnCanWrite())
+ .WillOnce(Invoke([this, stream4, stream6]() {
+ session_.SendLargeFakeData(stream4, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ }));
+ EXPECT_CALL(*stream6, OnCanWrite())
+ .WillOnce(Invoke([this, stream4, stream6]() {
+ session_.SendStreamData(stream6);
+ session_.SendLargeFakeData(stream4, 6000);
+ }));
+ session_.OnCanWrite();
+
+ // Stream4 alread did 6k worth of writes, so after doing another 12k it should
+ // cede and 2 should resume.
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendLargeFakeData(stream4, 12000);
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ }));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendLargeFakeData(stream2, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ session_.OnCanWrite();
+}
+
+TEST_P(QuicSpdySessionTestServer, OnCanWriteBundlesStreams) {
+ if (IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(
+ this, &QuicSpdySessionTestServer::ClearMaxStreamIdControlFrame));
+ }
+ // Encryption needs to be established before data can be sent.
+ CryptoHandshakeMessage msg;
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm, GetCongestionWindow())
+ .WillRepeatedly(Return(kMaxOutgoingPacketSize * 10));
+ EXPECT_CALL(*send_algorithm, InRecovery()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendStreamData(stream4);
+ }));
+ EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() {
+ session_.SendStreamData(stream6);
+ }));
+
+ // Expect that we only send one packet, the writes from different streams
+ // should be bundled together.
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+ EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _));
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSpdySessionTestServer, OnCanWriteCongestionControlBlocks) {
+ session_.set_writev_consumes_all_data(true);
+ InSequence s;
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() {
+ session_.SendStreamData(stream6);
+ }));
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false));
+ // stream4->OnCanWrite is not called.
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Still congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false));
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // stream4->OnCanWrite is called once the connection stops being
+ // congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendStreamData(stream4);
+ }));
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSpdySessionTestServer, OnCanWriteWriterBlocks) {
+ // Drive congestion control manually in order to ensure that
+ // application-limited signaling is handled correctly.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true));
+
+ // Drive packet writer manually.
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)).Times(0);
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+
+ EXPECT_CALL(*stream2, OnCanWrite()).Times(0);
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)).Times(0);
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSpdySessionTestServer, BufferedHandshake) {
+ session_.set_writev_consumes_all_data(true);
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
+
+ // Test that blocking other streams does not change our status.
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ TestStream* stream3 = session_.CreateOutgoingBidirectionalStream();
+ session_.MarkConnectionLevelWriteBlocked(stream3->id());
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ // Blocking (due to buffering of) the Crypto stream is detected.
+ session_.MarkConnectionLevelWriteBlocked(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ InSequence s;
+ // Force most streams to re-register, which is common scenario when we block
+ // the Crypto stream, and only the crypto stream can "really" write.
+
+ // Due to prioritization, we *should* be asked to write the crypto stream
+ // first.
+ // Don't re-register the crypto stream (which signals complete writing).
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(Invoke([this, stream3]() {
+ session_.SendStreamData(stream3);
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendStreamData(stream4);
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ }));
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote.
+}
+
+TEST_P(QuicSpdySessionTestServer, OnCanWriteWithClosedStream) {
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ CloseStream(stream6->id());
+
+ InSequence s;
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendStreamData(stream4);
+ }));
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSpdySessionTestServer,
+ OnCanWriteLimitsNumWritesIfFlowControlBlocked) {
+ // Drive congestion control manually in order to ensure that
+ // application-limited signaling is handled correctly.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true));
+
+ // Ensure connection level flow control blockage.
+ QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0);
+ EXPECT_TRUE(session_.flow_controller()->IsBlocked());
+ EXPECT_TRUE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+
+ // Mark the crypto and headers streams as write blocked, we expect them to be
+ // allowed to write later.
+ session_.MarkConnectionLevelWriteBlocked(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+
+ // Create a data stream, and although it is write blocked we never expect it
+ // to be allowed to write as we are connection level flow control blocked.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ session_.MarkConnectionLevelWriteBlocked(stream->id());
+ EXPECT_CALL(*stream, OnCanWrite()).Times(0);
+
+ // The crypto and headers streams should be called even though we are
+ // connection flow control blocked.
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+ QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr);
+ TestHeadersStream* headers_stream = new TestHeadersStream(&session_);
+ QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream);
+ session_.MarkConnectionLevelWriteBlocked(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()));
+ EXPECT_CALL(*headers_stream, OnCanWrite());
+
+ // After the crypto and header streams perform a write, the connection will be
+ // blocked by the flow control, hence it should become application-limited.
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSpdySessionTestServer, SendGoAway) {
+ if (IsVersion99()) {
+ // GoAway frames are not in version 99
+ return;
+ }
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(
+ Invoke(connection_, &MockQuicConnection::ReallySendControlFrame));
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+
+ const QuicStreamId kTestStreamId = 5u;
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ EXPECT_CALL(*connection_,
+ OnStreamReset(kTestStreamId, QUIC_STREAM_PEER_GOING_AWAY))
+ .Times(0);
+ EXPECT_TRUE(session_.GetOrCreateDynamicStream(kTestStreamId));
+}
+
+TEST_P(QuicSpdySessionTestServer, DoNotSendGoAwayTwice) {
+ if (IsVersion99()) {
+ // TODO(b/118808809): Enable this test for version 99 when GOAWAY is
+ // supported.
+ return;
+ }
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+}
+
+TEST_P(QuicSpdySessionTestServer, InvalidGoAway) {
+ if (IsVersion99()) {
+ // TODO(b/118808809): Enable this test for version 99 when GOAWAY is
+ // supported.
+ return;
+ }
+ QuicGoAwayFrame go_away(kInvalidControlFrameId, QUIC_PEER_GOING_AWAY,
+ session_.next_outgoing_bidirectional_stream_id(), "");
+ session_.OnGoAway(go_away);
+}
+
+// Test that server session will send a connectivity probe in response to a
+// connectivity probe on the same path.
+TEST_P(QuicSpdySessionTestServer, ServerReplyToConnecitivityProbe) {
+ QuicSocketAddress old_peer_address =
+ QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort);
+ EXPECT_EQ(old_peer_address, session_.peer_address());
+
+ QuicSocketAddress new_peer_address =
+ QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort + 1);
+
+ EXPECT_CALL(*connection_,
+ SendConnectivityProbingResponsePacket(new_peer_address));
+ if (IsVersion99()) {
+ // Need to explicitly do this to emulate the reception of a PathChallenge,
+ // which stores its payload for use in generating the response.
+ connection_->OnPathChallengeFrame(
+ QuicPathChallengeFrame(0, {{0, 1, 2, 3, 4, 5, 6, 7}}));
+ }
+ session_.OnConnectivityProbeReceived(session_.self_address(),
+ new_peer_address);
+ EXPECT_EQ(old_peer_address, session_.peer_address());
+}
+
+TEST_P(QuicSpdySessionTestServer, IncreasedTimeoutAfterCryptoHandshake) {
+ EXPECT_EQ(kInitialIdleTimeoutSecs + 3,
+ QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
+ CryptoHandshakeMessage msg;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+ EXPECT_EQ(kMaximumIdleTimeoutSecs + 3,
+ QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
+}
+
+TEST_P(QuicSpdySessionTestServer, RstStreamBeforeHeadersDecompressed) {
+ // Send two bytes of payload.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams());
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ if (!IsVersion99()) {
+ // For version99, OnStreamReset gets called because of the STOP_SENDING,
+ // below. EXPECT the call there.
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0), _));
+ }
+ QuicRstStreamFrame rst1(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ session_.OnRstStream(rst1);
+
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a
+ // one-way close.
+ if (IsVersion99()) {
+ // Only needed for version 99/IETF QUIC.
+ QuicStopSendingFrame stop_sending(
+ kInvalidControlFrameId, GetNthClientInitiatedBidirectionalId(0),
+ static_cast<QuicApplicationErrorCode>(QUIC_ERROR_PROCESSING_STREAM));
+ // Expect the RESET_STREAM that is generated in response to receiving a
+ // STOP_SENDING.
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM));
+ session_.OnStopSendingFrame(stop_sending);
+ }
+
+ EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams());
+ // Connection should remain alive.
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicSpdySessionTestServer, OnStreamFrameFinStaticStreamId) {
+ // Send two bytes of payload.
+ QuicStreamFrame data1(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()), true, 0,
+ QuicStringPiece("HT"));
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to close a static stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.OnStreamFrame(data1);
+}
+
+TEST_P(QuicSpdySessionTestServer, OnRstStreamStaticStreamId) {
+ // Send two bytes of payload.
+ QuicRstStreamFrame rst1(
+ kInvalidControlFrameId,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.OnRstStream(rst1);
+}
+
+TEST_P(QuicSpdySessionTestServer, OnStreamFrameInvalidStreamId) {
+ // Send two bytes of payload.
+ QuicStreamFrame data1(
+ QuicUtils::GetInvalidStreamId(connection_->transport_version()), true, 0,
+ QuicStringPiece("HT"));
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received data for an invalid stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.OnStreamFrame(data1);
+}
+
+TEST_P(QuicSpdySessionTestServer, OnRstStreamInvalidStreamId) {
+ // Send two bytes of payload.
+ QuicRstStreamFrame rst1(
+ kInvalidControlFrameId,
+ QuicUtils::GetInvalidStreamId(connection_->transport_version()),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received data for an invalid stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.OnRstStream(rst1);
+}
+
+TEST_P(QuicSpdySessionTestServer, HandshakeUnblocksFlowControlBlockedStream) {
+ // Test that if a stream is flow control blocked, then on receipt of the SHLO
+ // containing a suitable send window offset, the stream becomes unblocked.
+
+ // Ensure that Writev consumes all the data it is given (simulate no socket
+ // blocking).
+ session_.set_writev_consumes_all_data(true);
+
+ // Create a stream, and send enough data to make it flow control blocked.
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ std::string body(kMinimumFlowControlSendWindow, '.');
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AtLeast(1));
+ stream2->WriteOrBufferBody(body, false);
+ EXPECT_TRUE(stream2->flow_controller()->IsBlocked());
+ EXPECT_TRUE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_TRUE(session_.IsStreamFlowControlBlocked());
+
+ // Now complete the crypto handshake, resulting in an increased flow control
+ // send window.
+ CryptoHandshakeMessage msg;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+ EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked(&session_, stream2->id()));
+ // Stream is now unblocked.
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+}
+
+TEST_P(QuicSpdySessionTestServer,
+ HandshakeUnblocksFlowControlBlockedCryptoStream) {
+ if (QuicVersionUsesCryptoFrames(GetParam().transport_version)) {
+ // QUIC version 47 onwards uses CRYPTO frames for the handshake, so this
+ // test doesn't make sense for those versions.
+ 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->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ QuicHeadersStream* headers_stream =
+ QuicSpdySessionPeer::GetHeadersStream(&session_);
+ EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ if (IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ } else {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ }
+ for (QuicStreamId i = 0;
+ !crypto_stream->flow_controller()->IsBlocked() && 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);
+ crypto_stream->SendHandshakeMessage(crypto_message);
+ char buf[1000];
+ QuicDataWriter writer(1000, buf, NETWORK_BYTE_ORDER);
+ crypto_stream->WriteStreamData(offset, crypto_message.size(), &writer);
+ }
+ EXPECT_TRUE(crypto_stream->flow_controller()->IsBlocked());
+ EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked());
+ 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.
+ CryptoHandshakeMessage msg;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+ 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->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+}
+
+#if !defined(OS_IOS)
+// This test is failing flakily for iOS bots.
+// http://crbug.com/425050
+// NOTE: It's not possible to use the standard MAYBE_ convention to disable
+// this test on iOS because when this test gets instantiated it ends up with
+// various names that are dependent on the parameters passed.
+TEST_P(QuicSpdySessionTestServer,
+ HandshakeUnblocksFlowControlBlockedHeadersStream) {
+ // Test that if the header 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->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ QuicHeadersStream* headers_stream =
+ QuicSpdySessionPeer::GetHeadersStream(&session_);
+ EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ QuicStreamId stream_id = 5;
+ // Write until the header stream is flow control blocked.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ SpdyHeaderBlock headers;
+ SimpleRandom random;
+ while (!headers_stream->flow_controller()->IsBlocked() && stream_id < 2000) {
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ headers["header"] = QuicStrCat(random.RandUint64(), random.RandUint64(),
+ random.RandUint64());
+ session_.WriteHeadersOnHeadersStream(stream_id, headers.Clone(), true, 0,
+ nullptr);
+ stream_id += IdDelta();
+ }
+ // Write once more to ensure that the headers stream has buffered data. The
+ // random headers may have exactly filled the flow control window.
+ session_.WriteHeadersOnHeadersStream(stream_id, std::move(headers), true, 0,
+ nullptr);
+ EXPECT_TRUE(headers_stream->HasBufferedData());
+
+ EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked());
+ EXPECT_FALSE(crypto_stream->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_TRUE(session_.IsStreamFlowControlBlocked());
+ EXPECT_FALSE(session_.HasDataToWrite());
+
+ // Now complete the crypto handshake, resulting in an increased flow control
+ // send window.
+ CryptoHandshakeMessage msg;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+
+ // Stream is now unblocked and will no longer have buffered data.
+ EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ EXPECT_TRUE(headers_stream->HasBufferedData());
+ EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked(
+ &session_,
+ QuicUtils::GetHeadersStreamId(connection_->transport_version())));
+}
+#endif // !defined(OS_IOS)
+
+TEST_P(QuicSpdySessionTestServer,
+ ConnectionFlowControlAccountingRstOutOfOrder) {
+ // Test that when we receive an out of order stream RST we correctly adjust
+ // our connection level flow control receive window.
+ // On close, the stream should mark as consumed all bytes between the highest
+ // byte consumed so far and the final byte offset from the RST frame.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+
+ const QuicStreamOffset kByteOffset =
+ 1 + kInitialSessionFlowControlWindowForTest / 2;
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ if (!IsVersion99()) {
+ // For version99 the call to OnStreamReset happens as a result of receiving
+ // the STOP_SENDING, so set up the EXPECT there.
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ }
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
+ QUIC_STREAM_CANCELLED, kByteOffset);
+ session_.OnRstStream(rst_frame);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a
+ // one-way close.
+ if (IsVersion99()) {
+ // Only needed for version 99/IETF QUIC.
+ QuicStopSendingFrame stop_sending(
+ kInvalidControlFrameId, stream->id(),
+ static_cast<QuicApplicationErrorCode>(QUIC_STREAM_CANCELLED));
+ // Expect the RESET_STREAM that is generated in response to receiving a
+ // STOP_SENDING.
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream->id(), QUIC_STREAM_CANCELLED));
+ session_.OnStopSendingFrame(stop_sending);
+ }
+
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSpdySessionTestServer,
+ ConnectionFlowControlAccountingFinAndLocalReset) {
+ // Test the situation where we receive a FIN on a stream, and before we fully
+ // consume all the data from the sequencer buffer we locally RST the stream.
+ // The bytes between highest consumed byte, and the final byte offset that we
+ // determined when the FIN arrived, should be marked as consumed at the
+ // connection level flow controller when the stream is reset.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+
+ const QuicStreamOffset kByteOffset =
+ kInitialSessionFlowControlWindowForTest / 2 - 1;
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, ".");
+ session_.OnStreamFrame(frame);
+ EXPECT_TRUE(connection_->connected());
+
+ EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed());
+ EXPECT_EQ(kByteOffset + frame.data_length,
+ stream->flow_controller()->highest_received_byte_offset());
+
+ // Reset stream locally.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_EQ(kByteOffset + frame.data_length,
+ session_.flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingFinAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a FIN from the peer, we correctly adjust our connection level flow
+ // control receive window.
+
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64_t kInitialConnectionBytesConsumed = 567;
+ const uint64_t kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+
+ // Now receive a response from the peer with a FIN. We should handle this by
+ // adjusting the connection level flow control receive window to take into
+ // account the total number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ std::string body = "hello";
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, QuicStringPiece(body));
+ session_.OnStreamFrame(frame);
+
+ QuicStreamOffset total_stream_bytes_sent_by_peer =
+ kByteOffset + body.length();
+ EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(
+ kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingRstAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a RST from the peer, we correctly adjust our connection level flow
+ // control receive window.
+
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64_t kInitialConnectionBytesConsumed = 567;
+ const uint64_t kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream));
+
+ // Now receive a RST from the peer. We should handle this by adjusting the
+ // connection level flow control receive window to take into account the total
+ // number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
+ QUIC_STREAM_CANCELLED, kByteOffset);
+ session_.OnRstStream(rst_frame);
+
+ EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSpdySessionTestServer, InvalidStreamFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) stream flow control window from
+ // the peer results in the connection being torn down.
+ const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _));
+ session_.OnConfigNegotiated();
+}
+
+TEST_P(QuicSpdySessionTestServer, InvalidSessionFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) session flow control window
+ // from the peer results in the connection being torn down.
+ const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _));
+ session_.OnConfigNegotiated();
+}
+
+// Test negotiation of custom server initial flow control window.
+TEST_P(QuicSpdySessionTestServer, CustomFlowControlWindow) {
+ QuicTagVector copt;
+ copt.push_back(kIFW7);
+ QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt);
+
+ session_.OnConfigNegotiated();
+ EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize(
+ session_.flow_controller()));
+}
+
+TEST_P(QuicSpdySessionTestServer, FlowControlWithInvalidFinalOffset) {
+ // Test that if we receive a stream RST with a highest byte offset that
+ // violates flow control, that we close the connection.
+ const uint64_t kLargeOffset = kInitialSessionFlowControlWindowForTest + 1;
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _))
+ .Times(2);
+
+ // Check that stream frame + FIN results in connection close.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ QuicStreamFrame frame(stream->id(), true, kLargeOffset, QuicStringPiece());
+ session_.OnStreamFrame(frame);
+
+ // Check that RST results in connection close.
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
+ QUIC_STREAM_CANCELLED, kLargeOffset);
+ session_.OnRstStream(rst_frame);
+}
+
+TEST_P(QuicSpdySessionTestServer, WindowUpdateUnblocksHeadersStream) {
+ // Test that a flow control blocked headers stream gets unblocked on recipt of
+ // a WINDOW_UPDATE frame.
+
+ // Set the headers stream to be flow control blocked.
+ QuicHeadersStream* headers_stream =
+ QuicSpdySessionPeer::GetHeadersStream(&session_);
+ QuicFlowControllerPeer::SetSendWindowOffset(headers_stream->flow_controller(),
+ 0);
+ EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_TRUE(session_.IsStreamFlowControlBlocked());
+
+ // Unblock the headers stream by supplying a WINDOW_UPDATE.
+ QuicWindowUpdateFrame window_update_frame(kInvalidControlFrameId,
+ headers_stream->id(),
+ 2 * kMinimumFlowControlSendWindow);
+ session_.OnWindowUpdateFrame(window_update_frame);
+ EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+}
+
+TEST_P(QuicSpdySessionTestServer,
+ TooManyUnfinishedStreamsCauseServerRejectStream) {
+ // If a buggy/malicious peer creates too many streams that are not ended
+ // with a FIN or RST then we send an RST to refuse streams for versions other
+ // than version 99. In version 99 the connection gets closed.
+ const QuicStreamId kMaxStreams = 5;
+ QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+ const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0);
+ const QuicStreamId kFinalStreamId =
+ GetNthClientInitiatedBidirectionalId(kMaxStreams);
+ // Create kMaxStreams data streams, and close them all without receiving a
+ // FIN or a RST_STREAM from the client.
+ const QuicStreamId kNextId =
+ QuicUtils::StreamIdDelta(connection_->transport_version());
+ for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += kNextId) {
+ QuicStreamFrame data1(i, false, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ // EXPECT_EQ(1u, session_.GetNumOpenStreams());
+ if (!IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ } else {
+ // V99 has two frames, RST_STREAM and STOP_SENDING
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ }
+ // Close the stream only if not version 99. If we are version 99
+ // then closing the stream opens up the available stream id space,
+ // so we never bump into the limit.
+ EXPECT_CALL(*connection_, OnStreamReset(i, _));
+ session_.CloseStream(i);
+ }
+ // Try and open a stream that exceeds the limit.
+ if (!IsVersion99()) {
+ // On versions other than 99, opening such a stream results in a
+ // RST_STREAM.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ EXPECT_CALL(*connection_,
+ OnStreamReset(kFinalStreamId, QUIC_REFUSED_STREAM))
+ .Times(1);
+ } else {
+ // On version 99 opening such a stream results in a connection close.
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Stream id 28 above 24", _));
+ }
+ // Create one more data streams to exceed limit of open stream.
+ QuicStreamFrame data1(kFinalStreamId, false, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+}
+
+TEST_P(QuicSpdySessionTestServer, DrainingStreamsDoNotCountAsOpened) {
+ // Verify that a draining stream (which has received a FIN but not consumed
+ // it) does not count against the open quota (because it is closed from the
+ // protocol point of view).
+ if (IsVersion99()) {
+ // Version 99 will result in a MAX_STREAM_ID frame as streams are consumed
+ // (via the OnStreamFrame call) and then released (via
+ // StreamDraining). Eventually this node will believe that the peer is
+ // running low on available stream ids and then send a MAX_STREAM_ID frame,
+ // caught by this EXPECT_CALL.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ } else {
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ }
+ EXPECT_CALL(*connection_, OnStreamReset(_, QUIC_REFUSED_STREAM)).Times(0);
+ const QuicStreamId kMaxStreams = 5;
+ QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+
+ // Create kMaxStreams + 1 data streams, and mark them draining.
+ const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0);
+ const QuicStreamId kFinalStreamId =
+ GetNthClientInitiatedBidirectionalId(kMaxStreams + 1);
+ for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += IdDelta()) {
+ QuicStreamFrame data1(i, true, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams());
+ session_.StreamDraining(i);
+ EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams());
+ }
+}
+
+class QuicSpdySessionTestClient : public QuicSpdySessionTestBase {
+ protected:
+ QuicSpdySessionTestClient()
+ : QuicSpdySessionTestBase(Perspective::IS_CLIENT) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSpdySessionTestClient,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSpdySessionTestClient, AvailableStreamsClient) {
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedBidirectionalId(2)) != nullptr);
+ // Both server initiated streams with smaller stream IDs should be available.
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthServerInitiatedBidirectionalId(0)));
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthServerInitiatedBidirectionalId(1)));
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedBidirectionalId(0)) != nullptr);
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedBidirectionalId(1)) != nullptr);
+ // And client initiated stream ID should be not available.
+ EXPECT_FALSE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedBidirectionalId(0)));
+}
+
+TEST_P(QuicSpdySessionTestClient, RecordFinAfterReadSideClosed) {
+ // Verify that an incoming FIN is recorded in a stream object even if the read
+ // side has been closed. This prevents an entry from being made in
+ // locally_closed_streams_highest_offset_ (which will never be deleted).
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = stream->id();
+
+ // Close the read side manually.
+ QuicStreamPeer::CloseReadSide(stream);
+
+ // Receive a stream data frame with FIN.
+ QuicStreamFrame frame(stream_id, true, 0, QuicStringPiece());
+ session_.OnStreamFrame(frame);
+ EXPECT_TRUE(stream->fin_received());
+
+ // Reset stream locally.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream));
+
+ EXPECT_TRUE(connection_->connected());
+ EXPECT_TRUE(QuicSessionPeer::IsStreamClosed(&session_, stream_id));
+ EXPECT_FALSE(QuicSessionPeer::IsStreamCreated(&session_, stream_id));
+
+ // The stream is not waiting for the arrival of the peer's final offset as it
+ // was received with the FIN earlier.
+ EXPECT_EQ(
+ 0u,
+ QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(&session_).size());
+}
+
+TEST_P(QuicSpdySessionTestClient, WritePriority) {
+ QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr);
+ TestHeadersStream* headers_stream = new TestHeadersStream(&session_);
+ QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream);
+
+ // Make packet writer blocked so |headers_stream| will buffer its write data.
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true));
+
+ const QuicStreamId id = 4;
+ const QuicStreamId parent_stream_id = 9;
+ const SpdyPriority priority = kV3HighestPriority;
+ const bool exclusive = true;
+ session_.WritePriority(id, parent_stream_id,
+ Spdy3PriorityToHttp2Weight(priority), exclusive);
+
+ QuicStreamSendBuffer& send_buffer =
+ QuicStreamPeer::SendBuffer(headers_stream);
+ if (transport_version() > QUIC_VERSION_39) {
+ ASSERT_EQ(1u, send_buffer.size());
+
+ SpdyPriorityIR priority_frame(
+ id, parent_stream_id, Spdy3PriorityToHttp2Weight(priority), exclusive);
+ SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION);
+ SpdySerializedFrame frame = spdy_framer.SerializeFrame(priority_frame);
+
+ const QuicMemSlice& slice =
+ QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer)->slice;
+ EXPECT_EQ(QuicStringPiece(frame.data(), frame.size()),
+ QuicStringPiece(slice.data(), slice.length()));
+ } else {
+ EXPECT_EQ(0u, send_buffer.size());
+ }
+}
+
+TEST_P(QuicSpdySessionTestServer, ZombieStreams) {
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamPeer::SetStreamBytesWritten(3, stream2);
+ EXPECT_TRUE(stream2->IsWaitingForAcks());
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _));
+ session_.CloseStream(stream2->id());
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+ ASSERT_EQ(1u, session_.closed_streams()->size());
+ EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id());
+ session_.OnStreamDoneWaitingForAcks(2);
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+ EXPECT_EQ(1u, session_.closed_streams()->size());
+ EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id());
+}
+
+TEST_P(QuicSpdySessionTestServer, OnStreamFrameLost) {
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+ InSequence s;
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+
+ QuicStreamFrame frame2(stream2->id(), false, 0, 9);
+ QuicStreamFrame frame3(stream4->id(), false, 0, 9);
+
+ // Lost data on cryption stream, streams 2 and 4.
+ EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true));
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+ .WillOnce(Return(true));
+ }
+ EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true));
+ session_.OnFrameLost(QuicFrame(frame3));
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ QuicStreamFrame frame1(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()), false,
+ 0, 1300);
+ session_.OnFrameLost(QuicFrame(frame1));
+ } else {
+ QuicCryptoFrame crypto_frame(ENCRYPTION_INITIAL, 0, 1300);
+ session_.OnFrameLost(QuicFrame(&crypto_frame));
+ }
+ session_.OnFrameLost(QuicFrame(frame2));
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Mark streams 2 and 4 write blocked.
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ // Lost data is retransmitted before new data, and retransmissions for crypto
+ // stream go first.
+ // Do not check congestion window when crypto stream has lost data.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).Times(0);
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+ EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+ .WillOnce(Return(false));
+ }
+ // Check congestion window for non crypto streams.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream4, OnCanWrite());
+ EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(false));
+ // Connection is blocked.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false));
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Unblock connection.
+ // Stream 2 retransmits lost data.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false));
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ // Stream 2 sends new data.
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream4, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSpdySessionTestServer, DonotRetransmitDataOfClosedStreams) {
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+ InSequence s;
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ QuicStreamFrame frame1(stream2->id(), false, 0, 9);
+ QuicStreamFrame frame2(stream4->id(), false, 0, 9);
+ QuicStreamFrame frame3(stream6->id(), false, 0, 9);
+
+ EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(true));
+ EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true));
+ EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true));
+ session_.OnFrameLost(QuicFrame(frame3));
+ session_.OnFrameLost(QuicFrame(frame2));
+ session_.OnFrameLost(QuicFrame(frame1));
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+
+ // Reset stream 4 locally.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream4->id(), _));
+ stream4->Reset(QUIC_STREAM_CANCELLED);
+
+ // Verify stream 4 is removed from streams with lost data list.
+ EXPECT_CALL(*stream6, OnCanWrite());
+ EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(false));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false));
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*stream6, OnCanWrite());
+ session_.OnCanWrite();
+}
+
+TEST_P(QuicSpdySessionTestServer, RetransmitFrames) {
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+ InSequence s;
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ session_.SendWindowUpdate(stream2->id(), 9);
+
+ QuicStreamFrame frame1(stream2->id(), false, 0, 9);
+ QuicStreamFrame frame2(stream4->id(), false, 0, 9);
+ QuicStreamFrame frame3(stream6->id(), false, 0, 9);
+ QuicWindowUpdateFrame window_update(1, stream2->id(), 9);
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1));
+ frames.push_back(QuicFrame(&window_update));
+ frames.push_back(QuicFrame(frame2));
+ frames.push_back(QuicFrame(frame3));
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+
+ EXPECT_CALL(*stream2, RetransmitStreamData(_, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ EXPECT_CALL(*stream4, RetransmitStreamData(_, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*stream6, RetransmitStreamData(_, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+ session_.RetransmitFrames(frames, TLP_RETRANSMISSION);
+}
+
+TEST_P(QuicSpdySessionTestServer, OnPriorityFrame) {
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
+ TestStream* stream = session_.CreateIncomingStream(stream_id);
+ session_.OnPriorityFrame(stream_id, kV3HighestPriority);
+ EXPECT_EQ(kV3HighestPriority, stream->priority());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..674b0760d23
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.cc
@@ -0,0 +1,661 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyPriority;
+
+namespace quic {
+
+// Visitor of HttpDecoder that passes data frame to QuicSpdyStream and closes
+// the connection on unexpected frames.
+class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor {
+ public:
+ explicit HttpDecoderVisitor(QuicSpdyStream* stream) : stream_(stream) {}
+ HttpDecoderVisitor(const HttpDecoderVisitor&) = delete;
+ HttpDecoderVisitor& operator=(const HttpDecoderVisitor&) = delete;
+
+ void OnError(HttpDecoder* decoder) override {
+ stream_->session()->connection()->CloseConnection(
+ QUIC_HTTP_DECODER_ERROR, "Http decoder internal error",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ void OnPriorityFrame(const PriorityFrame& frame) override {
+ CloseConnectionOnWrongFrame("Priority");
+ }
+
+ void OnCancelPushFrame(const CancelPushFrame& frame) override {
+ CloseConnectionOnWrongFrame("Cancel Push");
+ }
+
+ void OnMaxPushIdFrame(const MaxPushIdFrame& frame) override {
+ CloseConnectionOnWrongFrame("Max Push Id");
+ }
+
+ void OnGoAwayFrame(const GoAwayFrame& frame) override {
+ CloseConnectionOnWrongFrame("Goaway");
+ }
+
+ void OnSettingsFrameStart(Http3FrameLengths frame_lengths) override {
+ CloseConnectionOnWrongFrame("Settings");
+ }
+
+ void OnSettingsFrame(const SettingsFrame& frame) override {
+ CloseConnectionOnWrongFrame("Settings");
+ }
+
+ void OnDuplicatePushFrame(const DuplicatePushFrame& frame) override {
+ CloseConnectionOnWrongFrame("Duplicate Push");
+ }
+
+ void OnDataFrameStart(Http3FrameLengths frame_lengths) override {
+ stream_->OnDataFrameStart(frame_lengths);
+ }
+
+ void OnDataFramePayload(QuicStringPiece payload) override {
+ DCHECK(!payload.empty());
+ stream_->OnDataFramePayload(payload);
+ }
+
+ void OnDataFrameEnd() override { stream_->OnDataFrameEnd(); }
+
+ void OnHeadersFrameStart(Http3FrameLengths frame_length) override {
+ if (!VersionUsesQpack(
+ stream_->session()->connection()->transport_version())) {
+ CloseConnectionOnWrongFrame("Headers");
+ return;
+ }
+ stream_->OnHeadersFrameStart(frame_length);
+ }
+
+ void OnHeadersFramePayload(QuicStringPiece payload) override {
+ DCHECK(!payload.empty());
+ if (!VersionUsesQpack(
+ stream_->session()->connection()->transport_version())) {
+ CloseConnectionOnWrongFrame("Headers");
+ return;
+ }
+ stream_->OnHeadersFramePayload(payload);
+ }
+
+ void OnHeadersFrameEnd() override {
+ if (!VersionUsesQpack(
+ stream_->session()->connection()->transport_version())) {
+ CloseConnectionOnWrongFrame("Headers");
+ return;
+ }
+ stream_->OnHeadersFrameEnd();
+ }
+
+ void OnPushPromiseFrameStart(PushId push_id) override {
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ void OnPushPromiseFramePayload(QuicStringPiece payload) override {
+ DCHECK(!payload.empty());
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ void OnPushPromiseFrameEnd() override {
+ CloseConnectionOnWrongFrame("Push Promise");
+ }
+
+ private:
+ void CloseConnectionOnWrongFrame(std::string frame_type) {
+ stream_->session()->connection()->CloseConnection(
+ QUIC_HTTP_DECODER_ERROR, frame_type + " frame received on data stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ QuicSpdyStream* stream_;
+};
+
+#define ENDPOINT \
+ (session()->perspective() == Perspective::IS_SERVER ? "Server: " \
+ : "Client:" \
+ " ")
+
+QuicSpdyStream::QuicSpdyStream(QuicStreamId id,
+ QuicSpdySession* spdy_session,
+ StreamType type)
+ : QuicStream(id, spdy_session, /*is_static=*/false, type),
+ spdy_session_(spdy_session),
+ visitor_(nullptr),
+ headers_decompressed_(false),
+ trailers_decompressed_(false),
+ trailers_consumed_(false),
+ http_decoder_visitor_(new HttpDecoderVisitor(this)),
+ body_buffer_(sequencer()),
+ ack_listener_(nullptr) {
+ DCHECK_NE(QuicUtils::GetCryptoStreamId(
+ spdy_session->connection()->transport_version()),
+ id);
+ // Don't receive any callbacks from the sequencer until headers
+ // are complete.
+ sequencer()->SetBlockedUntilFlush();
+
+ if (VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version())) {
+ sequencer()->set_level_triggered(true);
+ }
+ decoder_.set_visitor(http_decoder_visitor_.get());
+}
+
+QuicSpdyStream::QuicSpdyStream(PendingStream pending,
+ QuicSpdySession* spdy_session,
+ StreamType type)
+ : QuicStream(std::move(pending), type),
+ spdy_session_(spdy_session),
+ visitor_(nullptr),
+ headers_decompressed_(false),
+ trailers_decompressed_(false),
+ trailers_consumed_(false),
+ http_decoder_visitor_(new HttpDecoderVisitor(this)),
+ body_buffer_(sequencer()),
+ ack_listener_(nullptr) {
+ DCHECK_NE(QuicUtils::GetCryptoStreamId(
+ spdy_session->connection()->transport_version()),
+ id());
+ // Don't receive any callbacks from the sequencer until headers
+ // are complete.
+ sequencer()->SetBlockedUntilFlush();
+
+ if (VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version())) {
+ sequencer()->set_level_triggered(true);
+ }
+ decoder_.set_visitor(http_decoder_visitor_.get());
+}
+
+QuicSpdyStream::~QuicSpdyStream() {}
+
+size_t QuicSpdyStream::WriteHeaders(
+ SpdyHeaderBlock header_block,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ size_t bytes_written =
+ WriteHeadersImpl(std::move(header_block), fin, std::move(ack_listener));
+ if (fin) {
+ // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent.
+ set_fin_sent(true);
+ CloseWriteSide();
+ }
+ return bytes_written;
+}
+
+void QuicSpdyStream::WriteOrBufferBody(QuicStringPiece data, bool fin) {
+ if (!VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version()) ||
+ data.length() == 0) {
+ WriteOrBufferData(data, fin, nullptr);
+ return;
+ }
+ QuicConnection::ScopedPacketFlusher flusher(
+ spdy_session_->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+
+ // Write frame header.
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(data.length(), &buffer);
+ unacked_frame_headers_offsets_.Add(
+ send_buffer().stream_offset(),
+ send_buffer().stream_offset() + header_length);
+ QUIC_DLOG(INFO) << "Stream " << id() << " is writing header of length "
+ << header_length;
+ WriteOrBufferData(QuicStringPiece(buffer.get(), header_length), false,
+ nullptr);
+
+ // Write body.
+ QUIC_DLOG(INFO) << "Stream " << id() << " is writing body of length "
+ << data.length();
+ WriteOrBufferData(data, fin, nullptr);
+}
+
+size_t QuicSpdyStream::WriteTrailers(
+ SpdyHeaderBlock trailer_block,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ if (fin_sent()) {
+ QUIC_BUG << "Trailers cannot be sent after a FIN, on stream " << id();
+ return 0;
+ }
+
+ // The header block must contain the final offset for this stream, as the
+ // trailers may be processed out of order at the peer.
+ QUIC_DLOG(INFO) << "Inserting trailer: (" << kFinalOffsetHeaderKey << ", "
+ << stream_bytes_written() + BufferedDataBytes() << ")";
+ trailer_block.insert(
+ std::make_pair(kFinalOffsetHeaderKey,
+ QuicTextUtils::Uint64ToString(stream_bytes_written() +
+ BufferedDataBytes())));
+
+ // Write the trailing headers with a FIN, and close stream for writing:
+ // trailers are the last thing to be sent on a stream.
+ const bool kFin = true;
+ size_t bytes_written =
+ WriteHeadersImpl(std::move(trailer_block), kFin, std::move(ack_listener));
+ set_fin_sent(kFin);
+
+ // Trailers are the last thing to be sent on a stream, but if there is still
+ // queued data then CloseWriteSide() will cause it never to be sent.
+ if (BufferedDataBytes() == 0) {
+ CloseWriteSide();
+ }
+
+ return bytes_written;
+}
+
+QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov,
+ int count,
+ bool fin) {
+ QuicMemSliceStorage storage(
+ iov, count,
+ session()->connection()->helper()->GetStreamSendBufferAllocator(),
+ GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size));
+ return WriteBodySlices(storage.ToSpan(), fin);
+}
+
+QuicConsumedData QuicSpdyStream::WriteBodySlices(QuicMemSliceSpan slices,
+ bool fin) {
+ if (!VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version()) ||
+ slices.empty()) {
+ return WriteMemSlices(slices, fin);
+ }
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(slices.total_length(), &buffer);
+ if (!CanWriteNewDataAfterData(header_length)) {
+ return {0, false};
+ }
+
+ QuicConnection::ScopedPacketFlusher flusher(
+ spdy_session_->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+
+ // Write frame header.
+ struct iovec header_iov = {static_cast<void*>(buffer.get()), header_length};
+ 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) << "Stream " << id() << " is writing header of length "
+ << header_length;
+ WriteMemSlices(storage.ToSpan(), false);
+
+ // Write body.
+ QUIC_DLOG(INFO) << "Stream " << id() << " is writing body of length "
+ << slices.total_length();
+ return WriteMemSlices(slices, fin);
+}
+
+size_t QuicSpdyStream::Readv(const struct iovec* iov, size_t iov_len) {
+ DCHECK(FinishedReadingHeaders());
+ if (!VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version())) {
+ return sequencer()->Readv(iov, iov_len);
+ }
+ return body_buffer_.ReadBody(iov, iov_len);
+}
+
+int QuicSpdyStream::GetReadableRegions(iovec* iov, size_t iov_len) const {
+ DCHECK(FinishedReadingHeaders());
+ if (!VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version())) {
+ return sequencer()->GetReadableRegions(iov, iov_len);
+ }
+ return body_buffer_.PeekBody(iov, iov_len);
+}
+
+void QuicSpdyStream::MarkConsumed(size_t num_bytes) {
+ DCHECK(FinishedReadingHeaders());
+ if (!VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version())) {
+ sequencer()->MarkConsumed(num_bytes);
+ return;
+ }
+ body_buffer_.MarkBodyConsumed(num_bytes);
+}
+
+bool QuicSpdyStream::IsDoneReading() const {
+ bool done_reading_headers = FinishedReadingHeaders();
+ bool done_reading_body = sequencer()->IsClosed();
+ bool done_reading_trailers = FinishedReadingTrailers();
+ return done_reading_headers && done_reading_body && done_reading_trailers;
+}
+
+bool QuicSpdyStream::HasBytesToRead() const {
+ if (!VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version())) {
+ return sequencer()->HasBytesToRead();
+ }
+ return body_buffer_.HasBytesToRead();
+}
+
+void QuicSpdyStream::MarkTrailersConsumed() {
+ trailers_consumed_ = true;
+}
+
+uint64_t QuicSpdyStream::total_body_bytes_read() const {
+ if (VersionHasDataFrameHeader(
+ spdy_session_->connection()->transport_version())) {
+ return body_buffer_.total_body_bytes_received();
+ }
+ return sequencer()->NumBytesConsumed();
+}
+
+void QuicSpdyStream::ConsumeHeaderList() {
+ header_list_.Clear();
+ if (FinishedReadingHeaders()) {
+ sequencer()->SetUnblocked();
+ }
+}
+
+void QuicSpdyStream::OnStreamHeadersPriority(SpdyPriority priority) {
+ DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective());
+ SetPriority(priority);
+}
+
+void QuicSpdyStream::OnStreamHeaderList(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ // The headers list avoid infinite buffering by clearing the headers list
+ // if the current headers are too large. So if the list is empty here
+ // then the headers list must have been too large, and the stream should
+ // be reset.
+ // TODO(rch): Use an explicit "headers too large" signal. An empty header list
+ // might be acceptable if it corresponds to a trailing header frame.
+ if (header_list.empty()) {
+ OnHeadersTooLarge();
+ if (IsDoneReading()) {
+ return;
+ }
+ }
+ if (!headers_decompressed_) {
+ OnInitialHeadersComplete(fin, frame_len, header_list);
+ } else {
+ OnTrailingHeadersComplete(fin, frame_len, header_list);
+ }
+}
+
+void QuicSpdyStream::OnHeadersTooLarge() {
+ Reset(QUIC_HEADERS_TOO_LARGE);
+}
+
+void QuicSpdyStream::OnInitialHeadersComplete(
+ bool fin,
+ size_t /*frame_len*/,
+ const QuicHeaderList& header_list) {
+ headers_decompressed_ = true;
+ header_list_ = header_list;
+ if (fin) {
+ OnStreamFrame(QuicStreamFrame(id(), fin, 0, QuicStringPiece()));
+ }
+ if (FinishedReadingHeaders()) {
+ sequencer()->SetUnblocked();
+ }
+}
+
+void QuicSpdyStream::OnPromiseHeaderList(
+ QuicStreamId /* promised_id */,
+ size_t /* frame_len */,
+ const QuicHeaderList& /*header_list */) {
+ // To be overridden in QuicSpdyClientStream. Not supported on
+ // server side.
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_HEADERS_STREAM_DATA, "Promise headers received by server",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+void QuicSpdyStream::OnTrailingHeadersComplete(
+ bool fin,
+ size_t /*frame_len*/,
+ const QuicHeaderList& header_list) {
+ DCHECK(!trailers_decompressed_);
+ if (fin_received()) {
+ QUIC_DLOG(ERROR) << "Received Trailers after FIN, on stream: " << id();
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers after fin",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ if (!fin) {
+ QUIC_DLOG(ERROR) << "Trailers must have FIN set, on stream: " << id();
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_HEADERS_STREAM_DATA, "Fin missing from trailers",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ size_t final_byte_offset = 0;
+ if (!SpdyUtils::CopyAndValidateTrailers(header_list,
+ /* expect_final_byte_offset = */ true,
+ &final_byte_offset,
+ &received_trailers_)) {
+ QUIC_DLOG(ERROR) << "Trailers for stream " << id() << " are malformed.";
+ session()->connection()->CloseConnection(
+ QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers are malformed",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ trailers_decompressed_ = true;
+ OnStreamFrame(
+ QuicStreamFrame(id(), fin, final_byte_offset, QuicStringPiece()));
+}
+
+size_t QuicSpdyStream::WriteHeadersImpl(
+ spdy::SpdyHeaderBlock header_block,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ return spdy_session_->WriteHeadersOnHeadersStream(
+ id(), std::move(header_block), fin, priority(), std::move(ack_listener));
+}
+
+void QuicSpdyStream::OnPriorityFrame(SpdyPriority priority) {
+ DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective());
+ SetPriority(priority);
+}
+
+void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ if (frame.error_code != QUIC_STREAM_NO_ERROR) {
+ QuicStream::OnStreamReset(frame);
+ return;
+ }
+ QUIC_DVLOG(1) << "Received QUIC_STREAM_NO_ERROR, not discarding response";
+ set_rst_received(true);
+ MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
+ set_stream_error(frame.error_code);
+ CloseWriteSide();
+}
+
+void QuicSpdyStream::OnDataAvailable() {
+ DCHECK(FinishedReadingHeaders());
+
+ if (!VersionHasDataFrameHeader(
+ session()->connection()->transport_version())) {
+ OnBodyAvailable();
+ return;
+ }
+
+ iovec iov;
+ while (!reading_stopped() && sequencer()->PrefetchNextRegion(&iov)) {
+ decoder_.ProcessInput(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len);
+ }
+
+ if (body_buffer_.HasBytesToRead()) {
+ OnBodyAvailable();
+ return;
+ }
+
+ if (sequencer()->IsClosed()) {
+ OnBodyAvailable();
+ return;
+ }
+}
+
+void QuicSpdyStream::OnClose() {
+ QuicStream::OnClose();
+
+ if (visitor_) {
+ Visitor* visitor = visitor_;
+ // Calling Visitor::OnClose() may result the destruction of the visitor,
+ // so we need to ensure we don't call it again.
+ visitor_ = nullptr;
+ visitor->OnClose(this);
+ }
+}
+
+void QuicSpdyStream::OnCanWrite() {
+ QuicStream::OnCanWrite();
+
+ // Trailers (and hence a FIN) may have been sent ahead of queued body bytes.
+ if (!HasBufferedData() && fin_sent()) {
+ CloseWriteSide();
+ }
+}
+
+bool QuicSpdyStream::FinishedReadingHeaders() const {
+ return headers_decompressed_ && header_list_.empty();
+}
+
+bool QuicSpdyStream::ParseHeaderStatusCode(const SpdyHeaderBlock& header,
+ int* status_code) const {
+ SpdyHeaderBlock::const_iterator it = header.find(spdy::kHttp2StatusHeader);
+ if (it == header.end()) {
+ return false;
+ }
+ const QuicStringPiece status(it->second);
+ if (status.size() != 3) {
+ return false;
+ }
+ // First character must be an integer in range [1,5].
+ if (status[0] < '1' || status[0] > '5') {
+ return false;
+ }
+ // The remaining two characters must be integers.
+ if (!isdigit(status[1]) || !isdigit(status[2])) {
+ return false;
+ }
+ return QuicTextUtils::StringToInt(status, status_code);
+}
+
+bool QuicSpdyStream::FinishedReadingTrailers() const {
+ // If no further trailing headers are expected, and the decompressed trailers
+ // (if any) have been consumed, then reading of trailers is finished.
+ if (!fin_received()) {
+ return false;
+ } else if (!trailers_decompressed_) {
+ return true;
+ } else {
+ return trailers_consumed_;
+ }
+}
+
+void QuicSpdyStream::ClearSession() {
+ spdy_session_ = nullptr;
+}
+
+void QuicSpdyStream::OnDataFrameStart(Http3FrameLengths frame_lengths) {
+ DCHECK(
+ VersionHasDataFrameHeader(session()->connection()->transport_version()));
+
+ body_buffer_.OnDataHeader(frame_lengths);
+}
+
+void QuicSpdyStream::OnDataFramePayload(QuicStringPiece payload) {
+ DCHECK(
+ VersionHasDataFrameHeader(session()->connection()->transport_version()));
+
+ body_buffer_.OnDataPayload(payload);
+}
+
+void QuicSpdyStream::OnDataFrameEnd() {
+ DCHECK(
+ VersionHasDataFrameHeader(session()->connection()->transport_version()));
+ DVLOG(1) << "Reaches the end of a data frame. Total bytes received are "
+ << body_buffer_.total_body_bytes_received();
+}
+
+bool QuicSpdyStream::OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) {
+ const bool new_data_acked = QuicStream::OnStreamFrameAcked(
+ offset, data_length, fin_acked, ack_delay_time, newly_acked_length);
+
+ const QuicByteCount newly_acked_header_length =
+ GetNumFrameHeadersInInterval(offset, data_length);
+ DCHECK_LE(newly_acked_header_length, *newly_acked_length);
+ unacked_frame_headers_offsets_.Difference(offset, offset + data_length);
+ if (ack_listener_ != nullptr && new_data_acked) {
+ ack_listener_->OnPacketAcked(
+ *newly_acked_length - newly_acked_header_length, ack_delay_time);
+ }
+ return new_data_acked;
+}
+
+void QuicSpdyStream::OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted) {
+ QuicStream::OnStreamFrameRetransmitted(offset, data_length,
+ fin_retransmitted);
+
+ const QuicByteCount retransmitted_header_length =
+ GetNumFrameHeadersInInterval(offset, data_length);
+ DCHECK_LE(retransmitted_header_length, data_length);
+
+ if (ack_listener_ != nullptr) {
+ ack_listener_->OnPacketRetransmitted(data_length -
+ retransmitted_header_length);
+ }
+}
+
+QuicByteCount QuicSpdyStream::GetNumFrameHeadersInInterval(
+ QuicStreamOffset offset,
+ QuicByteCount data_length) const {
+ QuicByteCount header_acked_length = 0;
+ QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+ newly_acked.Intersection(unacked_frame_headers_offsets_);
+ for (const auto& interval : newly_acked) {
+ header_acked_length += interval.Length();
+ }
+ return header_acked_length;
+}
+
+void QuicSpdyStream::OnHeadersFrameStart(Http3FrameLengths frame_length) {
+ DCHECK(VersionUsesQpack(spdy_session_->connection()->transport_version()));
+}
+
+void QuicSpdyStream::OnHeadersFramePayload(QuicStringPiece payload) {
+ DCHECK(VersionUsesQpack(spdy_session_->connection()->transport_version()));
+}
+
+void QuicSpdyStream::OnHeadersFrameEnd() {
+ DCHECK(VersionUsesQpack(spdy_session_->connection()->transport_version()));
+}
+
+#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
new file mode 100644
index 00000000000..f81a421a44b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h
@@ -0,0 +1,283 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The base class for streams which deliver data to/from an application.
+// In each direction, the data on such a stream first contains compressed
+// headers then body data.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_H_
+
+#include <sys/types.h>
+
+#include <cstddef>
+#include <list>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+namespace test {
+class QuicSpdyStreamPeer;
+class QuicStreamPeer;
+} // namespace test
+
+class QuicSpdySession;
+
+// A QUIC stream that can send and receive HTTP2 (SPDY) headers.
+class QUIC_EXPORT_PRIVATE QuicSpdyStream : public QuicStream {
+ public:
+ // Visitor receives callbacks from the stream.
+ class QUIC_EXPORT_PRIVATE Visitor {
+ public:
+ Visitor() {}
+ Visitor(const Visitor&) = delete;
+ Visitor& operator=(const Visitor&) = delete;
+
+ // Called when the stream is closed.
+ virtual void OnClose(QuicSpdyStream* stream) = 0;
+
+ // Allows subclasses to override and do work.
+ virtual void OnPromiseHeadersComplete(QuicStreamId promised_id,
+ size_t frame_len) {}
+
+ protected:
+ virtual ~Visitor() {}
+ };
+
+ QuicSpdyStream(QuicStreamId id,
+ QuicSpdySession* spdy_session,
+ StreamType type);
+ QuicSpdyStream(PendingStream pending,
+ QuicSpdySession* spdy_session,
+ StreamType type);
+ QuicSpdyStream(const QuicSpdyStream&) = delete;
+ QuicSpdyStream& operator=(const QuicSpdyStream&) = delete;
+ ~QuicSpdyStream() override;
+
+ // QuicStream implementation
+ void OnClose() override;
+
+ // Override to maybe close the write side after writing.
+ void OnCanWrite() override;
+
+ // Called by the session when headers with a priority have been received
+ // for this stream. This method will only be called for server streams.
+ virtual void OnStreamHeadersPriority(spdy::SpdyPriority priority);
+
+ // Called by the session when decompressed headers have been completely
+ // delivered to this stream. If |fin| is true, then this stream
+ // should be closed; no more data will be sent by the peer.
+ virtual void OnStreamHeaderList(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list);
+
+ // Called by the session when decompressed push promise headers have
+ // been completely delivered to this stream.
+ virtual void OnPromiseHeaderList(QuicStreamId promised_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list);
+
+ // Called by the session when a PRIORITY frame has been been received for this
+ // stream. This method will only be called for server streams.
+ void OnPriorityFrame(spdy::SpdyPriority priority);
+
+ // Override the base class to not discard response when receiving
+ // QUIC_STREAM_NO_ERROR.
+ void OnStreamReset(const QuicRstStreamFrame& frame) override;
+
+ // Called by the sequencer when new data is available. Decodes the data and
+ // calls OnBodyAvailable() to pass to the upper layer.
+ void OnDataAvailable() override;
+
+ // Called in OnDataAvailable() after it finishes the decoding job.
+ virtual void OnBodyAvailable() = 0;
+
+ // Writes the headers contained in |header_block| to the dedicated
+ // headers stream.
+ virtual size_t WriteHeaders(
+ spdy::SpdyHeaderBlock header_block,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ // Sends |data| to the peer, or buffers if it can't be sent immediately.
+ void WriteOrBufferBody(QuicStringPiece data, bool fin);
+
+ // Writes the trailers contained in |trailer_block| to the dedicated
+ // headers stream. Trailers will always have the FIN set.
+ virtual size_t WriteTrailers(
+ spdy::SpdyHeaderBlock trailer_block,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ // Override to report newly acked bytes via ack_listener_.
+ bool OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) override;
+
+ // Override to report bytes retransmitted via ack_listener_.
+ void OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted) override;
+
+ // Does the same thing as WriteOrBufferBody except this method takes iovec
+ // as the data input. Right now it only calls WritevData.
+ // TODO(renjietang): Write data frame header before writing body.
+ QuicConsumedData WritevBody(const struct iovec* iov, int count, bool fin);
+
+ // Does the same thing as WriteOrBufferBody except this method takes
+ // memslicespan as the data input. Right now it only calls WriteMemSlices.
+ // TODO(renjietang): Write data frame header before writing body.
+ QuicConsumedData WriteBodySlices(QuicMemSliceSpan 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
+ // OnStreamHeaderList().
+ void MarkTrailersConsumed();
+
+ // Clears |header_list_|.
+ void ConsumeHeaderList();
+
+ // This block of functions wraps the sequencer's functions of the same
+ // name. These methods return uncompressed data until that has
+ // been fully processed. Then they simply delegate to the sequencer.
+ virtual size_t Readv(const struct iovec* iov, size_t iov_len);
+ virtual int GetReadableRegions(iovec* iov, size_t iov_len) const;
+ void MarkConsumed(size_t num_bytes);
+
+ // Returns true if header contains a valid 3-digit status and parse the status
+ // code to |status_code|.
+ bool ParseHeaderStatusCode(const spdy::SpdyHeaderBlock& header,
+ int* status_code) const;
+
+ // Returns true when all data has been read from the peer, including the fin.
+ bool IsDoneReading() const;
+ bool HasBytesToRead() const;
+
+ void set_visitor(Visitor* visitor) { visitor_ = visitor; }
+
+ bool headers_decompressed() const { return headers_decompressed_; }
+
+ // Returns total amount of body bytes that have been read.
+ uint64_t total_body_bytes_read() const;
+
+ const QuicHeaderList& header_list() const { return header_list_; }
+
+ bool trailers_decompressed() const { return trailers_decompressed_; }
+
+ // Returns whatever trailers have been received for this stream.
+ const spdy::SpdyHeaderBlock& received_trailers() const {
+ return received_trailers_;
+ }
+
+ // Returns true if headers have been fully read and consumed.
+ bool FinishedReadingHeaders() const;
+
+ // Returns true if trailers have been fully read and consumed, or FIN has
+ // been received and there are no trailers.
+ bool FinishedReadingTrailers() const;
+
+ // Called when owning session is getting deleted to avoid subsequent
+ // use of the spdy_session_ member.
+ void ClearSession();
+
+ // Returns true if the sequencer has delivered the FIN, and no more body bytes
+ // will be available.
+ bool IsClosed() { return sequencer()->IsClosed(); }
+
+ using QuicStream::CloseWriteSide;
+
+ protected:
+ // HTTP/3
+ void OnDataFrameStart(Http3FrameLengths frame_lengths);
+ void OnDataFramePayload(QuicStringPiece payload);
+ void OnDataFrameEnd();
+ void OnHeadersFrameStart(Http3FrameLengths frame_length);
+ void OnHeadersFramePayload(QuicStringPiece payload);
+ void OnHeadersFrameEnd();
+
+ // Called when the received headers are too large. By default this will
+ // reset the stream.
+ virtual void OnHeadersTooLarge();
+
+ virtual void OnInitialHeadersComplete(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list);
+ virtual void OnTrailingHeadersComplete(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list);
+ virtual size_t WriteHeadersImpl(
+ spdy::SpdyHeaderBlock header_block,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ QuicSpdySession* spdy_session() const { return spdy_session_; }
+ Visitor* visitor() { return visitor_; }
+
+ void set_headers_decompressed(bool val) { headers_decompressed_ = val; }
+
+ void set_ack_listener(
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ ack_listener_ = std::move(ack_listener);
+ }
+
+ private:
+ friend class test::QuicSpdyStreamPeer;
+ friend class test::QuicStreamPeer;
+ friend class QuicStreamUtils;
+ class HttpDecoderVisitor;
+
+ // Given the interval marked by [|offset|, |offset| + |data_length|), return
+ // the number of frame header bytes contained in it.
+ QuicByteCount GetNumFrameHeadersInInterval(QuicStreamOffset offset,
+ QuicByteCount data_length) const;
+
+ QuicSpdySession* spdy_session_;
+
+ Visitor* visitor_;
+ // True if the headers have been completely decompressed.
+ bool headers_decompressed_;
+ // Contains a copy of the decompressed header (name, value) pairs until they
+ // are consumed via Readv.
+ QuicHeaderList header_list_;
+
+ // True if the trailers have been completely decompressed.
+ bool trailers_decompressed_;
+ // True if the trailers have been consumed.
+ bool trailers_consumed_;
+ // The parsed trailers received from the peer.
+ spdy::SpdyHeaderBlock received_trailers_;
+
+ // Http encoder for writing streams.
+ HttpEncoder encoder_;
+ // Http decoder for processing raw incoming stream frames.
+ HttpDecoder decoder_;
+ // Visitor of the HttpDecoder.
+ std::unique_ptr<HttpDecoderVisitor> http_decoder_visitor_;
+ // Buffer that contains decoded data of the stream.
+ QuicSpdyStreamBodyBuffer body_buffer_;
+
+ // Ack listener of this stream, and it is notified when any of written bytes
+ // are acked or retransmitted.
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener_;
+
+ // Offset of unacked frame headers.
+ QuicIntervalSet<QuicStreamOffset> unacked_frame_headers_offsets_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.cc
new file mode 100644
index 00000000000..c0a77b29485
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.cc
@@ -0,0 +1,128 @@
+// 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 "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicSpdyStreamBodyBuffer::QuicSpdyStreamBodyBuffer(
+ QuicStreamSequencer* sequencer)
+ : bytes_remaining_(0),
+ total_body_bytes_readable_(0),
+ total_body_bytes_received_(0),
+ total_payload_lengths_(0),
+ sequencer_(sequencer) {}
+
+QuicSpdyStreamBodyBuffer::~QuicSpdyStreamBodyBuffer() {}
+
+void QuicSpdyStreamBodyBuffer::OnDataHeader(Http3FrameLengths frame_lengths) {
+ frame_meta_.push_back(frame_lengths);
+ total_payload_lengths_ += frame_lengths.payload_length;
+}
+
+void QuicSpdyStreamBodyBuffer::OnDataPayload(QuicStringPiece payload) {
+ DCHECK(!payload.empty());
+ bodies_.push_back(payload);
+ total_body_bytes_received_ += payload.length();
+ total_body_bytes_readable_ += payload.length();
+ DCHECK_LE(total_body_bytes_received_, total_payload_lengths_);
+}
+
+void QuicSpdyStreamBodyBuffer::MarkBodyConsumed(size_t num_bytes) {
+ // Check if the stream has enough decoded data.
+ if (num_bytes > total_body_bytes_readable_) {
+ QUIC_BUG << "Invalid argument to MarkBodyConsumed."
+ << " expect to consume: " << num_bytes
+ << ", but not enough bytes available. "
+ << "Total bytes readable are: " << total_body_bytes_readable_;
+ return;
+ }
+ // Discard references in the stream before the sequencer marks them consumed.
+ size_t remaining = num_bytes;
+ while (remaining > 0) {
+ if (bodies_.empty()) {
+ QUIC_BUG << "Failed to consume because body buffer is empty.";
+ return;
+ }
+ auto body = bodies_.front();
+ bodies_.pop_front();
+ if (body.length() <= remaining) {
+ remaining -= body.length();
+ } else {
+ body = body.substr(remaining, body.length() - remaining);
+ bodies_.push_front(body);
+ remaining = 0;
+ }
+ }
+ // Consume headers.
+ while (bytes_remaining_ < num_bytes) {
+ if (frame_meta_.empty()) {
+ QUIC_BUG << "Faild to consume because frame header buffer is empty.";
+ return;
+ }
+ auto meta = frame_meta_.front();
+ frame_meta_.pop_front();
+ bytes_remaining_ += meta.payload_length;
+ sequencer_->MarkConsumed(meta.header_length);
+ }
+ sequencer_->MarkConsumed(num_bytes);
+ // Update accountings.
+ bytes_remaining_ -= num_bytes;
+ total_body_bytes_readable_ -= num_bytes;
+}
+
+int QuicSpdyStreamBodyBuffer::PeekBody(iovec* iov, size_t iov_len) const {
+ DCHECK(iov != nullptr);
+ DCHECK_GT(iov_len, 0u);
+
+ if (bodies_.empty()) {
+ iov[0].iov_base = nullptr;
+ iov[0].iov_len = 0;
+ return 0;
+ }
+ // Fill iovs with references from the stream.
+ size_t iov_filled = 0;
+ while (iov_filled < bodies_.size() && iov_filled < iov_len) {
+ QuicStringPiece body = bodies_[iov_filled];
+ iov[iov_filled].iov_base = const_cast<char*>(body.data());
+ iov[iov_filled].iov_len = body.size();
+ iov_filled++;
+ }
+ return iov_filled;
+}
+
+size_t QuicSpdyStreamBodyBuffer::ReadBody(const struct iovec* iov,
+ size_t iov_len) {
+ size_t total_data_read = 0;
+ QuicByteCount total_remaining = total_body_bytes_readable_;
+ size_t index = 0;
+ size_t src_offset = 0;
+ for (size_t i = 0; i < iov_len && total_remaining > 0; ++i) {
+ char* dest = reinterpret_cast<char*>(iov[i].iov_base);
+ size_t dest_remaining = iov[i].iov_len;
+ while (dest_remaining > 0 && total_remaining > 0) {
+ auto body = bodies_[index];
+ size_t bytes_to_copy =
+ std::min<size_t>(body.length() - src_offset, dest_remaining);
+ memcpy(dest, body.substr(src_offset, bytes_to_copy).data(),
+ bytes_to_copy);
+ dest += bytes_to_copy;
+ dest_remaining -= bytes_to_copy;
+ total_data_read += bytes_to_copy;
+ total_remaining -= bytes_to_copy;
+ if (bytes_to_copy < body.length() - src_offset) {
+ src_offset += bytes_to_copy;
+ } else {
+ index++;
+ src_offset = 0;
+ }
+ }
+ }
+
+ MarkBodyConsumed(total_data_read);
+ return total_data_read;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h
new file mode 100644
index 00000000000..2485aacb446
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h
@@ -0,0 +1,75 @@
+// 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_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_
+#define QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_
+
+#include "net/third_party/quiche/src/quic/core/http/http_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+// Buffer decoded body for QuicSpdyStream. It also talks to the sequencer to
+// consume data.
+class QUIC_EXPORT_PRIVATE QuicSpdyStreamBodyBuffer {
+ public:
+ // QuicSpdyStreamBodyBuffer doesn't own the sequencer and the sequencer can
+ // outlive the buffer.
+ explicit QuicSpdyStreamBodyBuffer(QuicStreamSequencer* sequencer);
+
+ ~QuicSpdyStreamBodyBuffer();
+
+ // Add metadata of the frame to accountings.
+ // Called when QuicSpdyStream receives data frame header.
+ void OnDataHeader(Http3FrameLengths frame_lengths);
+
+ // Add new data payload to buffer.
+ // Called when QuicSpdyStream received data payload.
+ // Data pointed by payload must be alive until consumed by
+ // QuicStreamSequencer::MarkConsumed().
+ void OnDataPayload(QuicStringPiece payload);
+
+ // Take |num_bytes| as the body size, calculate header sizes accordingly, and
+ // consume the right amount of data in the stream sequencer.
+ void MarkBodyConsumed(size_t num_bytes);
+
+ // Fill up to |iov_len| with bodies available in buffer. No data is consumed.
+ // |iov|.iov_base will point to data in the buffer, and |iov|.iov_len will
+ // be set to the underlying data length accordingly.
+ // Returns the number of iov used.
+ int PeekBody(iovec* iov, size_t iov_len) const;
+
+ // Copies from buffer into |iov| up to |iov_len|, and consume data in
+ // sequencer. |iov.iov_base| and |iov.iov_len| are preassigned and will not be
+ // changed.
+ // Returns the number of bytes read.
+ size_t ReadBody(const struct iovec* iov, size_t iov_len);
+
+ bool HasBytesToRead() const { return !bodies_.empty(); }
+
+ uint64_t total_body_bytes_received() const {
+ return total_body_bytes_received_;
+ }
+
+ private:
+ // Storage for decoded data.
+ QuicDeque<QuicStringPiece> bodies_;
+ // Storage for header lengths.
+ QuicDeque<Http3FrameLengths> frame_meta_;
+ // Bytes in the first available data frame that are not consumed yet.
+ QuicByteCount bytes_remaining_;
+ // Total available body data in the stream.
+ QuicByteCount total_body_bytes_readable_;
+ // Total bytes read from the stream excluding headers.
+ QuicByteCount total_body_bytes_received_;
+ // Total length of payloads tracked by frame_meta_.
+ QuicByteCount total_payload_lengths_;
+ // Stream sequencer that directly manages data in the stream.
+ QuicStreamSequencer* sequencer_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_QUIC_SPDY_STREAM_BODY_BUFFER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer_test.cc b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer_test.cc
new file mode 100644
index 00000000000..ba2dbca22e3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer_test.cc
@@ -0,0 +1,241 @@
+// 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 "net/third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+class MockStream : public QuicStreamSequencer::StreamInterface {
+ public:
+ MOCK_METHOD0(OnFinRead, void());
+ MOCK_METHOD0(OnDataAvailable, void());
+ MOCK_METHOD2(CloseConnectionWithDetails,
+ void(QuicErrorCode error, const std::string& details));
+ MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error));
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD1(AddBytesConsumed, void(QuicByteCount bytes));
+
+ QuicStreamId id() const override { return 1; }
+
+ const QuicSocketAddress& PeerAddressOfLatestPacket() const override {
+ return peer_address_;
+ }
+
+ protected:
+ QuicSocketAddress peer_address_ =
+ QuicSocketAddress(QuicIpAddress::Any4(), 65535);
+};
+
+class MockSequencer : public QuicStreamSequencer {
+ public:
+ explicit MockSequencer(MockStream* stream) : QuicStreamSequencer(stream) {}
+ virtual ~MockSequencer() = default;
+ MOCK_METHOD1(MarkConsumed, void(size_t num_bytes_consumed));
+};
+
+class QuicSpdyStreamBodyBufferTest : public QuicTest {
+ public:
+ QuicSpdyStreamBodyBufferTest()
+ : sequencer_(&stream_), body_buffer_(&sequencer_) {}
+
+ protected:
+ MockStream stream_;
+ MockSequencer sequencer_;
+ QuicSpdyStreamBodyBuffer body_buffer_;
+ HttpEncoder encoder_;
+};
+
+TEST_F(QuicSpdyStreamBodyBufferTest, ReceiveBodies) {
+ std::string body(1024, 'a');
+ EXPECT_FALSE(body_buffer_.HasBytesToRead());
+ body_buffer_.OnDataHeader(Http3FrameLengths(3, 1024));
+ body_buffer_.OnDataPayload(QuicStringPiece(body));
+ EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received());
+ EXPECT_TRUE(body_buffer_.HasBytesToRead());
+}
+
+TEST_F(QuicSpdyStreamBodyBufferTest, PeekBody) {
+ std::string body(1024, 'a');
+ body_buffer_.OnDataHeader(Http3FrameLengths(3, 1024));
+ body_buffer_.OnDataPayload(QuicStringPiece(body));
+ EXPECT_EQ(1024u, body_buffer_.total_body_bytes_received());
+ iovec vec;
+ EXPECT_EQ(1, body_buffer_.PeekBody(&vec, 1));
+ EXPECT_EQ(1024u, vec.iov_len);
+ EXPECT_EQ(body,
+ QuicStringPiece(static_cast<const char*>(vec.iov_base), 1024));
+}
+
+// Buffer only receives 1 frame. Stream consumes less or equal than a frame.
+TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedPartialSingleFrame) {
+ testing::InSequence seq;
+ std::string body(1024, 'a');
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ Http3FrameLengths lengths(header_length, 1024);
+ std::string data = header + body;
+ QuicStreamFrame frame(1, false, 0, data);
+ sequencer_.OnStreamFrame(frame);
+ body_buffer_.OnDataHeader(lengths);
+ body_buffer_.OnDataPayload(QuicStringPiece(body));
+ EXPECT_CALL(stream_, AddBytesConsumed(header_length));
+ EXPECT_CALL(stream_, AddBytesConsumed(1024));
+ body_buffer_.MarkBodyConsumed(1024);
+}
+
+// Buffer received 2 frames. Stream consumes multiple times.
+TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedMultipleFrames) {
+ testing::InSequence seq;
+ // 1st frame.
+ std::string body1(1024, 'a');
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length1 =
+ encoder_.SerializeDataFrameHeader(body1.length(), &buffer);
+ std::string header1 = std::string(buffer.get(), header_length1);
+ Http3FrameLengths lengths1(header_length1, 1024);
+ std::string data1 = header1 + body1;
+ QuicStreamFrame frame1(1, false, 0, data1);
+ sequencer_.OnStreamFrame(frame1);
+ body_buffer_.OnDataHeader(lengths1);
+ body_buffer_.OnDataPayload(QuicStringPiece(body1));
+
+ // 2nd frame.
+ std::string body2(2048, 'b');
+ QuicByteCount header_length2 =
+ encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
+ std::string header2 = std::string(buffer.get(), header_length2);
+ Http3FrameLengths lengths2(header_length2, 2048);
+ std::string data2 = header2 + body2;
+ QuicStreamFrame frame2(1, false, data1.length(), data2);
+ sequencer_.OnStreamFrame(frame2);
+ body_buffer_.OnDataHeader(lengths2);
+ body_buffer_.OnDataPayload(QuicStringPiece(body2));
+
+ EXPECT_CALL(stream_, AddBytesConsumed(header_length1));
+ EXPECT_CALL(stream_, AddBytesConsumed(512));
+ body_buffer_.MarkBodyConsumed(512);
+ EXPECT_CALL(stream_, AddBytesConsumed(header_length2));
+ EXPECT_CALL(stream_, AddBytesConsumed(2048));
+ body_buffer_.MarkBodyConsumed(2048);
+ EXPECT_CALL(stream_, AddBytesConsumed(512));
+ body_buffer_.MarkBodyConsumed(512);
+}
+
+TEST_F(QuicSpdyStreamBodyBufferTest, MarkConsumedMoreThanBuffered) {
+ std::string body(1024, 'a');
+ Http3FrameLengths lengths(3, 1024);
+ body_buffer_.OnDataHeader(lengths);
+ body_buffer_.OnDataPayload(body);
+ EXPECT_QUIC_BUG(
+ body_buffer_.MarkBodyConsumed(2048),
+ "Invalid argument to MarkBodyConsumed. expect to consume: 2048, but not "
+ "enough bytes available. Total bytes readable are: 1024");
+}
+
+// Buffer receives 1 frame. Stream read from the buffer.
+TEST_F(QuicSpdyStreamBodyBufferTest, ReadSingleBody) {
+ testing::InSequence seq;
+ std::string body(1024, 'a');
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ Http3FrameLengths lengths(header_length, 1024);
+ std::string data = header + body;
+ QuicStreamFrame frame(1, false, 0, data);
+ sequencer_.OnStreamFrame(frame);
+ body_buffer_.OnDataHeader(lengths);
+ body_buffer_.OnDataPayload(QuicStringPiece(body));
+
+ EXPECT_CALL(stream_, AddBytesConsumed(header_length));
+ EXPECT_CALL(stream_, AddBytesConsumed(1024));
+
+ char base[1024];
+ iovec iov = {&base[0], 1024};
+ EXPECT_EQ(1024u, body_buffer_.ReadBody(&iov, 1));
+ EXPECT_EQ(1024u, iov.iov_len);
+ EXPECT_EQ(body,
+ QuicStringPiece(static_cast<const char*>(iov.iov_base), 1024));
+}
+
+// Buffer receives 2 frames, stream read from the buffer multiple times.
+TEST_F(QuicSpdyStreamBodyBufferTest, ReadMultipleBody) {
+ testing::InSequence seq;
+ // 1st frame.
+ std::string body1(1024, 'a');
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length1 =
+ encoder_.SerializeDataFrameHeader(body1.length(), &buffer);
+ std::string header1 = std::string(buffer.get(), header_length1);
+ Http3FrameLengths lengths1(header_length1, 1024);
+ std::string data1 = header1 + body1;
+ QuicStreamFrame frame1(1, false, 0, data1);
+ sequencer_.OnStreamFrame(frame1);
+ body_buffer_.OnDataHeader(lengths1);
+ body_buffer_.OnDataPayload(QuicStringPiece(body1));
+
+ // 2nd frame.
+ std::string body2(2048, 'b');
+ QuicByteCount header_length2 =
+ encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
+ std::string header2 = std::string(buffer.get(), header_length2);
+ Http3FrameLengths lengths2(header_length2, 2048);
+ std::string data2 = header2 + body2;
+ QuicStreamFrame frame2(1, false, data1.length(), data2);
+ sequencer_.OnStreamFrame(frame2);
+ body_buffer_.OnDataHeader(lengths2);
+ body_buffer_.OnDataPayload(QuicStringPiece(body2));
+
+ // First read of 512 bytes.
+ EXPECT_CALL(stream_, AddBytesConsumed(header_length1));
+ EXPECT_CALL(stream_, AddBytesConsumed(512));
+ char base[512];
+ iovec iov = {&base[0], 512};
+ EXPECT_EQ(512u, body_buffer_.ReadBody(&iov, 1));
+ EXPECT_EQ(512u, iov.iov_len);
+ EXPECT_EQ(body1.substr(0, 512),
+ QuicStringPiece(static_cast<const char*>(iov.iov_base), 512));
+
+ // Second read of 2048 bytes.
+ EXPECT_CALL(stream_, AddBytesConsumed(header_length2));
+ EXPECT_CALL(stream_, AddBytesConsumed(2048));
+ char base2[2048];
+ iovec iov2 = {&base2[0], 2048};
+ EXPECT_EQ(2048u, body_buffer_.ReadBody(&iov2, 1));
+ EXPECT_EQ(2048u, iov2.iov_len);
+ EXPECT_EQ(body1.substr(512, 512) + body2.substr(0, 1536),
+ QuicStringPiece(static_cast<const char*>(iov2.iov_base), 2048));
+
+ // Third read of the rest 512 bytes.
+ EXPECT_CALL(stream_, AddBytesConsumed(512));
+ char base3[512];
+ iovec iov3 = {&base3[0], 512};
+ EXPECT_EQ(512u, body_buffer_.ReadBody(&iov3, 1));
+ EXPECT_EQ(512u, iov3.iov_len);
+ EXPECT_EQ(body2.substr(1536, 512),
+ QuicStringPiece(static_cast<const char*>(iov3.iov_base), 512));
+}
+
+} // anonymous namespace
+
+} // namespace test
+
+} // 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
new file mode 100644
index 00000000000..1451533e8e0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/quic_spdy_stream_test.cc
@@ -0,0 +1,1559 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/http_encoder.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using spdy::kV3HighestPriority;
+using spdy::kV3LowestPriority;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyPriority;
+using testing::_;
+using testing::AtLeast;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+const bool kShouldProcessData = true;
+
+class TestStream : public QuicSpdyStream {
+ public:
+ TestStream(QuicStreamId id,
+ QuicSpdySession* session,
+ bool should_process_data)
+ : QuicSpdyStream(id, session, BIDIRECTIONAL),
+ should_process_data_(should_process_data) {}
+ ~TestStream() override = default;
+
+ using QuicSpdyStream::set_ack_listener;
+ using QuicStream::CloseWriteSide;
+ using QuicStream::WriteOrBufferData;
+
+ void OnBodyAvailable() override {
+ if (!should_process_data_) {
+ return;
+ }
+ char buffer[2048];
+ struct iovec vec;
+ vec.iov_base = buffer;
+ vec.iov_len = QUIC_ARRAYSIZE(buffer);
+ size_t bytes_read = Readv(&vec, 1);
+ data_ += std::string(buffer, bytes_read);
+ }
+
+ MOCK_METHOD1(WriteHeadersMock, void(bool fin));
+
+ size_t WriteHeadersImpl(spdy::SpdyHeaderBlock header_block,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface>
+ ack_listener) override {
+ saved_headers_ = std::move(header_block);
+ WriteHeadersMock(fin);
+ return 0;
+ }
+
+ const std::string& data() const { return data_; }
+ const spdy::SpdyHeaderBlock& saved_headers() const { return saved_headers_; }
+
+ private:
+ bool should_process_data_;
+ spdy::SpdyHeaderBlock saved_headers_;
+ std::string data_;
+};
+
+class TestMockUpdateStreamSession : public MockQuicSpdySession {
+ public:
+ explicit TestMockUpdateStreamSession(QuicConnection* connection)
+ : MockQuicSpdySession(connection) {}
+
+ void UpdateStreamPriority(QuicStreamId id, SpdyPriority priority) override {
+ EXPECT_EQ(id, expected_stream_->id());
+ EXPECT_EQ(expected_priority_, priority);
+ EXPECT_EQ(expected_priority_, expected_stream_->priority());
+ }
+
+ void SetExpectedStream(QuicSpdyStream* stream) { expected_stream_ = stream; }
+ void SetExpectedPriority(SpdyPriority priority) {
+ expected_priority_ = priority;
+ }
+
+ private:
+ QuicSpdyStream* expected_stream_;
+ SpdyPriority expected_priority_;
+};
+
+class QuicSpdyStreamTest : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ QuicSpdyStreamTest() {
+ headers_[":host"] = "www.google.com";
+ headers_[":path"] = "/index.hml";
+ headers_[":scheme"] = "https";
+ headers_["cookie"] =
+ "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
+ "__utmc=160408618; "
+ "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
+ "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
+ "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
+ "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
+ "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
+ "1zFMi5vzcns38-8_Sns; "
+ "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
+ "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
+ "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
+ "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
+ "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
+ "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
+ "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
+ "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
+ "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
+ "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
+ "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
+ "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
+ "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
+ "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
+ "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
+ }
+
+ void Initialize(bool stream_should_process_data) {
+ connection_ = new StrictMock<MockQuicConnection>(
+ &helper_, &alarm_factory_, Perspective::IS_SERVER,
+ SupportedVersions(GetParam()));
+ session_ = QuicMakeUnique<StrictMock<MockQuicSpdySession>>(connection_);
+ session_->Initialize();
+ ON_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillByDefault(Invoke(MockQuicSession::ConsumeData));
+
+ stream_ =
+ new StrictMock<TestStream>(GetNthClientInitiatedBidirectionalId(0),
+ session_.get(), stream_should_process_data);
+ session_->ActivateStream(QuicWrapUnique(stream_));
+ stream2_ =
+ new StrictMock<TestStream>(GetNthClientInitiatedBidirectionalId(1),
+ session_.get(), stream_should_process_data);
+ session_->ActivateStream(QuicWrapUnique(stream2_));
+ }
+
+ QuicHeaderList ProcessHeaders(bool fin, const SpdyHeaderBlock& headers) {
+ QuicHeaderList h = AsHeaderList(headers);
+ stream_->OnStreamHeaderList(fin, h.uncompressed_header_bytes(), h);
+ return h;
+ }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ bool HasFrameHeader() const {
+ return VersionHasDataFrameHeader(connection_->transport_version());
+ }
+
+ protected:
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnection* connection_;
+ std::unique_ptr<MockQuicSpdySession> session_;
+
+ // Owned by the |session_|.
+ TestStream* stream_;
+ TestStream* stream2_;
+
+ SpdyHeaderBlock headers_;
+
+ HttpEncoder encoder_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSpdyStreamTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSpdyStreamTest, ProcessHeaderList) {
+ Initialize(kShouldProcessData);
+
+ stream_->OnStreamHeadersPriority(kV3HighestPriority);
+ ProcessHeaders(false, headers_);
+ EXPECT_EQ("", stream_->data());
+ EXPECT_FALSE(stream_->header_list().empty());
+ EXPECT_FALSE(stream_->IsDoneReading());
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessTooLargeHeaderList) {
+ Initialize(kShouldProcessData);
+
+ QuicHeaderList headers;
+ stream_->OnStreamHeadersPriority(kV3HighestPriority);
+
+ EXPECT_CALL(*session_,
+ SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0));
+ stream_->OnStreamHeaderList(false, 1 << 20, headers);
+ EXPECT_EQ(QUIC_HEADERS_TOO_LARGE, stream_->stream_error());
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeaderListWithFin) {
+ Initialize(kShouldProcessData);
+
+ size_t total_bytes = 0;
+ QuicHeaderList headers;
+ for (auto p : headers_) {
+ headers.OnHeader(p.first, p.second);
+ total_bytes += p.first.size() + p.second.size();
+ }
+ stream_->OnStreamHeadersPriority(kV3HighestPriority);
+ stream_->OnStreamHeaderList(true, total_bytes, headers);
+ EXPECT_EQ("", stream_->data());
+ EXPECT_FALSE(stream_->header_list().empty());
+ EXPECT_FALSE(stream_->IsDoneReading());
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
+TEST_P(QuicSpdyStreamTest, ParseHeaderStatusCode) {
+ // A valid status code should be 3-digit integer. The first digit should be in
+ // the range of [1, 5]. All the others are invalid.
+ Initialize(kShouldProcessData);
+ int status_code = 0;
+
+ // Valid status codes.
+ headers_[":status"] = "404";
+ EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+ EXPECT_EQ(404, status_code);
+
+ headers_[":status"] = "100";
+ EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+ EXPECT_EQ(100, status_code);
+
+ headers_[":status"] = "599";
+ EXPECT_TRUE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+ EXPECT_EQ(599, status_code);
+
+ // Invalid status codes.
+ headers_[":status"] = "010";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = "600";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = "200 ok";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = "2000";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = "+200";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = "+20";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = "-10";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = "-100";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ // Leading or trailing spaces are also invalid.
+ headers_[":status"] = " 200";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = "200 ";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = " 200 ";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+
+ headers_[":status"] = " ";
+ EXPECT_FALSE(stream_->ParseHeaderStatusCode(headers_, &status_code));
+}
+
+TEST_P(QuicSpdyStreamTest, MarkHeadersConsumed) {
+ Initialize(kShouldProcessData);
+
+ std::string body = "this is the body";
+ QuicHeaderList headers = ProcessHeaders(false, headers_);
+ EXPECT_EQ(headers, stream_->header_list());
+
+ stream_->ConsumeHeaderList();
+ EXPECT_EQ(QuicHeaderList(), stream_->header_list());
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessWrongFramesOnSpdyStream) {
+ testing::InSequence s;
+ Initialize(kShouldProcessData);
+ if (!HasFrameHeader()) {
+ return;
+ }
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ GoAwayFrame goaway;
+ goaway.stream_id = 0x1;
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length = encoder_.SerializeGoAwayFrame(goaway, &buffer);
+ std::string data = std::string(buffer.get(), header_length);
+
+ EXPECT_EQ("", stream_->data());
+ QuicHeaderList headers = ProcessHeaders(false, headers_);
+ EXPECT_EQ(headers, stream_->header_list());
+ stream_->ConsumeHeaderList();
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_DECODER_ERROR, _, _))
+ .WillOnce(
+ (Invoke([this](QuicErrorCode error, const std::string& error_details,
+ ConnectionCloseBehavior connection_close_behavior) {
+ connection_->ReallyCloseConnection(error, error_details,
+ connection_close_behavior);
+ })));
+ EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
+ EXPECT_CALL(*session_, OnConnectionClosed(_, _, _))
+ .WillOnce(
+ Invoke([this](QuicErrorCode error, const std::string& error_details,
+ ConnectionCloseSource source) {
+ session_->ReallyOnConnectionClosed(error, error_details, source);
+ }));
+ EXPECT_CALL(*session_, SendRstStream(_, _, _));
+ EXPECT_CALL(*session_, SendRstStream(_, _, _));
+
+ stream_->OnStreamFrame(frame);
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBody) {
+ Initialize(kShouldProcessData);
+
+ std::string body = "this is the body";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ EXPECT_EQ("", stream_->data());
+ QuicHeaderList headers = ProcessHeaders(false, headers_);
+ EXPECT_EQ(headers, stream_->header_list());
+ stream_->ConsumeHeaderList();
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame);
+ EXPECT_EQ(QuicHeaderList(), stream_->header_list());
+ EXPECT_EQ(body, stream_->data());
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyFragments) {
+ Initialize(kShouldProcessData);
+ std::string body = "this is the body";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ for (size_t fragment_size = 1; fragment_size < data.size(); ++fragment_size) {
+ Initialize(kShouldProcessData);
+ QuicHeaderList headers = ProcessHeaders(false, headers_);
+ ASSERT_EQ(headers, stream_->header_list());
+ stream_->ConsumeHeaderList();
+ for (size_t offset = 0; offset < data.size(); offset += fragment_size) {
+ size_t remaining_data = data.size() - offset;
+ QuicStringPiece fragment(data.data() + offset,
+ std::min(fragment_size, remaining_data));
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false,
+ offset, QuicStringPiece(fragment));
+ stream_->OnStreamFrame(frame);
+ }
+ ASSERT_EQ(body, stream_->data()) << "fragment_size: " << fragment_size;
+ }
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyFragmentsSplit) {
+ Initialize(kShouldProcessData);
+ std::string body = "this is the body";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ for (size_t split_point = 1; split_point < data.size() - 1; ++split_point) {
+ Initialize(kShouldProcessData);
+ QuicHeaderList headers = ProcessHeaders(false, headers_);
+ ASSERT_EQ(headers, stream_->header_list());
+ stream_->ConsumeHeaderList();
+
+ QuicStringPiece fragment1(data.data(), split_point);
+ QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(fragment1));
+ stream_->OnStreamFrame(frame1);
+
+ QuicStringPiece fragment2(data.data() + split_point,
+ data.size() - split_point);
+ QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), false,
+ split_point, QuicStringPiece(fragment2));
+ stream_->OnStreamFrame(frame2);
+
+ ASSERT_EQ(body, stream_->data()) << "split_point: " << split_point;
+ }
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyReadv) {
+ Initialize(!kShouldProcessData);
+
+ std::string body = "this is the body";
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ ProcessHeaders(false, headers_);
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame);
+ stream_->ConsumeHeaderList();
+
+ char buffer[2048];
+ ASSERT_LT(data.length(), QUIC_ARRAYSIZE(buffer));
+ struct iovec vec;
+ vec.iov_base = buffer;
+ vec.iov_len = QUIC_ARRAYSIZE(buffer);
+
+ size_t bytes_read = stream_->Readv(&vec, 1);
+ QuicStreamPeer::CloseReadSide(stream_);
+ EXPECT_EQ(body.length(), bytes_read);
+ EXPECT_EQ(body, std::string(buffer, bytes_read));
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersAndLargeBodySmallReadv) {
+ Initialize(kShouldProcessData);
+ std::string body(12 * 1024, 'a');
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+ ProcessHeaders(false, headers_);
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame);
+ stream_->ConsumeHeaderList();
+ char buffer[2048];
+ char buffer2[2048];
+ struct iovec vec[2];
+ vec[0].iov_base = buffer;
+ vec[0].iov_len = QUIC_ARRAYSIZE(buffer);
+ vec[1].iov_base = buffer2;
+ vec[1].iov_len = QUIC_ARRAYSIZE(buffer2);
+ size_t bytes_read = stream_->Readv(vec, 2);
+ EXPECT_EQ(2048u * 2, bytes_read);
+ EXPECT_EQ(body.substr(0, 2048), std::string(buffer, 2048));
+ EXPECT_EQ(body.substr(2048, 2048), std::string(buffer2, 2048));
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyMarkConsumed) {
+ Initialize(!kShouldProcessData);
+
+ std::string body = "this is the body";
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ ProcessHeaders(false, headers_);
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame);
+ stream_->ConsumeHeaderList();
+
+ struct iovec vec;
+
+ EXPECT_EQ(1, stream_->GetReadableRegions(&vec, 1));
+ EXPECT_EQ(body.length(), vec.iov_len);
+ EXPECT_EQ(body, std::string(static_cast<char*>(vec.iov_base), vec.iov_len));
+
+ stream_->MarkConsumed(body.length());
+ EXPECT_EQ(data.length(), stream_->flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersAndConsumeMultipleBody) {
+ Initialize(!kShouldProcessData);
+ std::string body1 = "this is body 1";
+ std::string body2 = "body 2";
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body1.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data1 = HasFrameHeader() ? header + body1 : body1;
+ header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buf);
+ std::string data2 = HasFrameHeader() ? header + body2 : body2;
+
+ ProcessHeaders(false, headers_);
+ QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data1));
+ QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), false,
+ data1.length(), QuicStringPiece(data2));
+ stream_->OnStreamFrame(frame1);
+ stream_->OnStreamFrame(frame2);
+ stream_->ConsumeHeaderList();
+
+ stream_->MarkConsumed(body1.length() + body2.length());
+ EXPECT_EQ(data1.length() + data2.length(),
+ stream_->flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersAndBodyIncrementalReadv) {
+ Initialize(!kShouldProcessData);
+
+ std::string body = "this is the body";
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ ProcessHeaders(false, headers_);
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame);
+ stream_->ConsumeHeaderList();
+
+ char buffer[1];
+ struct iovec vec;
+ vec.iov_base = buffer;
+ vec.iov_len = QUIC_ARRAYSIZE(buffer);
+
+ for (size_t i = 0; i < body.length(); ++i) {
+ size_t bytes_read = stream_->Readv(&vec, 1);
+ ASSERT_EQ(1u, bytes_read);
+ EXPECT_EQ(body.data()[i], buffer[0]);
+ }
+}
+
+TEST_P(QuicSpdyStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
+ Initialize(!kShouldProcessData);
+
+ std::string body = "this is the body";
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ ProcessHeaders(false, headers_);
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame);
+ stream_->ConsumeHeaderList();
+
+ char buffer1[1];
+ char buffer2[1];
+ struct iovec vec[2];
+ vec[0].iov_base = buffer1;
+ vec[0].iov_len = QUIC_ARRAYSIZE(buffer1);
+ vec[1].iov_base = buffer2;
+ vec[1].iov_len = QUIC_ARRAYSIZE(buffer2);
+
+ for (size_t i = 0; i < body.length(); i += 2) {
+ size_t bytes_read = stream_->Readv(vec, 2);
+ ASSERT_EQ(2u, bytes_read) << i;
+ ASSERT_EQ(body.data()[i], buffer1[0]) << i;
+ ASSERT_EQ(body.data()[i + 1], buffer2[0]) << i;
+ }
+}
+
+TEST_P(QuicSpdyStreamTest, StreamFlowControlBlocked) {
+ testing::InSequence seq;
+ // Tests that we send a BLOCKED frame to the peer when we attempt to write,
+ // but are flow control blocked.
+ Initialize(kShouldProcessData);
+
+ // Set a small flow control limit.
+ const uint64_t kWindow = 36;
+ QuicFlowControllerPeer::SetSendWindowOffset(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::SendWindowOffset(
+ stream_->flow_controller()));
+
+ // Try to send more data than the flow control limit allows.
+ const uint64_t kOverflow = 15;
+ std::string body(kWindow + kOverflow, 'a');
+
+ const uint64_t kHeaderLength = HasFrameHeader() ? 2 : 0;
+ if (HasFrameHeader()) {
+ EXPECT_CALL(*session_, WritevData(_, _, kHeaderLength, _, NO_FIN));
+ }
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(kWindow - kHeaderLength, true)));
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ stream_->WriteOrBufferBody(body, false);
+
+ // Should have sent as much as possible, resulting in no send window left.
+ EXPECT_EQ(0u,
+ QuicFlowControllerPeer::SendWindowSize(stream_->flow_controller()));
+
+ // And we should have queued the overflowed data.
+ EXPECT_EQ(kOverflow + kHeaderLength, stream_->BufferedDataBytes());
+}
+
+TEST_P(QuicSpdyStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) {
+ // The flow control receive window decreases whenever we add new bytes to the
+ // sequencer, whether they are consumed immediately or buffered. However we
+ // only send WINDOW_UPDATE frames based on increasing number of bytes
+ // consumed.
+
+ // Don't process data - it will be buffered instead.
+ Initialize(!kShouldProcessData);
+
+ // Expect no WINDOW_UPDATE frames to be sent.
+ EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(0);
+
+ // Set a small flow control receive window.
+ const uint64_t kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream receives enough data to fill a fraction of the receive window.
+ std::string body(kWindow / 3, 'a');
+ QuicByteCount header_length = 0;
+ std::string data;
+
+ if (HasFrameHeader()) {
+ std::unique_ptr<char[]> buffer;
+ header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ data = header + body;
+ } else {
+ data = body;
+ }
+
+ ProcessHeaders(false, headers_);
+
+ QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame1);
+ EXPECT_EQ(
+ kWindow - (kWindow / 3) - header_length,
+ QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller()));
+
+ // Now receive another frame which results in the receive window being over
+ // half full. This should all be buffered, decreasing the receive window but
+ // not sending WINDOW_UPDATE.
+ QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), false,
+ kWindow / 3 + header_length, QuicStringPiece(data));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(
+ kWindow - (2 * kWindow / 3) - 2 * header_length,
+ QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller()));
+}
+
+TEST_P(QuicSpdyStreamTest, StreamFlowControlWindowUpdate) {
+ // Tests that on receipt of data, the stream updates its receive window offset
+ // appropriately, and sends WINDOW_UPDATE frames when its receive window drops
+ // too low.
+ Initialize(kShouldProcessData);
+
+ // Set a small flow control limit.
+ const uint64_t kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream receives enough data to fill a fraction of the receive window.
+ std::string body(kWindow / 3, 'a');
+ QuicByteCount header_length = 0;
+ std::string data;
+
+ if (HasFrameHeader()) {
+ std::unique_ptr<char[]> buffer;
+ header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ data = header + body;
+ } else {
+ data = body;
+ }
+
+ ProcessHeaders(false, headers_);
+ stream_->ConsumeHeaderList();
+
+ QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame1);
+ EXPECT_EQ(
+ kWindow - (kWindow / 3) - header_length,
+ QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller()));
+
+ // Now receive another frame which results in the receive window being over
+ // half full. This will trigger the stream to increase its receive window
+ // offset and send a WINDOW_UPDATE. The result will be again an available
+ // window of kWindow bytes.
+ QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), false,
+ kWindow / 3 + header_length, QuicStringPiece(data));
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
+}
+
+TEST_P(QuicSpdyStreamTest, ConnectionFlowControlWindowUpdate) {
+ // Tests that on receipt of data, the connection updates its receive window
+ // offset appropriately, and sends WINDOW_UPDATE frames when its receive
+ // window drops too low.
+ Initialize(kShouldProcessData);
+
+ // Set a small flow control limit for streams and connection.
+ const uint64_t kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream2_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream2_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(session_->flow_controller(),
+ kWindow);
+
+ // Supply headers to both streams so that they are happy to receive data.
+ auto headers = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+ stream_->ConsumeHeaderList();
+ stream2_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
+ headers);
+ stream2_->ConsumeHeaderList();
+
+ // Each stream gets a quarter window of data. This should not trigger a
+ // WINDOW_UPDATE for either stream, nor for the connection.
+ QuicByteCount header_length = 0;
+ std::string body;
+ std::string data;
+ std::string data2;
+ std::string body2(1, 'a');
+
+ if (HasFrameHeader()) {
+ body = std::string(kWindow / 4 - 2, 'a');
+ std::unique_ptr<char[]> buffer;
+ header_length = encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ data = header + body;
+ std::unique_ptr<char[]> buffer2;
+ QuicByteCount header_length2 =
+ encoder_.SerializeDataFrameHeader(body2.length(), &buffer2);
+ std::string header2 = std::string(buffer2.get(), header_length2);
+ data2 = header2 + body2;
+ } else {
+ body = std::string(kWindow / 4, 'a');
+ data = body;
+ data2 = body2;
+ }
+
+ QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ stream_->OnStreamFrame(frame1);
+ QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(1), false, 0,
+ QuicStringPiece(data));
+ stream2_->OnStreamFrame(frame2);
+
+ // Now receive a further single byte on one stream - again this does not
+ // trigger a stream WINDOW_UPDATE, but now the connection flow control window
+ // is over half full and thus a connection WINDOW_UPDATE is sent.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ QuicStreamFrame frame3(GetNthClientInitiatedBidirectionalId(0), false,
+ body.length() + header_length, QuicStringPiece(data2));
+ stream_->OnStreamFrame(frame3);
+}
+
+TEST_P(QuicSpdyStreamTest, StreamFlowControlViolation) {
+ // Tests that on if the peer sends too much data (i.e. violates the flow
+ // control protocol), then we terminate the connection.
+
+ // Stream should not process data, so that data gets buffered in the
+ // sequencer, triggering flow control limits.
+ Initialize(!kShouldProcessData);
+
+ // Set a small flow control limit.
+ const uint64_t kWindow = 50;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+
+ ProcessHeaders(false, headers_);
+
+ // Receive data to overflow the window, violating flow control.
+ std::string body(kWindow + 1, 'a');
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _));
+ stream_->OnStreamFrame(frame);
+}
+
+TEST_P(QuicSpdyStreamTest, TestHandlingQuicRstStreamNoError) {
+ Initialize(kShouldProcessData);
+ ProcessHeaders(false, headers_);
+
+ stream_->OnStreamReset(QuicRstStreamFrame(
+ kInvalidControlFrameId, stream_->id(), QUIC_STREAM_NO_ERROR, 0));
+ EXPECT_TRUE(stream_->write_side_closed());
+ EXPECT_FALSE(stream_->reading_stopped());
+}
+
+TEST_P(QuicSpdyStreamTest, ConnectionFlowControlViolation) {
+ // Tests that on if the peer sends too much data (i.e. violates the flow
+ // control protocol), at the connection level (rather than the stream level)
+ // then we terminate the connection.
+
+ // Stream should not process data, so that data gets buffered in the
+ // sequencer, triggering flow control limits.
+ Initialize(!kShouldProcessData);
+
+ // Set a small flow control window on streams, and connection.
+ const uint64_t kStreamWindow = 50;
+ const uint64_t kConnectionWindow = 10;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kStreamWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kConnectionWindow);
+
+ ProcessHeaders(false, headers_);
+
+ // Send enough data to overflow the connection level flow control window.
+ std::string body(kConnectionWindow + 1, 'a');
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ EXPECT_LT(data.size(), kStreamWindow);
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece(data));
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _));
+ stream_->OnStreamFrame(frame);
+}
+
+TEST_P(QuicSpdyStreamTest, StreamFlowControlFinNotBlocked) {
+ // An attempt to write a FIN with no data should not be flow control blocked,
+ // even if the send window is 0.
+
+ Initialize(kShouldProcessData);
+
+ // Set a flow control limit of zero.
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), 0);
+ EXPECT_EQ(0u, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Send a frame with a FIN but no data. This should not be blocked.
+ std::string body = "";
+ bool fin = true;
+
+ EXPECT_CALL(*connection_,
+ SendBlocked(GetNthClientInitiatedBidirectionalId(0)))
+ .Times(0);
+ EXPECT_CALL(*session_, WritevData(_, _, 0, _, FIN));
+
+ stream_->WriteOrBufferBody(body, fin);
+}
+
+TEST_P(QuicSpdyStreamTest, ReceivingTrailersViaHeaderList) {
+ // Test that receiving trailing headers from the peer via
+ // OnStreamHeaderList() works, and can be read from the stream and consumed.
+ Initialize(kShouldProcessData);
+
+ // Receive initial headers.
+ size_t total_bytes = 0;
+ QuicHeaderList headers;
+ for (const auto& p : headers_) {
+ headers.OnHeader(p.first, p.second);
+ total_bytes += p.first.size() + p.second.size();
+ }
+
+ stream_->OnStreamHeadersPriority(kV3HighestPriority);
+ stream_->OnStreamHeaderList(/*fin=*/false, total_bytes, headers);
+ stream_->ConsumeHeaderList();
+
+ // Receive trailing headers.
+ SpdyHeaderBlock trailers_block;
+ trailers_block["key1"] = "value1";
+ trailers_block["key2"] = "value2";
+ trailers_block["key3"] = "value3";
+ SpdyHeaderBlock trailers_block_with_final_offset = trailers_block.Clone();
+ trailers_block_with_final_offset[kFinalOffsetHeaderKey] = "0";
+ total_bytes = 0;
+ QuicHeaderList trailers;
+ for (const auto& p : trailers_block_with_final_offset) {
+ trailers.OnHeader(p.first, p.second);
+ total_bytes += p.first.size() + p.second.size();
+ }
+ stream_->OnStreamHeaderList(/*fin=*/true, total_bytes, trailers);
+
+ // The trailers should be decompressed, and readable from the stream.
+ EXPECT_TRUE(stream_->trailers_decompressed());
+ EXPECT_EQ(trailers_block, stream_->received_trailers());
+
+ // IsDoneReading() returns false until trailers marked consumed.
+ EXPECT_FALSE(stream_->IsDoneReading());
+ stream_->MarkTrailersConsumed();
+ EXPECT_TRUE(stream_->IsDoneReading());
+}
+
+TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithOffset) {
+ // Test that when receiving trailing headers with an offset before response
+ // body, stream is closed at the right offset.
+ Initialize(kShouldProcessData);
+
+ // Receive initial headers.
+ QuicHeaderList headers = ProcessHeaders(false, headers_);
+ stream_->ConsumeHeaderList();
+
+ const std::string body = "this is the body";
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ // Receive trailing headers.
+ SpdyHeaderBlock trailers_block;
+ trailers_block["key1"] = "value1";
+ trailers_block["key2"] = "value2";
+ trailers_block["key3"] = "value3";
+ trailers_block[kFinalOffsetHeaderKey] =
+ QuicTextUtils::Uint64ToString(data.size());
+
+ QuicHeaderList trailers = ProcessHeaders(true, trailers_block);
+
+ // The trailers should be decompressed, and readable from the stream.
+ EXPECT_TRUE(stream_->trailers_decompressed());
+
+ // The final offset trailer will be consumed by QUIC.
+ trailers_block.erase(kFinalOffsetHeaderKey);
+ EXPECT_EQ(trailers_block, stream_->received_trailers());
+
+ // Consuming the trailers erases them from the stream.
+ stream_->MarkTrailersConsumed();
+ EXPECT_TRUE(stream_->FinishedReadingTrailers());
+
+ EXPECT_FALSE(stream_->IsDoneReading());
+ // Receive and consume body.
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), /*fin=*/false,
+ 0, data);
+ stream_->OnStreamFrame(frame);
+ EXPECT_EQ(body, stream_->data());
+ EXPECT_TRUE(stream_->IsDoneReading());
+}
+
+TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithoutOffset) {
+ // Test that receiving trailers without a final offset field is an error.
+ Initialize(kShouldProcessData);
+
+ // Receive initial headers.
+ ProcessHeaders(false, headers_);
+ stream_->ConsumeHeaderList();
+
+ // Receive trailing headers, without kFinalOffsetHeaderKey.
+ SpdyHeaderBlock trailers_block;
+ trailers_block["key1"] = "value1";
+ trailers_block["key2"] = "value2";
+ trailers_block["key3"] = "value3";
+ auto trailers = AsHeaderList(trailers_block);
+
+ // Verify that the trailers block didn't contain a final offset.
+ EXPECT_EQ("", trailers_block[kFinalOffsetHeaderKey].as_string());
+
+ // Receipt of the malformed trailers will close the connection.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _))
+ .Times(1);
+ stream_->OnStreamHeaderList(/*fin=*/true,
+ trailers.uncompressed_header_bytes(), trailers);
+}
+
+TEST_P(QuicSpdyStreamTest, ReceivingTrailersWithoutFin) {
+ // Test that received Trailers must always have the FIN set.
+ Initialize(kShouldProcessData);
+
+ // Receive initial headers.
+ auto headers = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(/*fin=*/false,
+ headers.uncompressed_header_bytes(), headers);
+ stream_->ConsumeHeaderList();
+
+ // Receive trailing headers with FIN deliberately set to false.
+ SpdyHeaderBlock trailers_block;
+ trailers_block["foo"] = "bar";
+ auto trailers = AsHeaderList(trailers_block);
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _))
+ .Times(1);
+ stream_->OnStreamHeaderList(/*fin=*/false,
+ trailers.uncompressed_header_bytes(), trailers);
+}
+
+TEST_P(QuicSpdyStreamTest, ReceivingTrailersAfterHeadersWithFin) {
+ // If headers are received with a FIN, no trailers should then arrive.
+ Initialize(kShouldProcessData);
+
+ // Receive initial headers with FIN set.
+ ProcessHeaders(true, headers_);
+ stream_->ConsumeHeaderList();
+
+ // Receive trailing headers after FIN already received.
+ SpdyHeaderBlock trailers_block;
+ trailers_block["foo"] = "bar";
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _))
+ .Times(1);
+ ProcessHeaders(true, trailers_block);
+}
+
+TEST_P(QuicSpdyStreamTest, ReceivingTrailersAfterBodyWithFin) {
+ // If body data are received with a FIN, no trailers should then arrive.
+ Initialize(kShouldProcessData);
+
+ // Receive initial headers without FIN set.
+ ProcessHeaders(false, headers_);
+ stream_->ConsumeHeaderList();
+
+ // Receive body data, with FIN.
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), /*fin=*/true,
+ 0, "body");
+ stream_->OnStreamFrame(frame);
+
+ // Receive trailing headers after FIN already received.
+ SpdyHeaderBlock trailers_block;
+ trailers_block["foo"] = "bar";
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA, _, _))
+ .Times(1);
+ ProcessHeaders(true, trailers_block);
+}
+
+TEST_P(QuicSpdyStreamTest, ClosingStreamWithNoTrailers) {
+ // Verify that a stream receiving headers, body, and no trailers is correctly
+ // marked as done reading on consumption of headers and body.
+ Initialize(kShouldProcessData);
+
+ // Receive and consume initial headers with FIN not set.
+ auto h = AsHeaderList(headers_);
+ stream_->OnStreamHeaderList(/*fin=*/false, h.uncompressed_header_bytes(), h);
+ stream_->ConsumeHeaderList();
+
+ // Receive and consume body with FIN set, and no trailers.
+ std::string body(1024, 'x');
+ std::unique_ptr<char[]> buf;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ std::string header = std::string(buf.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body : body;
+
+ QuicStreamFrame frame(GetNthClientInitiatedBidirectionalId(0), /*fin=*/true,
+ 0, data);
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_TRUE(stream_->IsDoneReading());
+}
+
+TEST_P(QuicSpdyStreamTest, WritingTrailersSendsAFin) {
+ // Test that writing trailers will send a FIN, as Trailers are the last thing
+ // to be sent on a stream.
+ Initialize(kShouldProcessData);
+
+ // Write the initial headers, without a FIN.
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
+
+ // Writing trailers implicitly sends a FIN.
+ SpdyHeaderBlock trailers;
+ trailers["trailer key"] = "trailer value";
+ EXPECT_CALL(*stream_, WriteHeadersMock(true));
+ stream_->WriteTrailers(std::move(trailers), nullptr);
+ EXPECT_TRUE(stream_->fin_sent());
+}
+
+TEST_P(QuicSpdyStreamTest, WritingTrailersFinalOffset) {
+ // Test that when writing trailers, the trailers that are actually sent to the
+ // peer contain the final offset field indicating last byte of data.
+ Initialize(kShouldProcessData);
+
+ // Write the initial headers.
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
+
+ // Write non-zero body data to force a non-zero final offset.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ std::string body(1024, 'x'); // 1 kB
+ QuicByteCount header_length = 0;
+ if (HasFrameHeader()) {
+ std::unique_ptr<char[]> buf;
+ header_length = encoder_.SerializeDataFrameHeader(body.length(), &buf);
+ }
+
+ stream_->WriteOrBufferBody(body, false);
+
+ // The final offset field in the trailing headers is populated with the
+ // number of body bytes written (including queued bytes).
+ SpdyHeaderBlock trailers;
+ trailers["trailer key"] = "trailer value";
+ SpdyHeaderBlock trailers_with_offset(trailers.Clone());
+ trailers_with_offset[kFinalOffsetHeaderKey] =
+ QuicTextUtils::Uint64ToString(body.length() + header_length);
+ EXPECT_CALL(*stream_, WriteHeadersMock(true));
+ stream_->WriteTrailers(std::move(trailers), nullptr);
+ EXPECT_EQ(trailers_with_offset, stream_->saved_headers());
+}
+
+TEST_P(QuicSpdyStreamTest, WritingTrailersClosesWriteSide) {
+ // Test that if trailers are written after all other data has been written
+ // (headers and body), that this closes the stream for writing.
+ Initialize(kShouldProcessData);
+
+ // Write the initial headers.
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
+
+ // Write non-zero body data.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ const int kBodySize = 1 * 1024; // 1 kB
+ stream_->WriteOrBufferBody(std::string(kBodySize, 'x'), false);
+ EXPECT_EQ(0u, stream_->BufferedDataBytes());
+
+ // Headers and body have been fully written, there is no queued data. Writing
+ // trailers marks the end of this stream, and thus the write side is closed.
+ EXPECT_CALL(*stream_, WriteHeadersMock(true));
+ stream_->WriteTrailers(SpdyHeaderBlock(), nullptr);
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSpdyStreamTest, WritingTrailersWithQueuedBytes) {
+ // Test that the stream is not closed for writing when trailers are sent
+ // while there are still body bytes queued.
+ testing::InSequence seq;
+ Initialize(kShouldProcessData);
+
+ // Write the initial headers.
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/false, nullptr);
+
+ // Write non-zero body data, but only consume partially, ensuring queueing.
+ const int kBodySize = 1 * 1024; // 1 kB
+ if (HasFrameHeader()) {
+ EXPECT_CALL(*session_, WritevData(_, _, 3, _, NO_FIN));
+ }
+ EXPECT_CALL(*session_, WritevData(_, _, kBodySize, _, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(kBodySize - 1, false)));
+ stream_->WriteOrBufferBody(std::string(kBodySize, 'x'), false);
+ EXPECT_EQ(1u, stream_->BufferedDataBytes());
+
+ // Writing trailers will send a FIN, but not close the write side of the
+ // stream as there are queued bytes.
+ EXPECT_CALL(*stream_, WriteHeadersMock(true));
+ stream_->WriteTrailers(SpdyHeaderBlock(), nullptr);
+ EXPECT_TRUE(stream_->fin_sent());
+ EXPECT_FALSE(stream_->write_side_closed());
+
+ // Writing the queued bytes will close the write side of the stream.
+ EXPECT_CALL(*session_, WritevData(_, _, 1, _, NO_FIN));
+ stream_->OnCanWrite();
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSpdyStreamTest, WritingTrailersAfterFIN) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Test that it is not possible to write Trailers after a FIN has been sent.
+ Initialize(kShouldProcessData);
+
+ // Write the initial headers, with a FIN.
+ EXPECT_CALL(*stream_, WriteHeadersMock(true));
+ stream_->WriteHeaders(SpdyHeaderBlock(), /*fin=*/true, nullptr);
+ EXPECT_TRUE(stream_->fin_sent());
+
+ // Writing Trailers should fail, as the FIN has already been sent.
+ // populated with the number of body bytes written.
+ EXPECT_QUIC_BUG(stream_->WriteTrailers(SpdyHeaderBlock(), nullptr),
+ "Trailers cannot be sent after a FIN");
+}
+
+TEST_P(QuicSpdyStreamTest, HeaderStreamNotiferCorrespondingSpdyStream) {
+ Initialize(kShouldProcessData);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ testing::InSequence s;
+ QuicReferenceCountedPointer<MockAckListener> ack_listener1(
+ new MockAckListener());
+ QuicReferenceCountedPointer<MockAckListener> ack_listener2(
+ new MockAckListener());
+ stream_->set_ack_listener(ack_listener1);
+ stream2_->set_ack_listener(ack_listener2);
+
+ session_->headers_stream()->WriteOrBufferData("Header1", false,
+ ack_listener1);
+ stream_->WriteOrBufferBody("Test1", true);
+
+ session_->headers_stream()->WriteOrBufferData("Header2", false,
+ ack_listener2);
+ stream2_->WriteOrBufferBody("Test2", false);
+
+ QuicStreamFrame frame1(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()), false, 0,
+ "Header1");
+ std::string header = "";
+ if (HasFrameHeader()) {
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length = encoder_.SerializeDataFrameHeader(5, &buffer);
+ header = std::string(buffer.get(), header_length);
+ }
+ QuicStreamFrame frame2(stream_->id(), true, 0, header + "Test1");
+ QuicStreamFrame frame3(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()), false, 7,
+ "Header2");
+ QuicStreamFrame frame4(stream2_->id(), false, 0, header + "Test2");
+
+ EXPECT_CALL(*ack_listener1, OnPacketRetransmitted(7));
+ session_->OnStreamFrameRetransmitted(frame1);
+
+ EXPECT_CALL(*ack_listener1, OnPacketAcked(7, _));
+ EXPECT_TRUE(
+ session_->OnFrameAcked(QuicFrame(frame1), QuicTime::Delta::Zero()));
+ EXPECT_CALL(*ack_listener1, OnPacketAcked(5, _));
+ EXPECT_TRUE(
+ session_->OnFrameAcked(QuicFrame(frame2), QuicTime::Delta::Zero()));
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(7, _));
+ EXPECT_TRUE(
+ session_->OnFrameAcked(QuicFrame(frame3), QuicTime::Delta::Zero()));
+ EXPECT_CALL(*ack_listener2, OnPacketAcked(5, _));
+ EXPECT_TRUE(
+ session_->OnFrameAcked(QuicFrame(frame4), QuicTime::Delta::Zero()));
+}
+
+TEST_P(QuicSpdyStreamTest, StreamBecomesZombieWithWriteThatCloses) {
+ Initialize(kShouldProcessData);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ QuicStreamPeer::CloseReadSide(stream_);
+ // This write causes stream to be closed.
+ stream_->WriteOrBufferBody("Test1", true);
+ // stream_ has unacked data and should become zombie.
+ EXPECT_TRUE(QuicContainsKey(QuicSessionPeer::zombie_streams(session_.get()),
+ stream_->id()));
+ EXPECT_TRUE(QuicSessionPeer::closed_streams(session_.get()).empty());
+}
+
+TEST_P(QuicSpdyStreamTest, OnPriorityFrame) {
+ Initialize(kShouldProcessData);
+ stream_->OnPriorityFrame(kV3HighestPriority);
+ EXPECT_EQ(kV3HighestPriority, stream_->priority());
+}
+
+TEST_P(QuicSpdyStreamTest, OnPriorityFrameAfterSendingData) {
+ testing::InSequence seq;
+ Initialize(kShouldProcessData);
+
+ if (HasFrameHeader()) {
+ EXPECT_CALL(*session_, WritevData(_, _, 2, _, NO_FIN));
+ }
+ EXPECT_CALL(*session_, WritevData(_, _, 4, _, FIN));
+ stream_->WriteOrBufferBody("data", true);
+ stream_->OnPriorityFrame(kV3HighestPriority);
+ EXPECT_EQ(kV3HighestPriority, stream_->priority());
+}
+
+TEST_P(QuicSpdyStreamTest, SetPriorityBeforeUpdateStreamPriority) {
+ MockQuicConnection* connection = new StrictMock<MockQuicConnection>(
+ &helper_, &alarm_factory_, Perspective::IS_SERVER,
+ SupportedVersions(GetParam()));
+ std::unique_ptr<TestMockUpdateStreamSession> session(
+ new StrictMock<TestMockUpdateStreamSession>(connection));
+ auto stream = new StrictMock<TestStream>(
+ GetNthClientInitiatedBidirectionalStreamId(
+ session->connection()->transport_version(), 0),
+ session.get(),
+ /*should_process_data=*/true);
+ session->ActivateStream(QuicWrapUnique(stream));
+
+ // QuicSpdyStream::SetPriority() should eventually call UpdateStreamPriority()
+ // on the session. Make sure stream->priority() returns the updated priority
+ // if called within UpdateStreamPriority(). This expectation is enforced in
+ // TestMockUpdateStreamSession::UpdateStreamPriority().
+ session->SetExpectedStream(stream);
+ session->SetExpectedPriority(kV3HighestPriority);
+ stream->SetPriority(kV3HighestPriority);
+
+ session->SetExpectedPriority(kV3LowestPriority);
+ stream->SetPriority(kV3LowestPriority);
+}
+
+TEST_P(QuicSpdyStreamTest, StreamWaitsForAcks) {
+ Initialize(kShouldProcessData);
+ QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+ new StrictMock<MockAckListener>);
+ stream_->set_ack_listener(mock_ack_listener);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ // Stream is not waiting for acks initially.
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // Send kData1.
+ stream_->WriteOrBufferData("FooAndBar", false, nullptr);
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ // Stream is not waiting for acks as all sent data is acked.
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // Send kData2.
+ stream_->WriteOrBufferData("FooAndBar", false, nullptr);
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Send FIN.
+ stream_->WriteOrBufferData("", true, nullptr);
+ // Fin only frame is not stored in send buffer.
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // kData2 is retransmitted.
+ EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(9));
+ stream_->OnStreamFrameRetransmitted(9, 9, false);
+
+ // kData2 is acked.
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ // Stream is waiting for acks as FIN is not acked.
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // FIN is acked.
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 0, true, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+}
+
+TEST_P(QuicSpdyStreamTest, StreamDataGetAckedMultipleTimes) {
+ Initialize(kShouldProcessData);
+ QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+ new StrictMock<MockAckListener>);
+ stream_->set_ack_listener(mock_ack_listener);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ // Send [0, 27) and fin.
+ stream_->WriteOrBufferData("FooAndBar", false, nullptr);
+ stream_->WriteOrBufferData("FooAndBar", false, nullptr);
+ stream_->WriteOrBufferData("FooAndBar", true, nullptr);
+
+ // Ack [0, 9), [5, 22) and [18, 26)
+ // Verify [0, 9) 9 bytes are acked.
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(9, _));
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(2u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Verify [9, 22) 13 bytes are acked.
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(13, _));
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(5, 17, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Verify [22, 26) 4 bytes are acked.
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(4, _));
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 8, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+ // Ack [0, 27).
+ // Verify [26, 27) 1 byte is acked.
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(1, _));
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(26, 1, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+ // Ack Fin. Verify OnPacketAcked is called.
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+ // Ack [10, 27) and fin.
+ // No new data is acked, verify OnPacketAcked is not called.
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(_, _)).Times(0);
+ EXPECT_FALSE(stream_->OnStreamFrameAcked(
+ 10, 17, true, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+// HTTP/3 only.
+TEST_P(QuicSpdyStreamTest, HeadersAckNotReportedWriteOrBufferBody) {
+ Initialize(kShouldProcessData);
+ if (!HasFrameHeader()) {
+ return;
+ }
+ QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+ new StrictMock<MockAckListener>);
+ stream_->set_ack_listener(mock_ack_listener);
+ std::string body = "Test1";
+ std::string body2(100, 'x');
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ stream_->WriteOrBufferBody(body, false);
+ stream_->WriteOrBufferBody(body2, true);
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+
+ header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
+ std::string header2 = std::string(buffer.get(), header_length);
+
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(body.length(), _));
+ QuicStreamFrame frame(stream_->id(), false, 0, header + body);
+ EXPECT_TRUE(
+ session_->OnFrameAcked(QuicFrame(frame), QuicTime::Delta::Zero()));
+
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(0, _));
+ QuicStreamFrame frame2(stream_->id(), false, (header + body).length(),
+ header2);
+ EXPECT_TRUE(
+ session_->OnFrameAcked(QuicFrame(frame2), QuicTime::Delta::Zero()));
+
+ EXPECT_CALL(*mock_ack_listener, OnPacketAcked(body2.length(), _));
+ QuicStreamFrame frame3(stream_->id(), true,
+ (header + body).length() + header2.length(), body2);
+ EXPECT_TRUE(
+ session_->OnFrameAcked(QuicFrame(frame3), QuicTime::Delta::Zero()));
+
+ EXPECT_TRUE(
+ QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty());
+}
+
+// HTTP/3 only.
+TEST_P(QuicSpdyStreamTest, HeadersAckNotReportedWriteBodySlices) {
+ Initialize(kShouldProcessData);
+ if (!HasFrameHeader()) {
+ return;
+ }
+ QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+ new StrictMock<MockAckListener>);
+ stream_->set_ack_listener(mock_ack_listener);
+ std::string body = "Test1";
+ std::string body2(100, 'x');
+ struct iovec body1_iov = {const_cast<char*>(body.data()), body.length()};
+ struct iovec body2_iov = {const_cast<char*>(body2.data()), body2.length()};
+ QuicMemSliceStorage storage(&body1_iov, 1,
+ helper_.GetStreamSendBufferAllocator(), 1024);
+ QuicMemSliceStorage storage2(&body2_iov, 1,
+ helper_.GetStreamSendBufferAllocator(), 1024);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ stream_->WriteBodySlices(storage.ToSpan(), false);
+ stream_->WriteBodySlices(storage2.ToSpan(), true);
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+
+ header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
+ std::string header2 = std::string(buffer.get(), header_length);
+
+ EXPECT_CALL(*mock_ack_listener,
+ OnPacketAcked(body.length() + body2.length(), _));
+ QuicStreamFrame frame(stream_->id(), true, 0,
+ header + body + header2 + body2);
+ EXPECT_TRUE(
+ session_->OnFrameAcked(QuicFrame(frame), QuicTime::Delta::Zero()));
+
+ EXPECT_TRUE(
+ QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty());
+}
+
+// HTTP/3 only.
+TEST_P(QuicSpdyStreamTest, HeaderBytesNotReportedOnRetransmission) {
+ Initialize(kShouldProcessData);
+ if (!HasFrameHeader()) {
+ return;
+ }
+ QuicReferenceCountedPointer<MockAckListener> mock_ack_listener(
+ new StrictMock<MockAckListener>);
+ stream_->set_ack_listener(mock_ack_listener);
+ std::string body = "Test1";
+ std::string body2(100, 'x');
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(AtLeast(1));
+ stream_->WriteOrBufferBody(body, false);
+ stream_->WriteOrBufferBody(body2, true);
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+
+ header_length = encoder_.SerializeDataFrameHeader(body2.length(), &buffer);
+ std::string header2 = std::string(buffer.get(), header_length);
+
+ EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(body.length()));
+ QuicStreamFrame frame(stream_->id(), false, 0, header + body);
+ session_->OnStreamFrameRetransmitted(frame);
+
+ EXPECT_CALL(*mock_ack_listener, OnPacketRetransmitted(body2.length()));
+ QuicStreamFrame frame2(stream_->id(), true, (header + body).length(),
+ header2 + body2);
+ session_->OnStreamFrameRetransmitted(frame2);
+
+ EXPECT_FALSE(
+ QuicSpdyStreamPeer::unacked_frame_headers_offsets(stream_).Empty());
+}
+
+} // 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
new file mode 100644
index 00000000000..721ae33face
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.cc
@@ -0,0 +1,355 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "url/gurl.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+// static
+bool SpdyUtils::ExtractContentLengthFromHeaders(int64_t* content_length,
+ SpdyHeaderBlock* headers) {
+ auto it = headers->find("content-length");
+ if (it == headers->end()) {
+ return false;
+ } else {
+ // Check whether multiple values are consistent.
+ QuicStringPiece content_length_header = it->second;
+ std::vector<QuicStringPiece> values =
+ QuicTextUtils::Split(content_length_header, '\0');
+ for (const QuicStringPiece& value : values) {
+ uint64_t new_value;
+ if (!QuicTextUtils::StringToUint64(value, &new_value)) {
+ QUIC_DLOG(ERROR)
+ << "Content length was either unparseable or negative.";
+ return false;
+ }
+ if (*content_length < 0) {
+ *content_length = new_value;
+ continue;
+ }
+ if (new_value != static_cast<uint64_t>(*content_length)) {
+ QUIC_DLOG(ERROR)
+ << "Parsed content length " << new_value << " is "
+ << "inconsistent with previously detected content length "
+ << *content_length;
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+bool SpdyUtils::CopyAndValidateHeaders(const QuicHeaderList& header_list,
+ int64_t* content_length,
+ SpdyHeaderBlock* headers) {
+ for (const auto& p : header_list) {
+ const std::string& name = p.first;
+ if (name.empty()) {
+ QUIC_DLOG(ERROR) << "Header name must not be empty.";
+ return false;
+ }
+
+ if (QuicTextUtils::ContainsUpperCase(name)) {
+ QUIC_DLOG(ERROR) << "Malformed header: Header name " << name
+ << " contains upper-case characters.";
+ return false;
+ }
+
+ headers->AppendValueOrAddHeader(name, p.second);
+ }
+
+ if (QuicContainsKey(*headers, "content-length") &&
+ !ExtractContentLengthFromHeaders(content_length, headers)) {
+ return false;
+ }
+
+ QUIC_DVLOG(1) << "Successfully parsed headers: " << headers->DebugString();
+ return true;
+}
+
+bool SpdyUtils::CopyAndValidateTrailers(const QuicHeaderList& header_list,
+ bool expect_final_byte_offset,
+ size_t* final_byte_offset,
+ SpdyHeaderBlock* trailers) {
+ bool found_final_byte_offset = false;
+ for (const auto& p : header_list) {
+ const std::string& name = p.first;
+
+ // Pull out the final offset pseudo header which indicates the number of
+ // response body bytes expected.
+ if (expect_final_byte_offset && !found_final_byte_offset &&
+ name == kFinalOffsetHeaderKey &&
+ QuicTextUtils::StringToSizeT(p.second, final_byte_offset)) {
+ found_final_byte_offset = true;
+ continue;
+ }
+
+ if (name.empty() || name[0] == ':') {
+ QUIC_DLOG(ERROR)
+ << "Trailers must not be empty, and must not contain pseudo-"
+ << "headers. Found: '" << name << "'";
+ return false;
+ }
+
+ if (QuicTextUtils::ContainsUpperCase(name)) {
+ QUIC_DLOG(ERROR) << "Malformed header: Header name " << name
+ << " contains upper-case characters.";
+ return false;
+ }
+
+ trailers->AppendValueOrAddHeader(name, p.second);
+ }
+
+ if (expect_final_byte_offset && !found_final_byte_offset) {
+ QUIC_DLOG(ERROR) << "Required key '" << kFinalOffsetHeaderKey
+ << "' not present";
+ return false;
+ }
+
+ // TODO(rjshade): Check for other forbidden keys, following the HTTP/2 spec.
+
+ QUIC_DVLOG(1) << "Successfully parsed Trailers: " << trailers->DebugString();
+ return true;
+}
+
+// static
+std::string SpdyUtils::GetPromisedUrlFromHeaders(
+ const SpdyHeaderBlock& headers) {
+ // RFC 7540, Section 8.1.2.3: All HTTP/2 requests MUST include exactly
+ // one valid value for the ":method", ":scheme", and ":path" pseudo-header
+ // fields, unless it is a CONNECT request.
+
+ // RFC 7540, Section 8.2.1: The header fields in PUSH_PROMISE and any
+ // subsequent CONTINUATION frames MUST be a valid and complete set of request
+ // header fields (Section 8.1.2.3). The server MUST include a method in the
+ // ":method" pseudo-header field that is safe and cacheable.
+ //
+ // RFC 7231, Section 4.2.1: Of the request methods defined by this
+ // specification, the GET, HEAD, OPTIONS, and TRACE methods are defined to be
+ // safe.
+ //
+ // RFC 7231, Section 4.2.1: ... this specification defines GET, HEAD, and
+ // POST as cacheable, ...
+ //
+ // So the only methods allowed in a PUSH_PROMISE are GET and HEAD.
+ SpdyHeaderBlock::const_iterator it = headers.find(":method");
+ if (it == headers.end() || (it->second != "GET" && it->second != "HEAD")) {
+ return std::string();
+ }
+
+ it = headers.find(":scheme");
+ if (it == headers.end() || it->second.empty()) {
+ return std::string();
+ }
+ QuicStringPiece scheme = it->second;
+
+ // RFC 7540, Section 8.2: The server MUST include a value in the
+ // ":authority" pseudo-header field for which the server is authoritative
+ // (see Section 10.1).
+ it = headers.find(":authority");
+ if (it == headers.end() || it->second.empty()) {
+ return std::string();
+ }
+ QuicStringPiece authority = it->second;
+
+ // RFC 7540, Section 8.1.2.3 requires that the ":path" pseudo-header MUST
+ // NOT be empty for "http" or "https" URIs;
+ //
+ // However, to ensure the scheme is consistently canonicalized, that check
+ // is deferred to implementations in QuicUrlUtils::GetPushPromiseUrl().
+ it = headers.find(":path");
+ if (it == headers.end()) {
+ return std::string();
+ }
+ QuicStringPiece path = it->second;
+
+ return GetPushPromiseUrl(scheme, authority, path);
+}
+
+// static
+std::string SpdyUtils::GetPromisedHostNameFromHeaders(
+ const SpdyHeaderBlock& headers) {
+ // TODO(fayang): Consider just checking out the value of the ":authority" key
+ // in headers.
+ return GURL(GetPromisedUrlFromHeaders(headers)).host();
+}
+
+// static
+bool SpdyUtils::PromisedUrlIsValid(const SpdyHeaderBlock& headers) {
+ std::string url(GetPromisedUrlFromHeaders(headers));
+ return !url.empty() && GURL(url).is_valid();
+}
+
+// static
+bool SpdyUtils::PopulateHeaderBlockFromUrl(const std::string url,
+ SpdyHeaderBlock* headers) {
+ (*headers)[":method"] = "GET";
+ size_t pos = url.find("://");
+ if (pos == std::string::npos) {
+ return false;
+ }
+ (*headers)[":scheme"] = url.substr(0, pos);
+ size_t start = pos + 3;
+ pos = url.find("/", start);
+ if (pos == std::string::npos) {
+ (*headers)[":authority"] = url.substr(start);
+ (*headers)[":path"] = "/";
+ return true;
+ }
+ (*headers)[":authority"] = url.substr(start, pos - start);
+ (*headers)[":path"] = url.substr(pos);
+ return true;
+}
+
+// static
+std::string SpdyUtils::GetPushPromiseUrl(QuicStringPiece scheme,
+ QuicStringPiece authority,
+ QuicStringPiece path) {
+ // RFC 7540, Section 8.1.2.3: The ":path" pseudo-header field includes the
+ // path and query parts of the target URI (the "path-absolute" production
+ // and optionally a '?' character followed by the "query" production (see
+ // Sections 3.3 and 3.4 of RFC3986). A request in asterisk form includes the
+ // value '*' for the ":path" pseudo-header field.
+ //
+ // This pseudo-header field MUST NOT be empty for "http" or "https" URIs;
+ // "http" or "https" URIs that do not contain a path MUST include a value of
+ // '/'. The exception to this rule is an OPTIONS request for an "http" or
+ // "https" URI that does not include a path component; these MUST include a
+ // ":path" pseudo-header with a value of '*' (see RFC7230, Section 5.3.4).
+ //
+ // In addition to the above restriction from RFC 7540, note that RFC3986
+ // defines the "path-absolute" construction as starting with "/" but not "//".
+ //
+ // RFC 7540, Section 8.2.1: The header fields in PUSH_PROMISE and any
+ // subsequent CONTINUATION frames MUST be a valid and complete set of request
+ // header fields (Section 8.1.2.3). The server MUST include a method in the
+ // ":method" pseudo-header field that is safe and cacheable.
+ //
+ // RFC 7231, Section 4.2.1:
+ // ... this specification defines GET, HEAD, and POST as cacheable, ...
+ //
+ // Since the OPTIONS method is not cacheable, it cannot be the method of a
+ // PUSH_PROMISE. Therefore, the exception mentioned in RFC 7540, Section
+ // 8.1.2.3 about OPTIONS requests does not apply here (i.e. ":path" cannot be
+ // "*").
+ if (path.empty() || path[0] != '/' || (path.size() >= 2 && path[1] == '/')) {
+ return std::string();
+ }
+
+ // Validate the scheme; this is to ensure a scheme of "foo://bar" is not
+ // parsed as a URL of "foo://bar://baz" when combined with a host of "baz".
+ std::string canonical_scheme;
+ url::StdStringCanonOutput canon_scheme_output(&canonical_scheme);
+ url::Component canon_component;
+ url::Component scheme_component(0, scheme.size());
+
+ if (!url::CanonicalizeScheme(scheme.data(), scheme_component,
+ &canon_scheme_output, &canon_component) ||
+ !canon_component.is_nonempty() || canon_component.begin != 0) {
+ return std::string();
+ }
+ canonical_scheme.resize(canon_component.len + 1);
+
+ // Validate the authority; this is to ensure an authority such as
+ // "host/path" is not accepted, as when combined with a scheme like
+ // "http://", could result in a URL of "http://host/path".
+ url::Component auth_component(0, authority.size());
+ url::Component username_component;
+ url::Component password_component;
+ url::Component host_component;
+ url::Component port_component;
+
+ url::ParseAuthority(authority.data(), auth_component, &username_component,
+ &password_component, &host_component, &port_component);
+
+ // RFC 7540, Section 8.1.2.3: The authority MUST NOT include the deprecated
+ // "userinfo" subcomponent for "http" or "https" schemed URIs.
+ //
+ // Note: Although |canonical_scheme| has not yet been checked for that, as
+ // it is performed later in processing, only "http" and "https" schemed
+ // URIs are supported for PUSH.
+ if (username_component.is_valid() || password_component.is_valid()) {
+ return std::string();
+ }
+
+ // Failed parsing or no host present. ParseAuthority() will ensure that
+ // host_component + port_component cover the entire string, if
+ // username_component and password_component are not present.
+ if (!host_component.is_nonempty()) {
+ return std::string();
+ }
+
+ // Validate the port (if present; it's optional).
+ int parsed_port_number = url::PORT_INVALID;
+ if (port_component.is_nonempty()) {
+ parsed_port_number = url::ParsePort(authority.data(), port_component);
+ if (parsed_port_number < 0 && parsed_port_number != url::PORT_UNSPECIFIED) {
+ return std::string();
+ }
+ }
+
+ // Validate the host by attempting to canonicalize it. Invalid characters
+ // will result in a canonicalization failure (e.g. '/')
+ std::string canon_host;
+ url::StdStringCanonOutput canon_host_output(&canon_host);
+ canon_component.reset();
+ if (!url::CanonicalizeHost(authority.data(), host_component,
+ &canon_host_output, &canon_component) ||
+ !canon_component.is_nonempty() || canon_component.begin != 0) {
+ return std::string();
+ }
+
+ // At this point, "authority" has been validated to either be of the form
+ // 'host:port' or 'host', with 'host' being a valid domain or IP address,
+ // and 'port' (if present), being a valid port. Attempt to construct a
+ // URL of just the (scheme, host, port), which should be safe and will not
+ // result in ambiguous parsing.
+ //
+ // This also enforces that all PUSHed URLs are either HTTP or HTTPS-schemed
+ // URIs, consistent with the other restrictions enforced above.
+ //
+ // Note: url::CanonicalizeScheme() will have added the ':' to
+ // |canonical_scheme|.
+ GURL origin_url(canonical_scheme + "//" + std::string(authority));
+ if (!origin_url.is_valid() || !origin_url.SchemeIsHTTPOrHTTPS() ||
+ // The following checks are merely defense in depth.
+ origin_url.has_username() || origin_url.has_password() ||
+ (origin_url.has_path() && origin_url.path_piece() != "/") ||
+ origin_url.has_query() || origin_url.has_ref()) {
+ return std::string();
+ }
+
+ // Attempt to parse the path.
+ std::string spec = origin_url.GetWithEmptyPath().spec();
+ spec.pop_back(); // Remove the '/', as ":path" must contain it.
+ spec.append(std::string(path));
+
+ // Attempt to parse the full URL, with the path as well. Ensure there is no
+ // fragment to the query.
+ GURL full_url(spec);
+ if (!full_url.is_valid() || full_url.has_ref()) {
+ return std::string();
+ }
+
+ return full_url.spec();
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..dc3fabca33b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_HTTP_SPDY_UTILS_H_
+#define QUICHE_QUIC_CORE_HTTP_SPDY_UTILS_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE SpdyUtils {
+ public:
+ SpdyUtils() = delete;
+
+ // Populate |content length| with the value of the content-length header.
+ // Returns true on success, false if parsing fails or content-length header is
+ // missing.
+ static bool ExtractContentLengthFromHeaders(int64_t* content_length,
+ spdy::SpdyHeaderBlock* headers);
+
+ // Copies a list of headers to a SpdyHeaderBlock.
+ static bool CopyAndValidateHeaders(const QuicHeaderList& header_list,
+ int64_t* content_length,
+ spdy::SpdyHeaderBlock* headers);
+
+ // Copies a list of headers to a SpdyHeaderBlock.
+ // If |expect_final_byte_offset| is true, requires exactly one header field
+ // with key kFinalOffsetHeaderKey and an integer value.
+ // If |expect_final_byte_offset| is false, no kFinalOffsetHeaderKey may be
+ // present.
+ // Returns true if parsing is successful. Returns false if the presence of
+ // kFinalOffsetHeaderKey does not match the value of
+ // |expect_final_byte_offset|, the kFinalOffsetHeaderKey value cannot be
+ // parsed, any other pseudo-header is present, an empty header key is present,
+ // or a header key contains an uppercase character.
+ static bool CopyAndValidateTrailers(const QuicHeaderList& header_list,
+ bool expect_final_byte_offset,
+ size_t* final_byte_offset,
+ spdy::SpdyHeaderBlock* trailers);
+
+ // Returns a canonicalized URL composed from the :scheme, :authority, and
+ // :path headers of a PUSH_PROMISE. Returns empty string if the headers do not
+ // conform to HTTP/2 spec or if the ":method" header contains a forbidden
+ // method for PUSH_PROMISE.
+ static std::string GetPromisedUrlFromHeaders(
+ const spdy::SpdyHeaderBlock& headers);
+
+ // Returns hostname, or empty string if missing.
+ static std::string GetPromisedHostNameFromHeaders(
+ const spdy::SpdyHeaderBlock& headers);
+
+ // Returns true if result of |GetPromisedUrlFromHeaders()| is non-empty
+ // and is a well-formed URL.
+ static bool PromisedUrlIsValid(const spdy::SpdyHeaderBlock& headers);
+
+ // Populates the fields of |headers| to make a GET request of |url|,
+ // which must be fully-qualified.
+ static bool PopulateHeaderBlockFromUrl(const std::string url,
+ spdy::SpdyHeaderBlock* headers);
+
+ // Returns a canonical, valid URL for a PUSH_PROMISE with the specified
+ // ":scheme", ":authority", and ":path" header fields, or an empty
+ // string if the resulting URL is not valid or supported.
+ static std::string GetPushPromiseUrl(QuicStringPiece scheme,
+ QuicStringPiece authority,
+ QuicStringPiece path);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_HTTP_SPDY_UTILS_H_
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
new file mode 100644
index 00000000000..d5c96fb9375
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/http/spdy_utils_test.cc
@@ -0,0 +1,557 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using spdy::SpdyHeaderBlock;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
+namespace quic {
+namespace test {
+namespace {
+
+const bool kExpectFinalByteOffset = true;
+const bool kDoNotExpectFinalByteOffset = false;
+
+static std::unique_ptr<QuicHeaderList> FromList(
+ const QuicHeaderList::ListType& src) {
+ std::unique_ptr<QuicHeaderList> headers(new QuicHeaderList);
+ headers->OnHeaderBlockStart();
+ for (const auto& p : src) {
+ headers->OnHeader(p.first, p.second);
+ }
+ headers->OnHeaderBlockEnd(0, 0);
+ return headers;
+}
+
+} // anonymous namespace
+
+using CopyAndValidateHeaders = QuicTest;
+
+TEST_F(CopyAndValidateHeaders, NormalUsage) {
+ auto headers = FromList({// All cookie crumbs are joined.
+ {"cookie", " part 1"},
+ {"cookie", "part 2 "},
+ {"cookie", "part3"},
+
+ // Already-delimited headers are passed through.
+ {"passed-through", std::string("foo\0baz", 7)},
+
+ // Other headers are joined on \0.
+ {"joined", "value 1"},
+ {"joined", "value 2"},
+
+ // Empty headers remain empty.
+ {"empty", ""},
+
+ // Joined empty headers work as expected.
+ {"empty-joined", ""},
+ {"empty-joined", "foo"},
+ {"empty-joined", ""},
+ {"empty-joined", ""},
+
+ // Non-continguous cookie crumb.
+ {"cookie", " fin!"}});
+
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_TRUE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+ EXPECT_THAT(block,
+ UnorderedElementsAre(
+ Pair("cookie", " part 1; part 2 ; part3; fin!"),
+ Pair("passed-through", QuicStringPiece("foo\0baz", 7)),
+ Pair("joined", QuicStringPiece("value 1\0value 2", 15)),
+ Pair("empty", ""),
+ Pair("empty-joined", QuicStringPiece("\0foo\0\0", 6))));
+ EXPECT_EQ(-1, content_length);
+}
+
+TEST_F(CopyAndValidateHeaders, EmptyName) {
+ auto headers = FromList({{"foo", "foovalue"}, {"", "barvalue"}, {"baz", ""}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_FALSE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+}
+
+TEST_F(CopyAndValidateHeaders, UpperCaseName) {
+ auto headers =
+ FromList({{"foo", "foovalue"}, {"bar", "barvalue"}, {"bAz", ""}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_FALSE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+}
+
+TEST_F(CopyAndValidateHeaders, MultipleContentLengths) {
+ auto headers = FromList({{"content-length", "9"},
+ {"foo", "foovalue"},
+ {"content-length", "9"},
+ {"bar", "barvalue"},
+ {"baz", ""}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_TRUE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+ EXPECT_THAT(block, UnorderedElementsAre(
+ Pair("foo", "foovalue"), Pair("bar", "barvalue"),
+ Pair("content-length", QuicStringPiece("9\09", 3)),
+ Pair("baz", "")));
+ EXPECT_EQ(9, content_length);
+}
+
+TEST_F(CopyAndValidateHeaders, InconsistentContentLengths) {
+ auto headers = FromList({{"content-length", "9"},
+ {"foo", "foovalue"},
+ {"content-length", "8"},
+ {"bar", "barvalue"},
+ {"baz", ""}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_FALSE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+}
+
+TEST_F(CopyAndValidateHeaders, LargeContentLength) {
+ auto headers = FromList({{"content-length", "9000000000"},
+ {"foo", "foovalue"},
+ {"bar", "barvalue"},
+ {"baz", ""}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_TRUE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+ EXPECT_THAT(block, UnorderedElementsAre(
+ Pair("foo", "foovalue"), Pair("bar", "barvalue"),
+ Pair("content-length", QuicStringPiece("9000000000")),
+ Pair("baz", "")));
+ EXPECT_EQ(9000000000, content_length);
+}
+
+TEST_F(CopyAndValidateHeaders, MultipleValues) {
+ auto headers = FromList({{"foo", "foovalue"},
+ {"bar", "barvalue"},
+ {"baz", ""},
+ {"foo", "boo"},
+ {"baz", "buzz"}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_TRUE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+ EXPECT_THAT(block, UnorderedElementsAre(
+ Pair("foo", QuicStringPiece("foovalue\0boo", 12)),
+ Pair("bar", "barvalue"),
+ Pair("baz", QuicStringPiece("\0buzz", 5))));
+ EXPECT_EQ(-1, content_length);
+}
+
+TEST_F(CopyAndValidateHeaders, MoreThanTwoValues) {
+ auto headers = FromList({{"set-cookie", "value1"},
+ {"set-cookie", "value2"},
+ {"set-cookie", "value3"}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_TRUE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+ EXPECT_THAT(
+ block, UnorderedElementsAre(Pair(
+ "set-cookie", QuicStringPiece("value1\0value2\0value3", 20))));
+ EXPECT_EQ(-1, content_length);
+}
+
+TEST_F(CopyAndValidateHeaders, Cookie) {
+ auto headers = FromList({{"foo", "foovalue"},
+ {"bar", "barvalue"},
+ {"cookie", "value1"},
+ {"baz", ""}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_TRUE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+ EXPECT_THAT(block, UnorderedElementsAre(
+ Pair("foo", "foovalue"), Pair("bar", "barvalue"),
+ Pair("cookie", "value1"), Pair("baz", "")));
+ EXPECT_EQ(-1, content_length);
+}
+
+TEST_F(CopyAndValidateHeaders, MultipleCookies) {
+ auto headers = FromList({{"foo", "foovalue"},
+ {"bar", "barvalue"},
+ {"cookie", "value1"},
+ {"baz", ""},
+ {"cookie", "value2"}});
+ int64_t content_length = -1;
+ SpdyHeaderBlock block;
+ ASSERT_TRUE(
+ SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
+ EXPECT_THAT(block, UnorderedElementsAre(
+ Pair("foo", "foovalue"), Pair("bar", "barvalue"),
+ Pair("cookie", "value1; value2"), Pair("baz", "")));
+ EXPECT_EQ(-1, content_length);
+}
+
+using CopyAndValidateTrailers = QuicTest;
+
+TEST_F(CopyAndValidateTrailers, SimplestValidList) {
+ // Verify that the simplest trailers are valid: just a final byte offset that
+ // gets parsed successfully.
+ auto trailers = FromList({{kFinalOffsetHeaderKey, "1234"}});
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+ *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
+ EXPECT_EQ(1234u, final_byte_offset);
+}
+
+TEST_F(CopyAndValidateTrailers, EmptyTrailerListWithFinalByteOffsetExpected) {
+ // An empty trailer list will fail as expected key kFinalOffsetHeaderKey is
+ // not present.
+ QuicHeaderList trailers;
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+ trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
+}
+
+TEST_F(CopyAndValidateTrailers,
+ EmptyTrailerListWithFinalByteOffsetNotExpected) {
+ // An empty trailer list will pass successfully if kFinalOffsetHeaderKey is
+ // not expected.
+ QuicHeaderList trailers;
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+ trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
+ EXPECT_TRUE(block.empty());
+}
+
+TEST_F(CopyAndValidateTrailers, FinalByteOffsetExpectedButNotPresent) {
+ // Validation fails if expected kFinalOffsetHeaderKey is not present, even if
+ // the rest of the header block is valid.
+ auto trailers = FromList({{"key", "value"}});
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+ *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
+}
+
+TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotExpectedButPresent) {
+ // Validation fails if kFinalOffsetHeaderKey is present but should not be,
+ // even if the rest of the header block is valid.
+ auto trailers = FromList({{"key", "value"}, {kFinalOffsetHeaderKey, "1234"}});
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+ *trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
+}
+
+TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotExpectedAndNotPresent) {
+ // Validation succeeds if kFinalOffsetHeaderKey is not expected and not
+ // present.
+ auto trailers = FromList({{"key", "value"}});
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+ *trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
+ EXPECT_THAT(block, UnorderedElementsAre(Pair("key", "value")));
+}
+
+TEST_F(CopyAndValidateTrailers, EmptyName) {
+ // Trailer validation will fail with an empty header key, in an otherwise
+ // valid block of trailers.
+ auto trailers = FromList({{"", "value"}, {kFinalOffsetHeaderKey, "1234"}});
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+ *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
+}
+
+TEST_F(CopyAndValidateTrailers, PseudoHeaderInTrailers) {
+ // Pseudo headers are illegal in trailers.
+ auto trailers =
+ FromList({{":pseudo_key", "value"}, {kFinalOffsetHeaderKey, "1234"}});
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
+ *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
+}
+
+TEST_F(CopyAndValidateTrailers, DuplicateTrailers) {
+ // Duplicate trailers are allowed, and their values are concatenated into a
+ // single string delimted with '\0'. Some of the duplicate headers
+ // deliberately have an empty value.
+ auto trailers = FromList({{"key", "value0"},
+ {"key", "value1"},
+ {"key", ""},
+ {"key", ""},
+ {"key", "value2"},
+ {"key", ""},
+ {kFinalOffsetHeaderKey, "1234"},
+ {"other_key", "value"},
+ {"key", "non_contiguous_duplicate"}});
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+ *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
+ EXPECT_THAT(
+ block,
+ UnorderedElementsAre(
+ Pair("key",
+ QuicStringPiece(
+ "value0\0value1\0\0\0value2\0\0non_contiguous_duplicate",
+ 48)),
+ Pair("other_key", "value")));
+}
+
+TEST_F(CopyAndValidateTrailers, DuplicateCookies) {
+ // Duplicate cookie headers in trailers should be concatenated into a single
+ // "; " delimted string.
+ auto headers = FromList({{"cookie", " part 1"},
+ {"cookie", "part 2 "},
+ {"cookie", "part3"},
+ {"key", "value"},
+ {kFinalOffsetHeaderKey, "1234"},
+ {"cookie", " non_contiguous_cookie!"}});
+
+ size_t final_byte_offset = 0;
+ SpdyHeaderBlock block;
+ EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
+ *headers, kExpectFinalByteOffset, &final_byte_offset, &block));
+ EXPECT_THAT(
+ block,
+ UnorderedElementsAre(
+ Pair("cookie", " part 1; part 2 ; part3; non_contiguous_cookie!"),
+ Pair("key", "value")));
+}
+
+using GetPromisedUrlFromHeaders = QuicTest;
+
+TEST_F(GetPromisedUrlFromHeaders, Basic) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = "GET";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+ headers[":scheme"] = "https";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+ headers[":authority"] = "www.google.com";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+ headers[":path"] = "/index.html";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers),
+ "https://www.google.com/index.html");
+ headers["key1"] = "value1";
+ headers["key2"] = "value2";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers),
+ "https://www.google.com/index.html");
+}
+
+TEST_F(GetPromisedUrlFromHeaders, Connect) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = "CONNECT";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+ headers[":authority"] = "www.google.com";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+ headers[":scheme"] = "https";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+ headers[":path"] = "https";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+}
+
+TEST_F(GetPromisedUrlFromHeaders, InvalidUserinfo) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = "GET";
+ headers[":authority"] = "user@www.google.com";
+ headers[":scheme"] = "https";
+ headers[":path"] = "/";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+}
+
+TEST_F(GetPromisedUrlFromHeaders, InvalidPath) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = "GET";
+ headers[":authority"] = "www.google.com";
+ headers[":scheme"] = "https";
+ headers[":path"] = "";
+ EXPECT_EQ(SpdyUtils::GetPromisedUrlFromHeaders(headers), "");
+}
+
+using GetPromisedHostNameFromHeaders = QuicTest;
+
+TEST_F(GetPromisedHostNameFromHeaders, NormalUsage) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = "GET";
+ EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "");
+ headers[":scheme"] = "https";
+ EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "");
+ headers[":authority"] = "www.google.com";
+ EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "");
+ headers[":path"] = "/index.html";
+ EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers),
+ "www.google.com");
+ headers["key1"] = "value1";
+ headers["key2"] = "value2";
+ EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers),
+ "www.google.com");
+ headers[":authority"] = "www.google.com:6666";
+ EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers),
+ "www.google.com");
+ headers[":authority"] = "192.168.1.1";
+ EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "192.168.1.1");
+ headers[":authority"] = "192.168.1.1:6666";
+ EXPECT_EQ(SpdyUtils::GetPromisedHostNameFromHeaders(headers), "192.168.1.1");
+}
+
+using PopulateHeaderBlockFromUrl = QuicTest;
+
+TEST_F(PopulateHeaderBlockFromUrl, NormalUsage) {
+ std::string url = "https://www.google.com/index.html";
+ SpdyHeaderBlock headers;
+ EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers));
+ EXPECT_EQ("https", headers[":scheme"].as_string());
+ EXPECT_EQ("www.google.com", headers[":authority"].as_string());
+ EXPECT_EQ("/index.html", headers[":path"].as_string());
+}
+
+TEST_F(PopulateHeaderBlockFromUrl, UrlWithNoPath) {
+ std::string url = "https://www.google.com";
+ SpdyHeaderBlock headers;
+ EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers));
+ EXPECT_EQ("https", headers[":scheme"].as_string());
+ EXPECT_EQ("www.google.com", headers[":authority"].as_string());
+ EXPECT_EQ("/", headers[":path"].as_string());
+}
+
+TEST_F(PopulateHeaderBlockFromUrl, Failure) {
+ SpdyHeaderBlock headers;
+ EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/", &headers));
+ EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/index.html", &headers));
+ EXPECT_FALSE(
+ SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers));
+}
+
+using PushPromiseUrlTest = QuicTest;
+
+TEST_F(PushPromiseUrlTest, GetPushPromiseUrl) {
+ // Test rejection of various inputs.
+ EXPECT_EQ("",
+ SpdyUtils::GetPushPromiseUrl("file", "localhost", "/etc/password"));
+ EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("file", "",
+ "/C:/Windows/System32/Config/"));
+ EXPECT_EQ("",
+ SpdyUtils::GetPushPromiseUrl("", "https://www.google.com", "/"));
+
+ EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https://www.google.com",
+ "www.google.com", "/"));
+ EXPECT_EQ("",
+ SpdyUtils::GetPushPromiseUrl("https://", "www.google.com", "/"));
+ EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "", "/"));
+ EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "", "www.google.com/"));
+ EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google.com/", "/"));
+ EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google.com", ""));
+ EXPECT_EQ("", SpdyUtils::GetPushPromiseUrl("https", "www.google", ".com/"));
+
+ // Test acception/rejection of various input combinations.
+ // |input_headers| is an array of pairs. The first value of each pair is a
+ // string that will be used as one of the inputs of GetPushPromiseUrl(). The
+ // second value of each pair is a bitfield where the lowest 3 bits indicate
+ // for which headers that string is valid (in a PUSH_PROMISE). For example,
+ // the string "http" would be valid for both the ":scheme" and ":authority"
+ // headers, so the bitfield paired with it is set to SCHEME | AUTH.
+ const unsigned char SCHEME = (1u << 0);
+ const unsigned char AUTH = (1u << 1);
+ const unsigned char PATH = (1u << 2);
+ const std::pair<const char*, unsigned char> input_headers[] = {
+ {"http", SCHEME | AUTH},
+ {"https", SCHEME | AUTH},
+ {"hTtP", SCHEME | AUTH},
+ {"HTTPS", SCHEME | AUTH},
+ {"www.google.com", AUTH},
+ {"90af90e0", AUTH},
+ {"12foo%20-bar:00001233", AUTH},
+ {"GOO\u200b\u2060\ufeffgoo", AUTH},
+ {"192.168.0.5", AUTH},
+ {"[::ffff:192.168.0.1.]", AUTH},
+ {"http:", AUTH},
+ {"bife l", AUTH},
+ {"/", PATH},
+ {"/foo/bar/baz", PATH},
+ {"/%20-2DVdkj.cie/foe_.iif/", PATH},
+ {"http://", 0},
+ {":443", 0},
+ {":80/eddd", 0},
+ {"google.com:-0", 0},
+ {"google.com:65536", 0},
+ {"http://google.com", 0},
+ {"http://google.com:39", 0},
+ {"//google.com/foo", 0},
+ {".com/", 0},
+ {"http://www.google.com/", 0},
+ {"http://foo:439", 0},
+ {"[::ffff:192.168", 0},
+ {"]/", 0},
+ {"//", 0}};
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(input_headers); ++i) {
+ bool should_accept = (input_headers[i].second & SCHEME);
+ for (size_t j = 0; j < QUIC_ARRAYSIZE(input_headers); ++j) {
+ bool should_accept_2 = should_accept && (input_headers[j].second & AUTH);
+ for (size_t k = 0; k < QUIC_ARRAYSIZE(input_headers); ++k) {
+ // |should_accept_3| indicates whether or not GetPushPromiseUrl() is
+ // expected to accept this input combination.
+ bool should_accept_3 =
+ should_accept_2 && (input_headers[k].second & PATH);
+
+ std::string url = SpdyUtils::GetPushPromiseUrl(input_headers[i].first,
+ input_headers[j].first,
+ input_headers[k].first);
+
+ ::testing::AssertionResult result = ::testing::AssertionSuccess();
+ if (url.empty() == should_accept_3) {
+ result = ::testing::AssertionFailure()
+ << "GetPushPromiseUrl() accepted/rejected the inputs when "
+ "it shouldn't have."
+ << std::endl
+ << " scheme: " << input_headers[i].first << std::endl
+ << " authority: " << input_headers[j].first << std::endl
+ << " path: " << input_headers[k].first << std::endl
+ << "Output: " << url << std::endl;
+ }
+ ASSERT_TRUE(result);
+ }
+ }
+ }
+
+ // Test canonicalization of various valid inputs.
+ EXPECT_EQ("http://www.google.com/",
+ SpdyUtils::GetPushPromiseUrl("http", "www.google.com", "/"));
+ EXPECT_EQ(
+ "https://www.goo-gle.com/fOOo/baRR",
+ SpdyUtils::GetPushPromiseUrl("hTtPs", "wWw.gOo-gLE.cOm", "/fOOo/baRR"));
+ EXPECT_EQ("https://www.goo-gle.com:3278/pAth/To/reSOurce",
+ SpdyUtils::GetPushPromiseUrl("hTtPs", "Www.gOo-Gle.Com:000003278",
+ "/pAth/To/reSOurce"));
+ EXPECT_EQ("https://foo%20bar/foo/bar/baz",
+ SpdyUtils::GetPushPromiseUrl("https", "foo bar", "/foo/bar/baz"));
+ EXPECT_EQ("http://foo.com:70/e/",
+ SpdyUtils::GetPushPromiseUrl("http", "foo.com:0000070", "/e/"));
+ EXPECT_EQ(
+ "http://192.168.0.1:70/e/",
+ SpdyUtils::GetPushPromiseUrl("http", "0300.0250.00.01:0070", "/e/"));
+ EXPECT_EQ("http://192.168.0.1/e/",
+ SpdyUtils::GetPushPromiseUrl("http", "0xC0a80001", "/e/"));
+ EXPECT_EQ("http://[::c0a8:1]/",
+ SpdyUtils::GetPushPromiseUrl("http", "[::192.168.0.1]", "/"));
+ EXPECT_EQ(
+ "https://[::ffff:c0a8:1]/",
+ SpdyUtils::GetPushPromiseUrl("https", "[::ffff:0xC0.0Xa8.0x0.0x1]", "/"));
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..6fb662a2008
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.cc
@@ -0,0 +1,139 @@
+// 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 "net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+
+namespace quic {
+
+#define ENDPOINT \
+ (session_->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
+
+LegacyQuicStreamIdManager::LegacyQuicStreamIdManager(
+ QuicSession* session,
+ size_t max_open_outgoing_streams,
+ size_t max_open_incoming_streams)
+ : session_(session),
+ max_open_outgoing_streams_(max_open_outgoing_streams),
+ max_open_incoming_streams_(max_open_incoming_streams),
+ next_outgoing_stream_id_(
+ QuicUtils::GetCryptoStreamId(
+ session->connection()->transport_version()) +
+ (session->perspective() == Perspective::IS_SERVER ? 1 : 2)),
+ largest_peer_created_stream_id_(
+ session->perspective() == Perspective::IS_SERVER
+ ? QuicUtils::GetCryptoStreamId(
+ session->connection()->transport_version())
+ : QuicUtils::GetInvalidStreamId(
+ session->connection()->transport_version())) {}
+
+LegacyQuicStreamIdManager::~LegacyQuicStreamIdManager() {
+ QUIC_LOG_IF(WARNING,
+ session_->num_locally_closed_incoming_streams_highest_offset() >
+ max_open_incoming_streams_)
+ << "Surprisingly high number of locally closed peer initiated streams"
+ "still waiting for final byte offset: "
+ << session_->num_locally_closed_incoming_streams_highest_offset();
+ QUIC_LOG_IF(WARNING,
+ session_->GetNumLocallyClosedOutgoingStreamsHighestOffset() >
+ max_open_outgoing_streams_)
+ << "Surprisingly high number of locally closed self initiated streams"
+ "still waiting for final byte offset: "
+ << session_->GetNumLocallyClosedOutgoingStreamsHighestOffset();
+}
+
+bool LegacyQuicStreamIdManager::CanOpenNextOutgoingStream(
+ size_t current_num_open_outgoing_streams) const {
+ if (current_num_open_outgoing_streams >= max_open_outgoing_streams_) {
+ QUIC_DLOG(INFO) << "Failed to create a new outgoing stream. "
+ << "Already " << current_num_open_outgoing_streams
+ << " open.";
+ return false;
+ }
+ return true;
+}
+
+bool LegacyQuicStreamIdManager::CanOpenIncomingStream(
+ size_t current_num_open_incoming_streams) const {
+ // Check if the new number of open streams would cause the number of
+ // open streams to exceed the limit.
+ return current_num_open_incoming_streams < max_open_incoming_streams_;
+}
+
+bool LegacyQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
+ const QuicStreamId stream_id) {
+ available_streams_.erase(stream_id);
+
+ if (largest_peer_created_stream_id_ !=
+ QuicUtils::GetInvalidStreamId(
+ session_->connection()->transport_version()) &&
+ stream_id <= largest_peer_created_stream_id_) {
+ return true;
+ }
+
+ // Check if the new number of available streams would cause the number of
+ // available streams to exceed the limit. Note that the peer can create
+ // only alternately-numbered streams.
+ size_t additional_available_streams =
+ (stream_id - largest_peer_created_stream_id_) / 2 - 1;
+ size_t new_num_available_streams =
+ GetNumAvailableStreams() + additional_available_streams;
+ if (new_num_available_streams > MaxAvailableStreams()) {
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Failed to create a new incoming stream with id:"
+ << stream_id << ". There are already "
+ << GetNumAvailableStreams()
+ << " streams available, which would become "
+ << new_num_available_streams << ", which exceeds the limit "
+ << MaxAvailableStreams() << ".";
+ session_->connection()->CloseConnection(
+ QUIC_TOO_MANY_AVAILABLE_STREAMS,
+ QuicStrCat(new_num_available_streams, " above ", MaxAvailableStreams()),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ for (QuicStreamId id = largest_peer_created_stream_id_ + 2; id < stream_id;
+ id += 2) {
+ available_streams_.insert(id);
+ }
+ largest_peer_created_stream_id_ = stream_id;
+
+ return true;
+}
+
+QuicStreamId LegacyQuicStreamIdManager::GetNextOutgoingStreamId() {
+ QuicStreamId id = next_outgoing_stream_id_;
+ next_outgoing_stream_id_ += 2;
+ return id;
+}
+
+bool LegacyQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
+ if (!IsIncomingStream(id)) {
+ // Stream IDs under next_ougoing_stream_id_ are either open or previously
+ // open but now closed.
+ return id >= next_outgoing_stream_id_;
+ }
+ // For peer created streams, we also need to consider available streams.
+ return largest_peer_created_stream_id_ ==
+ QuicUtils::GetInvalidStreamId(
+ session_->connection()->transport_version()) ||
+ id > largest_peer_created_stream_id_ ||
+ QuicContainsKey(available_streams_, id);
+}
+
+bool LegacyQuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
+ return id % 2 != next_outgoing_stream_id_ % 2;
+}
+
+size_t LegacyQuicStreamIdManager::GetNumAvailableStreams() const {
+ return available_streams_.size();
+}
+
+size_t LegacyQuicStreamIdManager::MaxAvailableStreams() const {
+ return max_open_incoming_streams_ * kMaxAvailableStreamsMultiplier;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h
new file mode 100644
index 00000000000..78d45453c79
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h
@@ -0,0 +1,104 @@
+// 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_CORE_LEGACY_QUIC_STREAM_ID_MANAGER_H_
+#define QUICHE_QUIC_CORE_LEGACY_QUIC_STREAM_ID_MANAGER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h"
+
+namespace quic {
+
+namespace test {
+class QuicSessionPeer;
+} // namespace test
+
+class QuicSession;
+
+// Manages Google QUIC stream IDs. This manager is responsible for two
+// questions: 1) can next outgoing stream ID be allocated (if yes, what is the
+// next outgoing stream ID) and 2) can a new incoming stream be opened.
+class QUIC_EXPORT_PRIVATE LegacyQuicStreamIdManager {
+ public:
+ LegacyQuicStreamIdManager(QuicSession* session,
+ size_t max_open_outgoing_streams,
+ size_t max_open_incoming_streams);
+
+ ~LegacyQuicStreamIdManager();
+
+ // Returns true if the next outgoing stream ID can be allocated.
+ bool CanOpenNextOutgoingStream(
+ size_t current_num_open_outgoing_streams) const;
+
+ // Returns true if a new incoming stream can be opened.
+ bool CanOpenIncomingStream(size_t current_num_open_incoming_streams) const;
+
+ bool MaybeIncreaseLargestPeerStreamId(const QuicStreamId id);
+
+ // Returns true if |id| is still available.
+ bool IsAvailableStream(QuicStreamId id) const;
+
+ // Returns the stream ID for a new outgoing stream, and increments the
+ // underlying counter.
+ QuicStreamId GetNextOutgoingStreamId();
+
+ // Return true if |id| is peer initiated.
+ bool IsIncomingStream(QuicStreamId id) const;
+
+ size_t MaxAvailableStreams() const;
+
+ void set_max_open_incoming_streams(size_t max_open_incoming_streams) {
+ max_open_incoming_streams_ = max_open_incoming_streams;
+ }
+
+ void set_max_open_outgoing_streams(size_t max_open_outgoing_streams) {
+ max_open_outgoing_streams_ = max_open_outgoing_streams;
+ }
+
+ void set_largest_peer_created_stream_id(
+ QuicStreamId largest_peer_created_stream_id) {
+ largest_peer_created_stream_id_ = largest_peer_created_stream_id;
+ }
+
+ size_t max_open_incoming_streams() const {
+ return max_open_incoming_streams_;
+ }
+
+ size_t max_open_outgoing_streams() const {
+ return max_open_outgoing_streams_;
+ }
+
+ QuicStreamId next_outgoing_stream_id() const {
+ return next_outgoing_stream_id_;
+ }
+
+ QuicStreamId largest_peer_created_stream_id() const {
+ return largest_peer_created_stream_id_;
+ }
+
+ private:
+ friend class test::QuicSessionPeer;
+
+ size_t GetNumAvailableStreams() const;
+
+ // Not owned.
+ QuicSession* session_;
+
+ // The maximum number of outgoing streams this connection can open.
+ size_t max_open_outgoing_streams_;
+
+ // The maximum number of incoming streams this connection will allow.
+ size_t max_open_incoming_streams_;
+
+ // The ID to use for the next outgoing stream.
+ QuicStreamId next_outgoing_stream_id_;
+
+ // Set of stream ids that are less than the largest stream id that has been
+ // received, but are nonetheless available to be created.
+ QuicUnorderedSet<QuicStreamId> available_streams_;
+
+ QuicStreamId largest_peer_created_stream_id_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_LEGACY_QUIC_STREAM_ID_MANAGER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager_test.cc
new file mode 100644
index 00000000000..823cdc0d52e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager_test.cc
@@ -0,0 +1,179 @@
+// 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 "net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using testing::_;
+using testing::StrictMock;
+
+class LegacyQuicStreamIdManagerTest : public QuicTest {
+ protected:
+ void Initialize(Perspective perspective) {
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ connection_ = new MockQuicConnection(
+ &helper_, &alarm_factory_, perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0));
+ session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_);
+ manager_ = QuicSessionPeer::GetStreamIdManager(session_.get());
+ session_->Initialize();
+ }
+
+ QuicStreamId GetNthClientInitiatedId(int n) { return 3 + 2 * n; }
+
+ QuicStreamId GetNthServerInitiatedId(int n) { return 2 + 2 * n; }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnection* connection_;
+ std::unique_ptr<StrictMock<MockQuicSession>> session_;
+ LegacyQuicStreamIdManager* manager_;
+};
+
+class LegacyQuicStreamIdManagerTestServer
+ : public LegacyQuicStreamIdManagerTest {
+ protected:
+ LegacyQuicStreamIdManagerTestServer() { Initialize(Perspective::IS_SERVER); }
+};
+
+TEST_F(LegacyQuicStreamIdManagerTestServer, CanOpenNextOutgoingStream) {
+ EXPECT_TRUE(manager_->CanOpenNextOutgoingStream(
+ manager_->max_open_outgoing_streams() - 1));
+ EXPECT_FALSE(manager_->CanOpenNextOutgoingStream(
+ manager_->max_open_outgoing_streams()));
+}
+
+TEST_F(LegacyQuicStreamIdManagerTestServer, CanOpenIncomingStream) {
+ EXPECT_TRUE(manager_->CanOpenIncomingStream(
+ manager_->max_open_incoming_streams() - 1));
+ EXPECT_FALSE(
+ manager_->CanOpenIncomingStream(manager_->max_open_incoming_streams()));
+}
+
+TEST_F(LegacyQuicStreamIdManagerTestServer, AvailableStreams) {
+ ASSERT_TRUE(
+ manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(3)));
+ EXPECT_TRUE(manager_->IsAvailableStream(GetNthClientInitiatedId(1)));
+ EXPECT_TRUE(manager_->IsAvailableStream(GetNthClientInitiatedId(2)));
+ ASSERT_TRUE(
+ manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(2)));
+ ASSERT_TRUE(
+ manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(1)));
+}
+
+TEST_F(LegacyQuicStreamIdManagerTestServer, MaxAvailableStreams) {
+ // Test that the server closes the connection if a client makes too many data
+ // streams available. The server accepts slightly more than the negotiated
+ // stream limit to deal with rare cases where a client FIN/RST is lost.
+ const size_t kMaxStreamsForTest = 10;
+ session_->OnConfigNegotiated();
+ const size_t kAvailableStreamLimit = manager_->MaxAvailableStreams();
+ EXPECT_EQ(
+ manager_->max_open_incoming_streams() * kMaxAvailableStreamsMultiplier,
+ manager_->MaxAvailableStreams());
+ // The protocol specification requires that there can be at least 10 times
+ // as many available streams as the connection's maximum open streams.
+ EXPECT_LE(10 * kMaxStreamsForTest, kAvailableStreamLimit);
+
+ EXPECT_TRUE(
+ manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(0)));
+
+ // Establish available streams up to the server's limit.
+ const int kLimitingStreamId =
+ GetNthClientInitiatedId(kAvailableStreamLimit + 1);
+ // This exceeds the stream limit. In versions other than 99
+ // this is allowed. Version 99 hews to the IETF spec and does
+ // not allow it.
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(kLimitingStreamId));
+ // A further available stream will result in connection close.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _));
+
+ // This forces stream kLimitingStreamId + 2 to become available, which
+ // violates the quota.
+ EXPECT_FALSE(
+ manager_->MaybeIncreaseLargestPeerStreamId(kLimitingStreamId + 2 * 2));
+}
+
+TEST_F(LegacyQuicStreamIdManagerTestServer, MaximumAvailableOpenedStreams) {
+ QuicStreamId stream_id = GetNthClientInitiatedId(0);
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+ stream_id + 2 * (manager_->max_open_incoming_streams() - 1)));
+}
+
+TEST_F(LegacyQuicStreamIdManagerTestServer, TooManyAvailableStreams) {
+ QuicStreamId stream_id = GetNthClientInitiatedId(0);
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+
+ // A stream ID which is too large to create.
+ QuicStreamId stream_id2 =
+ GetNthClientInitiatedId(2 * manager_->MaxAvailableStreams() + 4);
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _));
+ EXPECT_FALSE(manager_->MaybeIncreaseLargestPeerStreamId(stream_id2));
+}
+
+TEST_F(LegacyQuicStreamIdManagerTestServer, ManyAvailableStreams) {
+ // When max_open_streams_ is 200, should be able to create 200 streams
+ // out-of-order, that is, creating the one with the largest stream ID first.
+ manager_->set_max_open_incoming_streams(200);
+ QuicStreamId stream_id = GetNthClientInitiatedId(0);
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+
+ // Create the largest stream ID of a threatened total of 200 streams.
+ // GetNth... starts at 0, so for 200 streams, get the 199th.
+ EXPECT_TRUE(
+ manager_->MaybeIncreaseLargestPeerStreamId(GetNthClientInitiatedId(199)));
+}
+
+TEST_F(LegacyQuicStreamIdManagerTestServer,
+ TestMaxIncomingAndOutgoingStreamsAllowed) {
+ // Tests that on server side, the value of max_open_incoming/outgoing
+ // streams are setup correctly during negotiation. The value for outgoing
+ // stream is limited to negotiated value and for incoming stream it is set to
+ // be larger than that.
+ session_->OnConfigNegotiated();
+ // The max number of open outgoing streams is less than that of incoming
+ // streams, and it should be same as negotiated value.
+ EXPECT_LT(manager_->max_open_outgoing_streams(),
+ manager_->max_open_incoming_streams());
+ EXPECT_EQ(manager_->max_open_outgoing_streams(),
+ kDefaultMaxStreamsPerConnection);
+ EXPECT_GT(manager_->max_open_incoming_streams(),
+ kDefaultMaxStreamsPerConnection);
+}
+
+class LegacyQuicStreamIdManagerTestClient
+ : public LegacyQuicStreamIdManagerTest {
+ protected:
+ LegacyQuicStreamIdManagerTestClient() { Initialize(Perspective::IS_CLIENT); }
+};
+
+TEST_F(LegacyQuicStreamIdManagerTestClient,
+ TestMaxIncomingAndOutgoingStreamsAllowed) {
+ // Tests that on client side, the value of max_open_incoming/outgoing
+ // streams are setup correctly during negotiation. When flag is true, the
+ // value for outgoing stream is limited to negotiated value and for incoming
+ // stream it is set to be larger than that.
+ session_->OnConfigNegotiated();
+ EXPECT_LT(manager_->max_open_outgoing_streams(),
+ manager_->max_open_incoming_streams());
+ EXPECT_EQ(manager_->max_open_outgoing_streams(),
+ kDefaultMaxStreamsPerConnection);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h b/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h
new file mode 100644
index 00000000000..695ff7d91ec
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h
@@ -0,0 +1,225 @@
+// 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_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_
+#define QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_number.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+// PacketNumberIndexedQueue is a queue of mostly continuous numbered entries
+// which supports the following operations:
+// - adding elements to the end of the queue, or at some point past the end
+// - removing elements in any order
+// - retrieving elements
+// If all elements are inserted in order, all of the operations above are
+// amortized O(1) time.
+//
+// Internally, the data structure is a deque where each element is marked as
+// present or not. The deque starts at the lowest present index. Whenever an
+// element is removed, it's marked as not present, and the front of the deque is
+// cleared of elements that are not present.
+//
+// The tail of the queue is not cleared due to the assumption of entries being
+// inserted in order, though removing all elements of the queue will return it
+// to its initial state.
+//
+// Note that this data structure is inherently hazardous, since an addition of
+// just two entries will cause it to consume all of the memory available.
+// Because of that, it is not a general-purpose container and should not be used
+// as one.
+template <typename T>
+class PacketNumberIndexedQueue {
+ public:
+ PacketNumberIndexedQueue() : number_of_present_entries_(0) {}
+
+ // Retrieve the entry associated with the packet number. Returns the pointer
+ // to the entry in case of success, or nullptr if the entry does not exist.
+ T* GetEntry(QuicPacketNumber packet_number);
+ const T* GetEntry(QuicPacketNumber packet_number) const;
+
+ // Inserts data associated |packet_number| into (or past) the end of the
+ // queue, filling up the missing intermediate entries as necessary. Returns
+ // true if the element has been inserted successfully, false if it was already
+ // in the queue or inserted out of order.
+ template <typename... Args>
+ bool Emplace(QuicPacketNumber packet_number, Args&&... args);
+
+ // Removes data associated with |packet_number| and frees the slots in the
+ // queue as necessary.
+ bool Remove(QuicPacketNumber packet_number);
+
+ // Same as above, but if an entry is present in the queue, also call f(entry)
+ // before removing it.
+ template <typename Function>
+ bool Remove(QuicPacketNumber packet_number, Function f);
+
+ bool IsEmpty() const { return number_of_present_entries_ == 0; }
+
+ // Returns the number of entries in the queue.
+ size_t number_of_present_entries() const {
+ return number_of_present_entries_;
+ }
+
+ // Returns the number of entries allocated in the underlying deque. This is
+ // proportional to the memory usage of the queue.
+ size_t entry_slots_used() const { return entries_.size(); }
+
+ // Packet number of the first entry in the queue.
+ QuicPacketNumber first_packet() const { return first_packet_; }
+
+ // Packet number of the last entry ever inserted in the queue. Note that the
+ // entry in question may have already been removed. Zero if the queue is
+ // empty.
+ QuicPacketNumber last_packet() const {
+ if (IsEmpty()) {
+ return QuicPacketNumber();
+ }
+ return first_packet_ + entries_.size() - 1;
+ }
+
+ private:
+ // Wrapper around T used to mark whether the entry is actually in the map.
+ struct EntryWrapper : T {
+ bool present;
+
+ EntryWrapper() : present(false) {}
+
+ template <typename... Args>
+ explicit EntryWrapper(Args&&... args)
+ : T(std::forward<Args>(args)...), present(true) {}
+ };
+
+ // Cleans up unused slots in the front after removing an element.
+ void Cleanup();
+
+ const EntryWrapper* GetEntryWrapper(QuicPacketNumber offset) const;
+ EntryWrapper* GetEntryWrapper(QuicPacketNumber offset) {
+ const auto* const_this = this;
+ return const_cast<EntryWrapper*>(const_this->GetEntryWrapper(offset));
+ }
+
+ QuicDeque<EntryWrapper> entries_;
+ size_t number_of_present_entries_;
+ QuicPacketNumber first_packet_;
+};
+
+template <typename T>
+T* PacketNumberIndexedQueue<T>::GetEntry(QuicPacketNumber packet_number) {
+ EntryWrapper* entry = GetEntryWrapper(packet_number);
+ if (entry == nullptr) {
+ return nullptr;
+ }
+ return entry;
+}
+
+template <typename T>
+const T* PacketNumberIndexedQueue<T>::GetEntry(
+ QuicPacketNumber packet_number) const {
+ const EntryWrapper* entry = GetEntryWrapper(packet_number);
+ if (entry == nullptr) {
+ return nullptr;
+ }
+ return entry;
+}
+
+template <typename T>
+template <typename... Args>
+bool PacketNumberIndexedQueue<T>::Emplace(QuicPacketNumber packet_number,
+ Args&&... args) {
+ if (!packet_number.IsInitialized()) {
+ QUIC_BUG << "Try to insert an uninitialized packet number";
+ return false;
+ }
+
+ if (IsEmpty()) {
+ DCHECK(entries_.empty());
+ DCHECK(!first_packet_.IsInitialized());
+
+ entries_.emplace_back(std::forward<Args>(args)...);
+ number_of_present_entries_ = 1;
+ first_packet_ = packet_number;
+ return true;
+ }
+
+ // Do not allow insertion out-of-order.
+ if (packet_number <= last_packet()) {
+ return false;
+ }
+
+ // Handle potentially missing elements.
+ size_t offset = packet_number - first_packet_;
+ if (offset > entries_.size()) {
+ entries_.resize(offset);
+ }
+
+ number_of_present_entries_++;
+ entries_.emplace_back(std::forward<Args>(args)...);
+ DCHECK_EQ(packet_number, last_packet());
+ return true;
+}
+
+template <typename T>
+bool PacketNumberIndexedQueue<T>::Remove(QuicPacketNumber packet_number) {
+ return Remove(packet_number, [](const T&) {});
+}
+
+template <typename T>
+template <typename Function>
+bool PacketNumberIndexedQueue<T>::Remove(QuicPacketNumber packet_number,
+ Function f) {
+ EntryWrapper* entry = GetEntryWrapper(packet_number);
+ if (entry == nullptr) {
+ return false;
+ }
+ f(*static_cast<const T*>(entry));
+ entry->present = false;
+ number_of_present_entries_--;
+
+ if (packet_number == first_packet()) {
+ Cleanup();
+ }
+ return true;
+}
+
+template <typename T>
+void PacketNumberIndexedQueue<T>::Cleanup() {
+ while (!entries_.empty() && !entries_.front().present) {
+ entries_.pop_front();
+ first_packet_++;
+ }
+ if (entries_.empty()) {
+ first_packet_.Clear();
+ }
+}
+
+template <typename T>
+auto PacketNumberIndexedQueue<T>::GetEntryWrapper(
+ QuicPacketNumber packet_number) const -> const EntryWrapper* {
+ if (!packet_number.IsInitialized() || IsEmpty() ||
+ packet_number < first_packet_) {
+ return nullptr;
+ }
+
+ uint64_t offset = packet_number - first_packet_;
+ if (offset >= entries_.size()) {
+ return nullptr;
+ }
+
+ const EntryWrapper* entry = &entries_[offset];
+ if (!entry->present) {
+ return nullptr;
+ }
+
+ return entry;
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_PACKET_NUMBER_INDEXED_QUEUE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue_test.cc b/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue_test.cc
new file mode 100644
index 00000000000..c2931336bc0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/packet_number_indexed_queue_test.cc
@@ -0,0 +1,179 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h"
+
+#include <limits>
+#include <map>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace {
+
+class PacketNumberIndexedQueueTest : public QuicTest {
+ public:
+ PacketNumberIndexedQueueTest() {}
+
+ protected:
+ PacketNumberIndexedQueue<std::string> queue_;
+};
+
+TEST_F(PacketNumberIndexedQueueTest, InitialState) {
+ EXPECT_TRUE(queue_.IsEmpty());
+ EXPECT_FALSE(queue_.first_packet().IsInitialized());
+ EXPECT_FALSE(queue_.last_packet().IsInitialized());
+ EXPECT_EQ(0u, queue_.number_of_present_entries());
+ EXPECT_EQ(0u, queue_.entry_slots_used());
+}
+
+TEST_F(PacketNumberIndexedQueueTest, InsertingContinuousElements) {
+ ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1001), "one"));
+ EXPECT_EQ("one", *queue_.GetEntry(QuicPacketNumber(1001)));
+
+ ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1002), "two"));
+ EXPECT_EQ("two", *queue_.GetEntry(QuicPacketNumber(1002)));
+
+ EXPECT_FALSE(queue_.IsEmpty());
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(1002u), queue_.last_packet());
+ EXPECT_EQ(2u, queue_.number_of_present_entries());
+ EXPECT_EQ(2u, queue_.entry_slots_used());
+}
+
+TEST_F(PacketNumberIndexedQueueTest, InsertingOutOfOrder) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+
+ ASSERT_TRUE(queue_.Emplace(QuicPacketNumber(1003), "three"));
+ EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1002)));
+ EXPECT_EQ("three", *queue_.GetEntry(QuicPacketNumber(1003)));
+
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet());
+ EXPECT_EQ(2u, queue_.number_of_present_entries());
+ EXPECT_EQ(3u, queue_.entry_slots_used());
+
+ ASSERT_FALSE(queue_.Emplace(QuicPacketNumber(1002), "two"));
+}
+
+TEST_F(PacketNumberIndexedQueueTest, InsertingIntoPast) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1000), "zero"));
+}
+
+TEST_F(PacketNumberIndexedQueueTest, InsertingDuplicate) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1001), "one"));
+}
+
+TEST_F(PacketNumberIndexedQueueTest, RemoveInTheMiddle) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ queue_.Emplace(QuicPacketNumber(1002), "two");
+ queue_.Emplace(QuicPacketNumber(1003), "three");
+
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1002)));
+ EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1002)));
+
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet());
+ EXPECT_EQ(2u, queue_.number_of_present_entries());
+ EXPECT_EQ(3u, queue_.entry_slots_used());
+
+ EXPECT_FALSE(queue_.Emplace(QuicPacketNumber(1002), "two"));
+ EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(1004), "four"));
+}
+
+TEST_F(PacketNumberIndexedQueueTest, RemoveAtImmediateEdges) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ queue_.Emplace(QuicPacketNumber(1002), "two");
+ queue_.Emplace(QuicPacketNumber(1003), "three");
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001)));
+ EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1001)));
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1003)));
+ EXPECT_EQ(nullptr, queue_.GetEntry(QuicPacketNumber(1003)));
+
+ EXPECT_EQ(QuicPacketNumber(1002u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(1003u), queue_.last_packet());
+ EXPECT_EQ(1u, queue_.number_of_present_entries());
+ EXPECT_EQ(2u, queue_.entry_slots_used());
+
+ EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(1004), "four"));
+}
+
+TEST_F(PacketNumberIndexedQueueTest, RemoveAtDistantFront) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ queue_.Emplace(QuicPacketNumber(1002), "one (kinda)");
+ queue_.Emplace(QuicPacketNumber(2001), "two");
+
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
+ EXPECT_EQ(3u, queue_.number_of_present_entries());
+ EXPECT_EQ(1001u, queue_.entry_slots_used());
+
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1002)));
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
+ EXPECT_EQ(2u, queue_.number_of_present_entries());
+ EXPECT_EQ(1001u, queue_.entry_slots_used());
+
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001)));
+ EXPECT_EQ(QuicPacketNumber(2001u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
+ EXPECT_EQ(1u, queue_.number_of_present_entries());
+ EXPECT_EQ(1u, queue_.entry_slots_used());
+}
+
+TEST_F(PacketNumberIndexedQueueTest, RemoveAtDistantBack) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ queue_.Emplace(QuicPacketNumber(2001), "two");
+
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
+
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(2001)));
+ EXPECT_EQ(QuicPacketNumber(1001u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(2001u), queue_.last_packet());
+}
+
+TEST_F(PacketNumberIndexedQueueTest, ClearAndRepopulate) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ queue_.Emplace(QuicPacketNumber(2001), "two");
+
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001)));
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(2001)));
+ EXPECT_TRUE(queue_.IsEmpty());
+ EXPECT_FALSE(queue_.first_packet().IsInitialized());
+ EXPECT_FALSE(queue_.last_packet().IsInitialized());
+
+ EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(101), "one"));
+ EXPECT_TRUE(queue_.Emplace(QuicPacketNumber(201), "two"));
+ EXPECT_EQ(QuicPacketNumber(101u), queue_.first_packet());
+ EXPECT_EQ(QuicPacketNumber(201u), queue_.last_packet());
+}
+
+TEST_F(PacketNumberIndexedQueueTest, FailToRemoveElementsThatNeverExisted) {
+ ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1000)));
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1000)));
+ ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1002)));
+}
+
+TEST_F(PacketNumberIndexedQueueTest, FailToRemoveElementsTwice) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ ASSERT_TRUE(queue_.Remove(QuicPacketNumber(1001)));
+ ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1001)));
+ ASSERT_FALSE(queue_.Remove(QuicPacketNumber(1001)));
+}
+
+TEST_F(PacketNumberIndexedQueueTest, ConstGetter) {
+ queue_.Emplace(QuicPacketNumber(1001), "one");
+ const auto& const_queue = queue_;
+
+ EXPECT_EQ("one", *const_queue.GetEntry(QuicPacketNumber(1001)));
+ EXPECT_EQ(nullptr, const_queue.GetEntry(QuicPacketNumber(1002)));
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/proto/cached_network_parameters.proto b/chromium/net/third_party/quiche/src/quic/core/proto/cached_network_parameters.proto
new file mode 100644
index 00000000000..d609be9b0d1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/proto/cached_network_parameters.proto
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package quic;
+
+// CachedNetworkParameters contains data that can be used to choose appropriate
+// connection parameters (initial RTT, initial CWND, etc.) in new connections.
+// Next id: 8
+message CachedNetworkParameters {
+ // Describes the state of the connection during which the supplied network
+ // parameters were calculated.
+ enum PreviousConnectionState {
+ SLOW_START = 0;
+ CONGESTION_AVOIDANCE = 1;
+ }
+
+ // serving_region is used to decide whether or not the bandwidth estimate and
+ // min RTT are reasonable and if they should be used.
+ // For example a group of geographically close servers may share the same
+ // serving_region string if they are expected to have similar network
+ // performance.
+ optional string serving_region = 1;
+ // The server can supply a bandwidth estimate (in bytes/s) which it may re-use
+ // on receipt of a source-address token with this field set.
+ optional int32 bandwidth_estimate_bytes_per_second = 2;
+ // The maximum bandwidth seen to the client, not necessarily the latest.
+ optional int32 max_bandwidth_estimate_bytes_per_second = 5;
+ // Timestamp (seconds since UNIX epoch) that indicates when the max bandwidth
+ // was seen by the server.
+ optional int64 max_bandwidth_timestamp_seconds = 6;
+ // The min RTT seen on a previous connection can be used by the server to
+ // inform initial connection parameters for new connections.
+ optional int32 min_rtt_ms = 3;
+ // Encodes the PreviousConnectionState enum.
+ optional int32 previous_connection_state = 4;
+ // UNIX timestamp when this bandwidth estimate was created.
+ optional int64 timestamp = 7;
+};
diff --git a/chromium/net/third_party/quiche/src/quic/core/proto/crypto_server_config.proto b/chromium/net/third_party/quiche/src/quic/core/proto/crypto_server_config.proto
new file mode 100644
index 00000000000..bb447dcc4b6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/proto/crypto_server_config.proto
@@ -0,0 +1,34 @@
+// Copyright 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package quic;
+
+// QuicServerConfigProtobuf contains QUIC server config block and the private
+// keys needed to prove ownership.
+message QuicServerConfigProtobuf {
+ // config is a serialised config in QUIC wire format.
+ required bytes config = 1;
+
+ // PrivateKey contains a QUIC tag of a key exchange algorithm and a
+ // serialised private key for that algorithm. The format of the serialised
+ // private key is specific to the algorithm in question.
+ message PrivateKey {
+ required uint32 tag = 1;
+ required bytes private_key = 2;
+ }
+ repeated PrivateKey key = 2;
+
+ // primary_time contains a UNIX epoch seconds value that indicates when this
+ // config should become primary.
+ optional int64 primary_time = 3;
+
+ // Relative priority of this config vs other configs with the same
+ // primary time. For use as a secondary sort key when selecting the
+ // primary config.
+ optional uint64 priority = 4;
+};
diff --git a/chromium/net/third_party/quiche/src/quic/core/proto/source_address_token.proto b/chromium/net/third_party/quiche/src/quic/core/proto/source_address_token.proto
new file mode 100644
index 00000000000..1897a256886
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/proto/source_address_token.proto
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+import "cached_network_parameters.proto";
+
+package quic;
+
+// A SourceAddressToken is serialised, encrypted and sent to clients so that
+// they can prove ownership of an IP address.
+message SourceAddressToken {
+ // ip contains either 4 (IPv4) or 16 (IPv6) bytes of IP address in network
+ // byte order.
+ required bytes ip = 1;
+ // timestamp contains a UNIX timestamp value of the time when the token was
+ // created.
+ required int64 timestamp = 2;
+ // The server can provide estimated network parameters to be used for
+ // initial parameter selection in future connections.
+ optional CachedNetworkParameters cached_network_parameters = 3;
+};
+
+// SourceAddressTokens are simply lists of SourceAddressToken messages.
+message SourceAddressTokens {
+ // This field has id 4 to avoid ambiguity between the serialized form of
+ // SourceAddressToken vs SourceAddressTokens.
+ repeated SourceAddressToken tokens = 4;
+};
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc
new file mode 100644
index 00000000000..42f572b3eac
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc
@@ -0,0 +1,273 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h"
+
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_file_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+QpackOfflineDecoder::QpackOfflineDecoder()
+ : encoder_stream_error_detected_(false),
+ decoder_(this, &decoder_stream_sender_delegate_) {}
+
+bool QpackOfflineDecoder::DecodeAndVerifyOfflineData(
+ QuicStringPiece input_filename,
+ QuicStringPiece expected_headers_filename) {
+ if (!ParseInputFilename(input_filename)) {
+ QUIC_LOG(ERROR) << "Error parsing input filename " << input_filename;
+ return false;
+ }
+
+ if (!DecodeHeaderBlocksFromFile(input_filename)) {
+ QUIC_LOG(ERROR) << "Error decoding header blocks in " << input_filename;
+ return false;
+ }
+
+ if (!VerifyDecodedHeaderLists(expected_headers_filename)) {
+ QUIC_LOG(ERROR) << "Header lists decoded from " << input_filename
+ << " to not match expected headers parsed from "
+ << expected_headers_filename;
+ return false;
+ }
+
+ return true;
+}
+
+void QpackOfflineDecoder::OnEncoderStreamError(QuicStringPiece error_message) {
+ QUIC_LOG(ERROR) << "Encoder stream error: " << error_message;
+ encoder_stream_error_detected_ = true;
+}
+
+bool QpackOfflineDecoder::ParseInputFilename(QuicStringPiece input_filename) {
+ auto pieces = QuicTextUtils::Split(input_filename, '.');
+
+ if (pieces.size() < 3) {
+ QUIC_LOG(ERROR) << "Not enough fields in input filename " << input_filename;
+ return false;
+ }
+
+ 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 {
+ QUIC_LOG(ERROR)
+ << "Header acknowledgement field must be 0 or 1 in input filename "
+ << input_filename;
+ return false;
+ }
+
+ ++piece_it;
+
+ // Maximum allowed number of blocked streams.
+ uint64_t max_blocked_streams = 0;
+ if (!QuicTextUtils::StringToUint64(*piece_it, &max_blocked_streams)) {
+ QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it
+ << "\" as an integer.";
+ return false;
+ }
+
+ if (max_blocked_streams > 0) {
+ // TODO(bnc): Implement blocked streams.
+ QUIC_LOG(ERROR) << "Blocked streams not implemented.";
+ return false;
+ }
+
+ ++piece_it;
+
+ // Dynamic Table Size in bytes
+ uint64_t dynamic_table_size = 0;
+ if (!QuicTextUtils::StringToUint64(*piece_it, &dynamic_table_size)) {
+ QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it
+ << "\" as an integer.";
+ return false;
+ }
+
+ decoder_.SetMaximumDynamicTableCapacity(dynamic_table_size);
+
+ return true;
+}
+
+bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile(
+ QuicStringPiece input_filename) {
+ // Store data in |input_data_storage|; use a QuicStringPiece to efficiently
+ // keep track of remaining portion yet to be decoded.
+ std::string input_data_storage;
+ ReadFileContents(input_filename, &input_data_storage);
+ QuicStringPiece input_data(input_data_storage);
+
+ while (!input_data.empty()) {
+ if (input_data.size() < sizeof(uint64_t) + sizeof(uint32_t)) {
+ QUIC_LOG(ERROR) << "Unexpected end of input file.";
+ return false;
+ }
+
+ uint64_t stream_id = QuicEndian::NetToHost64(
+ *reinterpret_cast<const uint64_t*>(input_data.data()));
+ input_data = input_data.substr(sizeof(uint64_t));
+
+ uint32_t length = QuicEndian::NetToHost32(
+ *reinterpret_cast<const uint32_t*>(input_data.data()));
+ input_data = input_data.substr(sizeof(uint32_t));
+
+ if (input_data.size() < length) {
+ QUIC_LOG(ERROR) << "Unexpected end of input file.";
+ return false;
+ }
+
+ QuicStringPiece data = input_data.substr(0, length);
+ input_data = input_data.substr(length);
+
+ if (stream_id == 0) {
+ decoder_.DecodeEncoderStreamData(data);
+
+ if (encoder_stream_error_detected_) {
+ QUIC_LOG(ERROR) << "Error detected on encoder stream.";
+ return false;
+ }
+
+ continue;
+ }
+
+ test::TestHeadersHandler headers_handler;
+
+ auto progressive_decoder =
+ decoder_.DecodeHeaderBlock(stream_id, &headers_handler);
+ progressive_decoder->Decode(data);
+ progressive_decoder->EndHeaderBlock();
+
+ if (headers_handler.decoding_error_detected()) {
+ QUIC_LOG(ERROR) << "Decoding error on stream " << stream_id;
+ return false;
+ }
+
+ decoded_header_lists_.push_back(headers_handler.ReleaseHeaderList());
+ }
+
+ return true;
+}
+
+bool QpackOfflineDecoder::VerifyDecodedHeaderLists(
+ QuicStringPiece expected_headers_filename) {
+ // Store data in |expected_headers_data_storage|; use a QuicStringPiece 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);
+ QuicStringPiece expected_headers_data(expected_headers_data_storage);
+
+ while (!decoded_header_lists_.empty()) {
+ spdy::SpdyHeaderBlock decoded_header_list =
+ std::move(decoded_header_lists_.front());
+ decoded_header_lists_.pop_front();
+
+ spdy::SpdyHeaderBlock expected_header_list;
+ if (!ReadNextExpectedHeaderList(&expected_headers_data,
+ &expected_header_list)) {
+ QUIC_LOG(ERROR)
+ << "Error parsing expected header list to match next decoded "
+ "header list.";
+ return false;
+ }
+
+ if (!CompareHeaderBlocks(std::move(decoded_header_list),
+ std::move(expected_header_list))) {
+ QUIC_LOG(ERROR) << "Decoded header does not match expected header.";
+ return false;
+ }
+ }
+
+ if (!expected_headers_data.empty()) {
+ QUIC_LOG(ERROR)
+ << "Not enough encoded header lists to match expected ones.";
+ return false;
+ }
+
+ return true;
+}
+
+bool QpackOfflineDecoder::ReadNextExpectedHeaderList(
+ QuicStringPiece* expected_headers_data,
+ spdy::SpdyHeaderBlock* expected_header_list) {
+ while (true) {
+ QuicStringPiece::size_type endline = expected_headers_data->find('\n');
+
+ // Even last header list must be followed by an empty line.
+ if (endline == QuicStringPiece::npos) {
+ QUIC_LOG(ERROR) << "Unexpected end of expected header list file.";
+ return false;
+ }
+
+ if (endline == 0) {
+ // Empty line indicates end of header list.
+ *expected_headers_data = expected_headers_data->substr(1);
+ return true;
+ }
+
+ QuicStringPiece header_field = expected_headers_data->substr(0, endline);
+ auto pieces = QuicTextUtils::Split(header_field, '\t');
+
+ if (pieces.size() != 2) {
+ QUIC_LOG(ERROR) << "Header key and value must be separated by TAB.";
+ return false;
+ }
+
+ expected_header_list->AppendValueOrAddHeader(pieces[0], pieces[1]);
+
+ *expected_headers_data = expected_headers_data->substr(endline + 1);
+ }
+}
+
+bool QpackOfflineDecoder::CompareHeaderBlocks(
+ spdy::SpdyHeaderBlock decoded_header_list,
+ spdy::SpdyHeaderBlock expected_header_list) {
+ if (decoded_header_list == expected_header_list) {
+ return true;
+ }
+
+ // The h2o decoder reshuffles the "content-length" header and pseudo-headers,
+ // see
+ // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md.
+ // Remove such headers one by one if they match.
+ const char* kContentLength = "content-length";
+ const char* kPseudoHeaderPrefix = ":";
+ for (spdy::SpdyHeaderBlock::iterator decoded_it = decoded_header_list.begin();
+ decoded_it != decoded_header_list.end();) {
+ const QuicStringPiece key = decoded_it->first;
+ if (key != kContentLength &&
+ !QuicTextUtils::StartsWith(key, kPseudoHeaderPrefix)) {
+ ++decoded_it;
+ continue;
+ }
+ spdy::SpdyHeaderBlock::iterator expected_it =
+ expected_header_list.find(key);
+ if (expected_it == expected_header_list.end() ||
+ decoded_it->second != expected_it->second) {
+ ++decoded_it;
+ continue;
+ }
+ // SpdyHeaderBlock does not support erasing by iterator, only by key.
+ ++decoded_it;
+ expected_header_list.erase(key);
+ // This will invalidate |key|.
+ decoded_header_list.erase(key);
+ }
+
+ return decoded_header_list == expected_header_list;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h
new file mode 100644
index 00000000000..922fd64835b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h
@@ -0,0 +1,71 @@
+// 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_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_
+#define QUICHE_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_
+
+#include <list>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+namespace quic {
+
+// A decoder to read encoded data from a file, decode it, and compare to
+// a list of expected header lists read from another file. File format is
+// described at
+// https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop.
+class QpackOfflineDecoder : public QpackDecoder::EncoderStreamErrorDelegate {
+ public:
+ QpackOfflineDecoder();
+ ~QpackOfflineDecoder() override = default;
+
+ // Read encoded header blocks and encoder stream data from |input_filename|
+ // and decode them, read expected header lists from
+ // |expected_headers_filename|, and compare decoded header lists to expected
+ // ones. Returns true if there is an equal number of them and the
+ // corresponding ones match, false otherwise.
+ bool DecodeAndVerifyOfflineData(QuicStringPiece input_filename,
+ QuicStringPiece expected_headers_filename);
+
+ // QpackDecoder::EncoderStreamErrorDelegate implementation:
+ void OnEncoderStreamError(QuicStringPiece error_message) override;
+
+ private:
+ // Parse decoder parameters from |input_filename| and set up |decoder_|
+ // accordingly.
+ bool ParseInputFilename(QuicStringPiece input_filename);
+
+ // Read encoded header blocks and encoder stream data from |input_filename|,
+ // pass them to |decoder_| for decoding, and add decoded header lists to
+ // |decoded_header_lists_|.
+ bool DecodeHeaderBlocksFromFile(QuicStringPiece input_filename);
+
+ // Read expected header lists from |expected_headers_filename| and verify
+ // decoded header lists in |decoded_header_lists_| against them.
+ bool VerifyDecodedHeaderLists(QuicStringPiece expected_headers_filename);
+
+ // Parse next header list from |*expected_headers_data| into
+ // |*expected_header_list|, removing consumed data from the beginning of
+ // |*expected_headers_data|. Returns true on success, false if parsing fails.
+ bool ReadNextExpectedHeaderList(QuicStringPiece* expected_headers_data,
+ spdy::SpdyHeaderBlock* expected_header_list);
+
+ // Compare two header lists. Allow for different orders of certain headers as
+ // described at
+ // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md.
+ bool CompareHeaderBlocks(spdy::SpdyHeaderBlock decoded_header_list,
+ spdy::SpdyHeaderBlock expected_header_list);
+
+ bool encoder_stream_error_detected_;
+ test::NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate_;
+ QpackDecoder decoder_;
+ std::list<spdy::SpdyHeaderBlock> decoded_header_lists_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
new file mode 100644
index 00000000000..a4f5373911c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
@@ -0,0 +1,44 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h"
+
+#include <cstddef>
+#include <iostream>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+int main(int argc, char* argv[]) {
+ const char* usage =
+ "Usage: qpack_offline_decoder input_filename expected_headers_filename "
+ "....";
+ std::vector<std::string> args =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+
+ if (args.size() < 2 || args.size() % 2 != 0) {
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ return 1;
+ }
+
+ size_t i;
+ for (i = 0; 2 * i < args.size(); ++i) {
+ const quic::QuicStringPiece input_filename(args[2 * i]);
+ const quic::QuicStringPiece expected_headers_filename(args[2 * i + 1]);
+
+ // Every file represents a different connection,
+ // therefore every file needs a fresh decoding context.
+ quic::QpackOfflineDecoder decoder;
+ if (!decoder.DecodeAndVerifyOfflineData(input_filename,
+ expected_headers_filename)) {
+ return 1;
+ }
+ }
+
+ std::cout << "Successfully verified " << i << " pairs of input files."
+ << std::endl;
+
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc
new file mode 100644
index 00000000000..dd4487d7060
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc
@@ -0,0 +1,200 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Validate that
+// * in each instruction, the bits of |value| that are zero in |mask| are zero;
+// * every byte matches exactly one opcode.
+void ValidateLangague(const QpackLanguage* language) {
+#ifndef NDEBUG
+ for (const auto* instruction : *language) {
+ DCHECK_EQ(0, instruction->opcode.value & ~instruction->opcode.mask);
+ }
+
+ for (uint8_t byte = 0; byte < std::numeric_limits<uint8_t>::max(); ++byte) {
+ size_t match_count = 0;
+ for (const auto* instruction : *language) {
+ if ((byte & instruction->opcode.mask) == instruction->opcode.value) {
+ ++match_count;
+ }
+ }
+ DCHECK_EQ(1u, match_count) << static_cast<int>(byte);
+ }
+#endif
+}
+
+} // namespace
+
+bool operator==(const QpackInstructionOpcode& a,
+ const QpackInstructionOpcode& b) {
+ return std::tie(a.value, a.mask) == std::tie(b.value, b.mask);
+}
+
+const QpackInstruction* InsertWithNameReferenceInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b10000000, 0b10000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kSbit, 0b01000000},
+ {QpackInstructionFieldType::kVarint, 6},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* InsertWithoutNameReferenceInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b01000000, 0b11000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kName, 5},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* DuplicateInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00000000, 0b11100000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 5}}};
+ return instruction;
+}
+
+const QpackInstruction* SetDynamicTableCapacityInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00100000, 0b11100000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 5}}};
+ return instruction;
+}
+
+const QpackLanguage* QpackEncoderStreamLanguage() {
+ static const QpackLanguage* const language = new QpackLanguage{
+ InsertWithNameReferenceInstruction(),
+ InsertWithoutNameReferenceInstruction(), DuplicateInstruction(),
+ SetDynamicTableCapacityInstruction()};
+ ValidateLangague(language);
+ return language;
+}
+
+const QpackInstruction* InsertCountIncrementInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00000000, 0b11000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 6}}};
+ return instruction;
+}
+
+const QpackInstruction* HeaderAcknowledgementInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b10000000, 0b10000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* StreamCancellationInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b01000000, 0b11000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 6}}};
+ return instruction;
+}
+
+const QpackLanguage* QpackDecoderStreamLanguage() {
+ static const QpackLanguage* const language = new QpackLanguage{
+ InsertCountIncrementInstruction(), HeaderAcknowledgementInstruction(),
+ StreamCancellationInstruction()};
+ ValidateLangague(language);
+ return language;
+}
+
+const QpackInstruction* QpackPrefixInstruction() {
+ // This opcode matches every input.
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00000000, 0b00000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kVarint, 8},
+ {QpackInstructionFieldType::kSbit, 0b10000000},
+ {QpackInstructionFieldType::kVarint2, 7}}};
+ return instruction;
+}
+
+const QpackLanguage* QpackPrefixLanguage() {
+ static const QpackLanguage* const language =
+ new QpackLanguage{QpackPrefixInstruction()};
+ ValidateLangague(language);
+ return language;
+}
+
+const QpackInstruction* QpackIndexedHeaderFieldInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b10000000, 0b10000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kSbit, 0b01000000},
+ {QpackInstructionFieldType::kVarint, 6}}};
+ return instruction;
+}
+
+const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00010000, 0b11110000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 4}}};
+ return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b01000000, 0b11000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kSbit, 0b00010000},
+ {QpackInstructionFieldType::kVarint, 4},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00000000, 0b11110000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kVarint, 3},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00100000, 0b11100000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kName, 3},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackLanguage* QpackRequestStreamLanguage() {
+ static const QpackLanguage* const language =
+ new QpackLanguage{QpackIndexedHeaderFieldInstruction(),
+ QpackIndexedHeaderFieldPostBaseInstruction(),
+ QpackLiteralHeaderFieldNameReferenceInstruction(),
+ QpackLiteralHeaderFieldPostBaseInstruction(),
+ QpackLiteralHeaderFieldInstruction()};
+ ValidateLangague(language);
+ return language;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h
new file mode 100644
index 00000000000..2812e63302d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h
@@ -0,0 +1,147 @@
+// 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_CORE_QPACK_QPACK_CONSTANTS_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_
+
+#include <cstdint>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Each instruction is identified with an opcode in the first byte.
+// |mask| determines which bits are part of the opcode.
+// |value| is the value of these bits. (Other bits in value must be zero.)
+struct QUIC_EXPORT_PRIVATE QpackInstructionOpcode {
+ uint8_t value;
+ uint8_t mask;
+};
+
+bool operator==(const QpackInstructionOpcode& a,
+ const QpackInstructionOpcode& b);
+
+// Possible types of an instruction field. Decoding a static bit does not
+// consume the current byte. Decoding an integer or a length-prefixed string
+// literal consumes all bytes containing the field value.
+enum class QpackInstructionFieldType {
+ // A single bit indicating whether the index refers to the static table, or
+ // indicating the sign of Delta Base. Called "S" bit because both "static"
+ // and "sign" start with the letter "S".
+ kSbit,
+ // An integer encoded with variable length encoding. This could be an index,
+ // stream ID, maximum size, or Encoded Required Insert Count.
+ kVarint,
+ // A second integer encoded with variable length encoding. This could be
+ // Delta Base.
+ kVarint2,
+ // A header name or header value encoded as:
+ // a bit indicating whether it is Huffman encoded;
+ // the encoded length of the string;
+ // the header name or value optionally Huffman encoded.
+ kName,
+ kValue
+};
+
+// Each instruction field has a type and a parameter.
+// The meaning of the parameter depends on the field type.
+struct QUIC_EXPORT_PRIVATE QpackInstructionField {
+ QpackInstructionFieldType type;
+ // For a kSbit field, |param| is a mask with exactly one bit set.
+ // For kVarint fields, |param| is the prefix length of the integer encoding.
+ // For kName and kValue fields, |param| is the prefix length of the length of
+ // the string, and the bit immediately preceding the prefix is interpreted as
+ // the Huffman bit.
+ uint8_t param;
+};
+
+using QpackInstructionFields = std::vector<QpackInstructionField>;
+
+// A QPACK instruction consists of an opcode identifying the instruction,
+// followed by a non-empty list of fields. The last field must be integer or
+// string literal type to guarantee that all bytes of the instruction are
+// consumed.
+struct QUIC_EXPORT_PRIVATE QpackInstruction {
+ QpackInstruction(const QpackInstruction&) = delete;
+ const QpackInstruction& operator=(const QpackInstruction&) = delete;
+
+ QpackInstructionOpcode opcode;
+ QpackInstructionFields fields;
+};
+
+// A language is a collection of instructions. The order does not matter.
+// Every possible input must match exactly one instruction.
+using QpackLanguage = std::vector<const QpackInstruction*>;
+
+// TODO(bnc): Move this into HpackVarintEncoder.
+// The integer encoder can encode up to 2^64-1, which can take up to 10 bytes
+// (each carrying 7 bits) after the prefix.
+const uint8_t kMaxExtensionBytesForVarintEncoding = 10;
+
+// Wire format defined in
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5
+
+// 5.2 Encoder stream instructions
+
+// 5.2.1 Insert With Name Reference
+const QpackInstruction* InsertWithNameReferenceInstruction();
+
+// 5.2.2 Insert Without Name Reference
+const QpackInstruction* InsertWithoutNameReferenceInstruction();
+
+// 5.2.3 Duplicate
+const QpackInstruction* DuplicateInstruction();
+
+// 5.2.4 Dynamic Table Size Update
+const QpackInstruction* SetDynamicTableCapacityInstruction();
+
+// Encoder stream language.
+const QpackLanguage* QpackEncoderStreamLanguage();
+
+// 5.3 Decoder stream instructions
+
+// 5.3.1 Insert Count Increment
+const QpackInstruction* InsertCountIncrementInstruction();
+
+// 5.3.2 Header Acknowledgement
+const QpackInstruction* HeaderAcknowledgementInstruction();
+
+// 5.3.3 Stream Cancellation
+const QpackInstruction* StreamCancellationInstruction();
+
+// Decoder stream language.
+const QpackLanguage* QpackDecoderStreamLanguage();
+
+// 5.4.1. Header data prefix instructions
+
+const QpackInstruction* QpackPrefixInstruction();
+
+const QpackLanguage* QpackPrefixLanguage();
+
+// 5.4.2. Request and push stream instructions
+
+// 5.4.2.1. Indexed Header Field
+const QpackInstruction* QpackIndexedHeaderFieldInstruction();
+
+// 5.4.2.2. Indexed Header Field With Post-Base Index
+const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction();
+
+// 5.4.2.3. Literal Header Field With Name Reference
+const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction();
+
+// 5.4.2.4. Literal Header Field With Post-Base Name Reference
+const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction();
+
+// 5.4.2.5. Literal Header Field Without Name Reference
+const QpackInstruction* QpackLiteralHeaderFieldInstruction();
+
+// Request and push stream language.
+const QpackLanguage* QpackRequestStreamLanguage();
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc
new file mode 100644
index 00000000000..bcfe0e4e188
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc
@@ -0,0 +1,72 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h"
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+
+namespace quic {
+
+QpackDecodedHeadersAccumulator::QpackDecodedHeadersAccumulator(
+ QuicStreamId id,
+ QpackDecoder* qpack_decoder,
+ size_t max_header_list_size)
+ : decoder_(qpack_decoder->DecodeHeaderBlock(id, this)),
+ uncompressed_header_bytes_(0),
+ compressed_header_bytes_(0),
+ error_detected_(false) {
+ quic_header_list_.set_max_header_list_size(max_header_list_size);
+ quic_header_list_.OnHeaderBlockStart();
+}
+
+void QpackDecodedHeadersAccumulator::OnHeaderDecoded(QuicStringPiece name,
+ QuicStringPiece value) {
+ DCHECK(!error_detected_);
+
+ uncompressed_header_bytes_ += name.size() + value.size();
+ quic_header_list_.OnHeader(name, value);
+}
+
+void QpackDecodedHeadersAccumulator::OnDecodingCompleted() {}
+
+void QpackDecodedHeadersAccumulator::OnDecodingErrorDetected(
+ QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ // Copy error message to ensure it remains valid for the lifetime of |this|.
+ error_message_.assign(error_message.data(), error_message.size());
+}
+
+bool QpackDecodedHeadersAccumulator::Decode(QuicStringPiece data) {
+ DCHECK(!error_detected_);
+
+ compressed_header_bytes_ += data.size();
+ decoder_->Decode(data);
+
+ return !error_detected_;
+}
+
+bool QpackDecodedHeadersAccumulator::EndHeaderBlock() {
+ DCHECK(!error_detected_);
+
+ decoder_->EndHeaderBlock();
+
+ quic_header_list_.OnHeaderBlockEnd(uncompressed_header_bytes_,
+ compressed_header_bytes_);
+
+ return !error_detected_;
+}
+
+const QuicHeaderList& QpackDecodedHeadersAccumulator::quic_header_list() const {
+ DCHECK(!error_detected_);
+ return quic_header_list_;
+}
+
+QuicStringPiece QpackDecodedHeadersAccumulator::error_message() const {
+ DCHECK(error_detected_);
+ return error_message_;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h
new file mode 100644
index 00000000000..5db88d71b33
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
+
+#include <cstddef>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QpackDecoder;
+
+// A class that creates and owns a QpackProgressiveDecoder instance, accumulates
+// decoded headers in a QuicHeaderList, and keeps track of uncompressed and
+// compressed size so that it can be passed to QuicHeaderList::EndHeaderBlock().
+class QUIC_EXPORT_PRIVATE QpackDecodedHeadersAccumulator
+ : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+ QpackDecodedHeadersAccumulator(QuicStreamId id,
+ QpackDecoder* qpack_decoder,
+ size_t max_header_list_size);
+ virtual ~QpackDecodedHeadersAccumulator() = default;
+
+ // QpackProgressiveDecoder::HeadersHandlerInterface implementation.
+ // These methods should only be called by |decoder_|.
+ void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override;
+ void OnDecodingCompleted() override;
+ void OnDecodingErrorDetected(QuicStringPiece error_message) override;
+
+ // Decode payload data. Returns true on success, false on error.
+ // Must not be called if an error has been detected.
+ // Must not be called after EndHeaderBlock().
+ bool Decode(QuicStringPiece data);
+
+ // Signal end of HEADERS frame. Returns true on success, false on error.
+ // Must not be called if an error has been detected.
+ // Must not be called more that once.
+ bool EndHeaderBlock();
+
+ // Returns accumulated header list.
+ const QuicHeaderList& quic_header_list() const;
+
+ // Returns error message.
+ // Must not be called unless an error has been detected.
+ QuicStringPiece error_message() const;
+
+ private:
+ std::unique_ptr<QpackProgressiveDecoder> decoder_;
+ QuicHeaderList quic_header_list_;
+ size_t uncompressed_header_bytes_;
+ size_t compressed_header_bytes_;
+ bool error_detected_;
+ std::string error_message_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
new file mode 100644
index 00000000000..c3c0b8101b0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
@@ -0,0 +1,119 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h"
+
+#include <cstring>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Pair;
+using ::testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+// Arbitrary stream ID used for testing.
+QuicStreamId kTestStreamId = 1;
+
+// Limit on header list size.
+const size_t kMaxHeaderListSize = 100;
+
+// Header Acknowledgement decoder stream instruction with stream_id = 1.
+const char* const kHeaderAcknowledgement = "\x81";
+
+} // anonymous namespace
+
+class QpackDecodedHeadersAccumulatorTest : public QuicTest {
+ protected:
+ QpackDecodedHeadersAccumulatorTest()
+ : qpack_decoder_(&encoder_stream_error_delegate_,
+ &decoder_stream_sender_delegate_),
+ accumulator_(kTestStreamId, &qpack_decoder_, kMaxHeaderListSize) {}
+
+ NoopEncoderStreamErrorDelegate encoder_stream_error_delegate_;
+ StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_;
+ QpackDecoder qpack_decoder_;
+ QpackDecodedHeadersAccumulator accumulator_;
+};
+
+// HEADERS frame payload must have a complete Header Block Prefix.
+TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyPayload) {
+ EXPECT_FALSE(accumulator_.EndHeaderBlock());
+ EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message());
+}
+
+// HEADERS frame payload must have a complete Header Block Prefix.
+TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedHeaderBlockPrefix) {
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00")));
+ EXPECT_FALSE(accumulator_.EndHeaderBlock());
+ EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message());
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyHeaderList) {
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("0000")));
+ EXPECT_TRUE(accumulator_.EndHeaderBlock());
+
+ EXPECT_TRUE(accumulator_.quic_header_list().empty());
+}
+
+// This payload is the prefix of a valid payload, but EndHeaderBlock() is called
+// before it can be completely decoded.
+TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedPayload) {
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00002366")));
+ EXPECT_FALSE(accumulator_.EndHeaderBlock());
+ EXPECT_EQ("Incomplete header block.", accumulator_.error_message());
+}
+
+// This payload is invalid because it refers to a non-existing static entry.
+TEST_F(QpackDecodedHeadersAccumulatorTest, InvalidPayload) {
+ EXPECT_FALSE(accumulator_.Decode(QuicTextUtils::HexDecode("0000ff23ff24")));
+ EXPECT_EQ("Static table entry not found.", accumulator_.error_message());
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest, Success) {
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ std::string encoded_data(QuicTextUtils::HexDecode("000023666f6f03626172"));
+ EXPECT_TRUE(accumulator_.Decode(encoded_data));
+ EXPECT_TRUE(accumulator_.EndHeaderBlock());
+
+ const QuicHeaderList& header_list = accumulator_.quic_header_list();
+ EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar")));
+
+ EXPECT_EQ(strlen("foo") + strlen("bar"),
+ header_list.uncompressed_header_bytes());
+ EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes());
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedingLimit) {
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ // Total length of header list exceeds kMaxHeaderListSize.
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode(
+ "0000" // header block prefix
+ "26666f6f626172" // header key: "foobar"
+ "7d61616161616161616161616161616161616161" // header value: 'a' 125 times
+ "616161616161616161616161616161616161616161616161616161616161616161616161"
+ "616161616161616161616161616161616161616161616161616161616161616161616161"
+ "61616161616161616161616161616161616161616161616161616161616161616161")));
+ EXPECT_TRUE(accumulator_.EndHeaderBlock());
+
+ // QuicHeaderList signals header list over limit by clearing it.
+ EXPECT_TRUE(accumulator_.quic_header_list().empty());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc
new file mode 100644
index 00000000000..9efccb6c029
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc
@@ -0,0 +1,141 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+QpackDecoder::QpackDecoder(
+ EncoderStreamErrorDelegate* encoder_stream_error_delegate,
+ QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate)
+ : encoder_stream_error_delegate_(encoder_stream_error_delegate),
+ encoder_stream_receiver_(this),
+ decoder_stream_sender_(decoder_stream_sender_delegate) {
+ DCHECK(encoder_stream_error_delegate_);
+ DCHECK(decoder_stream_sender_delegate);
+}
+
+QpackDecoder::~QpackDecoder() {}
+
+void QpackDecoder::SetMaximumDynamicTableCapacity(
+ uint64_t maximum_dynamic_table_capacity) {
+ header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity);
+}
+
+void QpackDecoder::OnStreamReset(QuicStreamId stream_id) {
+ decoder_stream_sender_.SendStreamCancellation(stream_id);
+}
+
+void QpackDecoder::DecodeEncoderStreamData(QuicStringPiece data) {
+ encoder_stream_receiver_.Decode(data);
+}
+
+void QpackDecoder::OnInsertWithNameReference(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value) {
+ if (is_static) {
+ auto entry = header_table_.LookupEntry(/* is_static = */ true, name_index);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Invalid static table entry.");
+ return;
+ }
+
+ entry = header_table_.InsertEntry(entry->name(), value);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error inserting entry with name reference.");
+ }
+ return;
+ }
+
+ uint64_t absolute_index;
+ if (!EncoderStreamRelativeIndexToAbsoluteIndex(name_index, &absolute_index)) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Invalid relative index.");
+ return;
+ }
+
+ const QpackEntry* entry =
+ header_table_.LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Dynamic table entry not found.");
+ return;
+ }
+ entry = header_table_.InsertEntry(entry->name(), value);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error inserting entry with name reference.");
+ }
+}
+
+void QpackDecoder::OnInsertWithoutNameReference(QuicStringPiece name,
+ QuicStringPiece value) {
+ const QpackEntry* entry = header_table_.InsertEntry(name, value);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error inserting literal entry.");
+ }
+}
+
+void QpackDecoder::OnDuplicate(uint64_t index) {
+ uint64_t absolute_index;
+ if (!EncoderStreamRelativeIndexToAbsoluteIndex(index, &absolute_index)) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Invalid relative index.");
+ return;
+ }
+
+ const QpackEntry* entry =
+ header_table_.LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Dynamic table entry not found.");
+ return;
+ }
+ entry = header_table_.InsertEntry(entry->name(), entry->value());
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error inserting duplicate entry.");
+ }
+}
+
+void QpackDecoder::OnSetDynamicTableCapacity(uint64_t capacity) {
+ if (!header_table_.SetDynamicTableCapacity(capacity)) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error updating dynamic table capacity.");
+ }
+}
+
+void QpackDecoder::OnErrorDetected(QuicStringPiece error_message) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(error_message);
+}
+
+bool QpackDecoder::EncoderStreamRelativeIndexToAbsoluteIndex(
+ uint64_t relative_index,
+ uint64_t* absolute_index) const {
+ if (relative_index == std::numeric_limits<uint64_t>::max() ||
+ relative_index + 1 > std::numeric_limits<uint64_t>::max() -
+ header_table_.inserted_entry_count()) {
+ return false;
+ }
+
+ *absolute_index = header_table_.inserted_entry_count() - relative_index - 1;
+ return true;
+}
+
+std::unique_ptr<QpackProgressiveDecoder> QpackDecoder::DecodeHeaderBlock(
+ QuicStreamId stream_id,
+ QpackProgressiveDecoder::HeadersHandlerInterface* handler) {
+ return QuicMakeUnique<QpackProgressiveDecoder>(
+ stream_id, &header_table_, &decoder_stream_sender_, handler);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h
new file mode 100644
index 00000000000..c3b28a3e10b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h
@@ -0,0 +1,102 @@
+// 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_CORE_QPACK_QPACK_DECODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// QPACK decoder class. Exactly one instance should exist per QUIC connection.
+// This class vends a new QpackProgressiveDecoder instance for each new header
+// list to be encoded.
+class QUIC_EXPORT_PRIVATE QpackDecoder
+ : public QpackEncoderStreamReceiver::Delegate {
+ public:
+ // Interface for receiving notification that an error has occurred on the
+ // encoder stream. This MUST be treated as a connection error of type
+ // HTTP_QPACK_ENCODER_STREAM_ERROR.
+ class QUIC_EXPORT_PRIVATE EncoderStreamErrorDelegate {
+ public:
+ virtual ~EncoderStreamErrorDelegate() {}
+
+ virtual void OnEncoderStreamError(QuicStringPiece error_message) = 0;
+ };
+
+ QpackDecoder(
+ EncoderStreamErrorDelegate* encoder_stream_error_delegate,
+ QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate);
+ ~QpackDecoder() override;
+
+ // Set maximum capacity of dynamic table.
+ // This method must only be called at most once.
+ void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity);
+
+ // Signal to the peer's encoder that a stream is reset. This lets the peer's
+ // encoder know that no more header blocks will be processed on this stream,
+ // therefore references to dynamic table entries shall not prevent their
+ // eviction.
+ // This method should be called regardless of whether a header block is being
+ // decoded on that stream, because a header block might be in flight from the
+ // peer.
+ // This method should be called every time a request or push stream is reset
+ // for any reason: for example, client cancels request, or a decoding error
+ // occurs and HeadersHandlerInterface::OnDecodingErrorDetected() is called.
+ // This method should also be called if the stream is reset by the peer,
+ // because the peer's encoder can only evict entries referenced by header
+ // blocks once it receives acknowledgement from this endpoint that the stream
+ // is reset.
+ // However, this method should not be called if the stream is closed normally
+ // using the FIN bit.
+ void OnStreamReset(QuicStreamId stream_id);
+
+ // Factory method to create a QpackProgressiveDecoder for decoding a header
+ // block. |handler| must remain valid until the returned
+ // QpackProgressiveDecoder instance is destroyed or the decoder calls
+ // |handler->OnHeaderBlockEnd()|.
+ std::unique_ptr<QpackProgressiveDecoder> DecodeHeaderBlock(
+ QuicStreamId stream_id,
+ QpackProgressiveDecoder::HeadersHandlerInterface* handler);
+
+ // Decode data received on the encoder stream.
+ void DecodeEncoderStreamData(QuicStringPiece data);
+
+ // QpackEncoderStreamReceiver::Delegate implementation
+ void OnInsertWithNameReference(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value) override;
+ void OnInsertWithoutNameReference(QuicStringPiece name,
+ QuicStringPiece value) override;
+ void OnDuplicate(uint64_t index) override;
+ void OnSetDynamicTableCapacity(uint64_t capacity) override;
+ void OnErrorDetected(QuicStringPiece error_message) override;
+
+ private:
+ // The encoder stream uses relative index (but different from the kind of
+ // relative index used on a request stream). This method converts relative
+ // index to absolute index (zero based). It returns true on success, or false
+ // if conversion fails due to overflow/underflow.
+ bool EncoderStreamRelativeIndexToAbsoluteIndex(
+ uint64_t relative_index,
+ uint64_t* absolute_index) const;
+
+ EncoderStreamErrorDelegate* const encoder_stream_error_delegate_;
+ QpackEncoderStreamReceiver encoder_stream_receiver_;
+ QpackDecoderStreamSender decoder_stream_sender_;
+ QpackHeaderTable header_table_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc
new file mode 100644
index 00000000000..559ce433376
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc
@@ -0,0 +1,52 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h"
+
+#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
+#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+
+namespace quic {
+
+QpackDecoderStreamReceiver::QpackDecoderStreamReceiver(Delegate* delegate)
+ : instruction_decoder_(QpackDecoderStreamLanguage(), this),
+ delegate_(delegate),
+ error_detected_(false) {
+ DCHECK(delegate_);
+}
+
+void QpackDecoderStreamReceiver::Decode(QuicStringPiece data) {
+ if (data.empty() || error_detected_) {
+ return;
+ }
+
+ instruction_decoder_.Decode(data);
+}
+
+bool QpackDecoderStreamReceiver::OnInstructionDecoded(
+ const QpackInstruction* instruction) {
+ if (instruction == InsertCountIncrementInstruction()) {
+ delegate_->OnInsertCountIncrement(instruction_decoder_.varint());
+ return true;
+ }
+
+ if (instruction == HeaderAcknowledgementInstruction()) {
+ delegate_->OnHeaderAcknowledgement(instruction_decoder_.varint());
+ return true;
+ }
+
+ DCHECK_EQ(instruction, StreamCancellationInstruction());
+ delegate_->OnStreamCancellation(instruction_decoder_.varint());
+ return true;
+}
+
+void QpackDecoderStreamReceiver::OnError(QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ delegate_->OnErrorDetected(error_message);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h
new file mode 100644
index 00000000000..61c2773a62e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h
@@ -0,0 +1,63 @@
+// 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_CORE_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class decodes data received on the decoder stream,
+// and passes it along to its Delegate.
+class QUIC_EXPORT_PRIVATE QpackDecoderStreamReceiver
+ : public QpackInstructionDecoder::Delegate {
+ public:
+ // An interface for handling instructions decoded from the decoder stream, see
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.3
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // 5.3.1 Insert Count Increment
+ virtual void OnInsertCountIncrement(uint64_t increment) = 0;
+ // 5.3.2 Header Acknowledgement
+ virtual void OnHeaderAcknowledgement(QuicStreamId stream_id) = 0;
+ // 5.3.3 Stream Cancellation
+ virtual void OnStreamCancellation(QuicStreamId stream_id) = 0;
+ // Decoding error
+ virtual void OnErrorDetected(QuicStringPiece error_message) = 0;
+ };
+
+ explicit QpackDecoderStreamReceiver(Delegate* delegate);
+ QpackDecoderStreamReceiver() = delete;
+ QpackDecoderStreamReceiver(const QpackDecoderStreamReceiver&) = delete;
+ QpackDecoderStreamReceiver& operator=(const QpackDecoderStreamReceiver&) =
+ delete;
+
+ // Decode data and call appropriate Delegate method after each decoded
+ // instruction. Once an error occurs, Delegate::OnErrorDetected() is called,
+ // and all further data is ignored.
+ void Decode(QuicStringPiece data);
+
+ // QpackInstructionDecoder::Delegate implementation.
+ bool OnInstructionDecoded(const QpackInstruction* instruction) override;
+ void OnError(QuicStringPiece error_message) override;
+
+ private:
+ QpackInstructionDecoder instruction_decoder_;
+ Delegate* const delegate_;
+
+ // True if a decoding error has been detected.
+ bool error_detected_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
new file mode 100644
index 00000000000..fc7225f8139
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
@@ -0,0 +1,91 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using testing::Eq;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockDelegate : public QpackDecoderStreamReceiver::Delegate {
+ public:
+ ~MockDelegate() override = default;
+
+ MOCK_METHOD1(OnInsertCountIncrement, void(uint64_t increment));
+ MOCK_METHOD1(OnHeaderAcknowledgement, void(QuicStreamId stream_id));
+ MOCK_METHOD1(OnStreamCancellation, void(QuicStreamId stream_id));
+ MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message));
+};
+
+class QpackDecoderStreamReceiverTest : public QuicTest {
+ protected:
+ QpackDecoderStreamReceiverTest() : stream_(&delegate_) {}
+ ~QpackDecoderStreamReceiverTest() override = default;
+
+ QpackDecoderStreamReceiver stream_;
+ StrictMock<MockDelegate> delegate_;
+};
+
+TEST_F(QpackDecoderStreamReceiverTest, InsertCountIncrement) {
+ EXPECT_CALL(delegate_, OnInsertCountIncrement(0));
+ stream_.Decode(QuicTextUtils::HexDecode("00"));
+
+ EXPECT_CALL(delegate_, OnInsertCountIncrement(10));
+ stream_.Decode(QuicTextUtils::HexDecode("0a"));
+
+ EXPECT_CALL(delegate_, OnInsertCountIncrement(63));
+ stream_.Decode(QuicTextUtils::HexDecode("3f00"));
+
+ EXPECT_CALL(delegate_, OnInsertCountIncrement(200));
+ stream_.Decode(QuicTextUtils::HexDecode("3f8901"));
+
+ EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+ stream_.Decode(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
+}
+
+TEST_F(QpackDecoderStreamReceiverTest, HeaderAcknowledgement) {
+ EXPECT_CALL(delegate_, OnHeaderAcknowledgement(0));
+ stream_.Decode(QuicTextUtils::HexDecode("80"));
+
+ EXPECT_CALL(delegate_, OnHeaderAcknowledgement(37));
+ stream_.Decode(QuicTextUtils::HexDecode("a5"));
+
+ EXPECT_CALL(delegate_, OnHeaderAcknowledgement(127));
+ stream_.Decode(QuicTextUtils::HexDecode("ff00"));
+
+ EXPECT_CALL(delegate_, OnHeaderAcknowledgement(503));
+ stream_.Decode(QuicTextUtils::HexDecode("fff802"));
+
+ EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+ stream_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff"));
+}
+
+TEST_F(QpackDecoderStreamReceiverTest, StreamCancellation) {
+ EXPECT_CALL(delegate_, OnStreamCancellation(0));
+ stream_.Decode(QuicTextUtils::HexDecode("40"));
+
+ EXPECT_CALL(delegate_, OnStreamCancellation(19));
+ stream_.Decode(QuicTextUtils::HexDecode("53"));
+
+ EXPECT_CALL(delegate_, OnStreamCancellation(63));
+ stream_.Decode(QuicTextUtils::HexDecode("7f00"));
+
+ EXPECT_CALL(delegate_, OnStreamCancellation(110));
+ stream_.Decode(QuicTextUtils::HexDecode("7f2f"));
+
+ EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+ stream_.Decode(QuicTextUtils::HexDecode("7fffffffffffffffffffff"));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc
new file mode 100644
index 00000000000..9474d6cdf93
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc
@@ -0,0 +1,61 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+
+#include <cstddef>
+#include <limits>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QpackDecoderStreamSender::QpackDecoderStreamSender(Delegate* delegate)
+ : delegate_(delegate) {
+ DCHECK(delegate_);
+}
+
+void QpackDecoderStreamSender::SendInsertCountIncrement(uint64_t increment) {
+ instruction_encoder_.set_varint(increment);
+
+ instruction_encoder_.Encode(InsertCountIncrementInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteDecoderStreamData(output);
+}
+
+void QpackDecoderStreamSender::SendHeaderAcknowledgement(
+ QuicStreamId stream_id) {
+ instruction_encoder_.set_varint(stream_id);
+
+ instruction_encoder_.Encode(HeaderAcknowledgementInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteDecoderStreamData(output);
+}
+
+void QpackDecoderStreamSender::SendStreamCancellation(QuicStreamId stream_id) {
+ instruction_encoder_.set_varint(stream_id);
+
+ instruction_encoder_.Encode(StreamCancellationInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteDecoderStreamData(output);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h
new file mode 100644
index 00000000000..a791173f801
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h
@@ -0,0 +1,55 @@
+// 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_CORE_QPACK_QPACK_DECODER_STREAM_SENDER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_SENDER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class serializes (encodes) instructions for transmission on the decoder
+// stream.
+class QUIC_EXPORT_PRIVATE QpackDecoderStreamSender {
+ public:
+ // An interface for handling encoded data.
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Encoded |data| is ready to be written on the decoder stream.
+ // WriteDecoderStreamData() is called exactly once for each instruction.
+ // |data| contains the entire encoded instruction and it is guaranteed to be
+ // not empty.
+ virtual void WriteDecoderStreamData(QuicStringPiece data) = 0;
+ };
+
+ explicit QpackDecoderStreamSender(Delegate* delegate);
+ QpackDecoderStreamSender() = delete;
+ QpackDecoderStreamSender(const QpackDecoderStreamSender&) = delete;
+ QpackDecoderStreamSender& operator=(const QpackDecoderStreamSender&) = delete;
+
+ // Methods for sending instructions, see
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.3
+
+ // 5.3.1 Insert Count Increment
+ void SendInsertCountIncrement(uint64_t increment);
+ // 5.3.2 Header Acknowledgement
+ void SendHeaderAcknowledgement(QuicStreamId stream_id);
+ // 5.3.3 Stream Cancellation
+ void SendStreamCancellation(QuicStreamId stream_id);
+
+ private:
+ Delegate* const delegate_;
+ QpackInstructionEncoder instruction_encoder_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_SENDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc
new file mode 100644
index 00000000000..cc957591083
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc
@@ -0,0 +1,91 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Eq;
+using ::testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockSenderDelegate : public QpackDecoderStreamSender::Delegate {
+ public:
+ ~MockSenderDelegate() override = default;
+
+ MOCK_METHOD1(WriteDecoderStreamData, void(QuicStringPiece data));
+};
+
+class QpackDecoderStreamSenderTest : public QuicTest {
+ protected:
+ QpackDecoderStreamSenderTest() : stream_(&delegate_) {}
+ ~QpackDecoderStreamSenderTest() override = default;
+
+ StrictMock<MockSenderDelegate> delegate_;
+ QpackDecoderStreamSender stream_;
+};
+
+TEST_F(QpackDecoderStreamSenderTest, InsertCountIncrement) {
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("00"))));
+ stream_.SendInsertCountIncrement(0);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("0a"))));
+ stream_.SendInsertCountIncrement(10);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("3f00"))));
+ stream_.SendInsertCountIncrement(63);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("3f8901"))));
+ stream_.SendInsertCountIncrement(200);
+}
+
+TEST_F(QpackDecoderStreamSenderTest, HeaderAcknowledgement) {
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("80"))));
+ stream_.SendHeaderAcknowledgement(0);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("a5"))));
+ stream_.SendHeaderAcknowledgement(37);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("ff00"))));
+ stream_.SendHeaderAcknowledgement(127);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("fff802"))));
+ stream_.SendHeaderAcknowledgement(503);
+}
+
+TEST_F(QpackDecoderStreamSenderTest, StreamCancellation) {
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("40"))));
+ stream_.SendStreamCancellation(0);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("53"))));
+ stream_.SendStreamCancellation(19);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("7f00"))));
+ stream_.SendStreamCancellation(63);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("7f2f"))));
+ stream_.SendStreamCancellation(110);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc
new file mode 100644
index 00000000000..8d83f0a8bc0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc
@@ -0,0 +1,703 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+
+#include <algorithm>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+using ::testing::Eq;
+using ::testing::Sequence;
+using ::testing::StrictMock;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+// Header Acknowledgement decoder stream instruction with stream_id = 1.
+const char* const kHeaderAcknowledgement = "\x81";
+
+class QpackDecoderTest : public QuicTestWithParam<FragmentMode> {
+ protected:
+ QpackDecoderTest()
+ : qpack_decoder_(&encoder_stream_error_delegate_,
+ &decoder_stream_sender_delegate_),
+ fragment_mode_(GetParam()) {
+ qpack_decoder_.SetMaximumDynamicTableCapacity(1024);
+ }
+
+ ~QpackDecoderTest() override = default;
+
+ void DecodeEncoderStreamData(QuicStringPiece data) {
+ qpack_decoder_.DecodeEncoderStreamData(data);
+ }
+
+ void DecodeHeaderBlock(QuicStringPiece data) {
+ auto fragment_size_generator =
+ FragmentModeToFragmentSizeGenerator(fragment_mode_);
+ auto progressive_decoder =
+ qpack_decoder_.DecodeHeaderBlock(/* stream_id = */ 1, &handler_);
+ while (!data.empty()) {
+ size_t fragment_size = std::min(fragment_size_generator(), data.size());
+ progressive_decoder->Decode(data.substr(0, fragment_size));
+ data = data.substr(fragment_size);
+ }
+ progressive_decoder->EndHeaderBlock();
+ }
+
+ StrictMock<MockEncoderStreamErrorDelegate> encoder_stream_error_delegate_;
+ StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_;
+ StrictMock<MockHeadersHandler> handler_;
+
+ private:
+ QpackDecoder qpack_decoder_;
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ QpackDecoderTest,
+ Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackDecoderTest, NoPrefix) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Incomplete header data prefix.")));
+
+ // Header Data Prefix is at least two bytes long.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00"));
+}
+
+TEST_P(QpackDecoderTest, EmptyHeaderBlock) {
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0000"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyName) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("foo")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002003666f6f"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f00"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002000"));
+}
+
+TEST_P(QpackDecoderTest, SimpleLiteralEntry) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f03626172"));
+}
+
+TEST_P(QpackDecoderTest, MultipleLiteralEntries) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ std::string str(127, 'a');
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foobaar"), QuicStringPiece(str)));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000" // prefix
+ "23666f6f03626172" // foo: bar
+ "2700666f6f62616172" // 7 octet long header name, the smallest number
+ // that does not fit on a 3-bit prefix.
+ "7f0061616161616161" // 127 octet long header value, the smallest number
+ "616161616161616161" // that does not fit on a 7-bit prefix.
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "616161616161"));
+}
+
+// Name Length value is too large for varint decoder to decode.
+TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Encoded integer too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffffffffffffffffffff"));
+}
+
+// Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_P(QpackDecoderTest, NameLenExceedsLimit) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("String literal too long.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffff7f"));
+}
+
+// Value Length value is too large for varint decoder to decode.
+TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Encoded integer too large.")));
+
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("000023666f6f7fffffffffffffffffffff"));
+}
+
+// Value Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_P(QpackDecoderTest, ValueLenExceedsLimit) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("String literal too long.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f7fffff7f"));
+}
+
+TEST_P(QpackDecoderTest, IncompleteHeaderBlock) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Incomplete header block.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002366"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanSimple) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value")))
+ .Times(4);
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000" // Prefix.
+ "2f0125a849e95ba97d7f" // Huffman-encoded name.
+ "8925a849e95bb8e8b4bf" // Huffman-encoded value.
+ "2703637573746f6d2d6b6579" // Non-Huffman encoded name.
+ "0c637573746f6d2d76616c7565" // Non-Huffman encoded value.
+ "2f0125a849e95ba97d7f" // Huffman-encoded name.
+ "0c637573746f6d2d76616c7565" // Non-Huffman encoded value.
+ "2703637573746f6d2d6b6579" // Non-Huffman encoded name.
+ "8925a849e95bb8e8b4bf")); // Huffman-encoded value.
+}
+
+TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // 'y' ends in 0b0 on the most significant bit of the last byte.
+ // The remaining 7 bits must be a prefix of EOS, which is all 1s.
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7e8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // 'e' ends in 0b101, taking up the 3 most significant bits of the last byte.
+ // The remaining 5 bits must be a prefix of EOS, which is all 1s.
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4be"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // The trailing EOS prefix must be at most 7 bits long. Appending one octet
+ // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a
+ // prefix of EOS.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "00002f0225a849e95ba97d7fff8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // The trailing EOS prefix must be at most 7 bits long. Appending one octet
+ // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a
+ // prefix of EOS.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "00002f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff"));
+}
+
+TEST_P(QpackDecoderTest, StaticTable) {
+ // A header name that has multiple entries with different values.
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("POST")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("TRACE")));
+
+ // A header name that has a single entry with non-empty value.
+ EXPECT_CALL(handler_,
+ OnHeaderDecoded(Eq("accept-encoding"), Eq("gzip, deflate, br")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("compress")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("")));
+
+ // A header name that has a single entry with empty value.
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("foo")));
+
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000d1dfccd45f108621e9aec2a11f5c8294e75f000554524143455f1000"));
+}
+
+TEST_P(QpackDecoderTest, TooHighStaticTableIndex) {
+ // This is the last entry in the static table with index 98.
+ EXPECT_CALL(handler_,
+ OnHeaderDecoded(Eq("x-frame-options"), Eq("sameorigin")));
+
+ // Addressing entry 99 should trigger an error.
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Static table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0000ff23ff24"));
+}
+
+TEST_P(QpackDecoderTest, DynamicTable) {
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "80035a5a5a" // Add entry with name of dynamic table entry index 0
+ // (relative index) and value "ZZZ".
+ "cf8294e7" // Add entry with name of static table entry index 15
+ // and value "foo".
+ "01")); // Duplicate entry with relative index 1.
+
+ // Now there are four entries in the dynamic table.
+ // Entry 0: "foo", "bar"
+ // Entry 1: "foo", "ZZZ"
+ // Entry 2: ":method", "foo"
+ // Entry 3: "foo", "ZZZ"
+
+ // Use a Sequence to test that mock methods are called in order.
+ Sequence s;
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "83" // Dynamic table entry with relative index 3, absolute index 0.
+ "82" // Dynamic table entry with relative index 2, absolute index 1.
+ "81" // Dynamic table entry with relative index 1, absolute index 2.
+ "80" // Dynamic table entry with relative index 0, absolute index 3.
+ "41025a5a")); // Name of entry 1 (relative index) from dynamic table,
+ // with value "ZZ".
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0502" // Required Insert Count 4 and Delta Base 2.
+ // Base is 4 + 2 = 6.
+ "85" // Dynamic table entry with relative index 5, absolute index 0.
+ "84" // Dynamic table entry with relative index 4, absolute index 1.
+ "83" // Dynamic table entry with relative index 3, absolute index 2.
+ "82" // Dynamic table entry with relative index 2, absolute index 3.
+ "43025a5a")); // Name of entry 3 (relative index) from dynamic table,
+ // with value "ZZ".
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0582" // Required Insert Count 4 and Delta Base 2 with sign bit set.
+ // Base is 4 - 2 - 1 = 1.
+ "80" // Dynamic table entry with relative index 0, absolute index 0.
+ "10" // Dynamic table entry with post-base index 0, absolute index 1.
+ "11" // Dynamic table entry with post-base index 1, absolute index 2.
+ "12" // Dynamic table entry with post-base index 2, absolute index 3.
+ "01025a5a")); // Name of entry 1 (post-base index) from dynamic table,
+ // with value "ZZ".
+}
+
+TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "80")); // Dynamic table entry with relative index 0, absolute index 0.
+
+ // Change dynamic table capacity to 32 bytes, smaller than the entry.
+ // This must cause the entry to be evicted.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f01"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "80")); // Dynamic table entry with relative index 0, absolute index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorEntryTooLarge) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Error inserting literal entry.")));
+
+ // Set dynamic table capacity to 34.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f03"));
+ // Add literal entry with name "foo" and value "bar", size is 32 + 3 + 3 = 38.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidStaticTableEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Invalid static table entry.")));
+
+ // Address invalid static table entry index 99.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("ff2400"));
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Dynamic table entry not found.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "8100")); // Address dynamic table entry with relative index 1. Such
+ // entry does not exist. The most recently added and only
+ // dynamic table entry has relative index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorDuplicateInvalidEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Dynamic table entry not found.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "01")); // Duplicate dynamic table entry with relative index 1. Such
+ // entry does not exist. The most recently added and only
+ // dynamic table entry has relative index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Encoded integer too large.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0280" // Required Insert Count is 1. Base 1 - 1 - 0 = 0 is explicitly
+ // permitted by the spec.
+ "80")); // However, addressing entry with relative index 0 would point to
+ // absolute index -1, which is invalid.
+}
+
+TEST_P(QpackDecoderTest, InvalidNegativeBase) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Error calculating Base.")));
+
+ // Required Insert Count 1, Delta Base 1 with sign bit set, Base would
+ // be 1 - 1 - 1 = -1, but it is not allowed to be negative.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0281"));
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "82")); // Indexed Header Field instruction addressing relative index 2.
+ // This is absolute index 1. Such entry does not exist.
+
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "84")); // Indexed Header Field instruction addressing relative index 4.
+ // This is absolute index -1, which is invalid.
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "4200")); // Literal Header Field with Name Reference instruction
+ // addressing relative index 2. This is absolute index 1. Such
+ // entry does not exist.
+
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "4400")); // Literal Header Field with Name Reference instruction
+ // addressing relative index 4. This is absolute index -1,
+ // which is invalid.
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryByPostBaseIndex) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set.
+ // Base is 2 - 0 - 1 = 1
+ "10")); // Indexed Header Field instruction addressing dynamic table
+ // entry with post-base index 0, absolute index 1. Such entry
+ // does not exist.
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set.
+ // Base is 2 - 0 - 1 = 1
+ "0000")); // Literal Header Field With Name Reference instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. Such entry does not exist.
+}
+
+TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) {
+ EXPECT_CALL(
+ encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Error updating dynamic table capacity.")));
+
+ // Try to update dynamic table capacity to 2048, which exceeds the maximum.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe10f"));
+}
+
+TEST_P(QpackDecoderTest, SetMaximumDynamicTableCapacity) {
+ // Update dynamic table capacity to 128, which does not exceed the maximum.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61"));
+}
+
+TEST_P(QpackDecoderTest, InvalidEncodedRequiredInsertCount) {
+ // Maximum dynamic table capacity is 1024.
+ // MaxEntries is 1024 / 32 = 32.
+ // Required Insert Count is decoded modulo 2 * MaxEntries, that is, modulo 64.
+ // A value of 1 cannot be encoded as 65 even though it has the same remainder.
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(
+ Eq("Error decoding Required Insert Count.")));
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("4100"));
+}
+
+TEST_P(QpackDecoderTest, WrappedRequiredInsertCount) {
+ // Maximum dynamic table capacity is 1024.
+ // MaxEntries is 1024 / 32 = 32.
+
+ // Add literal entry with name "foo" and a 600 byte long value. This will fit
+ // in the dynamic table once but not twice.
+ DecodeEncoderStreamData(
+ QuicTextUtils::HexDecode("6294e7" // Name "foo".
+ "7fd903")); // Value length 600.
+ std::string header_value(600, 'Z');
+ DecodeEncoderStreamData(header_value);
+
+ // Duplicate most recent entry 200 times.
+ DecodeEncoderStreamData(std::string(200, '\x00'));
+
+ // Now there is only one entry in the dynamic table, with absolute index 200.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(header_value)));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ // Send header block with Required Insert Count = 201.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0a00" // Encoded Required Insert Count 10, Required Insert Count 201,
+ // Delta Base 0, Base 201.
+ "80")); // Emit dynamic table entry with relative index 0.
+}
+
+TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count is 1.
+ "d1")); // But the only instruction references the static table.
+}
+
+TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) {
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0201" // Required Insert Count 1 and Delta Base 1.
+ // Base is 1 + 1 = 2.
+ "80")); // Indexed Header Field instruction addressing dynamic table
+ // entry with relative index 0, absolute index 1. This is not
+ // allowed by Required Insert Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0201" // Required Insert Count 1 and Delta Base 1.
+ // Base is 1 + 1 = 2.
+ "4000")); // Literal Header Field with Name Reference instruction
+ // addressing dynamic table entry with relative index 0,
+ // absolute index 1. This is not allowed by Required Index
+ // Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "10")); // Indexed Header Field with Post-Base Index instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. This is not allowed by Required Insert
+ // Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "0000")); // Literal Header Field with Post-Base Name Reference
+ // instruction addressing dynamic table entry with post-base
+ // index 0, absolute index 1. This is not allowed by Required
+ // Index Count.
+}
+
+TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+ // Duplicate entry.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("00"));
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0300" // Required Insert Count 2 and Delta Base 0.
+ // Base is 2 + 0 = 2.
+ "81")); // Indexed Header Field instruction addressing dynamic table
+ // entry with relative index 1, absolute index 0. Header block
+ // requires insert count of 1, even though Required Insert Count
+ // is 2.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0300" // Required Insert Count 2 and Delta Base 0.
+ // Base is 2 + 0 = 2.
+ "4100")); // Literal Header Field with Name Reference instruction
+ // addressing dynamic table entry with relative index 1,
+ // absolute index 0. Header block requires insert count of 1,
+ // even though Required Insert Count is 2.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set.
+ // Base is 3 - 1 - 1 = 1.
+ "10")); // Indexed Header Field with Post-Base Index instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. Header block requires insert count of 2,
+ // even though Required Insert Count is 3.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set.
+ // Base is 3 - 1 - 1 = 1.
+ "0000")); // Literal Header Field with Post-Base Name Reference
+ // instruction addressing dynamic table entry with post-base
+ // index 0, absolute index 1. Header block requires insert
+ // count of 2, even though Required Insert Count is 3.
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc
new file mode 100644
index 00000000000..e8bbd178e62
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc
@@ -0,0 +1,82 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <utility>
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace quic {
+namespace test {
+
+void NoopEncoderStreamErrorDelegate::OnEncoderStreamError(
+ QuicStringPiece error_message) {}
+
+void NoopDecoderStreamSenderDelegate::WriteDecoderStreamData(
+ QuicStringPiece data) {}
+
+TestHeadersHandler::TestHeadersHandler()
+ : decoding_completed_(false), decoding_error_detected_(false) {}
+
+void TestHeadersHandler::OnHeaderDecoded(QuicStringPiece name,
+ QuicStringPiece value) {
+ ASSERT_FALSE(decoding_completed_);
+ ASSERT_FALSE(decoding_error_detected_);
+
+ header_list_.AppendValueOrAddHeader(name, value);
+}
+
+void TestHeadersHandler::OnDecodingCompleted() {
+ ASSERT_FALSE(decoding_completed_);
+ ASSERT_FALSE(decoding_error_detected_);
+
+ decoding_completed_ = true;
+}
+
+void TestHeadersHandler::OnDecodingErrorDetected(
+ QuicStringPiece error_message) {
+ ASSERT_FALSE(decoding_completed_);
+ ASSERT_FALSE(decoding_error_detected_);
+
+ decoding_error_detected_ = true;
+}
+
+spdy::SpdyHeaderBlock TestHeadersHandler::ReleaseHeaderList() {
+ DCHECK(decoding_completed_);
+ DCHECK(!decoding_error_detected_);
+
+ return std::move(header_list_);
+}
+
+bool TestHeadersHandler::decoding_completed() const {
+ return decoding_completed_;
+}
+
+bool TestHeadersHandler::decoding_error_detected() const {
+ return decoding_error_detected_;
+}
+
+void QpackDecode(
+ QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate,
+ QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate,
+ QpackProgressiveDecoder::HeadersHandlerInterface* handler,
+ const FragmentSizeGenerator& fragment_size_generator,
+ QuicStringPiece data) {
+ QpackDecoder decoder(encoder_stream_error_delegate,
+ decoder_stream_sender_delegate);
+ auto progressive_decoder =
+ decoder.DecodeHeaderBlock(/* stream_id = */ 1, handler);
+ while (!data.empty()) {
+ size_t fragment_size = std::min(fragment_size_generator(), data.size());
+ progressive_decoder->Decode(data.substr(0, fragment_size));
+ data = data.substr(fragment_size);
+ }
+ progressive_decoder->EndHeaderBlock();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h
new file mode 100644
index 00000000000..ca5b60818fa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h
@@ -0,0 +1,114 @@
+// 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_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+namespace quic {
+namespace test {
+
+// QpackDecoder::EncoderStreamErrorDelegate implementation that does nothing.
+class NoopEncoderStreamErrorDelegate
+ : public QpackDecoder::EncoderStreamErrorDelegate {
+ public:
+ ~NoopEncoderStreamErrorDelegate() override = default;
+
+ void OnEncoderStreamError(QuicStringPiece error_message) override;
+};
+
+// Mock QpackDecoder::EncoderStreamErrorDelegate implementation.
+class MockEncoderStreamErrorDelegate
+ : public QpackDecoder::EncoderStreamErrorDelegate {
+ public:
+ ~MockEncoderStreamErrorDelegate() override = default;
+
+ MOCK_METHOD1(OnEncoderStreamError, void(QuicStringPiece error_message));
+};
+
+// QpackDecoderStreamSender::Delegate implementation that does nothing.
+class NoopDecoderStreamSenderDelegate
+ : public QpackDecoderStreamSender::Delegate {
+ public:
+ ~NoopDecoderStreamSenderDelegate() override = default;
+
+ void WriteDecoderStreamData(QuicStringPiece data) override;
+};
+
+// Mock QpackDecoderStreamSender::Delegate implementation.
+class MockDecoderStreamSenderDelegate
+ : public QpackDecoderStreamSender::Delegate {
+ public:
+ ~MockDecoderStreamSenderDelegate() override = default;
+
+ MOCK_METHOD1(WriteDecoderStreamData, void(QuicStringPiece data));
+};
+
+// HeadersHandlerInterface implementation that collects decoded headers
+// into a SpdyHeaderBlock.
+class TestHeadersHandler
+ : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+ TestHeadersHandler();
+ ~TestHeadersHandler() override = default;
+
+ // HeadersHandlerInterface implementation:
+ void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override;
+ void OnDecodingCompleted() override;
+ void OnDecodingErrorDetected(QuicStringPiece error_message) override;
+
+ // Release decoded header list. Must only be called if decoding is complete
+ // and no errors have been detected.
+ spdy::SpdyHeaderBlock ReleaseHeaderList();
+
+ bool decoding_completed() const;
+ bool decoding_error_detected() const;
+
+ private:
+ spdy::SpdyHeaderBlock header_list_;
+ bool decoding_completed_;
+ bool decoding_error_detected_;
+};
+
+class MockHeadersHandler
+ : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+ MockHeadersHandler() = default;
+ MockHeadersHandler(const MockHeadersHandler&) = delete;
+ MockHeadersHandler& operator=(const MockHeadersHandler&) = delete;
+ ~MockHeadersHandler() override = default;
+
+ MOCK_METHOD2(OnHeaderDecoded,
+ void(QuicStringPiece name, QuicStringPiece value));
+ MOCK_METHOD0(OnDecodingCompleted, void());
+ MOCK_METHOD1(OnDecodingErrorDetected, void(QuicStringPiece error_message));
+};
+
+class NoOpHeadersHandler
+ : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+ ~NoOpHeadersHandler() override = default;
+
+ void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override {}
+ void OnDecodingCompleted() override {}
+ void OnDecodingErrorDetected(QuicStringPiece error_message) override {}
+};
+
+void QpackDecode(
+ QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate,
+ QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate,
+ QpackProgressiveDecoder::HeadersHandlerInterface* handler,
+ const FragmentSizeGenerator& fragment_size_generator,
+ QuicStringPiece data);
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc
new file mode 100644
index 00000000000..108ffd57ee4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc
@@ -0,0 +1,55 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QpackEncoder::QpackEncoder(
+ DecoderStreamErrorDelegate* decoder_stream_error_delegate,
+ QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate)
+ : decoder_stream_error_delegate_(decoder_stream_error_delegate),
+ decoder_stream_receiver_(this),
+ encoder_stream_sender_(encoder_stream_sender_delegate) {
+ DCHECK(decoder_stream_error_delegate_);
+ DCHECK(encoder_stream_sender_delegate);
+}
+
+QpackEncoder::~QpackEncoder() {}
+
+std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder>
+QpackEncoder::EncodeHeaderList(QuicStreamId stream_id,
+ const spdy::SpdyHeaderBlock* header_list) {
+ return QuicMakeUnique<QpackProgressiveEncoder>(
+ stream_id, &header_table_, &encoder_stream_sender_, header_list);
+}
+
+void QpackEncoder::DecodeDecoderStreamData(QuicStringPiece data) {
+ decoder_stream_receiver_.Decode(data);
+}
+
+void QpackEncoder::OnInsertCountIncrement(uint64_t increment) {
+ // TODO(bnc): Implement dynamic table management for encoding.
+}
+
+void QpackEncoder::OnHeaderAcknowledgement(QuicStreamId stream_id) {
+ // TODO(bnc): Implement dynamic table management for encoding.
+}
+
+void QpackEncoder::OnStreamCancellation(QuicStreamId stream_id) {
+ // TODO(bnc): Implement dynamic table management for encoding.
+}
+
+void QpackEncoder::OnErrorDetected(QuicStringPiece error_message) {
+ decoder_stream_error_delegate_->OnDecoderStreamError(error_message);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h
new file mode 100644
index 00000000000..4e655329b5f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h
@@ -0,0 +1,72 @@
+// 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_CORE_QPACK_QPACK_ENCODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h"
+
+namespace spdy {
+
+class SpdyHeaderBlock;
+
+}
+
+namespace quic {
+
+// QPACK encoder class. Exactly one instance should exist per QUIC connection.
+// This class vends a new QpackProgressiveEncoder instance for each new header
+// list to be encoded.
+class QUIC_EXPORT_PRIVATE QpackEncoder
+ : public QpackDecoderStreamReceiver::Delegate {
+ public:
+ // Interface for receiving notification that an error has occurred on the
+ // decoder stream. This MUST be treated as a connection error of type
+ // HTTP_QPACK_DECODER_STREAM_ERROR.
+ class QUIC_EXPORT_PRIVATE DecoderStreamErrorDelegate {
+ public:
+ virtual ~DecoderStreamErrorDelegate() {}
+
+ virtual void OnDecoderStreamError(QuicStringPiece error_message) = 0;
+ };
+
+ QpackEncoder(
+ DecoderStreamErrorDelegate* decoder_stream_error_delegate,
+ QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate);
+ ~QpackEncoder() override;
+
+ // This factory method is called to start encoding a header list.
+ // |*header_list| must remain valid and must not change
+ // during the lifetime of the returned ProgressiveEncoder instance.
+ std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> EncodeHeaderList(
+ QuicStreamId stream_id,
+ const spdy::SpdyHeaderBlock* header_list);
+
+ // Decode data received on the decoder stream.
+ void DecodeDecoderStreamData(QuicStringPiece data);
+
+ // QpackDecoderStreamReceiver::Delegate implementation
+ void OnInsertCountIncrement(uint64_t increment) override;
+ void OnHeaderAcknowledgement(QuicStreamId stream_id) override;
+ void OnStreamCancellation(QuicStreamId stream_id) override;
+ void OnErrorDetected(QuicStringPiece error_message) override;
+
+ private:
+ DecoderStreamErrorDelegate* const decoder_stream_error_delegate_;
+ QpackDecoderStreamReceiver decoder_stream_receiver_;
+ QpackEncoderStreamSender encoder_stream_sender_;
+ QpackHeaderTable header_table_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc
new file mode 100644
index 00000000000..3f8ef08a7ee
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc
@@ -0,0 +1,60 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
+
+#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
+#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+
+namespace quic {
+
+QpackEncoderStreamReceiver::QpackEncoderStreamReceiver(Delegate* delegate)
+ : instruction_decoder_(QpackEncoderStreamLanguage(), this),
+ delegate_(delegate),
+ error_detected_(false) {
+ DCHECK(delegate_);
+}
+
+void QpackEncoderStreamReceiver::Decode(QuicStringPiece data) {
+ if (data.empty() || error_detected_) {
+ return;
+ }
+
+ instruction_decoder_.Decode(data);
+}
+
+bool QpackEncoderStreamReceiver::OnInstructionDecoded(
+ const QpackInstruction* instruction) {
+ if (instruction == InsertWithNameReferenceInstruction()) {
+ delegate_->OnInsertWithNameReference(instruction_decoder_.s_bit(),
+ instruction_decoder_.varint(),
+ instruction_decoder_.value());
+ return true;
+ }
+
+ if (instruction == InsertWithoutNameReferenceInstruction()) {
+ delegate_->OnInsertWithoutNameReference(instruction_decoder_.name(),
+ instruction_decoder_.value());
+ return true;
+ }
+
+ if (instruction == DuplicateInstruction()) {
+ delegate_->OnDuplicate(instruction_decoder_.varint());
+ return true;
+ }
+
+ DCHECK_EQ(instruction, SetDynamicTableCapacityInstruction());
+ delegate_->OnSetDynamicTableCapacity(instruction_decoder_.varint());
+ return true;
+}
+
+void QpackEncoderStreamReceiver::OnError(QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ delegate_->OnErrorDetected(error_message);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h
new file mode 100644
index 00000000000..5519b4c67a2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h
@@ -0,0 +1,68 @@
+// 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_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
+
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class decodes data received on the encoder stream.
+class QUIC_EXPORT_PRIVATE QpackEncoderStreamReceiver
+ : public QpackInstructionDecoder::Delegate {
+ public:
+ // An interface for handling instructions decoded from the encoder stream, see
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // 5.2.1. Insert With Name Reference
+ virtual void OnInsertWithNameReference(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value) = 0;
+ // 5.2.2. Insert Without Name Reference
+ virtual void OnInsertWithoutNameReference(QuicStringPiece name,
+ QuicStringPiece value) = 0;
+ // 5.2.3. Duplicate
+ virtual void OnDuplicate(uint64_t index) = 0;
+ // 5.2.4. Set Dynamic Table Capacity
+ virtual void OnSetDynamicTableCapacity(uint64_t capacity) = 0;
+ // Decoding error
+ virtual void OnErrorDetected(QuicStringPiece error_message) = 0;
+ };
+
+ explicit QpackEncoderStreamReceiver(Delegate* delegate);
+ QpackEncoderStreamReceiver() = delete;
+ QpackEncoderStreamReceiver(const QpackEncoderStreamReceiver&) = delete;
+ QpackEncoderStreamReceiver& operator=(const QpackEncoderStreamReceiver&) =
+ delete;
+ ~QpackEncoderStreamReceiver() override = default;
+
+ // Decode data and call appropriate Delegate method after each decoded
+ // instruction. Once an error occurs, Delegate::OnErrorDetected() is called,
+ // and all further data is ignored.
+ void Decode(QuicStringPiece data);
+
+ // QpackInstructionDecoder::Delegate implementation.
+ bool OnInstructionDecoded(const QpackInstruction* instruction) override;
+ void OnError(QuicStringPiece error_message) override;
+
+ private:
+ QpackInstructionDecoder instruction_decoder_;
+ Delegate* const delegate_;
+
+ // True if a decoding error has been detected.
+ bool error_detected_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
new file mode 100644
index 00000000000..dcb2039ccfd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -0,0 +1,169 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using testing::Eq;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockDelegate : public QpackEncoderStreamReceiver::Delegate {
+ public:
+ ~MockDelegate() override = default;
+
+ MOCK_METHOD3(OnInsertWithNameReference,
+ void(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value));
+ MOCK_METHOD2(OnInsertWithoutNameReference,
+ void(QuicStringPiece name, QuicStringPiece value));
+ MOCK_METHOD1(OnDuplicate, void(uint64_t index));
+ MOCK_METHOD1(OnSetDynamicTableCapacity, void(uint64_t capacity));
+ MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message));
+};
+
+class QpackEncoderStreamReceiverTest : public QuicTest {
+ protected:
+ QpackEncoderStreamReceiverTest() : stream_(&delegate_) {}
+ ~QpackEncoderStreamReceiverTest() override = default;
+
+ void Decode(QuicStringPiece data) { stream_.Decode(data); }
+ StrictMock<MockDelegate>* delegate() { return &delegate_; }
+
+ private:
+ QpackEncoderStreamReceiver stream_;
+ StrictMock<MockDelegate> delegate_;
+};
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReference) {
+ // Static, index fits in prefix, empty value.
+ EXPECT_CALL(*delegate(), OnInsertWithNameReference(true, 5, Eq("")));
+ // Static, index fits in prefix, Huffman encoded value.
+ EXPECT_CALL(*delegate(), OnInsertWithNameReference(true, 2, Eq("foo")));
+ // Not static, index does not fit in prefix, not Huffman encoded value.
+ EXPECT_CALL(*delegate(), OnInsertWithNameReference(false, 137, Eq("bar")));
+ // Value length does not fit in prefix.
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ EXPECT_CALL(*delegate(),
+ OnInsertWithNameReference(false, 42, Eq(std::string(127, 'Z'))));
+
+ Decode(QuicTextUtils::HexDecode(
+ "c500"
+ "c28294e7"
+ "bf4a03626172"
+ "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceIndexTooLarge) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("bfffffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceValueTooLong) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("c57fffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReference) {
+ // Empty name and value.
+ EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq(""), Eq("")));
+ // Huffman encoded short strings.
+ EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq("bar"), Eq("bar")));
+ // Not Huffman encoded short strings.
+ EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq("foo"), Eq("foo")));
+ // Not Huffman encoded long strings; length does not fit on prefix.
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ EXPECT_CALL(*delegate(),
+ OnInsertWithoutNameReference(Eq(std::string(31, 'Z')),
+ Eq(std::string(127, 'Z'))));
+
+ Decode(QuicTextUtils::HexDecode(
+ "4000"
+ "4362617203626172"
+ "6294e78294e7"
+ "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f005a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"));
+}
+
+// Name Length value is too large for varint decoder to decode.
+TEST_F(QpackEncoderStreamReceiverTest,
+ InsertWithoutNameReferenceNameTooLongForVarintDecoder) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("5fffffffffffffffffffff"));
+}
+
+// Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_F(QpackEncoderStreamReceiverTest,
+ InsertWithoutNameReferenceNameExceedsLimit) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long.")));
+
+ Decode(QuicTextUtils::HexDecode("5fffff7f"));
+}
+
+// Value Length value is too large for varint decoder to decode.
+TEST_F(QpackEncoderStreamReceiverTest,
+ InsertWithoutNameReferenceValueTooLongForVarintDecoder) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("436261727fffffffffffffffffffff"));
+}
+
+// Value Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_F(QpackEncoderStreamReceiverTest,
+ InsertWithoutNameReferenceValueExceedsLimit) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long.")));
+
+ Decode(QuicTextUtils::HexDecode("436261727fffff7f"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, Duplicate) {
+ // Small index fits in prefix.
+ EXPECT_CALL(*delegate(), OnDuplicate(17));
+ // Large index requires two extension bytes.
+ EXPECT_CALL(*delegate(), OnDuplicate(500));
+
+ Decode(QuicTextUtils::HexDecode("111fd503"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, DuplicateIndexTooLarge) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("1fffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacity) {
+ // Small capacity fits in prefix.
+ EXPECT_CALL(*delegate(), OnSetDynamicTableCapacity(17));
+ // Large capacity requires two extension bytes.
+ EXPECT_CALL(*delegate(), OnSetDynamicTableCapacity(500));
+
+ Decode(QuicTextUtils::HexDecode("313fd503"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacityTooLarge) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc
new file mode 100644
index 00000000000..3fb3b33f35e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc
@@ -0,0 +1,81 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+
+#include <cstddef>
+#include <limits>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QpackEncoderStreamSender::QpackEncoderStreamSender(Delegate* delegate)
+ : delegate_(delegate) {
+ DCHECK(delegate_);
+}
+
+void QpackEncoderStreamSender::SendInsertWithNameReference(
+ bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value) {
+ instruction_encoder_.set_s_bit(is_static);
+ instruction_encoder_.set_varint(name_index);
+ instruction_encoder_.set_value(value);
+
+ instruction_encoder_.Encode(InsertWithNameReferenceInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteEncoderStreamData(output);
+}
+
+void QpackEncoderStreamSender::SendInsertWithoutNameReference(
+ QuicStringPiece name,
+ QuicStringPiece value) {
+ instruction_encoder_.set_name(name);
+ instruction_encoder_.set_value(value);
+
+ instruction_encoder_.Encode(InsertWithoutNameReferenceInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteEncoderStreamData(output);
+}
+
+void QpackEncoderStreamSender::SendDuplicate(uint64_t index) {
+ instruction_encoder_.set_varint(index);
+
+ instruction_encoder_.Encode(DuplicateInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteEncoderStreamData(output);
+}
+
+void QpackEncoderStreamSender::SendSetDynamicTableCapacity(uint64_t capacity) {
+ instruction_encoder_.set_varint(capacity);
+
+ instruction_encoder_.Encode(SetDynamicTableCapacityInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteEncoderStreamData(output);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h
new file mode 100644
index 00000000000..ad3456889bd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h
@@ -0,0 +1,58 @@
+// 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_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class serializes instructions for transmission on the encoder stream.
+class QUIC_EXPORT_PRIVATE QpackEncoderStreamSender {
+ public:
+ // An interface for handling encoded data.
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Encoded |data| is ready to be written on the encoder stream.
+ // WriteEncoderStreamData() is called exactly once for each instruction.
+ // |data| contains the entire encoded instruction and it is guaranteed to be
+ // not empty.
+ virtual void WriteEncoderStreamData(QuicStringPiece data) = 0;
+ };
+
+ explicit QpackEncoderStreamSender(Delegate* delegate);
+ QpackEncoderStreamSender() = delete;
+ QpackEncoderStreamSender(const QpackEncoderStreamSender&) = delete;
+ QpackEncoderStreamSender& operator=(const QpackEncoderStreamSender&) = delete;
+
+ // Methods for sending instructions, see
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2
+
+ // 5.2.1. Insert With Name Reference
+ void SendInsertWithNameReference(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value);
+ // 5.2.2. Insert Without Name Reference
+ void SendInsertWithoutNameReference(QuicStringPiece name,
+ QuicStringPiece value);
+ // 5.2.3. Duplicate
+ void SendDuplicate(uint64_t index);
+ // 5.2.4. Set Dynamic Table Capacity
+ void SendSetDynamicTableCapacity(uint64_t capacity);
+
+ private:
+ Delegate* const delegate_;
+ QpackInstructionEncoder instruction_encoder_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc
new file mode 100644
index 00000000000..a2e73763dea
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc
@@ -0,0 +1,113 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Eq;
+using ::testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackEncoderStreamSenderTest : public QuicTest {
+ protected:
+ QpackEncoderStreamSenderTest() : stream_(&delegate_) {}
+ ~QpackEncoderStreamSenderTest() override = default;
+
+ StrictMock<MockEncoderStreamSenderDelegate> delegate_;
+ QpackEncoderStreamSender stream_;
+};
+
+TEST_F(QpackEncoderStreamSenderTest, InsertWithNameReference) {
+ // Static, index fits in prefix, empty value.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("c500"))));
+ stream_.SendInsertWithNameReference(true, 5, "");
+
+ // Static, index fits in prefix, Huffman encoded value.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("c28294e7"))));
+ stream_.SendInsertWithNameReference(true, 2, "foo");
+
+ // Not static, index does not fit in prefix, not Huffman encoded value.
+ EXPECT_CALL(delegate_, WriteEncoderStreamData(
+ Eq(QuicTextUtils::HexDecode("bf4a03626172"))));
+ stream_.SendInsertWithNameReference(false, 137, "bar");
+
+ // Value length does not fit in prefix.
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ EXPECT_CALL(
+ delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode(
+ "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"))));
+ stream_.SendInsertWithNameReference(false, 42, std::string(127, 'Z'));
+}
+
+TEST_F(QpackEncoderStreamSenderTest, InsertWithoutNameReference) {
+ // Empty name and value.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("4000"))));
+ stream_.SendInsertWithoutNameReference("", "");
+
+ // Huffman encoded short strings.
+ EXPECT_CALL(delegate_, WriteEncoderStreamData(
+ Eq(QuicTextUtils::HexDecode("4362617203626172"))));
+ stream_.SendInsertWithoutNameReference("bar", "bar");
+
+ // Not Huffman encoded short strings.
+ EXPECT_CALL(delegate_, WriteEncoderStreamData(
+ Eq(QuicTextUtils::HexDecode("6294e78294e7"))));
+ stream_.SendInsertWithoutNameReference("foo", "foo");
+
+ // Not Huffman encoded long strings; length does not fit on prefix.
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ EXPECT_CALL(
+ delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode(
+ "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f"
+ "005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"))));
+ stream_.SendInsertWithoutNameReference(std::string(31, 'Z'),
+ std::string(127, 'Z'));
+}
+
+TEST_F(QpackEncoderStreamSenderTest, Duplicate) {
+ // Small index fits in prefix.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("11"))));
+ stream_.SendDuplicate(17);
+
+ // Large index requires two extension bytes.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("1fd503"))));
+ stream_.SendDuplicate(500);
+}
+
+TEST_F(QpackEncoderStreamSenderTest, SetDynamicTableCapacity) {
+ // Small capacity fits in prefix.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("31"))));
+ stream_.SendSetDynamicTableCapacity(17);
+
+ // Large capacity requires two extension bytes.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("3fd503"))));
+ stream_.SendSetDynamicTableCapacity(500);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc
new file mode 100644
index 00000000000..fb07d49e1ad
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc
@@ -0,0 +1,168 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Eq;
+using ::testing::StrictMock;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackEncoderTest : public QuicTestWithParam<FragmentMode> {
+ protected:
+ QpackEncoderTest() : fragment_mode_(GetParam()) {}
+ ~QpackEncoderTest() override = default;
+
+ std::string Encode(const spdy::SpdyHeaderBlock* header_list) {
+ return QpackEncode(
+ &decoder_stream_error_delegate_, &encoder_stream_sender_delegate_,
+ FragmentModeToFragmentSizeGenerator(fragment_mode_), header_list);
+ }
+
+ StrictMock<MockDecoderStreamErrorDelegate> decoder_stream_error_delegate_;
+ NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate_;
+
+ private:
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ QpackEncoderTest,
+ Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackEncoderTest, Empty) {
+ spdy::SpdyHeaderBlock header_list;
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000"), output);
+}
+
+TEST_P(QpackEncoderTest, EmptyName) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[""] = "foo";
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000208294e7"), output);
+}
+
+TEST_P(QpackEncoderTest, EmptyValue) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "";
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("00002a94e700"), output);
+}
+
+TEST_P(QpackEncoderTest, EmptyNameAndValue) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[""] = "";
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("00002000"), output);
+}
+
+TEST_P(QpackEncoderTest, Simple) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "bar";
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("00002a94e703626172"), output);
+}
+
+TEST_P(QpackEncoderTest, Multiple) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "bar";
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ header_list["ZZZZZZZ"] = std::string(127, 'Z');
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(
+ QuicTextUtils::HexDecode(
+ "0000" // prefix
+ "2a94e703626172" // foo: bar
+ "27005a5a5a5a5a5a5a" // 7 octet long header name, the smallest number
+ // that does not fit on a 3-bit prefix.
+ "7f005a5a5a5a5a5a5a" // 127 octet long header value, the smallest
+ "5a5a5a5a5a5a5a5a5a" // number that does not fit on a 7-bit prefix.
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a"),
+ output);
+}
+
+TEST_P(QpackEncoderTest, StaticTable) {
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "GET";
+ header_list["accept-encoding"] = "gzip, deflate, br";
+ header_list["location"] = "";
+
+ std::string output = Encode(&header_list);
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000d1dfcc"), output);
+ }
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "POST";
+ header_list["accept-encoding"] = "compress";
+ header_list["location"] = "foo";
+
+ std::string output = Encode(&header_list);
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000d45f108621e9aec2a11f5c8294e7"),
+ output);
+ }
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "TRACE";
+ header_list["accept-encoding"] = "";
+
+ std::string output = Encode(&header_list);
+ EXPECT_EQ(QuicTextUtils::HexDecode("00005f000554524143455f1000"), output);
+ }
+}
+
+TEST_P(QpackEncoderTest, SimpleIndexed) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":path"] = "/";
+
+ QpackEncoder encoder(&decoder_stream_error_delegate_,
+ &encoder_stream_sender_delegate_);
+ auto progressive_encoder =
+ encoder.EncodeHeaderList(/* stream_id = */ 1, &header_list);
+ EXPECT_TRUE(progressive_encoder->HasNext());
+
+ // This indexed header field takes exactly three bytes:
+ // two for the prefix, one for the indexed static entry.
+ std::string output;
+ progressive_encoder->Next(3, &output);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000c1"), output);
+ EXPECT_FALSE(progressive_encoder->HasNext());
+}
+
+TEST_P(QpackEncoderTest, DecoderStreamError) {
+ EXPECT_CALL(decoder_stream_error_delegate_,
+ OnDecoderStreamError(Eq("Encoded integer too large.")));
+
+ QpackEncoder encoder(&decoder_stream_error_delegate_,
+ &encoder_stream_sender_delegate_);
+ encoder.DecodeDecoderStreamData(
+ QuicTextUtils::HexDecode("ffffffffffffffffffffff"));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc
new file mode 100644
index 00000000000..dd1ccb31256
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc
@@ -0,0 +1,37 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
+
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h"
+
+namespace quic {
+namespace test {
+
+void NoopDecoderStreamErrorDelegate::OnDecoderStreamError(
+ QuicStringPiece error_message) {}
+
+void NoopEncoderStreamSenderDelegate::WriteEncoderStreamData(
+ QuicStringPiece data) {}
+
+std::string QpackEncode(
+ QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate,
+ QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate,
+ const FragmentSizeGenerator& fragment_size_generator,
+ const spdy::SpdyHeaderBlock* header_list) {
+ QpackEncoder encoder(decoder_stream_error_delegate,
+ encoder_stream_sender_delegate);
+ auto progressive_encoder =
+ encoder.EncodeHeaderList(/* stream_id = */ 1, header_list);
+
+ std::string output;
+ while (progressive_encoder->HasNext()) {
+ progressive_encoder->Next(fragment_size_generator(), &output);
+ }
+
+ return output;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h
new file mode 100644
index 00000000000..3c9b404a1d4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h
@@ -0,0 +1,64 @@
+// 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_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+namespace quic {
+namespace test {
+
+// QpackEncoder::DecoderStreamErrorDelegate implementation that does nothing.
+class NoopDecoderStreamErrorDelegate
+ : public QpackEncoder::DecoderStreamErrorDelegate {
+ public:
+ ~NoopDecoderStreamErrorDelegate() override = default;
+
+ void OnDecoderStreamError(QuicStringPiece error_message) override;
+};
+
+// Mock QpackEncoder::DecoderStreamErrorDelegate implementation.
+class MockDecoderStreamErrorDelegate
+ : public QpackEncoder::DecoderStreamErrorDelegate {
+ public:
+ ~MockDecoderStreamErrorDelegate() override = default;
+
+ MOCK_METHOD1(OnDecoderStreamError, void(QuicStringPiece error_message));
+};
+
+// QpackEncoderStreamSender::Delegate implementation that does nothing.
+class NoopEncoderStreamSenderDelegate
+ : public QpackEncoderStreamSender::Delegate {
+ public:
+ ~NoopEncoderStreamSenderDelegate() override = default;
+
+ void WriteEncoderStreamData(QuicStringPiece data) override;
+};
+
+// Mock QpackEncoderStreamSender::Delegate implementation.
+class MockEncoderStreamSenderDelegate
+ : public QpackEncoderStreamSender::Delegate {
+ public:
+ ~MockEncoderStreamSenderDelegate() override = default;
+
+ MOCK_METHOD1(WriteEncoderStreamData, void(QuicStringPiece data));
+};
+
+std::string QpackEncode(
+ QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate,
+ QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate,
+ const FragmentSizeGenerator& fragment_size_generator,
+ const spdy::SpdyHeaderBlock* header_list);
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc
new file mode 100644
index 00000000000..01943301c66
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc
@@ -0,0 +1,204 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+const uint64_t kEntrySizeOverhead = 32;
+
+uint64_t EntrySize(QuicStringPiece name, QuicStringPiece value) {
+ return name.size() + value.size() + kEntrySizeOverhead;
+}
+
+} // anonymous namespace
+
+QpackHeaderTable::QpackHeaderTable()
+ : static_entries_(ObtainQpackStaticTable().GetStaticEntries()),
+ static_index_(ObtainQpackStaticTable().GetStaticIndex()),
+ static_name_index_(ObtainQpackStaticTable().GetStaticNameIndex()),
+ dynamic_table_size_(0),
+ dynamic_table_capacity_(0),
+ maximum_dynamic_table_capacity_(0),
+ max_entries_(0),
+ dropped_entry_count_(0) {}
+
+QpackHeaderTable::~QpackHeaderTable() = default;
+
+const QpackEntry* QpackHeaderTable::LookupEntry(bool is_static,
+ uint64_t index) const {
+ if (is_static) {
+ if (index >= static_entries_.size()) {
+ return nullptr;
+ }
+
+ return &static_entries_[index];
+ }
+
+ if (index < dropped_entry_count_) {
+ return nullptr;
+ }
+
+ index -= dropped_entry_count_;
+
+ if (index >= dynamic_entries_.size()) {
+ return nullptr;
+ }
+
+ return &dynamic_entries_[index];
+}
+
+QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField(
+ QuicStringPiece name,
+ QuicStringPiece value,
+ bool* is_static,
+ uint64_t* index) const {
+ QpackEntry query(name, value);
+
+ // Look for exact match in static table.
+ auto index_it = static_index_.find(&query);
+ if (index_it != static_index_.end()) {
+ DCHECK((*index_it)->IsStatic());
+ *index = (*index_it)->InsertionIndex();
+ *is_static = true;
+ return MatchType::kNameAndValue;
+ }
+
+ // Look for exact match in dynamic table.
+ index_it = dynamic_index_.find(&query);
+ if (index_it != dynamic_index_.end()) {
+ DCHECK(!(*index_it)->IsStatic());
+ *index = (*index_it)->InsertionIndex();
+ *is_static = false;
+ return MatchType::kNameAndValue;
+ }
+
+ // Look for name match in static table.
+ auto name_index_it = static_name_index_.find(name);
+ if (name_index_it != static_name_index_.end()) {
+ DCHECK(name_index_it->second->IsStatic());
+ *index = name_index_it->second->InsertionIndex();
+ *is_static = true;
+ return MatchType::kName;
+ }
+
+ // Look for name match in dynamic table.
+ name_index_it = dynamic_name_index_.find(name);
+ if (name_index_it != dynamic_name_index_.end()) {
+ DCHECK(!name_index_it->second->IsStatic());
+ *index = name_index_it->second->InsertionIndex();
+ *is_static = false;
+ return MatchType::kName;
+ }
+
+ return MatchType::kNoMatch;
+}
+
+const QpackEntry* QpackHeaderTable::InsertEntry(QuicStringPiece name,
+ QuicStringPiece value) {
+ const uint64_t entry_size = EntrySize(name, value);
+ if (entry_size > dynamic_table_capacity_) {
+ return nullptr;
+ }
+
+ const uint64_t index = dropped_entry_count_ + dynamic_entries_.size();
+ dynamic_entries_.push_back({name, value, /* is_static = */ false, index});
+ QpackEntry* const new_entry = &dynamic_entries_.back();
+
+ // Evict entries after inserting the new entry instead of before
+ // in order to avoid invalidating |name| and |value|.
+ dynamic_table_size_ += entry_size;
+ EvictDownToCurrentCapacity();
+
+ auto index_result = dynamic_index_.insert(new_entry);
+ if (!index_result.second) {
+ // An entry with the same name and value already exists. It needs to be
+ // replaced, because |dynamic_index_| tracks the most recent entry for a
+ // given name and value.
+ DCHECK_GT(new_entry->InsertionIndex(),
+ (*index_result.first)->InsertionIndex());
+ dynamic_index_.erase(index_result.first);
+ auto result = dynamic_index_.insert(new_entry);
+ CHECK(result.second);
+ }
+
+ auto name_result = dynamic_name_index_.insert({new_entry->name(), new_entry});
+ if (!name_result.second) {
+ // An entry with the same name already exists. It needs to be replaced,
+ // because |dynamic_name_index_| tracks the most recent entry for a given
+ // name.
+ DCHECK_GT(new_entry->InsertionIndex(),
+ name_result.first->second->InsertionIndex());
+ dynamic_name_index_.erase(name_result.first);
+ auto result = dynamic_name_index_.insert({new_entry->name(), new_entry});
+ CHECK(result.second);
+ }
+
+ return new_entry;
+}
+
+bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) {
+ if (capacity > maximum_dynamic_table_capacity_) {
+ return false;
+ }
+
+ dynamic_table_capacity_ = capacity;
+ EvictDownToCurrentCapacity();
+
+ DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_);
+
+ return true;
+}
+
+void QpackHeaderTable::SetMaximumDynamicTableCapacity(
+ uint64_t maximum_dynamic_table_capacity) {
+ // This method can only be called once: in the decoding context, shortly after
+ // construction; in the encoding context, upon receiving the SETTINGS frame.
+ DCHECK_EQ(0u, dynamic_table_capacity_);
+ DCHECK_EQ(0u, maximum_dynamic_table_capacity_);
+ DCHECK_EQ(0u, max_entries_);
+
+ dynamic_table_capacity_ = maximum_dynamic_table_capacity;
+ maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity;
+ max_entries_ = maximum_dynamic_table_capacity / 32;
+}
+
+void QpackHeaderTable::EvictDownToCurrentCapacity() {
+ while (dynamic_table_size_ > dynamic_table_capacity_) {
+ DCHECK(!dynamic_entries_.empty());
+
+ QpackEntry* const entry = &dynamic_entries_.front();
+ const uint64_t entry_size = EntrySize(entry->name(), entry->value());
+
+ DCHECK_GE(dynamic_table_size_, entry_size);
+ dynamic_table_size_ -= entry_size;
+
+ auto index_it = dynamic_index_.find(entry);
+ // Remove |dynamic_index_| entry only if it points to the same
+ // QpackEntry in |dynamic_entries_|. Note that |dynamic_index_| has a
+ // comparison function that only considers name and value, not actual
+ // QpackEntry* address, so find() can return a different entry if name and
+ // value match.
+ if (index_it != dynamic_index_.end() && *index_it == entry) {
+ dynamic_index_.erase(index_it);
+ }
+
+ auto name_it = dynamic_name_index_.find(entry->name());
+ // Remove |dynamic_name_index_| entry only if it points to the same
+ // QpackEntry in |dynamic_entries_|.
+ if (name_it != dynamic_name_index_.end() && name_it->second == entry) {
+ dynamic_name_index_.erase(name_it);
+ }
+
+ dynamic_entries_.pop_front();
+ ++dropped_entry_count_;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h
new file mode 100644
index 00000000000..eda7ab525a2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h
@@ -0,0 +1,144 @@
+// 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_CORE_QPACK_QPACK_HEADER_TABLE_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h"
+
+namespace quic {
+
+using QpackEntry = spdy::HpackEntry;
+
+// This class manages the QPACK static and dynamic tables. For dynamic entries,
+// it only has a concept of absolute indices. The caller needs to perform the
+// necessary transformations to and from relative indices and post-base indices.
+class QUIC_EXPORT_PRIVATE QpackHeaderTable {
+ public:
+ using EntryTable = spdy::HpackHeaderTable::EntryTable;
+ using EntryHasher = spdy::HpackHeaderTable::EntryHasher;
+ using EntriesEq = spdy::HpackHeaderTable::EntriesEq;
+ using UnorderedEntrySet = spdy::HpackHeaderTable::UnorderedEntrySet;
+ using NameToEntryMap = spdy::HpackHeaderTable::NameToEntryMap;
+
+ // Result of header table lookup.
+ enum class MatchType { kNameAndValue, kName, kNoMatch };
+
+ QpackHeaderTable();
+ QpackHeaderTable(const QpackHeaderTable&) = delete;
+ QpackHeaderTable& operator=(const QpackHeaderTable&) = delete;
+
+ ~QpackHeaderTable();
+
+ // Returns the entry at absolute index |index| from the static or dynamic
+ // table according to |is_static|. |index| is zero based for both the static
+ // and the dynamic table. The returned pointer is valid until the entry is
+ // evicted, even if other entries are inserted into the dynamic table.
+ // Returns nullptr if entry does not exist.
+ const QpackEntry* LookupEntry(bool is_static, uint64_t index) const;
+
+ // Returns the absolute index of an entry with matching name and value if such
+ // exists, otherwise one with matching name is such exists. |index| is zero
+ // based for both the static and the dynamic table.
+ MatchType FindHeaderField(QuicStringPiece name,
+ QuicStringPiece value,
+ bool* is_static,
+ uint64_t* index) const;
+
+ // Insert (name, value) into the dynamic table. May evict entries. Returns a
+ // pointer to the inserted owned entry on success. Returns nullptr if entry
+ // is larger than the capacity of the dynamic table.
+ const QpackEntry* InsertEntry(QuicStringPiece name, QuicStringPiece value);
+
+ // Change dynamic table capacity to |capacity|. Returns true on success.
+ // Returns false is |capacity| exceeds maximum dynamic table capacity.
+ bool SetDynamicTableCapacity(uint64_t capacity);
+
+ // Set |maximum_dynamic_table_capacity_|. The initial value is zero. The
+ // final value is determined by the decoder and is sent to the encoder as
+ // SETTINGS_HEADER_TABLE_SIZE. Therefore in the decoding context the final
+ // value can be set upon connection establishment, whereas in the encoding
+ // context it can be set when the SETTINGS frame is received.
+ // This method must only be called at most once.
+ void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity);
+
+ // Used on request streams to encode and decode Required Insert Count.
+ uint64_t max_entries() const { return max_entries_; }
+
+ // The number of entries inserted to the dynamic table (including ones that
+ // were dropped since). Used for relative indexing on the encoder stream.
+ uint64_t inserted_entry_count() const {
+ return dynamic_entries_.size() + dropped_entry_count_;
+ }
+
+ // The number of entries dropped from the dynamic table.
+ uint64_t dropped_entry_count() const { return dropped_entry_count_; }
+
+ private:
+ // Evict entries from the dynamic table until table size is less than or equal
+ // to current value of |dynamic_table_capacity_|.
+ void EvictDownToCurrentCapacity();
+
+ // Static Table
+
+ // |static_entries_|, |static_index_|, |static_name_index_| are owned by
+ // QpackStaticTable singleton.
+
+ // Tracks QpackEntries by index.
+ const EntryTable& static_entries_;
+
+ // Tracks the unique static entry for a given header name and value.
+ const UnorderedEntrySet& static_index_;
+
+ // Tracks the first static entry for a given header name.
+ const NameToEntryMap& static_name_index_;
+
+ // Dynamic Table
+
+ // Queue of dynamic table entries, for lookup by index.
+ // |dynamic_entries_| owns the entries in the dynamic table.
+ EntryTable dynamic_entries_;
+
+ // An unordered set of QpackEntry pointers with a comparison operator that
+ // only cares about name and value. This allows fast lookup of the most
+ // recently inserted dynamic entry for a given header name and value pair.
+ // Entries point to entries owned by |dynamic_entries_|.
+ UnorderedEntrySet dynamic_index_;
+
+ // An unordered map of QpackEntry pointers keyed off header name. This allows
+ // fast lookup of the most recently inserted dynamic entry for a given header
+ // name. Entries point to entries owned by |dynamic_entries_|.
+ NameToEntryMap dynamic_name_index_;
+
+ // Size of the dynamic table. This is the sum of the size of its entries.
+ uint64_t dynamic_table_size_;
+
+ // Dynamic Table Capacity is the maximum allowed value of
+ // |dynamic_table_size_|. Entries are evicted if necessary before inserting a
+ // new entry to ensure that dynamic table size never exceeds capacity.
+ // Initial value is |maximum_dynamic_table_capacity_|. Capacity can be
+ // changed by the encoder, as long as it does not exceed
+ // |maximum_dynamic_table_capacity_|.
+ uint64_t dynamic_table_capacity_;
+
+ // Maximum allowed value of |dynamic_table_capacity|. The initial value is
+ // zero. Can be changed by SetMaximumDynamicTableCapacity().
+ uint64_t maximum_dynamic_table_capacity_;
+
+ // MaxEntries, see Section 3.2.2. Calculated based on
+ // |maximum_dynamic_table_capacity_|.
+ uint64_t max_entries_;
+
+ // The number of entries dropped from the dynamic table.
+ uint64_t dropped_entry_count_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc
new file mode 100644
index 00000000000..f3ac5b5d3ef
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc
@@ -0,0 +1,356 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const uint64_t kMaximumDynamicTableCapacityForTesting = 1024 * 1024;
+
+class QpackHeaderTableTest : public QuicTest {
+ protected:
+ QpackHeaderTableTest() {
+ table_.SetMaximumDynamicTableCapacity(
+ kMaximumDynamicTableCapacityForTesting);
+ }
+ ~QpackHeaderTableTest() override = default;
+
+ void ExpectEntryAtIndex(bool is_static,
+ uint64_t index,
+ QuicStringPiece expected_name,
+ QuicStringPiece expected_value) const {
+ const auto* entry = table_.LookupEntry(is_static, index);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(expected_name, entry->name());
+ EXPECT_EQ(expected_value, entry->value());
+ }
+
+ void ExpectNoEntryAtIndex(bool is_static, uint64_t index) const {
+ EXPECT_FALSE(table_.LookupEntry(is_static, index));
+ }
+
+ void ExpectMatch(QuicStringPiece name,
+ QuicStringPiece value,
+ QpackHeaderTable::MatchType expected_match_type,
+ bool expected_is_static,
+ uint64_t expected_index) const {
+ // Initialize outparams to a value different from the expected to ensure
+ // that FindHeaderField() sets them.
+ bool is_static = !expected_is_static;
+ uint64_t index = expected_index + 1;
+
+ QpackHeaderTable::MatchType matchtype =
+ table_.FindHeaderField(name, value, &is_static, &index);
+
+ EXPECT_EQ(expected_match_type, matchtype) << name << ": " << value;
+ EXPECT_EQ(expected_is_static, is_static) << name << ": " << value;
+ EXPECT_EQ(expected_index, index) << name << ": " << value;
+ }
+
+ void ExpectNoMatch(QuicStringPiece name, QuicStringPiece value) const {
+ bool is_static = false;
+ uint64_t index = 0;
+
+ QpackHeaderTable::MatchType matchtype =
+ table_.FindHeaderField(name, value, &is_static, &index);
+
+ EXPECT_EQ(QpackHeaderTable::MatchType::kNoMatch, matchtype)
+ << name << ": " << value;
+ }
+
+ void InsertEntry(QuicStringPiece name, QuicStringPiece value) {
+ EXPECT_TRUE(table_.InsertEntry(name, value));
+ }
+
+ void ExpectToFailInsertingEntry(QuicStringPiece name, QuicStringPiece value) {
+ EXPECT_FALSE(table_.InsertEntry(name, value));
+ }
+
+ bool SetDynamicTableCapacity(uint64_t capacity) {
+ return table_.SetDynamicTableCapacity(capacity);
+ }
+
+ uint64_t max_entries() const { return table_.max_entries(); }
+ uint64_t inserted_entry_count() const {
+ return table_.inserted_entry_count();
+ }
+ uint64_t dropped_entry_count() const { return table_.dropped_entry_count(); }
+
+ private:
+ QpackHeaderTable table_;
+};
+
+TEST_F(QpackHeaderTableTest, LookupStaticEntry) {
+ ExpectEntryAtIndex(/* is_static = */ true, 0, ":authority", "");
+
+ ExpectEntryAtIndex(/* is_static = */ true, 1, ":path", "/");
+
+ // 98 is the last entry.
+ ExpectEntryAtIndex(/* is_static = */ true, 98, "x-frame-options",
+ "sameorigin");
+
+ ASSERT_EQ(99u, QpackStaticTableVector().size());
+ ExpectNoEntryAtIndex(/* is_static = */ true, 99);
+}
+
+TEST_F(QpackHeaderTableTest, InsertAndLookupDynamicEntry) {
+ // Dynamic table is initially entry.
+ ExpectNoEntryAtIndex(/* is_static = */ false, 0);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 1);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 2);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 3);
+
+ // Insert one entry.
+ InsertEntry("foo", "bar");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar");
+
+ ExpectNoEntryAtIndex(/* is_static = */ false, 1);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 2);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 3);
+
+ // Insert a different entry.
+ InsertEntry("baz", "bing");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing");
+
+ ExpectNoEntryAtIndex(/* is_static = */ false, 2);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 3);
+
+ // Insert an entry identical to the most recently inserted one.
+ InsertEntry("baz", "bing");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 2, "baz", "bing");
+
+ ExpectNoEntryAtIndex(/* is_static = */ false, 3);
+}
+
+TEST_F(QpackHeaderTableTest, FindStaticHeaderField) {
+ // A header name that has multiple entries with different values.
+ ExpectMatch(":method", "GET", QpackHeaderTable::MatchType::kNameAndValue,
+ true, 17u);
+
+ ExpectMatch(":method", "POST", QpackHeaderTable::MatchType::kNameAndValue,
+ true, 20u);
+
+ ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kName, true,
+ 15u);
+
+ // A header name that has a single entry with non-empty value.
+ ExpectMatch("accept-encoding", "gzip, deflate, br",
+ QpackHeaderTable::MatchType::kNameAndValue, true, 31u);
+
+ ExpectMatch("accept-encoding", "compress", QpackHeaderTable::MatchType::kName,
+ true, 31u);
+
+ ExpectMatch("accept-encoding", "", QpackHeaderTable::MatchType::kName, true,
+ 31u);
+
+ // A header name that has a single entry with empty value.
+ ExpectMatch("location", "", QpackHeaderTable::MatchType::kNameAndValue, true,
+ 12u);
+
+ ExpectMatch("location", "foo", QpackHeaderTable::MatchType::kName, true, 12u);
+
+ // No matching header name.
+ ExpectNoMatch("foo", "");
+ ExpectNoMatch("foo", "bar");
+}
+
+TEST_F(QpackHeaderTableTest, FindDynamicHeaderField) {
+ // Dynamic table is initially entry.
+ ExpectNoMatch("foo", "bar");
+ ExpectNoMatch("foo", "baz");
+
+ // Insert one entry.
+ InsertEntry("foo", "bar");
+
+ // Match name and value.
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, false,
+ 0u);
+
+ // Match name only.
+ ExpectMatch("foo", "baz", QpackHeaderTable::MatchType::kName, false, 0u);
+
+ // Insert an identical entry. FindHeaderField() should return the index of
+ // the most recently inserted matching entry.
+ InsertEntry("foo", "bar");
+
+ // Match name and value.
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, false,
+ 1u);
+
+ // Match name only.
+ ExpectMatch("foo", "baz", QpackHeaderTable::MatchType::kName, false, 1u);
+}
+
+TEST_F(QpackHeaderTableTest, FindHeaderFieldPrefersStaticTable) {
+ // Insert an entry to the dynamic table that exists in the static table.
+ InsertEntry(":method", "GET");
+
+ // Insertion works.
+ ExpectEntryAtIndex(/* is_static = */ false, 0, ":method", "GET");
+
+ // FindHeaderField() prefers static table if both have name-and-value match.
+ ExpectMatch(":method", "GET", QpackHeaderTable::MatchType::kNameAndValue,
+ true, 17u);
+
+ // FindHeaderField() prefers static table if both have name match but no value
+ // match, and prefers the first entry with matching name.
+ ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kName, true,
+ 15u);
+
+ // Add new entry to the dynamic table.
+ InsertEntry(":method", "TRACE");
+
+ // FindHeaderField prefers name-and-value match in dynamic table over name
+ // only match in static table.
+ ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kNameAndValue,
+ false, 1u);
+}
+
+// MaxEntries is determined by maximum dynamic table capacity,
+// which is set at construction time.
+TEST_F(QpackHeaderTableTest, MaxEntries) {
+ QpackHeaderTable table1;
+ table1.SetMaximumDynamicTableCapacity(1024);
+ EXPECT_EQ(32u, table1.max_entries());
+
+ QpackHeaderTable table2;
+ table2.SetMaximumDynamicTableCapacity(500);
+ EXPECT_EQ(15u, table2.max_entries());
+}
+
+TEST_F(QpackHeaderTableTest, SetDynamicTableCapacity) {
+ // Dynamic table capacity does not affect MaxEntries.
+ EXPECT_TRUE(SetDynamicTableCapacity(1024));
+ EXPECT_EQ(32u * 1024, max_entries());
+
+ EXPECT_TRUE(SetDynamicTableCapacity(500));
+ EXPECT_EQ(32u * 1024, max_entries());
+
+ // Dynamic table capacity cannot exceed maximum dynamic table capacity.
+ EXPECT_FALSE(
+ SetDynamicTableCapacity(2 * kMaximumDynamicTableCapacityForTesting));
+}
+
+TEST_F(QpackHeaderTableTest, EvictByInsertion) {
+ EXPECT_TRUE(SetDynamicTableCapacity(40));
+
+ // Entry size is 3 + 3 + 32 = 38.
+ InsertEntry("foo", "bar");
+ EXPECT_EQ(1u, inserted_entry_count());
+ EXPECT_EQ(0u, dropped_entry_count());
+
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 0u);
+
+ // Inserting second entry evicts the first one.
+ InsertEntry("baz", "qux");
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(1u, dropped_entry_count());
+
+ ExpectNoMatch("foo", "bar");
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+
+ // Inserting an entry that does not fit results in error.
+ ExpectToFailInsertingEntry("foobar", "foobar");
+}
+
+TEST_F(QpackHeaderTableTest, EvictByUpdateTableSize) {
+ // Entry size is 3 + 3 + 32 = 38.
+ InsertEntry("foo", "bar");
+ InsertEntry("baz", "qux");
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(0u, dropped_entry_count());
+
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 0u);
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+
+ EXPECT_TRUE(SetDynamicTableCapacity(40));
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(1u, dropped_entry_count());
+
+ ExpectNoMatch("foo", "bar");
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+
+ EXPECT_TRUE(SetDynamicTableCapacity(20));
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(2u, dropped_entry_count());
+
+ ExpectNoMatch("foo", "bar");
+ ExpectNoMatch("baz", "qux");
+}
+
+TEST_F(QpackHeaderTableTest, EvictOldestOfIdentical) {
+ EXPECT_TRUE(SetDynamicTableCapacity(80));
+
+ // Entry size is 3 + 3 + 32 = 38.
+ // Insert same entry twice.
+ InsertEntry("foo", "bar");
+ InsertEntry("foo", "bar");
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(0u, dropped_entry_count());
+
+ // Find most recently inserted entry.
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+
+ // Inserting third entry evicts the first one, not the second.
+ InsertEntry("baz", "qux");
+ EXPECT_EQ(3u, inserted_entry_count());
+ EXPECT_EQ(1u, dropped_entry_count());
+
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 2u);
+}
+
+TEST_F(QpackHeaderTableTest, EvictOldestOfSameName) {
+ EXPECT_TRUE(SetDynamicTableCapacity(80));
+
+ // Entry size is 3 + 3 + 32 = 38.
+ // Insert two entries with same name but different values.
+ InsertEntry("foo", "bar");
+ InsertEntry("foo", "baz");
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(0u, dropped_entry_count());
+
+ // Find most recently inserted entry with matching name.
+ ExpectMatch("foo", "foo", QpackHeaderTable::MatchType::kName,
+ /* expected_is_static = */ false, 1u);
+
+ // Inserting third entry evicts the first one, not the second.
+ InsertEntry("baz", "qux");
+ EXPECT_EQ(3u, inserted_entry_count());
+ EXPECT_EQ(1u, dropped_entry_count());
+
+ ExpectMatch("foo", "foo", QpackHeaderTable::MatchType::kName,
+ /* expected_is_static = */ false, 1u);
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 2u);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..2076fa7c990
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc
@@ -0,0 +1,310 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Maximum length of header name and header value. This limits the amount of
+// memory the peer can make the decoder allocate when sending string literals.
+const size_t kStringLiteralLengthLimit = 1024 * 1024;
+
+} // namespace
+
+QpackInstructionDecoder::QpackInstructionDecoder(const QpackLanguage* language,
+ Delegate* delegate)
+ : language_(language),
+ delegate_(delegate),
+ s_bit_(false),
+ varint_(0),
+ varint2_(0),
+ is_huffman_encoded_(false),
+ string_length_(0),
+ error_detected_(false),
+ state_(State::kStartInstruction) {}
+
+void QpackInstructionDecoder::Decode(QuicStringPiece data) {
+ DCHECK(!data.empty());
+ DCHECK(!error_detected_);
+
+ while (true) {
+ size_t bytes_consumed = 0;
+
+ switch (state_) {
+ case State::kStartInstruction:
+ DoStartInstruction(data);
+ break;
+ case State::kStartField:
+ DoStartField();
+ break;
+ case State::kReadBit:
+ DoReadBit(data);
+ break;
+ case State::kVarintStart:
+ bytes_consumed = DoVarintStart(data);
+ break;
+ case State::kVarintResume:
+ bytes_consumed = DoVarintResume(data);
+ break;
+ case State::kVarintDone:
+ DoVarintDone();
+ break;
+ case State::kReadString:
+ bytes_consumed = DoReadString(data);
+ break;
+ case State::kReadStringDone:
+ DoReadStringDone();
+ break;
+ }
+
+ if (error_detected_) {
+ return;
+ }
+
+ DCHECK_LE(bytes_consumed, data.size());
+
+ data = QuicStringPiece(data.data() + bytes_consumed,
+ data.size() - bytes_consumed);
+
+ // Stop processing if no more data but next state would require it.
+ if (data.empty() && (state_ != State::kStartField) &&
+ (state_ != State::kVarintDone) && (state_ != State::kReadStringDone)) {
+ return;
+ }
+ }
+}
+
+bool QpackInstructionDecoder::AtInstructionBoundary() const {
+ return state_ == State::kStartInstruction;
+}
+
+void QpackInstructionDecoder::DoStartInstruction(QuicStringPiece data) {
+ DCHECK(!data.empty());
+
+ instruction_ = LookupOpcode(data[0]);
+ field_ = instruction_->fields.begin();
+
+ state_ = State::kStartField;
+}
+
+void QpackInstructionDecoder::DoStartField() {
+ if (field_ == instruction_->fields.end()) {
+ // Completed decoding this instruction.
+
+ if (!delegate_->OnInstructionDecoded(instruction_)) {
+ error_detected_ = true;
+ return;
+ }
+
+ state_ = State::kStartInstruction;
+ return;
+ }
+
+ switch (field_->type) {
+ case QpackInstructionFieldType::kSbit:
+ case QpackInstructionFieldType::kName:
+ case QpackInstructionFieldType::kValue:
+ state_ = State::kReadBit;
+ return;
+ case QpackInstructionFieldType::kVarint:
+ case QpackInstructionFieldType::kVarint2:
+ state_ = State::kVarintStart;
+ return;
+ }
+}
+
+void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) {
+ DCHECK(!data.empty());
+
+ switch (field_->type) {
+ case QpackInstructionFieldType::kSbit: {
+ const uint8_t bitmask = field_->param;
+ s_bit_ = (data[0] & bitmask) == bitmask;
+
+ ++field_;
+ state_ = State::kStartField;
+
+ return;
+ }
+ case QpackInstructionFieldType::kName:
+ case QpackInstructionFieldType::kValue: {
+ const uint8_t prefix_length = field_->param;
+ DCHECK_GE(7, prefix_length);
+ const uint8_t bitmask = 1 << prefix_length;
+ is_huffman_encoded_ = (data[0] & bitmask) == bitmask;
+
+ state_ = State::kVarintStart;
+
+ return;
+ }
+ default:
+ DCHECK(false);
+ }
+}
+
+size_t QpackInstructionDecoder::DoVarintStart(QuicStringPiece data) {
+ DCHECK(!data.empty());
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+ http2::DecodeStatus status =
+ varint_decoder_.Start(data[0], field_->param, &buffer);
+
+ size_t bytes_consumed = 1 + buffer.Offset();
+ switch (status) {
+ case http2::DecodeStatus::kDecodeDone:
+ state_ = State::kVarintDone;
+ return bytes_consumed;
+ case http2::DecodeStatus::kDecodeInProgress:
+ state_ = State::kVarintResume;
+ return bytes_consumed;
+ case http2::DecodeStatus::kDecodeError:
+ OnError("Encoded integer too large.");
+ return bytes_consumed;
+ }
+}
+
+size_t QpackInstructionDecoder::DoVarintResume(QuicStringPiece data) {
+ DCHECK(!data.empty());
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ http2::DecodeBuffer buffer(data);
+ http2::DecodeStatus status = varint_decoder_.Resume(&buffer);
+
+ size_t bytes_consumed = buffer.Offset();
+ switch (status) {
+ case http2::DecodeStatus::kDecodeDone:
+ state_ = State::kVarintDone;
+ return bytes_consumed;
+ case http2::DecodeStatus::kDecodeInProgress:
+ DCHECK_EQ(bytes_consumed, data.size());
+ DCHECK(buffer.Empty());
+ return bytes_consumed;
+ case http2::DecodeStatus::kDecodeError:
+ OnError("Encoded integer too large.");
+ return bytes_consumed;
+ }
+}
+
+void QpackInstructionDecoder::DoVarintDone() {
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ if (field_->type == QpackInstructionFieldType::kVarint) {
+ varint_ = varint_decoder_.value();
+
+ ++field_;
+ state_ = State::kStartField;
+ return;
+ }
+
+ if (field_->type == QpackInstructionFieldType::kVarint2) {
+ varint2_ = varint_decoder_.value();
+
+ ++field_;
+ state_ = State::kStartField;
+ return;
+ }
+
+ string_length_ = varint_decoder_.value();
+ if (string_length_ > kStringLiteralLengthLimit) {
+ OnError("String literal too long.");
+ return;
+ }
+
+ std::string* const string =
+ (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+ string->clear();
+
+ if (string_length_ == 0) {
+ ++field_;
+ state_ = State::kStartField;
+ return;
+ }
+
+ string->reserve(string_length_);
+
+ state_ = State::kReadString;
+}
+
+size_t QpackInstructionDecoder::DoReadString(QuicStringPiece data) {
+ DCHECK(!data.empty());
+ DCHECK(field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ std::string* const string =
+ (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+ DCHECK_LT(string->size(), string_length_);
+
+ size_t bytes_consumed =
+ std::min(string_length_ - string->size(), data.size());
+ string->append(data.data(), bytes_consumed);
+
+ DCHECK_LE(string->size(), string_length_);
+ if (string->size() == string_length_) {
+ state_ = State::kReadStringDone;
+ }
+ return bytes_consumed;
+}
+
+void QpackInstructionDecoder::DoReadStringDone() {
+ DCHECK(field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ std::string* const string =
+ (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+ DCHECK_EQ(string->size(), string_length_);
+
+ if (is_huffman_encoded_) {
+ huffman_decoder_.Reset();
+ // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
+ std::string decoded_value;
+ huffman_decoder_.Decode(*string, &decoded_value);
+ if (!huffman_decoder_.InputProperlyTerminated()) {
+ OnError("Error in Huffman-encoded string.");
+ return;
+ }
+ *string = std::move(decoded_value);
+ }
+
+ ++field_;
+ state_ = State::kStartField;
+}
+
+const QpackInstruction* QpackInstructionDecoder::LookupOpcode(
+ uint8_t byte) const {
+ for (const auto* instruction : *language_) {
+ if ((byte & instruction->opcode.mask) == instruction->opcode.value) {
+ return instruction;
+ }
+ }
+ // |language_| should be defined such that instruction opcodes cover every
+ // possible input.
+ DCHECK(false);
+ return nullptr;
+}
+
+void QpackInstructionDecoder::OnError(QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ delegate_->OnError(error_message);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h
new file mode 100644
index 00000000000..f478c249b07
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h
@@ -0,0 +1,146 @@
+// Copyright 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_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h"
+#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Generic instruction decoder class. Takes a QpackLanguage that describes a
+// language, that is, a set of instruction opcodes together with a list of
+// fields that follow each instruction.
+class QUIC_EXPORT_PRIVATE QpackInstructionDecoder {
+ public:
+ // Delegate is notified each time an instruction is decoded or when an error
+ // occurs.
+ class QUIC_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Called when an instruction (including all its fields) is decoded.
+ // |instruction| points to an entry in |language|.
+ // Returns true if decoded fields are valid.
+ // Returns false otherwise, in which case QpackInstructionDecoder stops
+ // decoding: Delegate methods will not be called, and Decode() must not be
+ // called.
+ virtual bool OnInstructionDecoded(const QpackInstruction* instruction) = 0;
+
+ // Called by QpackInstructionDecoder if an error has occurred.
+ // No more data is processed afterwards.
+ virtual void OnError(QuicStringPiece error_message) = 0;
+ };
+
+ // Both |*language| and |*delegate| must outlive this object.
+ QpackInstructionDecoder(const QpackLanguage* language, Delegate* delegate);
+ QpackInstructionDecoder() = delete;
+ QpackInstructionDecoder(const QpackInstructionDecoder&) = delete;
+ QpackInstructionDecoder& operator=(const QpackInstructionDecoder&) = delete;
+
+ // Provide a data fragment to decode. Must not be called after an error has
+ // occurred. Must not be called with empty |data|.
+ void Decode(QuicStringPiece data);
+
+ // Returns true if no decoding has taken place yet or if the last instruction
+ // has been entirely parsed.
+ bool AtInstructionBoundary() const;
+
+ // Accessors for decoded values. Should only be called for fields that are
+ // part of the most recently decoded instruction, and only after |this| calls
+ // Delegate::OnInstructionDecoded() but before Decode() is called again.
+ bool s_bit() const { return s_bit_; }
+ uint64_t varint() const { return varint_; }
+ uint64_t varint2() const { return varint2_; }
+ const std::string& name() const { return name_; }
+ const std::string& value() const { return value_; }
+
+ private:
+ enum class State {
+ // Identify instruction.
+ kStartInstruction,
+ // Start decoding next field.
+ kStartField,
+ // Read a single bit.
+ kReadBit,
+ // Start reading integer.
+ kVarintStart,
+ // Resume reading integer.
+ kVarintResume,
+ // Done reading integer.
+ kVarintDone,
+ // Read string.
+ kReadString,
+ // Done reading string.
+ kReadStringDone
+ };
+
+ // One method for each state. Some take input data and return the number of
+ // octets processed. Some take input data but do have void return type
+ // because they not consume any bytes. Some do not take any arguments because
+ // they only change internal state.
+ void DoStartInstruction(QuicStringPiece data);
+ void DoStartField();
+ void DoReadBit(QuicStringPiece data);
+ size_t DoVarintStart(QuicStringPiece data);
+ size_t DoVarintResume(QuicStringPiece data);
+ void DoVarintDone();
+ size_t DoReadString(QuicStringPiece data);
+ void DoReadStringDone();
+
+ // Identify instruction based on opcode encoded in |byte|.
+ // Returns a pointer to an element of |*language_|.
+ const QpackInstruction* LookupOpcode(uint8_t byte) const;
+
+ // Stops decoding and calls Delegate::OnError().
+ void OnError(QuicStringPiece error_message);
+
+ // Describes the language used for decoding.
+ const QpackLanguage* const language_;
+
+ // The Delegate to notify of decoded instructions and errors.
+ Delegate* const delegate_;
+
+ // Storage for decoded field values.
+ bool s_bit_;
+ uint64_t varint_;
+ uint64_t varint2_;
+ std::string name_;
+ std::string value_;
+ // Whether the currently decoded header name or value is Huffman encoded.
+ bool is_huffman_encoded_;
+ // Length of string being read into |name_| or |value_|.
+ size_t string_length_;
+
+ // Decoder instance for decoding integers.
+ http2::HpackVarintDecoder varint_decoder_;
+
+ // Decoder instance for decoding Huffman encoded strings.
+ http2::HpackHuffmanDecoder huffman_decoder_;
+
+ // True if a decoding error has been detected either by
+ // QpackInstructionDecoder or by Delegate.
+ bool error_detected_;
+
+ // Decoding state.
+ State state_;
+
+ // Instruction currently being decoded.
+ const QpackInstruction* instruction_;
+
+ // Field currently being decoded.
+ QpackInstructionFields::const_iterator field_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc
new file mode 100644
index 00000000000..f0d1d1d0564
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc
@@ -0,0 +1,171 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+
+#include <algorithm>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Expectation;
+using ::testing::Return;
+using ::testing::StrictMock;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+// This instruction has three fields: an S bit and two varints.
+const QpackInstruction* TestInstruction1() {
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{QpackInstructionOpcode{0x00, 0x80},
+ {{QpackInstructionFieldType::kSbit, 0x40},
+ {QpackInstructionFieldType::kVarint, 6},
+ {QpackInstructionFieldType::kVarint2, 8}}};
+ return instruction;
+}
+
+// This instruction has two fields: a header name with a 6-bit prefix, and a
+// header value with a 7-bit prefix, both preceded by a Huffman bit.
+const QpackInstruction* TestInstruction2() {
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{QpackInstructionOpcode{0x80, 0x80},
+ {{QpackInstructionFieldType::kName, 6},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackLanguage* TestLanguage() {
+ static const QpackLanguage* const language =
+ new QpackLanguage{TestInstruction1(), TestInstruction2()};
+ return language;
+}
+
+class MockDelegate : public QpackInstructionDecoder::Delegate {
+ public:
+ MockDelegate() {
+ ON_CALL(*this, OnInstructionDecoded(_)).WillByDefault(Return(true));
+ }
+
+ MockDelegate(const MockDelegate&) = delete;
+ MockDelegate& operator=(const MockDelegate&) = delete;
+ ~MockDelegate() override = default;
+
+ MOCK_METHOD1(OnInstructionDecoded, bool(const QpackInstruction* instruction));
+ MOCK_METHOD1(OnError, void(QuicStringPiece error_message));
+};
+
+class QpackInstructionDecoderTest : public QuicTestWithParam<FragmentMode> {
+ public:
+ QpackInstructionDecoderTest()
+ : decoder_(TestLanguage(), &delegate_), fragment_mode_(GetParam()) {}
+ ~QpackInstructionDecoderTest() override = default;
+
+ protected:
+ // Decode one full instruction with fragment sizes dictated by
+ // |fragment_mode_|.
+ // Verifies that AtInstructionBoundary() returns true before and after the
+ // instruction, and returns false while decoding is in progress.
+ void DecodeInstruction(QuicStringPiece data) {
+ EXPECT_TRUE(decoder_.AtInstructionBoundary());
+
+ FragmentSizeGenerator fragment_size_generator =
+ FragmentModeToFragmentSizeGenerator(fragment_mode_);
+
+ while (!data.empty()) {
+ size_t fragment_size = std::min(fragment_size_generator(), data.size());
+ decoder_.Decode(data.substr(0, fragment_size));
+ data = data.substr(fragment_size);
+ if (!data.empty()) {
+ EXPECT_FALSE(decoder_.AtInstructionBoundary());
+ }
+ }
+
+ EXPECT_TRUE(decoder_.AtInstructionBoundary());
+ }
+
+ StrictMock<MockDelegate> delegate_;
+ QpackInstructionDecoder decoder_;
+
+ private:
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ QpackInstructionDecoderTest,
+ Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackInstructionDecoderTest, SBitAndVarint2) {
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
+ DecodeInstruction(QuicTextUtils::HexDecode("7f01ff65"));
+
+ EXPECT_TRUE(decoder_.s_bit());
+ EXPECT_EQ(64u, decoder_.varint());
+ EXPECT_EQ(356u, decoder_.varint2());
+
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
+ DecodeInstruction(QuicTextUtils::HexDecode("05c8"));
+
+ EXPECT_FALSE(decoder_.s_bit());
+ EXPECT_EQ(5u, decoder_.varint());
+ EXPECT_EQ(200u, decoder_.varint2());
+}
+
+TEST_P(QpackInstructionDecoderTest, NameAndValue) {
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+ DecodeInstruction(QuicTextUtils::HexDecode("83666f6f03626172"));
+
+ EXPECT_EQ("foo", decoder_.name());
+ EXPECT_EQ("bar", decoder_.value());
+
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+ DecodeInstruction(QuicTextUtils::HexDecode("8000"));
+
+ EXPECT_EQ("", decoder_.name());
+ EXPECT_EQ("", decoder_.value());
+
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+ DecodeInstruction(QuicTextUtils::HexDecode("c294e7838c767f"));
+
+ EXPECT_EQ("foo", decoder_.name());
+ EXPECT_EQ("bar", decoder_.value());
+}
+
+TEST_P(QpackInstructionDecoderTest, InvalidHuffmanEncoding) {
+ EXPECT_CALL(delegate_, OnError(Eq("Error in Huffman-encoded string.")));
+ decoder_.Decode(QuicTextUtils::HexDecode("c1ff"));
+}
+
+TEST_P(QpackInstructionDecoderTest, InvalidVarintEncoding) {
+ EXPECT_CALL(delegate_, OnError(Eq("Encoded integer too large.")));
+ decoder_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff"));
+}
+
+TEST_P(QpackInstructionDecoderTest, DelegateSignalsError) {
+ // First instruction is valid.
+ Expectation first_call =
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
+ .WillOnce(Return(true));
+ // Second instruction is invalid. Decoding must halt.
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
+ .After(first_call)
+ .WillOnce(Return(false));
+ decoder_.Decode(QuicTextUtils::HexDecode("01000200030004000500"));
+
+ EXPECT_EQ(2u, decoder_.varint());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc
new file mode 100644
index 00000000000..3ae38f65697
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc
@@ -0,0 +1,217 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+
+#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h"
+
+namespace quic {
+
+QpackInstructionEncoder::QpackInstructionEncoder()
+ : s_bit_(false),
+ varint_(0),
+ varint2_(0),
+ byte_(0),
+ state_(State::kOpcode),
+ instruction_(nullptr) {}
+
+void QpackInstructionEncoder::Encode(const QpackInstruction* instruction) {
+ DCHECK(!HasNext());
+
+ state_ = State::kOpcode;
+ instruction_ = instruction;
+ field_ = instruction_->fields.begin();
+
+ // Field list must not be empty.
+ DCHECK(field_ != instruction_->fields.end());
+}
+
+bool QpackInstructionEncoder::HasNext() const {
+ return instruction_ && (field_ != instruction_->fields.end());
+}
+
+void QpackInstructionEncoder::Next(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK(HasNext());
+ DCHECK_NE(0u, max_encoded_bytes);
+
+ while (max_encoded_bytes > 0 && HasNext()) {
+ size_t encoded_bytes = 0;
+
+ switch (state_) {
+ case State::kOpcode:
+ DoOpcode();
+ break;
+ case State::kStartField:
+ DoStartField();
+ break;
+ case State::kSbit:
+ DoStaticBit();
+ break;
+ case State::kVarintStart:
+ encoded_bytes = DoVarintStart(max_encoded_bytes, output);
+ break;
+ case State::kVarintResume:
+ encoded_bytes = DoVarintResume(max_encoded_bytes, output);
+ break;
+ case State::kStartString:
+ DoStartString();
+ break;
+ case State::kWriteString:
+ encoded_bytes = DoWriteString(max_encoded_bytes, output);
+ break;
+ }
+
+ DCHECK_LE(encoded_bytes, max_encoded_bytes);
+ max_encoded_bytes -= encoded_bytes;
+ }
+}
+
+void QpackInstructionEncoder::DoOpcode() {
+ DCHECK_EQ(0u, byte_);
+
+ byte_ = instruction_->opcode.value;
+
+ state_ = State::kStartField;
+}
+
+void QpackInstructionEncoder::DoStartField() {
+ switch (field_->type) {
+ case QpackInstructionFieldType::kSbit:
+ state_ = State::kSbit;
+ return;
+ case QpackInstructionFieldType::kVarint:
+ case QpackInstructionFieldType::kVarint2:
+ state_ = State::kVarintStart;
+ return;
+ case QpackInstructionFieldType::kName:
+ case QpackInstructionFieldType::kValue:
+ state_ = State::kStartString;
+ return;
+ }
+}
+
+void QpackInstructionEncoder::DoStaticBit() {
+ DCHECK(field_->type == QpackInstructionFieldType::kSbit);
+
+ if (s_bit_) {
+ DCHECK_EQ(0, byte_ & field_->param);
+
+ byte_ |= field_->param;
+ }
+
+ ++field_;
+ state_ = State::kStartField;
+}
+
+size_t QpackInstructionEncoder::DoVarintStart(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+ DCHECK(!varint_encoder_.IsEncodingInProgress());
+
+ uint64_t integer_to_encode;
+ switch (field_->type) {
+ case QpackInstructionFieldType::kVarint:
+ integer_to_encode = varint_;
+ break;
+ case QpackInstructionFieldType::kVarint2:
+ integer_to_encode = varint2_;
+ break;
+ default:
+ integer_to_encode = string_to_write_.size();
+ break;
+ }
+
+ output->push_back(
+ varint_encoder_.StartEncoding(byte_, field_->param, integer_to_encode));
+ byte_ = 0;
+
+ if (varint_encoder_.IsEncodingInProgress()) {
+ state_ = State::kVarintResume;
+ return 1;
+ }
+
+ if (field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2) {
+ ++field_;
+ state_ = State::kStartField;
+ return 1;
+ }
+
+ state_ = State::kWriteString;
+ return 1;
+}
+
+size_t QpackInstructionEncoder::DoVarintResume(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+ DCHECK(varint_encoder_.IsEncodingInProgress());
+
+ const size_t encoded_bytes =
+ varint_encoder_.ResumeEncoding(max_encoded_bytes, output);
+ if (varint_encoder_.IsEncodingInProgress()) {
+ DCHECK_EQ(encoded_bytes, max_encoded_bytes);
+ return encoded_bytes;
+ }
+
+ DCHECK_LE(encoded_bytes, max_encoded_bytes);
+
+ if (field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2) {
+ ++field_;
+ state_ = State::kStartField;
+ return encoded_bytes;
+ }
+
+ state_ = State::kWriteString;
+ return encoded_bytes;
+}
+
+void QpackInstructionEncoder::DoStartString() {
+ DCHECK(field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ string_to_write_ =
+ (field_->type == QpackInstructionFieldType::kName) ? name_ : value_;
+ http2::HuffmanEncode(string_to_write_, &huffman_encoded_string_);
+
+ if (huffman_encoded_string_.size() < string_to_write_.size()) {
+ DCHECK_EQ(0, byte_ & (1 << field_->param));
+
+ byte_ |= (1 << field_->param);
+ string_to_write_ = huffman_encoded_string_;
+ }
+
+ state_ = State::kVarintStart;
+}
+
+size_t QpackInstructionEncoder::DoWriteString(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK(field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ if (max_encoded_bytes < string_to_write_.size()) {
+ const size_t encoded_bytes = max_encoded_bytes;
+ QuicStrAppend(output, string_to_write_.substr(0, encoded_bytes));
+ string_to_write_ = string_to_write_.substr(encoded_bytes);
+ return encoded_bytes;
+ }
+
+ const size_t encoded_bytes = string_to_write_.size();
+ QuicStrAppend(output, string_to_write_);
+
+ ++field_;
+ state_ = State::kStartField;
+ return encoded_bytes;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h
new file mode 100644
index 00000000000..917378ed8f3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h
@@ -0,0 +1,118 @@
+// Copyright 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_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Generic instruction encoder class. Takes a QpackLanguage that describes a
+// language, that is, a set of instruction opcodes together with a list of
+// fields that follow each instruction.
+class QUIC_EXPORT_PRIVATE QpackInstructionEncoder {
+ public:
+ QpackInstructionEncoder();
+ QpackInstructionEncoder(const QpackInstructionEncoder&) = delete;
+ QpackInstructionEncoder& operator=(const QpackInstructionEncoder&) = delete;
+
+ // Setters for values to be encoded.
+ // |name| and |value| must remain valid until the instruction is encoded.
+ void set_s_bit(bool s_bit) { s_bit_ = s_bit; }
+ void set_varint(uint64_t varint) { varint_ = varint; }
+ void set_varint2(uint64_t varint2) { varint2_ = varint2; }
+ void set_name(QuicStringPiece name) { name_ = name; }
+ void set_value(QuicStringPiece value) { value_ = value; }
+
+ // Start encoding an instruction. Must only be called after the previous
+ // instruction has been completely encoded.
+ void Encode(const QpackInstruction* instruction);
+
+ // Returns true iff more data remains to be encoded for the current
+ // instruction. Returns false if there is no current instruction, that is, if
+ // Encode() has never been called.
+ bool HasNext() const;
+
+ // Encodes the next up to |max_encoded_bytes| octets of the current
+ // instruction, appending to |output|. Must only be called when HasNext()
+ // returns true. |max_encoded_bytes| must be positive.
+ void Next(size_t max_encoded_bytes, std::string* output);
+
+ private:
+ enum class State {
+ // Write instruction opcode to |byte_|.
+ kOpcode,
+ // Select state based on type of current field.
+ kStartField,
+ // Write static bit to |byte_|.
+ kSbit,
+ // Start encoding an integer (|varint_| or |varint2_| or string length) with
+ // a prefix, using |byte_| for the high bits.
+ kVarintStart,
+ // Resume encoding an integer.
+ kVarintResume,
+ // Determine if Huffman encoding should be used for |name_| or |value_|, set
+ // up |name_| or |value_| and |huffman_encoded_string_| accordingly, and
+ // write the Huffman bit to |byte_|.
+ kStartString,
+ // Write string.
+ kWriteString
+ };
+
+ // One method for each state. Some encode up to |max_encoded_bytes| octets,
+ // appending to |output|. Some only change internal state.
+ void DoOpcode();
+ void DoStartField();
+ void DoStaticBit();
+ size_t DoVarintStart(size_t max_encoded_bytes, std::string* output);
+ size_t DoVarintResume(size_t max_encoded_bytes, std::string* output);
+ void DoStartString();
+ size_t DoWriteString(size_t max_encoded_bytes, std::string* output);
+
+ // Storage for field values to be encoded.
+ bool s_bit_;
+ uint64_t varint_;
+ uint64_t varint2_;
+ // The caller must keep the string that |name_| and |value_| point to
+ // valid until they are encoded.
+ QuicStringPiece name_;
+ QuicStringPiece value_;
+
+ // Storage for the Huffman encoded string literal to be written if Huffman
+ // encoding is used.
+ std::string huffman_encoded_string_;
+
+ // If Huffman encoding is used, points to a substring of
+ // |huffman_encoded_string_|.
+ // Otherwise points to a substring of |name_| or |value_|.
+ QuicStringPiece string_to_write_;
+
+ // Storage for a single byte that contains multiple fields, that is, multiple
+ // states are writing it.
+ uint8_t byte_;
+
+ // Encoding state.
+ State state_;
+
+ // Instruction currently being decoded.
+ const QpackInstruction* instruction_;
+
+ // Field currently being decoded.
+ QpackInstructionFields::const_iterator field_;
+
+ // Decoder instance for decoding integers.
+ http2::HpackVarintEncoder varint_encoder_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc
new file mode 100644
index 00000000000..8f6aeaab97a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc
@@ -0,0 +1,152 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackInstructionEncoderTest : public QuicTestWithParam<FragmentMode> {
+ protected:
+ QpackInstructionEncoderTest() : fragment_mode_(GetParam()) {}
+ ~QpackInstructionEncoderTest() override = default;
+
+ // Encode |instruction| with fragment sizes dictated by |fragment_mode_|.
+ std::string EncodeInstruction(const QpackInstruction* instruction) {
+ EXPECT_FALSE(encoder_.HasNext());
+
+ FragmentSizeGenerator fragment_size_generator =
+ FragmentModeToFragmentSizeGenerator(fragment_mode_);
+ std::string output;
+ encoder_.Encode(instruction);
+ while (encoder_.HasNext()) {
+ encoder_.Next(fragment_size_generator(), &output);
+ }
+
+ return output;
+ }
+
+ QpackInstructionEncoder encoder_;
+
+ private:
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ QpackInstructionEncoderTest,
+ Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackInstructionEncoderTest, Varint) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0x00, 0x80},
+ {{QpackInstructionFieldType::kVarint, 7}}};
+
+ encoder_.set_varint(5);
+ EXPECT_EQ(QuicTextUtils::HexDecode("05"), EncodeInstruction(&instruction));
+
+ encoder_.set_varint(127);
+ EXPECT_EQ(QuicTextUtils::HexDecode("7f00"), EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, SBitAndTwoVarint2) {
+ const QpackInstruction instruction{
+ QpackInstructionOpcode{0x80, 0xc0},
+ {{QpackInstructionFieldType::kSbit, 0x20},
+ {QpackInstructionFieldType::kVarint, 5},
+ {QpackInstructionFieldType::kVarint2, 8}}};
+
+ encoder_.set_s_bit(true);
+ encoder_.set_varint(5);
+ encoder_.set_varint2(200);
+ EXPECT_EQ(QuicTextUtils::HexDecode("a5c8"), EncodeInstruction(&instruction));
+
+ encoder_.set_s_bit(false);
+ encoder_.set_varint(31);
+ encoder_.set_varint2(356);
+ EXPECT_EQ(QuicTextUtils::HexDecode("9f00ff65"),
+ EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, SBitAndVarintAndValue) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0xc0, 0xc0},
+ {{QpackInstructionFieldType::kSbit, 0x20},
+ {QpackInstructionFieldType::kVarint, 5},
+ {QpackInstructionFieldType::kValue, 7}}};
+
+ encoder_.set_s_bit(true);
+ encoder_.set_varint(100);
+ encoder_.set_value("foo");
+ EXPECT_EQ(QuicTextUtils::HexDecode("ff458294e7"),
+ EncodeInstruction(&instruction));
+
+ encoder_.set_s_bit(false);
+ encoder_.set_varint(3);
+ encoder_.set_value("bar");
+ EXPECT_EQ(QuicTextUtils::HexDecode("c303626172"),
+ EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, Name) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0xe0, 0xe0},
+ {{QpackInstructionFieldType::kName, 4}}};
+
+ encoder_.set_name("");
+ EXPECT_EQ(QuicTextUtils::HexDecode("e0"), EncodeInstruction(&instruction));
+
+ encoder_.set_name("foo");
+ EXPECT_EQ(QuicTextUtils::HexDecode("f294e7"),
+ EncodeInstruction(&instruction));
+
+ encoder_.set_name("bar");
+ EXPECT_EQ(QuicTextUtils::HexDecode("e3626172"),
+ EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, Value) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0},
+ {{QpackInstructionFieldType::kValue, 3}}};
+
+ encoder_.set_value("");
+ EXPECT_EQ(QuicTextUtils::HexDecode("f0"), EncodeInstruction(&instruction));
+
+ encoder_.set_value("foo");
+ EXPECT_EQ(QuicTextUtils::HexDecode("fa94e7"),
+ EncodeInstruction(&instruction));
+
+ encoder_.set_value("bar");
+ EXPECT_EQ(QuicTextUtils::HexDecode("f3626172"),
+ EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, SBitAndNameAndValue) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0},
+ {{QpackInstructionFieldType::kSbit, 0x08},
+ {QpackInstructionFieldType::kName, 2},
+ {QpackInstructionFieldType::kValue, 7}}};
+
+ encoder_.set_s_bit(false);
+ encoder_.set_name("");
+ encoder_.set_value("");
+ EXPECT_EQ(QuicTextUtils::HexDecode("f000"), EncodeInstruction(&instruction));
+
+ encoder_.set_s_bit(true);
+ encoder_.set_name("foo");
+ encoder_.set_value("bar");
+ EXPECT_EQ(QuicTextUtils::HexDecode("fe94e703626172"),
+ EncodeInstruction(&instruction));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc
new file mode 100644
index 00000000000..05a5fc03d8a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc
@@ -0,0 +1,369 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+QpackProgressiveDecoder::QpackProgressiveDecoder(
+ QuicStreamId stream_id,
+ QpackHeaderTable* header_table,
+ QpackDecoderStreamSender* decoder_stream_sender,
+ HeadersHandlerInterface* handler)
+ : stream_id_(stream_id),
+ prefix_decoder_(
+ QuicMakeUnique<QpackInstructionDecoder>(QpackPrefixLanguage(), this)),
+ instruction_decoder_(QpackRequestStreamLanguage(), this),
+ header_table_(header_table),
+ decoder_stream_sender_(decoder_stream_sender),
+ handler_(handler),
+ required_insert_count_(0),
+ base_(0),
+ required_insert_count_so_far_(0),
+ prefix_decoded_(false),
+ decoding_(true),
+ error_detected_(false) {}
+
+// static
+bool QpackProgressiveDecoder::DecodeRequiredInsertCount(
+ uint64_t encoded_required_insert_count,
+ uint64_t max_entries,
+ uint64_t total_number_of_inserts,
+ uint64_t* required_insert_count) {
+ if (encoded_required_insert_count == 0) {
+ *required_insert_count = 0;
+ return true;
+ }
+
+ // |max_entries| is calculated by dividing an unsigned 64-bit integer by 32,
+ // precluding all calculations in this method from overflowing.
+ DCHECK_LE(max_entries, std::numeric_limits<uint64_t>::max() / 32);
+
+ if (encoded_required_insert_count > 2 * max_entries) {
+ return false;
+ }
+
+ *required_insert_count = encoded_required_insert_count - 1;
+ DCHECK_LT(*required_insert_count, std::numeric_limits<uint64_t>::max() / 16);
+
+ uint64_t current_wrapped = total_number_of_inserts % (2 * max_entries);
+ DCHECK_LT(current_wrapped, std::numeric_limits<uint64_t>::max() / 16);
+
+ if (current_wrapped >= *required_insert_count + max_entries) {
+ // Required Insert Count wrapped around 1 extra time.
+ *required_insert_count += 2 * max_entries;
+ } else if (current_wrapped + max_entries < *required_insert_count) {
+ // Decoder wrapped around 1 extra time.
+ current_wrapped += 2 * max_entries;
+ }
+
+ if (*required_insert_count >
+ std::numeric_limits<uint64_t>::max() - total_number_of_inserts) {
+ return false;
+ }
+
+ *required_insert_count += total_number_of_inserts;
+
+ // Prevent underflow, also disallow invalid value 0 for Required Insert Count.
+ if (current_wrapped >= *required_insert_count) {
+ return false;
+ }
+
+ *required_insert_count -= current_wrapped;
+
+ return true;
+}
+
+void QpackProgressiveDecoder::Decode(QuicStringPiece data) {
+ DCHECK(decoding_);
+
+ if (data.empty() || error_detected_) {
+ return;
+ }
+
+ // Decode prefix byte by byte until the first (and only) instruction is
+ // decoded.
+ while (!prefix_decoded_) {
+ prefix_decoder_->Decode(data.substr(0, 1));
+ data = data.substr(1);
+ if (data.empty()) {
+ return;
+ }
+ }
+
+ instruction_decoder_.Decode(data);
+}
+
+void QpackProgressiveDecoder::EndHeaderBlock() {
+ DCHECK(decoding_);
+ decoding_ = false;
+
+ if (error_detected_) {
+ return;
+ }
+
+ if (!instruction_decoder_.AtInstructionBoundary()) {
+ OnError("Incomplete header block.");
+ return;
+ }
+
+ if (!prefix_decoded_) {
+ OnError("Incomplete header data prefix.");
+ return;
+ }
+
+ if (required_insert_count_ != required_insert_count_so_far_) {
+ OnError("Required Insert Count too large.");
+ return;
+ }
+
+ decoder_stream_sender_->SendHeaderAcknowledgement(stream_id_);
+ handler_->OnDecodingCompleted();
+}
+
+bool QpackProgressiveDecoder::OnInstructionDecoded(
+ const QpackInstruction* instruction) {
+ if (instruction == QpackIndexedHeaderFieldInstruction()) {
+ return DoIndexedHeaderFieldInstruction();
+ }
+ if (instruction == QpackIndexedHeaderFieldPostBaseInstruction()) {
+ return DoIndexedHeaderFieldPostBaseInstruction();
+ }
+ if (instruction == QpackLiteralHeaderFieldNameReferenceInstruction()) {
+ return DoLiteralHeaderFieldNameReferenceInstruction();
+ }
+ if (instruction == QpackLiteralHeaderFieldPostBaseInstruction()) {
+ return DoLiteralHeaderFieldPostBaseInstruction();
+ }
+ if (instruction == QpackLiteralHeaderFieldInstruction()) {
+ return DoLiteralHeaderFieldInstruction();
+ }
+ DCHECK_EQ(instruction, QpackPrefixInstruction());
+ return DoPrefixInstruction();
+}
+
+void QpackProgressiveDecoder::OnError(QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ handler_->OnDecodingErrorDetected(error_message);
+}
+
+bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() {
+ if (!instruction_decoder_.s_bit()) {
+ uint64_t absolute_index;
+ if (!RequestStreamRelativeIndexToAbsoluteIndex(
+ instruction_decoder_.varint(), &absolute_index)) {
+ OnError("Invalid relative index.");
+ return false;
+ }
+
+ if (absolute_index >= required_insert_count_) {
+ OnError("Absolute Index must be smaller than Required Insert Count.");
+ return false;
+ }
+
+ DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+ required_insert_count_so_far_ =
+ std::max(required_insert_count_so_far_, absolute_index + 1);
+
+ auto entry =
+ header_table_->LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ OnError("Dynamic table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), entry->value());
+ return true;
+ }
+
+ auto entry = header_table_->LookupEntry(/* is_static = */ true,
+ instruction_decoder_.varint());
+ if (!entry) {
+ OnError("Static table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), entry->value());
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() {
+ uint64_t absolute_index;
+ if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(),
+ &absolute_index)) {
+ OnError("Invalid post-base index.");
+ return false;
+ }
+
+ if (absolute_index >= required_insert_count_) {
+ OnError("Absolute Index must be smaller than Required Insert Count.");
+ return false;
+ }
+
+ DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+ required_insert_count_so_far_ =
+ std::max(required_insert_count_so_far_, absolute_index + 1);
+
+ auto entry =
+ header_table_->LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ OnError("Dynamic table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), entry->value());
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() {
+ if (!instruction_decoder_.s_bit()) {
+ uint64_t absolute_index;
+ if (!RequestStreamRelativeIndexToAbsoluteIndex(
+ instruction_decoder_.varint(), &absolute_index)) {
+ OnError("Invalid relative index.");
+ return false;
+ }
+
+ if (absolute_index >= required_insert_count_) {
+ OnError("Absolute Index must be smaller than Required Insert Count.");
+ return false;
+ }
+
+ DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+ required_insert_count_so_far_ =
+ std::max(required_insert_count_so_far_, absolute_index + 1);
+
+ auto entry =
+ header_table_->LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ OnError("Dynamic table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value());
+ return true;
+ }
+
+ auto entry = header_table_->LookupEntry(/* is_static = */ true,
+ instruction_decoder_.varint());
+ if (!entry) {
+ OnError("Static table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value());
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() {
+ uint64_t absolute_index;
+ if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(),
+ &absolute_index)) {
+ OnError("Invalid post-base index.");
+ return false;
+ }
+
+ if (absolute_index >= required_insert_count_) {
+ OnError("Absolute Index must be smaller than Required Insert Count.");
+ return false;
+ }
+
+ DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+ required_insert_count_so_far_ =
+ std::max(required_insert_count_so_far_, absolute_index + 1);
+
+ auto entry =
+ header_table_->LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ OnError("Dynamic table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value());
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoLiteralHeaderFieldInstruction() {
+ handler_->OnHeaderDecoded(instruction_decoder_.name(),
+ instruction_decoder_.value());
+
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoPrefixInstruction() {
+ DCHECK(!prefix_decoded_);
+
+ if (!DecodeRequiredInsertCount(
+ prefix_decoder_->varint(), header_table_->max_entries(),
+ header_table_->inserted_entry_count(), &required_insert_count_)) {
+ OnError("Error decoding Required Insert Count.");
+ return false;
+ }
+
+ const bool sign = prefix_decoder_->s_bit();
+ const uint64_t delta_base = prefix_decoder_->varint2();
+ if (!DeltaBaseToBase(sign, delta_base, &base_)) {
+ OnError("Error calculating Base.");
+ return false;
+ }
+
+ prefix_decoded_ = true;
+
+ return true;
+}
+
+bool QpackProgressiveDecoder::DeltaBaseToBase(bool sign,
+ uint64_t delta_base,
+ uint64_t* base) {
+ if (sign) {
+ if (delta_base == std::numeric_limits<uint64_t>::max() ||
+ required_insert_count_ < delta_base + 1) {
+ return false;
+ }
+ *base = required_insert_count_ - delta_base - 1;
+ return true;
+ }
+
+ if (delta_base >
+ std::numeric_limits<uint64_t>::max() - required_insert_count_) {
+ return false;
+ }
+ *base = required_insert_count_ + delta_base;
+ return true;
+}
+
+bool QpackProgressiveDecoder::RequestStreamRelativeIndexToAbsoluteIndex(
+ uint64_t relative_index,
+ uint64_t* absolute_index) const {
+ if (relative_index == std::numeric_limits<uint64_t>::max() ||
+ relative_index + 1 > base_) {
+ return false;
+ }
+
+ *absolute_index = base_ - 1 - relative_index;
+ return true;
+}
+
+bool QpackProgressiveDecoder::PostBaseIndexToAbsoluteIndex(
+ uint64_t post_base_index,
+ uint64_t* absolute_index) const {
+ if (post_base_index >= std::numeric_limits<uint64_t>::max() - base_) {
+ return false;
+ }
+
+ *absolute_index = base_ + post_base_index;
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h
new file mode 100644
index 00000000000..52adf45b236
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h
@@ -0,0 +1,140 @@
+// 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_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QpackHeaderTable;
+
+// Class to decode a single header block.
+class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder
+ : public QpackInstructionDecoder::Delegate {
+ public:
+ // Interface for receiving decoded header block from the decoder.
+ class QUIC_EXPORT_PRIVATE HeadersHandlerInterface {
+ public:
+ virtual ~HeadersHandlerInterface() {}
+
+ // Called when a new header name-value pair is decoded. Multiple values for
+ // a given name will be emitted as multiple calls to OnHeader.
+ virtual void OnHeaderDecoded(QuicStringPiece name,
+ QuicStringPiece value) = 0;
+
+ // Called when the header block is completely decoded.
+ // Indicates the total number of bytes in this block.
+ // The decoder will not access the handler after this call.
+ // Note that this method might not be called synchronously when the header
+ // block is received on the wire, in case decoding is blocked on receiving
+ // entries on the encoder stream. TODO(bnc): Implement blocked decoding.
+ virtual void OnDecodingCompleted() = 0;
+
+ // Called when a decoding error has occurred. No other methods will be
+ // called afterwards.
+ virtual void OnDecodingErrorDetected(QuicStringPiece error_message) = 0;
+ };
+
+ QpackProgressiveDecoder() = delete;
+ QpackProgressiveDecoder(QuicStreamId stream_id,
+ QpackHeaderTable* header_table,
+ QpackDecoderStreamSender* decoder_stream_sender,
+ HeadersHandlerInterface* handler);
+ QpackProgressiveDecoder(const QpackProgressiveDecoder&) = delete;
+ QpackProgressiveDecoder& operator=(const QpackProgressiveDecoder&) = delete;
+ ~QpackProgressiveDecoder() override = default;
+
+ // Calculate Required Insert Count from Encoded Required Insert Count,
+ // MaxEntries, and total number of dynamic table insertions according to
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#ric.
+ // Returns true on success, false on invalid input or overflow/underflow.
+ static bool DecodeRequiredInsertCount(uint64_t encoded_required_insert_count,
+ uint64_t max_entries,
+ uint64_t total_number_of_inserts,
+ uint64_t* required_insert_count);
+
+ // Provide a data fragment to decode.
+ void Decode(QuicStringPiece data);
+
+ // Signal that the entire header block has been received and passed in
+ // through Decode(). No methods must be called afterwards.
+ void EndHeaderBlock();
+
+ // QpackInstructionDecoder::Delegate implementation.
+ bool OnInstructionDecoded(const QpackInstruction* instruction) override;
+ void OnError(QuicStringPiece error_message) override;
+
+ private:
+ bool DoIndexedHeaderFieldInstruction();
+ bool DoIndexedHeaderFieldPostBaseInstruction();
+ bool DoLiteralHeaderFieldNameReferenceInstruction();
+ bool DoLiteralHeaderFieldPostBaseInstruction();
+ bool DoLiteralHeaderFieldInstruction();
+ bool DoPrefixInstruction();
+
+ // Calculates Base from |required_insert_count_|, which must be set before
+ // calling this method, and sign bit and Delta Base in the Header Data Prefix,
+ // which are passed in as arguments. Returns true on success, false on
+ // failure due to overflow/underflow.
+ bool DeltaBaseToBase(bool sign, uint64_t delta_base, uint64_t* base);
+
+ // The request stream can use relative index (but different from the kind of
+ // relative index used on the encoder stream), and post-base index.
+ // These methods convert relative index and post-base index to absolute index
+ // (one based). They return true on success, or false if conversion fails due
+ // to overflow/underflow. On success, |*absolute_index| is guaranteed to be
+ // strictly less than std::numeric_limits<uint64_t>::max().
+ bool RequestStreamRelativeIndexToAbsoluteIndex(
+ uint64_t relative_index,
+ uint64_t* absolute_index) const;
+ bool PostBaseIndexToAbsoluteIndex(uint64_t post_base_index,
+ uint64_t* absolute_index) const;
+
+ const QuicStreamId stream_id_;
+
+ // |prefix_decoder_| only decodes a handful of bytes then it can be
+ // destroyed to conserve memory. |instruction_decoder_|, on the other hand,
+ // is used until the entire header block is decoded.
+ std::unique_ptr<QpackInstructionDecoder> prefix_decoder_;
+ QpackInstructionDecoder instruction_decoder_;
+
+ const QpackHeaderTable* const header_table_;
+ QpackDecoderStreamSender* const decoder_stream_sender_;
+ HeadersHandlerInterface* const handler_;
+
+ // Required Insert Count and Base are decoded from the Header Data Prefix.
+ uint64_t required_insert_count_;
+ uint64_t base_;
+
+ // Required Insert Count is one larger than the largest absolute index of all
+ // referenced dynamic table entries, or zero if no dynamic table entries are
+ // referenced. |required_insert_count_so_far_| starts out as zero and keeps
+ // track of the Required Insert Count based on entries decoded so far.
+ // After decoding is completed, it is compared to |required_insert_count_|.
+ uint64_t required_insert_count_so_far_;
+
+ // False until prefix is fully read and decoded.
+ bool prefix_decoded_;
+
+ // True until EndHeaderBlock() is called.
+ bool decoding_;
+
+ // True if a decoding error has been detected.
+ bool error_detected_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder_test.cc
new file mode 100644
index 00000000000..b3067023b97
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder_test.cc
@@ -0,0 +1,124 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+// For testing valid decodings, the Encoded Required Insert Count is calculated
+// from Required Insert Count, so that there is an expected value to compare
+// the decoded value against, and so that intricate inequalities can be
+// documented.
+struct {
+ uint64_t required_insert_count;
+ uint64_t max_entries;
+ uint64_t total_number_of_inserts;
+} kTestData[] = {
+ // Maximum dynamic table capacity is zero.
+ {0, 0, 0},
+ // No dynamic entries in header.
+ {0, 100, 0},
+ {0, 100, 500},
+ // Required Insert Count has not wrapped around yet, no entries evicted.
+ {15, 100, 25},
+ {20, 100, 10},
+ // Required Insert Count has not wrapped around yet, some entries evicted.
+ {90, 100, 110},
+ // Required Insert Count has wrapped around.
+ {234, 100, 180},
+ // Required Insert Count has wrapped around many times.
+ {5678, 100, 5701},
+ // Lowest and highest possible Required Insert Count values
+ // for given MaxEntries and total number of insertions.
+ {401, 100, 500},
+ {600, 100, 500}};
+
+uint64_t EncodeRequiredInsertCount(uint64_t required_insert_count,
+ uint64_t max_entries) {
+ if (required_insert_count == 0) {
+ return 0;
+ }
+
+ return required_insert_count % (2 * max_entries) + 1;
+}
+
+TEST(QpackProgressiveDecoderTest, DecodeRequiredInsertCount) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kTestData); ++i) {
+ const uint64_t required_insert_count = kTestData[i].required_insert_count;
+ const uint64_t max_entries = kTestData[i].max_entries;
+ const uint64_t total_number_of_inserts =
+ kTestData[i].total_number_of_inserts;
+
+ if (required_insert_count != 0) {
+ // Dynamic entries cannot be referenced if dynamic table capacity is zero.
+ ASSERT_LT(0u, max_entries) << i;
+ // Entry |total_number_of_inserts - 1 - max_entries| and earlier entries
+ // are evicted. Entry |required_insert_count - 1| is referenced. No
+ // evicted entry can be referenced.
+ ASSERT_LT(total_number_of_inserts, required_insert_count + max_entries)
+ << i;
+ // Entry |required_insert_count - 1 - max_entries| and earlier entries are
+ // evicted, entry |total_number_of_inserts - 1| is the last acknowledged
+ // entry. Every evicted entry must be acknowledged.
+ ASSERT_LE(required_insert_count, total_number_of_inserts + max_entries)
+ << i;
+ }
+
+ uint64_t encoded_required_insert_count =
+ EncodeRequiredInsertCount(required_insert_count, max_entries);
+
+ // Initialize to a value different from the expected output to confirm that
+ // DecodeRequiredInsertCount() modifies the value of
+ // |decoded_required_insert_count|.
+ uint64_t decoded_required_insert_count = required_insert_count + 1;
+ EXPECT_TRUE(QpackProgressiveDecoder::DecodeRequiredInsertCount(
+ encoded_required_insert_count, max_entries, total_number_of_inserts,
+ &decoded_required_insert_count))
+ << i;
+
+ EXPECT_EQ(decoded_required_insert_count, required_insert_count) << i;
+ }
+}
+
+// Failures are tested with hardcoded values for encoded required insert count,
+// to provide test coverage for values that would never be produced by a well
+// behaved encoding function.
+struct {
+ uint64_t encoded_required_insert_count;
+ uint64_t max_entries;
+ uint64_t total_number_of_inserts;
+} kInvalidTestData[] = {
+ // Maximum dynamic table capacity is zero, yet header block
+ // claims to have a reference to a dynamic table entry.
+ {1, 0, 0},
+ {9, 0, 0},
+ // Examples from
+ // https://github.com/quicwg/base-drafts/issues/2112#issue-389626872.
+ {1, 10, 2},
+ {18, 10, 2},
+ // Encoded Required Insert Count value too small or too large
+ // for given MaxEntries and total number of insertions.
+ {400, 100, 500},
+ {601, 100, 500}};
+
+TEST(QpackProgressiveDecoderTest, DecodeRequiredInsertCountError) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kInvalidTestData); ++i) {
+ uint64_t decoded_required_insert_count = 0;
+ EXPECT_FALSE(QpackProgressiveDecoder::DecodeRequiredInsertCount(
+ kInvalidTestData[i].encoded_required_insert_count,
+ kInvalidTestData[i].max_entries,
+ kInvalidTestData[i].total_number_of_inserts,
+ &decoded_required_insert_count))
+ << i;
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.cc
new file mode 100644
index 00000000000..05a025b28be
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.cc
@@ -0,0 +1,139 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QpackProgressiveEncoder::QpackProgressiveEncoder(
+ QuicStreamId stream_id,
+ QpackHeaderTable* header_table,
+ QpackEncoderStreamSender* encoder_stream_sender,
+ const spdy::SpdyHeaderBlock* header_list)
+ : stream_id_(stream_id),
+ header_table_(header_table),
+ encoder_stream_sender_(encoder_stream_sender),
+ header_list_(header_list),
+ header_list_iterator_(header_list_->begin()),
+ prefix_encoded_(false) {
+ // TODO(bnc): Use |stream_id_| for dynamic table entry management, and
+ // remove this dummy DCHECK.
+ DCHECK_LE(0u, stream_id_);
+
+ DCHECK(header_table_);
+ DCHECK(encoder_stream_sender_);
+ DCHECK(header_list_);
+}
+
+bool QpackProgressiveEncoder::HasNext() const {
+ return header_list_iterator_ != header_list_->end() || !prefix_encoded_;
+}
+
+void QpackProgressiveEncoder::Next(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK_NE(0u, max_encoded_bytes);
+ DCHECK(HasNext());
+
+ // Since QpackInstructionEncoder::Next() does not indicate the number of bytes
+ // written, save the maximum new size of |*output|.
+ const size_t max_length = output->size() + max_encoded_bytes;
+
+ DCHECK_LT(output->size(), max_length);
+
+ if (!prefix_encoded_ && !instruction_encoder_.HasNext()) {
+ // TODO(bnc): Implement dynamic entries and set Required Insert Count and
+ // Delta Base accordingly.
+ instruction_encoder_.set_varint(0);
+ instruction_encoder_.set_varint2(0);
+ instruction_encoder_.set_s_bit(false);
+
+ instruction_encoder_.Encode(QpackPrefixInstruction());
+
+ DCHECK(instruction_encoder_.HasNext());
+ }
+
+ do {
+ // Call QpackInstructionEncoder::Encode for |*header_list_iterator_| if it
+ // has not been called yet.
+ if (!instruction_encoder_.HasNext()) {
+ DCHECK(prefix_encoded_);
+
+ // Even after |name| and |value| go out of scope, copies of these
+ // QuicStringPieces retained by QpackInstructionEncoder are still valid as
+ // long as |header_list_| is valid.
+ QuicStringPiece name = header_list_iterator_->first;
+ QuicStringPiece value = header_list_iterator_->second;
+
+ // |is_static| and |index| are saved by QpackInstructionEncoder by value,
+ // there are no lifetime concerns.
+ bool is_static;
+ uint64_t index;
+
+ auto match_type =
+ header_table_->FindHeaderField(name, value, &is_static, &index);
+
+ switch (match_type) {
+ case QpackHeaderTable::MatchType::kNameAndValue:
+ DCHECK(is_static) << "Dynamic table entries not supported yet.";
+
+ instruction_encoder_.set_s_bit(is_static);
+ instruction_encoder_.set_varint(index);
+
+ instruction_encoder_.Encode(QpackIndexedHeaderFieldInstruction());
+
+ break;
+ case QpackHeaderTable::MatchType::kName:
+ DCHECK(is_static) << "Dynamic table entries not supported yet.";
+
+ instruction_encoder_.set_s_bit(is_static);
+ instruction_encoder_.set_varint(index);
+ instruction_encoder_.set_value(value);
+
+ instruction_encoder_.Encode(
+ QpackLiteralHeaderFieldNameReferenceInstruction());
+
+ break;
+ case QpackHeaderTable::MatchType::kNoMatch:
+ instruction_encoder_.set_name(name);
+ instruction_encoder_.set_value(value);
+
+ instruction_encoder_.Encode(QpackLiteralHeaderFieldInstruction());
+
+ break;
+ }
+ }
+
+ DCHECK(instruction_encoder_.HasNext());
+
+ instruction_encoder_.Next(max_length - output->size(), output);
+
+ if (instruction_encoder_.HasNext()) {
+ // There was not enough room to completely encode current header field.
+ DCHECK_EQ(output->size(), max_length);
+
+ return;
+ }
+
+ // It is possible that the output buffer was just large enough for encoding
+ // the current header field, hence equality is allowed here.
+ DCHECK_LE(output->size(), max_length);
+
+ if (prefix_encoded_) {
+ // Move on to the next header field.
+ ++header_list_iterator_;
+ } else {
+ // Mark prefix as encoded.
+ prefix_encoded_ = true;
+ }
+ } while (HasNext() && output->size() < max_length);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h
new file mode 100644
index 00000000000..8e204e23b38
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h
@@ -0,0 +1,57 @@
+// 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_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+namespace quic {
+
+class QpackHeaderTable;
+
+// An implementation of ProgressiveEncoder interface that encodes a single
+// header block.
+class QUIC_EXPORT_PRIVATE QpackProgressiveEncoder
+ : public spdy::HpackEncoder::ProgressiveEncoder {
+ public:
+ QpackProgressiveEncoder() = delete;
+ QpackProgressiveEncoder(QuicStreamId stream_id,
+ QpackHeaderTable* header_table,
+ QpackEncoderStreamSender* encoder_stream_sender,
+ const spdy::SpdyHeaderBlock* header_list);
+ QpackProgressiveEncoder(const QpackProgressiveEncoder&) = delete;
+ QpackProgressiveEncoder& operator=(const QpackProgressiveEncoder&) = delete;
+ ~QpackProgressiveEncoder() override = default;
+
+ // Returns true iff more remains to encode.
+ bool HasNext() const override;
+
+ // Encodes up to |max_encoded_bytes| octets, appending to |output|.
+ void Next(size_t max_encoded_bytes, std::string* output) override;
+
+ private:
+ const QuicStreamId stream_id_;
+ QpackInstructionEncoder instruction_encoder_;
+ const QpackHeaderTable* const header_table_;
+ QpackEncoderStreamSender* const encoder_stream_sender_;
+ const spdy::SpdyHeaderBlock* const header_list_;
+
+ // Header field currently being encoded.
+ spdy::SpdyHeaderBlock::const_iterator header_list_iterator_;
+
+ // False until prefix is fully encoded.
+ bool prefix_encoded_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc
new file mode 100644
index 00000000000..9b2df550b88
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc
@@ -0,0 +1,137 @@
+// 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 <string>
+#include <tuple>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+using ::testing::Combine;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackRoundTripTest
+ : public QuicTestWithParam<std::tuple<FragmentMode, FragmentMode>> {
+ public:
+ QpackRoundTripTest()
+ : encoding_fragment_mode_(std::get<0>(GetParam())),
+ decoding_fragment_mode_(std::get<1>(GetParam())) {}
+ ~QpackRoundTripTest() override = default;
+
+ spdy::SpdyHeaderBlock EncodeThenDecode(
+ const spdy::SpdyHeaderBlock& header_list) {
+ NoopDecoderStreamErrorDelegate decoder_stream_error_delegate;
+ NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate;
+ std::string encoded_header_block = QpackEncode(
+ &decoder_stream_error_delegate, &encoder_stream_sender_delegate,
+ FragmentModeToFragmentSizeGenerator(encoding_fragment_mode_),
+ &header_list);
+
+ TestHeadersHandler handler;
+ NoopEncoderStreamErrorDelegate encoder_stream_error_delegate;
+ NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate;
+ QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate,
+ &handler,
+ FragmentModeToFragmentSizeGenerator(decoding_fragment_mode_),
+ encoded_header_block);
+
+ EXPECT_TRUE(handler.decoding_completed());
+ EXPECT_FALSE(handler.decoding_error_detected());
+
+ return handler.ReleaseHeaderList();
+ }
+
+ private:
+ const FragmentMode encoding_fragment_mode_;
+ const FragmentMode decoding_fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ QpackRoundTripTest,
+ Combine(Values(FragmentMode::kSingleChunk, FragmentMode::kOctetByOctet),
+ Values(FragmentMode::kSingleChunk, FragmentMode::kOctetByOctet)));
+
+TEST_P(QpackRoundTripTest, Empty) {
+ spdy::SpdyHeaderBlock header_list;
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+}
+
+TEST_P(QpackRoundTripTest, EmptyName) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "bar";
+ header_list[""] = "bar";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+}
+
+TEST_P(QpackRoundTripTest, EmptyValue) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "";
+ header_list[""] = "";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+}
+
+TEST_P(QpackRoundTripTest, MultipleWithLongEntries) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "bar";
+ header_list[":path"] = "/";
+ header_list["foobaar"] = std::string(127, 'Z');
+ header_list[std::string(1000, 'b')] = std::string(1000, 'c');
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+}
+
+TEST_P(QpackRoundTripTest, StaticTable) {
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "GET";
+ header_list["accept-encoding"] = "gzip, deflate";
+ header_list["cache-control"] = "";
+ header_list["foo"] = "bar";
+ header_list[":path"] = "/";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+ }
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "POST";
+ header_list["accept-encoding"] = "brotli";
+ header_list["cache-control"] = "foo";
+ header_list["foo"] = "bar";
+ header_list[":path"] = "/";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+ }
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "CONNECT";
+ header_list["accept-encoding"] = "";
+ header_list["foo"] = "bar";
+ header_list[":path"] = "/";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.cc
new file mode 100644
index 00000000000..f1986f798ad
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.cc
@@ -0,0 +1,140 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+// The "constructor" for a QpackStaticEntry that computes the lengths at
+// compile time.
+#define STATIC_ENTRY(name, value) \
+ { name, QUIC_ARRAYSIZE(name) - 1, value, QUIC_ARRAYSIZE(value) - 1 }
+
+const std::vector<QpackStaticEntry>& QpackStaticTableVector() {
+ static const auto* kQpackStaticTable = new std::vector<QpackStaticEntry>{
+ STATIC_ENTRY(":authority", ""), // 0
+ STATIC_ENTRY(":path", "/"), // 1
+ STATIC_ENTRY("age", "0"), // 2
+ STATIC_ENTRY("content-disposition", ""), // 3
+ STATIC_ENTRY("content-length", "0"), // 4
+ STATIC_ENTRY("cookie", ""), // 5
+ STATIC_ENTRY("date", ""), // 6
+ STATIC_ENTRY("etag", ""), // 7
+ STATIC_ENTRY("if-modified-since", ""), // 8
+ STATIC_ENTRY("if-none-match", ""), // 9
+ STATIC_ENTRY("last-modified", ""), // 10
+ STATIC_ENTRY("link", ""), // 11
+ STATIC_ENTRY("location", ""), // 12
+ STATIC_ENTRY("referer", ""), // 13
+ STATIC_ENTRY("set-cookie", ""), // 14
+ STATIC_ENTRY(":method", "CONNECT"), // 15
+ STATIC_ENTRY(":method", "DELETE"), // 16
+ STATIC_ENTRY(":method", "GET"), // 17
+ STATIC_ENTRY(":method", "HEAD"), // 18
+ STATIC_ENTRY(":method", "OPTIONS"), // 19
+ STATIC_ENTRY(":method", "POST"), // 20
+ STATIC_ENTRY(":method", "PUT"), // 21
+ STATIC_ENTRY(":scheme", "http"), // 22
+ STATIC_ENTRY(":scheme", "https"), // 23
+ STATIC_ENTRY(":status", "103"), // 24
+ STATIC_ENTRY(":status", "200"), // 25
+ STATIC_ENTRY(":status", "304"), // 26
+ STATIC_ENTRY(":status", "404"), // 27
+ STATIC_ENTRY(":status", "503"), // 28
+ STATIC_ENTRY("accept", "*/*"), // 29
+ STATIC_ENTRY("accept", "application/dns-message"), // 30
+ STATIC_ENTRY("accept-encoding", "gzip, deflate, br"), // 31
+ STATIC_ENTRY("accept-ranges", "bytes"), // 32
+ STATIC_ENTRY("access-control-allow-headers", "cache-control"), // 33
+ STATIC_ENTRY("access-control-allow-headers", "content-type"), // 35
+ STATIC_ENTRY("access-control-allow-origin", "*"), // 35
+ STATIC_ENTRY("cache-control", "max-age=0"), // 36
+ STATIC_ENTRY("cache-control", "max-age=2592000"), // 37
+ STATIC_ENTRY("cache-control", "max-age=604800"), // 38
+ STATIC_ENTRY("cache-control", "no-cache"), // 39
+ STATIC_ENTRY("cache-control", "no-store"), // 40
+ STATIC_ENTRY("cache-control", "public, max-age=31536000"), // 41
+ STATIC_ENTRY("content-encoding", "br"), // 42
+ STATIC_ENTRY("content-encoding", "gzip"), // 43
+ STATIC_ENTRY("content-type", "application/dns-message"), // 44
+ STATIC_ENTRY("content-type", "application/javascript"), // 45
+ STATIC_ENTRY("content-type", "application/json"), // 46
+ STATIC_ENTRY("content-type", "application/x-www-form-urlencoded"), // 47
+ STATIC_ENTRY("content-type", "image/gif"), // 48
+ STATIC_ENTRY("content-type", "image/jpeg"), // 49
+ STATIC_ENTRY("content-type", "image/png"), // 50
+ STATIC_ENTRY("content-type", "text/css"), // 51
+ STATIC_ENTRY("content-type", "text/html; charset=utf-8"), // 52
+ STATIC_ENTRY("content-type", "text/plain"), // 53
+ STATIC_ENTRY("content-type", "text/plain;charset=utf-8"), // 54
+ STATIC_ENTRY("range", "bytes=0-"), // 55
+ STATIC_ENTRY("strict-transport-security", "max-age=31536000"), // 56
+ STATIC_ENTRY("strict-transport-security",
+ "max-age=31536000; includesubdomains"), // 57
+ STATIC_ENTRY("strict-transport-security",
+ "max-age=31536000; includesubdomains; preload"), // 58
+ STATIC_ENTRY("vary", "accept-encoding"), // 59
+ STATIC_ENTRY("vary", "origin"), // 60
+ STATIC_ENTRY("x-content-type-options", "nosniff"), // 61
+ STATIC_ENTRY("x-xss-protection", "1; mode=block"), // 62
+ STATIC_ENTRY(":status", "100"), // 63
+ STATIC_ENTRY(":status", "204"), // 64
+ STATIC_ENTRY(":status", "206"), // 65
+ STATIC_ENTRY(":status", "302"), // 66
+ STATIC_ENTRY(":status", "400"), // 67
+ STATIC_ENTRY(":status", "403"), // 68
+ STATIC_ENTRY(":status", "421"), // 69
+ STATIC_ENTRY(":status", "425"), // 70
+ STATIC_ENTRY(":status", "500"), // 71
+ STATIC_ENTRY("accept-language", ""), // 72
+ STATIC_ENTRY("access-control-allow-credentials", "FALSE"), // 73
+ STATIC_ENTRY("access-control-allow-credentials", "TRUE"), // 74
+ STATIC_ENTRY("access-control-allow-headers", "*"), // 75
+ STATIC_ENTRY("access-control-allow-methods", "get"), // 76
+ STATIC_ENTRY("access-control-allow-methods", "get, post, options"), // 77
+ STATIC_ENTRY("access-control-allow-methods", "options"), // 78
+ STATIC_ENTRY("access-control-expose-headers", "content-length"), // 79
+ STATIC_ENTRY("access-control-request-headers", "content-type"), // 80
+ STATIC_ENTRY("access-control-request-method", "get"), // 81
+ STATIC_ENTRY("access-control-request-method", "post"), // 82
+ STATIC_ENTRY("alt-svc", "clear"), // 83
+ STATIC_ENTRY("authorization", ""), // 84
+ STATIC_ENTRY(
+ "content-security-policy",
+ "script-src 'none'; object-src 'none'; base-uri 'none'"), // 85
+ STATIC_ENTRY("early-data", "1"), // 86
+ STATIC_ENTRY("expect-ct", ""), // 87
+ STATIC_ENTRY("forwarded", ""), // 88
+ STATIC_ENTRY("if-range", ""), // 89
+ STATIC_ENTRY("origin", ""), // 90
+ STATIC_ENTRY("purpose", "prefetch"), // 91
+ STATIC_ENTRY("server", ""), // 92
+ STATIC_ENTRY("timing-allow-origin", "*"), // 93
+ STATIC_ENTRY("upgrade-insecure-requests", "1"), // 94
+ STATIC_ENTRY("user-agent", ""), // 95
+ STATIC_ENTRY("x-forwarded-for", ""), // 96
+ STATIC_ENTRY("x-frame-options", "deny"), // 97
+ STATIC_ENTRY("x-frame-options", "sameorigin"), // 98
+ };
+ return *kQpackStaticTable;
+}
+
+#undef STATIC_ENTRY
+
+const QpackStaticTable& ObtainQpackStaticTable() {
+ static const QpackStaticTable* const shared_static_table = []() {
+ auto* table = new QpackStaticTable();
+ table->Initialize(QpackStaticTableVector().data(),
+ QpackStaticTableVector().size());
+ CHECK(table->IsInitialized());
+ return table;
+ }();
+ return *shared_static_table;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h
new file mode 100644
index 00000000000..d8c255568a4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h
@@ -0,0 +1,31 @@
+// 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_CORE_QPACK_QPACK_STATIC_TABLE_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_
+
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h"
+
+namespace quic {
+
+using QpackStaticEntry = spdy::HpackStaticEntry;
+using QpackStaticTable = spdy::HpackStaticTable;
+
+// QPACK static table defined at
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#static-table.
+QUIC_EXPORT_PRIVATE const std::vector<QpackStaticEntry>&
+QpackStaticTableVector();
+
+// Returns a QpackStaticTable instance initialized with kQpackStaticTable.
+// The instance is read-only, has static lifetime, and is safe to share amoung
+// threads. This function is thread-safe.
+QUIC_EXPORT_PRIVATE const QpackStaticTable& ObtainQpackStaticTable();
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc
new file mode 100644
index 00000000000..50289f2e298
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc
@@ -0,0 +1,53 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h"
+
+#include <set>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+// Check that an initialized instance has the right number of entries.
+TEST(QpackStaticTableTest, Initialize) {
+ QpackStaticTable table;
+ EXPECT_FALSE(table.IsInitialized());
+
+ table.Initialize(QpackStaticTableVector().data(),
+ QpackStaticTableVector().size());
+ EXPECT_TRUE(table.IsInitialized());
+
+ auto static_entries = table.GetStaticEntries();
+ EXPECT_EQ(QpackStaticTableVector().size(), static_entries.size());
+
+ auto static_index = table.GetStaticIndex();
+ EXPECT_EQ(QpackStaticTableVector().size(), static_index.size());
+
+ auto static_name_index = table.GetStaticNameIndex();
+ std::set<QuicStringPiece> names;
+ for (auto entry : static_index) {
+ names.insert(entry->name());
+ }
+ EXPECT_EQ(names.size(), static_name_index.size());
+}
+
+// Test that ObtainQpackStaticTable returns the same instance every time.
+TEST(QpackStaticTableTest, IsSingleton) {
+ const QpackStaticTable* static_table_one = &ObtainQpackStaticTable();
+ const QpackStaticTable* static_table_two = &ObtainQpackStaticTable();
+ EXPECT_EQ(static_table_one, static_table_two);
+}
+
+} // namespace
+
+} // namespace test
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc
new file mode 100644
index 00000000000..2d4a72e71b0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc
@@ -0,0 +1,23 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+
+#include <limits>
+
+namespace quic {
+namespace test {
+
+FragmentSizeGenerator FragmentModeToFragmentSizeGenerator(
+ FragmentMode fragment_mode) {
+ switch (fragment_mode) {
+ case FragmentMode::kSingleChunk:
+ return []() { return std::numeric_limits<size_t>::max(); };
+ case FragmentMode::kOctetByOctet:
+ return []() { return 1; };
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h
new file mode 100644
index 00000000000..65bb5a20ded
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h
@@ -0,0 +1,29 @@
+// 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_CORE_QPACK_QPACK_TEST_UTILS_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_
+
+#include <cstddef>
+#include <functional>
+
+namespace quic {
+namespace test {
+
+// Called repeatedly to determine the size of each fragment when encoding or
+// decoding. Must return a positive value.
+using FragmentSizeGenerator = std::function<size_t()>;
+
+enum class FragmentMode {
+ kSingleChunk,
+ kOctetByOctet,
+};
+
+FragmentSizeGenerator FragmentModeToFragmentSizeGenerator(
+ FragmentMode fragment_mode);
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_ack_listener_interface.cc b/chromium/net/third_party/quiche/src/quic/core/quic_ack_listener_interface.cc
new file mode 100644
index 00000000000..fc25a313980
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_ack_listener_interface.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+
+namespace quic {
+
+QuicAckListenerInterface::~QuicAckListenerInterface() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h b/chromium/net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h
new file mode 100644
index 00000000000..0a9c69425a8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h
@@ -0,0 +1,37 @@
+// 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 QUICHE_QUIC_CORE_QUIC_ACK_LISTENER_INTERFACE_H_
+#define QUICHE_QUIC_CORE_QUIC_ACK_LISTENER_INTERFACE_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+
+namespace quic {
+
+// Pure virtual class to listen for packet acknowledgements.
+class QUIC_EXPORT_PRIVATE QuicAckListenerInterface
+ : public QuicReferenceCounted {
+ public:
+ QuicAckListenerInterface() {}
+
+ // Called when a packet is acked. Called once per packet.
+ // |acked_bytes| is the number of data bytes acked.
+ virtual void OnPacketAcked(int acked_bytes,
+ QuicTime::Delta ack_delay_time) = 0;
+
+ // Called when a packet is retransmitted. Called once per packet.
+ // |retransmitted_bytes| is the number of data bytes retransmitted.
+ virtual void OnPacketRetransmitted(int retransmitted_bytes) = 0;
+
+ protected:
+ // Delegates are ref counted.
+ ~QuicAckListenerInterface() override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_ACK_LISTENER_INTERFACE_H_
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
new file mode 100644
index 00000000000..e31c7aa145a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.cc
@@ -0,0 +1,73 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+
+namespace quic {
+
+QuicAlarm::QuicAlarm(QuicArenaScopedPtr<Delegate> delegate)
+ : delegate_(std::move(delegate)), deadline_(QuicTime::Zero()) {}
+
+QuicAlarm::~QuicAlarm() {}
+
+void QuicAlarm::Set(QuicTime new_deadline) {
+ DCHECK(!IsSet());
+ DCHECK(new_deadline.IsInitialized());
+ deadline_ = new_deadline;
+ SetImpl();
+}
+
+void QuicAlarm::Cancel() {
+ if (!IsSet()) {
+ // Don't try to cancel an alarm that hasn't been set.
+ return;
+ }
+ deadline_ = QuicTime::Zero();
+ CancelImpl();
+}
+
+void QuicAlarm::Update(QuicTime new_deadline, QuicTime::Delta granularity) {
+ if (!new_deadline.IsInitialized()) {
+ Cancel();
+ return;
+ }
+ if (std::abs((new_deadline - deadline_).ToMicroseconds()) <
+ granularity.ToMicroseconds()) {
+ return;
+ }
+ const bool was_set = IsSet();
+ deadline_ = new_deadline;
+ if (was_set) {
+ UpdateImpl();
+ } else {
+ SetImpl();
+ }
+}
+
+bool QuicAlarm::IsSet() const {
+ return deadline_.IsInitialized();
+}
+
+void QuicAlarm::Fire() {
+ if (!IsSet()) {
+ return;
+ }
+
+ deadline_ = QuicTime::Zero();
+ delegate_->OnAlarm();
+}
+
+void QuicAlarm::UpdateImpl() {
+ // CancelImpl and SetImpl take the new deadline by way of the deadline_
+ // member, so save and restore deadline_ before canceling.
+ const QuicTime new_deadline = deadline_;
+
+ deadline_ = QuicTime::Zero();
+ CancelImpl();
+
+ deadline_ = new_deadline;
+ SetImpl();
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..df2ce623095
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm.h
@@ -0,0 +1,86 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_ALARM_H_
+#define QUICHE_QUIC_CORE_QUIC_ALARM_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Abstract class which represents an alarm which will go off at a
+// scheduled time, and execute the |OnAlarm| method of the delegate.
+// An alarm may be cancelled, in which case it may or may not be
+// removed from the underlying scheduling system, but in either case
+// the task will not be executed.
+class QUIC_EXPORT_PRIVATE QuicAlarm {
+ public:
+ class QUIC_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Invoked when the alarm fires.
+ virtual void OnAlarm() = 0;
+ };
+
+ explicit QuicAlarm(QuicArenaScopedPtr<Delegate> delegate);
+ QuicAlarm(const QuicAlarm&) = delete;
+ QuicAlarm& operator=(const QuicAlarm&) = delete;
+ virtual ~QuicAlarm();
+
+ // Sets the alarm to fire at |deadline|. Must not be called while
+ // the alarm is set. To reschedule an alarm, call Cancel() first,
+ // 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();
+
+ // Cancels and sets the alarm if the |deadline| is farther from the current
+ // deadline than |granularity|, and otherwise does nothing. If |deadline| is
+ // not initialized, the alarm is cancelled.
+ void Update(QuicTime new_deadline, QuicTime::Delta granularity);
+
+ // Returns true if |deadline_| has been set to a non-zero time.
+ bool IsSet() const;
+
+ QuicTime deadline() const { return deadline_; }
+
+ protected:
+ // Subclasses implement this method to perform the platform-specific
+ // scheduling of the alarm. Is called from Set() or Fire(), after the
+ // deadline has been updated.
+ virtual void SetImpl() = 0;
+
+ // Subclasses implement this method to perform the platform-specific
+ // cancelation of the alarm.
+ virtual void CancelImpl() = 0;
+
+ // Subclasses implement this method to perform the platform-specific update of
+ // the alarm if there exists a more optimal implementation than calling
+ // CancelImpl() and SetImpl().
+ virtual void UpdateImpl();
+
+ // Called by subclasses when the alarm fires. Invokes the
+ // delegates |OnAlarm| if a delegate is set, and if the deadline
+ // has been exceeded. Implementations which do not remove the
+ // alarm from the underlying scheduler on Cancel() may need to handle
+ // the situation where the task executes before the deadline has been
+ // reached, in which case they need to reschedule the task and must not
+ // call invoke this method.
+ void Fire();
+
+ private:
+ QuicArenaScopedPtr<Delegate> delegate_;
+ QuicTime deadline_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_ALARM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_alarm_factory.h b/chromium/net/third_party/quiche/src/quic/core/quic_alarm_factory.h
new file mode 100644
index 00000000000..0e0ce332b99
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm_factory.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_ALARM_FACTORY_H_
+#define QUICHE_QUIC_CORE_QUIC_ALARM_FACTORY_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Creates platform-specific alarms used throughout QUIC.
+class QUIC_EXPORT_PRIVATE QuicAlarmFactory {
+ public:
+ virtual ~QuicAlarmFactory() {}
+
+ // Creates a new platform-specific alarm which will be configured to notify
+ // |delegate| when the alarm fires. Returns an alarm allocated on the heap.
+ // Caller takes ownership of the new alarm, which will not yet be "set" to
+ // fire.
+ virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) = 0;
+
+ // Creates a new platform-specific alarm which will be configured to notify
+ // |delegate| when the alarm fires. Caller takes ownership of the new alarm,
+ // which will not yet be "set" to fire. If |arena| is null, then the alarm
+ // will be created on the heap. Otherwise, it will be created in |arena|.
+ virtual QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_ALARM_FACTORY_H_
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
new file mode 100644
index 00000000000..b2f4690f9a5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_alarm_test.cc
@@ -0,0 +1,166 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+using testing::Invoke;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockDelegate : public QuicAlarm::Delegate {
+ public:
+ MOCK_METHOD0(OnAlarm, void());
+};
+
+class DestructiveDelegate : public QuicAlarm::Delegate {
+ public:
+ DestructiveDelegate() : alarm_(nullptr) {}
+
+ void set_alarm(QuicAlarm* alarm) { alarm_ = alarm; }
+
+ void OnAlarm() override {
+ DCHECK(alarm_);
+ delete alarm_;
+ }
+
+ private:
+ QuicAlarm* alarm_;
+};
+
+class TestAlarm : public QuicAlarm {
+ public:
+ explicit TestAlarm(QuicAlarm::Delegate* delegate)
+ : QuicAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)) {}
+
+ bool scheduled() const { return scheduled_; }
+
+ void FireAlarm() {
+ scheduled_ = false;
+ Fire();
+ }
+
+ protected:
+ void SetImpl() override {
+ DCHECK(deadline().IsInitialized());
+ scheduled_ = true;
+ }
+
+ void CancelImpl() override {
+ DCHECK(!deadline().IsInitialized());
+ scheduled_ = false;
+ }
+
+ private:
+ bool scheduled_;
+};
+
+class DestructiveAlarm : public QuicAlarm {
+ public:
+ explicit DestructiveAlarm(DestructiveDelegate* delegate)
+ : QuicAlarm(QuicArenaScopedPtr<DestructiveDelegate>(delegate)) {}
+
+ void FireAlarm() { Fire(); }
+
+ protected:
+ void SetImpl() override {}
+
+ void CancelImpl() override {}
+};
+
+class QuicAlarmTest : public QuicTest {
+ public:
+ QuicAlarmTest()
+ : delegate_(new MockDelegate()),
+ alarm_(delegate_),
+ deadline_(QuicTime::Zero() + QuicTime::Delta::FromSeconds(7)),
+ deadline2_(QuicTime::Zero() + QuicTime::Delta::FromSeconds(14)),
+ new_deadline_(QuicTime::Zero()) {}
+
+ void ResetAlarm() { alarm_.Set(new_deadline_); }
+
+ MockDelegate* delegate_; // not owned
+ TestAlarm alarm_;
+ QuicTime deadline_;
+ QuicTime deadline2_;
+ QuicTime new_deadline_;
+};
+
+TEST_F(QuicAlarmTest, IsSet) {
+ EXPECT_FALSE(alarm_.IsSet());
+}
+
+TEST_F(QuicAlarmTest, Set) {
+ QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
+ alarm_.Set(deadline);
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(deadline, alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, Cancel) {
+ QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
+ alarm_.Set(deadline);
+ alarm_.Cancel();
+ 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);
+ QuicTime new_deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(8);
+ alarm_.Update(new_deadline, QuicTime::Delta::Zero());
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(new_deadline, alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, UpdateWithZero) {
+ QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
+ alarm_.Set(deadline);
+ alarm_.Update(QuicTime::Zero(), QuicTime::Delta::Zero());
+ EXPECT_FALSE(alarm_.IsSet());
+ EXPECT_FALSE(alarm_.scheduled());
+ EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, Fire) {
+ QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
+ alarm_.Set(deadline);
+ EXPECT_CALL(*delegate_, OnAlarm());
+ alarm_.FireAlarm();
+ EXPECT_FALSE(alarm_.IsSet());
+ EXPECT_FALSE(alarm_.scheduled());
+ EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, FireAndResetViaSet) {
+ alarm_.Set(deadline_);
+ new_deadline_ = deadline2_;
+ EXPECT_CALL(*delegate_, OnAlarm())
+ .WillOnce(Invoke(this, &QuicAlarmTest::ResetAlarm));
+ alarm_.FireAlarm();
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(deadline2_, alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, FireDestroysAlarm) {
+ DestructiveDelegate* delegate(new DestructiveDelegate);
+ DestructiveAlarm* alarm = new DestructiveAlarm(delegate);
+ delegate->set_alarm(alarm);
+ QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
+ alarm->Set(deadline);
+ // This should not crash, even though it will destroy alarm.
+ alarm->FireAlarm();
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h b/chromium/net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h
new file mode 100644
index 00000000000..7c20fc7af4c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h
@@ -0,0 +1,208 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// unique_ptr-style pointer that stores values that may be from an arena. Takes
+// up the same storage as the platform's native pointer type. Takes ownership
+// of the value it's constructed with; if holding a value in an arena, and the
+// type has a non-trivial destructor, the arena must outlive the
+// QuicArenaScopedPtr. Does not support array overloads.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_ARENA_SCOPED_PTR_H_
+#define QUICHE_QUIC_CORE_QUIC_ARENA_SCOPED_PTR_H_
+
+#include <cstdint> // for uintptr_t
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+template <typename T>
+class QuicArenaScopedPtr {
+ static_assert(QUIC_ALIGN_OF(T*) > 1,
+ "QuicArenaScopedPtr can only store objects that are aligned to "
+ "greater than 1 byte.");
+
+ public:
+ // Constructs an empty QuicArenaScopedPtr.
+ QuicArenaScopedPtr();
+
+ // Constructs a QuicArenaScopedPtr referencing the heap-allocated memory
+ // provided.
+ explicit QuicArenaScopedPtr(T* value);
+
+ template <typename U>
+ QuicArenaScopedPtr(QuicArenaScopedPtr<U>&& other); // NOLINT
+ template <typename U>
+ QuicArenaScopedPtr& operator=(QuicArenaScopedPtr<U>&& other);
+ ~QuicArenaScopedPtr();
+
+ // Returns a pointer to the value.
+ T* get() const;
+
+ // Returns a reference to the value.
+ T& operator*() const;
+
+ // Returns a pointer to the value.
+ T* operator->() const;
+
+ // Swaps the value of this pointer with |other|.
+ void swap(QuicArenaScopedPtr& other);
+
+ // Resets the held value to |value|.
+ void reset(T* value = nullptr);
+
+ // Returns true if |this| came from an arena. Primarily exposed for testing
+ // and assertions.
+ bool is_from_arena();
+
+ private:
+ // Friends with other derived types of QuicArenaScopedPtr, to support the
+ // derived-types case.
+ template <typename U>
+ friend class QuicArenaScopedPtr;
+ // Also befriend all known arenas, only to prevent misuse.
+ template <uint32_t ArenaSize>
+ friend class QuicOneBlockArena;
+
+ // Tag to denote that a QuicArenaScopedPtr is being explicitly created by an
+ // arena.
+ enum class ConstructFrom { kHeap, kArena };
+
+ // Constructs a QuicArenaScopedPtr with the given representation.
+ QuicArenaScopedPtr(void* value, ConstructFrom from);
+ QuicArenaScopedPtr(const QuicArenaScopedPtr&) = delete;
+ QuicArenaScopedPtr& operator=(const QuicArenaScopedPtr&) = delete;
+
+ // Low-order bits of value_ that determine if the pointer came from an arena.
+ static const uintptr_t kFromArenaMask = 0x1;
+
+ // Every platform we care about has at least 4B aligned integers, so store the
+ // is_from_arena bit in the least significant bit.
+ void* value_;
+};
+
+template <typename T>
+bool operator==(const QuicArenaScopedPtr<T>& left,
+ const QuicArenaScopedPtr<T>& right) {
+ return left.get() == right.get();
+}
+
+template <typename T>
+bool operator!=(const QuicArenaScopedPtr<T>& left,
+ const QuicArenaScopedPtr<T>& right) {
+ return left.get() != right.get();
+}
+
+template <typename T>
+bool operator==(std::nullptr_t, const QuicArenaScopedPtr<T>& right) {
+ return nullptr == right.get();
+}
+
+template <typename T>
+bool operator!=(std::nullptr_t, const QuicArenaScopedPtr<T>& right) {
+ return nullptr != right.get();
+}
+
+template <typename T>
+bool operator==(const QuicArenaScopedPtr<T>& left, std::nullptr_t) {
+ return left.get() == nullptr;
+}
+
+template <typename T>
+bool operator!=(const QuicArenaScopedPtr<T>& left, std::nullptr_t) {
+ return left.get() != nullptr;
+}
+
+template <typename T>
+QuicArenaScopedPtr<T>::QuicArenaScopedPtr() : value_(nullptr) {}
+
+template <typename T>
+QuicArenaScopedPtr<T>::QuicArenaScopedPtr(T* value)
+ : QuicArenaScopedPtr(value, ConstructFrom::kHeap) {}
+
+template <typename T>
+template <typename U>
+QuicArenaScopedPtr<T>::QuicArenaScopedPtr(QuicArenaScopedPtr<U>&& other)
+ : value_(other.value_) {
+ static_assert(
+ std::is_base_of<T, U>::value || std::is_same<T, U>::value,
+ "Cannot construct QuicArenaScopedPtr; type is not derived or same.");
+ other.value_ = nullptr;
+}
+
+template <typename T>
+template <typename U>
+QuicArenaScopedPtr<T>& QuicArenaScopedPtr<T>::operator=(
+ QuicArenaScopedPtr<U>&& other) {
+ static_assert(
+ std::is_base_of<T, U>::value || std::is_same<T, U>::value,
+ "Cannot assign QuicArenaScopedPtr; type is not derived or same.");
+ swap(other);
+ return *this;
+}
+
+template <typename T>
+QuicArenaScopedPtr<T>::~QuicArenaScopedPtr() {
+ reset();
+}
+
+template <typename T>
+T* QuicArenaScopedPtr<T>::get() const {
+ return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(value_) &
+ ~kFromArenaMask);
+}
+
+template <typename T>
+T& QuicArenaScopedPtr<T>::operator*() const {
+ return *get();
+}
+
+template <typename T>
+T* QuicArenaScopedPtr<T>::operator->() const {
+ return get();
+}
+
+template <typename T>
+void QuicArenaScopedPtr<T>::swap(QuicArenaScopedPtr& other) {
+ using std::swap;
+ swap(value_, other.value_);
+}
+
+template <typename T>
+bool QuicArenaScopedPtr<T>::is_from_arena() {
+ return (reinterpret_cast<uintptr_t>(value_) & kFromArenaMask) != 0;
+}
+
+template <typename T>
+void QuicArenaScopedPtr<T>::reset(T* value) {
+ if (value_ != nullptr) {
+ if (is_from_arena()) {
+ // Manually invoke the destructor.
+ get()->~T();
+ } else {
+ delete get();
+ }
+ }
+ DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(value) & kFromArenaMask);
+ value_ = value;
+}
+
+template <typename T>
+QuicArenaScopedPtr<T>::QuicArenaScopedPtr(void* value, ConstructFrom from_arena)
+ : value_(value) {
+ DCHECK_EQ(0u, reinterpret_cast<uintptr_t>(value_) & kFromArenaMask);
+ switch (from_arena) {
+ case ConstructFrom::kHeap:
+ break;
+ case ConstructFrom::kArena:
+ value_ = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(value_) |
+ QuicArenaScopedPtr<T>::kFromArenaMask);
+ break;
+ }
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_ARENA_SCOPED_PTR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr_test.cc
new file mode 100644
index 00000000000..109fd37cc27
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr_test.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace {
+
+enum class TestParam { kFromHeap, kFromArena };
+
+struct TestObject {
+ explicit TestObject(uintptr_t value) : value(value) { buffer.resize(1024); }
+ uintptr_t value;
+
+ // Ensure that we have a non-trivial destructor that will leak memory if it's
+ // not called.
+ std::vector<char> buffer;
+};
+
+class QuicArenaScopedPtrParamTest : public QuicTestWithParam<TestParam> {
+ protected:
+ QuicArenaScopedPtr<TestObject> CreateObject(uintptr_t value) {
+ QuicArenaScopedPtr<TestObject> ptr;
+ switch (GetParam()) {
+ case TestParam::kFromHeap:
+ ptr = QuicArenaScopedPtr<TestObject>(new TestObject(value));
+ CHECK(!ptr.is_from_arena());
+ break;
+ case TestParam::kFromArena:
+ ptr = arena_.New<TestObject>(value);
+ CHECK(ptr.is_from_arena());
+ break;
+ }
+ return ptr;
+ }
+
+ private:
+ QuicOneBlockArena<1024> arena_;
+};
+
+INSTANTIATE_TEST_SUITE_P(QuicArenaScopedPtrParamTest,
+ QuicArenaScopedPtrParamTest,
+ testing::Values(TestParam::kFromHeap,
+ TestParam::kFromArena));
+
+TEST_P(QuicArenaScopedPtrParamTest, NullObjects) {
+ QuicArenaScopedPtr<TestObject> def;
+ QuicArenaScopedPtr<TestObject> null(nullptr);
+ EXPECT_EQ(def, null);
+ EXPECT_EQ(def, nullptr);
+ EXPECT_EQ(null, nullptr);
+}
+
+TEST_P(QuicArenaScopedPtrParamTest, FromArena) {
+ QuicOneBlockArena<1024> arena_;
+ EXPECT_TRUE(arena_.New<TestObject>(0).is_from_arena());
+ EXPECT_FALSE(
+ QuicArenaScopedPtr<TestObject>(new TestObject(0)).is_from_arena());
+}
+
+TEST_P(QuicArenaScopedPtrParamTest, Assign) {
+ QuicArenaScopedPtr<TestObject> ptr = CreateObject(12345);
+ ptr = CreateObject(54321);
+ EXPECT_EQ(54321u, ptr->value);
+}
+
+TEST_P(QuicArenaScopedPtrParamTest, MoveConstruct) {
+ QuicArenaScopedPtr<TestObject> ptr1 = CreateObject(12345);
+ QuicArenaScopedPtr<TestObject> ptr2(std::move(ptr1));
+ EXPECT_EQ(nullptr, ptr1);
+ EXPECT_EQ(12345u, ptr2->value);
+}
+
+TEST_P(QuicArenaScopedPtrParamTest, Accessors) {
+ QuicArenaScopedPtr<TestObject> ptr = CreateObject(12345);
+ EXPECT_EQ(12345u, (*ptr).value);
+ EXPECT_EQ(12345u, ptr->value);
+ // We explicitly want to test that get() returns a valid pointer to the data,
+ // but the call looks redundant.
+ EXPECT_EQ(12345u, ptr.get()->value); // NOLINT
+}
+
+TEST_P(QuicArenaScopedPtrParamTest, Reset) {
+ QuicArenaScopedPtr<TestObject> ptr = CreateObject(12345);
+ ptr.reset(new TestObject(54321));
+ EXPECT_EQ(54321u, ptr->value);
+}
+
+TEST_P(QuicArenaScopedPtrParamTest, Swap) {
+ QuicArenaScopedPtr<TestObject> ptr1 = CreateObject(12345);
+ QuicArenaScopedPtr<TestObject> ptr2 = CreateObject(54321);
+ ptr1.swap(ptr2);
+ EXPECT_EQ(12345u, ptr2->value);
+ EXPECT_EQ(54321u, ptr1->value);
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth.cc b/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth.cc
new file mode 100644
index 00000000000..a1dbf67c99c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+
+#include <cinttypes>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+std::string QuicBandwidth::ToDebugValue() const {
+ if (bits_per_second_ < 80000) {
+ return QuicStringPrintf("%" PRId64 " bits/s (%" PRId64 " bytes/s)",
+ bits_per_second_, bits_per_second_ / 8);
+ }
+
+ double divisor;
+ char unit;
+ if (bits_per_second_ < 8 * 1000 * 1000) {
+ divisor = 1e3;
+ unit = 'k';
+ } else if (bits_per_second_ < INT64_C(8) * 1000 * 1000 * 1000) {
+ divisor = 1e6;
+ unit = 'M';
+ } else {
+ divisor = 1e9;
+ unit = 'G';
+ }
+
+ double bits_per_second_with_unit = bits_per_second_ / divisor;
+ double bytes_per_second_with_unit = bits_per_second_with_unit / 8;
+ return QuicStringPrintf("%.2f %cbits/s (%.2f %cbytes/s)",
+ bits_per_second_with_unit, unit,
+ bytes_per_second_with_unit, unit);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth.h b/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth.h
new file mode 100644
index 00000000000..5b747446d18
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth.h
@@ -0,0 +1,151 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// QuicBandwidth represents a bandwidth, stored in bits per second resolution.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_
+#define QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_
+
+#include <cmath>
+#include <cstdint>
+#include <limits>
+#include <ostream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicBandwidth {
+ public:
+ // Creates a new QuicBandwidth with an internal value of 0.
+ static constexpr QuicBandwidth Zero() { return QuicBandwidth(0); }
+
+ // Creates a new QuicBandwidth with an internal value of INT64_MAX.
+ static constexpr QuicBandwidth Infinite() {
+ return QuicBandwidth(std::numeric_limits<int64_t>::max());
+ }
+
+ // Create a new QuicBandwidth holding the bits per second.
+ static constexpr QuicBandwidth FromBitsPerSecond(int64_t bits_per_second) {
+ return QuicBandwidth(bits_per_second);
+ }
+
+ // Create a new QuicBandwidth holding the kilo bits per second.
+ static constexpr QuicBandwidth FromKBitsPerSecond(int64_t k_bits_per_second) {
+ return QuicBandwidth(k_bits_per_second * 1000);
+ }
+
+ // Create a new QuicBandwidth holding the bytes per second.
+ static constexpr QuicBandwidth FromBytesPerSecond(int64_t bytes_per_second) {
+ return QuicBandwidth(bytes_per_second * 8);
+ }
+
+ // Create a new QuicBandwidth holding the kilo bytes per second.
+ static constexpr QuicBandwidth FromKBytesPerSecond(
+ int64_t k_bytes_per_second) {
+ return QuicBandwidth(k_bytes_per_second * 8000);
+ }
+
+ // Create a new QuicBandwidth based on the bytes per the elapsed delta.
+ static inline QuicBandwidth FromBytesAndTimeDelta(QuicByteCount bytes,
+ QuicTime::Delta delta) {
+ return QuicBandwidth((bytes * kNumMicrosPerSecond) /
+ delta.ToMicroseconds() * 8);
+ }
+
+ inline int64_t ToBitsPerSecond() const { return bits_per_second_; }
+
+ inline int64_t ToKBitsPerSecond() const { return bits_per_second_ / 1000; }
+
+ inline int64_t ToBytesPerSecond() const { return bits_per_second_ / 8; }
+
+ inline int64_t ToKBytesPerSecond() const { return bits_per_second_ / 8000; }
+
+ inline QuicByteCount ToBytesPerPeriod(QuicTime::Delta time_period) const {
+ return ToBytesPerSecond() * time_period.ToMicroseconds() /
+ kNumMicrosPerSecond;
+ }
+
+ inline int64_t ToKBytesPerPeriod(QuicTime::Delta time_period) const {
+ return ToKBytesPerSecond() * time_period.ToMicroseconds() /
+ kNumMicrosPerSecond;
+ }
+
+ inline bool IsZero() const { return bits_per_second_ == 0; }
+
+ inline QuicTime::Delta TransferTime(QuicByteCount bytes) const {
+ if (bits_per_second_ == 0) {
+ return QuicTime::Delta::Zero();
+ }
+ return QuicTime::Delta::FromMicroseconds(bytes * 8 * kNumMicrosPerSecond /
+ bits_per_second_);
+ }
+
+ std::string ToDebugValue() const;
+
+ private:
+ explicit constexpr QuicBandwidth(int64_t bits_per_second)
+ : bits_per_second_(bits_per_second >= 0 ? bits_per_second : 0) {}
+
+ int64_t bits_per_second_;
+
+ friend QuicBandwidth operator+(QuicBandwidth lhs, QuicBandwidth rhs);
+ friend QuicBandwidth operator-(QuicBandwidth lhs, QuicBandwidth rhs);
+ friend QuicBandwidth operator*(QuicBandwidth lhs, float factor);
+};
+
+// Non-member relational operators for QuicBandwidth.
+inline bool operator==(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return lhs.ToBitsPerSecond() == rhs.ToBitsPerSecond();
+}
+inline bool operator!=(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return !(lhs == rhs);
+}
+inline bool operator<(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return lhs.ToBitsPerSecond() < rhs.ToBitsPerSecond();
+}
+inline bool operator>(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return rhs < lhs;
+}
+inline bool operator<=(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return !(rhs < lhs);
+}
+inline bool operator>=(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return !(lhs < rhs);
+}
+
+// Non-member arithmetic operators for QuicBandwidth.
+inline QuicBandwidth operator+(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return QuicBandwidth(lhs.bits_per_second_ + rhs.bits_per_second_);
+}
+inline QuicBandwidth operator-(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return QuicBandwidth(lhs.bits_per_second_ - rhs.bits_per_second_);
+}
+inline QuicBandwidth operator*(QuicBandwidth lhs, float rhs) {
+ return QuicBandwidth(
+ static_cast<int64_t>(std::llround(lhs.bits_per_second_ * rhs)));
+}
+inline QuicBandwidth operator*(float lhs, QuicBandwidth rhs) {
+ return rhs * lhs;
+}
+inline QuicByteCount operator*(QuicBandwidth lhs, QuicTime::Delta rhs) {
+ return lhs.ToBytesPerPeriod(rhs);
+}
+inline QuicByteCount operator*(QuicTime::Delta lhs, QuicBandwidth rhs) {
+ return rhs * lhs;
+}
+
+// Override stream output operator for gtest.
+inline std::ostream& operator<<(std::ostream& output,
+ const QuicBandwidth bandwidth) {
+ output << bandwidth.ToDebugValue();
+ return output;
+}
+
+} // namespace quic
+#endif // QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth_test.cc
new file mode 100644
index 00000000000..ed23dac1675
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_bandwidth_test.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class QuicBandwidthTest : public QuicTest {};
+
+TEST_F(QuicBandwidthTest, FromTo) {
+ EXPECT_EQ(QuicBandwidth::FromKBitsPerSecond(1),
+ QuicBandwidth::FromBitsPerSecond(1000));
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1),
+ QuicBandwidth::FromBytesPerSecond(1000));
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(8000),
+ QuicBandwidth::FromBytesPerSecond(1000));
+ EXPECT_EQ(QuicBandwidth::FromKBitsPerSecond(8),
+ QuicBandwidth::FromKBytesPerSecond(1));
+
+ EXPECT_EQ(0, QuicBandwidth::Zero().ToBitsPerSecond());
+ EXPECT_EQ(0, QuicBandwidth::Zero().ToKBitsPerSecond());
+ EXPECT_EQ(0, QuicBandwidth::Zero().ToBytesPerSecond());
+ EXPECT_EQ(0, QuicBandwidth::Zero().ToKBytesPerSecond());
+
+ EXPECT_EQ(1, QuicBandwidth::FromBitsPerSecond(1000).ToKBitsPerSecond());
+ EXPECT_EQ(1000, QuicBandwidth::FromKBitsPerSecond(1).ToBitsPerSecond());
+ EXPECT_EQ(1, QuicBandwidth::FromBytesPerSecond(1000).ToKBytesPerSecond());
+ EXPECT_EQ(1000, QuicBandwidth::FromKBytesPerSecond(1).ToBytesPerSecond());
+}
+
+TEST_F(QuicBandwidthTest, Add) {
+ QuicBandwidth bandwidht_1 = QuicBandwidth::FromKBitsPerSecond(1);
+ QuicBandwidth bandwidht_2 = QuicBandwidth::FromKBytesPerSecond(1);
+
+ EXPECT_EQ(9000, (bandwidht_1 + bandwidht_2).ToBitsPerSecond());
+ EXPECT_EQ(9000, (bandwidht_2 + bandwidht_1).ToBitsPerSecond());
+}
+
+TEST_F(QuicBandwidthTest, Subtract) {
+ QuicBandwidth bandwidht_1 = QuicBandwidth::FromKBitsPerSecond(1);
+ QuicBandwidth bandwidht_2 = QuicBandwidth::FromKBytesPerSecond(1);
+
+ EXPECT_EQ(7000, (bandwidht_2 - bandwidht_1).ToBitsPerSecond());
+}
+
+TEST_F(QuicBandwidthTest, TimeDelta) {
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1000),
+ QuicBandwidth::FromBytesAndTimeDelta(
+ 1000, QuicTime::Delta::FromMilliseconds(1)));
+
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(10),
+ QuicBandwidth::FromBytesAndTimeDelta(
+ 1000, QuicTime::Delta::FromMilliseconds(100)));
+}
+
+TEST_F(QuicBandwidthTest, Scale) {
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(500),
+ QuicBandwidth::FromKBytesPerSecond(1000) * 0.5f);
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(750),
+ 0.75f * QuicBandwidth::FromKBytesPerSecond(1000));
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1250),
+ QuicBandwidth::FromKBytesPerSecond(1000) * 1.25f);
+
+ // Ensure we are rounding correctly within a 1bps level of precision.
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(5),
+ QuicBandwidth::FromBitsPerSecond(9) * 0.5f);
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(2),
+ QuicBandwidth::FromBitsPerSecond(12) * 0.2f);
+}
+
+TEST_F(QuicBandwidthTest, BytesPerPeriod) {
+ EXPECT_EQ(2000u, QuicBandwidth::FromKBytesPerSecond(2000).ToBytesPerPeriod(
+ QuicTime::Delta::FromMilliseconds(1)));
+ EXPECT_EQ(2u, QuicBandwidth::FromKBytesPerSecond(2000).ToKBytesPerPeriod(
+ QuicTime::Delta::FromMilliseconds(1)));
+ EXPECT_EQ(200000u, QuicBandwidth::FromKBytesPerSecond(2000).ToBytesPerPeriod(
+ QuicTime::Delta::FromMilliseconds(100)));
+ EXPECT_EQ(200u, QuicBandwidth::FromKBytesPerSecond(2000).ToKBytesPerPeriod(
+ QuicTime::Delta::FromMilliseconds(100)));
+}
+
+TEST_F(QuicBandwidthTest, TransferTime) {
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(1),
+ QuicBandwidth::FromKBytesPerSecond(1).TransferTime(1000));
+ EXPECT_EQ(QuicTime::Delta::Zero(), QuicBandwidth::Zero().TransferTime(1000));
+}
+
+TEST_F(QuicBandwidthTest, RelOps) {
+ const QuicBandwidth b1 = QuicBandwidth::FromKBitsPerSecond(1);
+ const QuicBandwidth b2 = QuicBandwidth::FromKBytesPerSecond(2);
+ EXPECT_EQ(b1, b1);
+ EXPECT_NE(b1, b2);
+ EXPECT_LT(b1, b2);
+ EXPECT_GT(b2, b1);
+ EXPECT_LE(b1, b1);
+ EXPECT_LE(b1, b2);
+ EXPECT_GE(b1, b1);
+ EXPECT_GE(b2, b1);
+}
+
+TEST_F(QuicBandwidthTest, DebugValue) {
+ EXPECT_EQ("128 bits/s (16 bytes/s)",
+ QuicBandwidth::FromBytesPerSecond(16).ToDebugValue());
+ EXPECT_EQ("4096 bits/s (512 bytes/s)",
+ QuicBandwidth::FromBytesPerSecond(512).ToDebugValue());
+
+ QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(1000 * 50);
+ EXPECT_EQ("400.00 kbits/s (50.00 kbytes/s)", bandwidth.ToDebugValue());
+
+ bandwidth = bandwidth * 1000;
+ EXPECT_EQ("400.00 Mbits/s (50.00 Mbytes/s)", bandwidth.ToDebugValue());
+
+ bandwidth = bandwidth * 1000;
+ EXPECT_EQ("400.00 Gbits/s (50.00 Gbytes/s)", bandwidth.ToDebugValue());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h b/chromium/net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h
new file mode 100644
index 00000000000..8193b252244
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is an interface for all objects that want to be notified that
+// the underlying UDP socket is available for writing (not write blocked
+// anymore).
+
+#ifndef QUICHE_QUIC_CORE_QUIC_BLOCKED_WRITER_INTERFACE_H_
+#define QUICHE_QUIC_CORE_QUIC_BLOCKED_WRITER_INTERFACE_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicBlockedWriterInterface {
+ public:
+ virtual ~QuicBlockedWriterInterface() {}
+
+ // Called by the PacketWriter when the underlying socket becomes writable
+ // so that the BlockedWriter can go ahead and try writing.
+ virtual void OnBlockedWriterCanWrite() = 0;
+
+ virtual bool IsWriterBlocked() const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_BLOCKED_WRITER_INTERFACE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.cc
new file mode 100644
index 00000000000..c3802743cc6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+
+namespace quic {
+
+QuicBufferAllocator::~QuicBufferAllocator() = default;
+
+} // namespace quic
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
new file mode 100644
index 00000000000..10df3692f37
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffer_allocator.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_BUFFER_ALLOCATOR_H_
+#define QUICHE_QUIC_CORE_QUIC_BUFFER_ALLOCATOR_H_
+
+#include <stddef.h>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Abstract base class for classes which allocate and delete buffers.
+class QUIC_EXPORT_PRIVATE QuicBufferAllocator {
+ public:
+ virtual ~QuicBufferAllocator();
+
+ // Returns or allocates a new buffer of |size|. Never returns null.
+ virtual char* New(size_t size) = 0;
+
+ // Returns or allocates a new buffer of |size| if |flag_enable| is true.
+ // Otherwise, returns a buffer that is compatible with this class directly
+ // with operator new. Never returns null.
+ virtual char* New(size_t size, bool flag_enable) = 0;
+
+ // Releases a buffer.
+ virtual void Delete(char* buffer) = 0;
+
+ // Marks the allocator as being idle. Serves as a hint to notify the allocator
+ // that it should release any resources it's still holding on to.
+ virtual void MarkAllocatorIdle() {}
+};
+
+} // 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
new file mode 100644
index 00000000000..463cdc4cbbc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+
+namespace quic {
+
+typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket;
+typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList;
+typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult;
+
+// Max number of connections this store can keep track.
+static const size_t kDefaultMaxConnectionsInStore = 100;
+// Up to half of the capacity can be used for storing non-CHLO packets.
+static const size_t kMaxConnectionsWithoutCHLO =
+ kDefaultMaxConnectionsInStore / 2;
+
+namespace {
+
+// This alarm removes expired entries in map each time this alarm fires.
+class ConnectionExpireAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit ConnectionExpireAlarm(QuicBufferedPacketStore* store)
+ : connection_store_(store) {}
+
+ void OnAlarm() override { connection_store_->OnExpirationTimeout(); }
+
+ ConnectionExpireAlarm(const ConnectionExpireAlarm&) = delete;
+ ConnectionExpireAlarm& operator=(const ConnectionExpireAlarm&) = delete;
+
+ private:
+ QuicBufferedPacketStore* connection_store_;
+};
+
+} // namespace
+
+BufferedPacket::BufferedPacket(std::unique_ptr<QuicReceivedPacket> packet,
+ QuicSocketAddress self_address,
+ QuicSocketAddress peer_address)
+ : packet(std::move(packet)),
+ self_address(self_address),
+ peer_address(peer_address) {}
+
+BufferedPacket::BufferedPacket(BufferedPacket&& other) = default;
+
+BufferedPacket& BufferedPacket::operator=(BufferedPacket&& other) = default;
+
+BufferedPacket::~BufferedPacket() {}
+
+BufferedPacketList::BufferedPacketList()
+ : creation_time(QuicTime::Zero()),
+ ietf_quic(false),
+ version(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED) {}
+
+BufferedPacketList::BufferedPacketList(BufferedPacketList&& other) = default;
+
+BufferedPacketList& BufferedPacketList::operator=(BufferedPacketList&& other) =
+ default;
+
+BufferedPacketList::~BufferedPacketList() {}
+
+QuicBufferedPacketStore::QuicBufferedPacketStore(
+ VisitorInterface* visitor,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory)
+ : connection_life_span_(
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs)),
+ visitor_(visitor),
+ clock_(clock),
+ expiration_alarm_(
+ alarm_factory->CreateAlarm(new ConnectionExpireAlarm(this))) {}
+
+QuicBufferedPacketStore::~QuicBufferedPacketStore() {}
+
+EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ const QuicReceivedPacket& packet,
+ QuicSocketAddress self_address,
+ QuicSocketAddress peer_address,
+ bool is_chlo,
+ const std::string& alpn,
+ const ParsedQuicVersion& version) {
+ QUIC_BUG_IF(!FLAGS_quic_allow_chlo_buffering)
+ << "Shouldn't buffer packets if disabled via flag.";
+ QUIC_BUG_IF(is_chlo && QuicContainsKey(connections_with_chlo_, connection_id))
+ << "Shouldn't buffer duplicated CHLO on connection " << connection_id;
+ QUIC_BUG_IF(!is_chlo && !alpn.empty())
+ << "Shouldn't have an ALPN defined for a non-CHLO packet.";
+ QUIC_BUG_IF(is_chlo && version.transport_version == QUIC_VERSION_UNSUPPORTED)
+ << "Should have version for CHLO packet.";
+
+ if (!QuicContainsKey(undecryptable_packets_, connection_id) &&
+ ShouldBufferPacket(is_chlo)) {
+ // Drop the packet if the upper limit of undecryptable packets has been
+ // reached or the whole capacity of the store has been reached.
+ return TOO_MANY_CONNECTIONS;
+ } else if (!QuicContainsKey(undecryptable_packets_, connection_id)) {
+ undecryptable_packets_.emplace(
+ std::make_pair(connection_id, BufferedPacketList()));
+ undecryptable_packets_.back().second.ietf_quic = ietf_quic;
+ undecryptable_packets_.back().second.version = version;
+ }
+ CHECK(QuicContainsKey(undecryptable_packets_, 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();
+ if (num_non_chlo_packets >= kDefaultMaxUndecryptablePackets) {
+ // If there are kMaxBufferedPacketsPerConnection packets buffered up for
+ // this connection, drop the current packet.
+ return TOO_MANY_PACKETS;
+ }
+ }
+
+ if (queue.buffered_packets.empty()) {
+ // If this is the first packet arrived on a new connection, initialize the
+ // creation time.
+ queue.creation_time = clock_->ApproximateNow();
+ }
+
+ BufferedPacket new_entry(std::unique_ptr<QuicReceivedPacket>(packet.Clone()),
+ self_address, peer_address);
+ if (is_chlo) {
+ // Add CHLO to the beginning of buffered packets so that it can be delivered
+ // first later.
+ queue.buffered_packets.push_front(std::move(new_entry));
+ queue.alpn = alpn;
+ connections_with_chlo_[connection_id] = false; // Dummy value.
+ // Set the version of buffered packets of this connection on CHLO.
+ queue.version = version;
+ } else {
+ // Buffer non-CHLO packets in arrival order.
+ queue.buffered_packets.push_back(std::move(new_entry));
+ }
+ MaybeSetExpirationAlarm();
+ return SUCCESS;
+}
+
+bool QuicBufferedPacketStore::HasBufferedPackets(
+ QuicConnectionId connection_id) const {
+ return QuicContainsKey(undecryptable_packets_, connection_id);
+}
+
+bool QuicBufferedPacketStore::HasChlosBuffered() const {
+ return !connections_with_chlo_.empty();
+}
+
+BufferedPacketList QuicBufferedPacketStore::DeliverPackets(
+ QuicConnectionId connection_id) {
+ BufferedPacketList packets_to_deliver;
+ auto it = undecryptable_packets_.find(connection_id);
+ if (it != undecryptable_packets_.end()) {
+ packets_to_deliver = std::move(it->second);
+ undecryptable_packets_.erase(connection_id);
+ }
+ return packets_to_deliver;
+}
+
+void QuicBufferedPacketStore::DiscardPackets(QuicConnectionId connection_id) {
+ undecryptable_packets_.erase(connection_id);
+ connections_with_chlo_.erase(connection_id);
+}
+
+void QuicBufferedPacketStore::OnExpirationTimeout() {
+ QuicTime expiration_time = clock_->ApproximateNow() - connection_life_span_;
+ while (!undecryptable_packets_.empty()) {
+ auto& entry = undecryptable_packets_.front();
+ if (entry.second.creation_time > expiration_time) {
+ break;
+ }
+ QuicConnectionId connection_id = entry.first;
+ visitor_->OnExpiredPackets(connection_id, std::move(entry.second));
+ undecryptable_packets_.pop_front();
+ connections_with_chlo_.erase(connection_id);
+ }
+ if (!undecryptable_packets_.empty()) {
+ MaybeSetExpirationAlarm();
+ }
+}
+
+void QuicBufferedPacketStore::MaybeSetExpirationAlarm() {
+ if (!expiration_alarm_->IsSet()) {
+ expiration_alarm_->Set(clock_->ApproximateNow() + connection_life_span_);
+ }
+}
+
+bool QuicBufferedPacketStore::ShouldBufferPacket(bool is_chlo) {
+ bool is_store_full =
+ undecryptable_packets_.size() >= kDefaultMaxConnectionsInStore;
+
+ if (is_chlo) {
+ return is_store_full;
+ }
+
+ size_t num_connections_without_chlo =
+ undecryptable_packets_.size() - connections_with_chlo_.size();
+ bool reach_non_chlo_limit =
+ num_connections_without_chlo >= kMaxConnectionsWithoutCHLO;
+
+ return is_store_full || reach_non_chlo_limit;
+}
+
+BufferedPacketList QuicBufferedPacketStore::DeliverPacketsForNextConnection(
+ QuicConnectionId* connection_id) {
+ if (connections_with_chlo_.empty()) {
+ // Returns empty list if no CHLO has been buffered.
+ return BufferedPacketList();
+ }
+ *connection_id = connections_with_chlo_.front().first;
+ connections_with_chlo_.pop_front();
+
+ BufferedPacketList packets = DeliverPackets(*connection_id);
+ DCHECK(!packets.buffered_packets.empty())
+ << "Try to deliver connectons without CHLO";
+ return packets;
+}
+
+bool QuicBufferedPacketStore::HasChloForConnection(
+ QuicConnectionId connection_id) {
+ return QuicContainsKey(connections_with_chlo_, connection_id);
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..09bc2cc56c3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h
@@ -0,0 +1,178 @@
+// 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 QUICHE_QUIC_CORE_QUIC_BUFFERED_PACKET_STORE_H_
+#define QUICHE_QUIC_CORE_QUIC_BUFFERED_PACKET_STORE_H_
+
+#include <list>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+namespace test {
+class QuicBufferedPacketStorePeer;
+} // namespace test
+
+// This class buffers packets for each connection until either
+// 1) They are requested to be delivered via
+// DeliverPacket()/DeliverPacketsForNextConnection(), or
+// 2) They expire after exceeding their lifetime in the store.
+//
+// It can only buffer packets on certain number of connections. It has two pools
+// of connections: connections with CHLO buffered and those without CHLO. The
+// latter has its own upper limit along with the max number of connections this
+// store can hold. The former pool can grow till this store is full.
+class QUIC_EXPORT_PRIVATE QuicBufferedPacketStore {
+ public:
+ enum EnqueuePacketResult {
+ SUCCESS = 0,
+ TOO_MANY_PACKETS, // Too many packets stored up for a certain connection.
+ TOO_MANY_CONNECTIONS // Too many connections stored up in the store.
+ };
+
+ struct QUIC_EXPORT_PRIVATE BufferedPacket {
+ BufferedPacket(std::unique_ptr<QuicReceivedPacket> packet,
+ QuicSocketAddress self_address,
+ QuicSocketAddress peer_address);
+ BufferedPacket(BufferedPacket&& other);
+
+ BufferedPacket& operator=(BufferedPacket&& other);
+
+ ~BufferedPacket();
+
+ std::unique_ptr<QuicReceivedPacket> packet;
+ QuicSocketAddress self_address;
+ QuicSocketAddress peer_address;
+ };
+
+ // A queue of BufferedPackets for a connection.
+ struct QUIC_EXPORT_PRIVATE BufferedPacketList {
+ BufferedPacketList();
+ BufferedPacketList(BufferedPacketList&& other);
+
+ BufferedPacketList& operator=(BufferedPacketList&& other);
+
+ ~BufferedPacketList();
+
+ std::list<BufferedPacket> buffered_packets;
+ QuicTime creation_time;
+ // The alpn from the CHLO, if one was found.
+ std::string alpn;
+ // Indicating whether this is an IETF QUIC connection.
+ bool ietf_quic;
+ // If buffered_packets contains the CHLO, it is the version of the CHLO.
+ // Otherwise, it is the version of the first packet in |buffered_packets|.
+ ParsedQuicVersion version;
+ };
+
+ typedef QuicLinkedHashMap<QuicConnectionId,
+ BufferedPacketList,
+ QuicConnectionIdHash>
+ BufferedPacketMap;
+
+ class QUIC_EXPORT_PRIVATE VisitorInterface {
+ public:
+ virtual ~VisitorInterface() {}
+
+ // Called for each expired connection when alarm fires.
+ virtual void OnExpiredPackets(QuicConnectionId connection_id,
+ BufferedPacketList early_arrived_packets) = 0;
+ };
+
+ QuicBufferedPacketStore(VisitorInterface* vistor,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory);
+
+ QuicBufferedPacketStore(const QuicBufferedPacketStore&) = delete;
+
+ ~QuicBufferedPacketStore();
+
+ QuicBufferedPacketStore& operator=(const QuicBufferedPacketStore&) = delete;
+
+ // Adds a copy of packet into packet queue for given connection.
+ // TODO(danzh): Consider to split this method to EnqueueChlo() and
+ // EnqueueDataPacket().
+ EnqueuePacketResult EnqueuePacket(QuicConnectionId connection_id,
+ bool ietf_quic,
+ const QuicReceivedPacket& packet,
+ QuicSocketAddress self_address,
+ QuicSocketAddress peer_address,
+ bool is_chlo,
+ const std::string& alpn,
+ const ParsedQuicVersion& version);
+
+ // Returns true if there are any packets buffered for |connection_id|.
+ bool HasBufferedPackets(QuicConnectionId connection_id) const;
+
+ // Returns the list of buffered packets for |connection_id| and removes them
+ // from the store. Returns an empty list if no early arrived packets for this
+ // connection are present.
+ BufferedPacketList DeliverPackets(QuicConnectionId connection_id);
+
+ // Discards packets buffered for |connection_id|, if any.
+ void DiscardPackets(QuicConnectionId connection_id);
+
+ // Examines how long packets have been buffered in the store for each
+ // connection. If they stay too long, removes them for new coming packets and
+ // calls |visitor_|'s OnPotentialConnectionExpire().
+ // Resets the alarm at the end.
+ void OnExpirationTimeout();
+
+ // Delivers buffered packets for next connection with CHLO to open.
+ // Return connection id for next connection in |connection_id|
+ // and all buffered packets including CHLO.
+ // The returned list should at least has one packet(CHLO) if
+ // store does have any connection to open. If no connection in the store has
+ // received CHLO yet, empty list will be returned.
+ BufferedPacketList DeliverPacketsForNextConnection(
+ QuicConnectionId* connection_id);
+
+ // Is given connection already buffered in the store?
+ bool HasChloForConnection(QuicConnectionId connection_id);
+
+ // Is there any CHLO buffered in the store?
+ bool HasChlosBuffered() const;
+
+ private:
+ friend class test::QuicBufferedPacketStorePeer;
+
+ // Set expiration alarm if it hasn't been set.
+ void MaybeSetExpirationAlarm();
+
+ // Return true if add an extra packet will go beyond allowed max connection
+ // limit. The limit for non-CHLO packet and CHLO packet is different.
+ bool ShouldBufferPacket(bool is_chlo);
+
+ // A map to store packet queues with creation time for each connection.
+ BufferedPacketMap undecryptable_packets_;
+
+ // The max time the packets of a connection can be buffer in the store.
+ const QuicTime::Delta connection_life_span_;
+
+ VisitorInterface* visitor_; // Unowned.
+
+ const QuicClock* clock_; // Unowned.
+
+ // This alarm fires every |connection_life_span_| to clean up
+ // packets staying in the store for too long.
+ std::unique_ptr<QuicAlarm> expiration_alarm_;
+
+ // Keeps track of connection with CHLO buffered up already and the order they
+ // arrive.
+ QuicLinkedHashMap<QuicConnectionId, bool, QuicConnectionIdHash>
+ connections_with_chlo_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_BUFFERED_PACKET_STORE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc
new file mode 100644
index 00000000000..466dffb913a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc
@@ -0,0 +1,441 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h"
+
+#include <list>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket;
+typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult;
+
+static const size_t kDefaultMaxConnectionsInStore = 100;
+static const size_t kMaxConnectionsWithoutCHLO =
+ kDefaultMaxConnectionsInStore / 2;
+
+namespace test {
+namespace {
+
+typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket;
+typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList;
+
+class QuicBufferedPacketStoreVisitor
+ : public QuicBufferedPacketStore::VisitorInterface {
+ public:
+ QuicBufferedPacketStoreVisitor() {}
+
+ ~QuicBufferedPacketStoreVisitor() override {}
+
+ void OnExpiredPackets(QuicConnectionId connection_id,
+ BufferedPacketList early_arrived_packets) override {
+ last_expired_packet_queue_ = std::move(early_arrived_packets);
+ }
+
+ // The packets queue for most recently expirect connection.
+ BufferedPacketList last_expired_packet_queue_;
+};
+
+class QuicBufferedPacketStoreTest : public QuicTest {
+ public:
+ QuicBufferedPacketStoreTest()
+ : store_(&visitor_, &clock_, &alarm_factory_),
+ self_address_(QuicIpAddress::Any6(), 65535),
+ peer_address_(QuicIpAddress::Any6(), 65535),
+ packet_content_("some encrypted content"),
+ packet_time_(QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(42)),
+ packet_(packet_content_.data(), packet_content_.size(), packet_time_),
+ invalid_version_(UnsupportedQuicVersion()),
+ valid_version_(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44) {}
+
+ protected:
+ QuicBufferedPacketStoreVisitor visitor_;
+ MockClock clock_;
+ MockAlarmFactory alarm_factory_;
+ QuicBufferedPacketStore store_;
+ QuicSocketAddress self_address_;
+ QuicSocketAddress peer_address_;
+ std::string packet_content_;
+ QuicTime packet_time_;
+ QuicReceivedPacket packet_;
+ const ParsedQuicVersion invalid_version_;
+ const ParsedQuicVersion valid_version_;
+};
+
+TEST_F(QuicBufferedPacketStoreTest, SimpleEnqueueAndDeliverPacket) {
+ QuicConnectionId connection_id = TestConnectionId(1);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ EXPECT_TRUE(store_.HasBufferedPackets(connection_id));
+ auto packets = store_.DeliverPackets(connection_id);
+ const std::list<BufferedPacket>& queue = packets.buffered_packets;
+ ASSERT_EQ(1u, queue.size());
+ // The alpn should be ignored for non-chlo packets.
+ ASSERT_EQ("", packets.alpn);
+ // There is no valid version because CHLO has not arrived.
+ EXPECT_EQ(invalid_version_, packets.version);
+ // Check content of the only packet in the queue.
+ EXPECT_EQ(packet_content_, queue.front().packet->AsStringPiece());
+ EXPECT_EQ(packet_time_, queue.front().packet->receipt_time());
+ EXPECT_EQ(peer_address_, queue.front().peer_address);
+ EXPECT_EQ(self_address_, queue.front().self_address);
+ // No more packets on connection 1 should remain in the store.
+ EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty());
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
+}
+
+TEST_F(QuicBufferedPacketStoreTest, DifferentPacketAddressOnOneConnection) {
+ QuicSocketAddress addr_with_new_port(QuicIpAddress::Any4(), 256);
+ QuicConnectionId connection_id = TestConnectionId(1);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ addr_with_new_port, false, "", invalid_version_);
+ std::list<BufferedPacket> queue =
+ store_.DeliverPackets(connection_id).buffered_packets;
+ ASSERT_EQ(2u, queue.size());
+ // The address migration path should be preserved.
+ EXPECT_EQ(peer_address_, queue.front().peer_address);
+ EXPECT_EQ(addr_with_new_port, queue.back().peer_address);
+}
+
+TEST_F(QuicBufferedPacketStoreTest,
+ EnqueueAndDeliverMultiplePacketsOnMultipleConnections) {
+ size_t num_connections = 10;
+ for (uint64_t conn_id = 1; conn_id <= num_connections; ++conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ }
+
+ // Deliver packets in reversed order.
+ for (uint64_t conn_id = num_connections; conn_id > 0; --conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ std::list<BufferedPacket> queue =
+ store_.DeliverPackets(connection_id).buffered_packets;
+ ASSERT_EQ(2u, queue.size());
+ }
+}
+
+TEST_F(QuicBufferedPacketStoreTest,
+ FailToBufferTooManyPacketsOnExistingConnection) {
+ // Tests that for one connection, only limited number of packets can be
+ // buffered.
+ size_t num_packets = kDefaultMaxUndecryptablePackets + 1;
+ QuicConnectionId connection_id = TestConnectionId(1);
+ // Arrived CHLO packet shouldn't affect how many non-CHLO pacekts store can
+ // keep.
+ EXPECT_EQ(QuicBufferedPacketStore::SUCCESS,
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, true, "", valid_version_));
+ for (size_t i = 1; i <= num_packets; ++i) {
+ // Only first |kDefaultMaxUndecryptablePackets packets| will be buffered.
+ EnqueuePacketResult result =
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ if (i <= kDefaultMaxUndecryptablePackets) {
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS, result);
+ } else {
+ EXPECT_EQ(EnqueuePacketResult::TOO_MANY_PACKETS, result);
+ }
+ }
+
+ // Only first |kDefaultMaxUndecryptablePackets| non-CHLO packets and CHLO are
+ // buffered.
+ EXPECT_EQ(kDefaultMaxUndecryptablePackets + 1,
+ store_.DeliverPackets(connection_id).buffered_packets.size());
+}
+
+TEST_F(QuicBufferedPacketStoreTest, ReachNonChloConnectionUpperLimit) {
+ // Tests that store can only keep early arrived packets for limited number of
+ // connections.
+ const size_t kNumConnections = kMaxConnectionsWithoutCHLO + 1;
+ for (uint64_t conn_id = 1; conn_id <= kNumConnections; ++conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ EnqueuePacketResult result =
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ if (conn_id <= kMaxConnectionsWithoutCHLO) {
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS, result);
+ } else {
+ EXPECT_EQ(EnqueuePacketResult::TOO_MANY_CONNECTIONS, result);
+ }
+ }
+ // Store only keeps early arrived packets upto |kNumConnections| connections.
+ for (uint64_t conn_id = 1; conn_id <= kNumConnections; ++conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ std::list<BufferedPacket> queue =
+ store_.DeliverPackets(connection_id).buffered_packets;
+ if (conn_id <= kMaxConnectionsWithoutCHLO) {
+ EXPECT_EQ(1u, queue.size());
+ } else {
+ EXPECT_EQ(0u, queue.size());
+ }
+ }
+}
+
+TEST_F(QuicBufferedPacketStoreTest,
+ FullStoreFailToBufferDataPacketOnNewConnection) {
+ // Send enough CHLOs so that store gets full before number of connections
+ // without CHLO reaches its upper limit.
+ size_t num_chlos =
+ kDefaultMaxConnectionsInStore - kMaxConnectionsWithoutCHLO + 1;
+ for (uint64_t conn_id = 1; conn_id <= num_chlos; ++conn_id) {
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS,
+ store_.EnqueuePacket(TestConnectionId(conn_id), false, packet_,
+ self_address_, peer_address_, true, "",
+ valid_version_));
+ }
+
+ // Send data packets on another |kMaxConnectionsWithoutCHLO| connections.
+ // Store should only be able to buffer till it's full.
+ for (uint64_t conn_id = num_chlos + 1;
+ conn_id <= (kDefaultMaxConnectionsInStore + 1); ++conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ EnqueuePacketResult result =
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, true, "", valid_version_);
+ if (conn_id <= kDefaultMaxConnectionsInStore) {
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS, result);
+ } else {
+ EXPECT_EQ(EnqueuePacketResult::TOO_MANY_CONNECTIONS, result);
+ }
+ }
+}
+
+TEST_F(QuicBufferedPacketStoreTest, EnqueueChloOnTooManyDifferentConnections) {
+ // Buffer data packets on different connections upto limit.
+ for (uint64_t conn_id = 1; conn_id <= kMaxConnectionsWithoutCHLO; ++conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS,
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_));
+ }
+
+ // Buffer CHLOs on other connections till store is full.
+ for (size_t i = kMaxConnectionsWithoutCHLO + 1;
+ i <= kDefaultMaxConnectionsInStore + 1; ++i) {
+ QuicConnectionId connection_id = TestConnectionId(i);
+ EnqueuePacketResult rs =
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, true, "", valid_version_);
+ if (i <= kDefaultMaxConnectionsInStore) {
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS, rs);
+ EXPECT_TRUE(store_.HasChloForConnection(connection_id));
+ } else {
+ // Last CHLO can't be buffered because store is full.
+ EXPECT_EQ(EnqueuePacketResult::TOO_MANY_CONNECTIONS, rs);
+ EXPECT_FALSE(store_.HasChloForConnection(connection_id));
+ }
+ }
+
+ // But buffering a CHLO belonging to a connection already has data packet
+ // buffered in the store should success. This is the connection should be
+ // delivered at last.
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS,
+ store_.EnqueuePacket(
+ /*connection_id=*/TestConnectionId(1), false, packet_,
+ self_address_, peer_address_, true, "", valid_version_));
+ EXPECT_TRUE(store_.HasChloForConnection(
+ /*connection_id=*/TestConnectionId(1)));
+
+ QuicConnectionId delivered_conn_id;
+ for (size_t i = 0;
+ i < kDefaultMaxConnectionsInStore - kMaxConnectionsWithoutCHLO + 1;
+ ++i) {
+ if (i < kDefaultMaxConnectionsInStore - kMaxConnectionsWithoutCHLO) {
+ // Only CHLO is buffered.
+ EXPECT_EQ(1u, store_.DeliverPacketsForNextConnection(&delivered_conn_id)
+ .buffered_packets.size());
+ EXPECT_EQ(TestConnectionId(i + kMaxConnectionsWithoutCHLO + 1),
+ delivered_conn_id);
+ } else {
+ EXPECT_EQ(2u, store_.DeliverPacketsForNextConnection(&delivered_conn_id)
+ .buffered_packets.size());
+ EXPECT_EQ(TestConnectionId(1u), delivered_conn_id);
+ }
+ }
+ EXPECT_FALSE(store_.HasChlosBuffered());
+}
+
+// Tests that store expires long-staying connections appropriately for
+// connections both with and without CHLOs.
+TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery) {
+ QuicConnectionId connection_id = TestConnectionId(1);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS,
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, true, "", valid_version_));
+ QuicConnectionId connection_id2 = TestConnectionId(2);
+ EXPECT_EQ(EnqueuePacketResult::SUCCESS,
+ store_.EnqueuePacket(connection_id2, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_));
+
+ // CHLO on connection 3 arrives 1ms later.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ QuicConnectionId connection_id3 = TestConnectionId(3);
+ // Use different client address to differetiate packets from different
+ // connections.
+ QuicSocketAddress another_client_address(QuicIpAddress::Any4(), 255);
+ store_.EnqueuePacket(connection_id3, false, packet_, self_address_,
+ another_client_address, true, "", valid_version_);
+
+ // Advance clock to the time when connection 1 and 2 expires.
+ clock_.AdvanceTime(
+ QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline() -
+ clock_.ApproximateNow());
+ ASSERT_GE(clock_.ApproximateNow(),
+ QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline());
+ // Fire alarm to remove long-staying connection 1 and 2 packets.
+ alarm_factory_.FireAlarm(
+ QuicBufferedPacketStorePeer::expiration_alarm(&store_));
+ EXPECT_EQ(1u, visitor_.last_expired_packet_queue_.buffered_packets.size());
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id2));
+
+ // Try to deliver packets, but packet queue has been removed so no
+ // packets can be returned.
+ ASSERT_EQ(0u, store_.DeliverPackets(connection_id).buffered_packets.size());
+ ASSERT_EQ(0u, store_.DeliverPackets(connection_id2).buffered_packets.size());
+ QuicConnectionId delivered_conn_id;
+ auto queue = store_.DeliverPacketsForNextConnection(&delivered_conn_id)
+ .buffered_packets;
+ // Connection 3 is the next to be delivered as connection 1 already expired.
+ EXPECT_EQ(connection_id3, delivered_conn_id);
+ ASSERT_EQ(1u, queue.size());
+ // Packets in connection 3 should use another peer address.
+ EXPECT_EQ(another_client_address, queue.front().peer_address);
+
+ // Test the alarm is reset by enqueueing 2 packets for 4th connection and wait
+ // for them to expire.
+ QuicConnectionId connection_id4 = TestConnectionId(4);
+ store_.EnqueuePacket(connection_id4, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ store_.EnqueuePacket(connection_id4, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ clock_.AdvanceTime(
+ QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline() -
+ clock_.ApproximateNow());
+ alarm_factory_.FireAlarm(
+ QuicBufferedPacketStorePeer::expiration_alarm(&store_));
+ // |last_expired_packet_queue_| should be updated.
+ EXPECT_EQ(2u, visitor_.last_expired_packet_queue_.buffered_packets.size());
+}
+
+TEST_F(QuicBufferedPacketStoreTest, SimpleDiscardPackets) {
+ QuicConnectionId connection_id = TestConnectionId(1);
+
+ // Enqueue some packets
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ EXPECT_TRUE(store_.HasBufferedPackets(connection_id));
+ EXPECT_FALSE(store_.HasChlosBuffered());
+
+ // Dicard the packets
+ store_.DiscardPackets(connection_id);
+
+ // No packets on connection 1 should remain in the store
+ EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty());
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
+ EXPECT_FALSE(store_.HasChlosBuffered());
+
+ // Check idempotency
+ store_.DiscardPackets(connection_id);
+ EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty());
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
+ EXPECT_FALSE(store_.HasChlosBuffered());
+}
+
+TEST_F(QuicBufferedPacketStoreTest, DiscardWithCHLOs) {
+ QuicConnectionId connection_id = TestConnectionId(1);
+
+ // Enqueue some packets, which include a CHLO
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, true, "", valid_version_);
+ store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ EXPECT_TRUE(store_.HasBufferedPackets(connection_id));
+ EXPECT_TRUE(store_.HasChlosBuffered());
+
+ // Dicard the packets
+ store_.DiscardPackets(connection_id);
+
+ // No packets on connection 1 should remain in the store
+ EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty());
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
+ EXPECT_FALSE(store_.HasChlosBuffered());
+
+ // Check idempotency
+ store_.DiscardPackets(connection_id);
+ EXPECT_TRUE(store_.DeliverPackets(connection_id).buffered_packets.empty());
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
+ EXPECT_FALSE(store_.HasChlosBuffered());
+}
+
+TEST_F(QuicBufferedPacketStoreTest, MultipleDiscardPackets) {
+ QuicConnectionId connection_id_1 = TestConnectionId(1);
+ QuicConnectionId connection_id_2 = TestConnectionId(2);
+
+ // Enqueue some packets for two connection IDs
+ store_.EnqueuePacket(connection_id_1, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ store_.EnqueuePacket(connection_id_1, false, packet_, self_address_,
+ peer_address_, false, "", invalid_version_);
+ store_.EnqueuePacket(connection_id_2, false, packet_, self_address_,
+ peer_address_, true, "h3", valid_version_);
+ EXPECT_TRUE(store_.HasBufferedPackets(connection_id_1));
+ EXPECT_TRUE(store_.HasBufferedPackets(connection_id_2));
+ EXPECT_TRUE(store_.HasChlosBuffered());
+
+ // Discard the packets for connection 1
+ store_.DiscardPackets(connection_id_1);
+
+ // No packets on connection 1 should remain in the store
+ EXPECT_TRUE(store_.DeliverPackets(connection_id_1).buffered_packets.empty());
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id_1));
+ EXPECT_TRUE(store_.HasChlosBuffered());
+
+ // Packets on connection 2 should remain
+ EXPECT_TRUE(store_.HasBufferedPackets(connection_id_2));
+ auto packets = store_.DeliverPackets(connection_id_2);
+ EXPECT_EQ(1u, packets.buffered_packets.size());
+ EXPECT_EQ("h3", packets.alpn);
+ // Since connection_id_2's chlo arrives, verify version is set.
+ EXPECT_EQ(valid_version_, packets.version);
+ EXPECT_TRUE(store_.HasChlosBuffered());
+
+ // Discard the packets for connection 2
+ store_.DiscardPackets(connection_id_2);
+ EXPECT_FALSE(store_.HasChlosBuffered());
+}
+
+TEST_F(QuicBufferedPacketStoreTest, DiscardPacketsEmpty) {
+ // Check that DiscardPackets on an unknown connection ID is safe and does
+ // nothing.
+ QuicConnectionId connection_id = TestConnectionId(11235);
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
+ EXPECT_FALSE(store_.HasChlosBuffered());
+ store_.DiscardPackets(connection_id);
+ EXPECT_FALSE(store_.HasBufferedPackets(connection_id));
+ EXPECT_FALSE(store_.HasChlosBuffered());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config.cc b/chromium/net/third_party/quiche/src/quic/core/quic_config.cc
new file mode 100644
index 00000000000..25b549cdafe
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_config.cc
@@ -0,0 +1,826 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+
+#include <algorithm>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Reads the value corresponding to |name_| from |msg| into |out|. If the
+// |name_| is absent in |msg| and |presence| is set to OPTIONAL |out| is set
+// to |default_value|.
+QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg,
+ QuicTag tag,
+ QuicConfigPresence presence,
+ uint32_t default_value,
+ uint32_t* out,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+ QuicErrorCode error = msg.GetUint32(tag, out);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence == PRESENCE_REQUIRED) {
+ *error_details = "Missing " + QuicTagToString(tag);
+ break;
+ }
+ error = QUIC_NO_ERROR;
+ *out = default_value;
+ break;
+ case QUIC_NO_ERROR:
+ break;
+ default:
+ *error_details = "Bad " + QuicTagToString(tag);
+ break;
+ }
+ return error;
+}
+
+QuicConfigValue::QuicConfigValue(QuicTag tag, QuicConfigPresence presence)
+ : tag_(tag), presence_(presence) {}
+QuicConfigValue::~QuicConfigValue() {}
+
+QuicNegotiableValue::QuicNegotiableValue(QuicTag tag,
+ QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence), negotiated_(false) {}
+QuicNegotiableValue::~QuicNegotiableValue() {}
+
+QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag,
+ QuicConfigPresence presence)
+ : QuicNegotiableValue(tag, presence),
+ max_value_(0),
+ default_value_(0),
+ negotiated_value_(0) {}
+QuicNegotiableUint32::~QuicNegotiableUint32() {}
+
+void QuicNegotiableUint32::set(uint32_t max, uint32_t default_value) {
+ DCHECK_LE(default_value, max);
+ max_value_ = max;
+ default_value_ = default_value;
+}
+
+uint32_t QuicNegotiableUint32::GetUint32() const {
+ if (negotiated()) {
+ return negotiated_value_;
+ }
+ return default_value_;
+}
+
+// Returns the maximum value negotiable.
+uint32_t QuicNegotiableUint32::GetMax() const {
+ return max_value_;
+}
+
+void QuicNegotiableUint32::ToHandshakeMessage(
+ CryptoHandshakeMessage* out) const {
+ if (negotiated()) {
+ out->SetValue(tag_, negotiated_value_);
+ } else {
+ out->SetValue(tag_, max_value_);
+ }
+}
+
+QuicErrorCode QuicNegotiableUint32::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) {
+ DCHECK(!negotiated());
+ DCHECK(error_details != nullptr);
+ uint32_t value;
+ QuicErrorCode error = ReadUint32(peer_hello, tag_, presence_, default_value_,
+ &value, error_details);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+ return ReceiveValue(value, hello_type, error_details);
+}
+
+QuicErrorCode QuicNegotiableUint32::ReceiveValue(uint32_t value,
+ HelloType hello_type,
+ std::string* error_details) {
+ if (hello_type == SERVER && value > max_value_) {
+ *error_details = "Invalid value received for " + QuicTagToString(tag_);
+ return QUIC_INVALID_NEGOTIATED_VALUE;
+ }
+
+ set_negotiated(true);
+ negotiated_value_ = std::min(value, max_value_);
+ return QUIC_NO_ERROR;
+}
+
+QuicFixedUint32::QuicFixedUint32(QuicTag tag, QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {}
+QuicFixedUint32::~QuicFixedUint32() {}
+
+bool QuicFixedUint32::HasSendValue() const {
+ return has_send_value_;
+}
+
+uint32_t QuicFixedUint32::GetSendValue() const {
+ QUIC_BUG_IF(!has_send_value_)
+ << "No send value to get for tag:" << QuicTagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedUint32::SetSendValue(uint32_t value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedUint32::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+uint32_t QuicFixedUint32::GetReceivedValue() const {
+ QUIC_BUG_IF(!has_receive_value_)
+ << "No receive value to get for tag:" << QuicTagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedUint32::SetReceivedValue(uint32_t value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedUint32::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ out->SetValue(tag_, send_value_);
+ }
+}
+
+QuicErrorCode QuicFixedUint32::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+ QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicTagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_value_ = true;
+ break;
+ default:
+ *error_details = "Bad " + QuicTagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicFixedUint128::QuicFixedUint128(QuicTag tag, QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {}
+QuicFixedUint128::~QuicFixedUint128() {}
+
+bool QuicFixedUint128::HasSendValue() const {
+ return has_send_value_;
+}
+
+QuicUint128 QuicFixedUint128::GetSendValue() const {
+ QUIC_BUG_IF(!has_send_value_)
+ << "No send value to get for tag:" << QuicTagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedUint128::SetSendValue(QuicUint128 value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedUint128::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+QuicUint128 QuicFixedUint128::GetReceivedValue() const {
+ QUIC_BUG_IF(!has_receive_value_)
+ << "No receive value to get for tag:" << QuicTagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedUint128::SetReceivedValue(QuicUint128 value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedUint128::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ out->SetValue(tag_, send_value_);
+ }
+}
+
+QuicErrorCode QuicFixedUint128::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+ QuicErrorCode error = peer_hello.GetUint128(tag_, &receive_value_);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicTagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_value_ = true;
+ break;
+ default:
+ *error_details = "Bad " + QuicTagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicFixedTagVector::QuicFixedTagVector(QuicTag name,
+ QuicConfigPresence presence)
+ : QuicConfigValue(name, presence),
+ has_send_values_(false),
+ has_receive_values_(false) {}
+
+QuicFixedTagVector::QuicFixedTagVector(const QuicFixedTagVector& other) =
+ default;
+
+QuicFixedTagVector::~QuicFixedTagVector() {}
+
+bool QuicFixedTagVector::HasSendValues() const {
+ return has_send_values_;
+}
+
+QuicTagVector QuicFixedTagVector::GetSendValues() const {
+ QUIC_BUG_IF(!has_send_values_)
+ << "No send values to get for tag:" << QuicTagToString(tag_);
+ return send_values_;
+}
+
+void QuicFixedTagVector::SetSendValues(const QuicTagVector& values) {
+ has_send_values_ = true;
+ send_values_ = values;
+}
+
+bool QuicFixedTagVector::HasReceivedValues() const {
+ return has_receive_values_;
+}
+
+QuicTagVector QuicFixedTagVector::GetReceivedValues() const {
+ QUIC_BUG_IF(!has_receive_values_)
+ << "No receive value to get for tag:" << QuicTagToString(tag_);
+ return receive_values_;
+}
+
+void QuicFixedTagVector::SetReceivedValues(const QuicTagVector& values) {
+ has_receive_values_ = true;
+ receive_values_ = values;
+}
+
+void QuicFixedTagVector::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_values_) {
+ out->SetVector(tag_, send_values_);
+ }
+}
+
+QuicErrorCode QuicFixedTagVector::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+ QuicTagVector values;
+ QuicErrorCode error = peer_hello.GetTaglist(tag_, &values);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicTagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ QUIC_DVLOG(1) << "Received Connection Option tags from receiver.";
+ has_receive_values_ = true;
+ receive_values_.insert(receive_values_.end(), values.begin(),
+ values.end());
+ break;
+ default:
+ *error_details = "Bad " + QuicTagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicFixedSocketAddress::QuicFixedSocketAddress(QuicTag tag,
+ QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {}
+
+QuicFixedSocketAddress::~QuicFixedSocketAddress() {}
+
+bool QuicFixedSocketAddress::HasSendValue() const {
+ return has_send_value_;
+}
+
+const QuicSocketAddress& QuicFixedSocketAddress::GetSendValue() const {
+ QUIC_BUG_IF(!has_send_value_)
+ << "No send value to get for tag:" << QuicTagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedSocketAddress::SetSendValue(const QuicSocketAddress& value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedSocketAddress::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+const QuicSocketAddress& QuicFixedSocketAddress::GetReceivedValue() const {
+ QUIC_BUG_IF(!has_receive_value_)
+ << "No receive value to get for tag:" << QuicTagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedSocketAddress::SetReceivedValue(const QuicSocketAddress& value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedSocketAddress::ToHandshakeMessage(
+ CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ QuicSocketAddressCoder address_coder(send_value_);
+ out->SetStringPiece(tag_, address_coder.Encode());
+ }
+}
+
+QuicErrorCode QuicFixedSocketAddress::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) {
+ QuicStringPiece address;
+ if (!peer_hello.GetStringPiece(tag_, &address)) {
+ if (presence_ == PRESENCE_REQUIRED) {
+ *error_details = "Missing " + QuicTagToString(tag_);
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+ } else {
+ QuicSocketAddressCoder address_coder;
+ if (address_coder.Decode(address.data(), address.length())) {
+ SetReceivedValue(
+ QuicSocketAddress(address_coder.ip(), address_coder.port()));
+ }
+ }
+ return QUIC_NO_ERROR;
+}
+
+QuicConfig::QuicConfig()
+ : max_time_before_crypto_handshake_(QuicTime::Delta::Zero()),
+ max_idle_time_before_crypto_handshake_(QuicTime::Delta::Zero()),
+ max_undecryptable_packets_(0),
+ connection_options_(kCOPT, PRESENCE_OPTIONAL),
+ client_connection_options_(kCLOP, PRESENCE_OPTIONAL),
+ idle_network_timeout_seconds_(kICSL, PRESENCE_REQUIRED),
+ silent_close_(kSCLS, PRESENCE_OPTIONAL),
+ max_incoming_dynamic_streams_(kMIDS, PRESENCE_REQUIRED),
+ bytes_for_connection_id_(kTCID, PRESENCE_OPTIONAL),
+ initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL),
+ initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL),
+ initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL),
+ connection_migration_disabled_(kNCMR, PRESENCE_OPTIONAL),
+ alternate_server_address_(kASAD, PRESENCE_OPTIONAL),
+ support_max_header_list_size_(kSMHL, PRESENCE_OPTIONAL),
+ stateless_reset_token_(kSRST, PRESENCE_OPTIONAL) {
+ SetDefaults();
+}
+
+QuicConfig::QuicConfig(const QuicConfig& other) = default;
+
+QuicConfig::~QuicConfig() {}
+
+bool QuicConfig::SetInitialReceivedConnectionOptions(
+ const QuicTagVector& tags) {
+ if (HasReceivedConnectionOptions()) {
+ // If we have already received connection options (via handshake or due to
+ // a previous call), don't re-initialize.
+ return false;
+ }
+ connection_options_.SetReceivedValues(tags);
+ return true;
+}
+
+void QuicConfig::SetConnectionOptionsToSend(
+ const QuicTagVector& connection_options) {
+ connection_options_.SetSendValues(connection_options);
+}
+
+bool QuicConfig::HasReceivedConnectionOptions() const {
+ return connection_options_.HasReceivedValues();
+}
+
+QuicTagVector QuicConfig::ReceivedConnectionOptions() const {
+ return connection_options_.GetReceivedValues();
+}
+
+bool QuicConfig::HasSendConnectionOptions() const {
+ return connection_options_.HasSendValues();
+}
+
+QuicTagVector QuicConfig::SendConnectionOptions() const {
+ return connection_options_.GetSendValues();
+}
+
+bool QuicConfig::HasClientSentConnectionOption(QuicTag tag,
+ Perspective perspective) const {
+ if (perspective == Perspective::IS_SERVER) {
+ if (HasReceivedConnectionOptions() &&
+ ContainsQuicTag(ReceivedConnectionOptions(), tag)) {
+ return true;
+ }
+ } else if (HasSendConnectionOptions() &&
+ ContainsQuicTag(SendConnectionOptions(), tag)) {
+ return true;
+ }
+ return false;
+}
+
+void QuicConfig::SetClientConnectionOptions(
+ const QuicTagVector& client_connection_options) {
+ client_connection_options_.SetSendValues(client_connection_options);
+}
+
+bool QuicConfig::HasClientRequestedIndependentOption(
+ QuicTag tag,
+ Perspective perspective) const {
+ if (perspective == Perspective::IS_SERVER) {
+ return (HasReceivedConnectionOptions() &&
+ ContainsQuicTag(ReceivedConnectionOptions(), tag));
+ }
+
+ return (client_connection_options_.HasSendValues() &&
+ ContainsQuicTag(client_connection_options_.GetSendValues(), tag));
+}
+
+void QuicConfig::SetIdleNetworkTimeout(
+ QuicTime::Delta max_idle_network_timeout,
+ QuicTime::Delta default_idle_network_timeout) {
+ idle_network_timeout_seconds_.set(
+ static_cast<uint32_t>(max_idle_network_timeout.ToSeconds()),
+ static_cast<uint32_t>(default_idle_network_timeout.ToSeconds()));
+}
+
+QuicTime::Delta QuicConfig::IdleNetworkTimeout() const {
+ return QuicTime::Delta::FromSeconds(
+ idle_network_timeout_seconds_.GetUint32());
+}
+
+// TODO(ianswett) Use this for silent close on mobile, or delete.
+QUIC_UNUSED void QuicConfig::SetSilentClose(bool silent_close) {
+ silent_close_.set(silent_close ? 1 : 0, silent_close ? 1 : 0);
+}
+
+bool QuicConfig::SilentClose() const {
+ return silent_close_.GetUint32() > 0;
+}
+
+void QuicConfig::SetMaxIncomingDynamicStreamsToSend(
+ uint32_t max_incoming_dynamic_streams) {
+ max_incoming_dynamic_streams_.SetSendValue(max_incoming_dynamic_streams);
+}
+
+uint32_t QuicConfig::GetMaxIncomingDynamicStreamsToSend() {
+ return max_incoming_dynamic_streams_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedMaxIncomingDynamicStreams() {
+ return max_incoming_dynamic_streams_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedMaxIncomingDynamicStreams() {
+ return max_incoming_dynamic_streams_.GetReceivedValue();
+}
+
+bool QuicConfig::HasSetBytesForConnectionIdToSend() const {
+ return bytes_for_connection_id_.HasSendValue();
+}
+
+void QuicConfig::SetBytesForConnectionIdToSend(uint32_t bytes) {
+ bytes_for_connection_id_.SetSendValue(bytes);
+}
+
+bool QuicConfig::HasReceivedBytesForConnectionId() const {
+ return bytes_for_connection_id_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedBytesForConnectionId() const {
+ return bytes_for_connection_id_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialRoundTripTimeUsToSend(uint32_t rtt) {
+ initial_round_trip_time_us_.SetSendValue(rtt);
+}
+
+bool QuicConfig::HasReceivedInitialRoundTripTimeUs() const {
+ return initial_round_trip_time_us_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedInitialRoundTripTimeUs() const {
+ return initial_round_trip_time_us_.GetReceivedValue();
+}
+
+bool QuicConfig::HasInitialRoundTripTimeUsToSend() const {
+ return initial_round_trip_time_us_.HasSendValue();
+}
+
+uint32_t QuicConfig::GetInitialRoundTripTimeUsToSend() const {
+ return initial_round_trip_time_us_.GetSendValue();
+}
+
+void QuicConfig::SetInitialStreamFlowControlWindowToSend(
+ uint32_t window_bytes) {
+ if (window_bytes < kMinimumFlowControlSendWindow) {
+ QUIC_BUG << "Initial stream flow control receive window (" << window_bytes
+ << ") cannot be set lower than default ("
+ << kMinimumFlowControlSendWindow << ").";
+ window_bytes = kMinimumFlowControlSendWindow;
+ }
+ initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32_t QuicConfig::GetInitialStreamFlowControlWindowToSend() const {
+ return initial_stream_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialStreamFlowControlWindowBytes() const {
+ return initial_stream_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const {
+ return initial_stream_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialSessionFlowControlWindowToSend(
+ uint32_t window_bytes) {
+ if (window_bytes < kMinimumFlowControlSendWindow) {
+ QUIC_BUG << "Initial session flow control receive window (" << window_bytes
+ << ") cannot be set lower than default ("
+ << kMinimumFlowControlSendWindow << ").";
+ window_bytes = kMinimumFlowControlSendWindow;
+ }
+ initial_session_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32_t QuicConfig::GetInitialSessionFlowControlWindowToSend() const {
+ return initial_session_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialSessionFlowControlWindowBytes() const {
+ return initial_session_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const {
+ return initial_session_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetDisableConnectionMigration() {
+ connection_migration_disabled_.SetSendValue(1);
+}
+
+bool QuicConfig::DisableConnectionMigration() const {
+ return connection_migration_disabled_.HasReceivedValue();
+}
+
+void QuicConfig::SetAlternateServerAddressToSend(
+ const QuicSocketAddress& alternate_server_address) {
+ alternate_server_address_.SetSendValue(alternate_server_address);
+}
+
+bool QuicConfig::HasReceivedAlternateServerAddress() const {
+ return alternate_server_address_.HasReceivedValue();
+}
+
+const QuicSocketAddress& QuicConfig::ReceivedAlternateServerAddress() const {
+ return alternate_server_address_.GetReceivedValue();
+}
+
+void QuicConfig::SetSupportMaxHeaderListSize() {
+ support_max_header_list_size_.SetSendValue(1);
+}
+
+bool QuicConfig::SupportMaxHeaderListSize() const {
+ return support_max_header_list_size_.HasReceivedValue();
+}
+
+void QuicConfig::SetStatelessResetTokenToSend(
+ QuicUint128 stateless_reset_token) {
+ stateless_reset_token_.SetSendValue(stateless_reset_token);
+}
+
+bool QuicConfig::HasReceivedStatelessResetToken() const {
+ return stateless_reset_token_.HasReceivedValue();
+}
+
+QuicUint128 QuicConfig::ReceivedStatelessResetToken() const {
+ return stateless_reset_token_.GetReceivedValue();
+}
+
+bool QuicConfig::negotiated() const {
+ // TODO(ianswett): Add the negotiated parameters once and iterate over all
+ // of them in negotiated, ToHandshakeMessage, and ProcessPeerHello.
+ return idle_network_timeout_seconds_.negotiated();
+}
+
+void QuicConfig::SetCreateSessionTagIndicators(QuicTagVector tags) {
+ create_session_tag_indicators_ = std::move(tags);
+}
+
+const QuicTagVector& QuicConfig::create_session_tag_indicators() const {
+ return create_session_tag_indicators_;
+}
+
+void QuicConfig::SetDefaults() {
+ idle_network_timeout_seconds_.set(kMaximumIdleTimeoutSecs,
+ kDefaultIdleTimeoutSecs);
+ silent_close_.set(1, 0);
+ SetMaxIncomingDynamicStreamsToSend(kDefaultMaxStreamsPerConnection);
+ max_time_before_crypto_handshake_ =
+ QuicTime::Delta::FromSeconds(kMaxTimeForCryptoHandshakeSecs);
+ max_idle_time_before_crypto_handshake_ =
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs);
+ max_undecryptable_packets_ = kDefaultMaxUndecryptablePackets;
+
+ SetInitialStreamFlowControlWindowToSend(kMinimumFlowControlSendWindow);
+ SetInitialSessionFlowControlWindowToSend(kMinimumFlowControlSendWindow);
+ SetSupportMaxHeaderListSize();
+}
+
+void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ idle_network_timeout_seconds_.ToHandshakeMessage(out);
+ silent_close_.ToHandshakeMessage(out);
+ max_incoming_dynamic_streams_.ToHandshakeMessage(out);
+ bytes_for_connection_id_.ToHandshakeMessage(out);
+ initial_round_trip_time_us_.ToHandshakeMessage(out);
+ initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out);
+ initial_session_flow_control_window_bytes_.ToHandshakeMessage(out);
+ connection_migration_disabled_.ToHandshakeMessage(out);
+ connection_options_.ToHandshakeMessage(out);
+ alternate_server_address_.ToHandshakeMessage(out);
+ support_max_header_list_size_.ToHandshakeMessage(out);
+ stateless_reset_token_.ToHandshakeMessage(out);
+}
+
+QuicErrorCode QuicConfig::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ QuicErrorCode error = QUIC_NO_ERROR;
+ if (error == QUIC_NO_ERROR) {
+ error = idle_network_timeout_seconds_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error =
+ silent_close_.ProcessPeerHello(peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = max_incoming_dynamic_streams_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = bytes_for_connection_id_.ProcessPeerHello(peer_hello, hello_type,
+ error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = initial_round_trip_time_us_.ProcessPeerHello(peer_hello, hello_type,
+ error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = initial_stream_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = initial_session_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = connection_migration_disabled_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = connection_options_.ProcessPeerHello(peer_hello, hello_type,
+ error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = alternate_server_address_.ProcessPeerHello(peer_hello, hello_type,
+ error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = support_max_header_list_size_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = stateless_reset_token_.ProcessPeerHello(peer_hello, hello_type,
+ error_details);
+ }
+ return error;
+}
+
+bool QuicConfig::FillTransportParameters(TransportParameters* params) const {
+ params->initial_max_stream_data =
+ initial_stream_flow_control_window_bytes_.GetSendValue();
+ params->initial_max_data =
+ initial_session_flow_control_window_bytes_.GetSendValue();
+
+ uint32_t idle_timeout = idle_network_timeout_seconds_.GetUint32();
+ if (idle_timeout > std::numeric_limits<uint16_t>::max()) {
+ QUIC_BUG << "idle network timeout set too large";
+ return false;
+ }
+ params->idle_timeout = idle_timeout;
+
+ uint32_t initial_max_streams = max_incoming_dynamic_streams_.GetSendValue();
+ if (initial_max_streams > std::numeric_limits<uint16_t>::max()) {
+ QUIC_BUG << "max incoming streams set too large";
+ return false;
+ }
+ params->initial_max_bidi_streams.present = true;
+ params->initial_max_bidi_streams.value = initial_max_streams;
+
+ if (!params->google_quic_params) {
+ params->google_quic_params = QuicMakeUnique<CryptoHandshakeMessage>();
+ }
+ silent_close_.ToHandshakeMessage(params->google_quic_params.get());
+ initial_round_trip_time_us_.ToHandshakeMessage(
+ params->google_quic_params.get());
+ connection_options_.ToHandshakeMessage(params->google_quic_params.get());
+ return true;
+}
+
+QuicErrorCode QuicConfig::ProcessTransportParameters(
+ const TransportParameters& params,
+ HelloType hello_type,
+ std::string* error_details) {
+ QuicErrorCode error = idle_network_timeout_seconds_.ReceiveValue(
+ params.idle_timeout, hello_type, error_details);
+ if (error != QUIC_NO_ERROR) {
+ DCHECK(!error_details->empty());
+ return error;
+ }
+ const CryptoHandshakeMessage* peer_params = params.google_quic_params.get();
+ if (peer_params != nullptr) {
+ error =
+ silent_close_.ProcessPeerHello(*peer_params, hello_type, error_details);
+ if (error != QUIC_NO_ERROR) {
+ DCHECK(!error_details->empty());
+ return error;
+ }
+ error = initial_round_trip_time_us_.ProcessPeerHello(
+ *peer_params, hello_type, error_details);
+ if (error != QUIC_NO_ERROR) {
+ DCHECK(!error_details->empty());
+ return error;
+ }
+ error = connection_options_.ProcessPeerHello(*peer_params, hello_type,
+ error_details);
+ if (error != QUIC_NO_ERROR) {
+ DCHECK(!error_details->empty());
+ return error;
+ }
+ }
+
+ initial_stream_flow_control_window_bytes_.SetReceivedValue(
+ params.initial_max_stream_data);
+ initial_session_flow_control_window_bytes_.SetReceivedValue(
+ params.initial_max_data);
+ if (params.initial_max_bidi_streams.present) {
+ max_incoming_dynamic_streams_.SetReceivedValue(
+ params.initial_max_bidi_streams.value);
+ } else {
+ // An absent value for initial_max_bidi_streams is treated as a value of 0.
+ max_incoming_dynamic_streams_.SetReceivedValue(0);
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config.h b/chromium/net/third_party/quiche/src/quic/core/quic_config.h
new file mode 100644
index 00000000000..8ad161db9ec
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_config.h
@@ -0,0 +1,491 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CONFIG_H_
+#define QUICHE_QUIC_CORE_QUIC_CONFIG_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+namespace test {
+class QuicConfigPeer;
+} // namespace test
+
+class CryptoHandshakeMessage;
+
+// Describes whether or not a given QuicTag is required or optional in the
+// handshake message.
+enum QuicConfigPresence {
+ // This negotiable value can be absent from the handshake message. Default
+ // value is selected as the negotiated value in such a case.
+ PRESENCE_OPTIONAL,
+ // This negotiable value is required in the handshake message otherwise the
+ // Process*Hello function returns an error.
+ PRESENCE_REQUIRED,
+};
+
+// Whether the CryptoHandshakeMessage is from the client or server.
+enum HelloType {
+ CLIENT,
+ SERVER,
+};
+
+// An abstract base class that stores a value that can be sent in CHLO/SHLO
+// message. These values can be OPTIONAL or REQUIRED, depending on |presence_|.
+class QUIC_EXPORT_PRIVATE QuicConfigValue {
+ public:
+ QuicConfigValue(QuicTag tag, QuicConfigPresence presence);
+ virtual ~QuicConfigValue();
+
+ // Serialises tag name and value(s) to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const = 0;
+
+ // Selects a mutually acceptable value from those offered in |peer_hello|
+ // and those defined in the subclass.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) = 0;
+
+ protected:
+ const QuicTag tag_;
+ const QuicConfigPresence presence_;
+};
+
+class QUIC_EXPORT_PRIVATE QuicNegotiableValue : public QuicConfigValue {
+ public:
+ QuicNegotiableValue(QuicTag tag, QuicConfigPresence presence);
+ ~QuicNegotiableValue() override;
+
+ bool negotiated() const { return negotiated_; }
+
+ protected:
+ void set_negotiated(bool negotiated) { negotiated_ = negotiated; }
+
+ private:
+ bool negotiated_;
+};
+
+class QUIC_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
+ // TODO(fayang): some negotiated values use uint32 as bool (e.g., silent
+ // close). Consider adding a QuicNegotiableBool type.
+ public:
+ // Default and max values default to 0.
+ QuicNegotiableUint32(QuicTag name, QuicConfigPresence presence);
+ ~QuicNegotiableUint32() override;
+
+ // Sets the maximum possible value that can be achieved after negotiation and
+ // also the default values to be assumed if PRESENCE_OPTIONAL and the *HLO msg
+ // doesn't contain a value corresponding to |name_|. |max| is serialised via
+ // ToHandshakeMessage call if |negotiated_| is false.
+ void set(uint32_t max, uint32_t default_value);
+
+ // Returns the value negotiated if |negotiated_| is true, otherwise returns
+ // default_value_ (used to set default values before negotiation finishes).
+ uint32_t GetUint32() const;
+
+ // Returns the maximum value negotiable.
+ uint32_t GetMax() const;
+
+ // Serialises |name_| and value to |out|. If |negotiated_| is true then
+ // |negotiated_value_| is serialised, otherwise |max_value_| is serialised.
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const override;
+
+ // Processes the corresponding value from |peer_hello| and if present calls
+ // ReceiveValue with it. If the corresponding value is missing and
+ // PRESENCE_OPTIONAL then |negotiated_value_| is set to |default_value_|.
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) override;
+
+ // Takes a value |value| parsed from a handshake message (whether a TLS
+ // ClientHello/ServerHello or a CryptoHandshakeMessage) whose sender was
+ // |hello_type|, and sets |negotiated_value_| to the minimum of |value| and
+ // |max_value_|. On success this function returns QUIC_NO_ERROR; if there is
+ // an error, details are put in |*error_details|.
+ QuicErrorCode ReceiveValue(uint32_t value,
+ HelloType hello_type,
+ std::string* error_details);
+
+ private:
+ uint32_t max_value_;
+ uint32_t default_value_;
+ uint32_t negotiated_value_;
+};
+
+// Stores uint32_t from CHLO or SHLO messages that are not negotiated.
+class QUIC_EXPORT_PRIVATE QuicFixedUint32 : public QuicConfigValue {
+ public:
+ QuicFixedUint32(QuicTag name, QuicConfigPresence presence);
+ ~QuicFixedUint32() override;
+
+ bool HasSendValue() const;
+
+ uint32_t GetSendValue() const;
+
+ void SetSendValue(uint32_t value);
+
+ bool HasReceivedValue() const;
+
+ uint32_t GetReceivedValue() const;
+
+ void SetReceivedValue(uint32_t value);
+
+ // If has_send_value is true, serialises |tag_| and |send_value_| to |out|.
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const override;
+
+ // Sets |value_| to the corresponding value from |peer_hello_| if it exists.
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) override;
+
+ private:
+ uint32_t send_value_;
+ bool has_send_value_;
+ uint32_t receive_value_;
+ bool has_receive_value_;
+};
+
+// Stores uint128 from CHLO or SHLO messages that are not negotiated.
+class QUIC_EXPORT_PRIVATE QuicFixedUint128 : public QuicConfigValue {
+ public:
+ QuicFixedUint128(QuicTag tag, QuicConfigPresence presence);
+ ~QuicFixedUint128() override;
+
+ bool HasSendValue() const;
+
+ QuicUint128 GetSendValue() const;
+
+ void SetSendValue(QuicUint128 value);
+
+ bool HasReceivedValue() const;
+
+ QuicUint128 GetReceivedValue() const;
+
+ void SetReceivedValue(QuicUint128 value);
+
+ // If has_send_value is true, serialises |tag_| and |send_value_| to |out|.
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const override;
+
+ // Sets |value_| to the corresponding value from |peer_hello_| if it exists.
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) override;
+
+ private:
+ QuicUint128 send_value_;
+ bool has_send_value_;
+ QuicUint128 receive_value_;
+ bool has_receive_value_;
+};
+
+// Stores tag from CHLO or SHLO messages that are not negotiated.
+class QUIC_EXPORT_PRIVATE QuicFixedTagVector : public QuicConfigValue {
+ public:
+ QuicFixedTagVector(QuicTag name, QuicConfigPresence presence);
+ QuicFixedTagVector(const QuicFixedTagVector& other);
+ ~QuicFixedTagVector() override;
+
+ bool HasSendValues() const;
+
+ QuicTagVector GetSendValues() const;
+
+ void SetSendValues(const QuicTagVector& values);
+
+ bool HasReceivedValues() const;
+
+ QuicTagVector GetReceivedValues() const;
+
+ void SetReceivedValues(const QuicTagVector& values);
+
+ // If has_send_value is true, serialises |tag_vector_| and |send_value_| to
+ // |out|.
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const override;
+
+ // Sets |receive_values_| to the corresponding value from |client_hello_| if
+ // it exists.
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) override;
+
+ private:
+ QuicTagVector send_values_;
+ bool has_send_values_;
+ QuicTagVector receive_values_;
+ bool has_receive_values_;
+};
+
+// Stores QuicSocketAddress from CHLO or SHLO messages that are not negotiated.
+class QUIC_EXPORT_PRIVATE QuicFixedSocketAddress : public QuicConfigValue {
+ public:
+ QuicFixedSocketAddress(QuicTag tag, QuicConfigPresence presence);
+ ~QuicFixedSocketAddress() override;
+
+ bool HasSendValue() const;
+
+ const QuicSocketAddress& GetSendValue() const;
+
+ void SetSendValue(const QuicSocketAddress& value);
+
+ bool HasReceivedValue() const;
+
+ const QuicSocketAddress& GetReceivedValue() const;
+
+ void SetReceivedValue(const QuicSocketAddress& value);
+
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const override;
+
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) override;
+
+ private:
+ QuicSocketAddress send_value_;
+ bool has_send_value_;
+ QuicSocketAddress receive_value_;
+ bool has_receive_value_;
+};
+
+// QuicConfig contains non-crypto configuration options that are negotiated in
+// the crypto handshake.
+class QUIC_EXPORT_PRIVATE QuicConfig {
+ public:
+ QuicConfig();
+ QuicConfig(const QuicConfig& other);
+ ~QuicConfig();
+
+ void SetConnectionOptionsToSend(const QuicTagVector& connection_options);
+
+ bool HasReceivedConnectionOptions() const;
+
+ // Sets initial received connection options. All received connection options
+ // will be initialized with these fields. Initial received options may only be
+ // set once per config, prior to the setting of any other options. If options
+ // have already been set (either by previous calls or via handshake), this
+ // function does nothing and returns false.
+ bool SetInitialReceivedConnectionOptions(const QuicTagVector& tags);
+
+ QuicTagVector ReceivedConnectionOptions() const;
+
+ bool HasSendConnectionOptions() const;
+
+ QuicTagVector SendConnectionOptions() const;
+
+ // Returns true if the client is sending or the server has received a
+ // connection option.
+ // TODO(ianswett): Rename to HasClientRequestedSharedOption
+ bool HasClientSentConnectionOption(QuicTag tag,
+ Perspective perspective) const;
+
+ void SetClientConnectionOptions(
+ const QuicTagVector& client_connection_options);
+
+ // Returns true if the client has requested the specified connection option.
+ // Checks the client connection options if the |perspective| is client and
+ // connection options if the |perspective| is the server.
+ bool HasClientRequestedIndependentOption(QuicTag tag,
+ Perspective perspective) const;
+
+ void SetIdleNetworkTimeout(QuicTime::Delta max_idle_network_timeout,
+ QuicTime::Delta default_idle_network_timeout);
+
+ QuicTime::Delta IdleNetworkTimeout() const;
+
+ void SetSilentClose(bool silent_close);
+
+ bool SilentClose() const;
+
+ void SetMaxIncomingDynamicStreamsToSend(
+ uint32_t max_incoming_dynamic_streams);
+
+ uint32_t GetMaxIncomingDynamicStreamsToSend();
+
+ bool HasReceivedMaxIncomingDynamicStreams();
+
+ uint32_t ReceivedMaxIncomingDynamicStreams();
+
+ void set_max_time_before_crypto_handshake(
+ QuicTime::Delta max_time_before_crypto_handshake) {
+ max_time_before_crypto_handshake_ = max_time_before_crypto_handshake;
+ }
+
+ QuicTime::Delta max_time_before_crypto_handshake() const {
+ return max_time_before_crypto_handshake_;
+ }
+
+ void set_max_idle_time_before_crypto_handshake(
+ QuicTime::Delta max_idle_time_before_crypto_handshake) {
+ max_idle_time_before_crypto_handshake_ =
+ max_idle_time_before_crypto_handshake;
+ }
+
+ QuicTime::Delta max_idle_time_before_crypto_handshake() const {
+ return max_idle_time_before_crypto_handshake_;
+ }
+
+ QuicNegotiableUint32 idle_network_timeout_seconds() const {
+ return idle_network_timeout_seconds_;
+ }
+
+ void set_max_undecryptable_packets(size_t max_undecryptable_packets) {
+ max_undecryptable_packets_ = max_undecryptable_packets;
+ }
+
+ size_t max_undecryptable_packets() const {
+ return max_undecryptable_packets_;
+ }
+
+ bool HasSetBytesForConnectionIdToSend() const;
+
+ // Sets the peer's connection id length, in bytes.
+ void SetBytesForConnectionIdToSend(uint32_t bytes);
+
+ bool HasReceivedBytesForConnectionId() const;
+
+ uint32_t ReceivedBytesForConnectionId() const;
+
+ // Sets an estimated initial round trip time in us.
+ void SetInitialRoundTripTimeUsToSend(uint32_t rtt_us);
+
+ bool HasReceivedInitialRoundTripTimeUs() const;
+
+ uint32_t ReceivedInitialRoundTripTimeUs() const;
+
+ bool HasInitialRoundTripTimeUsToSend() const;
+
+ uint32_t GetInitialRoundTripTimeUsToSend() const;
+
+ // Sets an initial stream flow control window size to transmit to the peer.
+ void SetInitialStreamFlowControlWindowToSend(uint32_t window_bytes);
+
+ uint32_t GetInitialStreamFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialStreamFlowControlWindowBytes() const;
+
+ uint32_t ReceivedInitialStreamFlowControlWindowBytes() const;
+
+ // Sets an initial session flow control window size to transmit to the peer.
+ void SetInitialSessionFlowControlWindowToSend(uint32_t window_bytes);
+
+ uint32_t GetInitialSessionFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialSessionFlowControlWindowBytes() const;
+
+ uint32_t ReceivedInitialSessionFlowControlWindowBytes() const;
+
+ void SetDisableConnectionMigration();
+
+ bool DisableConnectionMigration() const;
+
+ void SetAlternateServerAddressToSend(
+ const QuicSocketAddress& alternate_server_address);
+
+ bool HasReceivedAlternateServerAddress() const;
+
+ const QuicSocketAddress& ReceivedAlternateServerAddress() const;
+
+ void SetSupportMaxHeaderListSize();
+
+ bool SupportMaxHeaderListSize() const;
+
+ void SetStatelessResetTokenToSend(QuicUint128 stateless_reset_token);
+
+ bool HasReceivedStatelessResetToken() const;
+
+ QuicUint128 ReceivedStatelessResetToken() const;
+
+ bool negotiated() const;
+
+ void SetCreateSessionTagIndicators(QuicTagVector tags);
+
+ const QuicTagVector& create_session_tag_indicators() const;
+
+ // ToHandshakeMessage serialises the settings in this object as a series of
+ // tags /value pairs and adds them to |out|.
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+
+ // Calls ProcessPeerHello on each negotiable parameter. On failure returns
+ // the corresponding QuicErrorCode and sets detailed error in |error_details|.
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details);
+
+ // FillTransportParameters writes the values to send for ICSL, MIDS, CFCW, and
+ // SFCW to |*params|, returning true if the values could be written and false
+ // if something prevents them from being written (e.g. a value is too large).
+ bool FillTransportParameters(TransportParameters* params) const;
+
+ // ProcessTransportParameters reads from |params| which was received from a
+ // peer operating as a |hello_type|. It processes values for ICSL, MIDS, CFCW,
+ // and SFCW and sets the corresponding members of this QuicConfig. On failure,
+ // it returns a QuicErrorCode and puts a detailed error in |*error_details|.
+ QuicErrorCode ProcessTransportParameters(const TransportParameters& params,
+ HelloType hello_type,
+ std::string* error_details);
+
+ private:
+ friend class test::QuicConfigPeer;
+
+ // SetDefaults sets the members to sensible, default values.
+ void SetDefaults();
+
+ // Configurations options that are not negotiated.
+ // Maximum time the session can be alive before crypto handshake is finished.
+ QuicTime::Delta max_time_before_crypto_handshake_;
+ // Maximum idle time before the crypto handshake has completed.
+ QuicTime::Delta max_idle_time_before_crypto_handshake_;
+ // Maximum number of undecryptable packets stored before CHLO/SHLO.
+ size_t max_undecryptable_packets_;
+
+ // Connection options which affect the server side. May also affect the
+ // client side in cases when identical behavior is desirable.
+ QuicFixedTagVector connection_options_;
+ // Connection options which only affect the client side.
+ QuicFixedTagVector client_connection_options_;
+ // Idle network timeout in seconds.
+ QuicNegotiableUint32 idle_network_timeout_seconds_;
+ // Whether to use silent close. Defaults to 0 (false) and is otherwise true.
+ QuicNegotiableUint32 silent_close_;
+ // Maximum number of incoming dynamic streams that the connection can support.
+ QuicFixedUint32 max_incoming_dynamic_streams_;
+ // The number of bytes required for the connection ID.
+ QuicFixedUint32 bytes_for_connection_id_;
+ // Initial round trip time estimate in microseconds.
+ QuicFixedUint32 initial_round_trip_time_us_;
+
+ // Initial stream flow control receive window in bytes.
+ QuicFixedUint32 initial_stream_flow_control_window_bytes_;
+ // Initial session flow control receive window in bytes.
+ QuicFixedUint32 initial_session_flow_control_window_bytes_;
+
+ // Whether tell peer not to attempt connection migration.
+ QuicFixedUint32 connection_migration_disabled_;
+
+ // An alternate server address the client could connect to.
+ QuicFixedSocketAddress alternate_server_address_;
+
+ // Whether support HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE SETTINGS frame.
+ QuicFixedUint32 support_max_header_list_size_;
+
+ // Stateless reset token used in IETF public reset packet.
+ QuicFixedUint128 stateless_reset_token_;
+
+ // List of QuicTags whose presence immediately causes the session to
+ // be created. This allows for CHLOs that are larger than a single
+ // packet to be processed.
+ QuicTagVector create_session_tag_indicators_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CONFIG_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc
new file mode 100644
index 00000000000..c18c5200708
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_config_test.cc
@@ -0,0 +1,288 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicConfigTest : public QuicTest {
+ protected:
+ QuicConfig config_;
+};
+
+TEST_F(QuicConfigTest, ToHandshakeMessage) {
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config_.SetIdleNetworkTimeout(QuicTime::Delta::FromSeconds(5),
+ QuicTime::Delta::FromSeconds(2));
+ CryptoHandshakeMessage msg;
+ config_.ToHandshakeMessage(&msg);
+
+ uint32_t value;
+ QuicErrorCode error = msg.GetUint32(kICSL, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(5u, value);
+
+ error = msg.GetUint32(kSFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialStreamFlowControlWindowForTest, value);
+
+ error = msg.GetUint32(kCFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
+}
+
+TEST_F(QuicConfigTest, ProcessClientHello) {
+ QuicConfig client_config;
+ QuicTagVector cgst;
+ cgst.push_back(kQBIC);
+ client_config.SetIdleNetworkTimeout(
+ QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs),
+ QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs));
+ client_config.SetInitialRoundTripTimeUsToSend(10 * kNumMicrosPerMilli);
+ client_config.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ client_config.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ QuicTagVector copt;
+ copt.push_back(kTBBR);
+ client_config.SetConnectionOptionsToSend(copt);
+ CryptoHandshakeMessage msg;
+ client_config.ToHandshakeMessage(&msg);
+
+ std::string error_details;
+ QuicTagVector initial_received_options;
+ initial_received_options.push_back(kIW50);
+ EXPECT_TRUE(
+ config_.SetInitialReceivedConnectionOptions(initial_received_options));
+ EXPECT_FALSE(
+ config_.SetInitialReceivedConnectionOptions(initial_received_options))
+ << "You can only set initial options once.";
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_FALSE(
+ config_.SetInitialReceivedConnectionOptions(initial_received_options))
+ << "You cannot set initial options after the hello.";
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs),
+ config_.IdleNetworkTimeout());
+ EXPECT_EQ(10 * kNumMicrosPerMilli, config_.ReceivedInitialRoundTripTimeUs());
+ EXPECT_TRUE(config_.HasReceivedConnectionOptions());
+ EXPECT_EQ(2u, config_.ReceivedConnectionOptions().size());
+ EXPECT_EQ(config_.ReceivedConnectionOptions()[0], kIW50);
+ EXPECT_EQ(config_.ReceivedConnectionOptions()[1], kTBBR);
+ EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+ 2 * kInitialStreamFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+}
+
+TEST_F(QuicConfigTest, ProcessServerHello) {
+ QuicIpAddress host;
+ host.FromString("127.0.3.1");
+ const QuicSocketAddress kTestServerAddress = QuicSocketAddress(host, 1234);
+ const QuicUint128 kTestResetToken = MakeQuicUint128(0, 10111100001);
+ QuicConfig server_config;
+ QuicTagVector cgst;
+ cgst.push_back(kQBIC);
+ server_config.SetIdleNetworkTimeout(
+ QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2),
+ QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2));
+ server_config.SetInitialRoundTripTimeUsToSend(10 * kNumMicrosPerMilli);
+ server_config.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ server_config.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ server_config.SetAlternateServerAddressToSend(kTestServerAddress);
+ server_config.SetStatelessResetTokenToSend(kTestResetToken);
+ CryptoHandshakeMessage msg;
+ server_config.ToHandshakeMessage(&msg);
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2),
+ config_.IdleNetworkTimeout());
+ EXPECT_EQ(10 * kNumMicrosPerMilli, config_.ReceivedInitialRoundTripTimeUs());
+ EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+ 2 * kInitialStreamFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_TRUE(config_.HasReceivedAlternateServerAddress());
+ EXPECT_EQ(kTestServerAddress, config_.ReceivedAlternateServerAddress());
+ EXPECT_TRUE(config_.HasReceivedStatelessResetToken());
+ EXPECT_EQ(kTestResetToken, config_.ReceivedStatelessResetToken());
+}
+
+TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) {
+ CryptoHandshakeMessage msg;
+ msg.SetValue(kICSL, 1);
+
+ // Set all REQUIRED tags.
+ msg.SetValue(kICSL, 1);
+ msg.SetValue(kMIDS, 1);
+
+ // No error, as rest are optional.
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+}
+
+TEST_F(QuicConfigTest, MissingOptionalValuesInSHLO) {
+ CryptoHandshakeMessage msg;
+
+ // Set all REQUIRED tags.
+ msg.SetValue(kICSL, 1);
+ msg.SetValue(kMIDS, 1);
+
+ // No error, as rest are optional.
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+}
+
+TEST_F(QuicConfigTest, MissingValueInCHLO) {
+ // Server receives CHLO with missing kICSL.
+ CryptoHandshakeMessage msg;
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
+}
+
+TEST_F(QuicConfigTest, MissingValueInSHLO) {
+ // Client receives SHLO with missing kICSL.
+ CryptoHandshakeMessage msg;
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
+}
+
+TEST_F(QuicConfigTest, OutOfBoundSHLO) {
+ QuicConfig server_config;
+ server_config.SetIdleNetworkTimeout(
+ QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs),
+ QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs));
+
+ CryptoHandshakeMessage msg;
+ server_config.ToHandshakeMessage(&msg);
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
+}
+
+TEST_F(QuicConfigTest, InvalidFlowControlWindow) {
+ // QuicConfig should not accept an invalid flow control window to send to the
+ // peer: the receive window must be at least the default of 16 Kb.
+ QuicConfig config;
+ const uint64_t kInvalidWindow = kMinimumFlowControlSendWindow - 1;
+ EXPECT_QUIC_BUG(
+ config.SetInitialStreamFlowControlWindowToSend(kInvalidWindow),
+ "Initial stream flow control receive window");
+
+ EXPECT_EQ(kMinimumFlowControlSendWindow,
+ config.GetInitialStreamFlowControlWindowToSend());
+}
+
+TEST_F(QuicConfigTest, HasClientSentConnectionOption) {
+ QuicConfig client_config;
+ QuicTagVector copt;
+ copt.push_back(kTBBR);
+ client_config.SetConnectionOptionsToSend(copt);
+ EXPECT_TRUE(client_config.HasClientSentConnectionOption(
+ kTBBR, Perspective::IS_CLIENT));
+
+ CryptoHandshakeMessage msg;
+ client_config.ToHandshakeMessage(&msg);
+
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+
+ EXPECT_TRUE(config_.HasReceivedConnectionOptions());
+ EXPECT_EQ(1u, config_.ReceivedConnectionOptions().size());
+ EXPECT_TRUE(
+ config_.HasClientSentConnectionOption(kTBBR, Perspective::IS_SERVER));
+}
+
+TEST_F(QuicConfigTest, DontSendClientConnectionOptions) {
+ QuicConfig client_config;
+ QuicTagVector copt;
+ copt.push_back(kTBBR);
+ client_config.SetClientConnectionOptions(copt);
+
+ CryptoHandshakeMessage msg;
+ client_config.ToHandshakeMessage(&msg);
+
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+
+ EXPECT_FALSE(config_.HasReceivedConnectionOptions());
+}
+
+TEST_F(QuicConfigTest, HasClientRequestedIndependentOption) {
+ QuicConfig client_config;
+ QuicTagVector client_opt;
+ client_opt.push_back(kRENO);
+ QuicTagVector copt;
+ copt.push_back(kTBBR);
+ client_config.SetClientConnectionOptions(client_opt);
+ client_config.SetConnectionOptionsToSend(copt);
+ EXPECT_TRUE(client_config.HasClientSentConnectionOption(
+ kTBBR, Perspective::IS_CLIENT));
+ EXPECT_TRUE(client_config.HasClientRequestedIndependentOption(
+ kRENO, Perspective::IS_CLIENT));
+ EXPECT_FALSE(client_config.HasClientRequestedIndependentOption(
+ kTBBR, Perspective::IS_CLIENT));
+
+ CryptoHandshakeMessage msg;
+ client_config.ToHandshakeMessage(&msg);
+
+ std::string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+
+ EXPECT_TRUE(config_.HasReceivedConnectionOptions());
+ EXPECT_EQ(1u, config_.ReceivedConnectionOptions().size());
+ EXPECT_FALSE(config_.HasClientRequestedIndependentOption(
+ kRENO, Perspective::IS_SERVER));
+ EXPECT_TRUE(config_.HasClientRequestedIndependentOption(
+ kTBBR, Perspective::IS_SERVER));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..cb901bafaf3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection.cc
@@ -0,0 +1,4141 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_generator.h"
+#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_client_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+class QuicDecrypter;
+class QuicEncrypter;
+
+namespace {
+
+// Maximum number of consecutive sent nonretransmittable packets.
+const QuicPacketCount kMaxConsecutiveNonRetransmittablePackets = 19;
+
+// Maximum number of retransmittable packets received before sending an ack.
+const QuicPacketCount kDefaultRetransmittablePacketsBeforeAck = 2;
+// Minimum number of packets received before ack decimation is enabled.
+// This intends to avoid the beginning of slow start, when CWNDs may be
+// rapidly increasing.
+const QuicPacketCount kMinReceivedBeforeAckDecimation = 100;
+// Wait for up to 10 retransmittable packets before sending an ack.
+const QuicPacketCount kMaxRetransmittablePacketsBeforeAck = 10;
+// One quarter RTT delay when doing ack decimation.
+const float kAckDecimationDelay = 0.25;
+// One eighth RTT delay when doing ack decimation.
+const float kShortAckDecimationDelay = 0.125;
+
+// The minimum release time into future in ms.
+const int kMinReleaseTimeIntoFutureMs = 1;
+
+bool Near(QuicPacketNumber a, QuicPacketNumber b) {
+ QuicPacketCount delta = (a > b) ? a - b : b - a;
+ return delta <= kMaxPacketGap;
+}
+
+// An alarm that is scheduled to send an ack if a timeout occurs.
+class AckAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit AckAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ AckAlarmDelegate(const AckAlarmDelegate&) = delete;
+ AckAlarmDelegate& operator=(const AckAlarmDelegate&) = delete;
+
+ void OnAlarm() override {
+ DCHECK(connection_->ack_frame_updated());
+ QuicConnection::ScopedPacketFlusher flusher(connection_,
+ QuicConnection::SEND_ACK);
+ if (connection_->packet_generator().deprecate_ack_bundling_mode()) {
+ if (connection_->SupportsMultiplePacketNumberSpaces()) {
+ connection_->SendAllPendingAcks();
+ } else {
+ DCHECK(!connection_->GetUpdatedAckFrame().ack_frame->packets.Empty());
+ 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 {
+ public:
+ explicit RetransmissionAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ RetransmissionAlarmDelegate(const RetransmissionAlarmDelegate&) = delete;
+ RetransmissionAlarmDelegate& operator=(const RetransmissionAlarmDelegate&) =
+ delete;
+
+ void OnAlarm() override { 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 {
+ public:
+ explicit SendAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ SendAlarmDelegate(const SendAlarmDelegate&) = delete;
+ SendAlarmDelegate& operator=(const SendAlarmDelegate&) = delete;
+
+ void OnAlarm() override { connection_->WriteAndBundleAcksIfNotBlocked(); }
+
+ private:
+ QuicConnection* connection_;
+};
+
+class PathDegradingAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit PathDegradingAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ PathDegradingAlarmDelegate(const PathDegradingAlarmDelegate&) = delete;
+ PathDegradingAlarmDelegate& operator=(const PathDegradingAlarmDelegate&) =
+ delete;
+
+ void OnAlarm() override { connection_->OnPathDegradingTimeout(); }
+
+ private:
+ QuicConnection* connection_;
+};
+
+class TimeoutAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit TimeoutAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ TimeoutAlarmDelegate(const TimeoutAlarmDelegate&) = delete;
+ TimeoutAlarmDelegate& operator=(const TimeoutAlarmDelegate&) = delete;
+
+ void OnAlarm() override { connection_->CheckForTimeout(); }
+
+ private:
+ QuicConnection* connection_;
+};
+
+class PingAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit PingAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ PingAlarmDelegate(const PingAlarmDelegate&) = delete;
+ PingAlarmDelegate& operator=(const PingAlarmDelegate&) = delete;
+
+ void OnAlarm() override { connection_->OnPingTimeout(); }
+
+ private:
+ QuicConnection* connection_;
+};
+
+class MtuDiscoveryAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit MtuDiscoveryAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ MtuDiscoveryAlarmDelegate(const MtuDiscoveryAlarmDelegate&) = delete;
+ MtuDiscoveryAlarmDelegate& operator=(const MtuDiscoveryAlarmDelegate&) =
+ delete;
+
+ void OnAlarm() override { connection_->DiscoverMtu(); }
+
+ private:
+ QuicConnection* connection_;
+};
+
+class RetransmittableOnWireAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit RetransmittableOnWireAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ RetransmittableOnWireAlarmDelegate(
+ const RetransmittableOnWireAlarmDelegate&) = delete;
+ RetransmittableOnWireAlarmDelegate& operator=(
+ const RetransmittableOnWireAlarmDelegate&) = delete;
+
+ void OnAlarm() override { connection_->OnPingTimeout(); }
+
+ private:
+ QuicConnection* connection_;
+};
+
+class ProcessUndecryptablePacketsAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit ProcessUndecryptablePacketsAlarmDelegate(QuicConnection* connection)
+ : connection_(connection) {}
+ ProcessUndecryptablePacketsAlarmDelegate(
+ const ProcessUndecryptablePacketsAlarmDelegate&) = delete;
+ ProcessUndecryptablePacketsAlarmDelegate& operator=(
+ const ProcessUndecryptablePacketsAlarmDelegate&) = delete;
+
+ void OnAlarm() override {
+ QuicConnection::ScopedPacketFlusher flusher(connection_,
+ QuicConnection::NO_ACK);
+ connection_->MaybeProcessUndecryptablePackets();
+ }
+
+ private:
+ QuicConnection* connection_;
+};
+
+// Whether this incoming packet is allowed to replace our connection ID.
+bool PacketCanReplaceConnectionId(const QuicPacketHeader& header,
+ Perspective perspective) {
+ return perspective == Perspective::IS_CLIENT &&
+ header.form == IETF_QUIC_LONG_HEADER_PACKET &&
+ QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ header.version.transport_version) &&
+ (header.long_packet_type == INITIAL ||
+ header.long_packet_type == RETRY);
+}
+
+} // namespace
+
+#define ENDPOINT \
+ (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ")
+
+QuicConnection::QuicConnection(
+ QuicConnectionId connection_id,
+ QuicSocketAddress initial_peer_address,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ QuicPacketWriter* writer,
+ bool owns_writer,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : framer_(supported_versions,
+ helper->GetClock()->ApproximateNow(),
+ perspective,
+ connection_id.length()),
+ current_packet_content_(NO_FRAMES_RECEIVED),
+ is_current_packet_connectivity_probing_(false),
+ current_effective_peer_migration_type_(NO_CHANGE),
+ helper_(helper),
+ alarm_factory_(alarm_factory),
+ per_packet_options_(nullptr),
+ writer_(writer),
+ owns_writer_(owns_writer),
+ encryption_level_(ENCRYPTION_INITIAL),
+ clock_(helper->GetClock()),
+ random_generator_(helper->GetRandomGenerator()),
+ connection_id_(connection_id),
+ peer_address_(initial_peer_address),
+ direct_peer_address_(initial_peer_address),
+ active_effective_peer_migration_type_(NO_CHANGE),
+ last_packet_decrypted_(false),
+ last_size_(0),
+ current_packet_data_(nullptr),
+ last_decrypted_packet_level_(ENCRYPTION_INITIAL),
+ should_last_packet_instigate_acks_(false),
+ was_last_packet_missing_(false),
+ max_undecryptable_packets_(0),
+ max_tracked_packets_(kMaxTrackedPackets),
+ pending_version_negotiation_packet_(false),
+ send_ietf_version_negotiation_packet_(false),
+ save_crypto_packets_as_termination_packets_(false),
+ idle_timeout_connection_close_behavior_(
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET),
+ close_connection_after_five_rtos_(false),
+ received_packet_manager_(&stats_),
+ uber_received_packet_manager_(&stats_),
+ ack_queued_(false),
+ num_retransmittable_packets_received_since_last_ack_sent_(0),
+ num_packets_received_since_last_ack_sent_(0),
+ stop_waiting_count_(0),
+ ack_mode_(GetQuicReloadableFlag(quic_enable_ack_decimation)
+ ? ACK_DECIMATION
+ : TCP_ACKING),
+ ack_decimation_delay_(kAckDecimationDelay),
+ unlimited_ack_decimation_(false),
+ fast_ack_after_quiescence_(false),
+ pending_retransmission_alarm_(false),
+ defer_send_in_response_to_packets_(false),
+ ping_timeout_(QuicTime::Delta::FromSeconds(kPingTimeoutSecs)),
+ retransmittable_on_wire_timeout_(QuicTime::Delta::Infinite()),
+ arena_(),
+ ack_alarm_(alarm_factory_->CreateAlarm(arena_.New<AckAlarmDelegate>(this),
+ &arena_)),
+ retransmission_alarm_(alarm_factory_->CreateAlarm(
+ arena_.New<RetransmissionAlarmDelegate>(this),
+ &arena_)),
+ send_alarm_(
+ alarm_factory_->CreateAlarm(arena_.New<SendAlarmDelegate>(this),
+ &arena_)),
+ timeout_alarm_(
+ alarm_factory_->CreateAlarm(arena_.New<TimeoutAlarmDelegate>(this),
+ &arena_)),
+ ping_alarm_(
+ alarm_factory_->CreateAlarm(arena_.New<PingAlarmDelegate>(this),
+ &arena_)),
+ mtu_discovery_alarm_(alarm_factory_->CreateAlarm(
+ arena_.New<MtuDiscoveryAlarmDelegate>(this),
+ &arena_)),
+ path_degrading_alarm_(alarm_factory_->CreateAlarm(
+ arena_.New<PathDegradingAlarmDelegate>(this),
+ &arena_)),
+ process_undecryptable_packets_alarm_(alarm_factory_->CreateAlarm(
+ arena_.New<ProcessUndecryptablePacketsAlarmDelegate>(this),
+ &arena_)),
+ visitor_(nullptr),
+ debug_visitor_(nullptr),
+ packet_generator_(connection_id_, &framer_, random_generator_, this),
+ idle_network_timeout_(QuicTime::Delta::Infinite()),
+ handshake_timeout_(QuicTime::Delta::Infinite()),
+ time_of_first_packet_sent_after_receiving_(
+ GetQuicReloadableFlag(
+ quic_fix_time_of_first_packet_sent_after_receiving)
+ ? QuicTime::Zero()
+ : clock_->ApproximateNow()),
+ time_of_last_received_packet_(clock_->ApproximateNow()),
+ time_of_previous_received_packet_(QuicTime::Zero()),
+ sent_packet_manager_(
+ perspective,
+ clock_,
+ &stats_,
+ GetQuicReloadableFlag(quic_default_to_bbr) ? kBBR : kCubicBytes,
+ kNack),
+ version_negotiation_state_(START_NEGOTIATION),
+ perspective_(perspective),
+ connected_(true),
+ can_truncate_connection_ids_(perspective == Perspective::IS_SERVER),
+ mtu_discovery_target_(0),
+ mtu_probe_count_(0),
+ packets_between_mtu_probes_(kPacketsBetweenMtuProbesBase),
+ next_mtu_probe_at_(kPacketsBetweenMtuProbesBase),
+ largest_received_packet_size_(0),
+ write_error_occurred_(false),
+ no_stop_waiting_frames_(transport_version() > QUIC_VERSION_43),
+ consecutive_num_packets_with_no_retransmittable_frames_(0),
+ max_consecutive_num_packets_with_no_retransmittable_frames_(
+ kMaxConsecutiveNonRetransmittablePackets),
+ min_received_before_ack_decimation_(kMinReceivedBeforeAckDecimation),
+ ack_frequency_before_ack_decimation_(
+ kDefaultRetransmittablePacketsBeforeAck),
+ fill_up_link_during_probing_(false),
+ probing_retransmission_pending_(false),
+ stateless_reset_token_received_(false),
+ received_stateless_reset_token_(0),
+ last_control_frame_id_(kInvalidControlFrameId),
+ is_path_degrading_(false),
+ processing_ack_frame_(false),
+ supports_release_time_(false),
+ release_time_into_future_(QuicTime::Delta::Zero()),
+ no_version_negotiation_(supported_versions.size() == 1),
+ fix_termination_packets_(
+ GetQuicReloadableFlag(quic_fix_termination_packets)),
+ send_ack_when_on_can_write_(false),
+ validate_packet_number_post_decryption_(
+ GetQuicReloadableFlag(quic_validate_packet_number_post_decryption)),
+ use_uber_received_packet_manager_(
+ received_packet_manager_.decide_when_to_send_acks() &&
+ validate_packet_number_post_decryption_ &&
+ GetQuicReloadableFlag(quic_use_uber_received_packet_manager)) {
+ if (ack_mode_ == ACK_DECIMATION) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_enable_ack_decimation);
+ }
+ if (perspective_ == Perspective::IS_SERVER &&
+ supported_versions.size() == 1) {
+ QUIC_RESTART_FLAG_COUNT(quic_no_server_conn_ver_negotiation2);
+ }
+ if (packet_generator_.deprecate_ack_bundling_mode()) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_deprecate_ack_bundling_mode);
+ }
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_rpm_decides_when_to_send_acks);
+ }
+ if (validate_packet_number_post_decryption_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_validate_packet_number_post_decryption);
+ }
+ if (use_uber_received_packet_manager_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_use_uber_received_packet_manager);
+ }
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Created connection with connection_id: " << connection_id
+ << " and version: " << ParsedQuicVersionToString(version());
+
+ QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(connection_id,
+ transport_version()))
+ << "QuicConnection: attempted to use connection ID " << connection_id
+ << " which is invalid with version "
+ << QuicVersionToString(transport_version());
+
+ framer_.set_visitor(this);
+ stats_.connection_creation_time = clock_->ApproximateNow();
+ // TODO(ianswett): Supply the NetworkChangeVisitor as a constructor argument
+ // and make it required non-null, because it's always used.
+ sent_packet_manager_.SetNetworkChangeVisitor(this);
+ if (GetQuicRestartFlag(quic_offload_pacing_to_usps2)) {
+ sent_packet_manager_.SetPacingAlarmGranularity(QuicTime::Delta::Zero());
+ release_time_into_future_ =
+ QuicTime::Delta::FromMilliseconds(kMinReleaseTimeIntoFutureMs);
+ }
+ // Allow the packet writer to potentially reduce the packet size to a value
+ // even smaller than kDefaultMaxPacketSize.
+ SetMaxPacketLength(perspective_ == Perspective::IS_SERVER
+ ? kDefaultServerMaxPacketSize
+ : kDefaultMaxPacketSize);
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.set_max_ack_ranges(255);
+ } else {
+ received_packet_manager_.set_max_ack_ranges(255);
+ }
+ MaybeEnableSessionDecidesWhatToWrite();
+ MaybeEnableMultiplePacketNumberSpacesSupport();
+ DCHECK(!GetQuicRestartFlag(quic_no_server_conn_ver_negotiation2) ||
+ perspective_ == Perspective::IS_CLIENT ||
+ supported_versions.size() == 1);
+}
+
+QuicConnection::~QuicConnection() {
+ if (owns_writer_) {
+ delete writer_;
+ }
+ ClearQueuedPackets();
+}
+
+void QuicConnection::ClearQueuedPackets() {
+ for (auto it = queued_packets_.begin(); it != queued_packets_.end(); ++it) {
+ // Delete the buffer before calling ClearSerializedPacket, which sets
+ // encrypted_buffer to nullptr.
+ delete[] it->encrypted_buffer;
+ ClearSerializedPacket(&(*it));
+ }
+ queued_packets_.clear();
+}
+
+void QuicConnection::SetFromConfig(const QuicConfig& config) {
+ if (config.negotiated()) {
+ // Handshake complete, set handshake timeout to Infinite.
+ SetNetworkTimeouts(QuicTime::Delta::Infinite(),
+ config.IdleNetworkTimeout());
+ if (config.SilentClose()) {
+ idle_timeout_connection_close_behavior_ =
+ ConnectionCloseBehavior::SILENT_CLOSE;
+ }
+ } else {
+ SetNetworkTimeouts(config.max_time_before_crypto_handshake(),
+ config.max_idle_time_before_crypto_handshake());
+ }
+
+ sent_packet_manager_.SetFromConfig(config);
+ if (config.HasReceivedBytesForConnectionId() &&
+ can_truncate_connection_ids_) {
+ packet_generator_.SetConnectionIdLength(
+ config.ReceivedBytesForConnectionId());
+ }
+ max_undecryptable_packets_ = config.max_undecryptable_packets();
+
+ if (config.HasClientSentConnectionOption(kMTUH, perspective_)) {
+ SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeHigh);
+ }
+ if (config.HasClientSentConnectionOption(kMTUL, perspective_)) {
+ SetMtuDiscoveryTarget(kMtuDiscoveryTargetPacketSizeLow);
+ }
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnSetFromConfig(config);
+ }
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.SetFromConfig(config, perspective_);
+ } else {
+ received_packet_manager_.SetFromConfig(config, perspective_);
+ }
+ } else {
+ if (GetQuicReloadableFlag(quic_enable_ack_decimation) &&
+ config.HasClientSentConnectionOption(kACD0, perspective_)) {
+ ack_mode_ = TCP_ACKING;
+ }
+ if (config.HasClientSentConnectionOption(kACKD, perspective_)) {
+ ack_mode_ = ACK_DECIMATION;
+ }
+ if (config.HasClientSentConnectionOption(kAKD2, perspective_)) {
+ ack_mode_ = ACK_DECIMATION_WITH_REORDERING;
+ }
+ if (config.HasClientSentConnectionOption(kAKD3, perspective_)) {
+ ack_mode_ = ACK_DECIMATION;
+ ack_decimation_delay_ = kShortAckDecimationDelay;
+ }
+ if (config.HasClientSentConnectionOption(kAKD4, perspective_)) {
+ ack_mode_ = ACK_DECIMATION_WITH_REORDERING;
+ ack_decimation_delay_ = kShortAckDecimationDelay;
+ }
+ if (config.HasClientSentConnectionOption(kAKDU, perspective_)) {
+ unlimited_ack_decimation_ = true;
+ }
+ if (config.HasClientSentConnectionOption(kACKQ, perspective_)) {
+ fast_ack_after_quiescence_ = true;
+ }
+ }
+ if (config.HasClientSentConnectionOption(k5RTO, perspective_)) {
+ close_connection_after_five_rtos_ = true;
+ }
+ if (config.HasClientSentConnectionOption(kNSTP, perspective_)) {
+ no_stop_waiting_frames_ = true;
+ }
+ if (config.HasReceivedStatelessResetToken()) {
+ stateless_reset_token_received_ = true;
+ received_stateless_reset_token_ = config.ReceivedStatelessResetToken();
+ }
+ if (GetQuicReloadableFlag(quic_send_timestamps) &&
+ config.HasClientSentConnectionOption(kSTMP, perspective_)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_send_timestamps);
+ framer_.set_process_timestamps(true);
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.set_save_timestamps(true);
+ } else {
+ received_packet_manager_.set_save_timestamps(true);
+ }
+ }
+
+ supports_release_time_ =
+ writer_ != nullptr && writer_->SupportsReleaseTime() &&
+ !config.HasClientSentConnectionOption(kNPCO, perspective_);
+
+ if (supports_release_time_) {
+ UpdateReleaseTimeIntoFuture();
+ }
+}
+
+void QuicConnection::OnSendConnectionState(
+ const CachedNetworkParameters& cached_network_params) {
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnSendConnectionState(cached_network_params);
+ }
+}
+
+void QuicConnection::OnReceiveConnectionState(
+ const CachedNetworkParameters& cached_network_params) {
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnReceiveConnectionState(cached_network_params);
+ }
+}
+
+void QuicConnection::ResumeConnectionState(
+ const CachedNetworkParameters& cached_network_params,
+ bool max_bandwidth_resumption) {
+ sent_packet_manager_.ResumeConnectionState(cached_network_params,
+ max_bandwidth_resumption);
+}
+
+void QuicConnection::SetMaxPacingRate(QuicBandwidth max_pacing_rate) {
+ sent_packet_manager_.SetMaxPacingRate(max_pacing_rate);
+}
+
+void QuicConnection::AdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) {
+ sent_packet_manager_.AdjustNetworkParameters(bandwidth, rtt);
+}
+
+QuicBandwidth QuicConnection::MaxPacingRate() const {
+ return sent_packet_manager_.MaxPacingRate();
+}
+
+bool QuicConnection::SelectMutualVersion(
+ const ParsedQuicVersionVector& available_versions) {
+ // Try to find the highest mutual version by iterating over supported
+ // versions, starting with the highest, and breaking out of the loop once we
+ // find a matching version in the provided available_versions vector.
+ const ParsedQuicVersionVector& supported_versions =
+ framer_.supported_versions();
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ const ParsedQuicVersion& version = supported_versions[i];
+ if (QuicContainsValue(available_versions, version)) {
+ framer_.set_version(version);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QuicConnection::OnError(QuicFramer* framer) {
+ // Packets that we can not or have not decrypted are dropped.
+ // TODO(rch): add stats to measure this.
+ if (!connected_ || last_packet_decrypted_ == false) {
+ return;
+ }
+ CloseConnection(framer->error(), framer->detailed_error(),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+void QuicConnection::OnPacket() {
+ last_packet_decrypted_ = false;
+}
+
+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.)
+ DCHECK_EQ(connection_id_, packet.connection_id);
+ DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnPublicResetPacket(packet);
+ }
+ std::string error_details = "Received public reset.";
+ if (perspective_ == Perspective::IS_CLIENT && !packet.endpoint_id.empty()) {
+ QuicStrAppend(&error_details, " From ", packet.endpoint_id, ".");
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << error_details;
+ QUIC_CODE_COUNT(quic_tear_down_local_connection_on_public_reset);
+ TearDownLocalConnectionState(QUIC_PUBLIC_RESET, error_details,
+ ConnectionCloseSource::FROM_PEER);
+}
+
+bool QuicConnection::OnProtocolVersionMismatch(
+ ParsedQuicVersion received_version,
+ PacketHeaderFormat form) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Received packet with mismatched version "
+ << ParsedQuicVersionToString(received_version);
+ if (perspective_ == Perspective::IS_CLIENT) {
+ const std::string error_details = "Protocol version mismatch.";
+ QUIC_BUG << ENDPOINT << error_details;
+ TearDownLocalConnectionState(QUIC_INTERNAL_ERROR, error_details,
+ ConnectionCloseSource::FROM_SELF);
+ return false;
+ }
+ if (no_version_negotiation_) {
+ // Drop old packets that were sent by the client before the version was
+ // negotiated.
+ return false;
+ }
+ DCHECK_NE(version(), received_version);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnProtocolVersionMismatch(received_version);
+ }
+
+ switch (version_negotiation_state_) {
+ case START_NEGOTIATION:
+ if (!framer_.IsSupportedVersion(received_version)) {
+ SendVersionNegotiationPacket(form != GOOGLE_QUIC_PACKET);
+ version_negotiation_state_ = NEGOTIATION_IN_PROGRESS;
+ return false;
+ }
+ break;
+
+ case NEGOTIATION_IN_PROGRESS:
+ if (!framer_.IsSupportedVersion(received_version)) {
+ SendVersionNegotiationPacket(form != GOOGLE_QUIC_PACKET);
+ return false;
+ }
+ break;
+
+ case NEGOTIATED_VERSION:
+ // Might be old packets that were sent by the client before the version
+ // was negotiated. Drop these.
+ return false;
+
+ default:
+ DCHECK(false);
+ }
+
+ // Store the new version.
+ framer_.set_version(received_version);
+ framer_.InferPacketHeaderTypeFromVersion();
+
+ version_negotiation_state_ = NEGOTIATED_VERSION;
+ visitor_->OnSuccessfulVersionNegotiation(received_version);
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnSuccessfulVersionNegotiation(received_version);
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "version negotiated "
+ << ParsedQuicVersionToString(received_version);
+
+ MaybeEnableSessionDecidesWhatToWrite();
+ no_stop_waiting_frames_ =
+ received_version.transport_version > QUIC_VERSION_43;
+
+ // TODO(satyamshekhar): Store the packet number of this packet and close the
+ // connection if we ever received a packet with incorrect version and whose
+ // packet number is greater.
+ return true;
+}
+
+// Handles version negotiation for client connection.
+void QuicConnection::OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& 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.)
+ DCHECK_EQ(connection_id_, packet.connection_id);
+ if (perspective_ == Perspective::IS_SERVER) {
+ const std::string error_details =
+ "Server receieved version negotiation packet.";
+ QUIC_BUG << error_details;
+ QUIC_CODE_COUNT(quic_tear_down_local_connection_on_version_negotiation);
+ TearDownLocalConnectionState(QUIC_INTERNAL_ERROR, error_details,
+ ConnectionCloseSource::FROM_SELF);
+ return;
+ }
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnVersionNegotiationPacket(packet);
+ }
+
+ if (version_negotiation_state_ != START_NEGOTIATION) {
+ // Possibly a duplicate version negotiation packet.
+ return;
+ }
+
+ if (QuicContainsValue(packet.versions, version())) {
+ const std::string error_details =
+ "Server already supports client's version and should have accepted the "
+ "connection.";
+ QUIC_DLOG(WARNING) << error_details;
+ TearDownLocalConnectionState(QUIC_INVALID_VERSION_NEGOTIATION_PACKET,
+ error_details,
+ ConnectionCloseSource::FROM_SELF);
+ return;
+ }
+
+ server_supported_versions_ = packet.versions;
+
+ if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_no_client_conn_ver_negotiation);
+ CloseConnection(
+ QUIC_INVALID_VERSION,
+ QuicStrCat(
+ "Client may support one of the versions in the server's list, but "
+ "it's going to close the connection anyway. Supported versions: {",
+ ParsedQuicVersionVectorToString(framer_.supported_versions()),
+ "}, peer supported versions: {",
+ ParsedQuicVersionVectorToString(packet.versions), "}"),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ if (!SelectMutualVersion(packet.versions)) {
+ CloseConnection(
+ QUIC_INVALID_VERSION,
+ QuicStrCat(
+ "No common version found. Supported versions: {",
+ ParsedQuicVersionVectorToString(framer_.supported_versions()),
+ "}, peer supported versions: {",
+ ParsedQuicVersionVectorToString(packet.versions), "}"),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ QUIC_DLOG(INFO) << ENDPOINT << "Negotiated version: "
+ << QuicVersionToString(transport_version());
+ no_stop_waiting_frames_ = transport_version() > QUIC_VERSION_43;
+ version_negotiation_state_ = NEGOTIATION_IN_PROGRESS;
+ RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION);
+}
+
+bool QuicConnection::HasIncomingConnectionId(QuicConnectionId connection_id) {
+ for (QuicConnectionId const& incoming_connection_id :
+ incoming_connection_ids_) {
+ if (incoming_connection_id == connection_id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void QuicConnection::AddIncomingConnectionId(QuicConnectionId connection_id) {
+ if (HasIncomingConnectionId(connection_id)) {
+ return;
+ }
+ incoming_connection_ids_.push_back(connection_id);
+}
+
+bool QuicConnection::OnUnauthenticatedPublicHeader(
+ const QuicPacketHeader& header) {
+ if (header.destination_connection_id == connection_id_ ||
+ HasIncomingConnectionId(header.destination_connection_id)) {
+ return true;
+ }
+
+ if (PacketCanReplaceConnectionId(header, perspective_)) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Accepting packet with new connection ID "
+ << header.destination_connection_id << " instead of "
+ << connection_id_;
+ return true;
+ }
+
+ ++stats_.packets_dropped;
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Ignoring packet from unexpected ConnectionId: "
+ << header.destination_connection_id << " instead of "
+ << connection_id_;
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnIncorrectConnectionId(header.destination_connection_id);
+ }
+ // If this is a server, the dispatcher routes each packet to the
+ // QuicConnection responsible for the packet's connection ID. So if control
+ // arrives here and this is a server, the dispatcher must be malfunctioning.
+ DCHECK_NE(Perspective::IS_SERVER, perspective_);
+ return false;
+}
+
+bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnUnauthenticatedHeader(header);
+ }
+
+ // Check that any public reset packet with a different connection ID that was
+ // routed to this QuicConnection has been redirected before control reaches
+ // here.
+ DCHECK(header.destination_connection_id == connection_id_ ||
+ HasIncomingConnectionId(header.destination_connection_id) ||
+ PacketCanReplaceConnectionId(header, perspective_));
+
+ if (!packet_generator_.IsPendingPacketEmpty()) {
+ // Incoming packets may change a queued ACK frame.
+ const std::string error_details =
+ "Pending frames must be serialized before incoming packets are "
+ "processed.";
+ QUIC_BUG << error_details << ", received header: " << header;
+ CloseConnection(QUIC_INTERNAL_ERROR, error_details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ // If this packet has already been seen, or the sender has told us that it
+ // will not be retransmitted, then stop processing the packet.
+ if (!validate_packet_number_post_decryption_) {
+ const bool is_awaiting =
+ use_uber_received_packet_manager_
+ ? uber_received_packet_manager_.IsAwaitingPacket(
+ last_decrypted_packet_level_, header.packet_number)
+ : received_packet_manager_.IsAwaitingPacket(header.packet_number);
+ if (!is_awaiting) {
+ if (framer_.IsIetfStatelessResetPacket(header)) {
+ QuicIetfStatelessResetPacket packet(
+ header, header.possible_stateless_reset_token);
+ OnAuthenticatedIetfStatelessResetPacket(packet);
+ return false;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Packet " << header.packet_number
+ << " no longer being waited for. Discarding.";
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnDuplicatePacket(header.packet_number);
+ }
+ ++stats_.packets_dropped;
+ return false;
+ }
+ }
+
+ if (version_negotiation_state_ != NEGOTIATED_VERSION &&
+ perspective_ == Perspective::IS_SERVER) {
+ if (!header.version_flag) {
+ // Packets should have the version flag till version negotiation is
+ // done.
+ std::string error_details =
+ QuicStrCat(ENDPOINT, "Packet ", header.packet_number.ToUint64(),
+ " without version flag before version negotiated.");
+ QUIC_DLOG(WARNING) << error_details;
+ CloseConnection(QUIC_INVALID_VERSION, error_details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ } else {
+ DCHECK_EQ(header.version, version());
+ version_negotiation_state_ = NEGOTIATED_VERSION;
+ framer_.InferPacketHeaderTypeFromVersion();
+ visitor_->OnSuccessfulVersionNegotiation(version());
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnSuccessfulVersionNegotiation(version());
+ }
+ }
+ DCHECK_EQ(NEGOTIATED_VERSION, version_negotiation_state_);
+ }
+
+ return true;
+}
+
+void QuicConnection::OnDecryptedPacket(EncryptionLevel level) {
+ last_decrypted_packet_level_ = level;
+ last_packet_decrypted_ = true;
+
+ // Once the server receives a forward secure packet, the handshake is
+ // confirmed.
+ if (level == ENCRYPTION_FORWARD_SECURE &&
+ perspective_ == Perspective::IS_SERVER) {
+ sent_packet_manager_.SetHandshakeConfirmed();
+ if (sent_packet_manager_.unacked_packets().use_uber_loss_algorithm()) {
+ // This may have changed the retransmission timer, so re-arm it.
+ SetRetransmissionAlarm();
+ }
+ }
+}
+
+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_;
+}
+
+bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnPacketHeader(header);
+ }
+
+ // Will be decremented below if we fall through to return true.
+ ++stats_.packets_dropped;
+
+ if (!ProcessValidatedPacket(header)) {
+ return false;
+ }
+
+ // Initialize the current packet content state.
+ current_packet_content_ = NO_FRAMES_RECEIVED;
+ is_current_packet_connectivity_probing_ = false;
+ current_effective_peer_migration_type_ = NO_CHANGE;
+
+ if (perspective_ == Perspective::IS_CLIENT) {
+ if (!GetLargestReceivedPacket().IsInitialized() ||
+ header.packet_number > GetLargestReceivedPacket()) {
+ // Update peer_address_ and effective_peer_address_ immediately for
+ // client connections.
+ // TODO(fayang): only change peer addresses in application data packet
+ // number space.
+ direct_peer_address_ = last_packet_source_address_;
+ effective_peer_address_ = GetEffectivePeerAddressFromCurrentPacket();
+ }
+ } else {
+ // At server, remember the address change type of effective_peer_address
+ // in current_effective_peer_migration_type_. But this variable alone
+ // doesn't necessarily starts a migration. A migration will be started
+ // later, once the current packet is confirmed to meet the following
+ // conditions:
+ // 1) current_effective_peer_migration_type_ is not NO_CHANGE.
+ // 2) The current packet is not a connectivity probing.
+ // 3) The current packet is not reordered, i.e. its packet number is the
+ // largest of this connection so far.
+ // Once the above conditions are confirmed, a new migration will start
+ // even if there is an active migration underway.
+ current_effective_peer_migration_type_ =
+ QuicUtils::DetermineAddressChangeType(
+ effective_peer_address_,
+ GetEffectivePeerAddressFromCurrentPacket());
+
+ QUIC_DLOG_IF(INFO, current_effective_peer_migration_type_ != NO_CHANGE)
+ << ENDPOINT << "Effective peer's ip:port changed from "
+ << effective_peer_address_.ToString() << " to "
+ << GetEffectivePeerAddressFromCurrentPacket().ToString()
+ << ", active_effective_peer_migration_type is "
+ << active_effective_peer_migration_type_;
+ }
+
+ --stats_.packets_dropped;
+ QUIC_DVLOG(1) << ENDPOINT << "Received packet header: " << header;
+ last_header_ = header;
+ // An ack will be sent if a missing retransmittable packet was received;
+ if (!use_uber_received_packet_manager_) {
+ was_last_packet_missing_ =
+ received_packet_manager_.IsMissing(last_header_.packet_number);
+ }
+
+ // Record packet receipt to populate ack info before processing stream
+ // frames, since the processing may result in sending a bundled ack.
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.RecordPacketReceived(
+ last_decrypted_packet_level_, last_header_,
+ time_of_last_received_packet_);
+ } else {
+ received_packet_manager_.RecordPacketReceived(
+ last_header_, time_of_last_received_packet_);
+ }
+ DCHECK(connected_);
+ return true;
+}
+
+bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a stream frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnStreamFrame(frame);
+ }
+ if (frame.stream_id != QuicUtils::GetCryptoStreamId(transport_version()) &&
+ last_decrypted_packet_level_ == ENCRYPTION_INITIAL) {
+ if (MaybeConsiderAsMemoryCorruption(frame)) {
+ CloseConnection(QUIC_MAYBE_CORRUPTED_MEMORY,
+ "Received crypto frame on non crypto stream.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ QUIC_PEER_BUG << ENDPOINT
+ << "Received an unencrypted data frame: closing connection"
+ << " packet_number:" << last_header_.packet_number
+ << " stream_id:" << frame.stream_id
+ << " received_packets:" << GetUpdatedAckFrame();
+ CloseConnection(QUIC_UNENCRYPTED_STREAM_DATA,
+ "Unencrypted stream data seen.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ visitor_->OnStreamFrame(frame);
+ stats_.stream_bytes_received += frame.data_length;
+ should_last_packet_instigate_acks_ = true;
+ return connected_;
+}
+
+bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a CRYPTO frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ visitor_->OnCryptoFrame(frame);
+ should_last_packet_instigate_acks_ = true;
+ return connected_;
+}
+
+bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) {
+ DCHECK(connected_);
+
+ if (processing_ack_frame_) {
+ CloseConnection(QUIC_INVALID_ACK_DATA,
+ "Received a new ack while processing an ack frame.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ // Since an ack frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ QUIC_DVLOG(1) << ENDPOINT
+ << "OnAckFrameStart, largest_acked: " << largest_acked;
+
+ if (GetLargestReceivedPacketWithAck().IsInitialized() &&
+ last_header_.packet_number <= GetLargestReceivedPacketWithAck()) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
+ return true;
+ }
+
+ if (!GetLargestSentPacket().IsInitialized() ||
+ largest_acked > GetLargestSentPacket()) {
+ QUIC_DLOG(WARNING) << ENDPOINT
+ << "Peer's observed unsent packet:" << largest_acked
+ << " vs " << GetLargestSentPacket();
+ // We got an ack for data we have not sent.
+ CloseConnection(QUIC_INVALID_ACK_DATA, "Largest observed too high.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ if (!GetLargestAckedPacket().IsInitialized() ||
+ largest_acked > GetLargestAckedPacket()) {
+ visitor_->OnForwardProgressConfirmed();
+ } else if (!sent_packet_manager_.tolerate_reneging() &&
+ largest_acked < GetLargestAckedPacket()) {
+ QUIC_LOG(INFO) << ENDPOINT << "Peer's largest_observed packet decreased:"
+ << largest_acked << " vs " << GetLargestAckedPacket()
+ << " packet_number:" << last_header_.packet_number
+ << " largest seen with ack:"
+ << GetLargestReceivedPacketWithAck()
+ << " connection_id: " << connection_id_;
+ // A new ack has a diminished largest_observed value.
+ // If this was an old packet, we wouldn't even have checked.
+ CloseConnection(QUIC_INVALID_ACK_DATA, "Largest observed too low.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ processing_ack_frame_ = true;
+ sent_packet_manager_.OnAckFrameStart(largest_acked, ack_delay_time,
+ time_of_last_received_packet_);
+ return true;
+}
+
+bool QuicConnection::OnAckRange(QuicPacketNumber start, QuicPacketNumber end) {
+ DCHECK(connected_);
+ QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end << ")";
+
+ if (GetLargestReceivedPacketWithAck().IsInitialized() &&
+ last_header_.packet_number <= GetLargestReceivedPacketWithAck()) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
+ return true;
+ }
+
+ sent_packet_manager_.OnAckRange(start, end);
+ return true;
+}
+
+bool QuicConnection::OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) {
+ DCHECK(connected_);
+ QUIC_DVLOG(1) << ENDPOINT << "OnAckTimestamp: [" << packet_number << ", "
+ << timestamp.ToDebuggingValue() << ")";
+
+ if (GetLargestReceivedPacketWithAck().IsInitialized() &&
+ last_header_.packet_number <= GetLargestReceivedPacketWithAck()) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
+ return true;
+ }
+
+ sent_packet_manager_.OnAckTimestamp(packet_number, timestamp);
+ return true;
+}
+
+bool QuicConnection::OnAckFrameEnd(QuicPacketNumber start) {
+ DCHECK(connected_);
+ QUIC_DVLOG(1) << ENDPOINT << "OnAckFrameEnd, start: " << start;
+
+ if (GetLargestReceivedPacketWithAck().IsInitialized() &&
+ last_header_.packet_number <= GetLargestReceivedPacketWithAck()) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
+ return true;
+ }
+ const AckResult ack_result = sent_packet_manager_.OnAckFrameEnd(
+ time_of_last_received_packet_, last_decrypted_packet_level_);
+ if (ack_result != PACKETS_NEWLY_ACKED &&
+ ack_result != NO_PACKETS_NEWLY_ACKED) {
+ // Error occurred (e.g., this ACK tries to ack packets in wrong packet
+ // number space), and this would cause the connection to be closed.
+ QUIC_DLOG(ERROR) << ENDPOINT
+ << "Error occurred when processing an ACK frame: "
+ << QuicUtils::AckResultToString(ack_result);
+ return false;
+ }
+ // Cancel the send alarm because new packets likely have been acked, which
+ // may change the congestion window and/or pacing rate. Canceling the alarm
+ // causes CanWrite to recalculate the next send time.
+ if (send_alarm_->IsSet()) {
+ send_alarm_->Cancel();
+ }
+ if (supports_release_time_) {
+ // Update pace time into future because smoothed RTT is likely updated.
+ UpdateReleaseTimeIntoFuture();
+ }
+ SetLargestReceivedPacketWithAck(last_header_.packet_number);
+ // If the incoming ack's packets set expresses missing packets: peer is still
+ // waiting for a packet lower than a packet that we are no longer planning to
+ // send.
+ // If the incoming ack's packets set expresses received packets: peer is still
+ // acking packets which we never care about.
+ // Send an ack to raise the high water mark.
+ PostProcessAfterAckFrame(GetLeastUnacked() > start,
+ ack_result == PACKETS_NEWLY_ACKED);
+ processing_ack_frame_ = false;
+
+ return connected_;
+}
+
+bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a stop waiting frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (no_stop_waiting_frames_) {
+ return true;
+ }
+ if (largest_seen_packet_with_stop_waiting_.IsInitialized() &&
+ last_header_.packet_number <= largest_seen_packet_with_stop_waiting_) {
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Received an old stop waiting frame: ignoring";
+ return true;
+ }
+
+ const char* error = ValidateStopWaitingFrame(frame);
+ if (error != nullptr) {
+ CloseConnection(QUIC_INVALID_STOP_WAITING_DATA, error,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnStopWaitingFrame(frame);
+ }
+
+ largest_seen_packet_with_stop_waiting_ = last_header_.packet_number;
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.DontWaitForPacketsBefore(
+ last_decrypted_packet_level_, frame.least_unacked);
+ } else {
+ received_packet_manager_.DontWaitForPacketsBefore(frame.least_unacked);
+ }
+ return connected_;
+}
+
+bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) {
+ DCHECK(connected_);
+ UpdatePacketContent(SECOND_FRAME_IS_PADDING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnPaddingFrame(frame);
+ }
+ return true;
+}
+
+bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) {
+ DCHECK(connected_);
+ UpdatePacketContent(FIRST_FRAME_IS_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnPingFrame(frame);
+ }
+ should_last_packet_instigate_acks_ = true;
+ return true;
+}
+
+const char* QuicConnection::ValidateStopWaitingFrame(
+ const QuicStopWaitingFrame& stop_waiting) {
+ const QuicPacketNumber peer_least_packet_awaiting_ack =
+ use_uber_received_packet_manager_
+ ? uber_received_packet_manager_.peer_least_packet_awaiting_ack()
+ : received_packet_manager_.peer_least_packet_awaiting_ack();
+ if (peer_least_packet_awaiting_ack.IsInitialized() &&
+ stop_waiting.least_unacked < peer_least_packet_awaiting_ack) {
+ QUIC_DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: "
+ << stop_waiting.least_unacked << " vs "
+ << peer_least_packet_awaiting_ack;
+ // We never process old ack frames, so this number should only increase.
+ return "Least unacked too small.";
+ }
+
+ if (stop_waiting.least_unacked > last_header_.packet_number) {
+ QUIC_DLOG(ERROR) << ENDPOINT
+ << "Peer sent least_unacked:" << stop_waiting.least_unacked
+ << " greater than the enclosing packet number:"
+ << last_header_.packet_number;
+ return "Least unacked too large.";
+ }
+
+ return nullptr;
+}
+
+bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a reset stream frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnRstStreamFrame(frame);
+ }
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "RST_STREAM_FRAME received for stream: " << frame.stream_id
+ << " with error: "
+ << QuicRstStreamErrorCodeToString(frame.error_code);
+ visitor_->OnRstStream(frame);
+ should_last_packet_instigate_acks_ = true;
+ return connected_;
+}
+
+bool QuicConnection::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a reset stream frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnStopSendingFrame(frame);
+ }
+
+ QUIC_DLOG(INFO) << ENDPOINT << "STOP_SENDING frame received for stream: "
+ << frame.stream_id
+ << " with error: " << frame.application_error_code;
+
+ visitor_->OnStopSendingFrame(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnPathChallengeFrame(const QuicPathChallengeFrame& frame) {
+ // Save the path challenge's payload, for later use in generating the
+ // response.
+ received_path_challenge_payloads_.push_back(frame.data_buffer);
+
+ // For VERSION 99 we define a "Padded PATH CHALLENGE" to be the same thing
+ // as a PADDED PING -- it will start a connectivity check and prevent
+ // connection migration. Insofar as the connectivity check and connection
+ // migration are concerned, logically the PATH CHALLENGE is the same as the
+ // PING, so as a stopgap, tell the FSM that determines whether we have a
+ // Padded PING or not that we received a PING.
+ UpdatePacketContent(FIRST_FRAME_IS_PING);
+ should_last_packet_instigate_acks_ = true;
+ return true;
+}
+
+bool QuicConnection::OnPathResponseFrame(const QuicPathResponseFrame& frame) {
+ should_last_packet_instigate_acks_ = true;
+ if (!transmitted_connectivity_probe_payload_ ||
+ *transmitted_connectivity_probe_payload_ != frame.data_buffer) {
+ // Is not for the probe we sent, ignore it.
+ return true;
+ }
+ // Have received the matching PATH RESPONSE, saved payload no longer valid.
+ transmitted_connectivity_probe_payload_ = nullptr;
+ UpdatePacketContent(FIRST_FRAME_IS_PING);
+ return true;
+}
+
+bool QuicConnection::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a connection close frame was received, this is not a connectivity
+ // probe. A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnConnectionCloseFrame(frame);
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Received ConnectionClose for connection: "
+ << connection_id() << ", with error: "
+ << QuicErrorCodeToString(frame.quic_error_code) << " ("
+ << frame.error_details << ")";
+ if (frame.close_type == GOOGLE_QUIC_CONNECTION_CLOSE &&
+ frame.quic_error_code == QUIC_BAD_MULTIPATH_FLAG) {
+ QUIC_LOG_FIRST_N(ERROR, 10) << "Unexpected QUIC_BAD_MULTIPATH_FLAG error."
+ << " last_received_header: " << last_header_
+ << " encryption_level: " << encryption_level_;
+ }
+ TearDownLocalConnectionState(frame.quic_error_code, frame.error_details,
+ ConnectionCloseSource::FROM_PEER);
+ return connected_;
+}
+
+bool QuicConnection::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) {
+ return visitor_->OnMaxStreamIdFrame(frame);
+}
+
+bool QuicConnection::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ return visitor_->OnStreamIdBlockedFrame(frame);
+}
+
+bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a go away frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnGoAwayFrame(frame);
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "GOAWAY_FRAME received with last good stream: "
+ << frame.last_good_stream_id
+ << " and error: " << QuicErrorCodeToString(frame.error_code)
+ << " and reason: " << frame.reason_phrase;
+
+ visitor_->OnGoAway(frame);
+ should_last_packet_instigate_acks_ = true;
+ return connected_;
+}
+
+bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a window update frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnWindowUpdateFrame(frame, time_of_last_received_packet_);
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "WINDOW_UPDATE_FRAME received for stream: "
+ << frame.stream_id
+ << " with byte offset: " << frame.byte_offset;
+ visitor_->OnWindowUpdateFrame(frame);
+ should_last_packet_instigate_acks_ = true;
+ return connected_;
+}
+
+bool QuicConnection::OnNewConnectionIdFrame(
+ const QuicNewConnectionIdFrame& frame) {
+ return true;
+}
+
+bool QuicConnection::OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) {
+ return true;
+}
+
+bool QuicConnection::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+ return true;
+}
+
+bool QuicConnection::OnMessageFrame(const QuicMessageFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a message frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnMessageFrame(frame);
+ }
+ visitor_->OnMessageReceived(
+ QuicStringPiece(frame.data, frame.message_length));
+ should_last_packet_instigate_acks_ = true;
+ return connected_;
+}
+
+bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ DCHECK(connected_);
+
+ // Since a blocked frame was received, this is not a connectivity probe.
+ // A probe only contains a PING and full padding.
+ UpdatePacketContent(NOT_PADDED_PING);
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnBlockedFrame(frame);
+ }
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "BLOCKED_FRAME received for stream: " << frame.stream_id;
+ visitor_->OnBlockedFrame(frame);
+ stats_.blocked_frames_received++;
+ should_last_packet_instigate_acks_ = true;
+ return connected_;
+}
+
+void QuicConnection::OnPacketComplete() {
+ // Don't do anything if this packet closed the connection.
+ if (!connected_) {
+ ClearLastFrames();
+ return;
+ }
+
+ if (IsCurrentPacketConnectivityProbing()) {
+ ++stats_.num_connectivity_probing_received;
+ }
+
+ QUIC_DVLOG(1) << ENDPOINT << "Got packet " << last_header_.packet_number
+ << " for " << last_header_.destination_connection_id;
+
+ QUIC_DLOG_IF(INFO, current_packet_content_ == SECOND_FRAME_IS_PADDING)
+ << ENDPOINT << "Received a padded PING packet. is_probing: "
+ << IsCurrentPacketConnectivityProbing();
+
+ if (perspective_ == Perspective::IS_CLIENT) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Received a speculative connectivity probing packet for "
+ << last_header_.destination_connection_id
+ << " from ip:port: " << last_packet_source_address_.ToString()
+ << " to ip:port: "
+ << last_packet_destination_address_.ToString();
+ // TODO(zhongyi): change the method name.
+ visitor_->OnConnectivityProbeReceived(last_packet_destination_address_,
+ last_packet_source_address_);
+ } else if (IsCurrentPacketConnectivityProbing()) {
+ // This node is not a client (is a server) AND the received packet was
+ // connectivity-probing, send an appropriate response.
+ QUIC_DVLOG(1) << ENDPOINT << "Received a connectivity probing packet for "
+ << last_header_.destination_connection_id
+ << " from ip:port: " << last_packet_source_address_.ToString()
+ << " to ip:port: "
+ << last_packet_destination_address_.ToString();
+ visitor_->OnConnectivityProbeReceived(last_packet_destination_address_,
+ last_packet_source_address_);
+ } else {
+ // This node is not a client (is a server) AND the received packet was
+ // NOT connectivity-probing. If the packet had PATH CHALLENGES, send
+ // appropriate RESPONSE. Then deal with possible peer migration.
+ if (transport_version() == QUIC_VERSION_99 &&
+ !received_path_challenge_payloads_.empty()) {
+ // If a PATH CHALLENGE was in a "Padded PING (or PATH CHALLENGE)"
+ // then it is taken care of above. This handles the case where a PATH
+ // CHALLENGE appeared someplace else (eg, the peer randomly added a PATH
+ // CHALLENGE frame to some other packet.
+ // There was at least one PATH CHALLENGE in the received packet,
+ // Generate the required PATH RESPONSE.
+ SendGenericPathProbePacket(nullptr, last_packet_source_address_,
+ /* is_response= */ true);
+ }
+
+ if (last_header_.packet_number == GetLargestReceivedPacket()) {
+ direct_peer_address_ = last_packet_source_address_;
+ if (current_effective_peer_migration_type_ != NO_CHANGE) {
+ // TODO(fayang): When multiple packet number spaces is supported, only
+ // start peer migration for the application data.
+ StartEffectivePeerMigration(current_effective_peer_migration_type_);
+ }
+ }
+ }
+
+ current_effective_peer_migration_type_ = NO_CHANGE;
+
+ // An ack will be sent if a missing retransmittable packet was received;
+ const bool was_missing =
+ should_last_packet_instigate_acks_ && was_last_packet_missing_;
+
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.MaybeUpdateAckTimeout(
+ should_last_packet_instigate_acks_, last_decrypted_packet_level_,
+ last_header_.packet_number, time_of_last_received_packet_,
+ clock_->ApproximateNow(), sent_packet_manager_.GetRttStats(),
+ sent_packet_manager_.delayed_ack_time());
+ } else {
+ received_packet_manager_.MaybeUpdateAckTimeout(
+ should_last_packet_instigate_acks_, last_header_.packet_number,
+ time_of_last_received_packet_, clock_->ApproximateNow(),
+ sent_packet_manager_.GetRttStats(),
+ sent_packet_manager_.delayed_ack_time());
+ }
+ } else if (ack_frame_updated()) {
+ // It's possible the ack frame was sent along with response data, so it
+ // no longer needs to be sent.
+ MaybeQueueAck(was_missing);
+ }
+
+ ClearLastFrames();
+ CloseIfTooManyOutstandingSentPackets();
+}
+
+bool QuicConnection::IsValidStatelessResetToken(QuicUint128 token) const {
+ return stateless_reset_token_received_ &&
+ token == received_stateless_reset_token_;
+}
+
+void QuicConnection::OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) {
+ // TODO(fayang): Add OnAuthenticatedIetfStatelessResetPacket to
+ // debug_visitor_.
+ const std::string error_details = "Received stateless reset.";
+ QUIC_CODE_COUNT(quic_tear_down_local_connection_on_stateless_reset);
+ TearDownLocalConnectionState(QUIC_PUBLIC_RESET, error_details,
+ ConnectionCloseSource::FROM_PEER);
+}
+
+void QuicConnection::MaybeQueueAck(bool was_missing) {
+ DCHECK(!received_packet_manager_.decide_when_to_send_acks());
+ ++num_packets_received_since_last_ack_sent_;
+ // Determine whether the newly received packet was missing before recording
+ // the received packet.
+ if (was_missing) {
+ // Only ack immediately if an ACK frame was sent with a larger
+ // largest acked than the newly received packet number.
+ const QuicPacketNumber largest_sent_largest_acked =
+ sent_packet_manager_.unacked_packets().largest_sent_largest_acked();
+ if (largest_sent_largest_acked.IsInitialized() &&
+ last_header_.packet_number < largest_sent_largest_acked) {
+ if (packet_generator_.deprecate_ack_bundling_mode()) {
+ MaybeSetAckAlarmTo(clock_->ApproximateNow());
+ } else {
+ ack_queued_ = true;
+ }
+ }
+ }
+
+ if (should_last_packet_instigate_acks_ && !ack_queued_) {
+ ++num_retransmittable_packets_received_since_last_ack_sent_;
+ if (ack_mode_ != TCP_ACKING &&
+ last_header_.packet_number >=
+ received_packet_manager_.PeerFirstSendingPacketNumber() +
+ min_received_before_ack_decimation_) {
+ // Ack up to 10 packets at once unless ack decimation is unlimited.
+ if (!unlimited_ack_decimation_ &&
+ num_retransmittable_packets_received_since_last_ack_sent_ >=
+ kMaxRetransmittablePacketsBeforeAck) {
+ if (packet_generator_.deprecate_ack_bundling_mode()) {
+ MaybeSetAckAlarmTo(clock_->ApproximateNow());
+ } else {
+ ack_queued_ = true;
+ }
+ } else if (ShouldSetAckAlarm()) {
+ // Wait for the minimum of the ack decimation delay or the delayed ack
+ // time before sending an ack.
+ QuicTime::Delta ack_delay =
+ std::min(sent_packet_manager_.delayed_ack_time(),
+ sent_packet_manager_.GetRttStats()->min_rtt() *
+ ack_decimation_delay_);
+ const QuicTime approximate_now = clock_->ApproximateNow();
+ if (fast_ack_after_quiescence_ &&
+ (approximate_now - time_of_previous_received_packet_) >
+ sent_packet_manager_.GetRttStats()->SmoothedOrInitialRtt()) {
+ // Ack the first packet out of queiscence faster, because QUIC does
+ // not pace the first few packets and commonly these may be handshake
+ // or TLP packets, which we'd like to acknowledge quickly.
+ ack_delay = QuicTime::Delta::FromMilliseconds(1);
+ }
+ ack_alarm_->Set(approximate_now + ack_delay);
+ }
+ } else {
+ // Ack with a timer or every 2 packets by default.
+ if (num_retransmittable_packets_received_since_last_ack_sent_ >=
+ ack_frequency_before_ack_decimation_) {
+ if (packet_generator_.deprecate_ack_bundling_mode()) {
+ MaybeSetAckAlarmTo(clock_->ApproximateNow());
+ } else {
+ ack_queued_ = true;
+ }
+ } else if (ShouldSetAckAlarm()) {
+ const QuicTime approximate_now = clock_->ApproximateNow();
+ if (fast_ack_after_quiescence_ &&
+ (approximate_now - time_of_previous_received_packet_) >
+ sent_packet_manager_.GetRttStats()->SmoothedOrInitialRtt()) {
+ // Ack the first packet out of queiscence faster, because QUIC does
+ // not pace the first few packets and commonly these may be handshake
+ // or TLP packets, which we'd like to acknowledge quickly.
+ ack_alarm_->Set(approximate_now +
+ QuicTime::Delta::FromMilliseconds(1));
+ } else {
+ ack_alarm_->Set(approximate_now +
+ sent_packet_manager_.delayed_ack_time());
+ }
+ }
+ }
+
+ // If there are new missing packets to report, send an ack immediately.
+ if (received_packet_manager_.HasNewMissingPackets()) {
+ if (ack_mode_ == ACK_DECIMATION_WITH_REORDERING) {
+ // Wait the minimum of an eighth min_rtt and the existing ack time.
+ QuicTime ack_time =
+ clock_->ApproximateNow() +
+ 0.125 * sent_packet_manager_.GetRttStats()->min_rtt();
+ if (ShouldSetAckAlarm() || ack_alarm_->deadline() > ack_time) {
+ ack_alarm_->Update(ack_time, QuicTime::Delta::Zero());
+ }
+ } else {
+ if (packet_generator_.deprecate_ack_bundling_mode()) {
+ MaybeSetAckAlarmTo(clock_->ApproximateNow());
+ } else {
+ ack_queued_ = true;
+ }
+ }
+ }
+
+ if (fast_ack_after_quiescence_) {
+ time_of_previous_received_packet_ = time_of_last_received_packet_;
+ }
+ }
+
+ if (ack_queued_) {
+ ack_alarm_->Cancel();
+ }
+}
+
+void QuicConnection::ClearLastFrames() {
+ should_last_packet_instigate_acks_ = false;
+}
+
+void QuicConnection::CloseIfTooManyOutstandingSentPackets() {
+ // This occurs if we don't discard old packets we've seen fast enough. It's
+ // possible largest observed is less than leaset unacked.
+ if (sent_packet_manager_.GetLargestObserved().IsInitialized() &&
+ sent_packet_manager_.GetLargestObserved() >
+ sent_packet_manager_.GetLeastUnacked() + max_tracked_packets_) {
+ CloseConnection(
+ QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS,
+ QuicStrCat("More than ", max_tracked_packets_,
+ " outstanding, least_unacked: ",
+ sent_packet_manager_.GetLeastUnacked().ToUint64()),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+}
+
+const QuicFrame QuicConnection::GetUpdatedAckFrame() {
+ if (use_uber_received_packet_manager_) {
+ return uber_received_packet_manager_.GetUpdatedAckFrame(
+ QuicUtils::GetPacketNumberSpace(encryption_level_),
+ clock_->ApproximateNow());
+ }
+ return received_packet_manager_.GetUpdatedAckFrame(clock_->ApproximateNow());
+}
+
+void QuicConnection::PopulateStopWaitingFrame(
+ QuicStopWaitingFrame* stop_waiting) {
+ stop_waiting->least_unacked = GetLeastUnacked();
+}
+
+QuicPacketNumber QuicConnection::GetLeastUnacked() const {
+ return sent_packet_manager_.GetLeastUnacked();
+}
+
+bool QuicConnection::HandleWriteBlocked() {
+ if (!writer_->IsWriteBlocked()) {
+ return false;
+ }
+
+ visitor_->OnWriteBlocked();
+ return true;
+}
+
+void QuicConnection::MaybeSendInResponseToPacket() {
+ if (!connected_) {
+ return;
+ }
+
+ // If the writer is blocked, don't attempt to send packets now or in the send
+ // alarm. When the writer unblocks, OnCanWrite() will be called for this
+ // connection to send.
+ if (HandleWriteBlocked()) {
+ return;
+ }
+
+ // Now that we have received an ack, we might be able to send packets which
+ // are queued locally, or drain streams which are blocked.
+ if (defer_send_in_response_to_packets_) {
+ send_alarm_->Update(clock_->ApproximateNow(), QuicTime::Delta::Zero());
+ } else {
+ WriteAndBundleAcksIfNotBlocked();
+ }
+}
+
+void QuicConnection::SendVersionNegotiationPacket(bool ietf_quic) {
+ pending_version_negotiation_packet_ = true;
+ send_ietf_version_negotiation_packet_ = ietf_quic;
+
+ if (HandleWriteBlocked()) {
+ return;
+ }
+
+ QUIC_DLOG(INFO) << ENDPOINT << "Sending version negotiation packet: {"
+ << ParsedQuicVersionVectorToString(
+ framer_.supported_versions())
+ << "}, ietf_quic: " << ietf_quic;
+ std::unique_ptr<QuicEncryptedPacket> version_packet(
+ packet_generator_.SerializeVersionNegotiationPacket(
+ ietf_quic, framer_.supported_versions()));
+ WriteResult result = writer_->WritePacket(
+ version_packet->data(), version_packet->length(), self_address().host(),
+ peer_address(), per_packet_options_);
+
+ if (IsWriteError(result.status)) {
+ OnWriteError(result.error_code);
+ return;
+ }
+ if (IsWriteBlockedStatus(result.status)) {
+ visitor_->OnWriteBlocked();
+ if (result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) {
+ pending_version_negotiation_packet_ = false;
+ }
+ return;
+ }
+
+ pending_version_negotiation_packet_ = false;
+}
+
+size_t QuicConnection::SendCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset) {
+ if (write_length == 0) {
+ QUIC_BUG << "Attempt to send empty crypto frame";
+ return 0;
+ }
+
+ ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING);
+ return packet_generator_.ConsumeCryptoData(level, write_length, offset);
+}
+
+QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ if (state == NO_FIN && write_length == 0) {
+ QUIC_BUG << "Attempt to send empty stream frame";
+ return QuicConsumedData(0, false);
+ }
+
+ // Opportunistically bundle an ack with every outgoing packet.
+ // Particularly, we want to bundle with handshake packets since we don't know
+ // which decrypter will be used on an ack packet following a handshake
+ // packet (a handshake packet from client to server could result in a REJ or a
+ // SHLO from the server, leading to two different decrypters at the server.)
+ ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING);
+ return packet_generator_.ConsumeData(id, write_length, offset, state);
+}
+
+bool QuicConnection::SendControlFrame(const QuicFrame& frame) {
+ if (!CanWrite(HAS_RETRANSMITTABLE_DATA) && frame.type != PING_FRAME) {
+ QUIC_DVLOG(1) << ENDPOINT << "Failed to send control frame: " << frame;
+ // Do not check congestion window for ping.
+ return false;
+ }
+ ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING);
+ packet_generator_.AddControlFrame(frame);
+ if (frame.type == PING_FRAME) {
+ // Flush PING frame immediately.
+ packet_generator_.FlushAllQueuedFrames();
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnPingSent();
+ }
+ }
+ if (frame.type == BLOCKED_FRAME) {
+ stats_.blocked_frames_sent++;
+ }
+ return true;
+}
+
+void QuicConnection::OnStreamReset(QuicStreamId id,
+ QuicRstStreamErrorCode error) {
+ if (error == QUIC_STREAM_NO_ERROR) {
+ // All data for streams which are reset with QUIC_STREAM_NO_ERROR must
+ // be received by the peer.
+ return;
+ }
+ // Flush stream frames of reset stream.
+ if (packet_generator_.HasPendingStreamFramesOfStream(id)) {
+ ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING);
+ packet_generator_.FlushAllQueuedFrames();
+ }
+
+ sent_packet_manager_.CancelRetransmissionsForStream(id);
+ // Remove all queued packets which only contain data for the reset stream.
+ // TODO(fayang): consider removing this because it should be rarely executed.
+ auto packet_iterator = queued_packets_.begin();
+ while (packet_iterator != queued_packets_.end()) {
+ QuicFrames* retransmittable_frames =
+ &packet_iterator->retransmittable_frames;
+ if (retransmittable_frames->empty()) {
+ ++packet_iterator;
+ continue;
+ }
+ // NOTE THAT RemoveFramesForStream removes only STREAM frames
+ // for the specified stream.
+ RemoveFramesForStream(retransmittable_frames, id);
+ if (!retransmittable_frames->empty()) {
+ ++packet_iterator;
+ continue;
+ }
+ delete[] packet_iterator->encrypted_buffer;
+ ClearSerializedPacket(&(*packet_iterator));
+ packet_iterator = queued_packets_.erase(packet_iterator);
+ }
+ // TODO(ianswett): Consider checking for 3 RTOs when the last stream is
+ // cancelled as well.
+}
+
+const QuicConnectionStats& QuicConnection::GetStats() {
+ const RttStats* rtt_stats = sent_packet_manager_.GetRttStats();
+
+ // Update rtt and estimated bandwidth.
+ QuicTime::Delta min_rtt = rtt_stats->min_rtt();
+ if (min_rtt.IsZero()) {
+ // If min RTT has not been set, use initial RTT instead.
+ min_rtt = rtt_stats->initial_rtt();
+ }
+ stats_.min_rtt_us = min_rtt.ToMicroseconds();
+
+ QuicTime::Delta srtt = rtt_stats->SmoothedOrInitialRtt();
+ stats_.srtt_us = srtt.ToMicroseconds();
+
+ stats_.estimated_bandwidth = sent_packet_manager_.BandwidthEstimate();
+ stats_.max_packet_size = packet_generator_.GetCurrentMaxPacketLength();
+ stats_.max_received_packet_size = largest_received_packet_size_;
+ return stats_;
+}
+
+void QuicConnection::OnCoalescedPacket(const QuicEncryptedPacket& packet) {
+ QueueCoalescedPacket(packet);
+}
+
+void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) {
+ if (!connected_) {
+ return;
+ }
+ QUIC_DVLOG(2) << ENDPOINT << "Received encrypted " << packet.length()
+ << " bytes:" << std::endl
+ << QuicTextUtils::HexDump(
+ QuicStringPiece(packet.data(), packet.length()));
+ QUIC_BUG_IF(current_packet_data_ != nullptr)
+ << "ProcessUdpPacket must not be called while processing a packet.";
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnPacketReceived(self_address, peer_address, packet);
+ }
+ last_size_ = packet.length();
+ current_packet_data_ = packet.data();
+
+ last_packet_destination_address_ = self_address;
+ last_packet_source_address_ = peer_address;
+ if (!self_address_.IsInitialized()) {
+ self_address_ = last_packet_destination_address_;
+ }
+
+ if (!direct_peer_address_.IsInitialized()) {
+ direct_peer_address_ = last_packet_source_address_;
+ }
+
+ if (!effective_peer_address_.IsInitialized()) {
+ const QuicSocketAddress effective_peer_addr =
+ GetEffectivePeerAddressFromCurrentPacket();
+
+ // effective_peer_address_ must be initialized at the beginning of the
+ // first packet processed(here). If effective_peer_addr is uninitialized,
+ // just set effective_peer_address_ to the direct peer address.
+ effective_peer_address_ = effective_peer_addr.IsInitialized()
+ ? effective_peer_addr
+ : direct_peer_address_;
+ }
+
+ stats_.bytes_received += packet.length();
+ ++stats_.packets_received;
+
+ // Ensure the time coming from the packet reader is within 2 minutes of now.
+ if (std::abs((packet.receipt_time() - clock_->ApproximateNow()).ToSeconds()) >
+ 2 * 60) {
+ QUIC_BUG << "Packet receipt time:"
+ << packet.receipt_time().ToDebuggingValue()
+ << " 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: "
+ << time_of_last_received_packet_.ToDebuggingValue();
+
+ ScopedPacketFlusher flusher(this, NO_ACK);
+ if (!framer_.ProcessPacket(packet)) {
+ // If we are unable to decrypt this packet, it might be
+ // because the CHLO or SHLO packet was lost.
+ if (framer_.error() == QUIC_DECRYPTION_FAILURE) {
+ if (encryption_level_ != ENCRYPTION_FORWARD_SECURE &&
+ undecryptable_packets_.size() < max_undecryptable_packets_) {
+ QueueUndecryptablePacket(packet);
+ } else if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnUndecryptablePacket();
+ }
+ }
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Unable to process packet. Last packet processed: "
+ << last_header_.packet_number;
+ current_packet_data_ = nullptr;
+ is_current_packet_connectivity_probing_ = false;
+
+ MaybeProcessCoalescedPackets();
+ return;
+ }
+
+ ++stats_.packets_processed;
+
+ QUIC_DLOG_IF(INFO, active_effective_peer_migration_type_ != NO_CHANGE)
+ << "sent_packet_manager_.GetLargestObserved() = "
+ << sent_packet_manager_.GetLargestObserved()
+ << ", highest_packet_sent_before_effective_peer_migration_ = "
+ << highest_packet_sent_before_effective_peer_migration_;
+ if (active_effective_peer_migration_type_ != NO_CHANGE &&
+ sent_packet_manager_.GetLargestObserved().IsInitialized() &&
+ (!highest_packet_sent_before_effective_peer_migration_.IsInitialized() ||
+ sent_packet_manager_.GetLargestObserved() >
+ highest_packet_sent_before_effective_peer_migration_)) {
+ if (perspective_ == Perspective::IS_SERVER) {
+ OnEffectivePeerMigrationValidated();
+ }
+ }
+
+ MaybeProcessCoalescedPackets();
+ MaybeProcessUndecryptablePackets();
+ MaybeSendInResponseToPacket();
+ SetPingAlarm();
+ current_packet_data_ = nullptr;
+ is_current_packet_connectivity_probing_ = false;
+}
+
+void QuicConnection::OnBlockedWriterCanWrite() {
+ writer_->SetWritable();
+ OnCanWrite();
+}
+
+void QuicConnection::OnCanWrite() {
+ DCHECK(!writer_->IsWriteBlocked());
+
+ // Add a flusher to ensure the connection is marked app-limited.
+ ScopedPacketFlusher flusher(this, NO_ACK);
+
+ WriteQueuedPackets();
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ const QuicTime ack_timeout =
+ use_uber_received_packet_manager_
+ ? uber_received_packet_manager_.GetEarliestAckTimeout()
+ : received_packet_manager_.ack_timeout();
+ if (ack_timeout.IsInitialized() &&
+ ack_timeout <= clock_->ApproximateNow()) {
+ // Send an ACK now because either 1) we were write blocked when we last
+ // tried to send an ACK, or 2) both ack alarm and send alarm were set to
+ // go off together.
+ if (SupportsMultiplePacketNumberSpaces()) {
+ SendAllPendingAcks();
+ } else {
+ SendAck();
+ }
+ }
+ } else if (send_ack_when_on_can_write_) {
+ // Send an ACK now because either 1) we were write blocked when we last
+ // tried to send an ACK, or 2) both ack alarm and send alarm were set to go
+ // off together.
+ DCHECK(packet_generator_.deprecate_ack_bundling_mode());
+ SendAck();
+ }
+ if (!session_decides_what_to_write()) {
+ WritePendingRetransmissions();
+ }
+
+ WriteNewData();
+}
+
+void QuicConnection::WriteNewData() {
+ // Sending queued packets may have caused the socket to become write blocked,
+ // or the congestion manager to prohibit sending. If we've sent everything
+ // we had queued and we're still not blocked, let the visitor know it can
+ // write more.
+ if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ return;
+ }
+
+ {
+ ScopedPacketFlusher flusher(this, SEND_ACK_IF_QUEUED);
+ visitor_->OnCanWrite();
+ }
+
+ // After the visitor writes, it may have caused the socket to become write
+ // blocked or the congestion manager to prohibit sending, so check again.
+ if (visitor_->WillingAndAbleToWrite() && !send_alarm_->IsSet() &&
+ CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ // We're not write blocked, but some stream didn't write out all of its
+ // bytes. Register for 'immediate' resumption so we'll keep writing after
+ // other connections and events have had a chance to use the thread.
+ send_alarm_->Set(clock_->ApproximateNow());
+ }
+}
+
+void QuicConnection::WriteIfNotBlocked() {
+ if (!HandleWriteBlocked()) {
+ OnCanWrite();
+ }
+}
+
+void QuicConnection::WriteAndBundleAcksIfNotBlocked() {
+ if (!HandleWriteBlocked()) {
+ ScopedPacketFlusher flusher(this, SEND_ACK_IF_QUEUED);
+ WriteIfNotBlocked();
+ }
+}
+
+bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) {
+ if (perspective_ == Perspective::IS_SERVER && self_address_.IsInitialized() &&
+ last_packet_destination_address_.IsInitialized() &&
+ self_address_ != last_packet_destination_address_) {
+ // Allow change between pure IPv4 and equivalent mapped IPv4 address.
+ if (self_address_.port() != last_packet_destination_address_.port() ||
+ self_address_.host().Normalized() !=
+ last_packet_destination_address_.host().Normalized()) {
+ if (!visitor_->AllowSelfAddressChange()) {
+ CloseConnection(
+ QUIC_ERROR_MIGRATING_ADDRESS,
+ "Self address migration is not supported at the server.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ }
+ self_address_ = last_packet_destination_address_;
+ }
+
+ if (PacketCanReplaceConnectionId(header, perspective_) &&
+ connection_id_ != header.source_connection_id) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Replacing connection ID " << connection_id_
+ << " with " << header.source_connection_id;
+ connection_id_ = header.source_connection_id;
+ packet_generator_.SetConnectionId(connection_id_);
+ }
+
+ if (!ValidateReceivedPacketNumber(header.packet_number)) {
+ return false;
+ }
+
+ if (version_negotiation_state_ != NEGOTIATED_VERSION) {
+ if (perspective_ == Perspective::IS_CLIENT) {
+ DCHECK(!header.version_flag || header.form != GOOGLE_QUIC_PACKET);
+ if (framer_.transport_version() <= QUIC_VERSION_43) {
+ // If the client gets a packet without the version flag from the server
+ // it should stop sending version since the version negotiation is done.
+ // IETF QUIC stops sending version once encryption level switches to
+ // forward secure.
+ packet_generator_.StopSendingVersion();
+ }
+ version_negotiation_state_ = NEGOTIATED_VERSION;
+ visitor_->OnSuccessfulVersionNegotiation(version());
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnSuccessfulVersionNegotiation(version());
+ }
+ }
+ }
+
+ if (last_size_ > largest_received_packet_size_) {
+ largest_received_packet_size_ = last_size_;
+ }
+
+ if (perspective_ == Perspective::IS_SERVER &&
+ encryption_level_ == ENCRYPTION_INITIAL &&
+ last_size_ > packet_generator_.GetCurrentMaxPacketLength()) {
+ SetMaxPacketLength(last_size_);
+ }
+ return true;
+}
+
+bool QuicConnection::ValidateReceivedPacketNumber(
+ QuicPacketNumber packet_number) {
+ if (validate_packet_number_post_decryption_) {
+ const bool is_awaiting =
+ use_uber_received_packet_manager_
+ ? uber_received_packet_manager_.IsAwaitingPacket(
+ last_decrypted_packet_level_, packet_number)
+ : received_packet_manager_.IsAwaitingPacket(packet_number);
+ if (!is_awaiting) {
+ if (use_uber_received_packet_manager_) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Packet " << packet_number
+ << " no longer being waited for at level "
+ << static_cast<int>(last_decrypted_packet_level_)
+ << ". Discarding.";
+ } else {
+ QUIC_DLOG(INFO) << ENDPOINT << "Packet " << packet_number
+ << " no longer being waited for. Discarding.";
+ }
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnDuplicatePacket(packet_number);
+ }
+ return false;
+ }
+ }
+
+ if (use_uber_received_packet_manager_) {
+ // When using uber_received_packet_manager, accept any packet numbers.
+ return true;
+ }
+
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_enable_accept_random_ipn, 2, 2);
+ // Configured to accept any packet number in range 1...0x7fffffff as initial
+ // packet number.
+ bool out_of_bound = false;
+ std::string error_detail = "Packet number out of bounds.";
+ if (last_header_.packet_number.IsInitialized()) {
+ out_of_bound = !Near(packet_number, last_header_.packet_number);
+ } else if ((packet_number > MaxRandomInitialPacketNumber())) {
+ out_of_bound = true;
+ error_detail = "Initial packet number out of bounds.";
+ }
+ if (out_of_bound) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Packet " << packet_number
+ << " out of bounds. Discarding";
+ CloseConnection(QUIC_INVALID_PACKET_HEADER, error_detail,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ return true;
+ }
+
+ if (packet_number > received_packet_manager_.PeerFirstSendingPacketNumber() &&
+ packet_number <= MaxRandomInitialPacketNumber()) {
+ QUIC_CODE_COUNT_N(had_possibly_random_ipn, 2, 2);
+ }
+ const bool out_of_bound =
+ last_header_.packet_number.IsInitialized()
+ ? !Near(packet_number, last_header_.packet_number)
+ : packet_number >=
+ (received_packet_manager_.PeerFirstSendingPacketNumber() +
+ kMaxPacketGap);
+ if (!out_of_bound) {
+ return true;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Packet " << packet_number
+ << " out of bounds. Discarding";
+ QuicStringPiece packet_data = GetCurrentPacket();
+ const size_t kMaxPacketLengthInErrorDetails = 64;
+ CloseConnection(
+ QUIC_INVALID_PACKET_HEADER,
+ QuicStrCat(
+ "Packet number out of bounds. ",
+ last_header_.packet_number.IsInitialized()
+ ? QuicStrCat("last_pkn=", last_header_.packet_number.ToUint64())
+ : "first received packet",
+ ", current_pkn=", packet_number.ToUint64(),
+ ", current_pkt_len=", packet_data.length(), ", current_hdr=",
+ QuicTextUtils::HexEncode(
+ packet_data.length() > kMaxPacketLengthInErrorDetails
+ ? QuicStringPiece(packet_data.data(),
+ kMaxPacketLengthInErrorDetails)
+ : packet_data)),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+}
+
+void QuicConnection::WriteQueuedPackets() {
+ DCHECK(!writer_->IsWriteBlocked());
+
+ if (pending_version_negotiation_packet_) {
+ SendVersionNegotiationPacket(send_ietf_version_negotiation_packet_);
+ }
+
+ QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsBeforeWrite",
+ queued_packets_.size(), 1, 1000, 50, "");
+ while (!queued_packets_.empty()) {
+ // WritePacket() can potentially clear all queued packets, so we need to
+ // save the first queued packet to a local variable before calling it.
+ SerializedPacket packet(std::move(queued_packets_.front()));
+ queued_packets_.pop_front();
+
+ const bool write_result = WritePacket(&packet);
+
+ if (connected_ && !write_result) {
+ // Write failed but connection is open, re-insert |packet| into the
+ // front of the queue, it will be retried later.
+ queued_packets_.emplace_front(std::move(packet));
+ break;
+ }
+
+ delete[] packet.encrypted_buffer;
+ ClearSerializedPacket(&packet);
+ if (!connected_) {
+ DCHECK(queued_packets_.empty()) << "Queued packets should have been "
+ "cleared while closing connection";
+ break;
+ }
+
+ // Continue to send the next packet in queue.
+ }
+}
+
+void QuicConnection::WritePendingRetransmissions() {
+ DCHECK(!session_decides_what_to_write());
+ // Keep writing as long as there's a pending retransmission which can be
+ // written.
+ while (sent_packet_manager_.HasPendingRetransmissions() &&
+ CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ const QuicPendingRetransmission pending =
+ sent_packet_manager_.NextPendingRetransmission();
+
+ // Re-packetize the frames with a new packet number for retransmission.
+ // Retransmitted packets use the same packet number length as the
+ // original.
+ // Flush the packet generator before making a new packet.
+ // TODO(ianswett): Implement ReserializeAllFrames as a separate path that
+ // does not require the creator to be flushed.
+ // TODO(fayang): FlushAllQueuedFrames should only be called once, and should
+ // be moved outside of the loop. Also, CanWrite is not checked after the
+ // generator is flushed.
+ {
+ ScopedPacketFlusher flusher(this, NO_ACK);
+ packet_generator_.FlushAllQueuedFrames();
+ }
+ DCHECK(!packet_generator_.HasQueuedFrames());
+ char buffer[kMaxOutgoingPacketSize];
+ packet_generator_.ReserializeAllFrames(pending, buffer,
+ kMaxOutgoingPacketSize);
+ }
+}
+
+void QuicConnection::SendProbingRetransmissions() {
+ while (sent_packet_manager_.GetSendAlgorithm()->ShouldSendProbingPacket() &&
+ CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ const bool can_retransmit =
+ sent_packet_manager_.MaybeRetransmitOldestPacket(
+ PROBING_RETRANSMISSION);
+ if (!can_retransmit) {
+ QUIC_DVLOG(1)
+ << "Cannot send probing retransmissions: nothing to retransmit.";
+ break;
+ }
+
+ if (!session_decides_what_to_write()) {
+ DCHECK(sent_packet_manager_.HasPendingRetransmissions());
+ WritePendingRetransmissions();
+ }
+ }
+}
+
+void QuicConnection::RetransmitUnackedPackets(
+ TransmissionType retransmission_type) {
+ sent_packet_manager_.RetransmitUnackedPackets(retransmission_type);
+
+ WriteIfNotBlocked();
+}
+
+void QuicConnection::NeuterUnencryptedPackets() {
+ sent_packet_manager_.NeuterUnencryptedPackets();
+ // This may have changed the retransmission timer, so re-arm it.
+ SetRetransmissionAlarm();
+}
+
+bool QuicConnection::ShouldGeneratePacket(
+ HasRetransmittableData retransmittable,
+ IsHandshake handshake) {
+ // We should serialize handshake packets immediately to ensure that they
+ // end up sent at the right encryption level.
+ if (handshake == IS_HANDSHAKE) {
+ return true;
+ }
+
+ return CanWrite(retransmittable);
+}
+
+const QuicFrames QuicConnection::MaybeBundleAckOpportunistically() {
+ DCHECK(packet_generator_.deprecate_ack_bundling_mode());
+ QuicFrames frames;
+ bool has_pending_ack = false;
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (use_uber_received_packet_manager_) {
+ has_pending_ack =
+ uber_received_packet_manager_
+ .GetAckTimeout(QuicUtils::GetPacketNumberSpace(encryption_level_))
+ .IsInitialized();
+ } else {
+ has_pending_ack = received_packet_manager_.ack_timeout().IsInitialized();
+ }
+ } else {
+ has_pending_ack = ack_alarm_->IsSet();
+ }
+ if (!has_pending_ack && stop_waiting_count_ <= 1) {
+ // No need to send an ACK.
+ return frames;
+ }
+ ResetAckStates();
+
+ QUIC_DVLOG(1) << ENDPOINT << "Bundle an ACK opportunistically";
+ frames.push_back(GetUpdatedAckFrame());
+ if (!no_stop_waiting_frames_) {
+ QuicStopWaitingFrame stop_waiting;
+ PopulateStopWaitingFrame(&stop_waiting);
+ frames.push_back(QuicFrame(stop_waiting));
+ }
+ return frames;
+}
+
+bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) {
+ if (!connected_) {
+ return false;
+ }
+
+ if (session_decides_what_to_write() &&
+ sent_packet_manager_.pending_timer_transmission_count() > 0) {
+ // Force sending the retransmissions for HANDSHAKE, TLP, RTO, PROBING cases.
+ return true;
+ }
+
+ if (HandleWriteBlocked()) {
+ return false;
+ }
+
+ // Allow acks to be sent immediately.
+ if (retransmittable == NO_RETRANSMITTABLE_DATA) {
+ return true;
+ }
+ // If the send alarm is set, wait for it to fire.
+ if (send_alarm_->IsSet()) {
+ return false;
+ }
+
+ QuicTime now = clock_->Now();
+ QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(now);
+ if (delay.IsInfinite()) {
+ send_alarm_->Cancel();
+ return false;
+ }
+
+ // Scheduler requires a delay.
+ if (!delay.IsZero()) {
+ if (delay <= release_time_into_future_) {
+ // Required delay is within pace time into future, send now.
+ return true;
+ }
+ // Cannot send packet now because delay is too far in the future.
+ send_alarm_->Update(now + delay, QuicTime::Delta::FromMilliseconds(1));
+ QUIC_DVLOG(1) << ENDPOINT << "Delaying sending " << delay.ToMilliseconds()
+ << "ms";
+ return false;
+ }
+ return true;
+}
+
+bool QuicConnection::WritePacket(SerializedPacket* packet) {
+ if (ShouldDiscardPacket(*packet)) {
+ ++stats_.packets_discarded;
+ return true;
+ }
+ if (sent_packet_manager_.GetLargestSentPacket().IsInitialized() &&
+ packet->packet_number < sent_packet_manager_.GetLargestSentPacket()) {
+ QUIC_BUG << "Attempt to write packet:" << packet->packet_number
+ << " after:" << sent_packet_manager_.GetLargestSentPacket();
+ QUIC_CLIENT_HISTOGRAM_COUNTS("QuicSession.NumQueuedPacketsAtOutOfOrder",
+ queued_packets_.size(), 1, 1000, 50, "");
+ CloseConnection(QUIC_INTERNAL_ERROR, "Packet written out of order.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return true;
+ }
+ // Termination packets are encrypted and saved, so don't exit early.
+ const bool is_termination_packet = IsTerminationPacket(*packet);
+ if (HandleWriteBlocked() && !is_termination_packet) {
+ return false;
+ }
+
+ QuicPacketNumber packet_number = packet->packet_number;
+
+ QuicPacketLength encrypted_length = packet->encrypted_length;
+ // Termination packets are eventually owned by TimeWaitListManager.
+ // Others are deleted at the end of this call.
+ if (is_termination_packet) {
+ if (termination_packets_ == nullptr) {
+ termination_packets_.reset(
+ new std::vector<std::unique_ptr<QuicEncryptedPacket>>);
+ }
+ // Copy the buffer so it's owned in the future.
+ char* buffer_copy = CopyBuffer(*packet);
+ termination_packets_->emplace_back(
+ new QuicEncryptedPacket(buffer_copy, encrypted_length, true));
+ // This assures we won't try to write *forced* packets when blocked.
+ // Return true to stop processing.
+ if (HandleWriteBlocked()) {
+ return true;
+ }
+ }
+
+ DCHECK_LE(encrypted_length, kMaxOutgoingPacketSize);
+ DCHECK_LE(encrypted_length, packet_generator_.GetCurrentMaxPacketLength());
+ QUIC_DVLOG(1) << ENDPOINT << "Sending packet " << packet_number << " : "
+ << (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA
+ ? "data bearing "
+ : " ack only ")
+ << ", encryption level: "
+ << QuicUtils::EncryptionLevelToString(packet->encryption_level)
+ << ", encrypted length:" << encrypted_length;
+ QUIC_DVLOG(2) << ENDPOINT << "packet(" << packet_number << "): " << std::endl
+ << QuicTextUtils::HexDump(QuicStringPiece(
+ packet->encrypted_buffer, encrypted_length));
+
+ // Measure the RTT from before the write begins to avoid underestimating the
+ // min_rtt_, especially in cases where the thread blocks or gets swapped out
+ // during the WritePacket below.
+ QuicTime packet_send_time = clock_->Now();
+ if (supports_release_time_ && per_packet_options_ != nullptr) {
+ QuicTime next_release_time = sent_packet_manager_.GetNextReleaseTime();
+ QuicTime::Delta release_time_delay = QuicTime::Delta::Zero();
+ QuicTime now = packet_send_time;
+ if (next_release_time > now) {
+ release_time_delay = next_release_time - now;
+ // Set packet_send_time to the future to make the RTT estimation accurate.
+ packet_send_time = next_release_time;
+ }
+ per_packet_options_->release_time_delay = release_time_delay;
+ }
+ WriteResult result = writer_->WritePacket(
+ packet->encrypted_buffer, encrypted_length, self_address().host(),
+ peer_address(), per_packet_options_);
+
+ QUIC_HISTOGRAM_ENUM(
+ "QuicConnection.WritePacketStatus", result.status,
+ WRITE_STATUS_NUM_VALUES,
+ "Status code returned by writer_->WritePacket() in QuicConnection.");
+
+ if (IsWriteBlockedStatus(result.status)) {
+ // Ensure the writer is still write blocked, otherwise QUIC may continue
+ // trying to write when it will not be able to.
+ DCHECK(writer_->IsWriteBlocked());
+ visitor_->OnWriteBlocked();
+ // If the socket buffers the data, then the packet should not
+ // be queued and sent again, which would result in an unnecessary
+ // duplicate packet being sent. The helper must call OnCanWrite
+ // when the write completes, and OnWriteError if an error occurs.
+ if (result.status != WRITE_STATUS_BLOCKED_DATA_BUFFERED) {
+ return false;
+ }
+ }
+
+ // In some cases, an MTU probe can cause EMSGSIZE. This indicates that the
+ // MTU discovery is permanently unsuccessful.
+ if (IsMsgTooBig(result) && packet->retransmittable_frames.empty() &&
+ packet->encrypted_length > long_term_mtu_) {
+ mtu_discovery_target_ = 0;
+ mtu_discovery_alarm_->Cancel();
+ // The write failed, but the writer is not blocked, so return true.
+ return true;
+ }
+
+ if (IsWriteError(result.status)) {
+ OnWriteError(result.error_code);
+ QUIC_LOG_FIRST_N(ERROR, 10)
+ << ENDPOINT << "failed writing " << encrypted_length
+ << " bytes from host " << self_address().host().ToString()
+ << " to address " << peer_address().ToString() << " with error code "
+ << result.error_code;
+ return false;
+ }
+
+ if (debug_visitor_ != nullptr) {
+ // Pass the write result to the visitor.
+ debug_visitor_->OnPacketSent(*packet, packet->original_packet_number,
+ packet->transmission_type, packet_send_time);
+ }
+ if (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA) {
+ if (!is_path_degrading_ && !path_degrading_alarm_->IsSet()) {
+ // This is the first retransmittable packet on the working path.
+ // Start the path degrading alarm to detect new path degrading.
+ SetPathDegradingAlarm();
+ }
+
+ if (GetQuicReloadableFlag(
+ quic_fix_time_of_first_packet_sent_after_receiving)) {
+ // Update |time_of_first_packet_sent_after_receiving_| if this is the
+ // first packet sent after the last packet was received. If it were
+ // updated on every sent packet, then sending into a black hole might
+ // never timeout.
+ if (time_of_first_packet_sent_after_receiving_ <
+ time_of_last_received_packet_) {
+ QUIC_RELOADABLE_FLAG_COUNT(
+ quic_fix_time_of_first_packet_sent_after_receiving);
+ time_of_first_packet_sent_after_receiving_ = packet_send_time;
+ }
+ } else {
+ // Only adjust the last sent time (for the purpose of tracking the idle
+ // timeout) if this is the first retransmittable packet sent after a
+ // packet is received. If it were updated on every sent packet, then
+ // sending into a black hole might never timeout.
+ if (time_of_first_packet_sent_after_receiving_ <=
+ time_of_last_received_packet_) {
+ time_of_first_packet_sent_after_receiving_ = packet_send_time;
+ }
+ }
+ }
+
+ MaybeSetMtuAlarm(packet_number);
+ QUIC_DVLOG(1) << ENDPOINT << "time we began writing last sent packet: "
+ << packet_send_time.ToDebuggingValue();
+
+ bool reset_retransmission_alarm = sent_packet_manager_.OnPacketSent(
+ packet, packet->original_packet_number, packet_send_time,
+ packet->transmission_type, IsRetransmittable(*packet));
+
+ if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) {
+ SetRetransmissionAlarm();
+ }
+ SetPingAlarm();
+
+ // The packet number length must be updated after OnPacketSent, because it
+ // may change the packet number length in packet.
+ packet_generator_.UpdatePacketNumberLength(
+ sent_packet_manager_.GetLeastUnacked(),
+ sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length()));
+
+ stats_.bytes_sent += result.bytes_written;
+ ++stats_.packets_sent;
+ if (packet->transmission_type != NOT_RETRANSMISSION) {
+ stats_.bytes_retransmitted += result.bytes_written;
+ ++stats_.packets_retransmitted;
+ }
+
+ return true;
+}
+
+void QuicConnection::FlushPackets() {
+ if (!connected_) {
+ return;
+ }
+
+ if (!writer_->IsBatchMode()) {
+ return;
+ }
+
+ if (HandleWriteBlocked()) {
+ QUIC_DLOG(INFO) << ENDPOINT << "FlushPackets called while blocked.";
+ return;
+ }
+
+ WriteResult result = writer_->Flush();
+
+ if (HandleWriteBlocked()) {
+ DCHECK_EQ(WRITE_STATUS_BLOCKED, result.status)
+ << "Unexpected flush result:" << result;
+ QUIC_DLOG(INFO) << ENDPOINT << "Write blocked in FlushPackets.";
+ return;
+ }
+
+ if (IsWriteError(result.status)) {
+ OnWriteError(result.error_code);
+ }
+}
+
+bool QuicConnection::IsMsgTooBig(const WriteResult& result) {
+ return (result.status == WRITE_STATUS_MSG_TOO_BIG) ||
+ (IsWriteError(result.status) && result.error_code == QUIC_EMSGSIZE);
+}
+
+bool QuicConnection::ShouldDiscardPacket(const SerializedPacket& packet) {
+ if (!connected_) {
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Not sending packet as connection is disconnected.";
+ return true;
+ }
+
+ QuicPacketNumber packet_number = packet.packet_number;
+ if (encryption_level_ == ENCRYPTION_FORWARD_SECURE &&
+ packet.encryption_level == ENCRYPTION_INITIAL) {
+ // Drop packets that are NULL encrypted since the peer won't accept them
+ // anymore.
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Dropping NULL encrypted packet: " << packet_number
+ << " since the connection is forward secure.";
+ return true;
+ }
+
+ return false;
+}
+
+void QuicConnection::OnWriteError(int error_code) {
+ if (write_error_occurred_) {
+ // A write error already occurred. The connection is being closed.
+ return;
+ }
+ write_error_occurred_ = true;
+
+ const std::string error_details = QuicStrCat(
+ "Write failed with error: ", error_code, " (", strerror(error_code), ")");
+ QUIC_LOG_FIRST_N(ERROR, 2) << ENDPOINT << error_details;
+ switch (error_code) {
+ case QUIC_EMSGSIZE:
+ CloseConnection(
+ QUIC_PACKET_WRITE_ERROR, error_details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK);
+ break;
+ default:
+ // We can't send an error as the socket is presumably borked.
+ if (transport_version() > QUIC_VERSION_43) {
+ QUIC_CODE_COUNT(quic_tear_down_local_connection_on_write_error_ietf);
+ } else {
+ QUIC_CODE_COUNT(
+ quic_tear_down_local_connection_on_write_error_non_ietf);
+ }
+ TearDownLocalConnectionState(QUIC_PACKET_WRITE_ERROR, error_details,
+ ConnectionCloseSource::FROM_SELF);
+ }
+}
+
+char* QuicConnection::GetPacketBuffer() {
+ return writer_->GetNextWriteLocation(self_address().host(), peer_address());
+}
+
+void QuicConnection::OnSerializedPacket(SerializedPacket* serialized_packet) {
+ if (serialized_packet->encrypted_buffer == nullptr) {
+ // We failed to serialize the packet, so close the connection.
+ // TearDownLocalConnectionState does not send close packet, so no infinite
+ // loop here.
+ // TODO(ianswett): This is actually an internal error, not an
+ // encryption failure.
+ if (transport_version() > QUIC_VERSION_43) {
+ QUIC_CODE_COUNT(
+ quic_tear_down_local_connection_on_serialized_packet_ietf);
+ } else {
+ QUIC_CODE_COUNT(
+ quic_tear_down_local_connection_on_serialized_packet_non_ietf);
+ }
+ TearDownLocalConnectionState(
+ QUIC_ENCRYPTION_FAILURE,
+ "Serialized packet does not have an encrypted buffer.",
+ ConnectionCloseSource::FROM_SELF);
+ return;
+ }
+
+ if (serialized_packet->retransmittable_frames.empty() &&
+ !serialized_packet->original_packet_number.IsInitialized()) {
+ // Increment consecutive_num_packets_with_no_retransmittable_frames_ if
+ // this packet is a new transmission with no retransmittable frames.
+ ++consecutive_num_packets_with_no_retransmittable_frames_;
+ } else {
+ consecutive_num_packets_with_no_retransmittable_frames_ = 0;
+ }
+ SendOrQueuePacket(serialized_packet);
+}
+
+void QuicConnection::OnUnrecoverableError(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {
+ // The packet creator or generator encountered an unrecoverable error: tear
+ // down local connection state immediately.
+ if (transport_version() > QUIC_VERSION_43) {
+ QUIC_CODE_COUNT(
+ quic_tear_down_local_connection_on_unrecoverable_error_ietf);
+ } else {
+ QUIC_CODE_COUNT(
+ quic_tear_down_local_connection_on_unrecoverable_error_non_ietf);
+ }
+ TearDownLocalConnectionState(error, error_details, source);
+}
+
+void QuicConnection::OnCongestionChange() {
+ visitor_->OnCongestionWindowChange(clock_->ApproximateNow());
+
+ // Uses the connection's smoothed RTT. If zero, uses initial_rtt.
+ QuicTime::Delta rtt = sent_packet_manager_.GetRttStats()->smoothed_rtt();
+ if (rtt.IsZero()) {
+ rtt = sent_packet_manager_.GetRttStats()->initial_rtt();
+ }
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnRttChanged(rtt);
+ }
+}
+
+void QuicConnection::OnPathMtuIncreased(QuicPacketLength packet_size) {
+ if (packet_size > max_packet_length()) {
+ SetMaxPacketLength(packet_size);
+ }
+}
+
+void QuicConnection::OnHandshakeComplete() {
+ sent_packet_manager_.SetHandshakeConfirmed();
+ if (sent_packet_manager_.unacked_packets().use_uber_loss_algorithm()) {
+ // This may have changed the retransmission timer, so re-arm it.
+ SetRetransmissionAlarm();
+ }
+ // The client should immediately ack the SHLO to confirm the handshake is
+ // complete with the server.
+ if (perspective_ == Perspective::IS_CLIENT && !ack_queued_ &&
+ ack_frame_updated()) {
+ ack_alarm_->Update(clock_->ApproximateNow(), QuicTime::Delta::Zero());
+ }
+}
+
+void QuicConnection::SendOrQueuePacket(SerializedPacket* packet) {
+ // The caller of this function is responsible for checking CanWrite().
+ if (packet->encrypted_buffer == nullptr) {
+ QUIC_BUG << "packet.encrypted_buffer == nullptr in to SendOrQueuePacket";
+ return;
+ }
+ // If there are already queued packets, queue this one immediately to ensure
+ // it's written in sequence number order.
+ if (!queued_packets_.empty() || !WritePacket(packet)) {
+ // Take ownership of the underlying encrypted packet.
+ packet->encrypted_buffer = CopyBuffer(*packet);
+ queued_packets_.push_back(*packet);
+ packet->retransmittable_frames.clear();
+ }
+
+ ClearSerializedPacket(packet);
+}
+
+void QuicConnection::OnPingTimeout() {
+ if (!retransmission_alarm_->IsSet()) {
+ bool enable_half_rtt_tail_loss_probe =
+ sent_packet_manager_.enable_half_rtt_tail_loss_probe();
+ if (enable_half_rtt_tail_loss_probe &&
+ GetQuicReloadableFlag(quic_ignore_tlpr_if_sending_ping)) {
+ sent_packet_manager_.set_enable_half_rtt_tail_loss_probe(false);
+ }
+ visitor_->SendPing();
+ if (enable_half_rtt_tail_loss_probe &&
+ GetQuicReloadableFlag(quic_ignore_tlpr_if_sending_ping)) {
+ sent_packet_manager_.set_enable_half_rtt_tail_loss_probe(true);
+ }
+ }
+}
+
+void QuicConnection::SendAck() {
+ DCHECK(!SupportsMultiplePacketNumberSpaces());
+ if (!received_packet_manager_.decide_when_to_send_acks()) {
+ // When received_packet_manager decides when to send ack, delaying
+ // ResetAckStates until ACK is successfully flushed.
+ ResetAckStates();
+ }
+
+ if (packet_generator_.deprecate_ack_bundling_mode()) {
+ QUIC_DVLOG(1) << ENDPOINT << "Sending an ACK proactively";
+ QuicFrames frames;
+ frames.push_back(GetUpdatedAckFrame());
+ if (!no_stop_waiting_frames_) {
+ QuicStopWaitingFrame stop_waiting;
+ PopulateStopWaitingFrame(&stop_waiting);
+ frames.push_back(QuicFrame(stop_waiting));
+ }
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (!packet_generator_.FlushAckFrame(frames)) {
+ return;
+ }
+ ResetAckStates();
+ } else {
+ send_ack_when_on_can_write_ = !packet_generator_.FlushAckFrame(frames);
+ }
+ } else {
+ packet_generator_.SetShouldSendAck(!no_stop_waiting_frames_);
+ }
+ if (consecutive_num_packets_with_no_retransmittable_frames_ <
+ max_consecutive_num_packets_with_no_retransmittable_frames_) {
+ return;
+ }
+ consecutive_num_packets_with_no_retransmittable_frames_ = 0;
+ if (packet_generator_.HasRetransmittableFrames() ||
+ visitor_->WillingAndAbleToWrite()) {
+ // There are pending retransmittable frames.
+ return;
+ }
+
+ visitor_->OnAckNeedsRetransmittableFrame();
+}
+
+void QuicConnection::OnPathDegradingTimeout() {
+ is_path_degrading_ = true;
+ visitor_->OnPathDegrading();
+}
+
+void QuicConnection::OnRetransmissionTimeout() {
+ DCHECK(!sent_packet_manager_.unacked_packets().empty());
+ if (close_connection_after_five_rtos_ &&
+ sent_packet_manager_.GetConsecutiveRtoCount() >= 4) {
+ // Close on the 5th consecutive RTO, so after 4 previous RTOs have occurred.
+ CloseConnection(QUIC_TOO_MANY_RTOS, "5 consecutive retransmission timeouts",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ sent_packet_manager_.OnRetransmissionTimeout();
+ WriteIfNotBlocked();
+
+ // A write failure can result in the connection being closed, don't attempt to
+ // write further packets, or to set alarms.
+ if (!connected_) {
+ return;
+ }
+
+ // In the TLP case, the SentPacketManager gives the connection the opportunity
+ // to send new data before retransmitting.
+ if (sent_packet_manager_.MaybeRetransmitTailLossProbe()) {
+ // Send the pending retransmission now that it's been queued.
+ WriteIfNotBlocked();
+ }
+
+ // Ensure the retransmission alarm is always set if there are unacked packets
+ // and nothing waiting to be sent.
+ // This happens if the loss algorithm invokes a timer based loss, but the
+ // packet doesn't need to be retransmitted.
+ if (!HasQueuedData() && !retransmission_alarm_->IsSet()) {
+ SetRetransmissionAlarm();
+ }
+}
+
+void QuicConnection::SetEncrypter(EncryptionLevel level,
+ std::unique_ptr<QuicEncrypter> encrypter) {
+ packet_generator_.SetEncrypter(level, std::move(encrypter));
+}
+
+void QuicConnection::SetDiversificationNonce(
+ const DiversificationNonce& nonce) {
+ DCHECK_EQ(Perspective::IS_SERVER, perspective_);
+ packet_generator_.SetDiversificationNonce(nonce);
+}
+
+void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) {
+ if (level != encryption_level_ && packet_generator_.HasQueuedFrames()) {
+ // Flush all queued frames when encryption level changes.
+ ScopedPacketFlusher flusher(this, NO_ACK);
+ packet_generator_.FlushAllQueuedFrames();
+ }
+ encryption_level_ = level;
+ packet_generator_.set_encryption_level(level);
+}
+
+void QuicConnection::SetDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter) {
+ framer_.SetDecrypter(level, std::move(decrypter));
+
+ if (!undecryptable_packets_.empty() &&
+ !process_undecryptable_packets_alarm_->IsSet()) {
+ process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow());
+ }
+}
+
+void QuicConnection::SetAlternativeDecrypter(
+ EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter,
+ bool latch_once_used) {
+ framer_.SetAlternativeDecrypter(level, std::move(decrypter), latch_once_used);
+
+ if (!undecryptable_packets_.empty() &&
+ !process_undecryptable_packets_alarm_->IsSet()) {
+ process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow());
+ }
+}
+
+void QuicConnection::InstallDecrypter(
+ EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter) {
+ framer_.InstallDecrypter(level, std::move(decrypter));
+ if (!undecryptable_packets_.empty() &&
+ !process_undecryptable_packets_alarm_->IsSet()) {
+ process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow());
+ }
+}
+
+void QuicConnection::RemoveDecrypter(EncryptionLevel level) {
+ framer_.RemoveDecrypter(level);
+}
+
+const QuicDecrypter* QuicConnection::decrypter() const {
+ return framer_.decrypter();
+}
+
+const QuicDecrypter* QuicConnection::alternative_decrypter() const {
+ return framer_.alternative_decrypter();
+}
+
+void QuicConnection::QueueUndecryptablePacket(
+ const QuicEncryptedPacket& packet) {
+ QUIC_DVLOG(1) << ENDPOINT << "Queueing undecryptable packet.";
+ undecryptable_packets_.push_back(packet.Clone());
+}
+
+void QuicConnection::MaybeProcessUndecryptablePackets() {
+ process_undecryptable_packets_alarm_->Cancel();
+
+ if (undecryptable_packets_.empty() ||
+ encryption_level_ == ENCRYPTION_INITIAL) {
+ return;
+ }
+
+ while (connected_ && !undecryptable_packets_.empty()) {
+ // Making sure there is no pending frames when processing next undecrypted
+ // packet because the queued ack frame may change.
+ packet_generator_.FlushAllQueuedFrames();
+ if (!connected_) {
+ return;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet";
+ QuicEncryptedPacket* packet = undecryptable_packets_.front().get();
+ if (!framer_.ProcessPacket(*packet) &&
+ framer_.error() == QUIC_DECRYPTION_FAILURE) {
+ QUIC_DVLOG(1) << ENDPOINT << "Unable to process undecryptable packet...";
+ break;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Processed undecryptable packet!";
+ ++stats_.packets_processed;
+ undecryptable_packets_.pop_front();
+ }
+
+ // Once forward secure encryption is in use, there will be no
+ // new keys installed and hence any undecryptable packets will
+ // never be able to be decrypted.
+ if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) {
+ if (debug_visitor_ != nullptr) {
+ // TODO(rtenneti): perhaps more efficient to pass the number of
+ // undecryptable packets as the argument to OnUndecryptablePacket so that
+ // we just need to call OnUndecryptablePacket once?
+ for (size_t i = 0; i < undecryptable_packets_.size(); ++i) {
+ debug_visitor_->OnUndecryptablePacket();
+ }
+ }
+ undecryptable_packets_.clear();
+ }
+}
+
+void QuicConnection::QueueCoalescedPacket(const QuicEncryptedPacket& packet) {
+ QUIC_DVLOG(1) << ENDPOINT << "Queueing coalesced packet.";
+ coalesced_packets_.push_back(packet.Clone());
+}
+
+void QuicConnection::MaybeProcessCoalescedPackets() {
+ bool processed = false;
+ for (const auto& packet : coalesced_packets_) {
+ if (!connected_) {
+ return;
+ }
+
+ // }
+ // while (connected_ && !coalesced_packets_.empty()) {
+ QUIC_DVLOG(1) << ENDPOINT << "Processing coalesced packet";
+ // QuicEncryptedPacket* packet = coalesced_packets_.front().get();
+ if (framer_.ProcessPacket(*packet)) {
+ processed = true;
+ } else {
+ // If we are unable to decrypt this packet, it might be
+ // because the CHLO or SHLO packet was lost.
+ if (framer_.error() == QUIC_DECRYPTION_FAILURE) {
+ if (encryption_level_ != ENCRYPTION_FORWARD_SECURE &&
+ undecryptable_packets_.size() < max_undecryptable_packets_) {
+ QueueUndecryptablePacket(*packet);
+ } else if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnUndecryptablePacket();
+ }
+ }
+ }
+ // coalesced_packets_.pop_front();
+ }
+ coalesced_packets_.clear();
+ if (processed) {
+ MaybeProcessUndecryptablePackets();
+ }
+}
+
+void QuicConnection::CloseConnection(
+ QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseBehavior connection_close_behavior) {
+ DCHECK(!error_details.empty());
+ if (!connected_) {
+ QUIC_DLOG(INFO) << "Connection is already closed.";
+ return;
+ }
+
+ QUIC_DLOG(INFO) << ENDPOINT << "Closing connection: " << connection_id()
+ << ", with error: " << QuicErrorCodeToString(error) << " ("
+ << error << "), and details: " << error_details;
+
+ if (connection_close_behavior ==
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET) {
+ SendConnectionClosePacket(error, error_details, SEND_ACK);
+ } else if (connection_close_behavior ==
+ ConnectionCloseBehavior::
+ SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK) {
+ SendConnectionClosePacket(error, error_details, NO_ACK);
+ }
+
+ ConnectionCloseSource source = ConnectionCloseSource::FROM_SELF;
+ if (perspective_ == Perspective::IS_CLIENT &&
+ error == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
+ // Regard stateless rejected connection as closed by server.
+ source = ConnectionCloseSource::FROM_PEER;
+ }
+ TearDownLocalConnectionState(error, error_details, source);
+}
+
+void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
+ const std::string& details,
+ AckBundling ack_mode) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet.";
+ if (fix_termination_packets_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_fix_termination_packets);
+ SetDefaultEncryptionLevel(GetConnectionCloseEncryptionLevel());
+ }
+ ClearQueuedPackets();
+ ScopedPacketFlusher flusher(this, ack_mode);
+ // When multiple packet number spaces is supported, an ACK frame will be
+ // bundled when connection is not write blocked.
+ if (!SupportsMultiplePacketNumberSpaces() &&
+ packet_generator_.deprecate_ack_bundling_mode() && ack_mode == SEND_ACK &&
+ !GetUpdatedAckFrame().ack_frame->packets.Empty()) {
+ SendAck();
+ }
+ QuicConnectionCloseFrame* frame =
+ new QuicConnectionCloseFrame(error, details);
+ // If version99/IETF QUIC set the close type. Default close type is Google
+ // QUIC.
+ if (transport_version() == QUIC_VERSION_99) {
+ frame->close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ }
+ packet_generator_.AddControlFrame(QuicFrame(frame));
+ packet_generator_.FlushAllQueuedFrames();
+}
+
+void QuicConnection::TearDownLocalConnectionState(
+ QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {
+ if (!connected_) {
+ QUIC_DLOG(INFO) << "Connection is already closed.";
+ return;
+ }
+
+ // If we are using a batch writer, flush packets queued in it, if any.
+ FlushPackets();
+ connected_ = false;
+ DCHECK(visitor_ != nullptr);
+ visitor_->OnConnectionClosed(error, error_details, source);
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnConnectionClosed(error, error_details, source);
+ }
+ // Cancel the alarms so they don't trigger any action now that the
+ // connection is closed.
+ CancelAllAlarms();
+}
+
+void QuicConnection::CancelAllAlarms() {
+ QUIC_DVLOG(1) << "Cancelling all QuicConnection alarms.";
+
+ ack_alarm_->Cancel();
+ ping_alarm_->Cancel();
+ retransmission_alarm_->Cancel();
+ send_alarm_->Cancel();
+ timeout_alarm_->Cancel();
+ mtu_discovery_alarm_->Cancel();
+ path_degrading_alarm_->Cancel();
+}
+
+QuicByteCount QuicConnection::max_packet_length() const {
+ return packet_generator_.GetCurrentMaxPacketLength();
+}
+
+void QuicConnection::SetMaxPacketLength(QuicByteCount length) {
+ long_term_mtu_ = length;
+ packet_generator_.SetMaxPacketLength(GetLimitedMaxPacketSize(length));
+}
+
+bool QuicConnection::HasQueuedData() const {
+ return pending_version_negotiation_packet_ || !queued_packets_.empty() ||
+ packet_generator_.HasQueuedFrames();
+}
+
+void QuicConnection::EnableSavingCryptoPackets() {
+ save_crypto_packets_as_termination_packets_ = true;
+}
+
+bool QuicConnection::CanWriteStreamData() {
+ // Don't write stream data if there are negotiation or queued data packets
+ // to send. Otherwise, continue and bundle as many frames as possible.
+ if (pending_version_negotiation_packet_ || !queued_packets_.empty()) {
+ return false;
+ }
+
+ IsHandshake pending_handshake =
+ visitor_->HasPendingHandshake() ? IS_HANDSHAKE : NOT_HANDSHAKE;
+ // Sending queued packets may have caused the socket to become write blocked,
+ // or the congestion manager to prohibit sending. If we've sent everything
+ // we had queued and we're still not blocked, let the visitor know it can
+ // write more.
+ return ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA, pending_handshake);
+}
+
+void QuicConnection::SetNetworkTimeouts(QuicTime::Delta handshake_timeout,
+ QuicTime::Delta idle_timeout) {
+ QUIC_BUG_IF(idle_timeout > handshake_timeout)
+ << "idle_timeout:" << idle_timeout.ToMilliseconds()
+ << " handshake_timeout:" << handshake_timeout.ToMilliseconds();
+ // Adjust the idle timeout on client and server to prevent clients from
+ // sending requests to servers which have already closed the connection.
+ if (perspective_ == Perspective::IS_SERVER) {
+ idle_timeout = idle_timeout + QuicTime::Delta::FromSeconds(3);
+ } else if (idle_timeout > QuicTime::Delta::FromSeconds(1)) {
+ idle_timeout = idle_timeout - QuicTime::Delta::FromSeconds(1);
+ }
+ handshake_timeout_ = handshake_timeout;
+ idle_network_timeout_ = idle_timeout;
+
+ SetTimeoutAlarm();
+}
+
+void QuicConnection::CheckForTimeout() {
+ QuicTime now = clock_->ApproximateNow();
+ QuicTime time_of_last_packet =
+ std::max(time_of_last_received_packet_,
+ time_of_first_packet_sent_after_receiving_);
+
+ // |delta| can be < 0 as |now| is approximate time but |time_of_last_packet|
+ // is accurate time. However, this should not change the behavior of
+ // timeout handling.
+ QuicTime::Delta idle_duration = now - time_of_last_packet;
+ QUIC_DVLOG(1) << ENDPOINT << "last packet "
+ << time_of_last_packet.ToDebuggingValue()
+ << " now:" << now.ToDebuggingValue()
+ << " idle_duration:" << idle_duration.ToMicroseconds()
+ << " idle_network_timeout: "
+ << idle_network_timeout_.ToMicroseconds();
+ if (idle_duration >= idle_network_timeout_) {
+ const std::string error_details = "No recent network activity.";
+ QUIC_DVLOG(1) << ENDPOINT << error_details;
+ if ((sent_packet_manager_.GetConsecutiveTlpCount() > 0 ||
+ sent_packet_manager_.GetConsecutiveRtoCount() > 0 ||
+ visitor_->ShouldKeepConnectionAlive())) {
+ CloseConnection(QUIC_NETWORK_IDLE_TIMEOUT, error_details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ } else {
+ CloseConnection(QUIC_NETWORK_IDLE_TIMEOUT, error_details,
+ idle_timeout_connection_close_behavior_);
+ }
+ return;
+ }
+
+ if (!handshake_timeout_.IsInfinite()) {
+ QuicTime::Delta connected_duration = now - stats_.connection_creation_time;
+ QUIC_DVLOG(1) << ENDPOINT
+ << "connection time: " << connected_duration.ToMicroseconds()
+ << " handshake timeout: "
+ << handshake_timeout_.ToMicroseconds();
+ if (connected_duration >= handshake_timeout_) {
+ const std::string error_details = "Handshake timeout expired.";
+ QUIC_DVLOG(1) << ENDPOINT << error_details;
+ CloseConnection(QUIC_HANDSHAKE_TIMEOUT, error_details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ }
+
+ SetTimeoutAlarm();
+}
+
+void QuicConnection::SetTimeoutAlarm() {
+ QuicTime time_of_last_packet =
+ std::max(time_of_last_received_packet_,
+ time_of_first_packet_sent_after_receiving_);
+
+ QuicTime deadline = time_of_last_packet + idle_network_timeout_;
+ if (!handshake_timeout_.IsInfinite()) {
+ deadline = std::min(deadline,
+ stats_.connection_creation_time + handshake_timeout_);
+ }
+
+ timeout_alarm_->Update(deadline, QuicTime::Delta::Zero());
+}
+
+void QuicConnection::SetPingAlarm() {
+ if (perspective_ == Perspective::IS_SERVER) {
+ // Only clients send pings.
+ return;
+ }
+ if (!visitor_->ShouldKeepConnectionAlive()) {
+ ping_alarm_->Cancel();
+ // Don't send a ping unless there are open streams.
+ return;
+ }
+ if (retransmittable_on_wire_timeout_.IsInfinite() ||
+ sent_packet_manager_.HasInFlightPackets()) {
+ // Extend the ping alarm.
+ ping_alarm_->Update(clock_->ApproximateNow() + ping_timeout_,
+ QuicTime::Delta::FromSeconds(1));
+ return;
+ }
+ DCHECK_LT(retransmittable_on_wire_timeout_, ping_timeout_);
+ // If it's already set to an earlier time, then don't update it.
+ if (ping_alarm_->IsSet() &&
+ ping_alarm_->deadline() <
+ clock_->ApproximateNow() + retransmittable_on_wire_timeout_) {
+ return;
+ }
+ // Use a shorter timeout if there are open streams, but nothing on the wire.
+ ping_alarm_->Update(
+ clock_->ApproximateNow() + retransmittable_on_wire_timeout_,
+ QuicTime::Delta::FromMilliseconds(1));
+}
+
+void QuicConnection::SetRetransmissionAlarm() {
+ if (packet_generator_.PacketFlusherAttached()) {
+ pending_retransmission_alarm_ = true;
+ return;
+ }
+ QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime();
+ retransmission_alarm_->Update(retransmission_time,
+ QuicTime::Delta::FromMilliseconds(1));
+}
+
+void QuicConnection::SetPathDegradingAlarm() {
+ if (perspective_ == Perspective::IS_SERVER) {
+ return;
+ }
+ const QuicTime::Delta delay = sent_packet_manager_.GetPathDegradingDelay();
+ path_degrading_alarm_->Update(clock_->ApproximateNow() + delay,
+ QuicTime::Delta::FromMilliseconds(1));
+}
+
+void QuicConnection::MaybeSetMtuAlarm(QuicPacketNumber sent_packet_number) {
+ // Do not set the alarm if the target size is less than the current size.
+ // This covers the case when |mtu_discovery_target_| is at its default value,
+ // zero.
+ if (mtu_discovery_target_ <= max_packet_length()) {
+ return;
+ }
+
+ if (mtu_probe_count_ >= kMtuDiscoveryAttempts) {
+ return;
+ }
+
+ if (mtu_discovery_alarm_->IsSet()) {
+ return;
+ }
+
+ if (sent_packet_number >= next_mtu_probe_at_) {
+ // Use an alarm to send the MTU probe to ensure that no ScopedPacketFlushers
+ // are active.
+ mtu_discovery_alarm_->Set(clock_->ApproximateNow());
+ }
+}
+
+void QuicConnection::MaybeSetAckAlarmTo(QuicTime time) {
+ DCHECK(packet_generator_.deprecate_ack_bundling_mode());
+ if (!ack_alarm_->IsSet() || ack_alarm_->deadline() > time) {
+ ack_alarm_->Update(time, QuicTime::Delta::Zero());
+ }
+}
+
+QuicConnection::ScopedPacketFlusher::ScopedPacketFlusher(
+ QuicConnection* connection,
+ AckBundling ack_mode)
+ : connection_(connection),
+ flush_and_set_pending_retransmission_alarm_on_delete_(false) {
+ if (connection_ == nullptr) {
+ return;
+ }
+
+ if (!connection_->packet_generator_.PacketFlusherAttached()) {
+ flush_and_set_pending_retransmission_alarm_on_delete_ = true;
+ connection->packet_generator_.AttachPacketFlusher();
+ }
+ if (connection_->packet_generator_.deprecate_ack_bundling_mode()) {
+ return;
+ }
+
+ // If caller wants us to include an ack, check the delayed-ack timer to see if
+ // there's ack info to be sent.
+ if (ShouldSendAck(ack_mode)) {
+ if (!connection_->GetUpdatedAckFrame().ack_frame->packets.Empty()) {
+ QUIC_DVLOG(1) << "Bundling ack with outgoing packet.";
+ connection_->SendAck();
+ }
+ }
+}
+
+bool QuicConnection::ScopedPacketFlusher::ShouldSendAck(
+ AckBundling ack_mode) const {
+ DCHECK(!connection_->packet_generator_.deprecate_ack_bundling_mode());
+ // If the ack alarm is set, make sure the ack has been updated.
+ DCHECK(!connection_->ack_alarm_->IsSet() || connection_->ack_frame_updated())
+ << "ack_mode:" << ack_mode;
+ switch (ack_mode) {
+ case SEND_ACK:
+ return true;
+ case SEND_ACK_IF_QUEUED:
+ return connection_->ack_queued();
+ case SEND_ACK_IF_PENDING:
+ return connection_->ack_alarm_->IsSet() ||
+ connection_->stop_waiting_count_ > 1;
+ case NO_ACK:
+ return false;
+ default:
+ QUIC_BUG << "Unsupported ack_mode.";
+ return true;
+ }
+}
+
+QuicConnection::ScopedPacketFlusher::~ScopedPacketFlusher() {
+ if (connection_ == nullptr) {
+ return;
+ }
+
+ if (flush_and_set_pending_retransmission_alarm_on_delete_) {
+ if (connection_->packet_generator_.deprecate_ack_bundling_mode()) {
+ if (connection_->received_packet_manager_.decide_when_to_send_acks()) {
+ const QuicTime ack_timeout =
+ connection_->use_uber_received_packet_manager_
+ ? connection_->uber_received_packet_manager_
+ .GetEarliestAckTimeout()
+ : connection_->received_packet_manager_.ack_timeout();
+ if (ack_timeout.IsInitialized()) {
+ if (ack_timeout <= connection_->clock_->ApproximateNow() &&
+ !connection_->CanWrite(NO_RETRANSMITTABLE_DATA)) {
+ // Cancel ACK alarm if connection is write blocked, and ACK will be
+ // sent when connection gets unblocked.
+ connection_->ack_alarm_->Cancel();
+ } else {
+ connection_->MaybeSetAckAlarmTo(ack_timeout);
+ }
+ }
+ }
+ if (connection_->ack_alarm_->IsSet() &&
+ connection_->ack_alarm_->deadline() <=
+ connection_->clock_->ApproximateNow()) {
+ // An ACK needs to be sent right now. This ACK did not get bundled
+ // because either there was no data to write or packets were marked as
+ // received after frames were queued in the generator.
+ if (connection_->send_alarm_->IsSet() &&
+ connection_->send_alarm_->deadline() <=
+ connection_->clock_->ApproximateNow()) {
+ // If send alarm will go off soon, let send alarm send the ACK.
+ connection_->ack_alarm_->Cancel();
+ if (!connection_->received_packet_manager_
+ .decide_when_to_send_acks()) {
+ connection_->send_ack_when_on_can_write_ = true;
+ }
+ } else if (connection_->SupportsMultiplePacketNumberSpaces()) {
+ connection_->SendAllPendingAcks();
+ } else {
+ connection_->SendAck();
+ }
+ }
+ }
+ connection_->packet_generator_.Flush();
+ connection_->FlushPackets();
+ if (connection_->session_decides_what_to_write()) {
+ // Reset transmission type.
+ connection_->SetTransmissionType(NOT_RETRANSMISSION);
+ }
+
+ // Once all transmissions are done, check if there is any outstanding data
+ // to send and notify the congestion controller if not.
+ //
+ // Note that this means that the application limited check will happen as
+ // soon as the last flusher gets destroyed, which is typically after a
+ // single stream write is finished. This means that if all the data from a
+ // single write goes through the connection, the application-limited signal
+ // will fire even if the caller does a write operation immediately after.
+ // There are two important approaches to remedy this situation:
+ // (1) Instantiate ScopedPacketFlusher before performing multiple subsequent
+ // writes, thus deferring this check until all writes are done.
+ // (2) Write data in chunks sufficiently large so that they cause the
+ // connection to be limited by the congestion control. Typically, this
+ // would mean writing chunks larger than the product of the current
+ // pacing rate and the pacer granularity. So, for instance, if the
+ // pacing rate of the connection is 1 Gbps, and the pacer granularity is
+ // 1 ms, the caller should send at least 125k bytes in order to not
+ // be marked as application-limited.
+ connection_->CheckIfApplicationLimited();
+
+ if (connection_->pending_retransmission_alarm_) {
+ connection_->SetRetransmissionAlarm();
+ connection_->pending_retransmission_alarm_ = false;
+ }
+ }
+ DCHECK_EQ(flush_and_set_pending_retransmission_alarm_on_delete_,
+ !connection_->packet_generator_.PacketFlusherAttached());
+}
+
+HasRetransmittableData QuicConnection::IsRetransmittable(
+ const SerializedPacket& packet) {
+ // Retransmitted packets retransmittable frames are owned by the unacked
+ // packet map, but are not present in the serialized packet.
+ if (packet.transmission_type != NOT_RETRANSMISSION ||
+ !packet.retransmittable_frames.empty()) {
+ return HAS_RETRANSMITTABLE_DATA;
+ } else {
+ return NO_RETRANSMITTABLE_DATA;
+ }
+}
+
+bool QuicConnection::IsTerminationPacket(const SerializedPacket& packet) {
+ if (packet.retransmittable_frames.empty()) {
+ return false;
+ }
+ for (const QuicFrame& frame : packet.retransmittable_frames) {
+ if (frame.type == CONNECTION_CLOSE_FRAME) {
+ return true;
+ }
+ if (save_crypto_packets_as_termination_packets_ &&
+ QuicUtils::IsHandshakeFrame(frame, transport_version())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void QuicConnection::SetMtuDiscoveryTarget(QuicByteCount target) {
+ mtu_discovery_target_ = GetLimitedMaxPacketSize(target);
+}
+
+QuicByteCount QuicConnection::GetLimitedMaxPacketSize(
+ QuicByteCount suggested_max_packet_size) {
+ if (!peer_address_.IsInitialized()) {
+ QUIC_BUG << "Attempted to use a connection without a valid peer address";
+ return suggested_max_packet_size;
+ }
+
+ const QuicByteCount writer_limit = writer_->GetMaxPacketSize(peer_address());
+
+ QuicByteCount max_packet_size = suggested_max_packet_size;
+ if (max_packet_size > writer_limit) {
+ max_packet_size = writer_limit;
+ }
+ if (max_packet_size > kMaxOutgoingPacketSize) {
+ max_packet_size = kMaxOutgoingPacketSize;
+ }
+ return max_packet_size;
+}
+
+void QuicConnection::SendMtuDiscoveryPacket(QuicByteCount target_mtu) {
+ // Currently, this limit is ensured by the caller.
+ DCHECK_EQ(target_mtu, GetLimitedMaxPacketSize(target_mtu));
+
+ // Send the probe.
+ packet_generator_.GenerateMtuDiscoveryPacket(target_mtu);
+}
+
+// TODO(zhongyi): change this method to generate a connectivity probing packet
+// and let the caller to call writer to write the packet and handle write
+// status.
+bool QuicConnection::SendConnectivityProbingPacket(
+ QuicPacketWriter* probing_writer,
+ const QuicSocketAddress& peer_address) {
+ return SendGenericPathProbePacket(probing_writer, peer_address,
+ /* is_response= */ false);
+}
+
+void QuicConnection::SendConnectivityProbingResponsePacket(
+ const QuicSocketAddress& peer_address) {
+ SendGenericPathProbePacket(nullptr, peer_address,
+ /* is_response= */ true);
+}
+
+bool QuicConnection::SendGenericPathProbePacket(
+ QuicPacketWriter* probing_writer,
+ const QuicSocketAddress& peer_address,
+ bool is_response) {
+ DCHECK(peer_address.IsInitialized());
+ if (!connected_) {
+ QUIC_BUG << "Not sending connectivity probing packet as connection is "
+ << "disconnected.";
+ return false;
+ }
+ if (perspective_ == Perspective::IS_SERVER && probing_writer == nullptr) {
+ // Server can use default packet writer to write packet.
+ probing_writer = writer_;
+ }
+ DCHECK(probing_writer);
+
+ if (probing_writer->IsWriteBlocked()) {
+ QUIC_DLOG(INFO)
+ << ENDPOINT
+ << "Writer blocked when sending connectivity probing packet.";
+ if (probing_writer == writer_) {
+ // Visitor should not be write blocked if the probing writer is not the
+ // default packet writer.
+ visitor_->OnWriteBlocked();
+ }
+ return true;
+ }
+
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Sending path probe packet for connection_id = "
+ << connection_id_;
+
+ OwningSerializedPacketPointer probing_packet;
+ if (transport_version() != QUIC_VERSION_99) {
+ // Non-IETF QUIC, generate a padded ping regardless of whether this is a
+ // request or a response.
+ probing_packet = packet_generator_.SerializeConnectivityProbingPacket();
+ } else {
+ if (is_response) {
+ // Respond using IETF QUIC PATH_RESPONSE frame
+ if (IsCurrentPacketConnectivityProbing()) {
+ // Pad the response if the request was a google connectivity probe
+ // (padded).
+ probing_packet =
+ packet_generator_.SerializePathResponseConnectivityProbingPacket(
+ received_path_challenge_payloads_, /* is_padded = */ true);
+ received_path_challenge_payloads_.clear();
+ } else {
+ // Do not pad the response if the path challenge was not a google
+ // connectivity probe.
+ probing_packet =
+ packet_generator_.SerializePathResponseConnectivityProbingPacket(
+ received_path_challenge_payloads_,
+ /* is_padded = */ false);
+ received_path_challenge_payloads_.clear();
+ }
+ } else {
+ // Request using IETF QUIC PATH_CHALLENGE frame
+ transmitted_connectivity_probe_payload_ =
+ QuicMakeUnique<QuicPathFrameBuffer>();
+ probing_packet =
+ packet_generator_.SerializePathChallengeConnectivityProbingPacket(
+ transmitted_connectivity_probe_payload_.get());
+ if (!probing_packet) {
+ transmitted_connectivity_probe_payload_ = nullptr;
+ }
+ }
+ }
+
+ DCHECK_EQ(IsRetransmittable(*probing_packet), NO_RETRANSMITTABLE_DATA);
+
+ const QuicTime packet_send_time = clock_->Now();
+ WriteResult result = probing_writer->WritePacket(
+ probing_packet->encrypted_buffer, probing_packet->encrypted_length,
+ self_address().host(), peer_address, per_packet_options_);
+
+ // If using a batch writer and the probing packet is buffered, flush it.
+ if (probing_writer->IsBatchMode() && result.status == WRITE_STATUS_OK &&
+ result.bytes_written == 0) {
+ result = probing_writer->Flush();
+ }
+
+ if (IsWriteError(result.status)) {
+ // Write error for any connectivity probe should not affect the connection
+ // as it is sent on a different path.
+ QUIC_DLOG(INFO) << ENDPOINT << "Write probing packet failed with error = "
+ << result.error_code;
+ return false;
+ }
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnPacketSent(
+ *probing_packet, probing_packet->original_packet_number,
+ probing_packet->transmission_type, packet_send_time);
+ }
+
+ // Call OnPacketSent regardless of the write result.
+ sent_packet_manager_.OnPacketSent(
+ probing_packet.get(), probing_packet->original_packet_number,
+ packet_send_time, probing_packet->transmission_type,
+ NO_RETRANSMITTABLE_DATA);
+
+ if (IsWriteBlockedStatus(result.status)) {
+ if (probing_writer == writer_) {
+ // Visitor should not be write blocked if the probing writer is not the
+ // default packet writer.
+ visitor_->OnWriteBlocked();
+ }
+ if (result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Write probing packet blocked";
+ }
+ }
+
+ return true;
+}
+
+void QuicConnection::DiscoverMtu() {
+ DCHECK(!mtu_discovery_alarm_->IsSet());
+
+ // Check if the MTU has been already increased.
+ if (mtu_discovery_target_ <= max_packet_length()) {
+ return;
+ }
+
+ // Calculate the packet number of the next probe *before* sending the current
+ // one. Otherwise, when SendMtuDiscoveryPacket() is called,
+ // MaybeSetMtuAlarm() will not realize that the probe has been just sent, and
+ // will reschedule this probe again.
+ packets_between_mtu_probes_ *= 2;
+ next_mtu_probe_at_ = sent_packet_manager_.GetLargestSentPacket() +
+ packets_between_mtu_probes_ + 1;
+ ++mtu_probe_count_;
+
+ QUIC_DVLOG(2) << "Sending a path MTU discovery packet #" << mtu_probe_count_;
+ SendMtuDiscoveryPacket(mtu_discovery_target_);
+
+ DCHECK(!mtu_discovery_alarm_->IsSet());
+}
+
+void QuicConnection::OnEffectivePeerMigrationValidated() {
+ if (active_effective_peer_migration_type_ == NO_CHANGE) {
+ QUIC_BUG << "No migration underway.";
+ return;
+ }
+ highest_packet_sent_before_effective_peer_migration_.Clear();
+ active_effective_peer_migration_type_ = NO_CHANGE;
+}
+
+void QuicConnection::StartEffectivePeerMigration(AddressChangeType type) {
+ // TODO(fayang): Currently, all peer address change type are allowed. Need to
+ // add a method ShouldAllowPeerAddressChange(PeerAddressChangeType type) to
+ // determine whether |type| is allowed.
+ if (type == NO_CHANGE) {
+ QUIC_BUG << "EffectivePeerMigration started without address change.";
+ return;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Effective peer's ip:port changed from "
+ << effective_peer_address_.ToString() << " to "
+ << GetEffectivePeerAddressFromCurrentPacket().ToString()
+ << ", address change type is " << type
+ << ", migrating connection.";
+
+ highest_packet_sent_before_effective_peer_migration_ =
+ sent_packet_manager_.GetLargestSentPacket();
+ effective_peer_address_ = GetEffectivePeerAddressFromCurrentPacket();
+ active_effective_peer_migration_type_ = type;
+
+ // TODO(wub): Move these calls to OnEffectivePeerMigrationValidated.
+ OnConnectionMigration(type);
+}
+
+void QuicConnection::OnConnectionMigration(AddressChangeType addr_change_type) {
+ visitor_->OnConnectionMigration(addr_change_type);
+ sent_packet_manager_.OnConnectionMigration(addr_change_type);
+}
+
+bool QuicConnection::IsCurrentPacketConnectivityProbing() const {
+ return is_current_packet_connectivity_probing_;
+}
+
+bool QuicConnection::ack_frame_updated() const {
+ if (use_uber_received_packet_manager_) {
+ return uber_received_packet_manager_.IsAckFrameUpdated();
+ }
+ return received_packet_manager_.ack_frame_updated();
+}
+
+QuicStringPiece QuicConnection::GetCurrentPacket() {
+ if (current_packet_data_ == nullptr) {
+ return QuicStringPiece();
+ }
+ return QuicStringPiece(current_packet_data_, last_size_);
+}
+
+bool QuicConnection::MaybeConsiderAsMemoryCorruption(
+ const QuicStreamFrame& frame) {
+ if (frame.stream_id == QuicUtils::GetCryptoStreamId(transport_version()) ||
+ last_decrypted_packet_level_ != ENCRYPTION_INITIAL) {
+ return false;
+ }
+
+ if (perspective_ == Perspective::IS_SERVER &&
+ frame.data_length >= sizeof(kCHLO) &&
+ strncmp(frame.data_buffer, reinterpret_cast<const char*>(&kCHLO),
+ sizeof(kCHLO)) == 0) {
+ return true;
+ }
+
+ if (perspective_ == Perspective::IS_CLIENT &&
+ frame.data_length >= sizeof(kREJ) &&
+ strncmp(frame.data_buffer, reinterpret_cast<const char*>(&kREJ),
+ sizeof(kREJ)) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+void QuicConnection::MaybeSendProbingRetransmissions() {
+ DCHECK(fill_up_link_during_probing_);
+
+ // Don't send probing retransmissions until the handshake has completed.
+ if (!sent_packet_manager_.handshake_confirmed() ||
+ sent_packet_manager().HasUnackedCryptoPackets()) {
+ return;
+ }
+
+ if (probing_retransmission_pending_) {
+ QUIC_BUG << "MaybeSendProbingRetransmissions is called while another call "
+ "to it is already in progress";
+ return;
+ }
+
+ probing_retransmission_pending_ = true;
+ SendProbingRetransmissions();
+ probing_retransmission_pending_ = false;
+}
+
+void QuicConnection::CheckIfApplicationLimited() {
+ if (session_decides_what_to_write() && probing_retransmission_pending_) {
+ return;
+ }
+
+ bool application_limited =
+ queued_packets_.empty() &&
+ !sent_packet_manager_.HasPendingRetransmissions() &&
+ !visitor_->WillingAndAbleToWrite();
+
+ if (!application_limited) {
+ return;
+ }
+
+ if (fill_up_link_during_probing_) {
+ MaybeSendProbingRetransmissions();
+ if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ return;
+ }
+ }
+
+ sent_packet_manager_.OnApplicationLimited();
+}
+
+void QuicConnection::UpdatePacketContent(PacketContent type) {
+ if (current_packet_content_ == NOT_PADDED_PING) {
+ // We have already learned the current packet is not a connectivity
+ // probing packet. Peer migration should have already been started earlier
+ // if needed.
+ return;
+ }
+
+ if (type == NO_FRAMES_RECEIVED) {
+ return;
+ }
+
+ if (type == FIRST_FRAME_IS_PING) {
+ if (current_packet_content_ == NO_FRAMES_RECEIVED) {
+ current_packet_content_ = FIRST_FRAME_IS_PING;
+ return;
+ }
+ }
+
+ // In Google QUIC we look for a packet with just a PING and PADDING.
+ // For IETF QUIC, the packet must consist of just a PATH_CHALLENGE frame,
+ // followed by PADDING. If the condition is met, mark things as
+ // connectivity-probing, causing later processing to generate the correct
+ // response.
+ if (type == SECOND_FRAME_IS_PADDING &&
+ current_packet_content_ == FIRST_FRAME_IS_PING) {
+ current_packet_content_ = SECOND_FRAME_IS_PADDING;
+ if (perspective_ == Perspective::IS_SERVER) {
+ is_current_packet_connectivity_probing_ =
+ current_effective_peer_migration_type_ != NO_CHANGE;
+ } else {
+ is_current_packet_connectivity_probing_ =
+ (last_packet_source_address_ != peer_address_) ||
+ (last_packet_destination_address_ != self_address_);
+ }
+ return;
+ }
+
+ current_packet_content_ = NOT_PADDED_PING;
+ if (GetLargestReceivedPacket().IsInitialized() &&
+ last_header_.packet_number == GetLargestReceivedPacket()) {
+ direct_peer_address_ = last_packet_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.
+ // TODO(fayang): When multiple packet number spaces is supported, only
+ // start peer migration for the application data.
+ StartEffectivePeerMigration(current_effective_peer_migration_type_);
+ }
+ }
+ current_effective_peer_migration_type_ = NO_CHANGE;
+}
+
+void QuicConnection::MaybeEnableSessionDecidesWhatToWrite() {
+ // Only enable session decides what to write code path for version 42+,
+ // because it needs the receiver to allow receiving overlapping stream data.
+ const bool enable_session_decides_what_to_write =
+ transport_version() > QUIC_VERSION_39;
+ sent_packet_manager_.SetSessionDecideWhatToWrite(
+ enable_session_decides_what_to_write);
+ packet_generator_.SetCanSetTransmissionType(
+ enable_session_decides_what_to_write);
+}
+
+void QuicConnection::PostProcessAfterAckFrame(bool send_stop_waiting,
+ bool acked_new_packet) {
+ if (no_stop_waiting_frames_) {
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.DontWaitForPacketsBefore(
+ last_decrypted_packet_level_,
+ sent_packet_manager_.largest_packet_peer_knows_is_acked());
+ } else {
+ received_packet_manager_.DontWaitForPacketsBefore(
+ sent_packet_manager_.largest_packet_peer_knows_is_acked());
+ }
+ }
+ // Always reset the retransmission alarm when an ack comes in, since we now
+ // have a better estimate of the current rtt than when it was set.
+ SetRetransmissionAlarm();
+ MaybeSetPathDegradingAlarm(acked_new_packet);
+
+ // TODO(ianswett): Only increment stop_waiting_count_ if StopWaiting frames
+ // are sent.
+ if (send_stop_waiting) {
+ ++stop_waiting_count_;
+ } else {
+ stop_waiting_count_ = 0;
+ }
+}
+
+void QuicConnection::MaybeSetPathDegradingAlarm(bool acked_new_packet) {
+ if (!sent_packet_manager_.HasInFlightPackets()) {
+ // There are no retransmittable packets on the wire, so it's impossible to
+ // say if the connection has degraded.
+ path_degrading_alarm_->Cancel();
+ } else if (acked_new_packet) {
+ // A previously-unacked packet has been acked, which means forward progress
+ // has been made. Unset |is_path_degrading| if the path was considered as
+ // degrading previously. Set/update the path degrading alarm.
+ is_path_degrading_ = false;
+ SetPathDegradingAlarm();
+ }
+}
+
+void QuicConnection::SetSessionNotifier(
+ SessionNotifierInterface* session_notifier) {
+ sent_packet_manager_.SetSessionNotifier(session_notifier);
+}
+
+void QuicConnection::SetDataProducer(
+ QuicStreamFrameDataProducer* data_producer) {
+ framer_.set_data_producer(data_producer);
+}
+
+void QuicConnection::SetTransmissionType(TransmissionType type) {
+ packet_generator_.SetTransmissionType(type);
+}
+
+bool QuicConnection::session_decides_what_to_write() const {
+ return sent_packet_manager_.session_decides_what_to_write();
+}
+
+void QuicConnection::UpdateReleaseTimeIntoFuture() {
+ DCHECK(supports_release_time_);
+
+ release_time_into_future_ = std::max(
+ QuicTime::Delta::FromMilliseconds(kMinReleaseTimeIntoFutureMs),
+ std::min(
+ QuicTime::Delta::FromMilliseconds(
+ GetQuicFlag(FLAGS_quic_max_pace_time_into_future_ms)),
+ sent_packet_manager_.GetRttStats()->SmoothedOrInitialRtt() *
+ GetQuicFlag(FLAGS_quic_pace_time_into_future_srtt_fraction)));
+}
+
+void QuicConnection::ResetAckStates() {
+ ack_alarm_->Cancel();
+ ack_queued_ = false;
+ stop_waiting_count_ = 0;
+ num_retransmittable_packets_received_since_last_ack_sent_ = 0;
+ num_packets_received_since_last_ack_sent_ = 0;
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.ResetAckStates(encryption_level_);
+ } else {
+ received_packet_manager_.ResetAckStates();
+ }
+ }
+}
+
+MessageStatus QuicConnection::SendMessage(QuicMessageId message_id,
+ QuicMemSliceSpan message) {
+ if (transport_version() <= QUIC_VERSION_44) {
+ QUIC_BUG << "MESSAGE frame is not supported for version "
+ << transport_version();
+ return MESSAGE_STATUS_UNSUPPORTED;
+ }
+ if (message.total_length() > GetCurrentLargestMessagePayload()) {
+ return MESSAGE_STATUS_TOO_LARGE;
+ }
+ if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ return MESSAGE_STATUS_BLOCKED;
+ }
+ ScopedPacketFlusher flusher(this, SEND_ACK_IF_PENDING);
+ return packet_generator_.AddMessageFrame(message_id, message);
+}
+
+QuicPacketLength QuicConnection::GetCurrentLargestMessagePayload() const {
+ return packet_generator_.GetCurrentLargestMessagePayload();
+}
+
+QuicPacketLength QuicConnection::GetGuaranteedLargestMessagePayload() const {
+ return packet_generator_.GetGuaranteedLargestMessagePayload();
+}
+
+uint32_t QuicConnection::cipher_id() const {
+ if (version().KnowsWhichDecrypterToUse()) {
+ return framer_.GetDecrypter(last_decrypted_packet_level_)->cipher_id();
+ }
+ return framer_.decrypter()->cipher_id();
+}
+
+bool QuicConnection::ShouldSetAckAlarm() const {
+ DCHECK(ack_frame_updated());
+ if (ack_alarm_->IsSet()) {
+ // ACK alarm has been set.
+ return false;
+ }
+ if (GetQuicReloadableFlag(quic_fix_spurious_ack_alarm) &&
+ packet_generator_.should_send_ack()) {
+ // If the generator is already configured to send an ACK, then there is no
+ // need to schedule the ACK alarm. The updated ACK information will be sent
+ // when the generator flushes.
+ QUIC_RELOADABLE_FLAG_COUNT(quic_fix_spurious_ack_alarm);
+ return false;
+ }
+ return true;
+}
+
+EncryptionLevel QuicConnection::GetConnectionCloseEncryptionLevel() const {
+ DCHECK(fix_termination_packets_);
+ if (perspective_ == Perspective::IS_CLIENT) {
+ return encryption_level_;
+ }
+ if (sent_packet_manager_.handshake_confirmed()) {
+ // A forward secure packet has been received.
+ QUIC_BUG_IF(encryption_level_ != ENCRYPTION_FORWARD_SECURE);
+ return ENCRYPTION_FORWARD_SECURE;
+ }
+ if (framer_.HasEncrypterOfEncryptionLevel(ENCRYPTION_ZERO_RTT)) {
+ if (encryption_level_ != ENCRYPTION_ZERO_RTT) {
+ if (transport_version() > QUIC_VERSION_43) {
+ QUIC_CODE_COUNT(quic_wrong_encryption_level_connection_close_ietf);
+ } else {
+ QUIC_CODE_COUNT(quic_wrong_encryption_level_connection_close);
+ }
+ }
+ return ENCRYPTION_ZERO_RTT;
+ }
+ return ENCRYPTION_INITIAL;
+}
+
+void QuicConnection::SendAllPendingAcks() {
+ DCHECK(SupportsMultiplePacketNumberSpaces());
+ QUIC_DVLOG(1) << ENDPOINT << "Trying to send all pending ACKs";
+ // 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));
+ if (!ack_timeout.IsInitialized() ||
+ ack_timeout > clock_->ApproximateNow()) {
+ continue;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Sending ACK of packet number space: "
+ << static_cast<uint32_t>(i);
+ // Switch to the appropriate encryption level.
+ SetDefaultEncryptionLevel(
+ QuicUtils::GetEncryptionLevel(static_cast<PacketNumberSpace>(i)));
+ QuicFrames frames;
+ frames.push_back(uber_received_packet_manager_.GetUpdatedAckFrame(
+ static_cast<PacketNumberSpace>(i), clock_->ApproximateNow()));
+ const bool flushed = packet_generator_.FlushAckFrame(frames);
+ if (!flushed) {
+ // Connection is write blocked.
+ QUIC_BUG_IF(!writer_->IsWriteBlocked())
+ << "Writer not blocked, but ACK not flushed for packet space:" << i;
+ break;
+ }
+ ResetAckStates();
+ }
+ // Restores encryption level.
+ SetDefaultEncryptionLevel(current_encryption_level);
+
+ const QuicTime timeout =
+ uber_received_packet_manager_.GetEarliestAckTimeout();
+ if (timeout.IsInitialized()) {
+ // If there are ACKs pending, re-arm ack alarm.
+ ack_alarm_->Set(timeout);
+ }
+ // Only try to bundle retransmittable data with ACK frame if default
+ // encryption level is forward secure.
+ if (encryption_level_ != ENCRYPTION_FORWARD_SECURE ||
+ consecutive_num_packets_with_no_retransmittable_frames_ <
+ max_consecutive_num_packets_with_no_retransmittable_frames_) {
+ return;
+ }
+ consecutive_num_packets_with_no_retransmittable_frames_ = 0;
+ if (packet_generator_.HasRetransmittableFrames() ||
+ visitor_->WillingAndAbleToWrite()) {
+ // There are pending retransmittable frames.
+ return;
+ }
+
+ visitor_->OnAckNeedsRetransmittableFrame();
+}
+
+void QuicConnection::MaybeEnableMultiplePacketNumberSpacesSupport() {
+ const bool enable_multiple_packet_number_spaces =
+ version().handshake_protocol == PROTOCOL_TLS1_3 &&
+ use_uber_received_packet_manager_ &&
+ sent_packet_manager_.use_uber_loss_algorithm() &&
+ GetQuicRestartFlag(quic_enable_accept_random_ipn);
+ if (!enable_multiple_packet_number_spaces) {
+ return;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "connection " << connection_id()
+ << " supports multiple packet number spaces";
+ framer_.EnableMultiplePacketNumberSpacesSupport();
+ sent_packet_manager_.EnableMultiplePacketNumberSpacesSupport();
+ uber_received_packet_manager_.EnableMultiplePacketNumberSpacesSupport();
+}
+
+bool QuicConnection::SupportsMultiplePacketNumberSpaces() const {
+ return sent_packet_manager_.supports_multiple_packet_number_spaces();
+}
+
+void QuicConnection::SetLargestReceivedPacketWithAck(
+ QuicPacketNumber new_value) {
+ if (SupportsMultiplePacketNumberSpaces()) {
+ largest_seen_packets_with_ack_[QuicUtils::GetPacketNumberSpace(
+ last_decrypted_packet_level_)] = new_value;
+ } else {
+ largest_seen_packet_with_ack_ = new_value;
+ }
+}
+
+QuicPacketNumber QuicConnection::GetLargestReceivedPacketWithAck() const {
+ if (SupportsMultiplePacketNumberSpaces()) {
+ return largest_seen_packets_with_ack_[QuicUtils::GetPacketNumberSpace(
+ last_decrypted_packet_level_)];
+ }
+ return largest_seen_packet_with_ack_;
+}
+
+QuicPacketNumber QuicConnection::GetLargestSentPacket() const {
+ if (SupportsMultiplePacketNumberSpaces()) {
+ return sent_packet_manager_.GetLargestSentPacket(
+ last_decrypted_packet_level_);
+ }
+ return sent_packet_manager_.GetLargestSentPacket();
+}
+
+QuicPacketNumber QuicConnection::GetLargestAckedPacket() const {
+ if (SupportsMultiplePacketNumberSpaces()) {
+ return sent_packet_manager_.GetLargestAckedPacket(
+ last_decrypted_packet_level_);
+ }
+ return sent_packet_manager_.GetLargestObserved();
+}
+
+QuicPacketNumber QuicConnection::GetLargestReceivedPacket() const {
+ if (use_uber_received_packet_manager_) {
+ return uber_received_packet_manager_.GetLargestObserved(
+ last_decrypted_packet_level_);
+ }
+ return received_packet_manager_.GetLargestObserved();
+}
+
+size_t QuicConnection::min_received_before_ack_decimation() const {
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (use_uber_received_packet_manager_) {
+ return uber_received_packet_manager_.min_received_before_ack_decimation();
+ }
+ return received_packet_manager_.min_received_before_ack_decimation();
+ }
+ return min_received_before_ack_decimation_;
+}
+
+void QuicConnection::set_min_received_before_ack_decimation(size_t new_value) {
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.set_min_received_before_ack_decimation(
+ new_value);
+ } else {
+ received_packet_manager_.set_min_received_before_ack_decimation(
+ new_value);
+ }
+ } else {
+ min_received_before_ack_decimation_ = new_value;
+ }
+}
+
+size_t QuicConnection::ack_frequency_before_ack_decimation() const {
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (use_uber_received_packet_manager_) {
+ return uber_received_packet_manager_
+ .ack_frequency_before_ack_decimation();
+ }
+ return received_packet_manager_.ack_frequency_before_ack_decimation();
+ }
+ return ack_frequency_before_ack_decimation_;
+}
+
+void QuicConnection::set_ack_frequency_before_ack_decimation(size_t new_value) {
+ DCHECK_GT(new_value, 0u);
+ if (received_packet_manager_.decide_when_to_send_acks()) {
+ if (use_uber_received_packet_manager_) {
+ uber_received_packet_manager_.set_ack_frequency_before_ack_decimation(
+ new_value);
+ } else {
+ received_packet_manager_.set_ack_frequency_before_ack_decimation(
+ new_value);
+ }
+ } else {
+ ack_frequency_before_ack_decimation_ = new_value;
+ }
+}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic
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
new file mode 100644
index 00000000000..c921d1b202a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection.h
@@ -0,0 +1,1525 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The entity that handles framing writes for a Quic client or server.
+// Each QuicSession will have a connection associated with it.
+//
+// On the server side, the Dispatcher handles the raw reads, and hands off
+// packets via ProcessUdpPacket for framing and processing.
+//
+// On the client side, the Connection handles the raw reads, as well as the
+// processing.
+//
+// Note: this class is not thread-safe.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CONNECTION_H_
+#define QUICHE_QUIC_CORE_QUIC_CONNECTION_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_generator.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/uber_received_packet_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicClock;
+class QuicConfig;
+class QuicConnection;
+class QuicRandom;
+
+namespace test {
+class QuicConnectionPeer;
+} // namespace test
+
+// The initial number of packets between MTU probes. After each attempt the
+// number is doubled.
+const QuicPacketCount kPacketsBetweenMtuProbesBase = 100;
+
+// The number of MTU probes that get sent before giving up.
+const size_t kMtuDiscoveryAttempts = 3;
+
+// Ensure that exponential back-off does not result in an integer overflow.
+// The number of packets can be potentially capped, but that is not useful at
+// current kMtuDiscoveryAttempts value, and hence is not implemented at present.
+static_assert(kMtuDiscoveryAttempts + 8 < 8 * sizeof(QuicPacketNumber),
+ "The number of MTU discovery attempts is too high");
+static_assert(kPacketsBetweenMtuProbesBase < (1 << 8),
+ "The initial number of packets between MTU probes is too high");
+
+// The incresed packet size targeted when doing path MTU discovery.
+const QuicByteCount kMtuDiscoveryTargetPacketSizeHigh = 1450;
+const QuicByteCount kMtuDiscoveryTargetPacketSizeLow = 1430;
+
+static_assert(kMtuDiscoveryTargetPacketSizeLow <= kMaxOutgoingPacketSize,
+ "MTU discovery target is too large");
+static_assert(kMtuDiscoveryTargetPacketSizeHigh <= kMaxOutgoingPacketSize,
+ "MTU discovery target is too large");
+
+static_assert(kMtuDiscoveryTargetPacketSizeLow > kDefaultMaxPacketSize,
+ "MTU discovery target does not exceed the default packet size");
+static_assert(kMtuDiscoveryTargetPacketSizeHigh > kDefaultMaxPacketSize,
+ "MTU discovery target does not exceed the default packet size");
+
+// Class that receives callbacks from the connection when frames are received
+// and when other interesting events happen.
+class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface {
+ public:
+ virtual ~QuicConnectionVisitorInterface() {}
+
+ // A simple visitor interface for dealing with a data frame.
+ virtual void OnStreamFrame(const QuicStreamFrame& frame) = 0;
+
+ // Called when a CRYPTO frame containing handshake data is received.
+ virtual void OnCryptoFrame(const QuicCryptoFrame& frame) = 0;
+
+ // The session should process the WINDOW_UPDATE frame, adjusting both stream
+ // and connection level flow control windows.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0;
+
+ // A BLOCKED frame indicates the peer is flow control blocked
+ // on a specified stream.
+ virtual void OnBlockedFrame(const QuicBlockedFrame& frame) = 0;
+
+ // Called when the stream is reset by the peer.
+ virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0;
+
+ // Called when the connection is going away according to the peer.
+ virtual void OnGoAway(const QuicGoAwayFrame& frame) = 0;
+
+ // Called when |message| has been received.
+ virtual void OnMessageReceived(QuicStringPiece message) = 0;
+
+ // Called when a MAX_STREAM_ID frame has been received from the peer.
+ virtual bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) = 0;
+
+ // Called when a STREAM_ID_BLOCKED frame has been received from the peer.
+ virtual bool OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) = 0;
+
+ // Called when the connection is closed either locally by the framer, or
+ // remotely by the peer.
+ virtual void OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) = 0;
+
+ // Called when the connection failed to write because the socket was blocked.
+ virtual void OnWriteBlocked() = 0;
+
+ // Called once a specific QUIC version is agreed by both endpoints.
+ virtual void OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) = 0;
+
+ // Called when a connectivity probe has been received by the connection.
+ virtual void OnConnectivityProbeReceived(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address) = 0;
+
+ // Called when a blocked socket becomes writable.
+ virtual void OnCanWrite() = 0;
+
+ // Called when the connection experiences a change in congestion window.
+ virtual void OnCongestionWindowChange(QuicTime now) = 0;
+
+ // Called when the connection receives a packet from a migrated client.
+ virtual void OnConnectionMigration(AddressChangeType type) = 0;
+
+ // Called when the peer seems unreachable over the current path.
+ virtual void OnPathDegrading() = 0;
+
+ // Called when the connection sends ack after
+ // max_consecutive_num_packets_with_no_retransmittable_frames_ consecutive not
+ // retransmittable packets sent. To instigate an ack from peer, a
+ // retransmittable frame needs to be added.
+ virtual void OnAckNeedsRetransmittableFrame() = 0;
+
+ // Called when a ping needs to be sent.
+ virtual void SendPing() = 0;
+
+ // Called to ask if the visitor wants to schedule write resumption as it both
+ // has pending data to write, and is able to write (e.g. based on flow control
+ // limits).
+ // Writes may be pending because they were write-blocked, congestion-throttled
+ // or yielded to other connections.
+ virtual bool WillingAndAbleToWrite() const = 0;
+
+ // Called to ask if any handshake messages are pending in this visitor.
+ virtual bool HasPendingHandshake() const = 0;
+
+ // Called to ask if the connection should be kept alive and prevented
+ // from timing out, for example if there are outstanding application
+ // transactions expecting a response.
+ virtual bool ShouldKeepConnectionAlive() const = 0;
+
+ // Called when a self address change is observed. Returns true if self address
+ // change is allowed.
+ virtual bool AllowSelfAddressChange() const = 0;
+
+ // Called when an ACK is received with a larger |largest_acked| than
+ // previously observed.
+ virtual void OnForwardProgressConfirmed() = 0;
+
+ // Called when a STOP_SENDING frame has been received.
+ virtual bool OnStopSendingFrame(const QuicStopSendingFrame& frame) = 0;
+};
+
+// Interface which gets callbacks from the QuicConnection at interesting
+// points. Implementations must not mutate the state of the connection
+// as a result of these callbacks.
+class QUIC_EXPORT_PRIVATE QuicConnectionDebugVisitor
+ : public QuicSentPacketManager::DebugDelegate {
+ public:
+ ~QuicConnectionDebugVisitor() override {}
+
+ // Called when a packet has been sent.
+ virtual void OnPacketSent(const SerializedPacket& serialized_packet,
+ QuicPacketNumber original_packet_number,
+ TransmissionType transmission_type,
+ QuicTime sent_time) {}
+
+ // Called when a PING frame has been sent.
+ virtual void OnPingSent() {}
+
+ // Called when a packet has been received, but before it is
+ // validated or parsed.
+ virtual void OnPacketReceived(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicEncryptedPacket& packet) {}
+
+ // Called when the unauthenticated portion of the header has been parsed.
+ virtual void OnUnauthenticatedHeader(const QuicPacketHeader& header) {}
+
+ // Called when a packet is received with a connection id that does not
+ // match the ID of this connection.
+ virtual void OnIncorrectConnectionId(QuicConnectionId connection_id) {}
+
+ // Called when an undecryptable packet has been received.
+ virtual void OnUndecryptablePacket() {}
+
+ // Called when a duplicate packet has been received.
+ virtual void OnDuplicatePacket(QuicPacketNumber packet_number) {}
+
+ // Called when the protocol version on the received packet doensn't match
+ // current protocol version of the connection.
+ virtual void OnProtocolVersionMismatch(ParsedQuicVersion version) {}
+
+ // Called when the complete header of a packet has been parsed.
+ virtual void OnPacketHeader(const QuicPacketHeader& header) {}
+
+ // Called when a StreamFrame has been parsed.
+ virtual void OnStreamFrame(const QuicStreamFrame& frame) {}
+
+ // Called when a StopWaitingFrame has been parsed.
+ virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {}
+
+ // Called when a QuicPaddingFrame has been parsed.
+ virtual void OnPaddingFrame(const QuicPaddingFrame& frame) {}
+
+ // Called when a Ping has been parsed.
+ virtual void OnPingFrame(const QuicPingFrame& frame) {}
+
+ // Called when a GoAway has been parsed.
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) {}
+
+ // Called when a RstStreamFrame has been parsed.
+ virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) {}
+
+ // Called when a ConnectionCloseFrame has been parsed. All forms
+ // of CONNECTION CLOSE are handled, Google QUIC, IETF QUIC
+ // CONNECTION CLOSE/Transport and IETF QUIC CONNECTION CLOSE/Application
+ virtual void OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) {}
+
+ // Called when a WindowUpdate has been parsed.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ const QuicTime& receive_time) {}
+
+ // Called when a BlockedFrame has been parsed.
+ virtual void OnBlockedFrame(const QuicBlockedFrame& frame) {}
+
+ // Called when a MessageFrame has been parsed.
+ virtual void OnMessageFrame(const QuicMessageFrame& frame) {}
+
+ // Called when a public reset packet has been received.
+ virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {}
+
+ // Called when a version negotiation packet has been received.
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) {}
+
+ // Called when the connection is closed.
+ virtual void OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {}
+
+ // Called when the version negotiation is successful.
+ virtual void OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) {}
+
+ // Called when a CachedNetworkParameters is sent to the client.
+ virtual void OnSendConnectionState(
+ const CachedNetworkParameters& cached_network_params) {}
+
+ // Called when a CachedNetworkParameters are received from the client.
+ virtual void OnReceiveConnectionState(
+ const CachedNetworkParameters& cached_network_params) {}
+
+ // Called when the connection parameters are set from the supplied
+ // |config|.
+ virtual void OnSetFromConfig(const QuicConfig& config) {}
+
+ // Called when RTT may have changed, including when an RTT is read from
+ // the config.
+ virtual void OnRttChanged(QuicTime::Delta rtt) const {}
+
+ // Called when a StopSendingFrame has been parsed.
+ virtual void OnStopSendingFrame(const QuicStopSendingFrame& frame) {}
+};
+
+class QUIC_EXPORT_PRIVATE QuicConnectionHelperInterface {
+ public:
+ virtual ~QuicConnectionHelperInterface() {}
+
+ // Returns a QuicClock to be used for all time related functions.
+ virtual const QuicClock* GetClock() const = 0;
+
+ // Returns a QuicRandom to be used for all random number related functions.
+ virtual QuicRandom* GetRandomGenerator() = 0;
+
+ // Returns a QuicBufferAllocator to be used for stream send buffers.
+ virtual QuicBufferAllocator* GetStreamSendBufferAllocator() = 0;
+};
+
+class QUIC_EXPORT_PRIVATE QuicConnection
+ : public QuicFramerVisitorInterface,
+ public QuicBlockedWriterInterface,
+ public QuicPacketGenerator::DelegateInterface,
+ public QuicSentPacketManager::NetworkChangeVisitor {
+ public:
+ // TODO(fayang): Remove this enum when deprecating
+ // quic_deprecate_ack_bundling_mode.
+ enum AckBundling {
+ // Send an ack if it's already queued in the connection.
+ SEND_ACK_IF_QUEUED,
+ // Always send an ack.
+ SEND_ACK,
+ // Bundle an ack with outgoing data.
+ SEND_ACK_IF_PENDING,
+ // Do not send ack.
+ NO_ACK,
+ };
+
+ // Constructs a new QuicConnection for |connection_id| and
+ // |initial_peer_address| using |writer| to write packets. |owns_writer|
+ // specifies whether the connection takes ownership of |writer|. |helper| must
+ // outlive this connection.
+ QuicConnection(QuicConnectionId connection_id,
+ QuicSocketAddress initial_peer_address,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ QuicPacketWriter* writer,
+ bool owns_writer,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions);
+ QuicConnection(const QuicConnection&) = delete;
+ QuicConnection& operator=(const QuicConnection&) = delete;
+ ~QuicConnection() override;
+
+ // Sets connection parameters from the supplied |config|.
+ void SetFromConfig(const QuicConfig& config);
+
+ // Called by the session when sending connection state to the client.
+ virtual void OnSendConnectionState(
+ const CachedNetworkParameters& cached_network_params);
+
+ // Called by the session when receiving connection state from the client.
+ virtual void OnReceiveConnectionState(
+ const CachedNetworkParameters& cached_network_params);
+
+ // Called by the Session when the client has provided CachedNetworkParameters.
+ virtual void ResumeConnectionState(
+ const CachedNetworkParameters& cached_network_params,
+ bool max_bandwidth_resumption);
+
+ // Called by the Session when a max pacing rate for the connection is needed.
+ virtual void SetMaxPacingRate(QuicBandwidth max_pacing_rate);
+
+ // Allows the client to adjust network parameters based on external
+ // information.
+ void AdjustNetworkParameters(QuicBandwidth bandwidth, QuicTime::Delta rtt);
+
+ // Returns the max pacing rate for the connection.
+ virtual QuicBandwidth MaxPacingRate() const;
+
+ // Sends crypto handshake messages of length |write_length| to the peer in as
+ // few packets as possible. Returns the number of bytes consumed from the
+ // data.
+ virtual size_t SendCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset);
+
+ // Send the data of length |write_length| to the peer in as few packets as
+ // possible. Returns the number of bytes consumed from data, and a boolean
+ // indicating if the fin bit was consumed. This does not indicate the data
+ // has been sent on the wire: it may have been turned into a packet and queued
+ // if the socket was unexpectedly blocked.
+ virtual QuicConsumedData SendStreamData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state);
+
+ // Send |frame| to the peer. Returns true if frame is consumed, false
+ // otherwise.
+ virtual bool SendControlFrame(const QuicFrame& frame);
+
+ // Called when stream |id| is reset because of |error|.
+ virtual void OnStreamReset(QuicStreamId id, QuicRstStreamErrorCode error);
+
+ // Closes the connection.
+ // |connection_close_behavior| determines whether or not a connection close
+ // packet is sent to the peer.
+ virtual void CloseConnection(
+ QuicErrorCode error,
+ const std::string& details,
+ ConnectionCloseBehavior connection_close_behavior);
+
+ // Returns statistics tracked for this connection.
+ const QuicConnectionStats& GetStats();
+
+ // Processes an incoming UDP packet (consisting of a QuicEncryptedPacket) from
+ // the peer.
+ // In a client, the packet may be "stray" and have a different connection ID
+ // than that of this connection.
+ virtual void ProcessUdpPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet);
+
+ // QuicBlockedWriterInterface
+ // Called when the underlying connection becomes writable to allow queued
+ // writes to happen.
+ void OnBlockedWriterCanWrite() override;
+
+ bool IsWriterBlocked() const override {
+ return writer_ != nullptr && writer_->IsWriteBlocked();
+ }
+
+ // Called when the caller thinks it's worth a try to write.
+ virtual void OnCanWrite();
+
+ // Called when an error occurs while attempting to write a packet to the
+ // network.
+ void OnWriteError(int error_code);
+
+ // Whether |result| represents a MSG TOO BIG write error.
+ bool IsMsgTooBig(const WriteResult& result);
+
+ // If the socket is not blocked, writes queued packets.
+ void WriteIfNotBlocked();
+
+ // If the socket is not blocked, writes queued packets and bundles any pending
+ // ACKs.
+ void WriteAndBundleAcksIfNotBlocked();
+
+ // Set the packet writer.
+ void SetQuicPacketWriter(QuicPacketWriter* writer, bool owns_writer) {
+ DCHECK(writer != nullptr);
+ if (writer_ != nullptr && owns_writer_) {
+ delete writer_;
+ }
+ writer_ = writer;
+ owns_writer_ = owns_writer;
+ }
+
+ // Set self address.
+ void SetSelfAddress(QuicSocketAddress address) { self_address_ = address; }
+
+ // The version of the protocol this connection is using.
+ QuicTransportVersion transport_version() const {
+ return framer_.transport_version();
+ }
+
+ ParsedQuicVersion version() const { return framer_.version(); }
+
+ // The versions of the protocol that this connection supports.
+ const ParsedQuicVersionVector& supported_versions() const {
+ return framer_.supported_versions();
+ }
+
+ // From QuicFramerVisitorInterface
+ void OnError(QuicFramer* framer) override;
+ bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
+ PacketHeaderFormat form) override;
+ void OnPacket() override;
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override;
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override;
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
+ void OnDecryptedPacket(EncryptionLevel level) override;
+ bool OnPacketHeader(const QuicPacketHeader& header) override;
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
+ bool OnStreamFrame(const QuicStreamFrame& frame) override;
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override;
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override;
+ bool OnAckFrameEnd(QuicPacketNumber start) override;
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
+ bool OnPingFrame(const QuicPingFrame& frame) override;
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override;
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override;
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override;
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override;
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override;
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override;
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override;
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override;
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override;
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
+ bool OnMessageFrame(const QuicMessageFrame& frame) override;
+ void OnPacketComplete() override;
+ bool IsValidStatelessResetToken(QuicUint128 token) const override;
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override;
+
+ // QuicConnectionCloseDelegateInterface
+ void OnUnrecoverableError(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) override;
+
+ // QuicPacketGenerator::DelegateInterface
+ bool ShouldGeneratePacket(HasRetransmittableData retransmittable,
+ IsHandshake handshake) override;
+ const QuicFrames MaybeBundleAckOpportunistically() override;
+ const QuicFrame GetUpdatedAckFrame() override;
+ void PopulateStopWaitingFrame(QuicStopWaitingFrame* stop_waiting) override;
+
+ // QuicPacketCreator::DelegateInterface
+ char* GetPacketBuffer() override;
+ void OnSerializedPacket(SerializedPacket* packet) override;
+
+ // QuicSentPacketManager::NetworkChangeVisitor
+ void OnCongestionChange() override;
+ void OnPathMtuIncreased(QuicPacketLength packet_size) override;
+
+ // Called by the crypto stream when the handshake completes. In the server's
+ // case this is when the SHLO has been ACKed. Clients call this on receipt of
+ // the SHLO.
+ void OnHandshakeComplete();
+
+ // Accessors
+ void set_visitor(QuicConnectionVisitorInterface* visitor) {
+ visitor_ = visitor;
+ }
+ void set_debug_visitor(QuicConnectionDebugVisitor* debug_visitor) {
+ debug_visitor_ = debug_visitor;
+ sent_packet_manager_.SetDebugDelegate(debug_visitor);
+ }
+ // Used in Chromium, but not internally.
+ // Must only be called before ping_alarm_ is set.
+ void set_ping_timeout(QuicTime::Delta ping_timeout) {
+ DCHECK(!ping_alarm_->IsSet());
+ ping_timeout_ = ping_timeout;
+ }
+ const QuicTime::Delta ping_timeout() { return ping_timeout_; }
+ // Used in Chromium, but not internally.
+ // Sets a timeout for the ping alarm when there is no retransmittable data
+ // in flight, allowing for a more aggressive ping alarm in that case.
+ void set_retransmittable_on_wire_timeout(
+ QuicTime::Delta retransmittable_on_wire_timeout) {
+ DCHECK(!ping_alarm_->IsSet());
+ retransmittable_on_wire_timeout_ = retransmittable_on_wire_timeout;
+ }
+ const QuicTime::Delta retransmittable_on_wire_timeout() {
+ return retransmittable_on_wire_timeout_;
+ }
+ // Used in Chromium, but not internally.
+ void set_creator_debug_delegate(QuicPacketCreator::DebugDelegate* visitor) {
+ packet_generator_.set_debug_delegate(visitor);
+ }
+ const QuicSocketAddress& self_address() const { return self_address_; }
+ const QuicSocketAddress& peer_address() const { return direct_peer_address_; }
+ const QuicSocketAddress& effective_peer_address() const {
+ return effective_peer_address_;
+ }
+ QuicConnectionId connection_id() const { return connection_id_; }
+ const QuicClock* clock() const { return clock_; }
+ QuicRandom* random_generator() const { return random_generator_; }
+ QuicByteCount max_packet_length() const;
+ void SetMaxPacketLength(QuicByteCount length);
+
+ size_t mtu_probe_count() const { return mtu_probe_count_; }
+
+ bool connected() const { return connected_; }
+
+ // Must only be called on client connections.
+ const ParsedQuicVersionVector& server_supported_versions() const {
+ DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
+ return server_supported_versions_;
+ }
+
+ // Testing only.
+ size_t NumQueuedPackets() const { return queued_packets_.size(); }
+
+ // Once called, any sent crypto packets to be saved as the
+ // termination packet, for use with stateless rejections.
+ void EnableSavingCryptoPackets();
+
+ // Returns true if the underlying UDP socket is writable, there is
+ // no queued data and the connection is not congestion-control
+ // blocked.
+ bool CanWriteStreamData();
+
+ // Returns true if the connection has queued packets or frames.
+ bool HasQueuedData() const;
+
+ // Sets the handshake and idle state connection timeouts.
+ void SetNetworkTimeouts(QuicTime::Delta handshake_timeout,
+ QuicTime::Delta idle_timeout);
+
+ // If the connection has timed out, this will close the connection.
+ // Otherwise, it will reschedule the timeout alarm.
+ void CheckForTimeout();
+
+ // Called when the ping alarm fires. Causes a ping frame to be sent only
+ // if the retransmission alarm is not running.
+ void OnPingTimeout();
+
+ // Sets up a packet with an QuicAckFrame and sends it out.
+ void SendAck();
+
+ // Called when the path degrading alarm fires.
+ void OnPathDegradingTimeout();
+
+ // Called when an RTO fires. Resets the retransmission alarm if there are
+ // remaining unacked packets.
+ void OnRetransmissionTimeout();
+
+ // Retransmits all unacked packets with retransmittable frames if
+ // |retransmission_type| is ALL_UNACKED_PACKETS, otherwise retransmits only
+ // initially encrypted packets. Used when the negotiated protocol version is
+ // different from what was initially assumed and when the initial encryption
+ // changes.
+ void RetransmitUnackedPackets(TransmissionType retransmission_type);
+
+ // Calls |sent_packet_manager_|'s NeuterUnencryptedPackets. Used when the
+ // connection becomes forward secure and hasn't received acks for all packets.
+ void NeuterUnencryptedPackets();
+
+ // Changes the encrypter used for level |level| to |encrypter|.
+ void SetEncrypter(EncryptionLevel level,
+ std::unique_ptr<QuicEncrypter> encrypter);
+
+ // SetNonceForPublicHeader sets the nonce that will be transmitted in the
+ // header of each packet encrypted at the initial encryption level decrypted.
+ // This should only be called on the server side.
+ void SetDiversificationNonce(const DiversificationNonce& nonce);
+
+ // SetDefaultEncryptionLevel sets the encryption level that will be applied
+ // to new packets.
+ void SetDefaultEncryptionLevel(EncryptionLevel level);
+
+ // SetDecrypter sets the primary decrypter, replacing any that already exists.
+ // If an alternative decrypter is in place then the function DCHECKs. This is
+ // intended for cases where one knows that future packets will be using the
+ // new decrypter and the previous decrypter is now obsolete. |level| indicates
+ // the encryption level of the new decrypter.
+ void SetDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter);
+
+ // SetAlternativeDecrypter sets a decrypter that may be used to decrypt
+ // future packets. |level| indicates the encryption level of the decrypter. If
+ // |latch_once_used| is true, then the first time that the decrypter is
+ // successful it will replace the primary decrypter. Otherwise both
+ // decrypters will remain active and the primary decrypter will be the one
+ // last used.
+ void SetAlternativeDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter,
+ bool latch_once_used);
+
+ void InstallDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter);
+ void RemoveDecrypter(EncryptionLevel level);
+
+ const QuicDecrypter* decrypter() const;
+ const QuicDecrypter* alternative_decrypter() const;
+
+ Perspective perspective() const { return perspective_; }
+
+ // Allow easy overriding of truncated connection IDs.
+ void set_can_truncate_connection_ids(bool can) {
+ can_truncate_connection_ids_ = can;
+ }
+
+ // Returns the underlying sent packet manager.
+ const QuicSentPacketManager& sent_packet_manager() const {
+ return sent_packet_manager_;
+ }
+
+ // Returns the underlying sent packet manager.
+ QuicSentPacketManager& sent_packet_manager() { return sent_packet_manager_; }
+
+ bool CanWrite(HasRetransmittableData retransmittable);
+
+ // When the flusher is out of scope, only the outermost flusher will cause a
+ // flush of the connection and set the retransmission alarm if there is one
+ // pending. In addition, this flusher can be configured to ensure that an ACK
+ // frame is included in the first packet created, if there's new ack
+ // information to be sent.
+ class QUIC_EXPORT_PRIVATE ScopedPacketFlusher {
+ public:
+ // Setting |include_ack| to true ensures that an ACK frame is
+ // opportunistically bundled with the first outgoing packet.
+ // TODO(fayang): Remove |ack_mode| when deprecating
+ // quic_deprecate_ack_bundling_mode.
+ ScopedPacketFlusher(QuicConnection* connection, AckBundling ack_mode);
+ ~ScopedPacketFlusher();
+
+ private:
+ bool ShouldSendAck(AckBundling ack_mode) const;
+
+ QuicConnection* connection_;
+ // If true, when this flusher goes out of scope, flush connection and set
+ // retransmission alarm if there is one pending.
+ bool flush_and_set_pending_retransmission_alarm_on_delete_;
+ };
+
+ QuicPacketWriter* writer() { return writer_; }
+ const QuicPacketWriter* writer() const { return writer_; }
+
+ // Sends an MTU discovery packet of size |target_mtu|. If the packet is
+ // acknowledged by the peer, the maximum packet size will be increased to
+ // |target_mtu|.
+ void SendMtuDiscoveryPacket(QuicByteCount target_mtu);
+
+ // Sends a connectivity probing packet to |peer_address| with
+ // |probing_writer|. If |probing_writer| is nullptr, will use default
+ // packet writer to write the packet. Returns true if subsequent packets can
+ // be written to the probing writer. If connection is V99, a padded IETF QUIC
+ // PATH_CHALLENGE packet is transmitted; if not V99, a Google QUIC padded PING
+ // packet is transmitted.
+ virtual bool SendConnectivityProbingPacket(
+ QuicPacketWriter* probing_writer,
+ const QuicSocketAddress& peer_address);
+
+ // Sends response to a connectivity probe. Sends either a Padded Ping
+ // or an IETF PATH_RESPONSE based on the version of the connection.
+ // Is the counterpart to SendConnectivityProbingPacket().
+ virtual void SendConnectivityProbingResponsePacket(
+ const QuicSocketAddress& peer_address);
+
+ // Sends an MTU discovery packet of size |mtu_discovery_target_| and updates
+ // the MTU discovery alarm.
+ void DiscoverMtu();
+
+ // Sets the session notifier on the SentPacketManager.
+ void SetSessionNotifier(SessionNotifierInterface* session_notifier);
+
+ // Set data producer in framer.
+ void SetDataProducer(QuicStreamFrameDataProducer* data_producer);
+
+ // Set transmission type of next sending packets.
+ void SetTransmissionType(TransmissionType type);
+
+ // Tries to send |message| and returns the message status.
+ virtual MessageStatus SendMessage(QuicMessageId message_id,
+ QuicMemSliceSpan message);
+
+ // Returns the largest payload that will fit into a single MESSAGE frame.
+ // Because overhead can vary during a connection, this method should be
+ // checked for every message.
+ QuicPacketLength GetCurrentLargestMessagePayload() const;
+ // Returns the largest payload that will fit into a single MESSAGE frame at
+ // any point during the connection. This assumes the version and
+ // connection ID lengths do not change.
+ QuicPacketLength GetGuaranteedLargestMessagePayload() const;
+
+ // Returns the id of the cipher last used for decrypting packets.
+ uint32_t cipher_id() const;
+
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets() {
+ return termination_packets_.get();
+ }
+
+ bool ack_queued() const { return ack_queued_; }
+
+ bool ack_frame_updated() const;
+
+ QuicConnectionHelperInterface* helper() { return helper_; }
+ QuicAlarmFactory* alarm_factory() { return alarm_factory_; }
+
+ QuicStringPiece GetCurrentPacket();
+
+ const QuicFramer& framer() const { return framer_; }
+
+ const QuicPacketGenerator& packet_generator() const {
+ return packet_generator_;
+ }
+
+ EncryptionLevel encryption_level() const { return encryption_level_; }
+ EncryptionLevel last_decrypted_level() const {
+ return last_decrypted_packet_level_;
+ }
+
+ const QuicSocketAddress& last_packet_source_address() const {
+ return last_packet_source_address_;
+ }
+
+ bool fill_up_link_during_probing() const {
+ return fill_up_link_during_probing_;
+ }
+ void set_fill_up_link_during_probing(bool new_value) {
+ fill_up_link_during_probing_ = new_value;
+ }
+
+ // This setting may be changed during the crypto handshake in order to
+ // enable/disable padding of different packets in the crypto handshake.
+ //
+ // This setting should never be set to false in public facing endpoints. It
+ // can only be set to false if there is some other mechanism of preventing
+ // amplification attacks, such as ICE (plus its a non-standard quic).
+ void set_fully_pad_crypto_hadshake_packets(bool new_value) {
+ packet_generator_.set_fully_pad_crypto_hadshake_packets(new_value);
+ }
+
+ bool fully_pad_during_crypto_handshake() const {
+ return packet_generator_.fully_pad_crypto_handshake_packets();
+ }
+
+ size_t min_received_before_ack_decimation() const;
+ void set_min_received_before_ack_decimation(size_t new_value);
+
+ size_t ack_frequency_before_ack_decimation() const;
+ void set_ack_frequency_before_ack_decimation(size_t new_value);
+
+ // If |defer| is true, configures the connection to defer sending packets in
+ // response to an ACK to the SendAlarm. If |defer| is false, packets may be
+ // sent immediately after receiving an ACK.
+ void set_defer_send_in_response_to_packets(bool defer) {
+ defer_send_in_response_to_packets_ = defer;
+ }
+
+ bool session_decides_what_to_write() const;
+
+ void SetRetransmittableOnWireAlarm();
+
+ // Sets the current per-packet options for the connection. The QuicConnection
+ // does not take ownership of |options|; |options| must live for as long as
+ // the QuicConnection is in use.
+ void set_per_packet_options(PerPacketOptions* options) {
+ per_packet_options_ = options;
+ }
+
+ bool IsPathDegrading() const { return is_path_degrading_; }
+
+ // Attempts to process any queued undecryptable packets.
+ void MaybeProcessUndecryptablePackets();
+
+ // Queue a coalesced packet.
+ void QueueCoalescedPacket(const QuicEncryptedPacket& packet);
+
+ // Process previously queued coalesced packets.
+ void MaybeProcessCoalescedPackets();
+
+ enum PacketContent : uint8_t {
+ NO_FRAMES_RECEIVED,
+ // TODO(fkastenholz): Change name when we get rid of padded ping/
+ // pre-version-99.
+ // Also PATH CHALLENGE and PATH RESPONSE.
+ FIRST_FRAME_IS_PING,
+ SECOND_FRAME_IS_PADDING,
+ NOT_PADDED_PING, // Set if the packet is not {PING, PADDING}.
+ };
+
+ // Whether the handshake is confirmed from this connection's perspective.
+ bool IsHandshakeConfirmed() const {
+ return sent_packet_manager_.handshake_confirmed();
+ }
+
+ // Returns the largest received packet number sent by peer.
+ QuicPacketNumber GetLargestReceivedPacket() const;
+
+ // Adds the connection ID to a set of connection IDs that are accepted as
+ // destination on incoming packets.
+ void AddIncomingConnectionId(QuicConnectionId connection_id);
+
+ // Called when ACK alarm goes off. Sends ACKs of those packet number spaces
+ // which have expired ACK timeout. Only used when this connection supports
+ // multiple packet number spaces.
+ void SendAllPendingAcks();
+
+ // Returns true if this connection supports multiple packet number spaces.
+ bool SupportsMultiplePacketNumberSpaces() const;
+
+ protected:
+ // Calls cancel() on all the alarms owned by this connection.
+ void CancelAllAlarms();
+
+ // Send a packet to the peer, and takes ownership of the packet if the packet
+ // cannot be written immediately.
+ virtual void SendOrQueuePacket(SerializedPacket* packet);
+
+ // Called after a packet is received from a new effective peer address and is
+ // decrypted. Starts validation of effective peer's address change. Calls
+ // OnConnectionMigration as soon as the address changed.
+ void StartEffectivePeerMigration(AddressChangeType type);
+
+ // Called when a effective peer address migration is validated.
+ virtual void OnEffectivePeerMigrationValidated();
+
+ // Get the effective peer address from the packet being processed. For proxied
+ // connections, effective peer address is the address of the endpoint behind
+ // the proxy. For non-proxied connections, effective peer address is the same
+ // as peer address.
+ //
+ // Notes for implementations in subclasses:
+ // - If the connection is not proxied, the overridden method should use the
+ // base implementation:
+ //
+ // return QuicConnection::GetEffectivePeerAddressFromCurrentPacket();
+ //
+ // - If the connection is proxied, the overridden method may return either of
+ // the following:
+ // a) The address of the endpoint behind the proxy. The address is used to
+ // drive effective peer migration.
+ // b) An uninitialized address, meaning the effective peer address does not
+ // change.
+ virtual QuicSocketAddress GetEffectivePeerAddressFromCurrentPacket() const;
+
+ // Selects and updates the version of the protocol being used by selecting a
+ // version from |available_versions| which is also supported. Returns true if
+ // such a version exists, false otherwise.
+ bool SelectMutualVersion(const ParsedQuicVersionVector& available_versions);
+
+ // Returns the current per-packet options for the connection.
+ PerPacketOptions* per_packet_options() { return per_packet_options_; }
+
+ AddressChangeType active_effective_peer_migration_type() const {
+ return active_effective_peer_migration_type_;
+ }
+
+ // Sends the connection close packet to the peer. |ack_mode| determines
+ // whether ack frame will be bundled with the connection close packet.
+ // TODO(fayang): change |ack_mode| to bool |force_sending_ack| when
+ // deprecating quic_deprecate_ack_bundling_mode.
+ virtual void SendConnectionClosePacket(QuicErrorCode error,
+ const std::string& details,
+ AckBundling ack_mode);
+
+ // Returns true if the packet should be discarded and not sent.
+ virtual bool ShouldDiscardPacket(const SerializedPacket& packet);
+
+ // Retransmits packets continuously until blocked by the congestion control.
+ // If there are no packets to retransmit, does not do anything.
+ void SendProbingRetransmissions();
+
+ // Decides whether to send probing retransmissions, and does so if required.
+ void MaybeSendProbingRetransmissions();
+
+ // Notify various components(SendPacketManager, Session etc.) that this
+ // connection has been migrated.
+ virtual void OnConnectionMigration(AddressChangeType addr_change_type);
+
+ // Return whether the packet being processed is a connectivity probing.
+ // A packet is a connectivity probing if it is a padded ping packet with self
+ // and/or peer address changes.
+ bool IsCurrentPacketConnectivityProbing() const;
+
+ // Return true iff the writer is blocked, if blocked, call
+ // visitor_->OnWriteBlocked() to add the connection into the write blocked
+ // list.
+ bool HandleWriteBlocked();
+
+ private:
+ friend class test::QuicConnectionPeer;
+
+ typedef std::list<SerializedPacket> QueuedPacketList;
+
+ // Notifies the visitor of the close and marks the connection as disconnected.
+ // Does not send a connection close frame to the peer.
+ void TearDownLocalConnectionState(QuicErrorCode error,
+ const std::string& details,
+ ConnectionCloseSource source);
+
+ // Writes the given packet to socket, encrypted with packet's
+ // encryption_level. Returns true on successful write, and false if the writer
+ // was blocked and the write needs to be tried again. Notifies the
+ // SentPacketManager when the write is successful and sets
+ // retransmittable frames to nullptr.
+ // Saves the connection close packet for later transmission, even if the
+ // writer is write blocked.
+ bool WritePacket(SerializedPacket* packet);
+
+ // Flush packets buffered in the writer, if any.
+ void FlushPackets();
+
+ // Make sure a stop waiting we got from our peer is sane.
+ // Returns nullptr if the frame is valid or an error string if it was invalid.
+ const char* ValidateStopWaitingFrame(
+ const QuicStopWaitingFrame& stop_waiting);
+
+ // Sends a version negotiation packet to the peer.
+ void SendVersionNegotiationPacket(bool ietf_quic);
+
+ // Clears any accumulated frames from the last received packet.
+ void ClearLastFrames();
+
+ // Deletes and clears any queued packets.
+ void ClearQueuedPackets();
+
+ // Closes the connection if the sent packet manager is tracking too many
+ // outstanding packets.
+ void CloseIfTooManyOutstandingSentPackets();
+
+ // Writes as many queued packets as possible. The connection must not be
+ // blocked when this is called.
+ void WriteQueuedPackets();
+
+ // Writes as many pending retransmissions as possible.
+ void WritePendingRetransmissions();
+
+ // Writes new data if congestion control allows.
+ void WriteNewData();
+
+ // Queues |packet| in the hopes that it can be decrypted in the
+ // future, when a new key is installed.
+ void QueueUndecryptablePacket(const QuicEncryptedPacket& packet);
+
+ // Sends any packets which are a response to the last packet, including both
+ // acks and pending writes if an ack opened the congestion window.
+ void MaybeSendInResponseToPacket();
+
+ // Queue an ack or set the ack alarm if needed. |was_missing| is true if
+ // the most recently received packet was formerly missing.
+ void MaybeQueueAck(bool was_missing);
+
+ // Gets the least unacked packet number, which is the next packet number to be
+ // sent if there are no outstanding packets.
+ QuicPacketNumber GetLeastUnacked() const;
+
+ // Sets the timeout alarm to the appropriate value, if any.
+ void SetTimeoutAlarm();
+
+ // Sets the ping alarm to the appropriate value, if any.
+ void SetPingAlarm();
+
+ // Sets the retransmission alarm based on SentPacketManager.
+ void SetRetransmissionAlarm();
+
+ // Sets the path degrading alarm.
+ void SetPathDegradingAlarm();
+
+ // Sets the MTU discovery alarm if necessary.
+ // |sent_packet_number| is the recently sent packet number.
+ void MaybeSetMtuAlarm(QuicPacketNumber sent_packet_number);
+
+ // Sets ack alarm to |time| if ack alarm is not set or the deadline > time.
+ void MaybeSetAckAlarmTo(QuicTime time);
+
+ HasRetransmittableData IsRetransmittable(const SerializedPacket& packet);
+ bool IsTerminationPacket(const SerializedPacket& packet);
+
+ // Set the size of the packet we are targeting while doing path MTU discovery.
+ void SetMtuDiscoveryTarget(QuicByteCount target);
+
+ // Returns |suggested_max_packet_size| clamped to any limits set by the
+ // underlying writer, connection, or protocol.
+ QuicByteCount GetLimitedMaxPacketSize(
+ QuicByteCount suggested_max_packet_size);
+
+ // Do any work which logically would be done in OnPacket but can not be
+ // safely done until the packet is validated. Returns true if packet can be
+ // handled, false otherwise.
+ bool ProcessValidatedPacket(const QuicPacketHeader& header);
+
+ // Returns true if received |packet_number| can be processed. Please note,
+ // this is called after packet got decrypted successfully.
+ bool ValidateReceivedPacketNumber(QuicPacketNumber packet_number);
+
+ // Consider receiving crypto frame on non crypto stream as memory corruption.
+ bool MaybeConsiderAsMemoryCorruption(const QuicStreamFrame& frame);
+
+ // Check if the connection has no outstanding data to send and notify
+ // congestion controller if it is the case.
+ void CheckIfApplicationLimited();
+
+ // Sets |current_packet_content_| to |type| if applicable. And
+ // starts effective peer migration if current packet is confirmed not a
+ // connectivity probe and |current_effective_peer_migration_type_| indicates
+ // effective peer address change.
+ void UpdatePacketContent(PacketContent type);
+
+ // Enables session decide what to write based on version and flags.
+ void MaybeEnableSessionDecidesWhatToWrite();
+
+ // Called when last received ack frame has been processed.
+ // |send_stop_waiting| indicates whether a stop waiting needs to be sent.
+ // |acked_new_packet| is true if a previously-unacked packet was acked.
+ void PostProcessAfterAckFrame(bool send_stop_waiting, bool acked_new_packet);
+
+ // Called when an ACK is received to set the path degrading alarm or
+ // retransmittable on wire alarm.
+ void MaybeSetPathDegradingAlarm(bool acked_new_packet);
+
+ // Updates the release time into the future.
+ void UpdateReleaseTimeIntoFuture();
+
+ // Sends generic path probe packet to the peer. If we are not IETF QUIC, will
+ // always send a padded ping, regardless of whether this is a request or
+ // response. If version 99/ietf quic, will send a PATH_RESPONSE if
+ // |is_response| is true, a PATH_CHALLENGE if not.
+ bool SendGenericPathProbePacket(QuicPacketWriter* probing_writer,
+ const QuicSocketAddress& peer_address,
+ bool is_response);
+
+ // Called when an ACK is about to send. Resets ACK related internal states,
+ // e.g., cancels ack_alarm_, resets
+ // num_retransmittable_packets_received_since_last_ack_sent_ etc.
+ void ResetAckStates();
+
+ // Enables multiple packet number spaces support based on handshake protocol
+ // and flags.
+ void MaybeEnableMultiplePacketNumberSpacesSupport();
+
+ // Returns true if ack alarm is not set and there is no pending ack in the
+ // generator.
+ bool ShouldSetAckAlarm() const;
+
+ // Returns the encryption level the connection close packet should be sent at,
+ // which is the highest encryption level that peer can guarantee to process.
+ EncryptionLevel GetConnectionCloseEncryptionLevel() const;
+
+ // Called after an ACK frame is successfully processed to update largest
+ // received packet number which contains an ACK frame.
+ void SetLargestReceivedPacketWithAck(QuicPacketNumber new_value);
+
+ // Returns largest received packet number which contains an ACK frame.
+ QuicPacketNumber GetLargestReceivedPacketWithAck() const;
+
+ // Returns the largest packet number that has been sent.
+ QuicPacketNumber GetLargestSentPacket() const;
+
+ // 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);
+
+ QuicFramer framer_;
+
+ // Contents received in the current packet, especially used to identify
+ // whether the current packet is a padded PING packet.
+ PacketContent current_packet_content_;
+ // Set to true as soon as the packet currently being processed has been
+ // detected as a connectivity probing.
+ // Always false outside the context of ProcessUdpPacket().
+ bool is_current_packet_connectivity_probing_;
+
+ // Caches the current effective peer migration type if a effective peer
+ // migration might be initiated. As soon as the current packet is confirmed
+ // not a connectivity probe, effective peer migration will start.
+ AddressChangeType current_effective_peer_migration_type_;
+ QuicConnectionHelperInterface* helper_; // Not owned.
+ QuicAlarmFactory* alarm_factory_; // Not owned.
+ PerPacketOptions* per_packet_options_; // Not owned.
+ QuicPacketWriter* writer_; // Owned or not depending on |owns_writer_|.
+ bool owns_writer_;
+ // Encryption level for new packets. Should only be changed via
+ // SetDefaultEncryptionLevel().
+ EncryptionLevel encryption_level_;
+ const QuicClock* clock_;
+ QuicRandom* random_generator_;
+
+ QuicConnectionId connection_id_;
+ // Address on the last successfully processed packet received from the
+ // direct peer.
+ QuicSocketAddress self_address_;
+ QuicSocketAddress peer_address_;
+
+ QuicSocketAddress direct_peer_address_;
+ // Address of the endpoint behind the proxy if the connection is proxied.
+ // Otherwise it is the same as |peer_address_|.
+ // NOTE: Currently |effective_peer_address_| and |peer_address_| are always
+ // the same(the address of the direct peer), but soon we'll change
+ // |effective_peer_address_| to be the address of the endpoint behind the
+ // proxy if the connection is proxied.
+ QuicSocketAddress effective_peer_address_;
+
+ // Records change type when the effective peer initiates migration to a new
+ // address. Reset to NO_CHANGE after effective peer migration is validated.
+ AddressChangeType active_effective_peer_migration_type_;
+
+ // Records highest sent packet number when effective peer migration is
+ // started.
+ QuicPacketNumber highest_packet_sent_before_effective_peer_migration_;
+
+ // True if the last packet has gotten far enough in the framer to be
+ // decrypted.
+ bool last_packet_decrypted_;
+ QuicByteCount last_size_; // Size of the last received packet.
+ // TODO(rch): remove this when b/27221014 is fixed.
+ const char* current_packet_data_; // UDP payload of packet currently being
+ // parsed or nullptr.
+ EncryptionLevel last_decrypted_packet_level_;
+ QuicPacketHeader last_header_;
+ bool should_last_packet_instigate_acks_;
+ // Whether the most recent packet was missing before it was received.
+ // TODO(fayang): Remove was_last_packet_missing_ when deprecating
+ // quic_rpm_decides_when_to_send_acks.
+ bool was_last_packet_missing_;
+
+ // Track some peer state so we can do less bookkeeping
+ // Largest sequence sent by the peer which had an ack frame (latest ack info).
+ // Do not read or write directly, use GetLargestReceivedPacketWithAck() and
+ // SetLargestReceivedPacketWithAck() instead.
+ QuicPacketNumber largest_seen_packet_with_ack_;
+ // Largest packet number sent by the peer which had an ACK frame per packet
+ // number space. Only used when this connection supports multiple packet
+ // number spaces.
+ QuicPacketNumber largest_seen_packets_with_ack_[NUM_PACKET_NUMBER_SPACES];
+
+ // Largest packet number sent by the peer which had a stop waiting frame.
+ QuicPacketNumber largest_seen_packet_with_stop_waiting_;
+
+ // Collection of packets which were received before encryption was
+ // established, but which could not be decrypted. We buffer these on
+ // the assumption that they could not be processed because they were
+ // sent with the INITIAL encryption and the CHLO message was lost.
+ QuicDeque<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_;
+
+ // Collection of coalesced packets which were received while processing
+ // the current packet.
+ QuicDeque<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_;
+
+ // Maximum number of undecryptable packets the connection will store.
+ size_t max_undecryptable_packets_;
+
+ // Maximum number of tracked packets.
+ QuicPacketCount max_tracked_packets_;
+
+ // When the version negotiation packet could not be sent because the socket
+ // was not writable, this is set to true.
+ bool pending_version_negotiation_packet_;
+ // Used when pending_version_negotiation_packet_ is true.
+ bool send_ietf_version_negotiation_packet_;
+
+ // When packets could not be sent because the socket was not writable,
+ // they are added to this list. All corresponding frames are in
+ // unacked_packets_ if they are to be retransmitted. Packets encrypted_buffer
+ // fields are owned by the QueuedPacketList, in order to ensure they outlast
+ // the original scope of the SerializedPacket.
+ QueuedPacketList queued_packets_;
+
+ // If true, then crypto packets will be saved as termination packets.
+ bool save_crypto_packets_as_termination_packets_;
+
+ // Contains the connection close packets if the connection has been closed.
+ std::unique_ptr<std::vector<std::unique_ptr<QuicEncryptedPacket>>>
+ termination_packets_;
+
+ // Determines whether or not a connection close packet is sent to the peer
+ // after idle timeout due to lack of network activity.
+ // This is particularly important on mobile, where waking up the radio is
+ // undesirable.
+ ConnectionCloseBehavior idle_timeout_connection_close_behavior_;
+
+ // When true, close the QUIC connection after 5 RTOs. Due to the min rto of
+ // 200ms, this is over 5 seconds.
+ bool close_connection_after_five_rtos_;
+
+ // TODO(fayang): remove received_packet_manager_ when deprecating
+ // quic_use_uber_received_packet_manager.
+ QuicReceivedPacketManager received_packet_manager_;
+ // Used when use_uber_received_packet_manager_ is true.
+ UberReceivedPacketManager uber_received_packet_manager_;
+
+ // Indicates whether an ack should be sent the next time we try to write.
+ // TODO(fayang): Remove ack_queued_ when deprecating
+ // quic_deprecate_ack_bundling_mode.
+ bool ack_queued_;
+ // How many retransmittable packets have arrived without sending an ack.
+ // TODO(fayang): Remove
+ // num_retransmittable_packets_received_since_last_ack_sent_ when deprecating
+ // quic_rpm_decides_when_to_send_acks.
+ QuicPacketCount num_retransmittable_packets_received_since_last_ack_sent_;
+ // How many consecutive packets have arrived without sending an ack.
+ QuicPacketCount num_packets_received_since_last_ack_sent_;
+ // Indicates how many consecutive times an ack has arrived which indicates
+ // the peer needs to stop waiting for some packets.
+ int stop_waiting_count_;
+ // TODO(fayang): Remove ack_mode_, ack_decimation_delay_,
+ // unlimited_ack_decimation_, fast_ack_after_quiescence_ when deprecating
+ // quic_rpm_decides_when_to_send_acks.
+ // Indicates the current ack mode, defaults to acking every 2 packets.
+ AckMode ack_mode_;
+ // The max delay in fraction of min_rtt to use when sending decimated acks.
+ float ack_decimation_delay_;
+ // When true, removes ack decimation's max number of packets(10) before
+ // sending an ack.
+ bool unlimited_ack_decimation_;
+ // When true, use a 1ms delayed ack timer if it's been an SRTT since a packet
+ // was received.
+ bool fast_ack_after_quiescence_;
+
+ // Indicates the retransmission alarm needs to be set.
+ bool pending_retransmission_alarm_;
+
+ // If true, defer sending data in response to received packets to the
+ // SendAlarm.
+ bool defer_send_in_response_to_packets_;
+
+ // The timeout for PING.
+ QuicTime::Delta ping_timeout_;
+
+ // Timeout for how long the wire can have no retransmittable packets.
+ QuicTime::Delta retransmittable_on_wire_timeout_;
+
+ // Arena to store class implementations within the QuicConnection.
+ QuicConnectionArena arena_;
+
+ // An alarm that fires when an ACK should be sent to the peer.
+ QuicArenaScopedPtr<QuicAlarm> ack_alarm_;
+ // An alarm that fires when a packet needs to be retransmitted.
+ QuicArenaScopedPtr<QuicAlarm> retransmission_alarm_;
+ // An alarm that is scheduled when the SentPacketManager requires a delay
+ // before sending packets and fires when the packet may be sent.
+ QuicArenaScopedPtr<QuicAlarm> send_alarm_;
+ // An alarm that is scheduled when the connection can still write and there
+ // may be more data to send.
+ // An alarm that fires when the connection may have timed out.
+ QuicArenaScopedPtr<QuicAlarm> timeout_alarm_;
+ // An alarm that fires when a ping should be sent.
+ QuicArenaScopedPtr<QuicAlarm> ping_alarm_;
+ // An alarm that fires when an MTU probe should be sent.
+ QuicArenaScopedPtr<QuicAlarm> mtu_discovery_alarm_;
+ // An alarm that fires when this connection is considered degrading.
+ QuicArenaScopedPtr<QuicAlarm> path_degrading_alarm_;
+ // An alarm that fires to process undecryptable packets when new decyrption
+ // keys are available.
+ QuicArenaScopedPtr<QuicAlarm> process_undecryptable_packets_alarm_;
+ // Neither visitor is owned by this class.
+ QuicConnectionVisitorInterface* visitor_;
+ QuicConnectionDebugVisitor* debug_visitor_;
+
+ QuicPacketGenerator packet_generator_;
+
+ // Network idle time before this connection is closed.
+ QuicTime::Delta idle_network_timeout_;
+ // The connection will wait this long for the handshake to complete.
+ QuicTime::Delta handshake_timeout_;
+
+ // Statistics for this session.
+ QuicConnectionStats stats_;
+
+ // Timestamps used for timeouts.
+ // The time of the first retransmittable packet that was sent after the most
+ // recently received packet.
+ QuicTime time_of_first_packet_sent_after_receiving_;
+ // The time that a packet is received for this connection. Initialized to
+ // connection creation time.
+ // This is used for timeouts, and does not indicate the packet was processed.
+ QuicTime time_of_last_received_packet_;
+
+ // The time the previous ack-instigating packet was received and processed.
+ // TODO(fayang): Remove time_of_previous_received_packet_ when deprecating
+ // quic_rpm_decides_when_to_send_acks.
+ QuicTime time_of_previous_received_packet_;
+
+ // Sent packet manager which tracks the status of packets sent by this
+ // connection and contains the send and receive algorithms to determine when
+ // to send packets.
+ QuicSentPacketManager sent_packet_manager_;
+
+ // The state of connection in version negotiation finite state machine.
+ enum QuicVersionNegotiationState {
+ START_NEGOTIATION = 0,
+ // Server-side this implies we've sent a version negotiation packet and are
+ // waiting on the client to select a compatible version. Client-side this
+ // implies we've gotten a version negotiation packet, are retransmitting the
+ // initial packets with a supported version and are waiting for our first
+ // packet from the server.
+ NEGOTIATION_IN_PROGRESS,
+ // This indicates this endpoint has received a packet from the peer with a
+ // version this endpoint supports. Version negotiation is complete, and the
+ // version number will no longer be sent with future packets.
+ NEGOTIATED_VERSION
+ };
+ QuicVersionNegotiationState version_negotiation_state_;
+
+ // Tracks if the connection was created by the server or the client.
+ Perspective perspective_;
+
+ // True by default. False if we've received or sent an explicit connection
+ // 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_;
+
+ // Set to false if the connection should not send truncated connection IDs to
+ // the peer, even if the peer supports it.
+ bool can_truncate_connection_ids_;
+
+ // If non-empty this contains the set of versions received in a
+ // version negotiation packet.
+ ParsedQuicVersionVector server_supported_versions_;
+
+ // The size of the packet we are targeting while doing path MTU discovery.
+ QuicByteCount mtu_discovery_target_;
+
+ // The number of MTU probes already sent.
+ size_t mtu_probe_count_;
+
+ // The number of packets between MTU probes.
+ QuicPacketCount packets_between_mtu_probes_;
+
+ // The packet number of the packet after which the next MTU probe will be
+ // sent.
+ QuicPacketNumber next_mtu_probe_at_;
+
+ // The value of the MTU regularly used by the connection. This is different
+ // from the value returned by max_packet_size(), as max_packet_size() returns
+ // the value of the MTU as currently used by the serializer, so if
+ // serialization of an MTU probe is in progress, those two values will be
+ // different.
+ QuicByteCount long_term_mtu_;
+
+ // The size of the largest packet received from peer.
+ QuicByteCount largest_received_packet_size_;
+
+ // Indicates whether a write error is encountered currently. This is used to
+ // avoid infinite write errors.
+ bool write_error_occurred_;
+
+ // Indicates not to send or process stop waiting frames.
+ bool no_stop_waiting_frames_;
+
+ // Consecutive number of sent packets which have no retransmittable frames.
+ size_t consecutive_num_packets_with_no_retransmittable_frames_;
+
+ // After this many packets sent without retransmittable frames, an artificial
+ // retransmittable frame(a WINDOW_UPDATE) will be created to solicit an ack
+ // from the peer. Default to kMaxConsecutiveNonRetransmittablePackets.
+ size_t max_consecutive_num_packets_with_no_retransmittable_frames_;
+
+ // Ack decimation will start happening after this many packets are received.
+ // TODO(fayang): Remove min_received_before_ack_decimation_ when deprecating
+ // quic_rpm_decides_when_to_send_acks.
+ size_t min_received_before_ack_decimation_;
+
+ // Before ack decimation starts (if enabled), we ack every n-th packet.
+ // TODO(fayang): Remove ack_frequency_before_ack_decimation_ when deprecating
+ // quic_rpm_decides_when_to_send_acks.
+ size_t ack_frequency_before_ack_decimation_;
+
+ // If true, the connection will fill up the pipe with extra data whenever the
+ // congestion controller needs it in order to make a bandwidth estimate. This
+ // is useful if the application pesistently underutilizes the link, but still
+ // relies on having a reasonable bandwidth estimate from the connection, e.g.
+ // for real time applications.
+ bool fill_up_link_during_probing_;
+
+ // If true, the probing retransmission will not be started again. This is
+ // used to safeguard against an accidental tail recursion in probing
+ // 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.
+ QuicUint128 received_stateless_reset_token_;
+
+ // Id of latest sent control frame. 0 if no control frame has been sent.
+ QuicControlFrameId last_control_frame_id_;
+
+ // True if the peer is unreachable on the current path.
+ bool is_path_degrading_;
+
+ // True if an ack frame is being processed.
+ bool processing_ack_frame_;
+
+ // True if the writer supports release timestamp.
+ bool supports_release_time_;
+
+ // Time this connection can release packets into the future.
+ QuicTime::Delta release_time_into_future_;
+
+ // Indicates whether server connection does version negotiation. Server
+ // connection does not support version negotiation if a single version is
+ // provided in constructor.
+ const bool no_version_negotiation_;
+
+ // Payload of most recently transmitted QUIC_VERSION_99 connectivity
+ // probe packet (the PATH_CHALLENGE payload). This implementation transmits
+ // only one PATH_CHALLENGE per connectivity probe, so only one
+ // QuicPathFrameBuffer is needed.
+ std::unique_ptr<QuicPathFrameBuffer> transmitted_connectivity_probe_payload_;
+
+ // Payloads that were received in the most recent probe. This needs to be a
+ // Deque because the peer might no be using this implementation, and others
+ // might send a packet with more than one PATH_CHALLENGE, so all need to be
+ // saved and responded to.
+ QuicDeque<QuicPathFrameBuffer> received_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_;
+
+ // Latched value of quic_fix_termination_packets.
+ const bool fix_termination_packets_;
+
+ // Indicates whether an ACK needs to be sent in OnCanWrite(). Only used when
+ // deprecate_ack_bundling_mode is true.
+ // TODO(fayang): Remove this when ACK sending logic is moved to received
+ // packet manager, and an ACK timeout would be used to record when an ACK
+ // needs to be sent.
+ bool send_ack_when_on_can_write_;
+
+ // Latched value of quic_validate_packet_number_post_decryption.
+ const bool validate_packet_number_post_decryption_;
+
+ // Latched value of quic_rpm_decides_when_to_send_acks and
+ // quic_use_uber_received_packet_manager.
+ const bool use_uber_received_packet_manager_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_close_delegate_interface.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_close_delegate_interface.h
new file mode 100644
index 00000000000..b245f4c16a9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_close_delegate_interface.h
@@ -0,0 +1,29 @@
+// 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 QUICHE_QUIC_CORE_QUIC_CONNECTION_CLOSE_DELEGATE_INTERFACE_H_
+#define QUICHE_QUIC_CORE_QUIC_CONNECTION_CLOSE_DELEGATE_INTERFACE_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Pure virtual class to close connection on unrecoverable errors.
+class QUIC_EXPORT_PRIVATE QuicConnectionCloseDelegateInterface {
+ public:
+ virtual ~QuicConnectionCloseDelegateInterface() {}
+
+ // Called when an unrecoverable error is encountered.
+ virtual void OnUnrecoverableError(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_CLOSE_DELEGATE_INTERFACE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc
new file mode 100644
index 00000000000..4022283b55f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.cc
@@ -0,0 +1,106 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+
+#include <cstdint>
+#include <cstring>
+#include <iomanip>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+QuicConnectionId::QuicConnectionId() : length_(0) {}
+
+QuicConnectionId::QuicConnectionId(const char* data, uint8_t length) {
+ if (length > kQuicMaxConnectionIdLength) {
+ QUIC_BUG << "Attempted to create connection ID of length " << length;
+ length = kQuicMaxConnectionIdLength;
+ }
+ length_ = length;
+ if (length_ > 0) {
+ memcpy(data_, data, length_);
+ }
+}
+
+QuicConnectionId::~QuicConnectionId() {}
+
+const char* QuicConnectionId::data() const {
+ return data_;
+}
+
+char* QuicConnectionId::mutable_data() {
+ return data_;
+}
+
+uint8_t QuicConnectionId::length() const {
+ return length_;
+}
+
+void QuicConnectionId::set_length(uint8_t length) {
+ length_ = length;
+}
+
+bool QuicConnectionId::IsEmpty() const {
+ return length_ == 0;
+}
+
+size_t QuicConnectionId::Hash() const {
+ uint64_t data_bytes[3] = {0, 0, 0};
+ static_assert(sizeof(data_bytes) >= sizeof(data_), "sizeof(data_) changed");
+ memcpy(data_bytes, data_, length_);
+ // This Hash function is designed to return the same value as the host byte
+ // order representation when the connection ID length is 64 bits.
+ return QuicEndian::NetToHost64(kQuicDefaultConnectionIdLength ^ length_ ^
+ data_bytes[0] ^ data_bytes[1] ^ data_bytes[2]);
+}
+
+std::string QuicConnectionId::ToString() const {
+ if (IsEmpty()) {
+ return std::string("0");
+ }
+ return QuicTextUtils::HexEncode(data_, length_);
+}
+
+std::ostream& operator<<(std::ostream& os, const QuicConnectionId& v) {
+ os << v.ToString();
+ return os;
+}
+
+bool QuicConnectionId::operator==(const QuicConnectionId& v) const {
+ return length_ == v.length_ && memcmp(data_, v.data_, length_) == 0;
+}
+
+bool QuicConnectionId::operator!=(const QuicConnectionId& v) const {
+ return !(v == *this);
+}
+
+bool QuicConnectionId::operator<(const QuicConnectionId& v) const {
+ if (length_ < v.length_) {
+ return true;
+ }
+ if (length_ > v.length_) {
+ return false;
+ }
+ return memcmp(data_, v.data_, length_) < 0;
+}
+
+QuicConnectionId EmptyQuicConnectionId() {
+ return QuicConnectionId();
+}
+
+static_assert(kQuicDefaultConnectionIdLength == sizeof(uint64_t),
+ "kQuicDefaultConnectionIdLength changed");
+static_assert(kQuicDefaultConnectionIdLength == PACKET_8BYTE_CONNECTION_ID,
+ "kQuicDefaultConnectionIdLength changed");
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h
new file mode 100644
index 00000000000..d51366f75bb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id.h
@@ -0,0 +1,103 @@
+// Copyright 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_CORE_QUIC_CONNECTION_ID_H_
+#define QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+enum QuicConnectionIdLength {
+ PACKET_0BYTE_CONNECTION_ID = 0,
+ PACKET_8BYTE_CONNECTION_ID = 8,
+};
+
+// This is a property of QUIC headers, it indicates whether the connection ID
+// should actually be sent over the wire (or was sent on received packets).
+enum QuicConnectionIdIncluded : uint8_t {
+ CONNECTION_ID_PRESENT = 1,
+ CONNECTION_ID_ABSENT = 2,
+};
+
+// Connection IDs can be 0-18 bytes per IETF specifications.
+const uint8_t kQuicMaxConnectionIdLength = 18;
+
+// kQuicDefaultConnectionIdLength is the only supported length for QUIC
+// versions < v99, and is the default picked for all versions.
+const uint8_t kQuicDefaultConnectionIdLength = 8;
+
+// According to the IETF spec, the initial server connection ID generated by
+// the client must be at least this long.
+const uint8_t kQuicMinimumInitialConnectionIdLength = 8;
+
+class QUIC_EXPORT_PRIVATE QuicConnectionId {
+ public:
+ // Creates a connection ID of length zero.
+ QuicConnectionId();
+
+ // Creates a connection ID from network order bytes.
+ QuicConnectionId(const char* data, uint8_t length);
+
+ ~QuicConnectionId();
+
+ // Returns the length of the connection ID, in bytes.
+ uint8_t length() const;
+
+ // Sets the length of the connection ID, in bytes.
+ void set_length(uint8_t length);
+
+ // Returns a pointer to the connection ID bytes, in network byte order.
+ const char* data() const;
+
+ // Returns a mutable pointer to the connection ID bytes,
+ // in network byte order.
+ char* mutable_data();
+
+ // Returns whether the connection ID has length zero.
+ bool IsEmpty() const;
+
+ // Hash() is required to use connection IDs as keys in hash tables.
+ size_t Hash() const;
+
+ // Generates an ASCII string that represents
+ // the contents of the connection ID, or "0" if it is empty.
+ std::string ToString() const;
+
+ // operator<< allows easily logging connection IDs.
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicConnectionId& v);
+
+ bool operator==(const QuicConnectionId& v) const;
+ bool operator!=(const QuicConnectionId& v) const;
+ // operator< is required to use connection IDs as keys in hash tables.
+ bool operator<(const QuicConnectionId& v) const;
+
+ private:
+ // The connection ID is represented in network byte order
+ // in the first |length_| bytes of |data_|.
+ char data_[kQuicMaxConnectionIdLength];
+ uint8_t length_;
+};
+
+// Creates a connection ID of length zero, unless the restart flag
+// quic_connection_ids_network_byte_order is false in which case
+// it returns an 8-byte all-zeroes connection ID.
+QUIC_EXPORT_PRIVATE QuicConnectionId EmptyQuicConnectionId();
+
+// QuicConnectionIdHash can be passed as hash argument to hash tables.
+class QuicConnectionIdHash {
+ public:
+ size_t operator()(QuicConnectionId const& connection_id) const noexcept {
+ return connection_id.Hash();
+ }
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_test.cc
new file mode 100644
index 00000000000..c855192c8c8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_id_test.cc
@@ -0,0 +1,97 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+
+#include <cstdint>
+#include <cstring>
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+namespace {
+
+class QuicConnectionIdTest : public QuicTest {};
+
+TEST_F(QuicConnectionIdTest, Empty) {
+ QuicConnectionId connection_id_empty = EmptyQuicConnectionId();
+ EXPECT_TRUE(connection_id_empty.IsEmpty());
+}
+
+TEST_F(QuicConnectionIdTest, DefaultIsEmpty) {
+ QuicConnectionId connection_id_empty = QuicConnectionId();
+ EXPECT_TRUE(connection_id_empty.IsEmpty());
+}
+
+TEST_F(QuicConnectionIdTest, NotEmpty) {
+ QuicConnectionId connection_id = test::TestConnectionId(1);
+ EXPECT_FALSE(connection_id.IsEmpty());
+}
+
+TEST_F(QuicConnectionIdTest, ZeroIsNotEmpty) {
+ QuicConnectionId connection_id = test::TestConnectionId(0);
+ EXPECT_FALSE(connection_id.IsEmpty());
+}
+
+TEST_F(QuicConnectionIdTest, Data) {
+ char connection_id_data[kQuicDefaultConnectionIdLength];
+ memset(connection_id_data, 0x42, sizeof(connection_id_data));
+ QuicConnectionId connection_id1 =
+ QuicConnectionId(connection_id_data, sizeof(connection_id_data));
+ QuicConnectionId connection_id2 =
+ QuicConnectionId(connection_id_data, sizeof(connection_id_data));
+ EXPECT_EQ(connection_id1, connection_id2);
+ EXPECT_EQ(connection_id1.length(), kQuicDefaultConnectionIdLength);
+ EXPECT_EQ(connection_id1.data(), connection_id1.mutable_data());
+ EXPECT_EQ(0, memcmp(connection_id1.data(), connection_id2.data(),
+ sizeof(connection_id_data)));
+ EXPECT_EQ(0, memcmp(connection_id1.data(), connection_id_data,
+ sizeof(connection_id_data)));
+ connection_id2.mutable_data()[0] = 0x33;
+ EXPECT_NE(connection_id1, connection_id2);
+ static const uint8_t kNewLength = 4;
+ connection_id2.set_length(kNewLength);
+ EXPECT_EQ(kNewLength, connection_id2.length());
+}
+
+TEST_F(QuicConnectionIdTest, DoubleConvert) {
+ QuicConnectionId connection_id64_1 = test::TestConnectionId(1);
+ QuicConnectionId connection_id64_2 = test::TestConnectionId(42);
+ QuicConnectionId connection_id64_3 =
+ test::TestConnectionId(UINT64_C(0xfedcba9876543210));
+ EXPECT_EQ(connection_id64_1,
+ test::TestConnectionId(
+ test::TestConnectionIdToUInt64(connection_id64_1)));
+ EXPECT_EQ(connection_id64_2,
+ test::TestConnectionId(
+ test::TestConnectionIdToUInt64(connection_id64_2)));
+ EXPECT_EQ(connection_id64_3,
+ test::TestConnectionId(
+ test::TestConnectionIdToUInt64(connection_id64_3)));
+ EXPECT_NE(connection_id64_1, connection_id64_2);
+ EXPECT_NE(connection_id64_1, connection_id64_3);
+ EXPECT_NE(connection_id64_2, connection_id64_3);
+}
+
+TEST_F(QuicConnectionIdTest, Hash) {
+ QuicConnectionId connection_id64_1 = test::TestConnectionId(1);
+ QuicConnectionId connection_id64_1b = test::TestConnectionId(1);
+ QuicConnectionId connection_id64_2 = test::TestConnectionId(42);
+ QuicConnectionId connection_id64_3 =
+ test::TestConnectionId(UINT64_C(0xfedcba9876543210));
+ EXPECT_EQ(connection_id64_1.Hash(), connection_id64_1b.Hash());
+ EXPECT_NE(connection_id64_1.Hash(), connection_id64_2.Hash());
+ EXPECT_NE(connection_id64_1.Hash(), connection_id64_3.Hash());
+ EXPECT_NE(connection_id64_2.Hash(), connection_id64_3.Hash());
+}
+
+} // namespace
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc
new file mode 100644
index 00000000000..4d5c578c3a6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.cc
@@ -0,0 +1,97 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+
+namespace quic {
+
+QuicConnectionStats::QuicConnectionStats()
+ : bytes_sent(0),
+ packets_sent(0),
+ stream_bytes_sent(0),
+ packets_discarded(0),
+ bytes_received(0),
+ packets_received(0),
+ packets_processed(0),
+ stream_bytes_received(0),
+ bytes_retransmitted(0),
+ packets_retransmitted(0),
+ bytes_spuriously_retransmitted(0),
+ packets_spuriously_retransmitted(0),
+ packets_lost(0),
+ slowstart_count(0),
+ slowstart_num_rtts(0),
+ slowstart_packets_sent(0),
+ slowstart_bytes_sent(0),
+ slowstart_packets_lost(0),
+ slowstart_bytes_lost(0),
+ slowstart_duration(QuicTime::Delta::Zero()),
+ slowstart_start_time(QuicTime::Zero()),
+ packets_dropped(0),
+ crypto_retransmit_count(0),
+ loss_timeout_count(0),
+ tlp_count(0),
+ rto_count(0),
+ min_rtt_us(0),
+ srtt_us(0),
+ max_packet_size(0),
+ max_received_packet_size(0),
+ estimated_bandwidth(QuicBandwidth::Zero()),
+ packets_reordered(0),
+ max_sequence_reordering(0),
+ max_time_reordering_us(0),
+ tcp_loss_events(0),
+ connection_creation_time(QuicTime::Zero()),
+ blocked_frames_received(0),
+ blocked_frames_sent(0),
+ num_connectivity_probing_received(0) {}
+
+QuicConnectionStats::QuicConnectionStats(const QuicConnectionStats& other) =
+ default;
+
+QuicConnectionStats::~QuicConnectionStats() {}
+
+std::ostream& operator<<(std::ostream& os, const QuicConnectionStats& s) {
+ os << "{ bytes_sent: " << s.bytes_sent;
+ os << " packets_sent: " << s.packets_sent;
+ os << " stream_bytes_sent: " << s.stream_bytes_sent;
+ os << " packets_discarded: " << s.packets_discarded;
+ os << " bytes_received: " << s.bytes_received;
+ os << " packets_received: " << s.packets_received;
+ os << " packets_processed: " << s.packets_processed;
+ os << " stream_bytes_received: " << s.stream_bytes_received;
+ os << " bytes_retransmitted: " << s.bytes_retransmitted;
+ os << " packets_retransmitted: " << s.packets_retransmitted;
+ os << " bytes_spuriously_retransmitted: " << s.bytes_spuriously_retransmitted;
+ os << " packets_spuriously_retransmitted: "
+ << s.packets_spuriously_retransmitted;
+ os << " packets_lost: " << s.packets_lost;
+ os << " slowstart_packets_sent: " << s.slowstart_packets_sent;
+ os << " slowstart_packets_lost: " << s.slowstart_packets_lost;
+ os << " slowstart_bytes_lost: " << s.slowstart_bytes_lost;
+ os << " packets_dropped: " << s.packets_dropped;
+ os << " crypto_retransmit_count: " << s.crypto_retransmit_count;
+ os << " loss_timeout_count: " << s.loss_timeout_count;
+ os << " tlp_count: " << s.tlp_count;
+ os << " rto_count: " << s.rto_count;
+ os << " min_rtt_us: " << s.min_rtt_us;
+ os << " srtt_us: " << s.srtt_us;
+ os << " max_packet_size: " << s.max_packet_size;
+ os << " max_received_packet_size: " << s.max_received_packet_size;
+ os << " estimated_bandwidth: " << s.estimated_bandwidth;
+ os << " packets_reordered: " << s.packets_reordered;
+ os << " max_sequence_reordering: " << s.max_sequence_reordering;
+ os << " max_time_reordering_us: " << s.max_time_reordering_us;
+ os << " tcp_loss_events: " << s.tcp_loss_events;
+ os << " connection_creation_time: "
+ << s.connection_creation_time.ToDebuggingValue();
+ os << " blocked_frames_received: " << s.blocked_frames_received;
+ os << " blocked_frames_sent: " << s.blocked_frames_sent;
+ os << " num_connectivity_probing_received: "
+ << s.num_connectivity_probing_received << " }";
+
+ return os;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h
new file mode 100644
index 00000000000..df2f7f8ba7d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_stats.h
@@ -0,0 +1,107 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CONNECTION_STATS_H_
+#define QUICHE_QUIC_CORE_QUIC_CONNECTION_STATS_H_
+
+#include <cstdint>
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+// Structure to hold stats for a QuicConnection.
+struct QUIC_EXPORT_PRIVATE QuicConnectionStats {
+ QuicConnectionStats();
+ QuicConnectionStats(const QuicConnectionStats& other);
+ ~QuicConnectionStats();
+
+ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os,
+ const QuicConnectionStats& s);
+
+ QuicByteCount bytes_sent; // Includes retransmissions.
+ QuicPacketCount packets_sent;
+ // Non-retransmitted bytes sent in a stream frame.
+ QuicByteCount stream_bytes_sent;
+ // Packets serialized and discarded before sending.
+ QuicPacketCount packets_discarded;
+
+ // These include version negotiation and public reset packets, which do not
+ // have packet numbers or frame data.
+ QuicByteCount bytes_received; // Includes duplicate data for a stream.
+ // Includes packets which were not processable.
+ QuicPacketCount packets_received;
+ // Excludes packets which were not processable.
+ QuicPacketCount packets_processed;
+ QuicByteCount stream_bytes_received; // Bytes received in a stream frame.
+
+ QuicByteCount bytes_retransmitted;
+ QuicPacketCount packets_retransmitted;
+
+ QuicByteCount bytes_spuriously_retransmitted;
+ QuicPacketCount packets_spuriously_retransmitted;
+ // Number of packets abandoned as lost by the loss detection algorithm.
+ QuicPacketCount packets_lost;
+
+ // Number of times this connection went through the slow start phase.
+ uint32_t slowstart_count;
+ // Number of round trips spent in slow start.
+ uint32_t slowstart_num_rtts;
+ // Number of packets sent in slow start.
+ QuicPacketCount slowstart_packets_sent;
+ // Number of bytes sent in slow start.
+ QuicByteCount slowstart_bytes_sent;
+ // Number of packets lost exiting slow start.
+ QuicPacketCount slowstart_packets_lost;
+ // Number of bytes lost exiting slow start.
+ QuicByteCount slowstart_bytes_lost;
+ // Time spent in COMPLETED slow start phases.
+ QuicTime::Delta slowstart_duration;
+ // Start time of the last slow start phase.
+ QuicTime slowstart_start_time;
+
+ QuicPacketCount packets_dropped; // Duplicate or less than least unacked.
+ size_t crypto_retransmit_count;
+ // Count of times the loss detection alarm fired. At least one packet should
+ // be lost when the alarm fires.
+ size_t loss_timeout_count;
+ size_t tlp_count;
+ size_t rto_count; // Count of times the rto timer fired.
+
+ int64_t min_rtt_us; // Minimum RTT in microseconds.
+ int64_t srtt_us; // Smoothed RTT in microseconds.
+ QuicByteCount max_packet_size;
+ QuicByteCount max_received_packet_size;
+ QuicBandwidth estimated_bandwidth;
+
+ // Reordering stats for received packets.
+ // Number of packets received out of packet number order.
+ QuicPacketCount packets_reordered;
+ // Maximum reordering observed in packet number space.
+ QuicPacketCount max_sequence_reordering;
+ // Maximum reordering observed in microseconds
+ int64_t max_time_reordering_us;
+
+ // The following stats are used only in TcpCubicSender.
+ // The number of loss events from TCP's perspective. Each loss event includes
+ // one or more lost packets.
+ uint32_t tcp_loss_events;
+
+ // Creation time, as reported by the QuicClock.
+ QuicTime connection_creation_time;
+
+ uint64_t blocked_frames_received;
+ uint64_t blocked_frames_sent;
+
+ // Number of connectivity probing packets received by this connection.
+ uint64_t num_connectivity_probing_received;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CONNECTION_STATS_H_
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
new file mode 100644
index 00000000000..56b0453b51a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_connection_test.cc
@@ -0,0 +1,8864 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+
+#include <errno.h>
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::DoAll;
+using testing::Exactly;
+using testing::Ge;
+using testing::IgnoreResult;
+using testing::InSequence;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::Lt;
+using testing::Ref;
+using testing::Return;
+using testing::SaveArg;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+const char data1[] = "foo";
+const char data2[] = "bar";
+
+const bool kHasStopWaiting = true;
+
+const int kDefaultRetransmissionTimeMs = 500;
+
+DiversificationNonce kTestDiversificationNonce = {
+ 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a',
+ 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b',
+ 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b',
+};
+
+const QuicSocketAddress kPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(),
+ /*port=*/12345);
+const QuicSocketAddress kSelfAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(),
+ /*port=*/443);
+
+Perspective InvertPerspective(Perspective perspective) {
+ return perspective == Perspective::IS_CLIENT ? Perspective::IS_SERVER
+ : Perspective::IS_CLIENT;
+}
+
+QuicStreamId GetNthClientInitiatedStreamId(int n,
+ QuicTransportVersion version) {
+ return QuicUtils::GetHeadersStreamId(version) + n * 2;
+}
+
+QuicLongHeaderType EncryptionlevelToLongHeaderType(EncryptionLevel level) {
+ switch (level) {
+ case ENCRYPTION_INITIAL:
+ return INITIAL;
+ case ENCRYPTION_HANDSHAKE:
+ return HANDSHAKE;
+ case ENCRYPTION_ZERO_RTT:
+ return ZERO_RTT_PROTECTED;
+ case ENCRYPTION_FORWARD_SECURE:
+ DCHECK(false);
+ return INVALID_PACKET_TYPE;
+ default:
+ DCHECK(false);
+ return INVALID_PACKET_TYPE;
+ }
+}
+
+// TaggingEncrypter appends kTagSize bytes of |tag| to the end of each message.
+class TaggingEncrypter : public QuicEncrypter {
+ public:
+ explicit TaggingEncrypter(uint8_t tag) : tag_(tag) {}
+ TaggingEncrypter(const TaggingEncrypter&) = delete;
+ TaggingEncrypter& operator=(const TaggingEncrypter&) = delete;
+
+ ~TaggingEncrypter() override {}
+
+ // QuicEncrypter interface.
+ bool SetKey(QuicStringPiece key) override { return true; }
+
+ bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; }
+
+ bool SetIV(QuicStringPiece iv) override { return true; }
+
+ bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; }
+
+ bool EncryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) override {
+ const size_t len = plaintext.size() + kTagSize;
+ if (max_output_length < len) {
+ return false;
+ }
+ // Memmove is safe for inplace encryption.
+ memmove(output, plaintext.data(), plaintext.size());
+ output += plaintext.size();
+ memset(output, tag_, kTagSize);
+ *output_length = len;
+ return true;
+ }
+
+ std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override {
+ return std::string(5, 0);
+ }
+
+ size_t GetKeySize() const override { return 0; }
+ size_t GetNoncePrefixSize() const override { return 0; }
+ size_t GetIVSize() const override { return 0; }
+
+ size_t GetMaxPlaintextSize(size_t ciphertext_size) const override {
+ return ciphertext_size - kTagSize;
+ }
+
+ size_t GetCiphertextSize(size_t plaintext_size) const override {
+ return plaintext_size + kTagSize;
+ }
+
+ QuicStringPiece GetKey() const override { return QuicStringPiece(); }
+
+ QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); }
+
+ private:
+ enum {
+ kTagSize = 12,
+ };
+
+ const uint8_t tag_;
+};
+
+// TaggingDecrypter ensures that the final kTagSize bytes of the message all
+// have the same value and then removes them.
+class TaggingDecrypter : public QuicDecrypter {
+ public:
+ ~TaggingDecrypter() override {}
+
+ // QuicDecrypter interface
+ bool SetKey(QuicStringPiece key) override { return true; }
+
+ bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; }
+
+ bool SetIV(QuicStringPiece iv) override { return true; }
+
+ bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; }
+
+ bool SetPreliminaryKey(QuicStringPiece key) override {
+ QUIC_BUG << "should not be called";
+ return false;
+ }
+
+ bool SetDiversificationNonce(const DiversificationNonce& key) override {
+ return true;
+ }
+
+ bool DecryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) override {
+ if (ciphertext.size() < kTagSize) {
+ return false;
+ }
+ if (!CheckTag(ciphertext, GetTag(ciphertext))) {
+ return false;
+ }
+ *output_length = ciphertext.size() - kTagSize;
+ memcpy(output, ciphertext.data(), *output_length);
+ return true;
+ }
+
+ std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) override {
+ return std::string(5, 0);
+ }
+
+ size_t GetKeySize() const override { return 0; }
+ size_t GetIVSize() const override { return 0; }
+ QuicStringPiece GetKey() const override { return QuicStringPiece(); }
+ QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); }
+ // Use a distinct value starting with 0xFFFFFF, which is never used by TLS.
+ uint32_t cipher_id() const override { return 0xFFFFFFF0; }
+
+ protected:
+ virtual uint8_t GetTag(QuicStringPiece ciphertext) {
+ return ciphertext.data()[ciphertext.size() - 1];
+ }
+
+ private:
+ enum {
+ kTagSize = 12,
+ };
+
+ bool CheckTag(QuicStringPiece ciphertext, uint8_t tag) {
+ for (size_t i = ciphertext.size() - kTagSize; i < ciphertext.size(); i++) {
+ if (ciphertext.data()[i] != tag) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+// StringTaggingDecrypter ensures that the final kTagSize bytes of the message
+// match the expected value.
+class StrictTaggingDecrypter : public TaggingDecrypter {
+ public:
+ explicit StrictTaggingDecrypter(uint8_t tag) : tag_(tag) {}
+ ~StrictTaggingDecrypter() override {}
+
+ // TaggingQuicDecrypter
+ uint8_t GetTag(QuicStringPiece ciphertext) override { return tag_; }
+
+ // Use a distinct value starting with 0xFFFFFF, which is never used by TLS.
+ uint32_t cipher_id() const override { return 0xFFFFFFF1; }
+
+ private:
+ const uint8_t tag_;
+};
+
+class TestConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+ TestConnectionHelper(MockClock* clock, MockRandom* random_generator)
+ : clock_(clock), random_generator_(random_generator) {
+ clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+ TestConnectionHelper(const TestConnectionHelper&) = delete;
+ TestConnectionHelper& operator=(const TestConnectionHelper&) = delete;
+
+ // QuicConnectionHelperInterface
+ const QuicClock* GetClock() const override { return clock_; }
+
+ QuicRandom* GetRandomGenerator() override { return random_generator_; }
+
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override {
+ return &buffer_allocator_;
+ }
+
+ private:
+ MockClock* clock_;
+ MockRandom* random_generator_;
+ SimpleBufferAllocator buffer_allocator_;
+};
+
+class TestAlarmFactory : public QuicAlarmFactory {
+ public:
+ class TestAlarm : public QuicAlarm {
+ public:
+ explicit TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
+ : QuicAlarm(std::move(delegate)) {}
+
+ void SetImpl() override {}
+ void CancelImpl() override {}
+ using QuicAlarm::Fire;
+ };
+
+ TestAlarmFactory() {}
+ TestAlarmFactory(const TestAlarmFactory&) = delete;
+ TestAlarmFactory& operator=(const TestAlarmFactory&) = delete;
+
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override {
+ return new TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
+ }
+
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override {
+ return arena->New<TestAlarm>(std::move(delegate));
+ }
+};
+
+class TestPacketWriter : public QuicPacketWriter {
+ public:
+ TestPacketWriter(ParsedQuicVersion version, MockClock* clock)
+ : version_(version),
+ framer_(SupportedVersions(version_), Perspective::IS_SERVER),
+ last_packet_size_(0),
+ write_blocked_(false),
+ write_should_fail_(false),
+ block_on_next_flush_(false),
+ block_on_next_write_(false),
+ next_packet_too_large_(false),
+ always_get_packet_too_large_(false),
+ is_write_blocked_data_buffered_(false),
+ is_batch_mode_(false),
+ final_bytes_of_last_packet_(0),
+ final_bytes_of_previous_packet_(0),
+ use_tagging_decrypter_(false),
+ packets_write_attempts_(0),
+ clock_(clock),
+ write_pause_time_delta_(QuicTime::Delta::Zero()),
+ max_packet_size_(kMaxOutgoingPacketSize),
+ supports_release_time_(false) {}
+ TestPacketWriter(const TestPacketWriter&) = delete;
+ TestPacketWriter& operator=(const TestPacketWriter&) = delete;
+
+ // QuicPacketWriter interface
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override {
+ QuicEncryptedPacket packet(buffer, buf_len);
+ ++packets_write_attempts_;
+
+ if (packet.length() >= sizeof(final_bytes_of_last_packet_)) {
+ final_bytes_of_previous_packet_ = final_bytes_of_last_packet_;
+ memcpy(&final_bytes_of_last_packet_, packet.data() + packet.length() - 4,
+ sizeof(final_bytes_of_last_packet_));
+ }
+
+ if (use_tagging_decrypter_) {
+ if (framer_.framer()->version().KnowsWhichDecrypterToUse()) {
+ framer_.framer()->InstallDecrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingDecrypter>());
+ framer_.framer()->InstallDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingDecrypter>());
+ framer_.framer()->InstallDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TaggingDecrypter>());
+ } else {
+ framer_.framer()->SetDecrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingDecrypter>());
+ }
+ } else if (framer_.framer()->version().KnowsWhichDecrypterToUse()) {
+ framer_.framer()->InstallDecrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_SERVER));
+ }
+ EXPECT_TRUE(framer_.ProcessPacket(packet));
+ if (block_on_next_write_) {
+ write_blocked_ = true;
+ block_on_next_write_ = false;
+ }
+ if (next_packet_too_large_) {
+ next_packet_too_large_ = false;
+ return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE);
+ }
+ if (always_get_packet_too_large_) {
+ return WriteResult(WRITE_STATUS_ERROR, QUIC_EMSGSIZE);
+ }
+ if (IsWriteBlocked()) {
+ return WriteResult(is_write_blocked_data_buffered_
+ ? WRITE_STATUS_BLOCKED_DATA_BUFFERED
+ : WRITE_STATUS_BLOCKED,
+ 0);
+ }
+
+ if (ShouldWriteFail()) {
+ return WriteResult(WRITE_STATUS_ERROR, 0);
+ }
+
+ last_packet_size_ = packet.length();
+ last_packet_header_ = framer_.header();
+
+ if (!write_pause_time_delta_.IsZero()) {
+ clock_->AdvanceTime(write_pause_time_delta_);
+ }
+ return WriteResult(WRITE_STATUS_OK, last_packet_size_);
+ }
+
+ bool ShouldWriteFail() { return write_should_fail_; }
+
+ bool IsWriteBlocked() const override { return write_blocked_; }
+
+ void SetWriteBlocked() { write_blocked_ = true; }
+
+ void SetWritable() override { write_blocked_ = false; }
+
+ void SetShouldWriteFail() { write_should_fail_ = true; }
+
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& /*peer_address*/) const override {
+ return max_packet_size_;
+ }
+
+ bool SupportsReleaseTime() const { return supports_release_time_; }
+
+ bool IsBatchMode() const override { return is_batch_mode_; }
+
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override {
+ return nullptr;
+ }
+
+ WriteResult Flush() override {
+ if (block_on_next_flush_) {
+ block_on_next_flush_ = false;
+ SetWriteBlocked();
+ return WriteResult(WRITE_STATUS_BLOCKED, /*errno*/ -1);
+ }
+ return WriteResult(WRITE_STATUS_OK, 0);
+ }
+
+ void BlockOnNextFlush() { block_on_next_flush_ = true; }
+
+ void BlockOnNextWrite() { block_on_next_write_ = true; }
+
+ void SimulateNextPacketTooLarge() { next_packet_too_large_ = true; }
+
+ void AlwaysGetPacketTooLarge() { always_get_packet_too_large_ = true; }
+
+ // Sets the amount of time that the writer should before the actual write.
+ void SetWritePauseTimeDelta(QuicTime::Delta delta) {
+ write_pause_time_delta_ = delta;
+ }
+
+ void SetBatchMode(bool new_value) { is_batch_mode_ = new_value; }
+
+ const QuicPacketHeader& header() { return framer_.header(); }
+
+ size_t frame_count() const { return framer_.num_frames(); }
+
+ const std::vector<QuicAckFrame>& ack_frames() const {
+ return framer_.ack_frames();
+ }
+
+ const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return framer_.stop_waiting_frames();
+ }
+
+ const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const {
+ return framer_.connection_close_frames();
+ }
+
+ const std::vector<QuicRstStreamFrame>& rst_stream_frames() const {
+ return framer_.rst_stream_frames();
+ }
+
+ const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const {
+ return framer_.stream_frames();
+ }
+
+ const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const {
+ return framer_.crypto_frames();
+ }
+
+ const std::vector<QuicPingFrame>& ping_frames() const {
+ return framer_.ping_frames();
+ }
+
+ const std::vector<QuicMessageFrame>& message_frames() const {
+ return framer_.message_frames();
+ }
+
+ const std::vector<QuicWindowUpdateFrame>& window_update_frames() const {
+ return framer_.window_update_frames();
+ }
+
+ const std::vector<QuicPaddingFrame>& padding_frames() const {
+ return framer_.padding_frames();
+ }
+
+ const std::vector<QuicPathChallengeFrame>& path_challenge_frames() const {
+ return framer_.path_challenge_frames();
+ }
+
+ const std::vector<QuicPathResponseFrame>& path_response_frames() const {
+ return framer_.path_response_frames();
+ }
+
+ size_t last_packet_size() { return last_packet_size_; }
+
+ const QuicPacketHeader& last_packet_header() const {
+ return last_packet_header_;
+ }
+
+ const QuicVersionNegotiationPacket* version_negotiation_packet() {
+ return framer_.version_negotiation_packet();
+ }
+
+ void set_is_write_blocked_data_buffered(bool buffered) {
+ is_write_blocked_data_buffered_ = buffered;
+ }
+
+ void set_perspective(Perspective perspective) {
+ // We invert perspective here, because the framer needs to parse packets
+ // we send.
+ QuicFramerPeer::SetPerspective(framer_.framer(),
+ InvertPerspective(perspective));
+ }
+
+ // final_bytes_of_last_packet_ returns the last four bytes of the previous
+ // packet as a little-endian, uint32_t. This is intended to be used with a
+ // TaggingEncrypter so that tests can determine which encrypter was used for
+ // a given packet.
+ uint32_t final_bytes_of_last_packet() { return final_bytes_of_last_packet_; }
+
+ // Returns the final bytes of the second to last packet.
+ uint32_t final_bytes_of_previous_packet() {
+ return final_bytes_of_previous_packet_;
+ }
+
+ void use_tagging_decrypter() { use_tagging_decrypter_ = true; }
+
+ uint32_t packets_write_attempts() { return packets_write_attempts_; }
+
+ void Reset() { framer_.Reset(); }
+
+ void SetSupportedVersions(const ParsedQuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
+ void set_max_packet_size(QuicByteCount max_packet_size) {
+ max_packet_size_ = max_packet_size;
+ }
+
+ void set_supports_release_time(bool supports_release_time) {
+ supports_release_time_ = supports_release_time;
+ }
+
+ SimpleQuicFramer* framer() { return &framer_; }
+
+ private:
+ ParsedQuicVersion version_;
+ SimpleQuicFramer framer_;
+ size_t last_packet_size_;
+ QuicPacketHeader last_packet_header_;
+ bool write_blocked_;
+ bool write_should_fail_;
+ bool block_on_next_flush_;
+ bool block_on_next_write_;
+ bool next_packet_too_large_;
+ bool always_get_packet_too_large_;
+ bool is_write_blocked_data_buffered_;
+ bool is_batch_mode_;
+ uint32_t final_bytes_of_last_packet_;
+ uint32_t final_bytes_of_previous_packet_;
+ bool use_tagging_decrypter_;
+ uint32_t packets_write_attempts_;
+ MockClock* clock_;
+ // If non-zero, the clock will pause during WritePacket for this amount of
+ // time.
+ QuicTime::Delta write_pause_time_delta_;
+ QuicByteCount max_packet_size_;
+ bool supports_release_time_;
+};
+
+class TestConnection : public QuicConnection {
+ public:
+ TestConnection(QuicConnectionId connection_id,
+ QuicSocketAddress address,
+ TestConnectionHelper* helper,
+ TestAlarmFactory* alarm_factory,
+ TestPacketWriter* writer,
+ Perspective perspective,
+ ParsedQuicVersion version)
+ : QuicConnection(connection_id,
+ address,
+ helper,
+ alarm_factory,
+ writer,
+ /* owns_writer= */ false,
+ perspective,
+ SupportedVersions(version)),
+ notifier_(nullptr) {
+ writer->set_perspective(perspective);
+ SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullEncrypter>(perspective));
+ SetDataProducer(&producer_);
+ }
+ TestConnection(const TestConnection&) = delete;
+ TestConnection& operator=(const TestConnection&) = delete;
+
+ void SendAck() { QuicConnectionPeer::SendAck(this); }
+
+ void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) {
+ QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
+ }
+
+ void SetLossAlgorithm(LossDetectionInterface* loss_algorithm) {
+ QuicConnectionPeer::SetLossAlgorithm(this, loss_algorithm);
+ }
+
+ void SendPacket(EncryptionLevel level,
+ uint64_t packet_number,
+ std::unique_ptr<QuicPacket> packet,
+ HasRetransmittableData retransmittable,
+ bool has_ack,
+ bool has_pending_frames) {
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ QuicConnectionPeer::GetFramer(this)->EncryptPayload(
+ ENCRYPTION_INITIAL, QuicPacketNumber(packet_number), *packet,
+ buffer, kMaxOutgoingPacketSize);
+ SerializedPacket serialized_packet(
+ QuicPacketNumber(packet_number), PACKET_4BYTE_PACKET_NUMBER, buffer,
+ encrypted_length, has_ack, has_pending_frames);
+ if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
+ serialized_packet.retransmittable_frames.push_back(
+ QuicFrame(QuicStreamFrame()));
+ }
+ OnSerializedPacket(&serialized_packet);
+ }
+
+ QuicConsumedData SaveAndSendStreamData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ size_t total_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ ScopedPacketFlusher flusher(this, NO_ACK);
+ producer_.SaveStreamData(id, iov, iov_count, 0u, total_length);
+ if (notifier_ != nullptr) {
+ return notifier_->WriteOrBufferData(id, total_length, state);
+ }
+ return QuicConnection::SendStreamData(id, total_length, offset, state);
+ }
+
+ QuicConsumedData SendStreamDataWithString(QuicStreamId id,
+ QuicStringPiece data,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ ScopedPacketFlusher flusher(this, NO_ACK);
+ if (id != QuicUtils::GetCryptoStreamId(transport_version()) &&
+ this->encryption_level() == ENCRYPTION_INITIAL) {
+ this->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ }
+ struct iovec iov;
+ MakeIOVector(data, &iov);
+ return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state);
+ }
+
+ QuicConsumedData SendApplicationDataAtLevel(EncryptionLevel encryption_level,
+ QuicStreamId id,
+ QuicStringPiece data,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ ScopedPacketFlusher flusher(this, NO_ACK);
+ DCHECK(encryption_level >= ENCRYPTION_ZERO_RTT);
+ SetEncrypter(encryption_level, QuicMakeUnique<TaggingEncrypter>(0x01));
+ SetDefaultEncryptionLevel(encryption_level);
+ struct iovec iov;
+ MakeIOVector(data, &iov);
+ return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state);
+ }
+
+ QuicConsumedData SendStreamData3() {
+ return SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, transport_version()), "food", 0,
+ NO_FIN);
+ }
+
+ QuicConsumedData SendStreamData5() {
+ return SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(2, transport_version()), "food2", 0,
+ NO_FIN);
+ }
+
+ // Ensures the connection can write stream data before writing.
+ QuicConsumedData EnsureWritableAndSendStreamData5() {
+ EXPECT_TRUE(CanWriteStreamData());
+ return SendStreamData5();
+ }
+
+ // The crypto stream has special semantics so that it is not blocked by a
+ // congestion window limitation, and also so that it gets put into a separate
+ // packet (so that it is easier to reason about a crypto frame not being
+ // split needlessly across packet boundaries). As a result, we have separate
+ // tests for some cases for this stream.
+ QuicConsumedData SendCryptoStreamData() {
+ QuicStreamOffset offset = 0;
+ QuicStringPiece data("chlo");
+ if (!QuicVersionUsesCryptoFrames(transport_version())) {
+ return SendStreamDataWithString(
+ QuicUtils::GetCryptoStreamId(transport_version()), data, offset,
+ NO_FIN);
+ }
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, offset, data);
+ size_t bytes_written;
+ if (notifier_) {
+ bytes_written =
+ notifier_->WriteCryptoData(ENCRYPTION_INITIAL, data.length(), offset);
+ } else {
+ bytes_written = QuicConnection::SendCryptoData(ENCRYPTION_INITIAL,
+ data.length(), offset);
+ }
+ return QuicConsumedData(bytes_written, /*fin_consumed*/ false);
+ }
+
+ void set_version(ParsedQuicVersion version) {
+ QuicConnectionPeer::GetFramer(this)->set_version(version);
+ }
+
+ void SetSupportedVersions(const ParsedQuicVersionVector& versions) {
+ QuicConnectionPeer::GetFramer(this)->SetSupportedVersions(versions);
+ QuicConnectionPeer::SetNoVersionNegotiation(this, versions.size() == 1);
+ writer()->SetSupportedVersions(versions);
+ }
+
+ void set_perspective(Perspective perspective) {
+ writer()->set_perspective(perspective);
+ QuicConnectionPeer::SetPerspective(this, perspective);
+ }
+
+ // Enable path MTU discovery. Assumes that the test is performed from the
+ // client perspective and the higher value of MTU target is used.
+ void EnablePathMtuDiscovery(MockSendAlgorithm* send_algorithm) {
+ ASSERT_EQ(Perspective::IS_CLIENT, perspective());
+
+ QuicConfig config;
+ QuicTagVector connection_options;
+ connection_options.push_back(kMTUH);
+ config.SetConnectionOptionsToSend(connection_options);
+ EXPECT_CALL(*send_algorithm, SetFromConfig(_, _));
+ SetFromConfig(config);
+
+ // Normally, the pacing would be disabled in the test, but calling
+ // SetFromConfig enables it. Set nearly-infinite bandwidth to make the
+ // pacing algorithm work.
+ EXPECT_CALL(*send_algorithm, PacingRate(_))
+ .WillRepeatedly(Return(QuicBandwidth::Infinite()));
+ }
+
+ TestAlarmFactory::TestAlarm* GetAckAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetAckAlarm(this));
+ }
+
+ TestAlarmFactory::TestAlarm* GetPingAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetPingAlarm(this));
+ }
+
+ TestAlarmFactory::TestAlarm* GetRetransmissionAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetRetransmissionAlarm(this));
+ }
+
+ TestAlarmFactory::TestAlarm* GetSendAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetSendAlarm(this));
+ }
+
+ TestAlarmFactory::TestAlarm* GetTimeoutAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetTimeoutAlarm(this));
+ }
+
+ TestAlarmFactory::TestAlarm* GetMtuDiscoveryAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetMtuDiscoveryAlarm(this));
+ }
+
+ TestAlarmFactory::TestAlarm* GetPathDegradingAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetPathDegradingAlarm(this));
+ }
+
+ TestAlarmFactory::TestAlarm* GetProcessUndecryptablePacketsAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(this));
+ }
+
+ void SetMaxTailLossProbes(size_t max_tail_loss_probes) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicConnectionPeer::GetSentPacketManager(this), max_tail_loss_probes);
+ }
+
+ QuicByteCount GetBytesInFlight() {
+ return QuicSentPacketManagerPeer::GetBytesInFlight(
+ QuicConnectionPeer::GetSentPacketManager(this));
+ }
+
+ void set_notifier(SimpleSessionNotifier* notifier) { notifier_ = notifier; }
+
+ void ReturnEffectivePeerAddressForNextPacket(const QuicSocketAddress& addr) {
+ next_effective_peer_addr_ = QuicMakeUnique<QuicSocketAddress>(addr);
+ }
+
+ using QuicConnection::active_effective_peer_migration_type;
+ using QuicConnection::IsCurrentPacketConnectivityProbing;
+ using QuicConnection::SelectMutualVersion;
+ using QuicConnection::SendProbingRetransmissions;
+ using QuicConnection::set_defer_send_in_response_to_packets;
+
+ protected:
+ QuicSocketAddress GetEffectivePeerAddressFromCurrentPacket() const override {
+ if (next_effective_peer_addr_) {
+ return *std::move(next_effective_peer_addr_);
+ }
+ return QuicConnection::GetEffectivePeerAddressFromCurrentPacket();
+ }
+
+ private:
+ TestPacketWriter* writer() {
+ return static_cast<TestPacketWriter*>(QuicConnection::writer());
+ }
+
+ SimpleDataProducer producer_;
+
+ SimpleSessionNotifier* notifier_;
+
+ std::unique_ptr<QuicSocketAddress> next_effective_peer_addr_;
+};
+
+enum class AckResponse { kDefer, kImmediate };
+
+// Run tests with combinations of {ParsedQuicVersion, AckResponse}.
+struct TestParams {
+ TestParams(ParsedQuicVersion version,
+ AckResponse ack_response,
+ bool no_stop_waiting)
+ : version(version),
+ ack_response(ack_response),
+ no_stop_waiting(no_stop_waiting) {}
+
+ friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+ os << "{ client_version: " << ParsedQuicVersionToString(p.version)
+ << " ack_response: "
+ << (p.ack_response == AckResponse::kDefer ? "defer" : "immediate")
+ << " no_stop_waiting: " << p.no_stop_waiting << " }";
+ return os;
+ }
+
+ ParsedQuicVersion version;
+ AckResponse ack_response;
+ bool no_stop_waiting;
+};
+
+// Constructs various test permutations.
+std::vector<TestParams> GetTestParams() {
+ QuicFlagSaver flags;
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ std::vector<TestParams> params;
+ ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
+ for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+ for (AckResponse ack_response :
+ {AckResponse::kDefer, AckResponse::kImmediate}) {
+ for (bool no_stop_waiting : {true, false}) {
+ // After version 43, never use STOP_WAITING.
+ if (all_supported_versions[i].transport_version <= QUIC_VERSION_43 ||
+ no_stop_waiting) {
+ params.push_back(TestParams(all_supported_versions[i], ack_response,
+ no_stop_waiting));
+ }
+ }
+ }
+ }
+ return params;
+}
+
+class QuicConnectionTest : public QuicTestWithParam<TestParams> {
+ protected:
+ QuicConnectionTest()
+ : connection_id_(TestConnectionId()),
+ framer_(SupportedVersions(version()),
+ QuicTime::Zero(),
+ Perspective::IS_CLIENT,
+ connection_id_.length()),
+ send_algorithm_(new StrictMock<MockSendAlgorithm>),
+ loss_algorithm_(new MockLossAlgorithm()),
+ helper_(new TestConnectionHelper(&clock_, &random_generator_)),
+ alarm_factory_(new TestAlarmFactory()),
+ peer_framer_(SupportedVersions(version()),
+ QuicTime::Zero(),
+ Perspective::IS_SERVER,
+ connection_id_.length()),
+ peer_creator_(connection_id_,
+ &peer_framer_,
+ /*delegate=*/nullptr),
+ writer_(new TestPacketWriter(version(), &clock_)),
+ connection_(connection_id_,
+ kPeerAddress,
+ helper_.get(),
+ alarm_factory_.get(),
+ writer_.get(),
+ Perspective::IS_CLIENT,
+ version()),
+ creator_(QuicConnectionPeer::GetPacketCreator(&connection_)),
+ generator_(QuicConnectionPeer::GetPacketGenerator(&connection_)),
+ manager_(QuicConnectionPeer::GetSentPacketManager(&connection_)),
+ frame1_(QuicUtils::GetCryptoStreamId(version().transport_version),
+ false,
+ 0,
+ QuicStringPiece(data1)),
+ frame2_(QuicUtils::GetCryptoStreamId(version().transport_version),
+ false,
+ 3,
+ QuicStringPiece(data2)),
+ packet_number_length_(PACKET_4BYTE_PACKET_NUMBER),
+ connection_id_included_(CONNECTION_ID_PRESENT),
+ notifier_(&connection_) {
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ connection_.set_defer_send_in_response_to_packets(GetParam().ack_response ==
+ AckResponse::kDefer);
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ QuicConnectionPeer::GetFramer(&connection_), connection_id_);
+ if (version().transport_version > QUIC_VERSION_43) {
+ EXPECT_TRUE(QuicConnectionPeer::GetNoStopWaitingFrames(&connection_));
+ } else {
+ QuicConnectionPeer::SetNoStopWaitingFrames(&connection_,
+ GetParam().no_stop_waiting);
+ }
+ connection_.set_visitor(&visitor_);
+ if (connection_.session_decides_what_to_write()) {
+ connection_.SetSessionNotifier(&notifier_);
+ connection_.set_notifier(&notifier_);
+ }
+ connection_.SetSendAlgorithm(send_algorithm_);
+ connection_.SetLossAlgorithm(loss_algorithm_.get());
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillRepeatedly(Return(kDefaultTCPMSS));
+ EXPECT_CALL(*send_algorithm_, PacingRate(_))
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, HasReliableBandwidthEstimate())
+ .Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, BandwidthEstimate())
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(AnyNumber());
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber());
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
+ if (connection_.session_decides_what_to_write()) {
+ EXPECT_CALL(visitor_, OnCanWrite())
+ .WillRepeatedly(
+ Invoke(&notifier_, &SimpleSessionNotifier::OnCanWrite));
+ } else {
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber());
+ }
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber());
+ EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(AnyNumber());
+ EXPECT_CALL(visitor_, OnForwardProgressConfirmed()).Times(AnyNumber());
+
+ EXPECT_CALL(*loss_algorithm_, GetLossTimeout())
+ .WillRepeatedly(Return(QuicTime::Zero()));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .Times(AnyNumber());
+
+ if (connection_.version().KnowsWhichDecrypterToUse()) {
+ connection_.InstallDecrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT));
+ }
+ }
+
+ QuicConnectionTest(const QuicConnectionTest&) = delete;
+ QuicConnectionTest& operator=(const QuicConnectionTest&) = delete;
+
+ ParsedQuicVersion version() { return GetParam().version; }
+
+ QuicAckFrame* outgoing_ack() {
+ QuicFrame ack_frame = QuicConnectionPeer::GetUpdatedAckFrame(&connection_);
+ ack_ = *ack_frame.ack_frame;
+ return &ack_;
+ }
+
+ QuicStopWaitingFrame* stop_waiting() {
+ QuicConnectionPeer::PopulateStopWaitingFrame(&connection_, &stop_waiting_);
+ return &stop_waiting_;
+ }
+
+ QuicPacketNumber least_unacked() {
+ if (writer_->stop_waiting_frames().empty()) {
+ return QuicPacketNumber();
+ }
+ return writer_->stop_waiting_frames()[0].least_unacked;
+ }
+
+ void use_tagging_decrypter() { writer_->use_tagging_decrypter(); }
+
+ void SetDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter) {
+ if (connection_.version().KnowsWhichDecrypterToUse()) {
+ connection_.InstallDecrypter(level, std::move(decrypter));
+ connection_.RemoveDecrypter(ENCRYPTION_INITIAL);
+ } else {
+ connection_.SetDecrypter(level, std::move(decrypter));
+ }
+ }
+
+ void ProcessPacket(uint64_t number) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacket(number);
+ if (connection_.GetSendAlarm()->IsSet()) {
+ connection_.GetSendAlarm()->Fire();
+ }
+ }
+
+ void ProcessReceivedPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) {
+ connection_.ProcessUdpPacket(self_address, peer_address, packet);
+ if (connection_.GetSendAlarm()->IsSet()) {
+ connection_.GetSendAlarm()->Fire();
+ }
+ }
+
+ void ProcessFramePacket(QuicFrame frame) {
+ ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress);
+ }
+
+ void ProcessFramePacketWithAddresses(QuicFrame frame,
+ QuicSocketAddress self_address,
+ QuicSocketAddress peer_address) {
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ QuicPacketCreatorPeer::SetSendVersionInPacket(
+ &peer_creator_, connection_.perspective() == Perspective::IS_SERVER);
+ if (QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_) >
+ ENCRYPTION_INITIAL) {
+ // Set peer_framer_'s corresponding encrypter.
+ peer_creator_.SetEncrypter(
+ QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_),
+ QuicMakeUnique<NullEncrypter>(peer_framer_.perspective()));
+ }
+
+ char buffer[kMaxOutgoingPacketSize];
+ SerializedPacket serialized_packet =
+ QuicPacketCreatorPeer::SerializeAllFrames(
+ &peer_creator_, frames, buffer, kMaxOutgoingPacketSize);
+ connection_.ProcessUdpPacket(
+ self_address, peer_address,
+ QuicReceivedPacket(serialized_packet.encrypted_buffer,
+ serialized_packet.encrypted_length, clock_.Now()));
+ if (connection_.GetSendAlarm()->IsSet()) {
+ connection_.GetSendAlarm()->Fire();
+ }
+ }
+
+ // Bypassing the packet creator is unrealistic, but allows us to process
+ // packets the QuicPacketCreator won't allow us to create.
+ void ForceProcessFramePacket(QuicFrame frame) {
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ bool send_version = connection_.perspective() == Perspective::IS_SERVER;
+ if (connection_.version().KnowsWhichDecrypterToUse()) {
+ send_version = true;
+ }
+ QuicPacketCreatorPeer::SetSendVersionInPacket(&peer_creator_, send_version);
+ QuicPacketHeader header;
+ QuicPacketCreatorPeer::FillPacketHeader(&peer_creator_, &header);
+ char encrypted_buffer[kMaxOutgoingPacketSize];
+ size_t length = peer_framer_.BuildDataPacket(
+ header, frames, encrypted_buffer, kMaxOutgoingPacketSize,
+ ENCRYPTION_INITIAL);
+ DCHECK_GT(length, 0u);
+
+ const size_t encrypted_length = peer_framer_.EncryptInPlace(
+ ENCRYPTION_INITIAL, header.packet_number,
+ GetStartOfEncryptedData(peer_framer_.version().transport_version,
+ header),
+ length, kMaxOutgoingPacketSize, encrypted_buffer);
+ DCHECK_GT(encrypted_length, 0u);
+
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(encrypted_buffer, encrypted_length, clock_.Now()));
+ }
+
+ size_t ProcessFramePacketAtLevel(uint64_t number,
+ QuicFrame frame,
+ EncryptionLevel level) {
+ QuicPacketHeader header;
+ header.destination_connection_id = connection_id_;
+ header.packet_number_length = packet_number_length_;
+ header.destination_connection_id_included = connection_id_included_;
+ if (peer_framer_.transport_version() > QUIC_VERSION_43 &&
+ peer_framer_.perspective() == Perspective::IS_SERVER) {
+ header.destination_connection_id_included = CONNECTION_ID_ABSENT;
+ }
+ if (level == ENCRYPTION_INITIAL &&
+ peer_framer_.version().KnowsWhichDecrypterToUse()) {
+ header.version_flag = true;
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ if (peer_framer_.perspective() == Perspective::IS_SERVER) {
+ header.source_connection_id = connection_id_;
+ header.source_connection_id_included = CONNECTION_ID_PRESENT;
+ }
+ }
+ header.packet_number = QuicPacketNumber(number);
+ QuicFrames frames;
+ frames.push_back(frame);
+ std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
+ // Set the correct encryption level and encrypter on peer_creator and
+ // peer_framer, respectively.
+ peer_creator_.set_encryption_level(level);
+ if (QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_) >
+ ENCRYPTION_INITIAL) {
+ peer_framer_.SetEncrypter(
+ QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_),
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+ // Set the corresponding decrypter.
+ if (connection_.version().KnowsWhichDecrypterToUse()) {
+ connection_.InstallDecrypter(
+ QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_),
+ QuicMakeUnique<StrictTaggingDecrypter>(0x01));
+ connection_.RemoveDecrypter(ENCRYPTION_INITIAL);
+ } else {
+ connection_.SetDecrypter(
+ QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_),
+ QuicMakeUnique<StrictTaggingDecrypter>(0x01));
+ }
+ }
+
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ peer_framer_.EncryptPayload(level, QuicPacketNumber(number), *packet,
+ buffer, kMaxOutgoingPacketSize);
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false));
+ if (connection_.GetSendAlarm()->IsSet()) {
+ connection_.GetSendAlarm()->Fire();
+ }
+ return encrypted_length;
+ }
+
+ size_t ProcessDataPacket(uint64_t number) {
+ return ProcessDataPacketAtLevel(number, false, ENCRYPTION_INITIAL);
+ }
+
+ size_t ProcessDataPacket(QuicPacketNumber packet_number) {
+ return ProcessDataPacketAtLevel(packet_number, false, ENCRYPTION_INITIAL);
+ }
+
+ size_t ProcessDataPacketAtLevel(QuicPacketNumber packet_number,
+ bool has_stop_waiting,
+ EncryptionLevel level) {
+ return ProcessDataPacketAtLevel(packet_number.ToUint64(), has_stop_waiting,
+ level);
+ }
+
+ size_t ProcessDataPacketAtLevel(uint64_t number,
+ bool has_stop_waiting,
+ EncryptionLevel level) {
+ std::unique_ptr<QuicPacket> packet(
+ ConstructDataPacket(number, has_stop_waiting, level));
+ char buffer[kMaxOutgoingPacketSize];
+ peer_creator_.set_encryption_level(level);
+ size_t encrypted_length =
+ peer_framer_.EncryptPayload(level, QuicPacketNumber(number), *packet,
+ buffer, kMaxOutgoingPacketSize);
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false));
+ if (connection_.GetSendAlarm()->IsSet()) {
+ connection_.GetSendAlarm()->Fire();
+ }
+ return encrypted_length;
+ }
+
+ void ProcessClosePacket(uint64_t number) {
+ std::unique_ptr<QuicPacket> packet(ConstructClosePacket(number));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length = peer_framer_.EncryptPayload(
+ ENCRYPTION_INITIAL, QuicPacketNumber(number), *packet, buffer,
+ kMaxOutgoingPacketSize);
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
+ }
+
+ QuicByteCount SendStreamDataToPeer(QuicStreamId id,
+ QuicStringPiece data,
+ QuicStreamOffset offset,
+ StreamSendingState state,
+ QuicPacketNumber* last_packet) {
+ QuicByteCount packet_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(SaveArg<3>(&packet_size));
+ connection_.SendStreamDataWithString(id, data, offset, state);
+ if (last_packet != nullptr) {
+ *last_packet = creator_->packet_number();
+ }
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AnyNumber());
+ return packet_size;
+ }
+
+ void SendAckPacketToPeer() {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ {
+ QuicConnection::ScopedPacketFlusher flusher(&connection_,
+ QuicConnection::NO_ACK);
+ connection_.SendAck();
+ }
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AnyNumber());
+ }
+
+ void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ if (connection_.session_decides_what_to_write()) {
+ notifier_.WriteOrBufferRstStream(id, error, bytes_written);
+ connection_.OnStreamReset(id, error);
+ return;
+ }
+ std::unique_ptr<QuicRstStreamFrame> rst_stream =
+ QuicMakeUnique<QuicRstStreamFrame>(1, id, error, bytes_written);
+ if (connection_.SendControlFrame(QuicFrame(rst_stream.get()))) {
+ rst_stream.release();
+ }
+ connection_.OnStreamReset(id, error);
+ }
+
+ void SendPing() {
+ if (connection_.session_decides_what_to_write()) {
+ notifier_.WriteOrBufferPing();
+ } else {
+ connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+ }
+ }
+
+ void ProcessAckPacket(uint64_t packet_number, QuicAckFrame* frame) {
+ if (packet_number > 1) {
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, packet_number - 1);
+ } else {
+ QuicPacketCreatorPeer::ClearPacketNumber(&peer_creator_);
+ }
+ ProcessFramePacket(QuicFrame(frame));
+ }
+
+ void ProcessAckPacket(QuicAckFrame* frame) {
+ ProcessFramePacket(QuicFrame(frame));
+ }
+
+ void ProcessStopWaitingPacket(QuicStopWaitingFrame frame) {
+ ProcessFramePacket(QuicFrame(frame));
+ }
+
+ size_t ProcessStopWaitingPacketAtLevel(uint64_t number,
+ QuicStopWaitingFrame frame,
+ EncryptionLevel level) {
+ return ProcessFramePacketAtLevel(number, QuicFrame(frame),
+ ENCRYPTION_ZERO_RTT);
+ }
+
+ void ProcessGoAwayPacket(QuicGoAwayFrame* frame) {
+ ProcessFramePacket(QuicFrame(frame));
+ }
+
+ bool IsMissing(uint64_t number) {
+ return IsAwaitingPacket(*outgoing_ack(), QuicPacketNumber(number),
+ QuicPacketNumber());
+ }
+
+ std::unique_ptr<QuicPacket> ConstructPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ auto packet = BuildUnsizedDataPacket(&peer_framer_, header, frames);
+ EXPECT_NE(nullptr, packet.get());
+ return packet;
+ }
+
+ std::unique_ptr<QuicPacket> ConstructDataPacket(uint64_t number,
+ bool has_stop_waiting,
+ EncryptionLevel level) {
+ QuicPacketHeader header;
+ if (peer_framer_.transport_version() > QUIC_VERSION_43 &&
+ level < ENCRYPTION_FORWARD_SECURE) {
+ // Set long header type accordingly.
+ header.version_flag = true;
+ header.long_packet_type = EncryptionlevelToLongHeaderType(level);
+ if (QuicVersionHasLongHeaderLengths(
+ peer_framer_.version().transport_version)) {
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ if (header.long_packet_type == INITIAL) {
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ }
+ }
+ }
+ // Set connection_id to peer's in memory representation as this data packet
+ // is created by peer_framer.
+ header.destination_connection_id = connection_id_;
+ header.packet_number_length = packet_number_length_;
+ header.destination_connection_id_included = connection_id_included_;
+ if (peer_framer_.transport_version() > QUIC_VERSION_43 &&
+ peer_framer_.perspective() == Perspective::IS_SERVER) {
+ 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;
+ if (GetParam().version.handshake_protocol == PROTOCOL_QUIC_CRYPTO &&
+ header.long_packet_type == ZERO_RTT_PROTECTED) {
+ header.nonce = &kTestDiversificationNonce;
+ }
+ }
+ }
+ header.packet_number = QuicPacketNumber(number);
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1_));
+ if (has_stop_waiting) {
+ frames.push_back(QuicFrame(stop_waiting_));
+ }
+ return ConstructPacket(header, frames);
+ }
+
+ OwningSerializedPacketPointer ConstructProbingPacket() {
+ if (version().transport_version == QUIC_VERSION_99) {
+ QuicPathFrameBuffer payload = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}};
+ return QuicPacketCreatorPeer::
+ SerializePathChallengeConnectivityProbingPacket(&peer_creator_,
+ &payload);
+ }
+ return QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
+ &peer_creator_);
+ }
+
+ std::unique_ptr<QuicPacket> ConstructClosePacket(uint64_t number) {
+ QuicPacketHeader header;
+ // Set connection_id to peer's in memory representation as this connection
+ // close packet is created by peer_framer.
+ header.destination_connection_id = connection_id_;
+ header.packet_number = QuicPacketNumber(number);
+ if (peer_framer_.transport_version() > QUIC_VERSION_43 &&
+ peer_framer_.perspective() == Perspective::IS_SERVER) {
+ header.destination_connection_id_included = CONNECTION_ID_ABSENT;
+ }
+
+ QuicConnectionCloseFrame qccf(QUIC_PEER_GOING_AWAY);
+ if (peer_framer_.transport_version() == QUIC_VERSION_99) {
+ // Default close-type is Google QUIC. If doing IETF/V99 then
+ // set close type to be IETF CC/T.
+ qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&qccf));
+ return ConstructPacket(header, frames);
+ }
+
+ QuicTime::Delta DefaultRetransmissionTime() {
+ return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
+ }
+
+ QuicTime::Delta DefaultDelayedAckTime() {
+ return QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+ }
+
+ const QuicStopWaitingFrame InitStopWaitingFrame(uint64_t least_unacked) {
+ QuicStopWaitingFrame frame;
+ frame.least_unacked = QuicPacketNumber(least_unacked);
+ return frame;
+ }
+
+ // Construct a ack_frame that acks all packet numbers between 1 and
+ // |largest_acked|, except |missing|.
+ // REQUIRES: 1 <= |missing| < |largest_acked|
+ QuicAckFrame ConstructAckFrame(uint64_t largest_acked, uint64_t missing) {
+ return ConstructAckFrame(QuicPacketNumber(largest_acked),
+ QuicPacketNumber(missing));
+ }
+
+ QuicAckFrame ConstructAckFrame(QuicPacketNumber largest_acked,
+ QuicPacketNumber missing) {
+ if (missing == QuicPacketNumber(1)) {
+ return InitAckFrame({{missing + 1, largest_acked + 1}});
+ }
+ return InitAckFrame(
+ {{QuicPacketNumber(1), missing}, {missing + 1, largest_acked + 1}});
+ }
+
+ // Undo nacking a packet within the frame.
+ void AckPacket(QuicPacketNumber arrived, QuicAckFrame* frame) {
+ EXPECT_FALSE(frame->packets.Contains(arrived));
+ frame->packets.Add(arrived);
+ }
+
+ void TriggerConnectionClose() {
+ // Send an erroneous packet to close the connection.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ // Triggers a connection by receiving ACK of unsent packet.
+ QuicAckFrame frame = InitAckFrame(10000);
+ ProcessAckPacket(1, &frame);
+
+ EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
+ nullptr);
+ }
+
+ void BlockOnNextWrite() {
+ writer_->BlockOnNextWrite();
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1));
+ }
+
+ void SimulateNextPacketTooLarge() { writer_->SimulateNextPacketTooLarge(); }
+
+ void AlwaysGetPacketTooLarge() { writer_->AlwaysGetPacketTooLarge(); }
+
+ void SetWritePauseTimeDelta(QuicTime::Delta delta) {
+ writer_->SetWritePauseTimeDelta(delta);
+ }
+
+ void CongestionBlockWrites() {
+ EXPECT_CALL(*send_algorithm_, CanSend(_))
+ .WillRepeatedly(testing::Return(false));
+ }
+
+ void CongestionUnblockWrites() {
+ EXPECT_CALL(*send_algorithm_, CanSend(_))
+ .WillRepeatedly(testing::Return(true));
+ }
+
+ void set_perspective(Perspective perspective) {
+ connection_.set_perspective(perspective);
+ if (perspective == Perspective::IS_SERVER) {
+ connection_.set_can_truncate_connection_ids(true);
+ }
+ QuicFramerPeer::SetPerspective(&peer_framer_,
+ InvertPerspective(perspective));
+ }
+
+ void set_packets_between_probes_base(
+ const QuicPacketCount packets_between_probes_base) {
+ QuicConnectionPeer::SetPacketsBetweenMtuProbes(&connection_,
+ packets_between_probes_base);
+ QuicConnectionPeer::SetNextMtuProbeAt(
+ &connection_, QuicPacketNumber(packets_between_probes_base));
+ }
+
+ bool IsDefaultTestConfiguration() {
+ TestParams p = GetParam();
+ return p.ack_response == AckResponse::kImmediate &&
+ p.version == AllSupportedVersions()[0] && p.no_stop_waiting;
+ }
+
+ QuicConnectionId connection_id_;
+ QuicFramer framer_;
+
+ MockSendAlgorithm* send_algorithm_;
+ std::unique_ptr<MockLossAlgorithm> loss_algorithm_;
+ MockClock clock_;
+ MockRandom random_generator_;
+ SimpleBufferAllocator buffer_allocator_;
+ std::unique_ptr<TestConnectionHelper> helper_;
+ std::unique_ptr<TestAlarmFactory> alarm_factory_;
+ QuicFramer peer_framer_;
+ QuicPacketCreator peer_creator_;
+ std::unique_ptr<TestPacketWriter> writer_;
+ TestConnection connection_;
+ QuicPacketCreator* creator_;
+ QuicPacketGenerator* generator_;
+ QuicSentPacketManager* manager_;
+ StrictMock<MockQuicConnectionVisitor> visitor_;
+
+ QuicStreamFrame frame1_;
+ QuicStreamFrame frame2_;
+ QuicAckFrame ack_;
+ QuicStopWaitingFrame stop_waiting_;
+ QuicPacketNumberLength packet_number_length_;
+ QuicConnectionIdIncluded connection_id_included_;
+
+ SimpleSessionNotifier notifier_;
+};
+
+// Run all end to end tests with all supported versions.
+INSTANTIATE_TEST_SUITE_P(SupportedVersion,
+ QuicConnectionTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicConnectionTest, SelfAddressChangeAtClient) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+ EXPECT_TRUE(connection_.connected());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_));
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ // Cause change in self_address.
+ QuicIpAddress host;
+ host.FromString("1.1.1.1");
+ QuicSocketAddress self_address(host, 123);
+ EXPECT_CALL(visitor_, OnStreamFrame(_));
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address,
+ kPeerAddress);
+ EXPECT_TRUE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, SelfAddressChangeAtServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+ EXPECT_TRUE(connection_.connected());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_));
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ // Cause change in self_address.
+ QuicIpAddress host;
+ host.FromString("1.1.1.1");
+ QuicSocketAddress self_address(host, 123);
+ EXPECT_CALL(visitor_, AllowSelfAddressChange()).WillOnce(Return(false));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_ERROR_MIGRATING_ADDRESS, _, _));
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address,
+ kPeerAddress);
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, AllowSelfAddressChangeToMappedIpv4AddressAtServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+ EXPECT_TRUE(connection_.connected());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(3);
+ QuicIpAddress host;
+ host.FromString("1.1.1.1");
+ QuicSocketAddress self_address1(host, 443);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address1,
+ kPeerAddress);
+ // Cause self_address change to mapped Ipv4 address.
+ QuicIpAddress host2;
+ host2.FromString(
+ QuicStrCat("::ffff:", connection_.self_address().host().ToString()));
+ QuicSocketAddress self_address2(host2, connection_.self_address().port());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address2,
+ kPeerAddress);
+ EXPECT_TRUE(connection_.connected());
+ // self_address change back to Ipv4 address.
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), self_address1,
+ kPeerAddress);
+ EXPECT_TRUE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, ClientAddressChangeAndPacketReordered) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5);
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ const QuicSocketAddress kNewPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(),
+ /*port=*/23456);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kNewPeerAddress);
+ EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
+
+ // Decrease packet number to simulate out-of-order packets.
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4);
+ // This is an old packet, do not migrate.
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, PeerAddressChangeAtServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+ EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ // Process another packet with a different peer address on server side will
+ // start connection migration.
+ const QuicSocketAddress kNewPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kNewPeerAddress);
+ EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+ EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is different from direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ const QuicSocketAddress kEffectivePeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/43210);
+ connection_.ReturnEffectivePeerAddressForNextPacket(kEffectivePeerAddress);
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kEffectivePeerAddress, connection_.effective_peer_address());
+
+ // Process another packet with the same direct peer address and different
+ // effective peer address on server side will start connection migration.
+ const QuicSocketAddress kNewEffectivePeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/54321);
+ connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress);
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address());
+
+ // Process another packet with a different direct peer address and the same
+ // effective peer address on server side will not start connection migration.
+ const QuicSocketAddress kNewPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+ connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress);
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ // ack_frame is used to complete the migration started by the last packet, we
+ // need to make sure a new migration does not start after the previous one is
+ // completed.
+ QuicAckFrame ack_frame = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _));
+ ProcessFramePacketWithAddresses(QuicFrame(&ack_frame), kSelfAddress,
+ kNewPeerAddress);
+ EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address());
+
+ // Process another packet with different direct peer address and different
+ // effective peer address on server side will start connection migration.
+ const QuicSocketAddress kNewerEffectivePeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/65432);
+ const QuicSocketAddress kFinalPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/34567);
+ connection_.ReturnEffectivePeerAddressForNextPacket(
+ kNewerEffectivePeerAddress);
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kFinalPeerAddress);
+ EXPECT_EQ(kFinalPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewerEffectivePeerAddress, connection_.effective_peer_address());
+ EXPECT_EQ(PORT_CHANGE, connection_.active_effective_peer_migration_type());
+
+ // While the previous migration is ongoing, process another packet with the
+ // same direct peer address and different effective peer address on server
+ // side will start a new connection migration.
+ const QuicSocketAddress kNewestEffectivePeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/65430);
+ connection_.ReturnEffectivePeerAddressForNextPacket(
+ kNewestEffectivePeerAddress);
+ EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnConnectionMigration()).Times(1);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kFinalPeerAddress);
+ EXPECT_EQ(kFinalPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewestEffectivePeerAddress, connection_.effective_peer_address());
+ EXPECT_EQ(IPV6_TO_IPV4_CHANGE,
+ connection_.active_effective_peer_migration_type());
+}
+
+TEST_P(QuicConnectionTest, ReceivePaddedPingAtServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+ EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(0);
+
+ // Process a padded PING or PATH CHALLENGE packet with no peer address change
+ // on server side will be ignored.
+ OwningSerializedPacketPointer probing_packet;
+ if (version().transport_version == QUIC_VERSION_99) {
+ QuicPathFrameBuffer payload = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}};
+ probing_packet =
+ QuicPacketCreatorPeer::SerializePathChallengeConnectivityProbingPacket(
+ &peer_creator_, &payload);
+ } else {
+ probing_packet = QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
+ &peer_creator_);
+ }
+ std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket(
+ QuicEncryptedPacket(probing_packet->encrypted_buffer,
+ probing_packet->encrypted_length),
+ clock_.Now()));
+
+ uint64_t num_probing_received =
+ connection_.GetStats().num_connectivity_probing_received;
+ ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received);
+
+ EXPECT_EQ(num_probing_received,
+ connection_.GetStats().num_connectivity_probing_received);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, WriteOutOfOrderQueuedPackets) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (!IsDefaultTestConfiguration() ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+
+ set_perspective(Perspective::IS_CLIENT);
+
+ BlockOnNextWrite();
+
+ QuicStreamId stream_id = 2;
+ connection_.SendStreamDataWithString(stream_id, "foo", 0, NO_FIN);
+
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ writer_->SetWritable();
+ connection_.SendConnectivityProbingPacket(writer_.get(),
+ connection_.peer_address());
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INTERNAL_ERROR,
+ "Packet written out of order.",
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_QUIC_BUG(connection_.OnCanWrite(),
+ "Attempt to write packet:1 after:2");
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, DiscardQueuedPacketsAfterConnectionClose) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Regression test for b/74073386.
+ {
+ InSequence seq;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1);
+ }
+
+ set_perspective(Perspective::IS_CLIENT);
+
+ writer_->SimulateNextPacketTooLarge();
+
+ // This packet write should fail, which should cause the connection to close
+ // after sending a connection close packet, then the failed packet should be
+ // queued.
+ connection_.SendStreamDataWithString(/*id=*/2, "foo", 0, NO_FIN);
+
+ EXPECT_FALSE(connection_.connected());
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ EXPECT_EQ(0u, connection_.GetStats().packets_discarded);
+ connection_.OnCanWrite();
+ EXPECT_EQ(1u, connection_.GetStats().packets_discarded);
+}
+
+TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+ EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1);
+
+ // Process a padded PING packet from a new peer address on server side
+ // is effectively receiving a connectivity probing.
+ const QuicSocketAddress kNewPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+
+ OwningSerializedPacketPointer probing_packet = ConstructProbingPacket();
+ std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket(
+ QuicEncryptedPacket(probing_packet->encrypted_buffer,
+ probing_packet->encrypted_length),
+ clock_.Now()));
+
+ uint64_t num_probing_received =
+ connection_.GetStats().num_connectivity_probing_received;
+ ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
+
+ EXPECT_EQ(num_probing_received + 1,
+ connection_.GetStats().num_connectivity_probing_received);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ // Process another packet with the old peer address on server side will not
+ // start peer migration.
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, ReceiveReorderedConnectivityProbingAtServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+ EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5);
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ // Decrease packet number to simulate out-of-order packets.
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4);
+
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1);
+
+ // Process a padded PING packet from a new peer address on server side
+ // is effectively receiving a connectivity probing, even if a newer packet has
+ // been received before this one.
+ const QuicSocketAddress kNewPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+
+ OwningSerializedPacketPointer probing_packet = ConstructProbingPacket();
+ std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket(
+ QuicEncryptedPacket(probing_packet->encrypted_buffer,
+ probing_packet->encrypted_length),
+ clock_.Now()));
+
+ uint64_t num_probing_received =
+ connection_.GetStats().num_connectivity_probing_received;
+ ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
+
+ EXPECT_EQ(num_probing_received + 1,
+ connection_.GetStats().num_connectivity_probing_received);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, MigrateAfterProbingAtServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+ EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1);
+
+ // Process a padded PING packet from a new peer address on server side
+ // is effectively receiving a connectivity probing.
+ const QuicSocketAddress kNewPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+
+ OwningSerializedPacketPointer probing_packet = ConstructProbingPacket();
+ std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket(
+ QuicEncryptedPacket(probing_packet->encrypted_buffer,
+ probing_packet->encrypted_length),
+ clock_.Now()));
+ ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ // Process another non-probing packet with the new peer address on server
+ // side will start peer migration.
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
+
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kNewPeerAddress);
+ EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, ReceivePaddedPingAtClient) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_CLIENT);
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ // Client takes all padded PING packet as speculative connectivity
+ // probing packet, and reports to visitor.
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1);
+
+ OwningSerializedPacketPointer probing_packet = ConstructProbingPacket();
+ std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket(
+ QuicEncryptedPacket(probing_packet->encrypted_buffer,
+ probing_packet->encrypted_length),
+ clock_.Now()));
+ uint64_t num_probing_received =
+ connection_.GetStats().num_connectivity_probing_received;
+ ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received);
+
+ EXPECT_EQ(num_probing_received,
+ connection_.GetStats().num_connectivity_probing_received);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtClient) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_CLIENT);
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ // Process a padded PING packet with a different self address on client side
+ // is effectively receiving a connectivity probing.
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1);
+
+ const QuicSocketAddress kNewSelfAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+
+ OwningSerializedPacketPointer probing_packet = ConstructProbingPacket();
+ std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket(
+ QuicEncryptedPacket(probing_packet->encrypted_buffer,
+ probing_packet->encrypted_length),
+ clock_.Now()));
+ uint64_t num_probing_received =
+ connection_.GetStats().num_connectivity_probing_received;
+ ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received);
+
+ EXPECT_EQ(num_probing_received + 1,
+ connection_.GetStats().num_connectivity_probing_received);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, PeerAddressChangeAtClient) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ set_perspective(Perspective::IS_CLIENT);
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ // Process another packet with a different peer address on client side will
+ // only update peer address.
+ const QuicSocketAddress kNewPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+ EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kNewPeerAddress);
+ EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
+}
+
+TEST_P(QuicConnectionTest, MaxPacketSize) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+ EXPECT_EQ(1350u, connection_.max_packet_length());
+}
+
+TEST_P(QuicConnectionTest, SmallerServerMaxPacketSize) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ TestConnection connection(TestConnectionId(), kPeerAddress, helper_.get(),
+ alarm_factory_.get(), writer_.get(),
+ Perspective::IS_SERVER, version());
+ EXPECT_EQ(Perspective::IS_SERVER, connection.perspective());
+ EXPECT_EQ(1000u, connection.max_packet_length());
+}
+
+TEST_P(QuicConnectionTest, IncreaseServerMaxPacketSize) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ set_perspective(Perspective::IS_SERVER);
+ connection_.SetMaxPacketLength(1000);
+
+ QuicPacketHeader header;
+ header.destination_connection_id = connection_id_;
+ header.version_flag = true;
+ header.packet_number = QuicPacketNumber(1);
+
+ if (QuicVersionHasLongHeaderLengths(
+ peer_framer_.version().transport_version)) {
+ header.long_packet_type = INITIAL;
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicFrames frames;
+ QuicPaddingFrame padding;
+ frames.push_back(QuicFrame(frame1_));
+ frames.push_back(QuicFrame(padding));
+ std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(12),
+ *packet, buffer, kMaxOutgoingPacketSize);
+ EXPECT_EQ(kMaxOutgoingPacketSize, encrypted_length);
+
+ framer_.set_version(version());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
+
+ EXPECT_EQ(kMaxOutgoingPacketSize, connection_.max_packet_length());
+}
+
+TEST_P(QuicConnectionTest, IncreaseServerMaxPacketSizeWhileWriterLimited) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ const QuicByteCount lower_max_packet_size = 1240;
+ writer_->set_max_packet_size(lower_max_packet_size);
+ set_perspective(Perspective::IS_SERVER);
+ connection_.SetMaxPacketLength(1000);
+ EXPECT_EQ(1000u, connection_.max_packet_length());
+
+ QuicPacketHeader header;
+ header.destination_connection_id = connection_id_;
+ header.version_flag = true;
+ header.packet_number = QuicPacketNumber(1);
+
+ if (QuicVersionHasLongHeaderLengths(
+ peer_framer_.version().transport_version)) {
+ header.long_packet_type = INITIAL;
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicFrames frames;
+ QuicPaddingFrame padding;
+ frames.push_back(QuicFrame(frame1_));
+ frames.push_back(QuicFrame(padding));
+ std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(12),
+ *packet, buffer, kMaxOutgoingPacketSize);
+ EXPECT_EQ(kMaxOutgoingPacketSize, encrypted_length);
+
+ framer_.set_version(version());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
+
+ // Here, the limit imposed by the writer is lower than the size of the packet
+ // received, so the writer max packet size is used.
+ EXPECT_EQ(lower_max_packet_size, connection_.max_packet_length());
+}
+
+TEST_P(QuicConnectionTest, LimitMaxPacketSizeByWriter) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ const QuicByteCount lower_max_packet_size = 1240;
+ writer_->set_max_packet_size(lower_max_packet_size);
+
+ static_assert(lower_max_packet_size < kDefaultMaxPacketSize,
+ "Default maximum packet size is too low");
+ connection_.SetMaxPacketLength(kDefaultMaxPacketSize);
+
+ EXPECT_EQ(lower_max_packet_size, connection_.max_packet_length());
+}
+
+TEST_P(QuicConnectionTest, LimitMaxPacketSizeByWriterForNewConnection) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ const QuicConnectionId connection_id = TestConnectionId(17);
+ const QuicByteCount lower_max_packet_size = 1240;
+ writer_->set_max_packet_size(lower_max_packet_size);
+ TestConnection connection(connection_id, kPeerAddress, helper_.get(),
+ alarm_factory_.get(), writer_.get(),
+ Perspective::IS_CLIENT, version());
+ EXPECT_EQ(Perspective::IS_CLIENT, connection.perspective());
+ EXPECT_EQ(lower_max_packet_size, connection.max_packet_length());
+}
+
+TEST_P(QuicConnectionTest, PacketsInOrder) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(1);
+ EXPECT_EQ(QuicPacketNumber(1u), LargestAcked(*outgoing_ack()));
+ EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
+
+ ProcessPacket(2);
+ EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(*outgoing_ack()));
+ EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
+
+ ProcessPacket(3);
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
+ EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
+}
+
+TEST_P(QuicConnectionTest, PacketsOutOfOrder) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(3);
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
+ EXPECT_TRUE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+
+ ProcessPacket(2);
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
+ EXPECT_FALSE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+
+ ProcessPacket(1);
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
+ EXPECT_FALSE(IsMissing(2));
+ EXPECT_FALSE(IsMissing(1));
+}
+
+TEST_P(QuicConnectionTest, DuplicatePacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(3);
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
+ EXPECT_TRUE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+
+ // Send packet 3 again, but do not set the expectation that
+ // the visitor OnStreamFrame() will be called.
+ ProcessDataPacket(3);
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
+ EXPECT_TRUE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+}
+
+TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(3);
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
+ EXPECT_TRUE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+
+ ProcessPacket(2);
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(*outgoing_ack()));
+ EXPECT_TRUE(IsMissing(1));
+
+ ProcessPacket(5);
+ EXPECT_EQ(QuicPacketNumber(5u), LargestAcked(*outgoing_ack()));
+ EXPECT_TRUE(IsMissing(1));
+ EXPECT_TRUE(IsMissing(4));
+
+ // Pretend at this point the client has gotten acks for 2 and 3 and 1 is a
+ // packet the peer will not retransmit. It indicates this by sending 'least
+ // awaiting' is 4. The connection should then realize 1 will not be
+ // retransmitted, and will remove it from the missing list.
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _));
+ ProcessAckPacket(6, &frame);
+
+ // Force an ack to be sent.
+ SendAckPacketToPeer();
+ EXPECT_TRUE(IsMissing(4));
+}
+
+TEST_P(QuicConnectionTest, RejectPacketTooFarOut) {
+ if (GetQuicReloadableFlag(quic_use_uber_received_packet_manager)) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, _,
+ ConnectionCloseSource::FROM_SELF));
+
+ // Call ProcessDataPacket rather than ProcessPacket, as we should not get a
+ // packet call to the visitor.
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ ProcessDataPacket(MaxRandomInitialPacketNumber() + 6000);
+ } else {
+ ProcessDataPacket(6000);
+ }
+ EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
+ nullptr);
+}
+
+TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (!IsDefaultTestConfiguration()) {
+ return;
+ }
+
+ // Process an unencrypted packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_UNENCRYPTED_STREAM_DATA, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_QUIC_PEER_BUG(ProcessDataPacket(1), "");
+ EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
+ nullptr);
+ const std::vector<QuicConnectionCloseFrame>& connection_close_frames =
+ writer_->connection_close_frames();
+ EXPECT_EQ(1u, connection_close_frames.size());
+ EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA,
+ connection_close_frames[0].quic_error_code);
+}
+
+TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(3);
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ // Should not cause an ack.
+ EXPECT_EQ(0u, writer_->packets_write_attempts());
+ } else {
+ // Should ack immediately since we have missing packets.
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ }
+
+ ProcessPacket(2);
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ // Should ack immediately, since this fills the last hole.
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ } else {
+ // Should ack immediately since we have missing packets.
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+ }
+
+ ProcessPacket(1);
+ // Should ack immediately, since this fills the last hole.
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+ } else {
+ EXPECT_EQ(3u, writer_->packets_write_attempts());
+ }
+
+ ProcessPacket(4);
+ // Should not cause an ack.
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+ } else {
+ EXPECT_EQ(3u, writer_->packets_write_attempts());
+ }
+}
+
+TEST_P(QuicConnectionTest, OutOfOrderAckReceiptCausesNoAck) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr);
+ SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr);
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+
+ QuicAckFrame ack1 = InitAckFrame(1);
+ QuicAckFrame ack2 = InitAckFrame(2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(2, &ack2);
+ // Should ack immediately since we have missing packets.
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+
+ ProcessAckPacket(1, &ack1);
+ // Should not ack an ack filling a missing packet.
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+}
+
+TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ QuicPacketNumber original, second;
+
+ QuicByteCount packet_size =
+ SendStreamDataToPeer(3, "foo", 0, NO_FIN, &original); // 1st packet.
+ SendStreamDataToPeer(3, "bar", 3, NO_FIN, &second); // 2nd packet.
+
+ QuicAckFrame frame = InitAckFrame({{second, second + 1}});
+ // First nack triggers early retransmit.
+ LostPacketVector lost_packets;
+ lost_packets.push_back(LostPacket(original, kMaxOutgoingPacketSize));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicPacketNumber retransmission;
+ // Packet 1 is short header for IETF QUIC because the encryption level
+ // switched to ENCRYPTION_FORWARD_SECURE in SendStreamDataToPeer.
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, _, _,
+ GetParam().version.transport_version > QUIC_VERSION_43
+ ? packet_size
+ : packet_size - kQuicVersionSize,
+ _))
+ .WillOnce(SaveArg<2>(&retransmission));
+
+ ProcessAckPacket(&frame);
+
+ QuicAckFrame frame2 = ConstructAckFrame(retransmission, original);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ ProcessAckPacket(&frame2);
+
+ // Now if the peer sends an ack which still reports the retransmitted packet
+ // as missing, that will bundle an ack with data after two acks in a row
+ // indicate the high water mark needs to be raised.
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA));
+ connection_.SendStreamDataWithString(3, "foo", 6, NO_FIN);
+ // No ack sent.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+
+ // No more packet loss for the rest of the test.
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .Times(AnyNumber());
+ ProcessAckPacket(&frame2);
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, _, HAS_RETRANSMITTABLE_DATA));
+ connection_.SendStreamDataWithString(3, "foo", 9, NO_FIN);
+ // Ack bundled.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ }
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+
+ // But an ack with no missing packets will not send an ack.
+ AckPacket(original, &frame2);
+ ProcessAckPacket(&frame2);
+ ProcessAckPacket(&frame2);
+}
+
+TEST_P(QuicConnectionTest, AckSentEveryNthPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.set_ack_frequency_before_ack_decimation(3);
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(39);
+
+ // Expect 13 acks, every 3rd packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(13);
+ // Receives packets 1 - 39.
+ for (size_t i = 1; i <= 39; ++i) {
+ ProcessDataPacket(i);
+ }
+}
+
+TEST_P(QuicConnectionTest, AckDecimationReducesAcks) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+
+ QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING);
+
+ // Start ack decimation from 10th packet.
+ connection_.set_min_received_before_ack_decimation(10);
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(30);
+
+ // Expect 6 acks: 5 acks between packets 1-10, and ack at 20.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
+ // Receives packets 1 - 29.
+ for (size_t i = 1; i <= 29; ++i) {
+ ProcessDataPacket(i);
+ }
+
+ // We now receive the 30th packet, and so we send an ack.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ ProcessDataPacket(30);
+}
+
+TEST_P(QuicConnectionTest, AckNeedsRetransmittableFrames) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(99);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(19);
+ // Receives packets 1 - 39.
+ for (size_t i = 1; i <= 39; ++i) {
+ ProcessDataPacket(i);
+ }
+ // Receiving Packet 40 causes 20th ack to send. Session is informed and adds
+ // WINDOW_UPDATE.
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame())
+ .WillOnce(Invoke([this]() {
+ connection_.SendControlFrame(
+ QuicFrame(new QuicWindowUpdateFrame(1, 0, 0)));
+ }));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ EXPECT_EQ(0u, writer_->window_update_frames().size());
+ ProcessDataPacket(40);
+ EXPECT_EQ(1u, writer_->window_update_frames().size());
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(9);
+ // Receives packets 41 - 59.
+ for (size_t i = 41; i <= 59; ++i) {
+ ProcessDataPacket(i);
+ }
+ // Send a packet containing stream frame.
+ SendStreamDataToPeer(
+ QuicUtils::GetCryptoStreamId(connection_.version().transport_version),
+ "bar", 0, NO_FIN, nullptr);
+
+ // Session will not be informed until receiving another 20 packets.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(19);
+ for (size_t i = 60; i <= 98; ++i) {
+ ProcessDataPacket(i);
+ EXPECT_EQ(0u, writer_->window_update_frames().size());
+ }
+ // Session does not add a retransmittable frame.
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame())
+ .WillOnce(Invoke([this]() {
+ connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+ }));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ EXPECT_EQ(0u, writer_->ping_frames().size());
+ ProcessDataPacket(99);
+ EXPECT_EQ(0u, writer_->window_update_frames().size());
+ // A ping frame will be added.
+ EXPECT_EQ(1u, writer_->ping_frames().size());
+}
+
+TEST_P(QuicConnectionTest, LeastUnackedLower) {
+ if (GetParam().version.transport_version > QUIC_VERSION_43 ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr);
+ SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr);
+ SendStreamDataToPeer(1, "eep", 6, NO_FIN, nullptr);
+
+ // Start out saying the least unacked is 2.
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5);
+ ProcessStopWaitingPacket(InitStopWaitingFrame(2));
+
+ // Change it to 1, but lower the packet number to fake out-of-order packets.
+ // This should be fine.
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 1);
+ // The scheduler will not process out of order acks, but all packet processing
+ // causes the connection to try to write.
+ if (!GetParam().no_stop_waiting) {
+ EXPECT_CALL(visitor_, OnCanWrite());
+ }
+ ProcessStopWaitingPacket(InitStopWaitingFrame(1));
+
+ // Now claim it's one, but set the ordering so it was sent "after" the first
+ // one. This should cause a connection error.
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 7);
+ if (!GetParam().no_stop_waiting) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_STOP_WAITING_DATA, _,
+ ConnectionCloseSource::FROM_SELF));
+ }
+ ProcessStopWaitingPacket(InitStopWaitingFrame(1));
+}
+
+TEST_P(QuicConnectionTest, TooManySentPackets) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicPacketCount max_tracked_packets = 50;
+ QuicConnectionPeer::SetMaxTrackedPackets(&connection_, max_tracked_packets);
+
+ const int num_packets = max_tracked_packets + 5;
+
+ for (int i = 0; i < num_packets; ++i) {
+ SendStreamDataToPeer(1, "foo", 3 * i, NO_FIN, nullptr);
+ }
+
+ // Ack packet 1, which leaves more than the limit outstanding.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, _,
+ ConnectionCloseSource::FROM_SELF));
+
+ // Nack the first packet and ack the rest, leaving a huge gap.
+ QuicAckFrame frame1 = ConstructAckFrame(num_packets, 1);
+ ProcessAckPacket(&frame1);
+}
+
+TEST_P(QuicConnectionTest, LargestObservedLower) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr);
+ SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr);
+ SendStreamDataToPeer(1, "eep", 6, NO_FIN, nullptr);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+
+ // Start out saying the largest observed is 2.
+ QuicAckFrame frame1 = InitAckFrame(1);
+ QuicAckFrame frame2 = InitAckFrame(2);
+ ProcessAckPacket(&frame2);
+
+ if (GetQuicReloadableFlag(quic_tolerate_reneging)) {
+ EXPECT_CALL(visitor_, OnCanWrite());
+ } else {
+ // Now change it to 1, and it should cause a connection error.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
+ }
+ ProcessAckPacket(&frame1);
+}
+
+TEST_P(QuicConnectionTest, AckUnsentData) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Ack a packet which has not been sent.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, BasicSending) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacket(1);
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2);
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); // Packet 1
+ EXPECT_EQ(QuicPacketNumber(1u), last_packet);
+ SendAckPacketToPeer(); // Packet 2
+
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ EXPECT_EQ(QuicPacketNumber(1u), least_unacked());
+ }
+
+ SendAckPacketToPeer(); // Packet 3
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ EXPECT_EQ(QuicPacketNumber(1u), least_unacked());
+ }
+
+ SendStreamDataToPeer(1, "bar", 3, NO_FIN, &last_packet); // Packet 4
+ EXPECT_EQ(QuicPacketNumber(4u), last_packet);
+ SendAckPacketToPeer(); // Packet 5
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ EXPECT_EQ(QuicPacketNumber(1u), least_unacked());
+ }
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+
+ // Peer acks up to packet 3.
+ QuicAckFrame frame = InitAckFrame(3);
+ ProcessAckPacket(&frame);
+ SendAckPacketToPeer(); // Packet 6
+
+ // As soon as we've acked one, we skip ack packets 2 and 3 and note lack of
+ // ack for 4.
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ EXPECT_EQ(QuicPacketNumber(4u), least_unacked());
+ }
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+
+ // Peer acks up to packet 4, the last packet.
+ QuicAckFrame frame2 = InitAckFrame(6);
+ ProcessAckPacket(&frame2); // Acks don't instigate acks.
+
+ // Verify that we did not send an ack.
+ EXPECT_EQ(QuicPacketNumber(6u), writer_->header().packet_number);
+
+ // So the last ack has not changed.
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ EXPECT_EQ(QuicPacketNumber(4u), least_unacked());
+ }
+
+ // If we force an ack, we shouldn't change our retransmit state.
+ SendAckPacketToPeer(); // Packet 7
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ EXPECT_EQ(QuicPacketNumber(7u), least_unacked());
+ }
+
+ // But if we send more data it should.
+ SendStreamDataToPeer(1, "eep", 6, NO_FIN, &last_packet); // Packet 8
+ EXPECT_EQ(QuicPacketNumber(8u), last_packet);
+ SendAckPacketToPeer(); // Packet 9
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ EXPECT_EQ(QuicPacketNumber(7u), least_unacked());
+ }
+}
+
+// QuicConnection should record the packet sent-time prior to sending the
+// packet.
+TEST_P(QuicConnectionTest, RecordSentTimeBeforePacketSent) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // We're using a MockClock for the tests, so we have complete control over the
+ // time.
+ // Our recorded timestamp for the last packet sent time will be passed in to
+ // the send_algorithm. Make sure that it is set to the correct value.
+ QuicTime actual_recorded_send_time = QuicTime::Zero();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(SaveArg<0>(&actual_recorded_send_time));
+
+ // First send without any pause and check the result.
+ QuicTime expected_recorded_send_time = clock_.Now();
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+ EXPECT_EQ(expected_recorded_send_time, actual_recorded_send_time)
+ << "Expected time = " << expected_recorded_send_time.ToDebuggingValue()
+ << ". Actual time = " << actual_recorded_send_time.ToDebuggingValue();
+
+ // Now pause during the write, and check the results.
+ actual_recorded_send_time = QuicTime::Zero();
+ const QuicTime::Delta write_pause_time_delta =
+ QuicTime::Delta::FromMilliseconds(5000);
+ SetWritePauseTimeDelta(write_pause_time_delta);
+ expected_recorded_send_time = clock_.Now();
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(SaveArg<0>(&actual_recorded_send_time));
+ connection_.SendStreamDataWithString(2, "baz", 0, NO_FIN);
+ EXPECT_EQ(expected_recorded_send_time, actual_recorded_send_time)
+ << "Expected time = " << expected_recorded_send_time.ToDebuggingValue()
+ << ". Actual time = " << actual_recorded_send_time.ToDebuggingValue();
+}
+
+TEST_P(QuicConnectionTest, FramePacking) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Send two stream frames in 1 packet by queueing them.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ {
+ QuicConnection::ScopedPacketFlusher flusher(&connection_,
+ QuicConnection::SEND_ACK);
+ connection_.SendStreamData3();
+ connection_.SendStreamData5();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ }
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's an ack and two stream frames from
+ // two different streams.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ }
+
+ EXPECT_TRUE(writer_->ack_frames().empty());
+
+ ASSERT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(GetNthClientInitiatedStreamId(1, connection_.transport_version()),
+ writer_->stream_frames()[0]->stream_id);
+ EXPECT_EQ(GetNthClientInitiatedStreamId(2, connection_.transport_version()),
+ writer_->stream_frames()[1]->stream_id);
+}
+
+TEST_P(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Send two stream frames (one non-crypto, then one crypto) in 2 packets by
+ // queueing them.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ QuicConnection::ScopedPacketFlusher flusher(&connection_,
+ QuicConnection::SEND_ACK);
+ connection_.SendStreamData3();
+ connection_.SendCryptoStreamData();
+ }
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's the crypto stream frame.
+ EXPECT_EQ(2u, writer_->frame_count());
+ ASSERT_EQ(1u, writer_->padding_frames().size());
+ if (!QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()),
+ writer_->stream_frames()[0]->stream_id);
+ } else {
+ EXPECT_EQ(1u, writer_->crypto_frames().size());
+ }
+}
+
+TEST_P(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Send two stream frames (one crypto, then one non-crypto) in 2 packets by
+ // queueing them.
+ {
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ QuicConnection::ScopedPacketFlusher flusher(&connection_,
+ QuicConnection::SEND_ACK);
+ connection_.SendCryptoStreamData();
+ connection_.SendStreamData3();
+ }
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's the stream frame from stream 3.
+ EXPECT_EQ(1u, writer_->frame_count());
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(GetNthClientInitiatedStreamId(1, connection_.transport_version()),
+ writer_->stream_frames()[0]->stream_id);
+}
+
+TEST_P(QuicConnectionTest, FramePackingAckResponse) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ // Process a data packet to queue up a pending ack.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacket(1);
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(
+ QuicUtils::GetCryptoStreamId(connection_.version().transport_version),
+ "foo", 0, NO_FIN, &last_packet);
+ // Verify ack is bundled with outging packet.
+ EXPECT_FALSE(writer_->ack_frames().empty());
+
+ EXPECT_CALL(visitor_, OnCanWrite())
+ .WillOnce(DoAll(IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData3)),
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData5))));
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+
+ // Process a data packet to cause the visitor's OnCanWrite to be invoked.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+ SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<StrictTaggingDecrypter>(0x01));
+ ProcessDataPacketAtLevel(2, false, ENCRYPTION_FORWARD_SECURE);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's an ack and two stream frames from
+ // two different streams.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ ASSERT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(GetNthClientInitiatedStreamId(1, connection_.transport_version()),
+ writer_->stream_frames()[0]->stream_id);
+ EXPECT_EQ(GetNthClientInitiatedStreamId(2, connection_.transport_version()),
+ writer_->stream_frames()[1]->stream_id);
+}
+
+TEST_P(QuicConnectionTest, FramePackingSendv) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Send data in 1 packet by writing multiple blocks in a single iovector
+ // using writev.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+
+ char data[] = "ABCDEF";
+ struct iovec iov[2];
+ iov[0].iov_base = data;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = data + 4;
+ iov[1].iov_len = 2;
+ connection_.SaveAndSendStreamData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), iov, 2, 6,
+ 0, NO_FIN);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure multiple iovector blocks have
+ // been packed into a single stream frame from one stream.
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(1u, writer_->padding_frames().size());
+ QuicStreamFrame* frame = writer_->stream_frames()[0].get();
+ EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()),
+ frame->stream_id);
+ EXPECT_EQ("ABCDEF", QuicStringPiece(frame->data_buffer, frame->data_length));
+}
+
+TEST_P(QuicConnectionTest, FramePackingSendvQueued) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Try to send two stream frames in 1 packet by using writev.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+
+ BlockOnNextWrite();
+ char data[] = "ABCDEF";
+ struct iovec iov[2];
+ iov[0].iov_base = data;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = data + 4;
+ iov[1].iov_len = 2;
+ connection_.SaveAndSendStreamData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), iov, 2, 6,
+ 0, NO_FIN);
+
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ EXPECT_TRUE(connection_.HasQueuedData());
+
+ // Unblock the writes and actually send.
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+
+ // Parse the last packet and ensure it's one stream frame from one stream.
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(1u, writer_->padding_frames().size());
+ EXPECT_EQ(QuicUtils::GetCryptoStreamId(connection_.transport_version()),
+ writer_->stream_frames()[0]->stream_id);
+}
+
+TEST_P(QuicConnectionTest, SendingZeroBytes) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ // Send a zero byte write with a fin using writev.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SaveAndSendStreamData(
+ QuicUtils::GetHeadersStreamId(connection_.transport_version()), nullptr,
+ 0, 0, 0, FIN);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's one stream frame from one stream.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(QuicUtils::GetHeadersStreamId(connection_.transport_version()),
+ writer_->stream_frames()[0]->stream_id);
+ EXPECT_TRUE(writer_->stream_frames()[0]->fin);
+}
+
+TEST_P(QuicConnectionTest, LargeSendWithPendingAck) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ // Set the ack alarm by processing a ping frame.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Processs a PING frame.
+ ProcessFramePacket(QuicFrame(QuicPingFrame()));
+ // Ensure that this has caused the ACK alarm to be set.
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ EXPECT_TRUE(ack_alarm->IsSet());
+
+ // Send data and ensure the ack is bundled.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(8);
+ size_t len = 10000;
+ std::unique_ptr<char[]> data_array(new char[len]);
+ memset(data_array.get(), '?', len);
+ struct iovec iov;
+ iov.iov_base = data_array.get();
+ iov.iov_len = len;
+ QuicConsumedData consumed = connection_.SaveAndSendStreamData(
+ QuicUtils::GetHeadersStreamId(connection_.transport_version()), &iov, 1,
+ len, 0, FIN);
+ EXPECT_EQ(len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's one stream frame with a fin.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(QuicUtils::GetHeadersStreamId(connection_.transport_version()),
+ writer_->stream_frames()[0]->stream_id);
+ EXPECT_TRUE(writer_->stream_frames()[0]->fin);
+ // Ensure the ack alarm was cancelled when the ack was sent.
+ EXPECT_FALSE(ack_alarm->IsSet());
+}
+
+TEST_P(QuicConnectionTest, OnCanWrite) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Visitor's OnCanWrite will send data, but will have more pending writes.
+ EXPECT_CALL(visitor_, OnCanWrite())
+ .WillOnce(DoAll(IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData3)),
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData5))));
+ {
+ InSequence seq;
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillOnce(Return(true));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite())
+ .WillRepeatedly(Return(false));
+ }
+
+ EXPECT_CALL(*send_algorithm_, CanSend(_))
+ .WillRepeatedly(testing::Return(true));
+
+ connection_.OnCanWrite();
+
+ // Parse the last packet and ensure it's the two stream frames from
+ // two different streams.
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(GetNthClientInitiatedStreamId(1, connection_.transport_version()),
+ writer_->stream_frames()[0]->stream_id);
+ EXPECT_EQ(GetNthClientInitiatedStreamId(2, connection_.transport_version()),
+ writer_->stream_frames()[1]->stream_id);
+}
+
+TEST_P(QuicConnectionTest, RetransmitOnNack) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ 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
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Don't lose a packet on an ack, and nothing is retransmitted.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame ack_one = InitAckFrame(1);
+ ProcessAckPacket(&ack_one);
+
+ // Lose a packet and ensure it triggers retransmission.
+ QuicAckFrame nack_two = ConstructAckFrame(3, 2);
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(2), kMaxOutgoingPacketSize));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ EXPECT_FALSE(QuicPacketCreatorPeer::SendVersionInPacket(creator_));
+ ProcessAckPacket(&nack_two);
+}
+
+TEST_P(QuicConnectionTest, DoNotSendQueuedPacketForResetStream) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Block the connection to queue the packet.
+ BlockOnNextWrite();
+
+ QuicStreamId stream_id = 2;
+ connection_.SendStreamDataWithString(stream_id, "foo", 0, NO_FIN);
+
+ // Now that there is a queued packet, reset the stream.
+ SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3);
+
+ // Unblock the connection and verify that only the RST_STREAM is sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ if (!connection_.session_decides_what_to_write()) {
+ // OnCanWrite will cause RST_STREAM be sent again.
+ connection_.SendControlFrame(QuicFrame(new QuicRstStreamFrame(
+ 1, stream_id, QUIC_ERROR_PROCESSING_STREAM, 14)));
+ }
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->rst_stream_frames().size());
+}
+
+TEST_P(QuicConnectionTest, SendQueuedPacketForQuicRstStreamNoError) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Block the connection to queue the packet.
+ BlockOnNextWrite();
+
+ QuicStreamId stream_id = 2;
+ connection_.SendStreamDataWithString(stream_id, "foo", 0, NO_FIN);
+
+ // Now that there is a queued packet, reset the stream.
+ SendRstStream(stream_id, QUIC_STREAM_NO_ERROR, 3);
+
+ // Unblock the connection and verify that the RST_STREAM is sent and the data
+ // packet is sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(2));
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ if (!connection_.session_decides_what_to_write()) {
+ // OnCanWrite will cause RST_STREAM be sent again.
+ connection_.SendControlFrame(QuicFrame(
+ new QuicRstStreamFrame(1, stream_id, QUIC_STREAM_NO_ERROR, 14)));
+ }
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->rst_stream_frames().size());
+}
+
+TEST_P(QuicConnectionTest, DoNotRetransmitForResetStreamOnNack) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicStreamId stream_id = 2;
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet);
+ SendStreamDataToPeer(stream_id, "foos", 3, NO_FIN, &last_packet);
+ SendStreamDataToPeer(stream_id, "fooos", 7, NO_FIN, &last_packet);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 12);
+
+ // Lose a packet and ensure it does not trigger retransmission.
+ QuicAckFrame nack_two = ConstructAckFrame(last_packet, last_packet - 1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ ProcessAckPacket(&nack_two);
+}
+
+TEST_P(QuicConnectionTest, RetransmitForQuicRstStreamNoErrorOnNack) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicStreamId stream_id = 2;
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet);
+ SendStreamDataToPeer(stream_id, "foos", 3, NO_FIN, &last_packet);
+ SendStreamDataToPeer(stream_id, "fooos", 7, NO_FIN, &last_packet);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ SendRstStream(stream_id, QUIC_STREAM_NO_ERROR, 12);
+
+ // Lose a packet, ensure it triggers retransmission.
+ QuicAckFrame nack_two = ConstructAckFrame(last_packet, last_packet - 1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ LostPacketVector lost_packets;
+ lost_packets.push_back(LostPacket(last_packet - 1, kMaxOutgoingPacketSize));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(1));
+ ProcessAckPacket(&nack_two);
+}
+
+TEST_P(QuicConnectionTest, DoNotRetransmitForResetStreamOnRTO) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicStreamId stream_id = 2;
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3);
+
+ // Fire the RTO and verify that the RST_STREAM is resent, not stream data.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->rst_stream_frames().size());
+ EXPECT_EQ(stream_id, writer_->rst_stream_frames().front().stream_id);
+}
+
+// Ensure that if the only data in flight is non-retransmittable, the
+// retransmission alarm is not set.
+TEST_P(QuicConnectionTest, CancelRetransmissionAlarmAfterResetStream) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicStreamId stream_id = 2;
+ QuicPacketNumber last_data_packet;
+ SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_data_packet);
+
+ // Cancel the stream.
+ const QuicPacketNumber rst_packet = last_data_packet + 1;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, rst_packet, _, _)).Times(1);
+ SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3);
+
+ // Ack the RST_STREAM frame (since it's retransmittable), but not the data
+ // packet, which is no longer retransmittable since the stream was cancelled.
+ QuicAckFrame nack_stream_data =
+ ConstructAckFrame(rst_packet, last_data_packet);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ ProcessAckPacket(&nack_stream_data);
+
+ // Ensure that the data is still in flight, but the retransmission alarm is no
+ // longer set.
+ EXPECT_GT(QuicSentPacketManagerPeer::GetBytesInFlight(manager_), 0u);
+ if (GetQuicReloadableFlag(quic_optimize_inflight_check)) {
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ // Firing the alarm should remove all bytes_in_flight.
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetBytesInFlight(manager_));
+ }
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, RetransmitForQuicRstStreamNoErrorOnRTO) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(0);
+
+ QuicStreamId stream_id = 2;
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ SendRstStream(stream_id, QUIC_STREAM_NO_ERROR, 3);
+
+ // Fire the RTO and verify that the RST_STREAM is resent, the stream data
+ // is sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(2));
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(1u, writer_->frame_count());
+ ASSERT_EQ(1u, writer_->rst_stream_frames().size());
+ EXPECT_EQ(stream_id, writer_->rst_stream_frames().front().stream_id);
+}
+
+TEST_P(QuicConnectionTest, DoNotSendPendingRetransmissionForResetStream) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicStreamId stream_id = 2;
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet);
+ SendStreamDataToPeer(stream_id, "foos", 3, NO_FIN, &last_packet);
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(stream_id, "fooos", 7, NO_FIN);
+
+ // Lose a packet which will trigger a pending retransmission.
+ QuicAckFrame ack = ConstructAckFrame(last_packet, last_packet - 1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ ProcessAckPacket(&ack);
+
+ SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 12);
+
+ // Unblock the connection and verify that the RST_STREAM is sent but not the
+ // second data packet nor a retransmit.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ if (!connection_.session_decides_what_to_write()) {
+ // OnCanWrite will cause this RST_STREAM_FRAME be sent again.
+ connection_.SendControlFrame(QuicFrame(new QuicRstStreamFrame(
+ 1, stream_id, QUIC_ERROR_PROCESSING_STREAM, 14)));
+ }
+ EXPECT_EQ(1u, writer_->frame_count());
+ ASSERT_EQ(1u, writer_->rst_stream_frames().size());
+ EXPECT_EQ(stream_id, writer_->rst_stream_frames().front().stream_id);
+}
+
+TEST_P(QuicConnectionTest, SendPendingRetransmissionForQuicRstStreamNoError) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicStreamId stream_id = 2;
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet);
+ SendStreamDataToPeer(stream_id, "foos", 3, NO_FIN, &last_packet);
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(stream_id, "fooos", 7, NO_FIN);
+
+ // Lose a packet which will trigger a pending retransmission.
+ QuicAckFrame ack = ConstructAckFrame(last_packet, last_packet - 1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ LostPacketVector lost_packets;
+ lost_packets.push_back(LostPacket(last_packet - 1, kMaxOutgoingPacketSize));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ ProcessAckPacket(&ack);
+
+ SendRstStream(stream_id, QUIC_STREAM_NO_ERROR, 12);
+
+ // Unblock the connection and verify that the RST_STREAM is sent and the
+ // second data packet or a retransmit is sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(2));
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ // The RST_STREAM_FRAME is sent after queued packets and pending
+ // retransmission.
+ connection_.SendControlFrame(QuicFrame(
+ new QuicRstStreamFrame(1, stream_id, QUIC_STREAM_NO_ERROR, 14)));
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->rst_stream_frames().size());
+}
+
+TEST_P(QuicConnectionTest, RetransmitAckedPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); // Packet 1
+ SendStreamDataToPeer(1, "foos", 3, NO_FIN, &last_packet); // Packet 2
+ SendStreamDataToPeer(1, "fooos", 7, NO_FIN, &last_packet); // Packet 3
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Instigate a loss with an ack.
+ QuicAckFrame nack_two = ConstructAckFrame(3, 2);
+ // The first nack should trigger a fast retransmission, but we'll be
+ // write blocked, so the packet will be queued.
+ BlockOnNextWrite();
+
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(2), kMaxOutgoingPacketSize));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&nack_two);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // Now, ack the previous transmission.
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(false, _, _, _, _));
+ QuicAckFrame ack_all = InitAckFrame(3);
+ ProcessAckPacket(&ack_all);
+
+ // Unblock the socket and attempt to send the queued packets. We will always
+ // send the retransmission.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(4), _, _))
+ .Times(1);
+
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ // We do not store retransmittable frames of this retransmission.
+ EXPECT_FALSE(QuicConnectionPeer::HasRetransmittableFrames(&connection_, 4));
+}
+
+TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ QuicPacketNumber original, second;
+
+ QuicByteCount packet_size =
+ SendStreamDataToPeer(3, "foo", 0, NO_FIN, &original); // 1st packet.
+ SendStreamDataToPeer(3, "bar", 3, NO_FIN, &second); // 2nd packet.
+
+ QuicAckFrame frame = InitAckFrame({{second, second + 1}});
+ // The first nack should retransmit the largest observed packet.
+ LostPacketVector lost_packets;
+ lost_packets.push_back(LostPacket(original, kMaxOutgoingPacketSize));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ // Packet 1 is short header for IETF QUIC because the encryption level
+ // switched to ENCRYPTION_FORWARD_SECURE in SendStreamDataToPeer.
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, _, _,
+ GetParam().version.transport_version > QUIC_VERSION_43
+ ? packet_size
+ : packet_size - kQuicVersionSize,
+ _));
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(0);
+
+ for (int i = 0; i < 10; ++i) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(3, "foo", i * 3, NO_FIN);
+ }
+
+ // Block the writer and ensure they're queued.
+ BlockOnNextWrite();
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ // Only one packet should be retransmitted.
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_TRUE(connection_.HasQueuedData());
+
+ // Unblock the writer.
+ writer_->SetWritable();
+ clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
+ 2 * DefaultRetransmissionTime().ToMicroseconds()));
+ // Retransmit already retransmitted packets event though the packet number
+ // greater than the largest observed.
+ if (connection_.session_decides_what_to_write()) {
+ // 2 RTOs + 1 TLP.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+ } else {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ }
+ connection_.GetRetransmissionAlarm()->Fire();
+ connection_.OnCanWrite();
+}
+
+TEST_P(QuicConnectionTest, WriteBlockedBufferedThenSent) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, WriteBlockedThenSent) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // The second packet should also be queued, in order to ensure packets are
+ // never sent out of order.
+ writer_->SetWritable();
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+ EXPECT_EQ(2u, connection_.NumQueuedPackets());
+
+ // Now both are sent in order when we unblock.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ connection_.OnCanWrite();
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, RetransmitWriteBlockedAckedOriginalThenSent) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ // Simulate the retransmission alarm firing.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Ack the sent packet before the callback returns, which happens in
+ // rare circumstances with write blocked sockets.
+ QuicAckFrame ack = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&ack);
+
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ // There is now a pending packet, but with no retransmittable frames.
+ if (GetQuicReloadableFlag(quic_optimize_inflight_check)) {
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ // Firing the alarm should remove all bytes_in_flight.
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetBytesInFlight(manager_));
+ }
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(QuicConnectionPeer::HasRetransmittableFrames(&connection_, 2));
+}
+
+TEST_P(QuicConnectionTest, AlarmsWhenWriteBlocked) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Block the connection.
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+
+ // Set the send alarm. Fire the alarm and ensure it doesn't attempt to write.
+ connection_.GetSendAlarm()->Set(clock_.ApproximateNow());
+ connection_.GetSendAlarm()->Fire();
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+}
+
+TEST_P(QuicConnectionTest, NoSendAlarmAfterProcessPacketWhenWriteBlocked) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Block the connection.
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ // Process packet number 1. Can not call ProcessPacket or ProcessDataPacket
+ // here, because they will fire the alarm after QuicConnection::ProcessPacket
+ // is returned.
+ const uint64_t received_packet_num = 1;
+ const bool has_stop_waiting = false;
+ const EncryptionLevel level = ENCRYPTION_INITIAL;
+ std::unique_ptr<QuicPacket> packet(ConstructDataPacket(
+ received_packet_num, has_stop_waiting, ENCRYPTION_INITIAL));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ peer_framer_.EncryptPayload(level, QuicPacketNumber(received_packet_num),
+ *packet, buffer, kMaxOutgoingPacketSize);
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false));
+
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, AddToWriteBlockedListIfWriterBlockedWhenProcessing) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr);
+
+ // Simulate the case where a shared writer gets blocked by another connection.
+ writer_->SetWriteBlocked();
+
+ // Process an ACK, make sure the connection calls visitor_->OnWriteBlocked().
+ QuicAckFrame ack1 = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _));
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1);
+ ProcessAckPacket(1, &ack1);
+}
+
+TEST_P(QuicConnectionTest, DoNotAddToWriteBlockedListAfterDisconnect) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ writer_->SetBatchMode(true);
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
+ ConnectionCloseSource::FROM_SELF));
+
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0);
+
+ {
+ QuicConnection::ScopedPacketFlusher flusher(&connection_,
+ QuicConnection::NO_ACK);
+ connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+
+ EXPECT_FALSE(connection_.connected());
+ writer_->SetWriteBlocked();
+ }
+}
+
+TEST_P(QuicConnectionTest, AddToWriteBlockedListIfBlockedOnFlushPackets) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ writer_->SetBatchMode(true);
+ writer_->BlockOnNextFlush();
+
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1);
+ {
+ QuicConnection::ScopedPacketFlusher flusher(&connection_,
+ QuicConnection::NO_ACK);
+ // flusher's destructor will call connection_.FlushPackets, which should add
+ // the connection to the write blocked list.
+ }
+}
+
+TEST_P(QuicConnectionTest, NoLimitPacketsPerNack) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ int offset = 0;
+ // Send packets 1 to 15.
+ for (int i = 0; i < 15; ++i) {
+ SendStreamDataToPeer(1, "foo", offset, NO_FIN, nullptr);
+ offset += 3;
+ }
+
+ // Ack 15, nack 1-14.
+
+ QuicAckFrame nack =
+ InitAckFrame({{QuicPacketNumber(15), QuicPacketNumber(16)}});
+
+ // 14 packets have been NACK'd and lost.
+ LostPacketVector lost_packets;
+ for (int i = 1; i < 15; ++i) {
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(i), kMaxOutgoingPacketSize));
+ }
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ if (connection_.session_decides_what_to_write()) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ } else {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(14);
+ }
+ ProcessAckPacket(&nack);
+}
+
+// Test sending multiple acks from the connection to the session.
+TEST_P(QuicConnectionTest, MultipleAcks) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacket(1);
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2);
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet); // Packet 1
+ EXPECT_EQ(QuicPacketNumber(1u), last_packet);
+ SendStreamDataToPeer(3, "foo", 0, NO_FIN, &last_packet); // Packet 2
+ EXPECT_EQ(QuicPacketNumber(2u), last_packet);
+ SendAckPacketToPeer(); // Packet 3
+ SendStreamDataToPeer(5, "foo", 0, NO_FIN, &last_packet); // Packet 4
+ EXPECT_EQ(QuicPacketNumber(4u), last_packet);
+ SendStreamDataToPeer(1, "foo", 3, NO_FIN, &last_packet); // Packet 5
+ EXPECT_EQ(QuicPacketNumber(5u), last_packet);
+ SendStreamDataToPeer(3, "foo", 3, NO_FIN, &last_packet); // Packet 6
+ EXPECT_EQ(QuicPacketNumber(6u), last_packet);
+
+ // Client will ack packets 1, 2, [!3], 4, 5.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame1 = ConstructAckFrame(5, 3);
+ ProcessAckPacket(&frame1);
+
+ // Now the client implicitly acks 3, and explicitly acks 6.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame2 = InitAckFrame(6);
+ ProcessAckPacket(&frame2);
+}
+
+TEST_P(QuicConnectionTest, DontLatchUnackedPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacket(1);
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2);
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr); // Packet 1;
+ // From now on, we send acks, so the send algorithm won't mark them pending.
+ SendAckPacketToPeer(); // Packet 2
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1);
+ ProcessAckPacket(&frame);
+
+ // Verify that our internal state has least-unacked as 2, because we're still
+ // waiting for a potential ack for 2.
+
+ EXPECT_EQ(QuicPacketNumber(2u), stop_waiting()->least_unacked);
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ frame = InitAckFrame(2);
+ ProcessAckPacket(&frame);
+ EXPECT_EQ(QuicPacketNumber(3u), stop_waiting()->least_unacked);
+
+ // When we send an ack, we make sure our least-unacked makes sense. In this
+ // case since we're not waiting on an ack for 2 and all packets are acked, we
+ // set it to 3.
+ SendAckPacketToPeer(); // Packet 3
+ // Least_unacked remains at 3 until another ack is received.
+ EXPECT_EQ(QuicPacketNumber(3u), stop_waiting()->least_unacked);
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ // Check that the outgoing ack had its packet number as least_unacked.
+ EXPECT_EQ(QuicPacketNumber(3u), least_unacked());
+ }
+
+ // Ack the ack, which updates the rtt and raises the least unacked.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ frame = InitAckFrame(3);
+ ProcessAckPacket(&frame);
+
+ SendStreamDataToPeer(1, "bar", 3, NO_FIN, nullptr); // Packet 4
+ EXPECT_EQ(QuicPacketNumber(4u), stop_waiting()->least_unacked);
+ SendAckPacketToPeer(); // Packet 5
+ if (GetParam().no_stop_waiting) {
+ // Expect no stop waiting frame is sent.
+ EXPECT_FALSE(least_unacked().IsInitialized());
+ } else {
+ EXPECT_EQ(QuicPacketNumber(4u), least_unacked());
+ }
+
+ // Send two data packets at the end, and ensure if the last one is acked,
+ // the least unacked is raised above the ack packets.
+ SendStreamDataToPeer(1, "bar", 6, NO_FIN, nullptr); // Packet 6
+ SendStreamDataToPeer(1, "bar", 9, NO_FIN, nullptr); // Packet 7
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ frame = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(5)},
+ {QuicPacketNumber(7), QuicPacketNumber(8)}});
+ ProcessAckPacket(&frame);
+
+ EXPECT_EQ(QuicPacketNumber(6u), stop_waiting()->least_unacked);
+}
+
+TEST_P(QuicConnectionTest, TLP) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(1);
+
+ SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr);
+ EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
+ QuicTime retransmission_time =
+ connection_.GetRetransmissionAlarm()->deadline();
+ EXPECT_NE(QuicTime::Zero(), retransmission_time);
+
+ EXPECT_EQ(QuicPacketNumber(1u), writer_->header().packet_number);
+ // Simulate the retransmission alarm firing and sending a tlp,
+ // so send algorithm's OnRetransmissionTimeout is not called.
+ clock_.AdvanceTime(retransmission_time - clock_.Now());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(QuicPacketNumber(2u), writer_->header().packet_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
+}
+
+TEST_P(QuicConnectionTest, TailLossProbeDelayForStreamDataInTLPR) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Set TLPR from QuicConfig.
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kTLPR);
+ config.SetConnectionOptionsToSend(options);
+ connection_.SetFromConfig(config);
+ connection_.SetMaxTailLossProbes(1);
+
+ SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr);
+ EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
+
+ QuicTime retransmission_time =
+ connection_.GetRetransmissionAlarm()->deadline();
+ EXPECT_NE(QuicTime::Zero(), retransmission_time);
+ QuicTime::Delta expected_tlp_delay =
+ 0.5 * manager_->GetRttStats()->SmoothedOrInitialRtt();
+ EXPECT_EQ(expected_tlp_delay, retransmission_time - clock_.Now());
+
+ EXPECT_EQ(QuicPacketNumber(1u), writer_->header().packet_number);
+ // Simulate firing of the retransmission alarm and retransmit the packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _));
+ clock_.AdvanceTime(retransmission_time - clock_.Now());
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(QuicPacketNumber(2u), writer_->header().packet_number);
+
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
+}
+
+TEST_P(QuicConnectionTest, TailLossProbeDelayForNonStreamDataInTLPR) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Set TLPR from QuicConfig.
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kTLPR);
+ config.SetConnectionOptionsToSend(options);
+ connection_.SetFromConfig(config);
+ connection_.SetMaxTailLossProbes(1);
+
+ // Sets retransmittable on wire.
+ const QuicTime::Delta retransmittable_on_wire_timeout =
+ QuicTime::Delta::FromMilliseconds(50);
+ connection_.set_retransmittable_on_wire_timeout(
+ retransmittable_on_wire_timeout);
+
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(true));
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.IsPathDegrading());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+
+ const char data[] = "data";
+ size_t data_size = strlen(data);
+ QuicStreamOffset offset = 0;
+
+ // Send a data packet.
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+
+ // Path degrading alarm should be set when there is a retransmittable packet
+ // on the wire.
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+
+ // Verify the path degrading delay.
+ // First TLP with stream data.
+ QuicTime::Delta srtt = manager_->GetRttStats()->SmoothedOrInitialRtt();
+ QuicTime::Delta expected_delay = 0.5 * srtt;
+ // Add 1st RTO.
+ QuicTime::Delta retransmission_delay =
+ QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
+ expected_delay = expected_delay + retransmission_delay;
+ // Add 2nd RTO.
+ expected_delay = expected_delay + retransmission_delay * 2;
+ EXPECT_EQ(expected_delay,
+ QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay());
+ ASSERT_TRUE(connection_.sent_packet_manager().HasInFlightPackets());
+
+ // The ping alarm is set for the ping timeout, not the shorter
+ // retransmittable_on_wire_timeout.
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
+ connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
+
+ // Receive an ACK for the data packet.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame =
+ InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
+ ProcessAckPacket(&frame);
+
+ // Path degrading alarm should be cancelled as there is no more
+ // reretransmittable packets on the wire.
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ // The ping alarm should be set to the retransmittable_on_wire_timeout.
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(retransmittable_on_wire_timeout,
+ connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
+
+ // Simulate firing of the retransmittable on wire and send a PING.
+ EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+ clock_.AdvanceTime(retransmittable_on_wire_timeout);
+ connection_.GetPingAlarm()->Fire();
+
+ // The retransmission alarm and the path degrading alarm should be set as
+ // there is a retransmittable packet (PING) on the wire,
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+
+ // Verify the retransmission delay.
+ QuicTime::Delta min_rto_timeout =
+ QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs);
+ srtt = manager_->GetRttStats()->SmoothedOrInitialRtt();
+ if (GetQuicReloadableFlag(quic_ignore_tlpr_if_sending_ping)) {
+ // First TLP without unacked stream data will no longer use TLPR.
+ expected_delay = std::max(2 * srtt, 1.5 * srtt + 0.5 * min_rto_timeout);
+ } else {
+ expected_delay =
+ std::max(QuicTime::Delta::FromMilliseconds(kMinTailLossProbeTimeoutMs),
+ srtt * 0.5);
+ }
+ EXPECT_EQ(expected_delay,
+ connection_.GetRetransmissionAlarm()->deadline() - clock_.Now());
+
+ // Verify the path degrading delay.
+ // Path degrading delay will count TLPR for the tail loss probe delay.
+ expected_delay =
+ std::max(QuicTime::Delta::FromMilliseconds(kMinTailLossProbeTimeoutMs),
+ srtt * 0.5);
+ // Add 1st RTO.
+ retransmission_delay =
+ std::max(manager_->GetRttStats()->smoothed_rtt() +
+ 4 * manager_->GetRttStats()->mean_deviation(),
+ min_rto_timeout);
+ expected_delay = expected_delay + retransmission_delay;
+ // Add 2nd RTO.
+ expected_delay = expected_delay + retransmission_delay * 2;
+ EXPECT_EQ(expected_delay,
+ QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay());
+
+ // The ping alarm is set for the ping timeout, not the shorter
+ // retransmittable_on_wire_timeout.
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kPingTimeoutSecs),
+ connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
+}
+
+TEST_P(QuicConnectionTest, RTO) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(0);
+
+ QuicTime default_retransmission_time =
+ clock_.ApproximateNow() + DefaultRetransmissionTime();
+ SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr);
+ EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
+
+ EXPECT_EQ(QuicPacketNumber(1u), writer_->header().packet_number);
+ EXPECT_EQ(default_retransmission_time,
+ connection_.GetRetransmissionAlarm()->deadline());
+ // Simulate the retransmission alarm firing.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2), _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(QuicPacketNumber(2u), writer_->header().packet_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(QuicPacketNumber(1u), stop_waiting()->least_unacked);
+}
+
+TEST_P(QuicConnectionTest, RetransmitWithSameEncryptionLevel) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ use_tagging_decrypter();
+
+ // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at
+ // the end of the packet. We can test this to check which encrypter was used.
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+ SendStreamDataToPeer(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0,
+ NO_FIN, nullptr);
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
+
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ SendStreamDataToPeer(3, "foo", 0, NO_FIN, nullptr);
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+
+ {
+ InSequence s;
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(3), _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(4), _, _));
+ }
+
+ // Manually mark both packets for retransmission.
+ connection_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION);
+
+ // Packet should have been sent with ENCRYPTION_INITIAL.
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet());
+
+ // Packet should have been sent with ENCRYPTION_ZERO_RTT.
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+}
+
+TEST_P(QuicConnectionTest, SendHandshakeMessages) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ use_tagging_decrypter();
+ // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at
+ // the end of the packet. We can test this to check which encrypter was used.
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+
+ // Attempt to send a handshake message and have the socket block.
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0,
+ NO_FIN);
+ // The packet should be serialized, but not queued.
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // Switch to the new encrypter.
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+
+ // Now become writeable and flush the packets.
+ writer_->SetWritable();
+ EXPECT_CALL(visitor_, OnCanWrite());
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+
+ // Verify that the handshake packet went out at the null encryption.
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
+}
+
+TEST_P(QuicConnectionTest,
+ DropRetransmitsForNullEncryptedPacketAfterForwardSecure) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+ QuicPacketNumber packet_number;
+ connection_.SendCryptoStreamData();
+
+ // Simulate the retransmission alarm firing and the socket blocking.
+ BlockOnNextWrite();
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Go forward secure.
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ notifier_.NeuterUnencryptedData();
+ connection_.NeuterUnencryptedPackets();
+
+ EXPECT_EQ(QuicTime::Zero(), connection_.GetRetransmissionAlarm()->deadline());
+ // Unblock the socket and ensure that no packets are sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+}
+
+TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+
+ SendStreamDataToPeer(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0,
+ NO_FIN, nullptr);
+
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+
+ SendStreamDataToPeer(2, "bar", 0, NO_FIN, nullptr);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+
+ connection_.RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION);
+}
+
+TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // SetFromConfig is always called after construction from InitializeSession.
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ use_tagging_decrypter();
+
+ const uint8_t tag = 0x07;
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+
+ // Process an encrypted packet which can not yet be decrypted which should
+ // result in the packet being buffered.
+ ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Transition to the new encryption state and process another encrypted packet
+ // which should result in the original packet being processed.
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(2);
+ ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Finally, process a third packet and note that we do not reprocess the
+ // buffered packet.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+}
+
+TEST_P(QuicConnectionTest, TestRetransmitOrder) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(0);
+
+ QuicByteCount first_packet_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(SaveArg<3>(&first_packet_size));
+
+ connection_.SendStreamDataWithString(3, "first_packet", 0, NO_FIN);
+ QuicByteCount second_packet_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(SaveArg<3>(&second_packet_size));
+ connection_.SendStreamDataWithString(3, "second_packet", 12, NO_FIN);
+ EXPECT_NE(first_packet_size, second_packet_size);
+ // Advance the clock by huge time to make sure packets will be retransmitted.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
+ {
+ InSequence s;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, first_packet_size, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, second_packet_size, _));
+ }
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Advance again and expect the packets to be sent again in the same order.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(20));
+ {
+ InSequence s;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, first_packet_size, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, second_packet_size, _));
+ }
+ connection_.GetRetransmissionAlarm()->Fire();
+}
+
+TEST_P(QuicConnectionTest, Buffer100NonDecryptablePacketsThenKeyChange) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // SetFromConfig is always called after construction from InitializeSession.
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ config.set_max_undecryptable_packets(100);
+ connection_.SetFromConfig(config);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ use_tagging_decrypter();
+
+ const uint8_t tag = 0x07;
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+
+ // Process an encrypted packet which can not yet be decrypted which should
+ // result in the packet being buffered.
+ for (uint64_t i = 1; i <= 100; ++i) {
+ ProcessDataPacketAtLevel(i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+
+ // Transition to the new encryption state and process another encrypted packet
+ // which should result in the original packets being processed.
+ EXPECT_FALSE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ EXPECT_TRUE(connection_.GetProcessUndecryptablePacketsAlarm()->IsSet());
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ connection_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(100);
+ connection_.GetProcessUndecryptablePacketsAlarm()->Fire();
+
+ // Finally, process a third packet and note that we do not reprocess the
+ // buffered packet.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(102, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+}
+
+TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+ // Make sure that RTO is not started when the packet is queued.
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ // Test that RTO is started once we write to the socket.
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(0);
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ connection_.SendStreamDataWithString(2, "foo", 0, NO_FIN);
+ connection_.SendStreamDataWithString(3, "bar", 0, NO_FIN);
+ QuicAlarm* retransmission_alarm = connection_.GetRetransmissionAlarm();
+ EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_EQ(clock_.Now() + DefaultRetransmissionTime(),
+ retransmission_alarm->deadline());
+
+ // Advance the time right before the RTO, then receive an ack for the first
+ // packet to delay the RTO.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame ack = InitAckFrame(1);
+ ProcessAckPacket(&ack);
+ // Now we have an RTT sample of DefaultRetransmissionTime(500ms),
+ // so the RTO has increased to 2 * SRTT.
+ EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_EQ(retransmission_alarm->deadline(),
+ clock_.Now() + 2 * DefaultRetransmissionTime());
+
+ // Move forward past the original RTO and ensure the RTO is still pending.
+ clock_.AdvanceTime(2 * DefaultRetransmissionTime());
+
+ // Ensure the second packet gets retransmitted when it finally fires.
+ EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_EQ(retransmission_alarm->deadline(), clock_.ApproximateNow());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ // Manually cancel the alarm to simulate a real test.
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // The new retransmitted packet number should set the RTO to a larger value
+ // than previously.
+ EXPECT_TRUE(retransmission_alarm->IsSet());
+ QuicTime next_rto_time = retransmission_alarm->deadline();
+ QuicTime expected_rto_time =
+ connection_.sent_packet_manager().GetRetransmissionTime();
+ EXPECT_EQ(next_rto_time, expected_rto_time);
+}
+
+TEST_P(QuicConnectionTest, TestQueued) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(0);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // Unblock the writes and actually send.
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, InitialTimeout) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+
+ // SetFromConfig sets the initial timeouts before negotiation.
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ // Subtract a second from the idle timeout on the client side.
+ QuicTime default_timeout =
+ clock_.ApproximateNow() +
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ // Simulate the timeout alarm firing.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1));
+ connection_.GetTimeoutAlarm()->Fire();
+
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, IdleTimeoutAfterFirstSentPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ QuicTime initial_ddl =
+ clock_.ApproximateNow() +
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+ EXPECT_EQ(initial_ddl, connection_.GetTimeoutAlarm()->deadline());
+ EXPECT_TRUE(connection_.connected());
+
+ // Advance the time and send the first packet to the peer.
+ clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20));
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);
+ EXPECT_EQ(QuicPacketNumber(1u), last_packet);
+ // This will be the updated deadline for the connection to idle time out.
+ QuicTime new_ddl = clock_.ApproximateNow() +
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+
+ // Simulate the timeout alarm firing, the connection should not be closed as
+ // a new packet has been sent.
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+ QuicTime::Delta delay = initial_ddl - clock_.ApproximateNow();
+ clock_.AdvanceTime(delay);
+ connection_.GetTimeoutAlarm()->Fire();
+ // Verify the timeout alarm deadline is updated.
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_EQ(new_ddl, connection_.GetTimeoutAlarm()->deadline());
+
+ // Simulate the timeout alarm firing again, the connection now should be
+ // closed.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ clock_.AdvanceTime(new_ddl - clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, IdleTimeoutAfterSendTwoPackets) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ QuicTime initial_ddl =
+ clock_.ApproximateNow() +
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+ EXPECT_EQ(initial_ddl, connection_.GetTimeoutAlarm()->deadline());
+ EXPECT_TRUE(connection_.connected());
+
+ // Immediately send the first packet, this is a rare case but test code will
+ // hit this issue often as MockClock used for tests doesn't move with code
+ // execution until manually adjusted.
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);
+ EXPECT_EQ(QuicPacketNumber(1u), last_packet);
+
+ // Advance the time and send the second packet to the peer.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, &last_packet);
+ EXPECT_EQ(QuicPacketNumber(2u), last_packet);
+
+ if (GetQuicReloadableFlag(
+ quic_fix_time_of_first_packet_sent_after_receiving)) {
+ // Simulate the timeout alarm firing, the connection will be closed.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ clock_.AdvanceTime(initial_ddl - clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ } else {
+ // Simulate the timeout alarm firing, the connection will not be closed.
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+ clock_.AdvanceTime(initial_ddl - clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+
+ // Advance another 20ms, and fire the alarm again. The connection will be
+ // closed.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+ connection_.GetTimeoutAlarm()->Fire();
+ }
+
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, HandshakeTimeout) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Use a shorter handshake timeout than idle timeout for this test.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
+ connection_.SetNetworkTimeouts(timeout, timeout);
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+
+ QuicTime handshake_timeout =
+ clock_.ApproximateNow() + timeout - QuicTime::Delta::FromSeconds(1);
+ EXPECT_EQ(handshake_timeout, connection_.GetTimeoutAlarm()->deadline());
+ EXPECT_TRUE(connection_.connected());
+
+ // Send and ack new data 3 seconds later to lengthen the idle timeout.
+ SendStreamDataToPeer(
+ QuicUtils::GetHeadersStreamId(connection_.transport_version()), "GET /",
+ 0, FIN, nullptr);
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(3));
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&frame);
+
+ // Fire early to verify it wouldn't timeout yet.
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+
+ clock_.AdvanceTime(timeout - QuicTime::Delta::FromSeconds(2));
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_HANDSHAKE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ // Simulate the timeout alarm firing.
+ connection_.GetTimeoutAlarm()->Fire();
+
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, PingAfterSend) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(true));
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+
+ // Advance to 5ms, and send a packet to the peer, which will set
+ // the ping alarm.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ SendStreamDataToPeer(
+ QuicUtils::GetHeadersStreamId(connection_.transport_version()), "GET /",
+ 0, FIN, nullptr);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow() + QuicTime::Delta::FromSeconds(15),
+ connection_.GetPingAlarm()->deadline());
+
+ // Now recevie an ACK of the previous packet, which will move the
+ // ping alarm forward.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ // The ping timer is set slightly less than 15 seconds in the future, because
+ // of the 1s ping timer alarm granularity.
+ EXPECT_EQ(clock_.ApproximateNow() + QuicTime::Delta::FromSeconds(15) -
+ QuicTime::Delta::FromMilliseconds(5),
+ connection_.GetPingAlarm()->deadline());
+
+ writer_->Reset();
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15));
+ EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+ connection_.GetPingAlarm()->Fire();
+ EXPECT_EQ(1u, writer_->frame_count());
+ ASSERT_EQ(1u, writer_->ping_frames().size());
+ writer_->Reset();
+
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(false));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ SendAckPacketToPeer();
+
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, ReducedPingTimeout) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(true));
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+
+ // Use a reduced ping timeout for this connection.
+ connection_.set_ping_timeout(QuicTime::Delta::FromSeconds(10));
+
+ // Advance to 5ms, and send a packet to the peer, which will set
+ // the ping alarm.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ SendStreamDataToPeer(
+ QuicUtils::GetHeadersStreamId(connection_.transport_version()), "GET /",
+ 0, FIN, nullptr);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow() + QuicTime::Delta::FromSeconds(10),
+ connection_.GetPingAlarm()->deadline());
+
+ // Now recevie an ACK of the previous packet, which will move the
+ // ping alarm forward.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ // The ping timer is set slightly less than 10 seconds in the future, because
+ // of the 1s ping timer alarm granularity.
+ EXPECT_EQ(clock_.ApproximateNow() + QuicTime::Delta::FromSeconds(10) -
+ QuicTime::Delta::FromMilliseconds(5),
+ connection_.GetPingAlarm()->deadline());
+
+ writer_->Reset();
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
+ EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+ connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+ }));
+ connection_.GetPingAlarm()->Fire();
+ EXPECT_EQ(1u, writer_->frame_count());
+ ASSERT_EQ(1u, writer_->ping_frames().size());
+ writer_->Reset();
+
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(false));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ SendAckPacketToPeer();
+
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+}
+
+// Tests whether sending an MTU discovery packet to peer successfully causes the
+// maximum packet size to increase.
+TEST_P(QuicConnectionTest, SendMtuDiscoveryPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+
+ // Send an MTU probe.
+ const size_t new_mtu = kDefaultMaxPacketSize + 100;
+ QuicByteCount mtu_probe_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(SaveArg<3>(&mtu_probe_size));
+ connection_.SendMtuDiscoveryPacket(new_mtu);
+ EXPECT_EQ(new_mtu, mtu_probe_size);
+ EXPECT_EQ(QuicPacketNumber(1u), creator_->packet_number());
+
+ // Send more than MTU worth of data. No acknowledgement was received so far,
+ // so the MTU should be at its old value.
+ const std::string data(kDefaultMaxPacketSize + 1, '.');
+ QuicByteCount size_before_mtu_change;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(2)
+ .WillOnce(SaveArg<3>(&size_before_mtu_change))
+ .WillOnce(Return());
+ connection_.SendStreamDataWithString(3, data, 0, FIN);
+ EXPECT_EQ(QuicPacketNumber(3u), creator_->packet_number());
+ EXPECT_EQ(kDefaultMaxPacketSize, size_before_mtu_change);
+
+ // Acknowledge all packets so far.
+ QuicAckFrame probe_ack = InitAckFrame(3);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&probe_ack);
+ EXPECT_EQ(new_mtu, connection_.max_packet_length());
+
+ // Send the same data again. Check that it fits into a single packet now.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(3, data, 0, FIN);
+ EXPECT_EQ(QuicPacketNumber(4u), creator_->packet_number());
+}
+
+// Tests whether MTU discovery does not happen when it is not explicitly enabled
+// by the connection options.
+TEST_P(QuicConnectionTest, MtuDiscoveryDisabled) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+
+ const QuicPacketCount packets_between_probes_base = 10;
+ set_packets_between_probes_base(packets_between_probes_base);
+
+ const QuicPacketCount number_of_packets = packets_between_probes_base * 2;
+ for (QuicPacketCount i = 0; i < number_of_packets; i++) {
+ SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
+ EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ EXPECT_EQ(0u, connection_.mtu_probe_count());
+ }
+}
+
+// Tests whether MTU discovery works when the probe gets acknowledged on the
+// first try.
+TEST_P(QuicConnectionTest, MtuDiscoveryEnabled) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+
+ connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+ const QuicPacketCount packets_between_probes_base = 5;
+ set_packets_between_probes_base(packets_between_probes_base);
+
+ // Send enough packets so that the next one triggers path MTU discovery.
+ for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
+ SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
+ ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ }
+
+ // Trigger the probe.
+ SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN,
+ nullptr);
+ ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ QuicByteCount probe_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(SaveArg<3>(&probe_size));
+ connection_.GetMtuDiscoveryAlarm()->Fire();
+ EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, probe_size);
+
+ const QuicPacketNumber probe_packet_number =
+ FirstSendingPacketNumber() + packets_between_probes_base;
+ ASSERT_EQ(probe_packet_number, creator_->packet_number());
+
+ // Acknowledge all packets sent so far.
+ QuicAckFrame probe_ack = InitAckFrame(probe_packet_number);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&probe_ack);
+ EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, connection_.max_packet_length());
+ EXPECT_EQ(0u, connection_.GetBytesInFlight());
+
+ // Send more packets, and ensure that none of them sets the alarm.
+ for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) {
+ SendStreamDataToPeer(3, ".", packets_between_probes_base + i, NO_FIN,
+ nullptr);
+ ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ }
+
+ EXPECT_EQ(1u, connection_.mtu_probe_count());
+}
+
+// Tests whether MTU discovery works correctly when the probes never get
+// acknowledged.
+TEST_P(QuicConnectionTest, MtuDiscoveryFailed) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+
+ connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+ const QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
+
+ EXPECT_EQ(kPacketsBetweenMtuProbesBase,
+ QuicConnectionPeer::GetPacketsBetweenMtuProbes(&connection_));
+ // Lower the number of probes between packets in order to make the test go
+ // much faster.
+ const QuicPacketCount packets_between_probes_base = 5;
+ set_packets_between_probes_base(packets_between_probes_base);
+
+ // This tests sends more packets than strictly necessary to make sure that if
+ // the connection was to send more discovery packets than needed, those would
+ // get caught as well.
+ const QuicPacketCount number_of_packets =
+ packets_between_probes_base * (1 << (kMtuDiscoveryAttempts + 1));
+ std::vector<QuicPacketNumber> mtu_discovery_packets;
+ // Called by the first ack.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ // Called on many acks.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _))
+ .Times(AnyNumber());
+ for (QuicPacketCount i = 0; i < number_of_packets; i++) {
+ SendStreamDataToPeer(3, "!", i, NO_FIN, nullptr);
+ clock_.AdvanceTime(rtt);
+
+ // Receive an ACK, which marks all data packets as received, and all MTU
+ // discovery packets as missing.
+
+ QuicAckFrame ack;
+
+ if (!mtu_discovery_packets.empty()) {
+ QuicPacketNumber min_packet = *min_element(mtu_discovery_packets.begin(),
+ mtu_discovery_packets.end());
+ QuicPacketNumber max_packet = *max_element(mtu_discovery_packets.begin(),
+ mtu_discovery_packets.end());
+ ack.packets.AddRange(QuicPacketNumber(1), min_packet);
+ ack.packets.AddRange(QuicPacketNumber(max_packet + 1),
+ creator_->packet_number() + 1);
+ ack.largest_acked = creator_->packet_number();
+
+ } else {
+ ack.packets.AddRange(QuicPacketNumber(1), creator_->packet_number() + 1);
+ ack.largest_acked = creator_->packet_number();
+ }
+
+ ProcessAckPacket(&ack);
+
+ // Trigger MTU probe if it would be scheduled now.
+ if (!connection_.GetMtuDiscoveryAlarm()->IsSet()) {
+ continue;
+ }
+
+ // Fire the alarm. The alarm should cause a packet to be sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.GetMtuDiscoveryAlarm()->Fire();
+ // Record the packet number of the MTU discovery packet in order to
+ // mark it as NACK'd.
+ mtu_discovery_packets.push_back(creator_->packet_number());
+ }
+
+ // Ensure the number of packets between probes grows exponentially by checking
+ // it against the closed-form expression for the packet number.
+ ASSERT_EQ(kMtuDiscoveryAttempts, mtu_discovery_packets.size());
+ for (uint64_t i = 0; i < kMtuDiscoveryAttempts; i++) {
+ // 2^0 + 2^1 + 2^2 + ... + 2^n = 2^(n + 1) - 1
+ const QuicPacketCount packets_between_probes =
+ packets_between_probes_base * ((1 << (i + 1)) - 1);
+ EXPECT_EQ(QuicPacketNumber(packets_between_probes + (i + 1)),
+ mtu_discovery_packets[i]);
+ }
+
+ EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ EXPECT_EQ(kDefaultMaxPacketSize, connection_.max_packet_length());
+ EXPECT_EQ(kMtuDiscoveryAttempts, connection_.mtu_probe_count());
+}
+
+// Tests whether MTU discovery works when the writer has a limit on how large a
+// packet can be.
+TEST_P(QuicConnectionTest, MtuDiscoveryWriterLimited) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+
+ const QuicByteCount mtu_limit = kMtuDiscoveryTargetPacketSizeHigh - 1;
+ writer_->set_max_packet_size(mtu_limit);
+ connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+ const QuicPacketCount packets_between_probes_base = 5;
+ set_packets_between_probes_base(packets_between_probes_base);
+
+ // Send enough packets so that the next one triggers path MTU discovery.
+ for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
+ SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
+ ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ }
+
+ // Trigger the probe.
+ SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN,
+ nullptr);
+ ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ QuicByteCount probe_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(SaveArg<3>(&probe_size));
+ connection_.GetMtuDiscoveryAlarm()->Fire();
+ EXPECT_EQ(mtu_limit, probe_size);
+
+ const QuicPacketNumber probe_sequence_number =
+ FirstSendingPacketNumber() + packets_between_probes_base;
+ ASSERT_EQ(probe_sequence_number, creator_->packet_number());
+
+ // Acknowledge all packets sent so far.
+ QuicAckFrame probe_ack = InitAckFrame(probe_sequence_number);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&probe_ack);
+ EXPECT_EQ(mtu_limit, connection_.max_packet_length());
+ EXPECT_EQ(0u, connection_.GetBytesInFlight());
+
+ // Send more packets, and ensure that none of them sets the alarm.
+ for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) {
+ SendStreamDataToPeer(3, ".", packets_between_probes_base + i, NO_FIN,
+ nullptr);
+ ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ }
+
+ EXPECT_EQ(1u, connection_.mtu_probe_count());
+}
+
+// Tests whether MTU discovery works when the writer returns an error despite
+// advertising higher packet length.
+TEST_P(QuicConnectionTest, MtuDiscoveryWriterFailed) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+
+ const QuicByteCount mtu_limit = kMtuDiscoveryTargetPacketSizeHigh - 1;
+ const QuicByteCount initial_mtu = connection_.max_packet_length();
+ EXPECT_LT(initial_mtu, mtu_limit);
+ writer_->set_max_packet_size(mtu_limit);
+ connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+ const QuicPacketCount packets_between_probes_base = 5;
+ set_packets_between_probes_base(packets_between_probes_base);
+
+ // Send enough packets so that the next one triggers path MTU discovery.
+ for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
+ SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
+ ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ }
+
+ // Trigger the probe.
+ SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN,
+ nullptr);
+ ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ writer_->SimulateNextPacketTooLarge();
+ connection_.GetMtuDiscoveryAlarm()->Fire();
+ ASSERT_TRUE(connection_.connected());
+
+ // Send more data.
+ QuicPacketNumber probe_number = creator_->packet_number();
+ QuicPacketCount extra_packets = packets_between_probes_base * 3;
+ for (QuicPacketCount i = 0; i < extra_packets; i++) {
+ connection_.EnsureWritableAndSendStreamData5();
+ ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ }
+
+ // Acknowledge all packets sent so far, except for the lost probe.
+ QuicAckFrame probe_ack =
+ ConstructAckFrame(creator_->packet_number(), probe_number);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&probe_ack);
+ EXPECT_EQ(initial_mtu, connection_.max_packet_length());
+
+ // Send more packets, and ensure that none of them sets the alarm.
+ for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) {
+ connection_.EnsureWritableAndSendStreamData5();
+ ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ }
+
+ EXPECT_EQ(initial_mtu, connection_.max_packet_length());
+ EXPECT_EQ(1u, connection_.mtu_probe_count());
+}
+
+TEST_P(QuicConnectionTest, NoMtuDiscoveryAfterConnectionClosed) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+
+ connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+ const QuicPacketCount packets_between_probes_base = 10;
+ set_packets_between_probes_base(packets_between_probes_base);
+
+ // Send enough packets so that the next one triggers path MTU discovery.
+ for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
+ SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
+ ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+ }
+
+ SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN,
+ nullptr);
+ EXPECT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _));
+ connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ EXPECT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterSend) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ EXPECT_FALSE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_));
+
+ const QuicTime::Delta initial_idle_timeout =
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+ const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime default_timeout = clock_.ApproximateNow() + initial_idle_timeout;
+
+ // When we send a packet, the timeout will change to 5ms +
+ // kInitialIdleTimeoutSecs.
+ clock_.AdvanceTime(five_ms);
+ SendStreamDataToPeer(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, FIN, nullptr);
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // Now send more data. This will not move the timeout because
+ // no data has been received since the previous write.
+ clock_.AdvanceTime(five_ms);
+ SendStreamDataToPeer(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 3, FIN, nullptr);
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // The original alarm will fire. We should not time out because we had a
+ // network event at t=5ms. The alarm will reregister.
+ clock_.AdvanceTime(initial_idle_timeout - five_ms - five_ms);
+ EXPECT_EQ(default_timeout, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_EQ(default_timeout + five_ms,
+ connection_.GetTimeoutAlarm()->deadline());
+
+ // This time, we should time out.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ clock_.AdvanceTime(five_ms);
+ EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterRetransmission) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ EXPECT_FALSE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_));
+
+ const QuicTime start_time = clock_.Now();
+ const QuicTime::Delta initial_idle_timeout =
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+ QuicTime default_timeout = clock_.Now() + initial_idle_timeout;
+
+ connection_.SetMaxTailLossProbes(0);
+ const QuicTime default_retransmission_time =
+ start_time + DefaultRetransmissionTime();
+
+ ASSERT_LT(default_retransmission_time, default_timeout);
+
+ // When we send a packet, the timeout will change to 5 ms +
+ // kInitialIdleTimeoutSecs (but it will not reschedule the alarm).
+ const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5);
+ const QuicTime send_time = start_time + five_ms;
+ clock_.AdvanceTime(five_ms);
+ ASSERT_EQ(send_time, clock_.Now());
+ SendStreamDataToPeer(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, FIN, nullptr);
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // Move forward 5 ms and receive a packet, which will move the timeout
+ // forward 5 ms more (but will not reschedule the alarm).
+ const QuicTime receive_time = send_time + five_ms;
+ clock_.AdvanceTime(receive_time - clock_.Now());
+ ASSERT_EQ(receive_time, clock_.Now());
+ ProcessPacket(1);
+
+ // Now move forward to the retransmission time and retransmit the
+ // packet, which should move the timeout forward again (but will not
+ // reschedule the alarm).
+ EXPECT_EQ(default_retransmission_time + five_ms,
+ connection_.GetRetransmissionAlarm()->deadline());
+ // Simulate the retransmission alarm firing.
+ const QuicTime rto_time = send_time + DefaultRetransmissionTime();
+ const QuicTime final_timeout = rto_time + initial_idle_timeout;
+ clock_.AdvanceTime(rto_time - clock_.Now());
+ ASSERT_EQ(rto_time, clock_.Now());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Advance to the original timeout and fire the alarm. The connection should
+ // timeout, and the alarm should be registered based on the time of the
+ // retransmission.
+ clock_.AdvanceTime(default_timeout - clock_.Now());
+ ASSERT_EQ(default_timeout.ToDebuggingValue(),
+ clock_.Now().ToDebuggingValue());
+ EXPECT_EQ(default_timeout, clock_.Now());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+ ASSERT_EQ(final_timeout.ToDebuggingValue(),
+ connection_.GetTimeoutAlarm()->deadline().ToDebuggingValue());
+
+ // This time, we should time out.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ clock_.AdvanceTime(final_timeout - clock_.Now());
+ EXPECT_EQ(connection_.GetTimeoutAlarm()->deadline(), clock_.Now());
+ EXPECT_EQ(final_timeout, clock_.Now());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, NewTimeoutAfterSendSilentClose) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Same test as above, but complete a handshake which enables silent close,
+ // causing no connection close packet to be sent.
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+
+ // Create a handshake message that also enables silent close.
+ CryptoHandshakeMessage msg;
+ std::string error_details;
+ QuicConfig client_config;
+ client_config.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ client_config.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ client_config.SetIdleNetworkTimeout(
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs),
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
+ client_config.ToHandshakeMessage(&msg);
+ const QuicErrorCode error =
+ config.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ connection_.SetFromConfig(config);
+ EXPECT_TRUE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_));
+
+ const QuicTime::Delta default_idle_timeout =
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs - 1);
+ const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime default_timeout = clock_.ApproximateNow() + default_idle_timeout;
+
+ // When we send a packet, the timeout will change to 5ms +
+ // kInitialIdleTimeoutSecs.
+ clock_.AdvanceTime(five_ms);
+ SendStreamDataToPeer(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, FIN, nullptr);
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // Now send more data. This will not move the timeout because
+ // no data has been received since the previous write.
+ clock_.AdvanceTime(five_ms);
+ SendStreamDataToPeer(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 3, FIN, nullptr);
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // The original alarm will fire. We should not time out because we had a
+ // network event at t=5ms. The alarm will reregister.
+ clock_.AdvanceTime(default_idle_timeout - five_ms - five_ms);
+ EXPECT_EQ(default_timeout, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_EQ(default_timeout + five_ms,
+ connection_.GetTimeoutAlarm()->deadline());
+
+ // This time, we should time out.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ clock_.AdvanceTime(five_ms);
+ EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseAndTLP) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Same test as above, but complete a handshake which enables silent close,
+ // but sending TLPs causes the connection close to be sent.
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+
+ // Create a handshake message that also enables silent close.
+ CryptoHandshakeMessage msg;
+ std::string error_details;
+ QuicConfig client_config;
+ client_config.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ client_config.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ client_config.SetIdleNetworkTimeout(
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs),
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
+ client_config.ToHandshakeMessage(&msg);
+ const QuicErrorCode error =
+ config.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ connection_.SetFromConfig(config);
+ EXPECT_TRUE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_));
+
+ const QuicTime::Delta default_idle_timeout =
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs - 1);
+ const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime default_timeout = clock_.ApproximateNow() + default_idle_timeout;
+
+ // When we send a packet, the timeout will change to 5ms +
+ // kInitialIdleTimeoutSecs.
+ clock_.AdvanceTime(five_ms);
+ SendStreamDataToPeer(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, FIN, nullptr);
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // Retransmit the packet via tail loss probe.
+ clock_.AdvanceTime(connection_.GetRetransmissionAlarm()->deadline() -
+ clock_.Now());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // This time, we should time out and send a connection close due to the TLP.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ clock_.AdvanceTime(connection_.GetTimeoutAlarm()->deadline() -
+ clock_.ApproximateNow() + five_ms);
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterSendSilentCloseWithOpenStreams) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Same test as above, but complete a handshake which enables silent close,
+ // but having open streams causes the connection close to be sent.
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+
+ // Create a handshake message that also enables silent close.
+ CryptoHandshakeMessage msg;
+ std::string error_details;
+ QuicConfig client_config;
+ client_config.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ client_config.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ client_config.SetIdleNetworkTimeout(
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs),
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
+ client_config.ToHandshakeMessage(&msg);
+ const QuicErrorCode error =
+ config.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ connection_.SetFromConfig(config);
+ EXPECT_TRUE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_));
+
+ const QuicTime::Delta default_idle_timeout =
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs - 1);
+ const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime default_timeout = clock_.ApproximateNow() + default_idle_timeout;
+
+ // When we send a packet, the timeout will change to 5ms +
+ // kInitialIdleTimeoutSecs.
+ clock_.AdvanceTime(five_ms);
+ SendStreamDataToPeer(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, FIN, nullptr);
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // Indicate streams are still open.
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(true));
+
+ // This time, we should time out and send a connection close due to the TLP.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ clock_.AdvanceTime(connection_.GetTimeoutAlarm()->deadline() -
+ clock_.ApproximateNow() + five_ms);
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterReceive) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ EXPECT_FALSE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_));
+
+ const QuicTime::Delta initial_idle_timeout =
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+ const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime default_timeout = clock_.ApproximateNow() + initial_idle_timeout;
+
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, NO_FIN);
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 3, NO_FIN);
+
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+ clock_.AdvanceTime(five_ms);
+
+ // When we receive a packet, the timeout will change to 5ms +
+ // kInitialIdleTimeoutSecs.
+ QuicAckFrame ack = InitAckFrame(2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&ack);
+
+ // The original alarm will fire. We should not time out because we had a
+ // network event at t=5ms. The alarm will reregister.
+ clock_.AdvanceTime(initial_idle_timeout - five_ms);
+ EXPECT_EQ(default_timeout, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_EQ(default_timeout + five_ms,
+ connection_.GetTimeoutAlarm()->deadline());
+
+ // This time, we should time out.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ clock_.AdvanceTime(five_ms);
+ EXPECT_EQ(default_timeout + five_ms, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterReceiveNotSendWhenUnacked) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ EXPECT_FALSE(QuicConnectionPeer::IsSilentCloseEnabled(&connection_));
+
+ const QuicTime::Delta initial_idle_timeout =
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+ connection_.SetNetworkTimeouts(
+ QuicTime::Delta::Infinite(),
+ initial_idle_timeout + QuicTime::Delta::FromSeconds(1));
+ const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime default_timeout = clock_.ApproximateNow() + initial_idle_timeout;
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, NO_FIN);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 3, NO_FIN);
+
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ clock_.AdvanceTime(five_ms);
+
+ // When we receive a packet, the timeout will change to 5ms +
+ // kInitialIdleTimeoutSecs.
+ QuicAckFrame ack = InitAckFrame(2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&ack);
+
+ // The original alarm will fire. We should not time out because we had a
+ // network event at t=5ms. The alarm will reregister.
+ clock_.AdvanceTime(initial_idle_timeout - five_ms);
+ EXPECT_EQ(default_timeout, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_EQ(default_timeout + five_ms,
+ connection_.GetTimeoutAlarm()->deadline());
+
+ // Now, send packets while advancing the time and verify that the connection
+ // eventually times out.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_NETWORK_IDLE_TIMEOUT, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+ for (int i = 0; i < 100 && connection_.connected(); ++i) {
+ QUIC_LOG(INFO) << "sending data packet";
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()),
+ "foo", 0, NO_FIN);
+ connection_.GetTimeoutAlarm()->Fire();
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+ EXPECT_FALSE(connection_.connected());
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfter5ClientRTOs) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(2);
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ QuicTagVector connection_options;
+ connection_options.push_back(k5RTO);
+ config.SetConnectionOptionsToSend(connection_options);
+ connection_.SetFromConfig(config);
+
+ // Send stream data.
+ SendStreamDataToPeer(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, FIN, nullptr);
+
+ // Fire the retransmission alarm 6 times, twice for TLP and 4 times for RTO.
+ for (int i = 0; i < 6; ++i) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+ }
+
+ EXPECT_EQ(2u, connection_.sent_packet_manager().GetConsecutiveTlpCount());
+ EXPECT_EQ(4u, connection_.sent_packet_manager().GetConsecutiveRtoCount());
+ // This time, we should time out.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_TOO_MANY_RTOS, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, SendScheduler) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Test that if we send a packet without delay, it is not queued.
+ QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT);
+ std::unique_ptr<QuicPacket> packet =
+ ConstructDataPacket(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
+ QuicPacketCreatorPeer::SetPacketNumber(creator_, 1);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SendPacket(ENCRYPTION_INITIAL, 1, std::move(packet),
+ HAS_RETRANSMITTABLE_DATA, false, false);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, FailToSendFirstPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Test that the connection does not crash when it fails to send the first
+ // packet at which point self_address_ might be uninitialized.
+ QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT);
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1);
+ std::unique_ptr<QuicPacket> packet =
+ ConstructDataPacket(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
+ QuicPacketCreatorPeer::SetPacketNumber(creator_, 1);
+ writer_->SetShouldWriteFail();
+ connection_.SendPacket(ENCRYPTION_INITIAL, 1, std::move(packet),
+ HAS_RETRANSMITTABLE_DATA, false, false);
+}
+
+TEST_P(QuicConnectionTest, SendSchedulerEAGAIN) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicFramerPeer::SetPerspective(&peer_framer_, Perspective::IS_CLIENT);
+ std::unique_ptr<QuicPacket> packet =
+ ConstructDataPacket(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
+ QuicPacketCreatorPeer::SetPacketNumber(creator_, 1);
+ BlockOnNextWrite();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(2u), _, _))
+ .Times(0);
+ connection_.SendPacket(ENCRYPTION_INITIAL, 1, std::move(packet),
+ HAS_RETRANSMITTABLE_DATA, false, false);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+
+ // Queue the first packet.
+ size_t payload_length = connection_.max_packet_length();
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(testing::Return(false));
+ const std::string payload(payload_length, 'a');
+ QuicStreamId first_bidi_stream_id(QuicUtils::GetFirstBidirectionalStreamId(
+ connection_.version().transport_version, Perspective::IS_CLIENT));
+ EXPECT_EQ(0u, connection_
+ .SendStreamDataWithString(first_bidi_stream_id, payload, 0,
+ NO_FIN)
+ .bytes_consumed);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, SendingThreePackets) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+
+ // Make the payload twice the size of the packet, so 3 packets are written.
+ size_t total_payload_length = 2 * connection_.max_packet_length();
+ const std::string payload(total_payload_length, 'a');
+ QuicStreamId first_bidi_stream_id(QuicUtils::GetFirstBidirectionalStreamId(
+ connection_.version().transport_version, Perspective::IS_CLIENT));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+ EXPECT_EQ(payload.size(), connection_
+ .SendStreamDataWithString(first_bidi_stream_id,
+ payload, 0, NO_FIN)
+ .bytes_consumed);
+}
+
+TEST_P(QuicConnectionTest, LoopThroughSendingPacketsWithTruncation) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ set_perspective(Perspective::IS_SERVER);
+ if (GetParam().version.transport_version <= QUIC_VERSION_43) {
+ // For IETF QUIC, encryption level will be switched to FORWARD_SECURE in
+ // SendStreamDataWithString.
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+ }
+ // Set up a larger payload than will fit in one packet.
+ const std::string payload(connection_.max_packet_length(), 'a');
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber());
+
+ // Now send some packets with no truncation.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ EXPECT_EQ(payload.size(),
+ connection_.SendStreamDataWithString(3, payload, 0, NO_FIN)
+ .bytes_consumed);
+ // Track the size of the second packet here. The overhead will be the largest
+ // we see in this test, due to the non-truncated connection id.
+ size_t non_truncated_packet_size = writer_->last_packet_size();
+
+ // Change to a 0 byte connection id.
+ QuicConfig config;
+ QuicConfigPeer::SetReceivedBytesForConnectionId(&config, 0);
+ connection_.SetFromConfig(config);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ EXPECT_EQ(payload.size(),
+ connection_.SendStreamDataWithString(3, payload, 1350, NO_FIN)
+ .bytes_consumed);
+ if (connection_.transport_version() > QUIC_VERSION_43) {
+ // Short header packets sent from server omit connection ID already, and
+ // stream offset size increases from 0 to 2.
+ EXPECT_EQ(non_truncated_packet_size, writer_->last_packet_size() - 2);
+ } else {
+ // Just like above, we save 8 bytes on payload, and 8 on truncation. -2
+ // because stream offset size is 2 instead of 0.
+ EXPECT_EQ(non_truncated_packet_size,
+ writer_->last_packet_size() + 8 * 2 - 2);
+ }
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAck) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicTime ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime();
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(DefaultDelayedAckTime());
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAfterQuiescence) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicConnectionPeer::SetFastAckAfterQuiescence(&connection_, true);
+
+ // The beginning of the connection counts as quiescence.
+ QuicTime ack_time =
+ clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(DefaultDelayedAckTime());
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // Process another packet immedately after sending the ack and expect the
+ // ack alarm to be set delayed ack time in the future.
+ ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime();
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(DefaultDelayedAckTime());
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // Wait 1 second and enesure the ack alarm is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckDecimation) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+ QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION);
+
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() +
+ QuicTime::Delta::FromMilliseconds(kMinRttMs / 4);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 0; i < 9; ++i) {
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ }
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckAckDecimationAfterQuiescence) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+ QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION);
+ QuicConnectionPeer::SetFastAckAfterQuiescence(&connection_, true);
+
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+
+ // The beginning of the connection counts as quiescence.
+ QuicTime ack_time =
+ clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(DefaultDelayedAckTime());
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // Process another packet immedately after sending the ack and expect the
+ // ack alarm to be set delayed ack time in the future.
+ ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime();
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(2, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(DefaultDelayedAckTime());
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // Wait 1 second and enesure the ack alarm is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(3, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // Process enough packets to get into ack decimation behavior.
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ ack_time = clock_.ApproximateNow() +
+ QuicTime::Delta::FromMilliseconds(kMinRttMs / 4);
+ uint64_t kFirstDecimatedPacket = 101;
+ for (unsigned int i = 0; i < kFirstDecimatedPacket - 4; ++i) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(4 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 0; i < 9; ++i) {
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ }
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // Wait 1 second and enesure the ack alarm is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckDecimationUnlimitedAggregation) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ QuicTagVector connection_options;
+ connection_options.push_back(kACKD);
+ // No limit on the number of packets received before sending an ack.
+ connection_options.push_back(kAKDU);
+ config.SetConnectionOptionsToSend(connection_options);
+ connection_.SetFromConfig(config);
+
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() +
+ QuicTime::Delta::FromMilliseconds(kMinRttMs / 4);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // 18 packets will not cause an ack to be sent. 19 will because when
+ // stop waiting frames are in use, we ack every 20 packets no matter what.
+ for (int i = 0; i < 18; ++i) {
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ }
+ // The delayed ack timer should still be set to the expected deadline.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckDecimationEighthRtt) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+ QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION);
+ QuicConnectionPeer::SetAckDecimationDelay(&connection_, 0.125);
+
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() +
+ QuicTime::Delta::FromMilliseconds(kMinRttMs / 8);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 0; i < 9; ++i) {
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ }
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReordering) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+ QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING);
+
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() +
+ QuicTime::Delta::FromMilliseconds(kMinRttMs / 4);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // Receive one packet out of order and then the rest in order.
+ // The loop leaves a one packet gap between acks sent to simulate some loss.
+ for (int j = 0; j < 3; ++j) {
+ // Process packet 10 first and ensure the alarm is one eighth min_rtt.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 9 + (j * 11),
+ !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // The 10th received packet causes an ack to be sent.
+ writer_->Reset();
+ for (int i = 0; i < 9; ++i) {
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ // The ACK shouldn't be sent until the 10th packet is processed.
+ EXPECT_TRUE(writer_->ack_frames().empty());
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + i + (j * 11),
+ !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ }
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithLargeReordering) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+ QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING);
+
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() +
+ QuicTime::Delta::FromMilliseconds(kMinRttMs / 4);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // Process packet 10 first and ensure the alarm is one eighth min_rtt.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 19, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 0; i < 8; ++i) {
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ }
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // The next packet received in order will cause an immediate ack,
+ // because it fills a hole.
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckDecimationWithReorderingEighthRtt) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+ QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING);
+ QuicConnectionPeer::SetAckDecimationDelay(&connection_, 0.125);
+
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() +
+ QuicTime::Delta::FromMilliseconds(kMinRttMs / 8);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // Process packet 10 first and ensure the alarm is one eighth min_rtt.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 9, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 0; i < 8; ++i) {
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ }
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest,
+ SendDelayedAckDecimationWithLargeReorderingEighthRtt) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame()).Times(AnyNumber());
+ QuicConnectionPeer::SetAckMode(&connection_, ACK_DECIMATION_WITH_REORDERING);
+ QuicConnectionPeer::SetAckDecimationDelay(&connection_, 0.125);
+
+ const size_t kMinRttMs = 40;
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kMinRttMs),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() +
+ QuicTime::Delta::FromMilliseconds(kMinRttMs / 8);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8_t tag = 0x07;
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(tag));
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (unsigned int i = 0; i < kFirstDecimatedPacket - 1; ++i) {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(1 + i, !kHasStopWaiting, ENCRYPTION_ZERO_RTT);
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // The same as ProcessPacket(1) except that ENCRYPTION_ZERO_RTT is used
+ // instead of ENCRYPTION_INITIAL.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // Process packet 10 first and ensure the alarm is one eighth min_rtt.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 19, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 0; i < 8; ++i) {
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 1 + i, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ }
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // The next packet received in order will cause an immediate ack,
+ // because it fills a hole.
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacketAtLevel(kFirstDecimatedPacket + 10, !kHasStopWaiting,
+ ENCRYPTION_ZERO_RTT);
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckOnHandshakeConfirmed) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ // Check that ack is sent and that delayed ack alarm is set.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ QuicTime ack_time = clock_.ApproximateNow() + DefaultDelayedAckTime();
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // Completing the handshake as the server does nothing.
+ QuicConnectionPeer::SetPerspective(&connection_, Perspective::IS_SERVER);
+ connection_.OnHandshakeComplete();
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+
+ // Complete the handshake as the client decreases the delayed ack time to 0ms.
+ QuicConnectionPeer::SetPerspective(&connection_, Perspective::IS_CLIENT);
+ connection_.OnHandshakeComplete();
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow(), connection_.GetAckAlarm()->deadline());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckOnSecondPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ ProcessPacket(2);
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, NoAckOnOldNacks) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ // Drop one packet, triggering a sequence of acks.
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ } else {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ }
+ ProcessPacket(2);
+ size_t frames_per_ack = GetParam().no_stop_waiting ? 1 : 2;
+ if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ writer_->Reset();
+ }
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ ProcessPacket(3);
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ writer_->Reset();
+
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ } else {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ }
+ ProcessPacket(4);
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_EQ(0u, writer_->frame_count());
+ } else {
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ writer_->Reset();
+ }
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ ProcessPacket(5);
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ writer_->Reset();
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ // Now only set the timer on the 6th packet, instead of sending another ack.
+ ProcessPacket(6);
+ EXPECT_EQ(0u, writer_->frame_count());
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_));
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+ SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<StrictTaggingDecrypter>(0x01));
+ ProcessDataPacketAtLevel(1, false, ENCRYPTION_FORWARD_SECURE);
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, NO_FIN);
+ // Check that ack is bundled with outgoing data and that delayed ack
+ // alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingCryptoPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ connection_.SendStreamDataWithString(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0,
+ NO_FIN);
+ // Check that ack is bundled with outgoing crypto data.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BlockAndBufferOnFirstCHLOPacketOfTwo) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ connection_.SendStreamDataWithString(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), "foo", 0,
+ NO_FIN);
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_FALSE(connection_.HasQueuedData());
+ connection_.SendStreamDataWithString(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), "bar", 3,
+ NO_FIN);
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_TRUE(connection_.HasQueuedData());
+}
+
+TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnCanWrite())
+ .WillOnce(IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendCryptoStreamData)));
+ // Process a packet from the crypto stream, which is frame1_'s default.
+ // Receiving the CHLO as packet 2 first will cause the connection to
+ // immediately send an ack, due to the packet gap.
+ ProcessPacket(2);
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ if (!QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ } else {
+ EXPECT_EQ(1u, writer_->crypto_frames().size());
+ }
+ EXPECT_EQ(1u, writer_->padding_frames().size());
+ ASSERT_FALSE(writer_->ack_frames().empty());
+ EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front()));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BundleAckForSecondCHLOTwoPacketReject) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ // Process two packets from the crypto stream, which is frame1_'s default,
+ // simulating a 2 packet reject.
+ {
+ ProcessPacket(1);
+ // Send the new CHLO when the REJ is processed.
+ EXPECT_CALL(visitor_, OnStreamFrame(_))
+ .WillOnce(IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendCryptoStreamData)));
+ ProcessDataPacket(2);
+ }
+ // Check that ack is sent and that delayed ack alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ if (!QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ } else {
+ EXPECT_EQ(1u, writer_->crypto_frames().size());
+ }
+ EXPECT_EQ(1u, writer_->padding_frames().size());
+ ASSERT_FALSE(writer_->ack_frames().empty());
+ EXPECT_EQ(QuicPacketNumber(2u), LargestAcked(writer_->ack_frames().front()));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, NO_FIN);
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 3, NO_FIN);
+ // Ack the second packet, which will retransmit the first packet.
+ QuicAckFrame ack = ConstructAckFrame(2, 1);
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(1), kMaxOutgoingPacketSize));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&ack);
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ writer_->Reset();
+
+ // Now ack the retransmission, which will both raise the high water mark
+ // and see if there is more data to send.
+ ack = ConstructAckFrame(3, 1);
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ ProcessAckPacket(&ack);
+
+ // Check that no packet is sent and the ack alarm isn't set.
+ EXPECT_EQ(0u, writer_->frame_count());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ writer_->Reset();
+
+ // Send the same ack, but send both data and an ack together.
+ ack = ConstructAckFrame(3, 1);
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(visitor_, OnCanWrite())
+ .WillOnce(IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::EnsureWritableAndSendStreamData5)));
+ ProcessAckPacket(&ack);
+
+ // Check that ack is bundled with outgoing data and the delayed ack
+ // alarm is reset.
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_TRUE(writer_->stop_waiting_frames().empty());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ }
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_EQ(QuicPacketNumber(3u), LargestAcked(writer_->ack_frames().front()));
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, NoAckSentForClose) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
+ ConnectionCloseSource::FROM_PEER));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ ProcessClosePacket(2);
+}
+
+TEST_P(QuicConnectionTest, SendWhenDisconnected) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
+ ConnectionCloseSource::FROM_SELF));
+ connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ EXPECT_FALSE(connection_.connected());
+ EXPECT_FALSE(connection_.CanWriteStreamData());
+ std::unique_ptr<QuicPacket> packet =
+ ConstructDataPacket(1, !kHasStopWaiting, ENCRYPTION_INITIAL);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+ .Times(0);
+ connection_.SendPacket(ENCRYPTION_INITIAL, 1, std::move(packet),
+ HAS_RETRANSMITTABLE_DATA, false, false);
+}
+
+TEST_P(QuicConnectionTest, SendConnectivityProbingWhenDisconnected) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (!IsDefaultTestConfiguration() ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
+ ConnectionCloseSource::FROM_SELF));
+ connection_.CloseConnection(QUIC_PEER_GOING_AWAY, "no reason",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ EXPECT_FALSE(connection_.connected());
+ EXPECT_FALSE(connection_.CanWriteStreamData());
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+ .Times(0);
+
+ EXPECT_QUIC_BUG(connection_.SendConnectivityProbingPacket(
+ writer_.get(), connection_.peer_address()),
+ "Not sending connectivity probing packet as connection is "
+ "disconnected.");
+}
+
+TEST_P(QuicConnectionTest, WriteBlockedAfterClientSendsConnectivityProbe) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+ TestPacketWriter probing_writer(version(), &clock_);
+ // Block next write so that sending connectivity probe will encounter a
+ // blocked write when send a connectivity probe to the peer.
+ probing_writer.BlockOnNextWrite();
+ // Connection will not be marked as write blocked as connectivity probe only
+ // affects the probing_writer which is not the default.
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(0);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+ .Times(1);
+ connection_.SendConnectivityProbingPacket(&probing_writer,
+ connection_.peer_address());
+}
+
+TEST_P(QuicConnectionTest, WriterBlockedAfterServerSendsConnectivityProbe) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ // Block next write so that sending connectivity probe will encounter a
+ // blocked write when send a connectivity probe to the peer.
+ writer_->BlockOnNextWrite();
+ // Connection will be marked as write blocked as server uses the default
+ // writer to send connectivity probes.
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+ .Times(1);
+ connection_.SendConnectivityProbingPacket(writer_.get(),
+ connection_.peer_address());
+}
+
+TEST_P(QuicConnectionTest, WriterErrorWhenClientSendsConnectivityProbe) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+ TestPacketWriter probing_writer(version(), &clock_);
+ probing_writer.SetShouldWriteFail();
+
+ // Connection should not be closed if a connectivity probe is failed to be
+ // sent.
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+ .Times(0);
+ connection_.SendConnectivityProbingPacket(&probing_writer,
+ connection_.peer_address());
+}
+
+TEST_P(QuicConnectionTest, WriterErrorWhenServerSendsConnectivityProbe) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ writer_->SetShouldWriteFail();
+ // Connection should not be closed if a connectivity probe is failed to be
+ // sent.
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(1), _, _))
+ .Times(0);
+ connection_.SendConnectivityProbingPacket(writer_.get(),
+ connection_.peer_address());
+}
+
+TEST_P(QuicConnectionTest, PublicReset) {
+ if (GetParam().version.transport_version > QUIC_VERSION_43 ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicPublicResetPacket header;
+ // Public reset packet in only built by server.
+ header.connection_id = connection_id_;
+ std::unique_ptr<QuicEncryptedPacket> packet(
+ framer_.BuildPublicResetPacket(header));
+ std::unique_ptr<QuicReceivedPacket> received(
+ ConstructReceivedPacket(*packet, QuicTime::Zero()));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PUBLIC_RESET, _,
+ ConnectionCloseSource::FROM_PEER));
+ connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received);
+}
+
+TEST_P(QuicConnectionTest, IetfStatelessReset) {
+ if (GetParam().version.transport_version <= QUIC_VERSION_43 ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ const QuicUint128 kTestStatelessResetToken = 1010101;
+ QuicConfig config;
+ QuicConfigPeer::SetReceivedStatelessResetToken(&config,
+ kTestStatelessResetToken);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ std::unique_ptr<QuicEncryptedPacket> packet(
+ QuicFramer::BuildIetfStatelessResetPacket(connection_id_,
+ kTestStatelessResetToken));
+ std::unique_ptr<QuicReceivedPacket> received(
+ ConstructReceivedPacket(*packet, QuicTime::Zero()));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PUBLIC_RESET, _,
+ ConnectionCloseSource::FROM_PEER));
+ connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received);
+}
+
+TEST_P(QuicConnectionTest, GoAway) {
+ if (GetParam().version.transport_version == QUIC_VERSION_99 ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ // GoAway is not available in version 99.
+ return;
+ }
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicGoAwayFrame goaway;
+ goaway.last_good_stream_id = 1;
+ goaway.error_code = QUIC_PEER_GOING_AWAY;
+ goaway.reason_phrase = "Going away.";
+ EXPECT_CALL(visitor_, OnGoAway(_));
+ ProcessGoAwayPacket(&goaway);
+}
+
+TEST_P(QuicConnectionTest, WindowUpdate) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicWindowUpdateFrame window_update;
+ window_update.stream_id = 3;
+ window_update.byte_offset = 1234;
+ EXPECT_CALL(visitor_, OnWindowUpdateFrame(_));
+ ProcessFramePacket(QuicFrame(&window_update));
+}
+
+TEST_P(QuicConnectionTest, Blocked) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicBlockedFrame blocked;
+ blocked.stream_id = 3;
+ EXPECT_CALL(visitor_, OnBlockedFrame(_));
+ ProcessFramePacket(QuicFrame(&blocked));
+ EXPECT_EQ(1u, connection_.GetStats().blocked_frames_received);
+ EXPECT_EQ(0u, connection_.GetStats().blocked_frames_sent);
+}
+
+TEST_P(QuicConnectionTest, ZeroBytePacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Don't close the connection for zero byte packets.
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(0);
+ QuicReceivedPacket encrypted(nullptr, 0, QuicTime::Zero());
+ connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, encrypted);
+}
+
+TEST_P(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
+ if (GetParam().version.transport_version > QUIC_VERSION_43 ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Set the packet number of the ack packet to be least unacked (4).
+ QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 3);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessStopWaitingPacket(InitStopWaitingFrame(4));
+ EXPECT_FALSE(outgoing_ack()->packets.Empty());
+}
+
+TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Turn off QUIC_VERSION_99.
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ connection_.SetSupportedVersions(CurrentSupportedVersions());
+ set_perspective(Perspective::IS_SERVER);
+ if (GetParam().version.transport_version > QUIC_VERSION_43) {
+ peer_framer_.set_version_for_tests(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99));
+ } else {
+ peer_framer_.set_version_for_tests(UnsupportedQuicVersion());
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = connection_id_;
+ header.version_flag = true;
+ header.packet_number = QuicPacketNumber(12);
+
+ if (QuicVersionHasLongHeaderLengths(
+ peer_framer_.version().transport_version)) {
+ header.long_packet_type = INITIAL;
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1_));
+ std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(12), *packet,
+ buffer, kMaxOutgoingPacketSize);
+
+ framer_.set_version(version());
+ // Writer's framer's perspective is client, so that it needs to have the right
+ // version to process either IETF or GQUIC version negotiation packet.
+ writer_->SetSupportedVersions({version()});
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
+ EXPECT_TRUE(writer_->version_negotiation_packet() != nullptr);
+
+ ParsedQuicVersionVector supported_versions = CurrentSupportedVersions();
+ ASSERT_EQ(supported_versions.size(),
+ writer_->version_negotiation_packet()->versions.size());
+
+ // We expect all versions in supported_versions to be
+ // included in the packet.
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ EXPECT_EQ(supported_versions[i],
+ writer_->version_negotiation_packet()->versions[i]);
+ }
+}
+
+TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Turn off QUIC_VERSION_99.
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ connection_.SetSupportedVersions(CurrentSupportedVersions());
+ set_perspective(Perspective::IS_SERVER);
+ if (GetParam().version.transport_version > QUIC_VERSION_43) {
+ peer_framer_.set_version_for_tests(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99));
+ } else {
+ peer_framer_.set_version_for_tests(UnsupportedQuicVersion());
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = connection_id_;
+ header.version_flag = true;
+ header.packet_number = QuicPacketNumber(12);
+
+ if (QuicVersionHasLongHeaderLengths(
+ peer_framer_.version().transport_version)) {
+ header.long_packet_type = INITIAL;
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1_));
+ std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(12), *packet,
+ buffer, kMaxOutgoingPacketSize);
+
+ framer_.set_version(version());
+ BlockOnNextWrite();
+ // Writer's framer's perspective is client, so that it needs to have the right
+ // version to process either IETF or GQUIC version negotiation packet.
+ writer_->SetSupportedVersions({version()});
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
+ EXPECT_EQ(0u, writer_->last_packet_size());
+ EXPECT_TRUE(connection_.HasQueuedData());
+
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_TRUE(writer_->version_negotiation_packet() != nullptr);
+
+ ParsedQuicVersionVector supported_versions = CurrentSupportedVersions();
+ ASSERT_EQ(supported_versions.size(),
+ writer_->version_negotiation_packet()->versions.size());
+
+ // We expect all versions in supported_versions to be
+ // included in the packet.
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ EXPECT_EQ(supported_versions[i],
+ writer_->version_negotiation_packet()->versions[i]);
+ }
+}
+
+TEST_P(QuicConnectionTest,
+ ServerSendsVersionNegotiationPacketSocketBlockedDataBuffered) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Turn off QUIC_VERSION_99.
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ connection_.SetSupportedVersions(CurrentSupportedVersions());
+ set_perspective(Perspective::IS_SERVER);
+ if (GetParam().version.transport_version > QUIC_VERSION_43) {
+ peer_framer_.set_version_for_tests(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99));
+ } else {
+ peer_framer_.set_version_for_tests(UnsupportedQuicVersion());
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = connection_id_;
+ header.version_flag = true;
+ header.packet_number = QuicPacketNumber(12);
+
+ if (QuicVersionHasLongHeaderLengths(
+ peer_framer_.version().transport_version)) {
+ header.long_packet_type = INITIAL;
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1_));
+ std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encryped_length =
+ framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(12), *packet,
+ buffer, kMaxOutgoingPacketSize);
+
+ framer_.set_version(version());
+ set_perspective(Perspective::IS_SERVER);
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ // Writer's framer's perspective is client, so that it needs to have the right
+ // version to process either IETF or GQUIC version negotiation packet.
+ writer_->SetSupportedVersions({version()});
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encryped_length, QuicTime::Zero(), false));
+ EXPECT_EQ(0u, writer_->last_packet_size());
+ EXPECT_FALSE(connection_.HasQueuedData());
+}
+
+TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Start out with some unsupported version.
+ QuicConnectionPeer::GetFramer(&connection_)
+ ->set_version_for_tests(ParsedQuicVersion(
+ PROTOCOL_UNSUPPORTED,
+ GetParam().version.transport_version == QUIC_VERSION_99
+ ? QUIC_VERSION_99
+ : QUIC_VERSION_UNSUPPORTED));
+
+ // Send a version negotiation packet.
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ peer_framer_.BuildVersionNegotiationPacket(
+ connection_id_, connection_.transport_version() > QUIC_VERSION_43,
+ AllSupportedVersions()));
+ std::unique_ptr<QuicReceivedPacket> received(
+ ConstructReceivedPacket(*encrypted, QuicTime::Zero()));
+ if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) {
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_VERSION, _,
+ ConnectionCloseSource::FROM_SELF));
+ }
+ connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received);
+ if (GetQuicReloadableFlag(quic_no_client_conn_ver_negotiation)) {
+ EXPECT_FALSE(connection_.connected());
+ return;
+ }
+
+ // Now force another packet. The connection should transition into
+ // NEGOTIATED_VERSION state and tell the packet creator to StopSendingVersion.
+ QuicPacketHeader header;
+ header.destination_connection_id = connection_id_;
+ header.destination_connection_id_included = CONNECTION_ID_ABSENT;
+ header.packet_number = QuicPacketNumber(12);
+ header.version_flag = false;
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1_));
+ std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(12),
+ *packet, buffer, kMaxOutgoingPacketSize);
+ ASSERT_NE(0u, encrypted_length);
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
+ if (GetParam().version.transport_version > QUIC_VERSION_43) {
+ // IETF QUIC stops sending version when switch to FORWARD_SECURE.
+ EXPECT_NE(ENCRYPTION_FORWARD_SECURE, connection_.encryption_level());
+ ASSERT_TRUE(QuicPacketCreatorPeer::SendVersionInPacket(creator_));
+ } else {
+ ASSERT_FALSE(QuicPacketCreatorPeer::SendVersionInPacket(creator_));
+ }
+}
+
+TEST_P(QuicConnectionTest, BadVersionNegotiation) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Send a version negotiation packet with the version the client started with.
+ // It should be rejected.
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, _,
+ ConnectionCloseSource::FROM_SELF));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ framer_.BuildVersionNegotiationPacket(
+ connection_id_, connection_.transport_version() > QUIC_VERSION_43,
+ AllSupportedVersions()));
+ std::unique_ptr<QuicReceivedPacket> received(
+ ConstructReceivedPacket(*encrypted, QuicTime::Zero()));
+ connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, *received);
+}
+
+TEST_P(QuicConnectionTest, CheckSendStats) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetMaxTailLossProbes(0);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SendStreamDataWithString(3, "first", 0, NO_FIN);
+ size_t first_packet_size = writer_->last_packet_size();
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SendStreamDataWithString(5, "second", 0, NO_FIN);
+ size_t second_packet_size = writer_->last_packet_size();
+
+ // 2 retransmissions due to rto, 1 due to explicit nack.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+
+ // Retransmit due to RTO.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Retransmit due to explicit nacks.
+ QuicAckFrame nack_three =
+ InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)},
+ {QuicPacketNumber(4), QuicPacketNumber(5)}});
+
+ LostPacketVector lost_packets;
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(1), kMaxOutgoingPacketSize));
+ lost_packets.push_back(
+ LostPacket(QuicPacketNumber(3), kMaxOutgoingPacketSize));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _))
+ .WillOnce(SetArgPointee<5>(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ if (!connection_.session_decides_what_to_write()) {
+ EXPECT_CALL(visitor_, OnCanWrite());
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessAckPacket(&nack_three);
+
+ EXPECT_CALL(*send_algorithm_, BandwidthEstimate())
+ .WillOnce(Return(QuicBandwidth::Zero()));
+
+ const QuicConnectionStats& stats = connection_.GetStats();
+ // For IETF QUIC, version is not included as the encryption level switches to
+ // FORWARD_SECURE in SendStreamDataWithString.
+ size_t save_on_version =
+ GetParam().version.transport_version > QUIC_VERSION_43 ? 0
+ : kQuicVersionSize;
+ EXPECT_EQ(3 * first_packet_size + 2 * second_packet_size - save_on_version,
+ stats.bytes_sent);
+ EXPECT_EQ(5u, stats.packets_sent);
+ EXPECT_EQ(2 * first_packet_size + second_packet_size - save_on_version,
+ stats.bytes_retransmitted);
+ EXPECT_EQ(3u, stats.packets_retransmitted);
+ EXPECT_EQ(1u, stats.rto_count);
+ EXPECT_EQ(kDefaultMaxPacketSize, stats.max_packet_size);
+}
+
+TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Construct a packet with stream frame and connection close frame.
+ QuicPacketHeader header;
+ header.destination_connection_id = connection_id_;
+ if (peer_framer_.transport_version() > QUIC_VERSION_43) {
+ header.destination_connection_id_included = CONNECTION_ID_ABSENT;
+ }
+ header.packet_number = QuicPacketNumber(1);
+ header.version_flag = false;
+
+ QuicConnectionCloseFrame qccf(QUIC_PEER_GOING_AWAY);
+ if (peer_framer_.transport_version() == QUIC_VERSION_99) {
+ // Default close-type is Google QUIC. If doing IETF/V99 then
+ // set close type to be IETF CC/T.
+ qccf.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1_));
+ frames.push_back(QuicFrame(&qccf));
+ std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames));
+ EXPECT_TRUE(nullptr != packet);
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(1),
+ *packet, buffer, kMaxOutgoingPacketSize);
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, _,
+ ConnectionCloseSource::FROM_PEER));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false));
+}
+
+TEST_P(QuicConnectionTest, SelectMutualVersion) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ connection_.SetSupportedVersions(AllSupportedVersions());
+ // Set the connection to speak the lowest quic version.
+ connection_.set_version(QuicVersionMin());
+ EXPECT_EQ(QuicVersionMin(), connection_.version());
+
+ // Pass in available versions which includes a higher mutually supported
+ // version. The higher mutually supported version should be selected.
+ ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ EXPECT_TRUE(connection_.SelectMutualVersion(supported_versions));
+ EXPECT_EQ(QuicVersionMax(), connection_.version());
+
+ // Expect that the lowest version is selected.
+ // Ensure the lowest supported version is less than the max, unless they're
+ // the same.
+ ParsedQuicVersionVector lowest_version_vector;
+ lowest_version_vector.push_back(QuicVersionMin());
+ EXPECT_TRUE(connection_.SelectMutualVersion(lowest_version_vector));
+ EXPECT_EQ(QuicVersionMin(), connection_.version());
+
+ // Shouldn't be able to find a mutually supported version.
+ ParsedQuicVersionVector unsupported_version;
+ unsupported_version.push_back(UnsupportedQuicVersion());
+ EXPECT_FALSE(connection_.SelectMutualVersion(unsupported_version));
+}
+
+TEST_P(QuicConnectionTest, ConnectionCloseWhenWritable) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_FALSE(writer_->IsWriteBlocked());
+
+ // Send a packet.
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+
+ TriggerConnectionClose();
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+}
+
+TEST_P(QuicConnectionTest, ConnectionCloseGettingWriteBlocked) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ BlockOnNextWrite();
+ TriggerConnectionClose();
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+}
+
+TEST_P(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ TriggerConnectionClose();
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+}
+
+TEST_P(QuicConnectionTest, OnPacketSentDebugVisitor) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ MockQuicConnectionDebugVisitor debug_visitor;
+ connection_.set_debug_visitor(&debug_visitor);
+
+ EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN);
+
+ EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1);
+ connection_.SendConnectivityProbingPacket(writer_.get(),
+ connection_.peer_address());
+}
+
+TEST_P(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.packet_number = QuicPacketNumber(1);
+ if (GetParam().version.transport_version > QUIC_VERSION_43) {
+ header.form = IETF_QUIC_LONG_HEADER_PACKET;
+ }
+
+ MockQuicConnectionDebugVisitor debug_visitor;
+ connection_.set_debug_visitor(&debug_visitor);
+ EXPECT_CALL(debug_visitor, OnPacketHeader(Ref(header))).Times(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)).Times(1);
+ EXPECT_CALL(debug_visitor, OnSuccessfulVersionNegotiation(_)).Times(1);
+ connection_.OnPacketHeader(header);
+}
+
+TEST_P(QuicConnectionTest, Pacing) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ TestConnection server(connection_id_, kSelfAddress, helper_.get(),
+ alarm_factory_.get(), writer_.get(),
+ Perspective::IS_SERVER, version());
+ TestConnection client(connection_id_, kPeerAddress, helper_.get(),
+ alarm_factory_.get(), writer_.get(),
+ Perspective::IS_CLIENT, version());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::UsingPacing(
+ static_cast<const QuicSentPacketManager*>(
+ &client.sent_packet_manager())));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::UsingPacing(
+ static_cast<const QuicSentPacketManager*>(
+ &server.sent_packet_manager())));
+}
+
+TEST_P(QuicConnectionTest, WindowUpdateInstigateAcks) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Send a WINDOW_UPDATE frame.
+ QuicWindowUpdateFrame window_update;
+ window_update.stream_id = 3;
+ window_update.byte_offset = 1234;
+ EXPECT_CALL(visitor_, OnWindowUpdateFrame(_));
+ ProcessFramePacket(QuicFrame(&window_update));
+
+ // Ensure that this has caused the ACK alarm to be set.
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ EXPECT_TRUE(ack_alarm->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BlockedFrameInstigateAcks) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Send a BLOCKED frame.
+ QuicBlockedFrame blocked;
+ blocked.stream_id = 3;
+ EXPECT_CALL(visitor_, OnBlockedFrame(_));
+ ProcessFramePacket(QuicFrame(&blocked));
+
+ // Ensure that this has caused the ACK alarm to be set.
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ EXPECT_TRUE(ack_alarm->IsSet());
+}
+
+TEST_P(QuicConnectionTest, ReevaluateTimeUntilSendOnAck) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Enable pacing.
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+
+ // Send two packets. One packet is not sufficient because if it gets acked,
+ // there will be no packets in flight after that and the pacer will always
+ // allow the next packet in that situation.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo",
+ 0, NO_FIN);
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "bar",
+ 3, NO_FIN);
+ connection_.OnCanWrite();
+
+ // Schedule the next packet for a few milliseconds in future.
+ QuicSentPacketManagerPeer::DisablePacerBursts(manager_);
+ QuicTime scheduled_pacing_time =
+ clock_.Now() + QuicTime::Delta::FromMilliseconds(5);
+ QuicSentPacketManagerPeer::SetNextPacedPacketTime(manager_,
+ scheduled_pacing_time);
+
+ // Send a packet and have it be blocked by congestion control.
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(false));
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), "baz",
+ 6, NO_FIN);
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+
+ // Process an ack and the send alarm will be set to the new 5ms delay.
+ QuicAckFrame ack = InitAckFrame(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
+ ProcessAckPacket(&ack);
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_TRUE(connection_.GetSendAlarm()->IsSet());
+ EXPECT_EQ(scheduled_pacing_time, connection_.GetSendAlarm()->deadline());
+ writer_->Reset();
+}
+
+TEST_P(QuicConnectionTest, SendAcksImmediately) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacket(1);
+ CongestionBlockWrites();
+ SendAckPacketToPeer();
+}
+
+TEST_P(QuicConnectionTest, SendPingImmediately) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ MockQuicConnectionDebugVisitor debug_visitor;
+ connection_.set_debug_visitor(&debug_visitor);
+
+ CongestionBlockWrites();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1);
+ EXPECT_CALL(debug_visitor, OnPingSent()).Times(1);
+ connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+ EXPECT_FALSE(connection_.HasQueuedData());
+}
+
+TEST_P(QuicConnectionTest, SendBlockedImmediately) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ MockQuicConnectionDebugVisitor debug_visitor;
+ connection_.set_debug_visitor(&debug_visitor);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(1);
+ EXPECT_EQ(0u, connection_.GetStats().blocked_frames_sent);
+ connection_.SendControlFrame(QuicFrame(new QuicBlockedFrame(1, 3)));
+ EXPECT_EQ(1u, connection_.GetStats().blocked_frames_sent);
+ EXPECT_FALSE(connection_.HasQueuedData());
+}
+
+TEST_P(QuicConnectionTest, SendingUnencryptedStreamDataFails) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (!IsDefaultTestConfiguration()) {
+ return;
+ }
+
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA,
+ _, ConnectionCloseSource::FROM_SELF));
+ struct iovec iov;
+ MakeIOVector("", &iov);
+ EXPECT_QUIC_BUG(connection_.SaveAndSendStreamData(3, &iov, 1, 0, 0, FIN),
+ "Cannot send stream data without encryption.");
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, SetRetransmissionAlarmForCryptoPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendCryptoStreamData();
+
+ // Verify retransmission timer is correctly set after crypto packet has been
+ // sent.
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ QuicTime retransmission_time =
+ QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetRetransmissionTime();
+ EXPECT_NE(retransmission_time, clock_.ApproximateNow());
+ EXPECT_EQ(retransmission_time,
+ connection_.GetRetransmissionAlarm()->deadline());
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.GetRetransmissionAlarm()->Fire();
+}
+
+TEST_P(QuicConnectionTest, PathDegradingAlarmForCryptoPacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.IsPathDegrading());
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendCryptoStreamData();
+
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.IsPathDegrading());
+ QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+
+ // Fire the path degrading alarm, path degrading signal should be sent to
+ // the visitor.
+ EXPECT_CALL(visitor_, OnPathDegrading());
+ clock_.AdvanceTime(delay);
+ connection_.GetPathDegradingAlarm()->Fire();
+ EXPECT_TRUE(connection_.IsPathDegrading());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+}
+
+// Includes regression test for b/69979024.
+TEST_P(QuicConnectionTest, PathDegradingAlarmForNonCryptoPackets) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.IsPathDegrading());
+
+ const char data[] = "data";
+ size_t data_size = strlen(data);
+ QuicStreamOffset offset = 0;
+
+ for (int i = 0; i < 2; ++i) {
+ // Send a packet. Now there's a retransmittable packet on the wire, so the
+ // path degrading alarm should be set.
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), data,
+ offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ // Check the deadline of the path degrading alarm.
+ QuicTime::Delta delay =
+ QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+
+ // Send a second packet. The path degrading alarm's deadline should remain
+ // the same.
+ // Regression test for b/69979024.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline();
+ connection_.SendStreamDataWithString(
+ GetNthClientInitiatedStreamId(1, connection_.transport_version()), data,
+ offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline());
+
+ // Now receive an ACK of the first packet. This should advance the path
+ // degrading alarm's deadline since forward progress has been made.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ if (i == 0) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ }
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame = InitAckFrame(
+ {{QuicPacketNumber(1u + 2u * i), QuicPacketNumber(2u + 2u * i)}});
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ // Check the deadline of the path degrading alarm.
+ delay = QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+
+ if (i == 0) {
+ // Now receive an ACK of the second packet. Since there are no more
+ // retransmittable packets on the wire, this should cancel the path
+ // degrading alarm.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
+ ProcessAckPacket(&frame);
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ } else {
+ // Advance time to the path degrading alarm's deadline and simulate
+ // firing the alarm.
+ clock_.AdvanceTime(delay);
+ EXPECT_CALL(visitor_, OnPathDegrading());
+ connection_.GetPathDegradingAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ }
+ }
+ EXPECT_TRUE(connection_.IsPathDegrading());
+}
+
+TEST_P(QuicConnectionTest, RetransmittableOnWireSetsPingAlarm) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ const QuicTime::Delta retransmittable_on_wire_timeout =
+ QuicTime::Delta::FromMilliseconds(50);
+ connection_.set_retransmittable_on_wire_timeout(
+ retransmittable_on_wire_timeout);
+
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.IsPathDegrading());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+
+ const char data[] = "data";
+ size_t data_size = strlen(data);
+ QuicStreamOffset offset = 0;
+
+ // Send a packet.
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ // Now there's a retransmittable packet on the wire, so the path degrading
+ // alarm should be set.
+ // The retransmittable-on-wire alarm should not be set.
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+ ASSERT_TRUE(connection_.sent_packet_manager().HasInFlightPackets());
+ // The ping alarm is set for the ping timeout, not the shorter
+ // retransmittable_on_wire_timeout.
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ QuicTime::Delta ping_delay = QuicTime::Delta::FromSeconds(kPingTimeoutSecs);
+ EXPECT_EQ((clock_.ApproximateNow() + ping_delay),
+ connection_.GetPingAlarm()->deadline());
+
+ // Now receive an ACK of the packet.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame =
+ InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
+ ProcessAckPacket(&frame);
+ // No more retransmittable packets on the wire, so the path degrading alarm
+ // should be cancelled, and the ping alarm should be set to the
+ // retransmittable_on_wire_timeout.
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout,
+ connection_.GetPingAlarm()->deadline());
+
+ // Simulate firing the ping alarm and sending a PING.
+ clock_.AdvanceTime(retransmittable_on_wire_timeout);
+ EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+ connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+ }));
+ connection_.GetPingAlarm()->Fire();
+
+ // Now there's a retransmittable packet (PING) on the wire, so the path
+ // degrading alarm should be set.
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ delay = QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+}
+
+// This test verifies that the connection marks path as degrading and does not
+// spin timer to detect path degrading when a new packet is sent on the
+// degraded path.
+TEST_P(QuicConnectionTest, NoPathDegradingAlarmIfPathIsDegrading) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.IsPathDegrading());
+
+ const char data[] = "data";
+ size_t data_size = strlen(data);
+ QuicStreamOffset offset = 0;
+
+ // Send the first packet. Now there's a retransmittable packet on the wire, so
+ // the path degrading alarm should be set.
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ // Check the deadline of the path degrading alarm.
+ QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+
+ // Send a second packet. The path degrading alarm's deadline should remain
+ // the same.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline();
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline());
+
+ // Now receive an ACK of the first packet. This should advance the path
+ // degrading alarm's deadline since forward progress has been made.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame =
+ InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}});
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ // Check the deadline of the path degrading alarm.
+ delay = QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+
+ // Advance time to the path degrading alarm's deadline and simulate
+ // firing the path degrading alarm. This path will be considered as
+ // degrading.
+ clock_.AdvanceTime(delay);
+ EXPECT_CALL(visitor_, OnPathDegrading()).Times(1);
+ connection_.GetPathDegradingAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_TRUE(connection_.IsPathDegrading());
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ // Send a third packet. The path degrading alarm is no longer set but path
+ // should still be marked as degrading.
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_TRUE(connection_.IsPathDegrading());
+}
+
+// This test verifies that the connection unmarks path as degrarding and spins
+// the timer to detect future path degrading when forward progress is made
+// after path has been marked degrading.
+TEST_P(QuicConnectionTest, UnmarkPathDegradingOnForwardProgress) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.IsPathDegrading());
+
+ const char data[] = "data";
+ size_t data_size = strlen(data);
+ QuicStreamOffset offset = 0;
+
+ // Send the first packet. Now there's a retransmittable packet on the wire, so
+ // the path degrading alarm should be set.
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ // Check the deadline of the path degrading alarm.
+ QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+
+ // Send a second packet. The path degrading alarm's deadline should remain
+ // the same.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline();
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline());
+
+ // Now receive an ACK of the first packet. This should advance the path
+ // degrading alarm's deadline since forward progress has been made.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame =
+ InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}});
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+ // Check the deadline of the path degrading alarm.
+ delay = QuicConnectionPeer::GetSentPacketManager(&connection_)
+ ->GetPathDegradingDelay();
+ EXPECT_EQ(clock_.ApproximateNow() + delay,
+ connection_.GetPathDegradingAlarm()->deadline());
+
+ // Advance time to the path degrading alarm's deadline and simulate
+ // firing the alarm.
+ clock_.AdvanceTime(delay);
+ EXPECT_CALL(visitor_, OnPathDegrading()).Times(1);
+ connection_.GetPathDegradingAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_TRUE(connection_.IsPathDegrading());
+
+ // Send a third packet. The path degrading alarm is no longer set but path
+ // should still be marked as degrading.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+ EXPECT_TRUE(connection_.IsPathDegrading());
+
+ // Now receive an ACK of the second packet. This should unmark the path as
+ // degrading. And will set a timer to detect new path degrading.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
+ ProcessAckPacket(&frame);
+ EXPECT_FALSE(connection_.IsPathDegrading());
+ EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, NoPathDegradingOnServer) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ EXPECT_FALSE(connection_.IsPathDegrading());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+
+ // Send data.
+ const char data[] = "data";
+ connection_.SendStreamDataWithString(1, data, 0, NO_FIN);
+ EXPECT_FALSE(connection_.IsPathDegrading());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+
+ // Ack data.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame =
+ InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}});
+ ProcessAckPacket(&frame);
+ EXPECT_FALSE(connection_.IsPathDegrading());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, NoPathDegradingAfterSendingAck) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessDataPacket(1);
+ SendAckPacketToPeer();
+ EXPECT_FALSE(connection_.sent_packet_manager().unacked_packets().empty());
+ EXPECT_FALSE(connection_.sent_packet_manager().HasInFlightPackets());
+ EXPECT_FALSE(connection_.IsPathDegrading());
+ EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, MultipleCallsToCloseConnection) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Verifies that multiple calls to CloseConnection do not
+ // result in multiple attempts to close the connection - it will be marked as
+ // disconnected after the first call.
+ EXPECT_CALL(visitor_, OnConnectionClosed(_, _, _)).Times(1);
+ connection_.CloseConnection(QUIC_NO_ERROR, "no reason",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ connection_.CloseConnection(QUIC_NO_ERROR, "no reason",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+}
+
+TEST_P(QuicConnectionTest, ServerReceivesChloOnNonCryptoStream) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ CryptoHandshakeMessage message;
+ CryptoFramer framer;
+ message.set_tag(kCHLO);
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ frame1_.stream_id = 10;
+ frame1_.data_buffer = data->data();
+ frame1_.data_length = data->length();
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_MAYBE_CORRUPTED_MEMORY, _,
+ ConnectionCloseSource::FROM_SELF));
+ ForceProcessFramePacket(QuicFrame(frame1_));
+}
+
+TEST_P(QuicConnectionTest, ClientReceivesRejOnNonCryptoStream) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ CryptoHandshakeMessage message;
+ CryptoFramer framer;
+ message.set_tag(kREJ);
+ std::unique_ptr<QuicData> data = framer.ConstructHandshakeMessage(message);
+ frame1_.stream_id = 10;
+ frame1_.data_buffer = data->data();
+ frame1_.data_length = data->length();
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_MAYBE_CORRUPTED_MEMORY, _,
+ ConnectionCloseSource::FROM_SELF));
+ ForceProcessFramePacket(QuicFrame(frame1_));
+}
+
+TEST_P(QuicConnectionTest, CloseConnectionOnPacketTooLarge) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ SimulateNextPacketTooLarge();
+ // A connection close packet is sent
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _,
+ ConnectionCloseSource::FROM_SELF))
+ .Times(1);
+ connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+}
+
+TEST_P(QuicConnectionTest, AlwaysGetPacketTooLarge) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Test even we always get packet too large, we do not infinitely try to send
+ // close packet.
+ AlwaysGetPacketTooLarge();
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _,
+ ConnectionCloseSource::FROM_SELF))
+ .Times(1);
+ connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+}
+
+// Verify that if connection has no outstanding data, it notifies the send
+// algorithm after the write.
+TEST_P(QuicConnectionTest, SendDataAndBecomeApplicationLimited) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(1);
+ {
+ InSequence seq;
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite())
+ .WillRepeatedly(Return(false));
+ }
+
+ connection_.SendStreamData3();
+}
+
+// Verify that the connection does not become app-limited if there is
+// outstanding data to send after the write.
+TEST_P(QuicConnectionTest, NotBecomeApplicationLimitedIfMoreDataAvailable) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(0);
+ {
+ InSequence seq;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true));
+ }
+
+ connection_.SendStreamData3();
+}
+
+// Verify that the connection does not become app-limited after blocked write
+// even if there is outstanding data to send after the write.
+TEST_P(QuicConnectionTest, NotBecomeApplicationLimitedDueToWriteBlock) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(0);
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true));
+ BlockOnNextWrite();
+
+ connection_.SendStreamData3();
+
+ // Now unblock the writer, become congestion control blocked,
+ // and ensure we become app-limited after writing.
+ writer_->SetWritable();
+ CongestionBlockWrites();
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(false));
+ {
+ InSequence seq;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(1);
+ }
+ connection_.OnCanWrite();
+}
+
+// Test the mode in which the link is filled up with probing retransmissions if
+// the connection becomes application-limited.
+TEST_P(QuicConnectionTest, SendDataWhenApplicationLimited) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, ShouldSendProbingPacket())
+ .WillRepeatedly(Return(true));
+ {
+ InSequence seq;
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite())
+ .WillRepeatedly(Return(false));
+ }
+ // Fix congestion window to be 20,000 bytes.
+ EXPECT_CALL(*send_algorithm_, CanSend(Ge(20000u)))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*send_algorithm_, CanSend(Lt(20000u)))
+ .WillRepeatedly(Return(true));
+
+ EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(0);
+ ASSERT_EQ(0u, connection_.GetStats().packets_sent);
+ connection_.set_fill_up_link_during_probing(true);
+ connection_.OnHandshakeComplete();
+ connection_.SendStreamData3();
+
+ // We expect a lot of packets from a 20 kbyte window.
+ EXPECT_GT(connection_.GetStats().packets_sent, 10u);
+ // Ensure that the packets are padded.
+ QuicByteCount average_packet_size =
+ connection_.GetStats().bytes_sent / connection_.GetStats().packets_sent;
+ EXPECT_GT(average_packet_size, 1000u);
+
+ // Acknowledge all packets sent, except for the last one.
+ QuicAckFrame ack = InitAckFrame(
+ connection_.sent_packet_manager().GetLargestSentPacket() - 1);
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+
+ // Ensure that since we no longer have retransmittable bytes in flight, this
+ // will not cause any responses to be sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(1);
+ ProcessAckPacket(&ack);
+}
+
+TEST_P(QuicConnectionTest, DonotForceSendingAckOnPacketTooLarge) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ // Send an ack by simulating delayed ack alarm firing.
+ ProcessPacket(1);
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ EXPECT_TRUE(ack_alarm->IsSet());
+ connection_.GetAckAlarm()->Fire();
+ // Simulate data packet causes write error.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _, _));
+ SimulateNextPacketTooLarge();
+ connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_FALSE(writer_->connection_close_frames().empty());
+ // Ack frame is not bundled in connection close packet.
+ EXPECT_TRUE(writer_->ack_frames().empty());
+}
+
+TEST_P(QuicConnectionTest, CloseConnectionForStatelessReject) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ std::string error_details("stateless reject");
+ EXPECT_CALL(visitor_, OnConnectionClosed(
+ QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT,
+ error_details, ConnectionCloseSource::FROM_PEER));
+ connection_.set_perspective(Perspective::IS_CLIENT);
+ connection_.CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT,
+ error_details,
+ ConnectionCloseBehavior::SILENT_CLOSE);
+}
+
+// Regression test for b/63620844.
+TEST_P(QuicConnectionTest, FailedToWriteHandshakePacket) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ SimulateNextPacketTooLarge();
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PACKET_WRITE_ERROR, _,
+ ConnectionCloseSource::FROM_SELF))
+ .Times(1);
+ connection_.SendCryptoStreamData();
+}
+
+TEST_P(QuicConnectionTest, MaxPacingRate) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_EQ(0, connection_.MaxPacingRate().ToBytesPerSecond());
+ connection_.SetMaxPacingRate(QuicBandwidth::FromBytesPerSecond(100));
+ EXPECT_EQ(100, connection_.MaxPacingRate().ToBytesPerSecond());
+}
+
+TEST_P(QuicConnectionTest, ClientAlwaysSendConnectionId) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN);
+ EXPECT_EQ(CONNECTION_ID_PRESENT,
+ writer_->last_packet_header().destination_connection_id_included);
+
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ QuicConfigPeer::SetReceivedBytesForConnectionId(&config, 0);
+ connection_.SetFromConfig(config);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(3, "bar", 3, NO_FIN);
+ // Verify connection id is still sent in the packet.
+ EXPECT_EQ(CONNECTION_ID_PRESENT,
+ writer_->last_packet_header().destination_connection_id_included);
+}
+
+TEST_P(QuicConnectionTest, SendProbingRetransmissions) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ MockQuicConnectionDebugVisitor debug_visitor;
+ connection_.set_debug_visitor(&debug_visitor);
+
+ const QuicStreamId stream_id = 2;
+ QuicPacketNumber last_packet;
+ SendStreamDataToPeer(stream_id, "foo", 0, NO_FIN, &last_packet);
+ SendStreamDataToPeer(stream_id, "bar", 3, NO_FIN, &last_packet);
+ SendStreamDataToPeer(stream_id, "test", 6, NO_FIN, &last_packet);
+
+ const QuicByteCount old_bytes_in_flight =
+ connection_.sent_packet_manager().GetBytesInFlight();
+
+ // Allow 9 probing retransmissions to be sent.
+ {
+ InSequence seq;
+ EXPECT_CALL(*send_algorithm_, CanSend(_))
+ .Times(9 * 2)
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+ }
+ // Expect them retransmitted in cyclic order (foo, bar, test, foo, bar...).
+ QuicPacketCount sent_count = 0;
+ EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _))
+ .WillRepeatedly(Invoke([this, &sent_count](const SerializedPacket&,
+ QuicPacketNumber,
+ TransmissionType, QuicTime) {
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ // Identify the frames by stream offset (0, 3, 6, 0, 3...).
+ EXPECT_EQ(3 * (sent_count % 3), writer_->stream_frames()[0]->offset);
+ sent_count++;
+ }));
+ EXPECT_CALL(*send_algorithm_, ShouldSendProbingPacket())
+ .WillRepeatedly(Return(true));
+
+ connection_.SendProbingRetransmissions();
+
+ // Ensure that the in-flight has increased.
+ const QuicByteCount new_bytes_in_flight =
+ connection_.sent_packet_manager().GetBytesInFlight();
+ EXPECT_GT(new_bytes_in_flight, old_bytes_in_flight);
+}
+
+// Ensure that SendProbingRetransmissions() does not retransmit anything when
+// there are no outstanding packets.
+TEST_P(QuicConnectionTest,
+ SendProbingRetransmissionsFailsWhenNothingToRetransmit) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ ASSERT_TRUE(connection_.sent_packet_manager().unacked_packets().empty());
+
+ MockQuicConnectionDebugVisitor debug_visitor;
+ connection_.set_debug_visitor(&debug_visitor);
+ EXPECT_CALL(debug_visitor, OnPacketSent(_, _, _, _)).Times(0);
+ EXPECT_CALL(*send_algorithm_, ShouldSendProbingPacket())
+ .WillRepeatedly(Return(true));
+
+ connection_.SendProbingRetransmissions();
+}
+
+TEST_P(QuicConnectionTest, PingAfterLastRetransmittablePacketAcked) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ const QuicTime::Delta retransmittable_on_wire_timeout =
+ QuicTime::Delta::FromMilliseconds(50);
+ connection_.set_retransmittable_on_wire_timeout(
+ retransmittable_on_wire_timeout);
+
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(true));
+
+ const char data[] = "data";
+ size_t data_size = strlen(data);
+ QuicStreamOffset offset = 0;
+
+ // Advance 5ms, send a retransmittable packet to the peer.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.sent_packet_manager().HasInFlightPackets());
+ // The ping alarm is set for the ping timeout, not the shorter
+ // retransmittable_on_wire_timeout.
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ QuicTime::Delta ping_delay = QuicTime::Delta::FromSeconds(kPingTimeoutSecs);
+ EXPECT_EQ((clock_.ApproximateNow() + ping_delay),
+ connection_.GetPingAlarm()->deadline());
+
+ // Advance 5ms, send a second retransmittable packet to the peer.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+
+ // Now receive an ACK of the first packet. This should not set the
+ // retransmittable-on-wire alarm since packet 2 is still on the wire.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame =
+ InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.sent_packet_manager().HasInFlightPackets());
+ // The ping alarm is set for the ping timeout, not the shorter
+ // retransmittable_on_wire_timeout.
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ // The ping alarm has a 1 second granularity, and the clock has been advanced
+ // 10ms since it was originally set.
+ EXPECT_EQ((clock_.ApproximateNow() + ping_delay -
+ QuicTime::Delta::FromMilliseconds(10)),
+ connection_.GetPingAlarm()->deadline());
+
+ // Now receive an ACK of the second packet. This should set the
+ // retransmittable-on-wire alarm now that no retransmittable packets are on
+ // the wire.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout,
+ connection_.GetPingAlarm()->deadline());
+
+ // Now receive a duplicate ACK of the second packet. This should not update
+ // the ping alarm.
+ QuicTime prev_deadline = connection_.GetPingAlarm()->deadline();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(prev_deadline, connection_.GetPingAlarm()->deadline());
+
+ // Now receive a non-ACK packet. This should not update the ping alarm.
+ prev_deadline = connection_.GetPingAlarm()->deadline();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ ProcessPacket(4);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(prev_deadline, connection_.GetPingAlarm()->deadline());
+
+ // Simulate the alarm firing and check that a PING is sent.
+ EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+ connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+ }));
+ connection_.GetPingAlarm()->Fire();
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ }
+ ASSERT_EQ(1u, writer_->ping_frames().size());
+}
+
+TEST_P(QuicConnectionTest, NoPingIfRetransmittablePacketSent) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ const QuicTime::Delta retransmittable_on_wire_timeout =
+ QuicTime::Delta::FromMilliseconds(50);
+ connection_.set_retransmittable_on_wire_timeout(
+ retransmittable_on_wire_timeout);
+
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
+ .WillRepeatedly(Return(true));
+
+ const char data[] = "data";
+ size_t data_size = strlen(data);
+ QuicStreamOffset offset = 0;
+
+ // Advance 5ms, send a retransmittable packet to the peer.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.sent_packet_manager().HasInFlightPackets());
+ // The ping alarm is set for the ping timeout, not the shorter
+ // retransmittable_on_wire_timeout.
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ QuicTime::Delta ping_delay = QuicTime::Delta::FromSeconds(kPingTimeoutSecs);
+ EXPECT_EQ((clock_.ApproximateNow() + ping_delay),
+ connection_.GetPingAlarm()->deadline());
+
+ // Now receive an ACK of the first packet. This should set the
+ // retransmittable-on-wire alarm now that no retransmittable packets are on
+ // the wire.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ QuicAckFrame frame =
+ InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout,
+ connection_.GetPingAlarm()->deadline());
+
+ // Before the alarm fires, send another retransmittable packet. This should
+ // cancel the retransmittable-on-wire alarm since now there's a
+ // retransmittable packet on the wire.
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+
+ // Now receive an ACK of the second packet. This should set the
+ // retransmittable-on-wire alarm now that no retransmittable packets are on
+ // the wire.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow() + retransmittable_on_wire_timeout,
+ connection_.GetPingAlarm()->deadline());
+
+ // Simulate the alarm firing and check that a PING is sent.
+ writer_->Reset();
+ EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+ connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+ }));
+ connection_.GetPingAlarm()->Fire();
+ if (GetParam().no_stop_waiting) {
+ EXPECT_EQ(2u, writer_->frame_count());
+ } else {
+ EXPECT_EQ(3u, writer_->frame_count());
+ }
+ ASSERT_EQ(1u, writer_->ping_frames().size());
+}
+
+TEST_P(QuicConnectionTest, OnForwardProgressConfirmed) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnForwardProgressConfirmed()).Times(Exactly(0));
+ EXPECT_TRUE(connection_.connected());
+
+ const char data[] = "data";
+ size_t data_size = strlen(data);
+ QuicStreamOffset offset = 0;
+
+ // Send two packets.
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+ connection_.SendStreamDataWithString(1, data, offset, NO_FIN);
+ offset += data_size;
+
+ // Ack packet 1. This increases the largest_acked to 1, so
+ // OnForwardProgressConfirmed() should be called
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(visitor_, OnForwardProgressConfirmed());
+ QuicAckFrame frame =
+ InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
+ ProcessAckPacket(&frame);
+
+ // Ack packet 1 again. largest_acked remains at 1, so
+ // OnForwardProgressConfirmed() should not be called.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ frame = InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(2)}});
+ ProcessAckPacket(&frame);
+
+ // Ack packet 2. This increases the largest_acked to 2, so
+ // OnForwardProgressConfirmed() should be called.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(visitor_, OnForwardProgressConfirmed());
+ frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, ValidStatelessResetToken) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ const QuicUint128 kTestToken = 1010101;
+ const QuicUint128 kWrongTestToken = 1010100;
+ QuicConfig config;
+ // No token has been received.
+ EXPECT_FALSE(connection_.IsValidStatelessResetToken(kTestToken));
+
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(2);
+ // Token is different from received token.
+ QuicConfigPeer::SetReceivedStatelessResetToken(&config, kTestToken);
+ connection_.SetFromConfig(config);
+ EXPECT_FALSE(connection_.IsValidStatelessResetToken(kWrongTestToken));
+
+ QuicConfigPeer::SetReceivedStatelessResetToken(&config, kTestToken);
+ connection_.SetFromConfig(config);
+ EXPECT_TRUE(connection_.IsValidStatelessResetToken(kTestToken));
+}
+
+TEST_P(QuicConnectionTest, WriteBlockedWithInvalidAck) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _, _));
+
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(5, "foo", 0, FIN);
+ // This causes connection to be closed because packet 1 has not been sent yet.
+ QuicAckFrame frame = InitAckFrame(1);
+ ProcessAckPacket(1, &frame);
+}
+
+TEST_P(QuicConnectionTest, SendMessage) {
+ if (connection_.transport_version() <= QUIC_VERSION_44 ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ std::string message(connection_.GetCurrentLargestMessagePayload() * 2, 'a');
+ QuicStringPiece message_data(message);
+ QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
+ {
+ QuicConnection::ScopedPacketFlusher flusher(&connection_,
+ QuicConnection::SEND_ACK);
+ connection_.SendStreamData3();
+ // Send a message which cannot fit into current open packet, and 2 packets
+ // get sent, one contains stream frame, and the other only contains the
+ // message frame.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ EXPECT_EQ(
+ MESSAGE_STATUS_SUCCESS,
+ connection_.SendMessage(
+ 1, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
+ QuicStringPiece(
+ message_data.data(),
+ connection_.GetCurrentLargestMessagePayload()),
+ &storage)));
+ }
+ // Fail to send a message if connection is congestion control blocked.
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+ EXPECT_EQ(
+ MESSAGE_STATUS_BLOCKED,
+ connection_.SendMessage(
+ 2, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
+ "message", &storage)));
+
+ // Always fail to send a message which cannot fit into one packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ EXPECT_EQ(
+ MESSAGE_STATUS_TOO_LARGE,
+ connection_.SendMessage(
+ 3, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
+ QuicStringPiece(
+ message_data.data(),
+ connection_.GetCurrentLargestMessagePayload() + 1),
+ &storage)));
+}
+
+// Test to check that the path challenge/path response logic works
+// correctly. This test is only for version-99
+TEST_P(QuicConnectionTest, PathChallengeResponse) {
+ if (connection_.version().transport_version != QUIC_VERSION_99 ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // First check if we can probe from server to client and back
+ set_perspective(Perspective::IS_SERVER);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ // Create and send the probe request (PATH_CHALLENGE frame).
+ // SendConnectivityProbingPacket ends up calling
+ // TestPacketWriter::WritePacket() which in turns receives and parses the
+ // packet by calling framer_.ProcessPacket() -- which in turn calls
+ // SimpleQuicFramer::OnPathChallengeFrame(). SimpleQuicFramer saves
+ // the packet in writer_->path_challenge_frames()
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendConnectivityProbingPacket(writer_.get(),
+ connection_.peer_address());
+ // Save the random contents of the challenge for later comparison to the
+ // response.
+ QuicPathFrameBuffer challenge_data =
+ writer_->path_challenge_frames().front().data_buffer;
+
+ // Normally, QuicConnection::OnPathChallengeFrame and OnPaddingFrame would be
+ // called and it will perform actions to ensure that the rest of the protocol
+ // is performed (specifically, call UpdatePacketContent to say that this is a
+ // path challenge so that when QuicConnection::OnPacketComplete is called
+ // (again, out of the framer), the response is generated). Simulate those
+ // calls so that the right internal state is set up for generating
+ // the response.
+ EXPECT_TRUE(connection_.OnPathChallengeFrame(
+ writer_->path_challenge_frames().front()));
+ EXPECT_TRUE(connection_.OnPaddingFrame(writer_->padding_frames().front()));
+ // Cause the response to be created and sent. Result is that the response
+ // should be stashed in writer's path_response_frames.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendConnectivityProbingResponsePacket(connection_.peer_address());
+
+ // The final check is to ensure that the random data in the response matches
+ // the random data from the challenge.
+ EXPECT_EQ(0, memcmp(&challenge_data,
+ &(writer_->path_response_frames().front().data_buffer),
+ sizeof(challenge_data)));
+}
+
+// Regression test for b/110259444
+TEST_P(QuicConnectionTest, DoNotScheduleSpuriousAckAlarm) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ SetQuicReloadableFlag(quic_fix_spurious_ack_alarm, true);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1));
+ writer_->SetWriteBlocked();
+
+ ProcessPacket(1);
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ // Verify ack alarm is set.
+ EXPECT_TRUE(ack_alarm->IsSet());
+ // Fire the ack alarm, verify no packet is sent because the writer is blocked.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ connection_.GetAckAlarm()->Fire();
+
+ writer_->SetWritable();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ ProcessPacket(2);
+ // Verify ack alarm is not set.
+ EXPECT_FALSE(ack_alarm->IsSet());
+}
+
+TEST_P(QuicConnectionTest, DisablePacingOffloadConnectionOptions) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_FALSE(QuicConnectionPeer::SupportsReleaseTime(&connection_));
+ writer_->set_supports_release_time(true);
+ QuicConfig config;
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ EXPECT_TRUE(QuicConnectionPeer::SupportsReleaseTime(&connection_));
+
+ QuicTagVector connection_options;
+ connection_options.push_back(kNPCO);
+ config.SetConnectionOptionsToSend(connection_options);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ // Verify pacing offload is disabled.
+ EXPECT_FALSE(QuicConnectionPeer::SupportsReleaseTime(&connection_));
+}
+
+// Regression test for b/110259444
+// Get a path response without having issued a path challenge...
+TEST_P(QuicConnectionTest, OrphanPathResponse) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ QuicPathFrameBuffer data = {{0, 1, 2, 3, 4, 5, 6, 7}};
+
+ QuicPathResponseFrame frame(99, data);
+ EXPECT_TRUE(connection_.OnPathResponseFrame(frame));
+ // If PATH_RESPONSE was accepted (payload matches the payload saved
+ // in QuicConnection::transmitted_connectivity_probe_payload_) then
+ // current_packet_content_ would be set to FIRST_FRAME_IS_PING.
+ // Since this PATH_RESPONSE does not match, current_packet_content_
+ // must not be FIRST_FRAME_IS_PING.
+ EXPECT_NE(QuicConnection::FIRST_FRAME_IS_PING,
+ QuicConnectionPeer::GetCurrentPacketContent(&connection_));
+}
+
+// Regression test for b/120791670
+TEST_P(QuicConnectionTest, StopProcessingGQuicPacketInIetfQuicConnection) {
+ if (connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // This test mimics a problematic scenario where an IETF QUIC connection
+ // receives a Google QUIC packet and continue processing it using Google QUIC
+ // wire format.
+ if (version().transport_version <= QUIC_VERSION_43) {
+ return;
+ }
+ set_perspective(Perspective::IS_SERVER);
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 0u,
+ QuicStringPiece());
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1);
+ ProcessFramePacketWithAddresses(QuicFrame(stream_frame), kSelfAddress,
+ kPeerAddress);
+
+ // Let connection process a Google QUIC packet.
+ peer_framer_.set_version_for_tests(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43));
+ std::unique_ptr<QuicPacket> packet(
+ ConstructDataPacket(2, !kHasStopWaiting, ENCRYPTION_INITIAL));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ peer_framer_.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(2),
+ *packet, buffer, kMaxOutgoingPacketSize);
+ // Make sure no stream frame is processed.
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(0);
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false));
+
+ EXPECT_EQ(2u, connection_.GetStats().packets_received);
+ EXPECT_EQ(1u, connection_.GetStats().packets_processed);
+}
+
+TEST_P(QuicConnectionTest, AcceptPacketNumberZero) {
+ if (version().transport_version != QUIC_VERSION_99 ||
+ connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // Set first_sending_packet_number to be 0 to allow successfully processing
+ // acks which ack packet number 0.
+ QuicFramerPeer::SetFirstSendingPacketNumber(writer_->framer()->framer(), 0);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(0);
+ EXPECT_EQ(QuicPacketNumber(0), LargestAcked(*outgoing_ack()));
+ EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
+
+ ProcessPacket(1);
+ EXPECT_EQ(QuicPacketNumber(1), LargestAcked(*outgoing_ack()));
+ EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
+
+ ProcessPacket(2);
+ EXPECT_EQ(QuicPacketNumber(2), LargestAcked(*outgoing_ack()));
+ EXPECT_EQ(1u, outgoing_ack()->packets.NumIntervals());
+}
+
+TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicSending) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+
+ connection_.SendCryptoStreamData();
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ QuicAckFrame frame1 = InitAckFrame(1);
+ // Received ACK for packet 1.
+ ProcessFramePacketAtLevel(30, QuicFrame(&frame1), ENCRYPTION_INITIAL);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(4);
+ connection_.SendApplicationDataAtLevel(ENCRYPTION_ZERO_RTT, 5, "data", 0,
+ NO_FIN);
+ connection_.SendApplicationDataAtLevel(ENCRYPTION_ZERO_RTT, 5, "data", 4,
+ NO_FIN);
+ connection_.SendApplicationDataAtLevel(ENCRYPTION_FORWARD_SECURE, 5, "data",
+ 8, NO_FIN);
+ connection_.SendApplicationDataAtLevel(ENCRYPTION_FORWARD_SECURE, 5, "data",
+ 12, FIN);
+ // Received ACK for packets 2, 4, 5.
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ QuicAckFrame frame2 =
+ InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)},
+ {QuicPacketNumber(4), QuicPacketNumber(6)}});
+ // Make sure although the same packet number is used, but they are in
+ // different packet number spaces.
+ ProcessFramePacketAtLevel(30, QuicFrame(&frame2), ENCRYPTION_FORWARD_SECURE);
+}
+
+TEST_P(QuicConnectionTest, PeerAcksPacketsInWrongPacketNumberSpace) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingEncrypter>(0x01));
+
+ connection_.SendCryptoStreamData();
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ QuicAckFrame frame1 = InitAckFrame(1);
+ // Received ACK for packet 1.
+ ProcessFramePacketAtLevel(30, QuicFrame(&frame1), ENCRYPTION_INITIAL);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ connection_.SendApplicationDataAtLevel(ENCRYPTION_ZERO_RTT, 5, "data", 0,
+ NO_FIN);
+ connection_.SendApplicationDataAtLevel(ENCRYPTION_ZERO_RTT, 5, "data", 4,
+ NO_FIN);
+
+ // Received ACK for packets 2 and 3 in wrong packet number space.
+ QuicAckFrame invalid_ack =
+ InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(4)}});
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ ProcessFramePacketAtLevel(300, QuicFrame(&invalid_ack), ENCRYPTION_INITIAL);
+}
+
+TEST_P(QuicConnectionTest, MultiplePacketNumberSpacesBasicReceiving) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ use_tagging_decrypter();
+ // Receives packet 1000 in initial data.
+ ProcessDataPacketAtLevel(1000, false, ENCRYPTION_INITIAL);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(0x02));
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ // Receives packet 1000 in application data.
+ ProcessDataPacketAtLevel(1000, false, ENCRYPTION_ZERO_RTT);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ connection_.SendApplicationDataAtLevel(ENCRYPTION_ZERO_RTT, 5, "data", 0,
+ NO_FIN);
+ // Verify application data ACK gets bundled with outgoing data.
+ EXPECT_EQ(2u, writer_->frame_count());
+ // Make sure ACK alarm is still set because initial data is not ACKed.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ // Receive packet 1001 in application data.
+ ProcessDataPacketAtLevel(1001, false, ENCRYPTION_ZERO_RTT);
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ // Simulates ACK alarm fires and verify two ACKs are flushed.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ connection_.GetAckAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // Receives more packets in application data.
+ ProcessDataPacketAtLevel(1002, false, ENCRYPTION_ZERO_RTT);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+
+ peer_framer_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<StrictTaggingDecrypter>(0x02));
+ // Verify zero rtt and forward secure packets get acked in the same packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ ProcessDataPacketAtLevel(1003, false, ENCRYPTION_FORWARD_SECURE);
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, CancelAckAlarmOnWriteBlocked) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ use_tagging_decrypter();
+ // Receives packet 1000 in initial data.
+ ProcessDataPacketAtLevel(1000, false, ENCRYPTION_INITIAL);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ peer_framer_.SetEncrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ SetDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<StrictTaggingDecrypter>(0x02));
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ // Receives packet 1000 in application data.
+ ProcessDataPacketAtLevel(1000, false, ENCRYPTION_ZERO_RTT);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+
+ writer_->SetWriteBlocked();
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AnyNumber());
+ // Simulates ACK alarm fires and verify no ACK is flushed because of write
+ // blocked.
+ clock_.AdvanceTime(DefaultDelayedAckTime());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TaggingEncrypter>(0x02));
+ connection_.GetAckAlarm()->Fire();
+ // Verify ACK alarm is not set.
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+
+ writer_->SetWritable();
+ // Verify 2 ACKs are sent when connection gets unblocked.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ connection_.OnCanWrite();
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_constants.cc b/chromium/net/third_party/quiche/src/quic/core/quic_constants.cc
new file mode 100644
index 00000000000..8e46f886080
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_constants.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+const char* const kFinalOffsetHeaderKey = ":final-offset";
+
+const char* const kEPIDGoogleFrontEnd = "GFE";
+const char* const kEPIDGoogleFrontEnd0 = "GFE0";
+
+QuicPacketNumber MaxRandomInitialPacketNumber() {
+ static const QuicPacketNumber kMaxRandomInitialPacketNumber =
+ QuicPacketNumber(0x7fffffff);
+ return kMaxRandomInitialPacketNumber;
+}
+
+QuicPacketNumber FirstSendingPacketNumber() {
+ static const QuicPacketNumber kFirstSendingPacketNumber = QuicPacketNumber(1);
+ return kFirstSendingPacketNumber;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..4181cb7449b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_constants.h
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CONSTANTS_H_
+#define QUICHE_QUIC_CORE_QUIC_CONSTANTS_H_
+
+#include <stddef.h>
+
+#include <cstdint>
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+// Definitions of constant values used throughout the QUIC code.
+
+namespace quic {
+
+// Simple time constants.
+const uint64_t kNumSecondsPerMinute = 60;
+const uint64_t kNumSecondsPerHour = kNumSecondsPerMinute * 60;
+const uint64_t kNumSecondsPerWeek = kNumSecondsPerHour * 24 * 7;
+const uint64_t kNumMicrosPerMilli = 1000;
+const uint64_t kNumMicrosPerSecond = 1000 * 1000;
+
+// Default number of connections for N-connection emulation.
+const uint32_t kDefaultNumConnections = 2;
+// Default initial maximum size in bytes of a QUIC packet.
+const QuicByteCount kDefaultMaxPacketSize = 1350;
+// Default initial maximum size in bytes of a QUIC packet for servers.
+const QuicByteCount kDefaultServerMaxPacketSize = 1000;
+// Maximum transmission unit on Ethernet.
+const QuicByteCount kEthernetMTU = 1500;
+// The maximum packet size of any QUIC packet over IPv6, based on ethernet's max
+// size, minus the IP and UDP headers. IPv6 has a 40 byte header, UDP adds an
+// additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's
+// max packet size is 1500 bytes, 1500 - 48 = 1452.
+const QuicByteCount kMaxV6PacketSize = 1452;
+// The maximum packet size of any QUIC packet over IPv4.
+// 1500(Ethernet) - 20(IPv4 header) - 8(UDP header) = 1472.
+const QuicByteCount kMaxV4PacketSize = 1472;
+// The maximum incoming packet size allowed.
+const QuicByteCount kMaxIncomingPacketSize = kMaxV4PacketSize;
+// The maximum outgoing packet size allowed.
+const QuicByteCount kMaxOutgoingPacketSize = kMaxV6PacketSize;
+// ETH_MAX_MTU - MAX(sizeof(iphdr), sizeof(ip6_hdr)) - sizeof(udphdr).
+const QuicByteCount kMaxGsoPacketSize = 65535 - 40 - 8;
+// Default maximum packet size used in the Linux TCP implementation.
+// Used in QUIC for congestion window computations in bytes.
+const QuicByteCount kDefaultTCPMSS = 1460;
+const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS;
+// The minimum size of a packet which can elicit a version negotiation packet,
+// as per section 8.1 of the QUIC spec.
+const QuicByteCount kMinPacketSizeForVersionNegotiation = 1200;
+
+// We match SPDY's use of 32 (since we'd compete with SPDY).
+const QuicPacketCount kInitialCongestionWindow = 32;
+
+// Minimum size of initial flow control window, for both stream and session.
+const uint32_t kMinimumFlowControlSendWindow = 16 * 1024; // 16 KB
+
+// Maximum flow control receive window limits for connection and stream.
+const QuicByteCount kStreamReceiveWindowLimit = 16 * 1024 * 1024; // 16 MB
+const QuicByteCount kSessionReceiveWindowLimit = 24 * 1024 * 1024; // 24 MB
+
+// Default limit on the size of uncompressed headers.
+const QuicByteCount kDefaultMaxUncompressedHeaderSize = 16 * 1024; // 16 KB
+
+// Minimum size of the CWND, in packets, when doing bandwidth resumption.
+const QuicPacketCount kMinCongestionWindowForBandwidthResumption = 10;
+
+// Maximum number of tracked packets.
+const QuicPacketCount kMaxTrackedPackets = 10000;
+
+// Default size of the socket receive buffer in bytes.
+const QuicByteCount kDefaultSocketReceiveBuffer = 1024 * 1024;
+
+// Don't allow a client to suggest an RTT shorter than 10ms.
+const uint32_t kMinInitialRoundTripTimeUs = 10 * kNumMicrosPerMilli;
+
+// Don't allow a client to suggest an RTT longer than 15 seconds.
+const uint32_t kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond;
+
+// Maximum number of open streams per connection.
+const size_t kDefaultMaxStreamsPerConnection = 100;
+
+// Number of bytes reserved for public flags in the packet header.
+const size_t kPublicFlagsSize = 1;
+// Number of bytes reserved for version number in the packet header.
+const size_t kQuicVersionSize = 4;
+
+// Signifies that the QuicPacket will contain version of the protocol.
+const bool kIncludeVersion = true;
+// Signifies that the QuicPacket will include a diversification nonce.
+const bool kIncludeDiversificationNonce = true;
+
+// Header key used to identify final offset on data stream when sending HTTP/2
+// trailing headers over QUIC.
+QUIC_EXPORT_PRIVATE extern const char* const kFinalOffsetHeaderKey;
+
+// Default maximum delayed ack time, in ms.
+// Uses a 25ms delayed ack timer. Helps with better signaling
+// in low-bandwidth (< ~384 kbps), where an ack is sent per packet.
+const int64_t kDefaultDelayedAckTimeMs = 25;
+
+// Minimum tail loss probe time in ms.
+static const int64_t kMinTailLossProbeTimeoutMs = 10;
+
+// The timeout before the handshake succeeds.
+const int64_t kInitialIdleTimeoutSecs = 5;
+// The default idle timeout.
+const int64_t kDefaultIdleTimeoutSecs = 30;
+// The maximum idle timeout that can be negotiated.
+const int64_t kMaximumIdleTimeoutSecs = 60 * 10; // 10 minutes.
+// The default timeout for a connection until the crypto handshake succeeds.
+const int64_t kMaxTimeForCryptoHandshakeSecs = 10; // 10 secs.
+
+// Default limit on the number of undecryptable packets the connection buffers
+// before the CHLO/SHLO arrive.
+const size_t kDefaultMaxUndecryptablePackets = 10;
+
+// Default ping timeout.
+const int64_t kPingTimeoutSecs = 15; // 15 secs.
+
+// Minimum number of RTTs between Server Config Updates (SCUP) sent to client.
+const int kMinIntervalBetweenServerConfigUpdatesRTTs = 10;
+
+// Minimum time between Server Config Updates (SCUP) sent to client.
+const int kMinIntervalBetweenServerConfigUpdatesMs = 1000;
+
+// Minimum number of packets between Server Config Updates (SCUP).
+const int kMinPacketsBetweenServerConfigUpdates = 100;
+
+// The number of open streams that a server will accept is set to be slightly
+// larger than the negotiated limit. Immediately closing the connection if the
+// client opens slightly too many streams is not ideal: the client may have sent
+// a FIN that was lost, and simultaneously opened a new stream. The number of
+// streams a server accepts is a fixed increment over the negotiated limit, or a
+// percentage increase, whichever is larger.
+const float kMaxStreamsMultiplier = 1.1f;
+const int kMaxStreamsMinimumIncrement = 10;
+
+// Available streams are ones with IDs less than the highest stream that has
+// been opened which have neither been opened or reset. The limit on the number
+// of available streams is 10 times the limit on the number of open streams.
+const int kMaxAvailableStreamsMultiplier = 10;
+
+// Track the number of promises that are not yet claimed by a
+// corresponding get. This must be smaller than
+// kMaxAvailableStreamsMultiplier, because RST on a promised stream my
+// create available streams entries.
+const int kMaxPromisedStreamsMultiplier = kMaxAvailableStreamsMultiplier - 1;
+
+// TCP RFC calls for 1 second RTO however Linux differs from this default and
+// define the minimum RTO to 200ms, we will use the same until we have data to
+// support a higher or lower value.
+static const int64_t kMinRetransmissionTimeMs = 200;
+// The delayed ack time must not be greater than half the min RTO.
+static_assert(kDefaultDelayedAckTimeMs <= kMinRetransmissionTimeMs / 2,
+ "Delayed ack time must be less than or equal half the MinRTO");
+
+// We define an unsigned 16-bit floating point value, inspired by IEEE floats
+// (http://en.wikipedia.org/wiki/Half_precision_floating-point_format),
+// with 5-bit exponent (bias 1), 11-bit mantissa (effective 12 with hidden
+// bit) and denormals, but without signs, transfinites or fractions. Wire format
+// 16 bits (little-endian byte order) are split into exponent (high 5) and
+// mantissa (low 11) and decoded as:
+// uint64_t value;
+// if (exponent == 0) value = mantissa;
+// else value = (mantissa | 1 << 11) << (exponent - 1)
+const int kUFloat16ExponentBits = 5;
+const int kUFloat16MaxExponent = (1 << kUFloat16ExponentBits) - 2; // 30
+const int kUFloat16MantissaBits = 16 - kUFloat16ExponentBits; // 11
+const int kUFloat16MantissaEffectiveBits = kUFloat16MantissaBits + 1; // 12
+const uint64_t kUFloat16MaxValue = // 0x3FFC0000000
+ ((UINT64_C(1) << kUFloat16MantissaEffectiveBits) - 1)
+ << kUFloat16MaxExponent;
+
+// kDiversificationNonceSize is the size, in bytes, of the nonce that a server
+// may set in the packet header to ensure that its INITIAL keys are not
+// duplicated.
+const size_t kDiversificationNonceSize = 32;
+
+// The largest gap in packets we'll accept without closing the connection.
+// This will likely have to be tuned.
+const QuicPacketCount kMaxPacketGap = 5000;
+
+// The maximum number of random padding bytes to add.
+const QuicByteCount kMaxNumRandomPaddingBytes = 256;
+
+// The size of stream send buffer data slice size in bytes. A data slice is
+// piece of stream data stored in contiguous memory, and a stream frame can
+// contain data from multiple data slices.
+const QuicByteCount kQuicStreamSendBufferSliceSize = 4 * 1024;
+
+// For When using Random Initial Packet Numbers, they can start
+// anyplace in the range 1...((2^31)-1) or 0x7fffffff
+QUIC_EXPORT_PRIVATE QuicPacketNumber MaxRandomInitialPacketNumber();
+
+// Used to represent an invalid or no control frame id.
+const QuicControlFrameId kInvalidControlFrameId = 0;
+
+// The max length a stream can have.
+const QuicByteCount kMaxStreamLength = (UINT64_C(1) << 62) - 1;
+
+// The max value that can be encoded using IETF Var Ints.
+const uint64_t kMaxIetfVarInt = UINT64_C(0x3fffffffffffffff);
+
+// The maximum stream id value that is supported - (2^32)-1
+// TODO(fkastenholz): Should update this to 64 bits for IETF Quic.
+const QuicStreamId kMaxQuicStreamId = 0xffffffff;
+
+// Number of bytes reserved for packet header type.
+const size_t kPacketHeaderTypeSize = 1;
+
+// Number of bytes reserved for connection ID length.
+const size_t kConnectionIdLengthSize = 1;
+
+// Minimum length of random bytes in IETF stateless reset packet.
+const size_t kMinRandomBytesLengthInStatelessReset = 24;
+
+// Maximum length allowed for the token in a NEW_TOKEN frame.
+const size_t kMaxNewTokenTokenLength = 0xffff;
+
+// Packet number of first sending packet of a connection. Please note, this
+// cannot be used as first received packet because peer can choose its starting
+// packet number.
+QUIC_EXPORT_PRIVATE QuicPacketNumber FirstSendingPacketNumber();
+
+// Used by clients to tell if a public reset is sent from a Google frontend.
+QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd;
+QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd0;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CONSTANTS_H_
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
new file mode 100644
index 00000000000..ed178f44594
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.cc
@@ -0,0 +1,312 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/quic_control_frame_manager.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+
+namespace quic {
+
+QuicControlFrameManager::QuicControlFrameManager(QuicSession* session)
+ : last_control_frame_id_(kInvalidControlFrameId),
+ least_unacked_(1),
+ least_unsent_(1),
+ session_(session) {}
+
+QuicControlFrameManager::~QuicControlFrameManager() {
+ while (!control_frames_.empty()) {
+ DeleteFrame(&control_frames_.front());
+ control_frames_.pop_front();
+ }
+}
+
+void QuicControlFrameManager::WriteOrBufferQuicFrame(QuicFrame frame) {
+ const bool had_buffered_frames = HasBufferedFrames();
+ control_frames_.emplace_back(frame);
+ if (had_buffered_frames) {
+ return;
+ }
+ WriteBufferedFrames();
+}
+
+void QuicControlFrameManager::WriteOrBufferRstStream(
+ QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ QUIC_DVLOG(1) << "Writing RST_STREAM_FRAME";
+ WriteOrBufferQuicFrame((QuicFrame(new QuicRstStreamFrame(
+ ++last_control_frame_id_, id, error, bytes_written))));
+}
+
+void QuicControlFrameManager::WriteOrBufferGoAway(
+ QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason) {
+ QUIC_DVLOG(1) << "Writing GOAWAY_FRAME";
+ WriteOrBufferQuicFrame(QuicFrame(new QuicGoAwayFrame(
+ ++last_control_frame_id_, error, last_good_stream_id, reason)));
+}
+
+void QuicControlFrameManager::WriteOrBufferWindowUpdate(
+ QuicStreamId id,
+ QuicStreamOffset byte_offset) {
+ QUIC_DVLOG(1) << "Writing WINDOW_UPDATE_FRAME";
+ WriteOrBufferQuicFrame(QuicFrame(
+ new QuicWindowUpdateFrame(++last_control_frame_id_, id, byte_offset)));
+}
+
+void QuicControlFrameManager::WriteOrBufferBlocked(QuicStreamId id) {
+ QUIC_DVLOG(1) << "Writing BLOCKED_FRAME";
+ WriteOrBufferQuicFrame(
+ QuicFrame(new QuicBlockedFrame(++last_control_frame_id_, id)));
+}
+
+void QuicControlFrameManager::WriteOrBufferStreamIdBlocked(QuicStreamId id) {
+ QUIC_DVLOG(1) << "Writing STREAM_ID_BLOCKED Frame";
+ QUIC_CODE_COUNT(stream_id_blocked_transmits);
+ WriteOrBufferQuicFrame(
+ QuicFrame(QuicStreamIdBlockedFrame(++last_control_frame_id_, id)));
+}
+
+void QuicControlFrameManager::WriteOrBufferMaxStreamId(QuicStreamId id) {
+ QUIC_DVLOG(1) << "Writing MAX_STREAM_ID Frame";
+ QUIC_CODE_COUNT(max_stream_id_transmits);
+ WriteOrBufferQuicFrame(
+ QuicFrame(QuicMaxStreamIdFrame(++last_control_frame_id_, id)));
+}
+
+void QuicControlFrameManager::WriteOrBufferStopSending(uint16_t code,
+ QuicStreamId stream_id) {
+ QUIC_DVLOG(1) << "Writing STOP_SENDING_FRAME";
+ WriteOrBufferQuicFrame(QuicFrame(
+ new QuicStopSendingFrame(++last_control_frame_id_, stream_id, code)));
+}
+
+void QuicControlFrameManager::WritePing() {
+ QUIC_DVLOG(1) << "Writing PING_FRAME";
+ if (HasBufferedFrames()) {
+ // Do not send ping if there is buffered frames.
+ QUIC_LOG(WARNING)
+ << "Try to send PING when there is buffered control frames.";
+ return;
+ }
+ control_frames_.emplace_back(
+ QuicFrame(QuicPingFrame(++last_control_frame_id_)));
+ WriteBufferedFrames();
+}
+
+void QuicControlFrameManager::OnControlFrameSent(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ QUIC_BUG
+ << "Send or retransmit a control frame with invalid control frame id";
+ return;
+ }
+ if (frame.type == WINDOW_UPDATE_FRAME) {
+ QuicStreamId stream_id = frame.window_update_frame->stream_id;
+ if (QuicContainsKey(window_update_frames_, 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)) {
+ // This is retransmitted control frame.
+ pending_retransmissions_.erase(id);
+ return;
+ }
+ if (id > least_unsent_) {
+ QUIC_BUG << "Try to send control frames out of order, id: " << id
+ << " least_unsent: " << least_unsent_;
+ session_->connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Try to send control frames out of order",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ ++least_unsent_;
+}
+
+bool QuicControlFrameManager::OnControlFrameAcked(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (!OnControlFrameIdAcked(id)) {
+ return false;
+ }
+ if (frame.type == WINDOW_UPDATE_FRAME) {
+ QuicStreamId stream_id = frame.window_update_frame->stream_id;
+ if (QuicContainsKey(window_update_frames_, stream_id) &&
+ window_update_frames_[stream_id] == id) {
+ window_update_frames_.erase(stream_id);
+ }
+ }
+ return true;
+}
+
+void QuicControlFrameManager::OnControlFrameLost(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ // Frame does not have a valid control frame ID, ignore it.
+ return;
+ }
+ if (id >= least_unsent_) {
+ QUIC_BUG << "Try to mark unsent control frame as lost";
+ session_->connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Try to mark unsent control frame as lost",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ // This frame has already been acked.
+ return;
+ }
+ if (!QuicContainsKey(pending_retransmissions_, id)) {
+ pending_retransmissions_[id] = true;
+ }
+}
+
+bool QuicControlFrameManager::IsControlFrameOutstanding(
+ const QuicFrame& frame) const {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ // Frame without a control frame ID should not be retransmitted.
+ return false;
+ }
+ // Consider this frame is outstanding if it does not get acked.
+ return id < least_unacked_ + control_frames_.size() && id >= least_unacked_ &&
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) !=
+ kInvalidControlFrameId;
+}
+
+bool QuicControlFrameManager::HasPendingRetransmission() const {
+ return !pending_retransmissions_.empty();
+}
+
+bool QuicControlFrameManager::WillingToWrite() const {
+ return HasPendingRetransmission() || HasBufferedFrames();
+}
+
+QuicFrame QuicControlFrameManager::NextPendingRetransmission() const {
+ QUIC_BUG_IF(pending_retransmissions_.empty())
+ << "Unexpected call to NextPendingRetransmission() with empty pending "
+ << "retransmission list.";
+ QuicControlFrameId id = pending_retransmissions_.begin()->first;
+ return control_frames_.at(id - least_unacked_);
+}
+
+void QuicControlFrameManager::OnCanWrite() {
+ if (HasPendingRetransmission()) {
+ // Exit early to allow streams to write pending retransmissions if any.
+ WritePendingRetransmission();
+ return;
+ }
+ WriteBufferedFrames();
+}
+
+bool QuicControlFrameManager::RetransmitControlFrame(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ // Frame does not have a valid control frame ID, ignore it. Returns true
+ // to allow writing following frames.
+ return true;
+ }
+ if (id >= least_unsent_) {
+ QUIC_BUG << "Try to retransmit unsent control frame";
+ session_->connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Try to retransmit unsent control frame",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ // This frame has already been acked.
+ return true;
+ }
+ QuicFrame copy = CopyRetransmittableControlFrame(frame);
+ QUIC_DVLOG(1) << "control frame manager is forced to retransmit frame: "
+ << frame;
+ if (session_->WriteControlFrame(copy)) {
+ return true;
+ }
+ DeleteFrame(&copy);
+ return false;
+}
+
+void QuicControlFrameManager::WriteBufferedFrames() {
+ while (HasBufferedFrames()) {
+ if (session_->session_decides_what_to_write()) {
+ session_->SetTransmissionType(NOT_RETRANSMISSION);
+ }
+ QuicFrame frame_to_send =
+ control_frames_.at(least_unsent_ - least_unacked_);
+ QuicFrame copy = CopyRetransmittableControlFrame(frame_to_send);
+ if (!session_->WriteControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(&copy);
+ break;
+ }
+ OnControlFrameSent(frame_to_send);
+ }
+}
+
+void QuicControlFrameManager::WritePendingRetransmission() {
+ while (HasPendingRetransmission()) {
+ QuicFrame pending = NextPendingRetransmission();
+ QuicFrame copy = CopyRetransmittableControlFrame(pending);
+ if (!session_->WriteControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(&copy);
+ break;
+ }
+ OnControlFrameSent(pending);
+ }
+}
+
+bool QuicControlFrameManager::OnControlFrameIdAcked(QuicControlFrameId id) {
+ if (id == kInvalidControlFrameId) {
+ // Frame does not have a valid control frame ID, ignore it.
+ return false;
+ }
+ if (id >= least_unsent_) {
+ QUIC_BUG << "Try to ack unsent control frame";
+ session_->connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Try to ack unsent control frame",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ // This frame has already been acked.
+ return false;
+ }
+
+ // Set control frame ID of acked frames to 0.
+ SetControlFrameId(kInvalidControlFrameId,
+ &control_frames_.at(id - least_unacked_));
+ // Remove acked control frames from pending retransmissions.
+ pending_retransmissions_.erase(id);
+ // Clean up control frames queue and increment least_unacked_.
+ while (!control_frames_.empty() &&
+ GetControlFrameId(control_frames_.front()) == kInvalidControlFrameId) {
+ DeleteFrame(&control_frames_.front());
+ control_frames_.pop_front();
+ ++least_unacked_;
+ }
+ return true;
+}
+
+bool QuicControlFrameManager::HasBufferedFrames() const {
+ return least_unsent_ < least_unacked_ + control_frames_.size();
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..a5213c42b41
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager.h
@@ -0,0 +1,153 @@
+// 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_CORE_QUIC_CONTROL_FRAME_MANAGER_H_
+#define QUICHE_QUIC_CORE_QUIC_CONTROL_FRAME_MANAGER_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+class QuicSession;
+
+namespace test {
+class QuicControlFrameManagerPeer;
+} // namespace test
+
+// Control frame manager contains a list of sent control frames with valid
+// control frame IDs. Control frames without valid control frame IDs include:
+// (1) non-retransmittable frames (e.g., ACK_FRAME, PADDING_FRAME,
+// STOP_WAITING_FRAME, etc.), (2) CONNECTION_CLOSE and IETF Quic
+// APPLICATION_CLOSE frames.
+// New control frames are added to the tail of the list when they are added to
+// the generator. Control frames are removed from the head of the list when they
+// get acked. Control frame manager also keeps track of lost control frames
+// which need to be retransmitted.
+class QUIC_EXPORT_PRIVATE QuicControlFrameManager {
+ public:
+ explicit QuicControlFrameManager(QuicSession* session);
+ QuicControlFrameManager(const QuicControlFrameManager& other) = delete;
+ QuicControlFrameManager(QuicControlFrameManager&& other) = delete;
+ ~QuicControlFrameManager();
+
+ // Tries to send a WINDOW_UPDATE_FRAME. Buffers the frame if it cannot be sent
+ // immediately.
+ void WriteOrBufferRstStream(QuicControlFrameId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
+
+ // Tries to send a GOAWAY_FRAME. Buffers the frame if it cannot be sent
+ // immediately.
+ void WriteOrBufferGoAway(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason);
+
+ // Tries to send a WINDOW_UPDATE_FRAME. Buffers the frame if it cannot be sent
+ // immediately.
+ void WriteOrBufferWindowUpdate(QuicStreamId id, QuicStreamOffset byte_offset);
+
+ // Tries to send a BLOCKED_FRAME. Buffers the frame if it cannot be sent
+ // immediately.
+ void WriteOrBufferBlocked(QuicStreamId id);
+
+ // Tries to send a STREAM_ID_BLOCKED Frame. Buffers the frame if it cannot be
+ // sent immediately.
+ void WriteOrBufferStreamIdBlocked(QuicStreamId id);
+
+ // Tries to send a MAX_STREAM_ID Frame. Buffers the frame if it cannot be sent
+ // immediately.
+ void WriteOrBufferMaxStreamId(QuicStreamId id);
+
+ // Tries to send an IETF-QUIC STOP_SENDING frame. The frame is buffered if it
+ // can not be sent immediately.
+ void WriteOrBufferStopSending(uint16_t code, QuicStreamId stream_id);
+
+ // Sends a PING_FRAME. Do not send PING if there is buffered frames.
+ void WritePing();
+
+ // Called when |frame| gets acked. Returns true if |frame| gets acked for the
+ // first time, return false otherwise.
+ bool OnControlFrameAcked(const QuicFrame& frame);
+
+ // Called when |frame| is considered as lost.
+ void OnControlFrameLost(const QuicFrame& frame);
+
+ // Called by the session when the connection becomes writable.
+ void OnCanWrite();
+
+ // Retransmit |frame| if it is still outstanding. Returns false if the frame
+ // does not get retransmitted because the connection is blocked. Otherwise,
+ // returns true.
+ bool RetransmitControlFrame(const QuicFrame& frame);
+
+ // Returns true if |frame| is outstanding and waiting to be acked. Returns
+ // false otherwise.
+ bool IsControlFrameOutstanding(const QuicFrame& frame) const;
+
+ // Returns true if there is any lost control frames waiting to be
+ // retransmitted.
+ bool HasPendingRetransmission() const;
+
+ // Returns true if there are any lost or new control frames waiting to be
+ // sent.
+ bool WillingToWrite() const;
+
+ private:
+ friend class test::QuicControlFrameManagerPeer;
+
+ // Tries to write buffered control frames to the peer.
+ void WriteBufferedFrames();
+
+ // Called when |frame| is sent for the first time or gets retransmitted.
+ void OnControlFrameSent(const QuicFrame& frame);
+
+ // Writes pending retransmissions if any.
+ void WritePendingRetransmission();
+
+ // Called when frame with |id| gets acked. Returns true if |id| gets acked for
+ // the first time, return false otherwise.
+ bool OnControlFrameIdAcked(QuicControlFrameId id);
+
+ // Retrieves the next pending retransmission. This must only be called when
+ // there are pending retransmissions.
+ QuicFrame NextPendingRetransmission() const;
+
+ // Returns true if there are buffered frames waiting to be sent for the first
+ // time.
+ bool HasBufferedFrames() const;
+
+ // Writes or buffers a control frame. Frame is buffered if there already
+ // are frames waiting to be sent. If no others waiting, will try to send the
+ // frame.
+ void WriteOrBufferQuicFrame(QuicFrame frame);
+
+ QuicDeque<QuicFrame> control_frames_;
+
+ // Id of latest saved control frame. 0 if no control frame has been saved.
+ QuicControlFrameId last_control_frame_id_;
+
+ // The control frame at the 0th index of control_frames_.
+ QuicControlFrameId least_unacked_;
+
+ // ID of the least unsent control frame.
+ QuicControlFrameId least_unsent_;
+
+ // 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_;
+
+ // Pointer to the owning QuicSession object.
+ QuicSession* session_;
+
+ // Last sent window update frame for each stream.
+ QuicSmallMap<QuicStreamId, QuicControlFrameId, 10> window_update_frames_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CONTROL_FRAME_MANAGER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc
new file mode 100644
index 00000000000..ccddadd1cc8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_control_frame_manager_test.cc
@@ -0,0 +1,306 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/quic_control_frame_manager.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::InSequence;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+
+class QuicControlFrameManagerPeer {
+ public:
+ static size_t QueueSize(QuicControlFrameManager* manager) {
+ return manager->control_frames_.size();
+ }
+};
+
+namespace {
+
+const QuicStreamId kTestStreamId = 5;
+const QuicStreamId kTestStopSendingCode = 321;
+
+class QuicControlFrameManagerTest : public QuicTest {
+ public:
+ bool ClearControlFrame(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+ bool SaveControlFrame(const QuicFrame& frame) {
+ frame_ = frame;
+ return true;
+ }
+
+ protected:
+ // Pre-fills the control frame queue with the following frames:
+ // ID Type
+ // 1 RST_STREAM
+ // 2 GO_AWAY
+ // 3 WINDOW_UPDATE
+ // 4 BLOCKED
+ // 5 STOP_SENDING
+ // This is verified. The tests then perform manipulations on these.
+ void Initialize() {
+ connection_ = new MockQuicConnection(&helper_, &alarm_factory_,
+ Perspective::IS_SERVER);
+ session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_);
+ manager_ = QuicMakeUnique<QuicControlFrameManager>(session_.get());
+ EXPECT_EQ(0u, QuicControlFrameManagerPeer::QueueSize(manager_.get()));
+ EXPECT_FALSE(manager_->HasPendingRetransmission());
+ EXPECT_FALSE(manager_->WillingToWrite());
+
+ EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false));
+ manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0);
+ manager_->WriteOrBufferGoAway(QUIC_PEER_GOING_AWAY, kTestStreamId,
+ "Going away.");
+ manager_->WriteOrBufferWindowUpdate(kTestStreamId, 100);
+ manager_->WriteOrBufferBlocked(kTestStreamId);
+ manager_->WriteOrBufferStopSending(kTestStopSendingCode, kTestStreamId);
+ number_of_frames_ = 5u;
+ ping_frame_id_ = 6u;
+ EXPECT_EQ(number_of_frames_,
+ QuicControlFrameManagerPeer::QueueSize(manager_.get()));
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&rst_stream_)));
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&goaway_)));
+ EXPECT_TRUE(
+ manager_->IsControlFrameOutstanding(QuicFrame(&window_update_)));
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&blocked_)));
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&stop_sending_)));
+ EXPECT_FALSE(manager_->IsControlFrameOutstanding(
+ QuicFrame(QuicPingFrame(ping_frame_id_))));
+
+ EXPECT_FALSE(manager_->HasPendingRetransmission());
+ EXPECT_TRUE(manager_->WillingToWrite());
+ }
+
+ QuicRstStreamFrame rst_stream_ = {1, kTestStreamId, QUIC_STREAM_CANCELLED, 0};
+ QuicGoAwayFrame goaway_ = {2, QUIC_PEER_GOING_AWAY, kTestStreamId,
+ "Going away."};
+ QuicWindowUpdateFrame window_update_ = {3, kTestStreamId, 100};
+ QuicBlockedFrame blocked_ = {4, kTestStreamId};
+ QuicStopSendingFrame stop_sending_ = {5, kTestStreamId, kTestStopSendingCode};
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnection* connection_;
+ std::unique_ptr<StrictMock<MockQuicSession>> session_;
+ std::unique_ptr<QuicControlFrameManager> manager_;
+ QuicFrame frame_;
+ size_t number_of_frames_;
+ int ping_frame_id_;
+};
+
+TEST_F(QuicControlFrameManagerTest, OnControlFrameAcked) {
+ Initialize();
+ InSequence s;
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(3)
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false));
+ // Send control frames 1, 2, 3.
+ manager_->OnCanWrite();
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&rst_stream_)));
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&goaway_)));
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&window_update_)));
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&blocked_)));
+ EXPECT_TRUE(manager_->IsControlFrameOutstanding(QuicFrame(&stop_sending_)));
+
+ EXPECT_FALSE(manager_->IsControlFrameOutstanding(
+ QuicFrame(QuicPingFrame(ping_frame_id_))));
+ EXPECT_TRUE(manager_->OnControlFrameAcked(QuicFrame(&window_update_)));
+ EXPECT_FALSE(manager_->IsControlFrameOutstanding(QuicFrame(&window_update_)));
+ EXPECT_EQ(number_of_frames_,
+ QuicControlFrameManagerPeer::QueueSize(manager_.get()));
+
+ EXPECT_TRUE(manager_->OnControlFrameAcked(QuicFrame(&goaway_)));
+ EXPECT_FALSE(manager_->IsControlFrameOutstanding(QuicFrame(&goaway_)));
+ EXPECT_EQ(number_of_frames_,
+ QuicControlFrameManagerPeer::QueueSize(manager_.get()));
+ EXPECT_TRUE(manager_->OnControlFrameAcked(QuicFrame(&rst_stream_)));
+ EXPECT_FALSE(manager_->IsControlFrameOutstanding(QuicFrame(&rst_stream_)));
+ // Only after the first frame in the queue is acked do the frames get
+ // removed ... now see that the length has been reduced by 3.
+ EXPECT_EQ(number_of_frames_ - 3u,
+ QuicControlFrameManagerPeer::QueueSize(manager_.get()));
+ // Duplicate ack.
+ EXPECT_FALSE(manager_->OnControlFrameAcked(QuicFrame(&goaway_)));
+
+ EXPECT_FALSE(manager_->HasPendingRetransmission());
+ EXPECT_TRUE(manager_->WillingToWrite());
+
+ // Send control frames 4, 5.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ manager_->OnCanWrite();
+ manager_->WritePing();
+ EXPECT_FALSE(manager_->WillingToWrite());
+}
+
+TEST_F(QuicControlFrameManagerTest, OnControlFrameLost) {
+ Initialize();
+ InSequence s;
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(3)
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false));
+ // Send control frames 1, 2, 3.
+ manager_->OnCanWrite();
+
+ // Lost control frames 1, 2, 3.
+ manager_->OnControlFrameLost(QuicFrame(&rst_stream_));
+ manager_->OnControlFrameLost(QuicFrame(&goaway_));
+ manager_->OnControlFrameLost(QuicFrame(&window_update_));
+ EXPECT_TRUE(manager_->HasPendingRetransmission());
+
+ // Ack control frame 2.
+ manager_->OnControlFrameAcked(QuicFrame(&goaway_));
+
+ // Retransmit control frames 1, 3.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ manager_->OnCanWrite();
+ EXPECT_FALSE(manager_->HasPendingRetransmission());
+ EXPECT_TRUE(manager_->WillingToWrite());
+
+ // Send control frames 4, 5, and 6.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(number_of_frames_ - 2u)
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ manager_->OnCanWrite();
+ manager_->WritePing();
+ EXPECT_FALSE(manager_->WillingToWrite());
+}
+
+TEST_F(QuicControlFrameManagerTest, RetransmitControlFrame) {
+ Initialize();
+ InSequence s;
+ // Send control frames 1, 2, 3, 4.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(number_of_frames_)
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ manager_->OnCanWrite();
+
+ // Ack control frame 2.
+ manager_->OnControlFrameAcked(QuicFrame(&goaway_));
+ // Do not retransmit an acked frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ EXPECT_TRUE(manager_->RetransmitControlFrame(QuicFrame(&goaway_)));
+
+ // Retransmit control frame 3.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ EXPECT_TRUE(manager_->RetransmitControlFrame(QuicFrame(&window_update_)));
+
+ // Retransmit control frame 4, and connection is write blocked.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false));
+ EXPECT_FALSE(manager_->RetransmitControlFrame(QuicFrame(&window_update_)));
+}
+
+TEST_F(QuicControlFrameManagerTest, DonotSendPingWithBufferedFrames) {
+ Initialize();
+ InSequence s;
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false));
+ // Send control frame 1.
+ manager_->OnCanWrite();
+ EXPECT_FALSE(manager_->HasPendingRetransmission());
+ EXPECT_TRUE(manager_->WillingToWrite());
+
+ // Send PING when there is buffered frames.
+ manager_->WritePing();
+ // Verify only the buffered frames are sent.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(number_of_frames_ - 1)
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ manager_->OnCanWrite();
+ EXPECT_FALSE(manager_->HasPendingRetransmission());
+ EXPECT_FALSE(manager_->WillingToWrite());
+}
+
+TEST_F(QuicControlFrameManagerTest, DonotRetransmitOldWindowUpdates) {
+ Initialize();
+ // Send two more window updates of the same stream.
+ manager_->WriteOrBufferWindowUpdate(kTestStreamId, 200);
+ QuicWindowUpdateFrame window_update2(number_of_frames_ + 1, kTestStreamId,
+ 200);
+
+ manager_->WriteOrBufferWindowUpdate(kTestStreamId, 300);
+ QuicWindowUpdateFrame window_update3(number_of_frames_ + 2, kTestStreamId,
+ 300);
+ InSequence s;
+ // Flush all buffered control frames.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ manager_->OnCanWrite();
+
+ // Mark all 3 window updates as lost.
+ manager_->OnControlFrameLost(QuicFrame(&window_update_));
+ manager_->OnControlFrameLost(QuicFrame(&window_update2));
+ manager_->OnControlFrameLost(QuicFrame(&window_update3));
+ EXPECT_TRUE(manager_->HasPendingRetransmission());
+ EXPECT_TRUE(manager_->WillingToWrite());
+
+ // Verify only the latest window update gets retransmitted.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &QuicControlFrameManagerTest::SaveControlFrame));
+ manager_->OnCanWrite();
+ EXPECT_EQ(number_of_frames_ + 2u,
+ frame_.window_update_frame->control_frame_id);
+ EXPECT_FALSE(manager_->HasPendingRetransmission());
+ EXPECT_FALSE(manager_->WillingToWrite());
+ DeleteFrame(&frame_);
+}
+
+TEST_F(QuicControlFrameManagerTest, RetransmitWindowUpdateOfDifferentStreams) {
+ Initialize();
+ // Send two more window updates of different streams.
+ manager_->WriteOrBufferWindowUpdate(kTestStreamId + 2, 200);
+ QuicWindowUpdateFrame window_update2(5, kTestStreamId + 2, 200);
+
+ manager_->WriteOrBufferWindowUpdate(kTestStreamId + 4, 300);
+ QuicWindowUpdateFrame window_update3(6, kTestStreamId + 4, 300);
+ InSequence s;
+ // Flush all buffered control frames.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ manager_->OnCanWrite();
+
+ // Mark all 3 window updates as lost.
+ manager_->OnControlFrameLost(QuicFrame(&window_update_));
+ manager_->OnControlFrameLost(QuicFrame(&window_update2));
+ manager_->OnControlFrameLost(QuicFrame(&window_update3));
+ EXPECT_TRUE(manager_->HasPendingRetransmission());
+ EXPECT_TRUE(manager_->WillingToWrite());
+
+ // Verify all 3 window updates get retransmitted.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(3)
+ .WillRepeatedly(
+ Invoke(this, &QuicControlFrameManagerTest::ClearControlFrame));
+ manager_->OnCanWrite();
+ EXPECT_FALSE(manager_->HasPendingRetransmission());
+ EXPECT_FALSE(manager_->WillingToWrite());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc
new file mode 100644
index 00000000000..d6c9af4bf04
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.cc
@@ -0,0 +1,702 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_client_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::
+ ChannelIDSourceCallbackImpl(QuicCryptoClientHandshaker* parent)
+ : parent_(parent) {}
+
+QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::
+ ~ChannelIDSourceCallbackImpl() {}
+
+void QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::Run(
+ std::unique_ptr<ChannelIDKey>* channel_id_key) {
+ if (parent_ == nullptr) {
+ return;
+ }
+
+ parent_->channel_id_key_ = std::move(*channel_id_key);
+ parent_->channel_id_source_callback_run_ = true;
+ parent_->channel_id_source_callback_ = nullptr;
+ parent_->DoHandshakeLoop(nullptr);
+
+ // The ChannelIDSource owns this object and will delete it when this method
+ // returns.
+}
+
+void QuicCryptoClientHandshaker::ChannelIDSourceCallbackImpl::Cancel() {
+ parent_ = nullptr;
+}
+
+QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::
+ ProofVerifierCallbackImpl(QuicCryptoClientHandshaker* parent)
+ : parent_(parent) {}
+
+QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::
+ ~ProofVerifierCallbackImpl() {}
+
+void QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::Run(
+ bool ok,
+ const std::string& error_details,
+ std::unique_ptr<ProofVerifyDetails>* details) {
+ if (parent_ == nullptr) {
+ return;
+ }
+
+ parent_->verify_ok_ = ok;
+ parent_->verify_error_details_ = error_details;
+ parent_->verify_details_ = std::move(*details);
+ parent_->proof_verify_callback_ = nullptr;
+ parent_->DoHandshakeLoop(nullptr);
+
+ // The ProofVerifier owns this object and will delete it when this method
+ // returns.
+}
+
+void QuicCryptoClientHandshaker::ProofVerifierCallbackImpl::Cancel() {
+ parent_ = nullptr;
+}
+
+QuicCryptoClientHandshaker::QuicCryptoClientHandshaker(
+ const QuicServerId& server_id,
+ QuicCryptoClientStream* stream,
+ QuicSession* session,
+ std::unique_ptr<ProofVerifyContext> verify_context,
+ QuicCryptoClientConfig* crypto_config,
+ QuicCryptoClientStream::ProofHandler* proof_handler)
+ : QuicCryptoHandshaker(stream, session),
+ stream_(stream),
+ session_(session),
+ next_state_(STATE_IDLE),
+ num_client_hellos_(0),
+ crypto_config_(crypto_config),
+ server_id_(server_id),
+ generation_counter_(0),
+ channel_id_sent_(false),
+ channel_id_source_callback_run_(false),
+ channel_id_source_callback_(nullptr),
+ verify_context_(std::move(verify_context)),
+ proof_verify_callback_(nullptr),
+ proof_handler_(proof_handler),
+ verify_ok_(false),
+ stateless_reject_received_(false),
+ proof_verify_start_time_(QuicTime::Zero()),
+ num_scup_messages_received_(0),
+ encryption_established_(false),
+ handshake_confirmed_(false),
+ crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {}
+
+QuicCryptoClientHandshaker::~QuicCryptoClientHandshaker() {
+ if (channel_id_source_callback_) {
+ channel_id_source_callback_->Cancel();
+ }
+ if (proof_verify_callback_) {
+ proof_verify_callback_->Cancel();
+ }
+}
+
+void QuicCryptoClientHandshaker::OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ QuicCryptoHandshaker::OnHandshakeMessage(message);
+ if (message.tag() == kSCUP) {
+ if (!handshake_confirmed()) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE,
+ "Early SCUP disallowed");
+ return;
+ }
+
+ // |message| is an update from the server, so we treat it differently from a
+ // handshake message.
+ HandleServerConfigUpdateMessage(message);
+ num_scup_messages_received_++;
+ return;
+ }
+
+ // Do not process handshake messages after the handshake is confirmed.
+ if (handshake_confirmed()) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE,
+ "Unexpected handshake message");
+ return;
+ }
+
+ DoHandshakeLoop(&message);
+}
+
+bool QuicCryptoClientHandshaker::CryptoConnect() {
+ next_state_ = STATE_INITIALIZE;
+ DoHandshakeLoop(nullptr);
+ return session()->connection()->connected();
+}
+
+int QuicCryptoClientHandshaker::num_sent_client_hellos() const {
+ return num_client_hellos_;
+}
+
+int QuicCryptoClientHandshaker::num_scup_messages_received() const {
+ return num_scup_messages_received_;
+}
+
+bool QuicCryptoClientHandshaker::WasChannelIDSent() const {
+ return channel_id_sent_;
+}
+
+bool QuicCryptoClientHandshaker::WasChannelIDSourceCallbackRun() const {
+ return channel_id_source_callback_run_;
+}
+
+std::string QuicCryptoClientHandshaker::chlo_hash() const {
+ return chlo_hash_;
+}
+
+bool QuicCryptoClientHandshaker::encryption_established() const {
+ return encryption_established_;
+}
+
+bool QuicCryptoClientHandshaker::handshake_confirmed() const {
+ return handshake_confirmed_;
+}
+
+const QuicCryptoNegotiatedParameters&
+QuicCryptoClientHandshaker::crypto_negotiated_params() const {
+ return *crypto_negotiated_params_;
+}
+
+CryptoMessageParser* QuicCryptoClientHandshaker::crypto_message_parser() {
+ return QuicCryptoHandshaker::crypto_message_parser();
+}
+
+void QuicCryptoClientHandshaker::HandleServerConfigUpdateMessage(
+ const CryptoHandshakeMessage& server_config_update) {
+ DCHECK(server_config_update.tag() == kSCUP);
+ std::string error_details;
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_->LookupOrCreate(server_id_);
+ QuicErrorCode error = crypto_config_->ProcessServerConfigUpdate(
+ server_config_update, session()->connection()->clock()->WallNow(),
+ session()->connection()->transport_version(), chlo_hash_, cached,
+ crypto_negotiated_params_, &error_details);
+
+ if (error != QUIC_NO_ERROR) {
+ stream_->CloseConnectionWithDetails(
+ error, "Server config update invalid: " + error_details);
+ return;
+ }
+
+ DCHECK(handshake_confirmed());
+ if (proof_verify_callback_) {
+ proof_verify_callback_->Cancel();
+ }
+ next_state_ = STATE_INITIALIZE_SCUP;
+ DoHandshakeLoop(nullptr);
+}
+
+void QuicCryptoClientHandshaker::DoHandshakeLoop(
+ const CryptoHandshakeMessage* in) {
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_->LookupOrCreate(server_id_);
+
+ QuicAsyncStatus rv = QUIC_SUCCESS;
+ do {
+ CHECK_NE(STATE_NONE, next_state_);
+ const State state = next_state_;
+ next_state_ = STATE_IDLE;
+ rv = QUIC_SUCCESS;
+ switch (state) {
+ case STATE_INITIALIZE:
+ DoInitialize(cached);
+ break;
+ case STATE_SEND_CHLO:
+ DoSendCHLO(cached);
+ return; // return waiting to hear from server.
+ case STATE_RECV_REJ:
+ DoReceiveREJ(in, cached);
+ break;
+ case STATE_VERIFY_PROOF:
+ rv = DoVerifyProof(cached);
+ break;
+ case STATE_VERIFY_PROOF_COMPLETE:
+ DoVerifyProofComplete(cached);
+ break;
+ case STATE_GET_CHANNEL_ID:
+ rv = DoGetChannelID(cached);
+ break;
+ case STATE_GET_CHANNEL_ID_COMPLETE:
+ DoGetChannelIDComplete();
+ break;
+ case STATE_RECV_SHLO:
+ DoReceiveSHLO(in, cached);
+ break;
+ case STATE_IDLE:
+ // This means that the peer sent us a message that we weren't expecting.
+ stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
+ "Handshake in idle state");
+ return;
+ case STATE_INITIALIZE_SCUP:
+ DoInitializeServerConfigUpdate(cached);
+ break;
+ case STATE_NONE:
+ QUIC_NOTREACHED();
+ return; // We are done.
+ }
+ } while (rv != QUIC_PENDING && next_state_ != STATE_NONE);
+}
+
+void QuicCryptoClientHandshaker::DoInitialize(
+ QuicCryptoClientConfig::CachedState* cached) {
+ if (!cached->IsEmpty() && !cached->signature().empty()) {
+ // Note that we verify the proof even if the cached proof is valid.
+ // This allows us to respond to CA trust changes or certificate
+ // expiration because it may have been a while since we last verified
+ // the proof.
+ DCHECK(crypto_config_->proof_verifier());
+ // Track proof verification time when cached server config is used.
+ proof_verify_start_time_ = session()->connection()->clock()->Now();
+ chlo_hash_ = cached->chlo_hash();
+ // If the cached state needs to be verified, do it now.
+ next_state_ = STATE_VERIFY_PROOF;
+ } else {
+ next_state_ = STATE_GET_CHANNEL_ID;
+ }
+}
+
+void QuicCryptoClientHandshaker::DoSendCHLO(
+ QuicCryptoClientConfig::CachedState* cached) {
+ if (stateless_reject_received_) {
+ // If we've gotten to this point, we've sent at least one hello
+ // and received a stateless reject in response. We cannot
+ // continue to send hellos because the server has abandoned state
+ // for this connection. Abandon further handshakes.
+ next_state_ = STATE_NONE;
+ if (session()->connection()->connected()) {
+ session()->connection()->CloseConnection(
+ QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject received",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ }
+ return;
+ }
+
+ // Send the client hello in plaintext.
+ session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ encryption_established_ = false;
+ if (num_client_hellos_ >= QuicCryptoClientStream::kMaxClientHellos) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_CRYPTO_TOO_MANY_REJECTS,
+ QuicStrCat("More than ", QuicCryptoClientStream::kMaxClientHellos,
+ " rejects"));
+ return;
+ }
+ num_client_hellos_++;
+
+ CryptoHandshakeMessage out;
+ DCHECK(session() != nullptr);
+ DCHECK(session()->config() != nullptr);
+ // Send all the options, regardless of whether we're sending an
+ // inchoate or subsequent hello.
+ session()->config()->ToHandshakeMessage(&out);
+
+ if (!cached->IsComplete(session()->connection()->clock()->WallNow())) {
+ crypto_config_->FillInchoateClientHello(
+ server_id_, session()->supported_versions().front(), cached,
+ session()->connection()->random_generator(),
+ /* demand_x509_proof= */ true, crypto_negotiated_params_, &out);
+ // Pad the inchoate client hello to fill up a packet.
+ const QuicByteCount kFramingOverhead = 50; // A rough estimate.
+ const QuicByteCount max_packet_size =
+ session()->connection()->max_packet_length();
+ if (max_packet_size <= kFramingOverhead) {
+ QUIC_DLOG(DFATAL) << "max_packet_length (" << max_packet_size
+ << ") has no room for framing overhead.";
+ stream_->CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+ "max_packet_size too smalll");
+ return;
+ }
+ if (kClientHelloMinimumSize > max_packet_size - kFramingOverhead) {
+ QUIC_DLOG(DFATAL) << "Client hello won't fit in a single packet.";
+ stream_->CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+ "CHLO too large");
+ return;
+ }
+ next_state_ = STATE_RECV_REJ;
+ chlo_hash_ = CryptoUtils::HashHandshakeMessage(out, Perspective::IS_CLIENT);
+ session()->connection()->set_fully_pad_crypto_hadshake_packets(
+ crypto_config_->pad_inchoate_hello());
+ SendHandshakeMessage(out);
+ return;
+ }
+
+ // If the server nonce is empty, copy over the server nonce from a previous
+ // SREJ, if there is one.
+ if (GetQuicReloadableFlag(enable_quic_stateless_reject_support) &&
+ crypto_negotiated_params_->server_nonce.empty() &&
+ cached->has_server_nonce()) {
+ crypto_negotiated_params_->server_nonce = cached->GetNextServerNonce();
+ DCHECK(!crypto_negotiated_params_->server_nonce.empty());
+ }
+
+ std::string error_details;
+ QuicErrorCode error = crypto_config_->FillClientHello(
+ server_id_, session()->connection()->connection_id(),
+ session()->supported_versions().front(), cached,
+ session()->connection()->clock()->WallNow(),
+ session()->connection()->random_generator(), channel_id_key_.get(),
+ crypto_negotiated_params_, &out, &error_details);
+ if (error != QUIC_NO_ERROR) {
+ // Flush the cached config so that, if it's bad, the server has a
+ // chance to send us another in the future.
+ cached->InvalidateServerConfig();
+ stream_->CloseConnectionWithDetails(error, error_details);
+ return;
+ }
+ chlo_hash_ = CryptoUtils::HashHandshakeMessage(out, Perspective::IS_CLIENT);
+ channel_id_sent_ = (channel_id_key_ != nullptr);
+ if (cached->proof_verify_details()) {
+ proof_handler_->OnProofVerifyDetailsAvailable(
+ *cached->proof_verify_details());
+ }
+ next_state_ = STATE_RECV_SHLO;
+ session()->connection()->set_fully_pad_crypto_hadshake_packets(
+ crypto_config_->pad_full_hello());
+ SendHandshakeMessage(out);
+ // Be prepared to decrypt with the new server write key.
+ if (session()->connection()->version().KnowsWhichDecrypterToUse()) {
+ session()->connection()->InstallDecrypter(
+ ENCRYPTION_ZERO_RTT,
+ std::move(crypto_negotiated_params_->initial_crypters.decrypter));
+ } else {
+ session()->connection()->SetAlternativeDecrypter(
+ ENCRYPTION_ZERO_RTT,
+ std::move(crypto_negotiated_params_->initial_crypters.decrypter),
+ true /* latch once used */);
+ }
+ // Send subsequent packets under encryption on the assumption that the
+ // server will accept the handshake.
+ session()->connection()->SetEncrypter(
+ ENCRYPTION_ZERO_RTT,
+ std::move(crypto_negotiated_params_->initial_crypters.encrypter));
+ session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+
+ // TODO(ianswett): Merge ENCRYPTION_REESTABLISHED and
+ // ENCRYPTION_FIRST_ESTABLSIHED
+ encryption_established_ = true;
+ session()->OnCryptoHandshakeEvent(QuicSession::ENCRYPTION_REESTABLISHED);
+}
+
+void QuicCryptoClientHandshaker::DoReceiveREJ(
+ const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached) {
+ // We sent a dummy CHLO because we didn't have enough information to
+ // perform a handshake, or we sent a full hello that the server
+ // rejected. Here we hope to have a REJ that contains the information
+ // that we need.
+ if ((in->tag() != kREJ) && (in->tag() != kSREJ)) {
+ next_state_ = STATE_NONE;
+ stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
+ "Expected REJ");
+ return;
+ }
+
+ QuicTagVector reject_reasons;
+ static_assert(sizeof(QuicTag) == sizeof(uint32_t), "header out of sync");
+ if (in->GetTaglist(kRREJ, &reject_reasons) == QUIC_NO_ERROR) {
+ uint32_t packed_error = 0;
+ for (size_t i = 0; i < reject_reasons.size(); ++i) {
+ // HANDSHAKE_OK is 0 and don't report that as error.
+ if (reject_reasons[i] == HANDSHAKE_OK || reject_reasons[i] >= 32) {
+ continue;
+ }
+ HandshakeFailureReason reason =
+ static_cast<HandshakeFailureReason>(reject_reasons[i]);
+ packed_error |= 1 << (reason - 1);
+ }
+ DVLOG(1) << "Reasons for rejection: " << packed_error;
+ if (num_client_hellos_ == QuicCryptoClientStream::kMaxClientHellos) {
+ QuicClientSparseHistogram("QuicClientHelloRejectReasons.TooMany",
+ packed_error);
+ }
+ QuicClientSparseHistogram("QuicClientHelloRejectReasons.Secure",
+ packed_error);
+ }
+
+ // Receipt of a REJ message means that the server received the CHLO
+ // so we can cancel and retransmissions.
+ session()->NeuterUnencryptedData();
+
+ stateless_reject_received_ = in->tag() == kSREJ;
+ std::string error_details;
+ QuicErrorCode error = crypto_config_->ProcessRejection(
+ *in, session()->connection()->clock()->WallNow(),
+ session()->connection()->transport_version(), chlo_hash_, cached,
+ crypto_negotiated_params_, &error_details);
+
+ if (error != QUIC_NO_ERROR) {
+ next_state_ = STATE_NONE;
+ stream_->CloseConnectionWithDetails(error, error_details);
+ return;
+ }
+ if (!cached->proof_valid()) {
+ if (!cached->signature().empty()) {
+ // Note that we only verify the proof if the cached proof is not
+ // valid. If the cached proof is valid here, someone else must have
+ // just added the server config to the cache and verified the proof,
+ // so we can assume no CA trust changes or certificate expiration
+ // has happened since then.
+ next_state_ = STATE_VERIFY_PROOF;
+ return;
+ }
+ }
+ next_state_ = STATE_GET_CHANNEL_ID;
+}
+
+QuicAsyncStatus QuicCryptoClientHandshaker::DoVerifyProof(
+ QuicCryptoClientConfig::CachedState* cached) {
+ ProofVerifier* verifier = crypto_config_->proof_verifier();
+ DCHECK(verifier);
+ next_state_ = STATE_VERIFY_PROOF_COMPLETE;
+ generation_counter_ = cached->generation_counter();
+
+ ProofVerifierCallbackImpl* proof_verify_callback =
+ new ProofVerifierCallbackImpl(this);
+
+ verify_ok_ = false;
+
+ QuicAsyncStatus status = verifier->VerifyProof(
+ server_id_.host(), server_id_.port(), cached->server_config(),
+ session()->connection()->transport_version(), chlo_hash_, cached->certs(),
+ cached->cert_sct(), cached->signature(), verify_context_.get(),
+ &verify_error_details_, &verify_details_,
+ std::unique_ptr<ProofVerifierCallback>(proof_verify_callback));
+
+ switch (status) {
+ case QUIC_PENDING:
+ proof_verify_callback_ = proof_verify_callback;
+ QUIC_DVLOG(1) << "Doing VerifyProof";
+ break;
+ case QUIC_FAILURE:
+ break;
+ case QUIC_SUCCESS:
+ verify_ok_ = true;
+ break;
+ }
+ return status;
+}
+
+void QuicCryptoClientHandshaker::DoVerifyProofComplete(
+ QuicCryptoClientConfig::CachedState* cached) {
+ if (proof_verify_start_time_.IsInitialized()) {
+ QUIC_CLIENT_HISTOGRAM_TIMES(
+ "QuicSession.VerifyProofTime.CachedServerConfig",
+ (session()->connection()->clock()->Now() - proof_verify_start_time_),
+ QuicTime::Delta::FromMilliseconds(1), QuicTime::Delta::FromSeconds(10),
+ 50, "");
+ }
+ if (!verify_ok_) {
+ if (verify_details_) {
+ proof_handler_->OnProofVerifyDetailsAvailable(*verify_details_);
+ }
+ if (num_client_hellos_ == 0) {
+ cached->Clear();
+ next_state_ = STATE_INITIALIZE;
+ return;
+ }
+ next_state_ = STATE_NONE;
+ QUIC_CLIENT_HISTOGRAM_BOOL("QuicVerifyProofFailed.HandshakeConfirmed",
+ handshake_confirmed(), "");
+ stream_->CloseConnectionWithDetails(
+ QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_);
+ return;
+ }
+
+ // Check if generation_counter has changed between STATE_VERIFY_PROOF and
+ // STATE_VERIFY_PROOF_COMPLETE state changes.
+ if (generation_counter_ != cached->generation_counter()) {
+ next_state_ = STATE_VERIFY_PROOF;
+ } else {
+ SetCachedProofValid(cached);
+ cached->SetProofVerifyDetails(verify_details_.release());
+ if (!handshake_confirmed()) {
+ next_state_ = STATE_GET_CHANNEL_ID;
+ } else {
+ // TODO: Enable Expect-Staple. https://crbug.com/631101
+ next_state_ = STATE_NONE;
+ }
+ }
+}
+
+QuicAsyncStatus QuicCryptoClientHandshaker::DoGetChannelID(
+ QuicCryptoClientConfig::CachedState* cached) {
+ next_state_ = STATE_GET_CHANNEL_ID_COMPLETE;
+ channel_id_key_.reset();
+ if (!RequiresChannelID(cached)) {
+ next_state_ = STATE_SEND_CHLO;
+ return QUIC_SUCCESS;
+ }
+
+ ChannelIDSourceCallbackImpl* channel_id_source_callback =
+ new ChannelIDSourceCallbackImpl(this);
+ QuicAsyncStatus status = crypto_config_->channel_id_source()->GetChannelIDKey(
+ server_id_.host(), &channel_id_key_, channel_id_source_callback);
+
+ switch (status) {
+ case QUIC_PENDING:
+ channel_id_source_callback_ = channel_id_source_callback;
+ QUIC_DVLOG(1) << "Looking up channel ID";
+ break;
+ case QUIC_FAILURE:
+ next_state_ = STATE_NONE;
+ delete channel_id_source_callback;
+ stream_->CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE,
+ "Channel ID lookup failed");
+ break;
+ case QUIC_SUCCESS:
+ delete channel_id_source_callback;
+ break;
+ }
+ return status;
+}
+
+void QuicCryptoClientHandshaker::DoGetChannelIDComplete() {
+ if (!channel_id_key_.get()) {
+ next_state_ = STATE_NONE;
+ stream_->CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE,
+ "Channel ID lookup failed");
+ return;
+ }
+ next_state_ = STATE_SEND_CHLO;
+}
+
+void QuicCryptoClientHandshaker::DoReceiveSHLO(
+ const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached) {
+ next_state_ = STATE_NONE;
+ // We sent a CHLO that we expected to be accepted and now we're
+ // hoping for a SHLO from the server to confirm that. First check
+ // to see whether the response was a reject, and if so, move on to
+ // the reject-processing state.
+ if ((in->tag() == kREJ) || (in->tag() == kSREJ)) {
+ // A reject message must be sent in ENCRYPTION_INITIAL.
+ if (session()->connection()->last_decrypted_level() != ENCRYPTION_INITIAL) {
+ // The rejection was sent encrypted!
+ stream_->CloseConnectionWithDetails(
+ QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, "encrypted REJ message");
+ return;
+ }
+ next_state_ = STATE_RECV_REJ;
+ return;
+ }
+
+ if (in->tag() != kSHLO) {
+ stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
+ "Expected SHLO or REJ");
+ return;
+ }
+
+ if (session()->connection()->last_decrypted_level() == ENCRYPTION_INITIAL) {
+ // The server hello was sent without encryption.
+ stream_->CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT,
+ "unencrypted SHLO message");
+ return;
+ }
+
+ std::string error_details;
+ QuicErrorCode error = crypto_config_->ProcessServerHello(
+ *in, session()->connection()->connection_id(),
+ session()->connection()->version(),
+ session()->connection()->server_supported_versions(), cached,
+ crypto_negotiated_params_, &error_details);
+
+ if (error != QUIC_NO_ERROR) {
+ stream_->CloseConnectionWithDetails(
+ error, "Server hello invalid: " + error_details);
+ return;
+ }
+ error = session()->config()->ProcessPeerHello(*in, SERVER, &error_details);
+ if (error != QUIC_NO_ERROR) {
+ stream_->CloseConnectionWithDetails(
+ error, "Server hello invalid: " + error_details);
+ return;
+ }
+ session()->OnConfigNegotiated();
+
+ CrypterPair* crypters = &crypto_negotiated_params_->forward_secure_crypters;
+ // TODO(agl): we don't currently latch this decrypter because the idea
+ // has been floated that the server shouldn't send packets encrypted
+ // with the FORWARD_SECURE key until it receives a FORWARD_SECURE
+ // packet from the client.
+ if (session()->connection()->version().KnowsWhichDecrypterToUse()) {
+ session()->connection()->InstallDecrypter(ENCRYPTION_FORWARD_SECURE,
+ std::move(crypters->decrypter));
+ } else {
+ session()->connection()->SetAlternativeDecrypter(
+ ENCRYPTION_FORWARD_SECURE, std::move(crypters->decrypter),
+ false /* don't latch */);
+ }
+ session()->connection()->SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ std::move(crypters->encrypter));
+ session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+
+ handshake_confirmed_ = true;
+ session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+ session()->connection()->OnHandshakeComplete();
+}
+
+void QuicCryptoClientHandshaker::DoInitializeServerConfigUpdate(
+ QuicCryptoClientConfig::CachedState* cached) {
+ bool update_ignored = false;
+ if (!cached->IsEmpty() && !cached->signature().empty()) {
+ // Note that we verify the proof even if the cached proof is valid.
+ DCHECK(crypto_config_->proof_verifier());
+ next_state_ = STATE_VERIFY_PROOF;
+ } else {
+ update_ignored = true;
+ next_state_ = STATE_NONE;
+ }
+ QUIC_CLIENT_HISTOGRAM_COUNTS("QuicNumServerConfig.UpdateMessagesIgnored",
+ update_ignored, 1, 1000000, 50, "");
+}
+
+void QuicCryptoClientHandshaker::SetCachedProofValid(
+ QuicCryptoClientConfig::CachedState* cached) {
+ cached->SetProofValid();
+ proof_handler_->OnProofValid(*cached);
+}
+
+bool QuicCryptoClientHandshaker::RequiresChannelID(
+ QuicCryptoClientConfig::CachedState* cached) {
+ if (server_id_.privacy_mode_enabled() ||
+ !crypto_config_->channel_id_source()) {
+ return false;
+ }
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ if (!scfg) { // scfg may be null then we send an inchoate CHLO.
+ return false;
+ }
+ QuicTagVector their_proof_demands;
+ if (scfg->GetTaglist(kPDMD, &their_proof_demands) != QUIC_NO_ERROR) {
+ return false;
+ }
+ for (const QuicTag tag : their_proof_demands) {
+ if (tag == kCHID) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h
new file mode 100644
index 00000000000..4aa5d469dcb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_
+#define QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An implementation of QuicCryptoClientStream::HandshakerDelegate which uses
+// QUIC crypto as the crypto handshake protocol.
+class QUIC_EXPORT_PRIVATE QuicCryptoClientHandshaker
+ : public QuicCryptoClientStream::HandshakerDelegate,
+ public QuicCryptoHandshaker {
+ public:
+ QuicCryptoClientHandshaker(
+ const QuicServerId& server_id,
+ QuicCryptoClientStream* stream,
+ QuicSession* session,
+ std::unique_ptr<ProofVerifyContext> verify_context,
+ QuicCryptoClientConfig* crypto_config,
+ QuicCryptoClientStream::ProofHandler* proof_handler);
+ QuicCryptoClientHandshaker(const QuicCryptoClientHandshaker&) = delete;
+ QuicCryptoClientHandshaker& operator=(const QuicCryptoClientHandshaker&) =
+ delete;
+
+ ~QuicCryptoClientHandshaker() override;
+
+ // From QuicCryptoClientStream::HandshakerDelegate
+ bool CryptoConnect() override;
+ int num_sent_client_hellos() const override;
+ int num_scup_messages_received() const override;
+ bool WasChannelIDSent() const override;
+ bool WasChannelIDSourceCallbackRun() const override;
+ std::string chlo_hash() const override;
+ bool encryption_established() const override;
+ bool handshake_confirmed() const override;
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override;
+ CryptoMessageParser* crypto_message_parser() override;
+
+ // From QuicCryptoHandshaker
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
+
+ protected:
+ // Returns the QuicSession that this stream belongs to.
+ QuicSession* session() const { return session_; }
+
+ // Send either InchoateClientHello or ClientHello message to the server.
+ void DoSendCHLO(QuicCryptoClientConfig::CachedState* cached);
+
+ private:
+ // ChannelIDSourceCallbackImpl is passed as the callback method to
+ // GetChannelIDKey. The ChannelIDSource calls this class with the result of
+ // channel ID lookup when lookup is performed asynchronously.
+ class ChannelIDSourceCallbackImpl : public ChannelIDSourceCallback {
+ public:
+ explicit ChannelIDSourceCallbackImpl(QuicCryptoClientHandshaker* parent);
+ ~ChannelIDSourceCallbackImpl() override;
+
+ // ChannelIDSourceCallback interface.
+ void Run(std::unique_ptr<ChannelIDKey>* channel_id_key) override;
+
+ // Cancel causes any future callbacks to be ignored. It must be called on
+ // the same thread as the callback will be made on.
+ void Cancel();
+
+ private:
+ QuicCryptoClientHandshaker* parent_;
+ };
+
+ // ProofVerifierCallbackImpl is passed as the callback method to VerifyProof.
+ // The ProofVerifier calls this class with the result of proof verification
+ // when verification is performed asynchronously.
+ class ProofVerifierCallbackImpl : public ProofVerifierCallback {
+ public:
+ explicit ProofVerifierCallbackImpl(QuicCryptoClientHandshaker* parent);
+ ~ProofVerifierCallbackImpl() override;
+
+ // ProofVerifierCallback interface.
+ void Run(bool ok,
+ const std::string& error_details,
+ std::unique_ptr<ProofVerifyDetails>* details) override;
+
+ // Cancel causes any future callbacks to be ignored. It must be called on
+ // the same thread as the callback will be made on.
+ void Cancel();
+
+ private:
+ QuicCryptoClientHandshaker* parent_;
+ };
+
+ enum State {
+ STATE_IDLE,
+ STATE_INITIALIZE,
+ STATE_SEND_CHLO,
+ STATE_RECV_REJ,
+ STATE_VERIFY_PROOF,
+ STATE_VERIFY_PROOF_COMPLETE,
+ STATE_GET_CHANNEL_ID,
+ STATE_GET_CHANNEL_ID_COMPLETE,
+ STATE_RECV_SHLO,
+ STATE_INITIALIZE_SCUP,
+ STATE_NONE,
+ };
+
+ // Handles new server config and optional source-address token provided by the
+ // server during a connection.
+ void HandleServerConfigUpdateMessage(
+ const CryptoHandshakeMessage& server_config_update);
+
+ // DoHandshakeLoop performs a step of the handshake state machine. Note that
+ // |in| may be nullptr if the call did not result from a received message.
+ void DoHandshakeLoop(const CryptoHandshakeMessage* in);
+
+ // Start the handshake process.
+ void DoInitialize(QuicCryptoClientConfig::CachedState* cached);
+
+ // Process REJ message from the server.
+ void DoReceiveREJ(const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached);
+
+ // Start the proof verification process. Returns the QuicAsyncStatus returned
+ // by the ProofVerifier's VerifyProof.
+ QuicAsyncStatus DoVerifyProof(QuicCryptoClientConfig::CachedState* cached);
+
+ // If proof is valid then it sets the proof as valid (which persists the
+ // server config). If not, it closes the connection.
+ void DoVerifyProofComplete(QuicCryptoClientConfig::CachedState* cached);
+
+ // Start the look up of Channel ID process. Returns either QUIC_SUCCESS if
+ // RequiresChannelID returns false or QuicAsyncStatus returned by
+ // GetChannelIDKey.
+ QuicAsyncStatus DoGetChannelID(QuicCryptoClientConfig::CachedState* cached);
+
+ // If there is no channel ID, then close the connection otherwise transtion to
+ // STATE_SEND_CHLO state.
+ void DoGetChannelIDComplete();
+
+ // Process SHLO message from the server.
+ void DoReceiveSHLO(const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached);
+
+ // Start the proof verification if |server_id_| is https and |cached| has
+ // signature.
+ void DoInitializeServerConfigUpdate(
+ QuicCryptoClientConfig::CachedState* cached);
+
+ // Called to set the proof of |cached| valid. Also invokes the session's
+ // OnProofValid() method.
+ void SetCachedProofValid(QuicCryptoClientConfig::CachedState* cached);
+
+ // Returns true if the server crypto config in |cached| requires a ChannelID
+ // and the client config settings also allow sending a ChannelID.
+ bool RequiresChannelID(QuicCryptoClientConfig::CachedState* cached);
+
+ QuicCryptoClientStream* stream_;
+
+ QuicSession* session_;
+
+ State next_state_;
+ // num_client_hellos_ contains the number of client hello messages that this
+ // connection has sent.
+ int num_client_hellos_;
+
+ QuicCryptoClientConfig* const crypto_config_;
+
+ // SHA-256 hash of the most recently sent CHLO.
+ std::string chlo_hash_;
+
+ // Server's (hostname, port, is_https, privacy_mode) tuple.
+ const QuicServerId server_id_;
+
+ // Generation counter from QuicCryptoClientConfig's CachedState.
+ uint64_t generation_counter_;
+
+ // True if a channel ID was sent.
+ bool channel_id_sent_;
+
+ // True if channel_id_source_callback_ was run.
+ bool channel_id_source_callback_run_;
+
+ // channel_id_source_callback_ contains the callback object that we passed
+ // to an asynchronous channel ID lookup. The ChannelIDSource owns this
+ // object.
+ ChannelIDSourceCallbackImpl* channel_id_source_callback_;
+
+ // These members are used to store the result of an asynchronous channel ID
+ // lookup. These members must not be used after
+ // STATE_GET_CHANNEL_ID_COMPLETE.
+ std::unique_ptr<ChannelIDKey> channel_id_key_;
+
+ // verify_context_ contains the context object that we pass to asynchronous
+ // proof verifications.
+ std::unique_ptr<ProofVerifyContext> verify_context_;
+
+ // proof_verify_callback_ contains the callback object that we passed to an
+ // asynchronous proof verification. The ProofVerifier owns this object.
+ ProofVerifierCallbackImpl* proof_verify_callback_;
+ // proof_handler_ contains the callback object used by a quic client
+ // for proof verification. It is not owned by this class.
+ QuicCryptoClientStream::ProofHandler* proof_handler_;
+
+ // These members are used to store the result of an asynchronous proof
+ // verification. These members must not be used after
+ // STATE_VERIFY_PROOF_COMPLETE.
+ bool verify_ok_;
+ std::string verify_error_details_;
+ std::unique_ptr<ProofVerifyDetails> verify_details_;
+
+ // True if the server responded to a previous CHLO with a stateless
+ // reject. Used for book-keeping between the STATE_RECV_REJ,
+ // STATE_VERIFY_PROOF*, and subsequent STATE_SEND_CHLO state.
+ bool stateless_reject_received_;
+
+ QuicTime proof_verify_start_time_;
+
+ int num_scup_messages_received_;
+
+ bool encryption_established_;
+ bool handshake_confirmed_;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ crypto_negotiated_params_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_HANDSHAKER_H_
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
new file mode 100644
index 00000000000..992df8f0378
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker_test.cc
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace {
+
+using ::testing::Test;
+
+class TestProofHandler : public QuicCryptoClientStream::ProofHandler {
+ public:
+ ~TestProofHandler() override {}
+ void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) override {}
+ void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) override {}
+};
+
+class InsecureProofVerifier : public ProofVerifier {
+ public:
+ InsecureProofVerifier() {}
+ ~InsecureProofVerifier() override {}
+
+ // ProofVerifier override.
+ QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const uint16_t port,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* verify_details,
+ std::unique_ptr<ProofVerifierCallback> callback) override {
+ return QUIC_SUCCESS;
+ }
+
+ QuicAsyncStatus VerifyCertChain(
+ const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override {
+ return QUIC_SUCCESS;
+ }
+
+ std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override {
+ return nullptr;
+ }
+};
+
+class DummyProofSource : public ProofSource {
+ public:
+ DummyProofSource() {}
+ ~DummyProofSource() override {}
+
+ // ProofSource override.
+ void GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) override {
+ QuicReferenceCountedPointer<ProofSource::Chain> chain =
+ GetCertChain(server_address, hostname);
+ QuicCryptoProof proof;
+ proof.signature = "Dummy signature";
+ proof.leaf_cert_scts = "Dummy timestamp";
+ callback->Run(true, chain, proof, nullptr /* details */);
+ }
+
+ QuicReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname) override {
+ std::vector<std::string> certs;
+ certs.push_back("Dummy cert");
+ return QuicReferenceCountedPointer<ProofSource::Chain>(
+ new ProofSource::Chain(certs));
+ }
+
+ void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) override {
+ callback->Run(true, "Dummy signature");
+ }
+};
+
+class Handshaker : public QuicCryptoClientHandshaker {
+ public:
+ Handshaker(const QuicServerId& server_id,
+ QuicCryptoClientStream* stream,
+ QuicSession* session,
+ std::unique_ptr<ProofVerifyContext> verify_context,
+ QuicCryptoClientConfig* crypto_config,
+ QuicCryptoClientStream::ProofHandler* proof_handler)
+ : QuicCryptoClientHandshaker(server_id,
+ stream,
+ session,
+ std::move(verify_context),
+ crypto_config,
+ proof_handler) {}
+
+ void DoSendCHLOTest(QuicCryptoClientConfig::CachedState* cached) {
+ QuicCryptoClientHandshaker::DoSendCHLO(cached);
+ }
+};
+
+class QuicCryptoClientHandshakerTest : public Test {
+ protected:
+ QuicCryptoClientHandshakerTest()
+ : proof_handler_(),
+ helper_(),
+ alarm_factory_(),
+ server_id_("host", 123),
+ connection_(new test::MockQuicConnection(&helper_,
+ &alarm_factory_,
+ Perspective::IS_CLIENT)),
+ session_(connection_, false),
+ crypto_client_config_(QuicMakeUnique<InsecureProofVerifier>(),
+ quic::TlsClientHandshaker::CreateSslCtx()),
+ client_stream_(new QuicCryptoClientStream(server_id_,
+ &session_,
+ nullptr,
+ &crypto_client_config_,
+ &proof_handler_)),
+ handshaker_(server_id_,
+ client_stream_,
+ &session_,
+ nullptr,
+ &crypto_client_config_,
+ &proof_handler_),
+ state_() {
+ // Session takes the ownership of the client stream! (but handshaker also
+ // takes a reference to it, but doesn't take the ownership).
+ session_.SetCryptoStream(client_stream_);
+ session_.Initialize();
+ }
+
+ void InitializeServerParametersToEnableFullHello() {
+ QuicCryptoServerConfig::ConfigOptions options;
+ QuicServerConfigProtobuf config = QuicCryptoServerConfig::GenerateConfig(
+ helper_.GetRandomGenerator(), helper_.GetClock(), options);
+ state_.Initialize(
+ config.config(), "sourcetoken", std::vector<std::string>{"Dummy cert"},
+ "", "chlo_hash", "signature", helper_.GetClock()->WallNow(),
+ helper_.GetClock()->WallNow().Add(QuicTime::Delta::FromSeconds(30)));
+
+ state_.SetProofValid();
+ }
+
+ TestProofHandler proof_handler_;
+ test::MockQuicConnectionHelper helper_;
+ test::MockAlarmFactory alarm_factory_;
+ QuicServerId server_id_;
+ // Session takes the ownership of the connection.
+ test::MockQuicConnection* connection_;
+ test::MockQuicSession session_;
+ QuicCryptoClientConfig crypto_client_config_;
+ QuicCryptoClientStream* client_stream_;
+ Handshaker handshaker_;
+ QuicCryptoClientConfig::CachedState state_;
+};
+
+TEST_F(QuicCryptoClientHandshakerTest, TestSendFullPaddingInInchoateHello) {
+ handshaker_.DoSendCHLOTest(&state_);
+
+ EXPECT_TRUE(connection_->fully_pad_during_crypto_handshake());
+}
+
+TEST_F(QuicCryptoClientHandshakerTest, TestDisabledPaddingInInchoateHello) {
+ crypto_client_config_.set_pad_inchoate_hello(false);
+ handshaker_.DoSendCHLOTest(&state_);
+ EXPECT_FALSE(connection_->fully_pad_during_crypto_handshake());
+}
+
+TEST_F(QuicCryptoClientHandshakerTest,
+ TestPaddingInFullHelloEvenIfInchoateDisabled) {
+ // Disable inchoate, but full hello should still be padded.
+ crypto_client_config_.set_pad_inchoate_hello(false);
+
+ InitializeServerParametersToEnableFullHello();
+
+ handshaker_.DoSendCHLOTest(&state_);
+ EXPECT_TRUE(connection_->fully_pad_during_crypto_handshake());
+}
+
+TEST_F(QuicCryptoClientHandshakerTest, TestNoPaddingInFullHelloWhenDisabled) {
+ crypto_client_config_.set_pad_full_hello(false);
+
+ InitializeServerParametersToEnableFullHello();
+
+ handshaker_.DoSendCHLOTest(&state_);
+ EXPECT_FALSE(connection_->fully_pad_during_crypto_handshake());
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc
new file mode 100644
index 00000000000..7c2aa404989
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+const int QuicCryptoClientStream::kMaxClientHellos;
+
+QuicCryptoClientStreamBase::QuicCryptoClientStreamBase(QuicSession* session)
+ : QuicCryptoStream(session) {}
+
+QuicCryptoClientStream::QuicCryptoClientStream(
+ const QuicServerId& server_id,
+ QuicSession* session,
+ std::unique_ptr<ProofVerifyContext> verify_context,
+ QuicCryptoClientConfig* crypto_config,
+ ProofHandler* proof_handler)
+ : QuicCryptoClientStreamBase(session) {
+ DCHECK_EQ(Perspective::IS_CLIENT, session->connection()->perspective());
+ switch (session->connection()->version().handshake_protocol) {
+ case PROTOCOL_QUIC_CRYPTO:
+ handshaker_ = QuicMakeUnique<QuicCryptoClientHandshaker>(
+ server_id, this, session, std::move(verify_context), crypto_config,
+ proof_handler);
+ break;
+ case PROTOCOL_TLS1_3:
+ handshaker_ = QuicMakeUnique<TlsClientHandshaker>(
+ this, session, server_id, crypto_config->proof_verifier(),
+ crypto_config->ssl_ctx(), std::move(verify_context),
+ crypto_config->user_agent_id());
+ break;
+ case PROTOCOL_UNSUPPORTED:
+ QUIC_BUG << "Attempting to create QuicCryptoClientStream for unknown "
+ "handshake protocol";
+ }
+}
+
+QuicCryptoClientStream::~QuicCryptoClientStream() {}
+
+bool QuicCryptoClientStream::CryptoConnect() {
+ return handshaker_->CryptoConnect();
+}
+
+int QuicCryptoClientStream::num_sent_client_hellos() const {
+ return handshaker_->num_sent_client_hellos();
+}
+
+int QuicCryptoClientStream::num_scup_messages_received() const {
+ return handshaker_->num_scup_messages_received();
+}
+
+bool QuicCryptoClientStream::encryption_established() const {
+ return handshaker_->encryption_established();
+}
+
+bool QuicCryptoClientStream::handshake_confirmed() const {
+ return handshaker_->handshake_confirmed();
+}
+
+const QuicCryptoNegotiatedParameters&
+QuicCryptoClientStream::crypto_negotiated_params() const {
+ return handshaker_->crypto_negotiated_params();
+}
+
+CryptoMessageParser* QuicCryptoClientStream::crypto_message_parser() {
+ return handshaker_->crypto_message_parser();
+}
+
+bool QuicCryptoClientStream::WasChannelIDSent() const {
+ return handshaker_->WasChannelIDSent();
+}
+
+bool QuicCryptoClientStream::WasChannelIDSourceCallbackRun() const {
+ return handshaker_->WasChannelIDSourceCallbackRun();
+}
+
+std::string QuicCryptoClientStream::chlo_hash() const {
+ return handshaker_->chlo_hash();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h
new file mode 100644
index 00000000000..d0e4ee1e3b1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h
@@ -0,0 +1,174 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_STREAM_H_
+#define QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_STREAM_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicCryptoClientStreamBase : public QuicCryptoStream {
+ public:
+ explicit QuicCryptoClientStreamBase(QuicSession* session);
+
+ ~QuicCryptoClientStreamBase() override {}
+
+ // Performs a crypto handshake with the server. Returns true if the connection
+ // is still connected.
+ virtual bool CryptoConnect() = 0;
+
+ // num_sent_client_hellos returns the number of client hello messages that
+ // have been sent. If the handshake has completed then this is one greater
+ // than the number of round-trips needed for the handshake.
+ virtual int num_sent_client_hellos() const = 0;
+
+ // The number of server config update messages received by the
+ // client. Does not count update messages that were received prior
+ // to handshake confirmation.
+ virtual int num_scup_messages_received() const = 0;
+};
+
+class QUIC_EXPORT_PRIVATE QuicCryptoClientStream
+ : public QuicCryptoClientStreamBase {
+ public:
+ // kMaxClientHellos is the maximum number of times that we'll send a client
+ // hello. The value 4 accounts for:
+ // * One failure due to an incorrect or missing source-address token.
+ // * One failure due the server's certificate chain being unavailible and
+ // the server being unwilling to send it without a valid source-address
+ // token.
+ // * One failure due to the ServerConfig private key being located on a
+ // remote oracle which has become unavailable, forcing the server to send
+ // the client a fallback ServerConfig.
+ static const int kMaxClientHellos = 4;
+
+ // QuicCryptoClientStream creates a HandshakerDelegate at construction time
+ // based on the QuicTransportVersion of the connection. Different
+ // HandshakerDelegates provide implementations of different crypto handshake
+ // protocols. Currently QUIC crypto is the only protocol implemented; a future
+ // HandshakerDelegate will use TLS as the handshake protocol.
+ // QuicCryptoClientStream delegates all of its public methods to its
+ // HandshakerDelegate.
+ //
+ // This setup of the crypto stream delegating its implementation to the
+ // handshaker results in the handshaker reading and writing bytes on the
+ // crypto stream, instead of the handshaker passing the stream bytes to send.
+ class QUIC_EXPORT_PRIVATE HandshakerDelegate {
+ public:
+ virtual ~HandshakerDelegate() {}
+
+ // Performs a crypto handshake with the server. Returns true if the
+ // connection is still connected.
+ virtual bool CryptoConnect() = 0;
+
+ // num_sent_client_hellos returns the number of client hello messages that
+ // have been sent. If the handshake has completed then this is one greater
+ // than the number of round-trips needed for the handshake.
+ virtual int num_sent_client_hellos() const = 0;
+
+ // The number of server config update messages received by the
+ // client. Does not count update messages that were received prior
+ // to handshake confirmation.
+ virtual int num_scup_messages_received() const = 0;
+
+ // Returns true if a channel ID was sent on this connection.
+ virtual bool WasChannelIDSent() const = 0;
+
+ // Returns true if our ChannelIDSourceCallback was run, which implies the
+ // ChannelIDSource operated asynchronously. Intended for testing.
+ virtual bool WasChannelIDSourceCallbackRun() const = 0;
+
+ virtual std::string chlo_hash() const = 0;
+
+ // Returns true once any encrypter (initial/0RTT or final/1RTT) has been set
+ // for the connection.
+ virtual bool encryption_established() const = 0;
+
+ // Returns true once the crypto handshake has completed.
+ virtual bool handshake_confirmed() const = 0;
+
+ // Returns the parameters negotiated in the crypto handshake.
+ virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const = 0;
+
+ // Used by QuicCryptoStream to parse data received on this stream.
+ virtual CryptoMessageParser* crypto_message_parser() = 0;
+ };
+
+ // ProofHandler is an interface that handles callbacks from the crypto
+ // stream when the client has proof verification details of the server.
+ class QUIC_EXPORT_PRIVATE ProofHandler {
+ public:
+ virtual ~ProofHandler() {}
+
+ // Called when the proof in |cached| is marked valid. If this is a secure
+ // QUIC session, then this will happen only after the proof verifier
+ // completes.
+ virtual void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) = 0;
+
+ // Called when proof verification details become available, either because
+ // proof verification is complete, or when cached details are used. This
+ // will only be called for secure QUIC connections.
+ virtual void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) = 0;
+ };
+
+ QuicCryptoClientStream(const QuicServerId& server_id,
+ QuicSession* session,
+ std::unique_ptr<ProofVerifyContext> verify_context,
+ QuicCryptoClientConfig* crypto_config,
+ ProofHandler* proof_handler);
+ QuicCryptoClientStream(const QuicCryptoClientStream&) = delete;
+ QuicCryptoClientStream& operator=(const QuicCryptoClientStream&) = delete;
+
+ ~QuicCryptoClientStream() override;
+
+ // From QuicCryptoClientStreamBase
+ bool CryptoConnect() override;
+ int num_sent_client_hellos() const override;
+
+ int num_scup_messages_received() const override;
+
+ // From QuicCryptoStream
+ bool encryption_established() const override;
+ bool handshake_confirmed() const override;
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override;
+ CryptoMessageParser* crypto_message_parser() override;
+
+ // Returns true if a channel ID was sent on this connection.
+ bool WasChannelIDSent() const;
+
+ // Returns true if our ChannelIDSourceCallback was run, which implies the
+ // ChannelIDSource operated asynchronously. Intended for testing.
+ bool WasChannelIDSourceCallbackRun() const;
+
+ std::string chlo_hash() const;
+
+ protected:
+ void set_handshaker(std::unique_ptr<HandshakerDelegate> handshaker) {
+ handshaker_ = std::move(handshaker);
+ }
+
+ private:
+ std::unique_ptr<HandshakerDelegate> handshaker_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_CLIENT_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream_test.cc
new file mode 100644
index 00000000000..5839477b8fc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_client_stream_test.cc
@@ -0,0 +1,474 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+
+using testing::_;
+
+namespace quic {
+namespace test {
+namespace {
+
+const char kServerHostname[] = "test.example.com";
+const uint16_t kServerPort = 443;
+
+class QuicCryptoClientStreamTest : public QuicTest {
+ public:
+ QuicCryptoClientStreamTest()
+ : supported_versions_(AllSupportedVersions()),
+ server_id_(kServerHostname, kServerPort, false),
+ crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx()) {
+ CreateConnection();
+ }
+
+ void CreateConnection() {
+ connection_ =
+ new PacketSavingConnection(&client_helper_, &alarm_factory_,
+ Perspective::IS_CLIENT, supported_versions_);
+ // Advance the time, because timers do not like uninitialized times.
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+
+ session_ = QuicMakeUnique<TestQuicSpdyClientSession>(
+ connection_, DefaultQuicConfig(), supported_versions_, server_id_,
+ &crypto_config_);
+ }
+
+ void CompleteCryptoHandshake() {
+ if (stream()->handshake_protocol() != PROTOCOL_TLS1_3) {
+ EXPECT_CALL(*session_, OnProofValid(testing::_));
+ }
+ EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_))
+ .Times(testing::AnyNumber());
+ stream()->CryptoConnect();
+ QuicConfig config;
+ crypto_test_utils::HandshakeWithFakeServer(&config, &server_helper_,
+ &alarm_factory_, connection_,
+ stream(), server_options_);
+ }
+
+ QuicCryptoClientStream* stream() {
+ return session_->GetMutableCryptoStream();
+ }
+
+ MockQuicConnectionHelper server_helper_;
+ MockQuicConnectionHelper client_helper_;
+ MockAlarmFactory alarm_factory_;
+ PacketSavingConnection* connection_;
+ ParsedQuicVersionVector supported_versions_;
+ std::unique_ptr<TestQuicSpdyClientSession> session_;
+ QuicServerId server_id_;
+ CryptoHandshakeMessage message_;
+ QuicCryptoClientConfig crypto_config_;
+ crypto_test_utils::FakeServerOptions server_options_;
+};
+
+TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) {
+ EXPECT_FALSE(stream()->encryption_established());
+ EXPECT_FALSE(stream()->handshake_confirmed());
+}
+
+TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) {
+ CompleteCryptoHandshake();
+ EXPECT_TRUE(stream()->encryption_established());
+ EXPECT_TRUE(stream()->handshake_confirmed());
+}
+
+TEST_F(QuicCryptoClientStreamTest, ConnectedAfterTlsHandshake) {
+ FLAGS_quic_supports_tls_handshake = true;
+ supported_versions_.clear();
+ for (QuicTransportVersion transport_version :
+ AllSupportedTransportVersions()) {
+ supported_versions_.push_back(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, transport_version));
+ }
+ CreateConnection();
+ CompleteCryptoHandshake();
+ EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol());
+ EXPECT_TRUE(stream()->encryption_established());
+ EXPECT_TRUE(stream()->handshake_confirmed());
+}
+
+TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) {
+ CompleteCryptoHandshake();
+
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, _, _));
+ message_.set_tag(kCHLO);
+ crypto_test_utils::SendHandshakeMessageToStream(stream(), message_,
+ Perspective::IS_CLIENT);
+}
+
+TEST_F(QuicCryptoClientStreamTest, BadMessageType) {
+ stream()->CryptoConnect();
+
+ message_.set_tag(kCHLO);
+
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
+ "Expected REJ", _));
+ crypto_test_utils::SendHandshakeMessageToStream(stream(), message_,
+ Perspective::IS_CLIENT);
+}
+
+TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) {
+ CompleteCryptoHandshake();
+
+ const QuicConfig* config = session_->config();
+ EXPECT_EQ(kMaximumIdleTimeoutSecs, config->IdleNetworkTimeout().ToSeconds());
+
+ const QuicCryptoNegotiatedParameters& crypto_params(
+ stream()->crypto_negotiated_params());
+ EXPECT_EQ(crypto_config_.aead[0], crypto_params.aead);
+ EXPECT_EQ(crypto_config_.kexs[0], crypto_params.key_exchange);
+}
+
+TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) {
+ // Seed the config with a cached server config.
+ CompleteCryptoHandshake();
+
+ // Recreate connection with the new config.
+ CreateConnection();
+
+ // Advance time 5 years to ensure that we pass the expiry time of the cached
+ // server config.
+ connection_->AdvanceTime(
+ QuicTime::Delta::FromSeconds(60 * 60 * 24 * 365 * 5));
+
+ EXPECT_CALL(*session_, OnProofValid(testing::_));
+ stream()->CryptoConnect();
+ // Check that a client hello was sent.
+ ASSERT_EQ(1u, connection_->encrypted_packets_.size());
+ EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+}
+
+TEST_F(QuicCryptoClientStreamTest, ClockSkew) {
+ // Test that if the client's clock is skewed with respect to the server,
+ // the handshake succeeds. In the past, the client would get the server
+ // config, notice that it had already expired and then close the connection.
+
+ // Advance time 5 years to ensure that we pass the expiry time in the server
+ // config, but the TTL is used instead.
+ connection_->AdvanceTime(
+ QuicTime::Delta::FromSeconds(60 * 60 * 24 * 365 * 5));
+
+ // The handshakes completes!
+ CompleteCryptoHandshake();
+}
+
+TEST_F(QuicCryptoClientStreamTest, InvalidCachedServerConfig) {
+ // Seed the config with a cached server config.
+ CompleteCryptoHandshake();
+
+ // Recreate connection with the new config.
+ CreateConnection();
+
+ QuicCryptoClientConfig::CachedState* state =
+ crypto_config_.LookupOrCreate(server_id_);
+
+ std::vector<std::string> certs = state->certs();
+ std::string cert_sct = state->cert_sct();
+ std::string signature = state->signature();
+ std::string chlo_hash = state->chlo_hash();
+ state->SetProof(certs, cert_sct, chlo_hash, signature + signature);
+
+ EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_))
+ .Times(testing::AnyNumber());
+ stream()->CryptoConnect();
+ // Check that a client hello was sent.
+ ASSERT_EQ(1u, connection_->encrypted_packets_.size());
+}
+
+TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdate) {
+ // Test that the crypto client stream can receive server config updates after
+ // the connection has been established.
+ CompleteCryptoHandshake();
+
+ QuicCryptoClientConfig::CachedState* state =
+ crypto_config_.LookupOrCreate(server_id_);
+
+ // Ensure cached STK is different to what we send in the handshake.
+ EXPECT_NE("xstk", state->source_address_token());
+
+ // Initialize using {...} syntax to avoid trailing \0 if converting from
+ // string.
+ unsigned char stk[] = {'x', 's', 't', 'k'};
+
+ // Minimum SCFG that passes config validation checks.
+ unsigned char scfg[] = {// SCFG
+ 0x53, 0x43, 0x46, 0x47,
+ // num entries
+ 0x01, 0x00,
+ // padding
+ 0x00, 0x00,
+ // EXPY
+ 0x45, 0x58, 0x50, 0x59,
+ // EXPY end offset
+ 0x08, 0x00, 0x00, 0x00,
+ // Value
+ '1', '2', '3', '4', '5', '6', '7', '8'};
+
+ CryptoHandshakeMessage server_config_update;
+ server_config_update.set_tag(kSCUP);
+ server_config_update.SetValue(kSourceAddressTokenTag, stk);
+ server_config_update.SetValue(kSCFG, scfg);
+ const uint64_t expiry_seconds = 60 * 60 * 24 * 2;
+ server_config_update.SetValue(kSTTL, expiry_seconds);
+
+ crypto_test_utils::SendHandshakeMessageToStream(
+ stream(), server_config_update, Perspective::IS_SERVER);
+
+ // Make sure that the STK and SCFG are cached correctly.
+ EXPECT_EQ("xstk", state->source_address_token());
+
+ const std::string& cached_scfg = state->server_config();
+ test::CompareCharArraysWithHexError(
+ "scfg", cached_scfg.data(), cached_scfg.length(),
+ reinterpret_cast<char*>(scfg), QUIC_ARRAYSIZE(scfg));
+
+ QuicStreamSequencer* sequencer = QuicStreamPeer::sequencer(stream());
+ EXPECT_FALSE(QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(sequencer));
+}
+
+TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdateWithCert) {
+ // Test that the crypto client stream can receive and use server config
+ // updates with certificates after the connection has been established.
+ CompleteCryptoHandshake();
+
+ // Build a server config update message with certificates
+ QuicCryptoServerConfig crypto_config(
+ QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(), KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ crypto_test_utils::FakeServerOptions options;
+ crypto_test_utils::SetupCryptoServerConfigForTest(
+ connection_->clock(), QuicRandom::GetInstance(), &crypto_config, options);
+ SourceAddressTokens tokens;
+ QuicCompressedCertsCache cache(1);
+ CachedNetworkParameters network_params;
+ CryptoHandshakeMessage server_config_update;
+
+ class Callback : public BuildServerConfigUpdateMessageResultCallback {
+ public:
+ Callback(bool* ok, CryptoHandshakeMessage* message)
+ : ok_(ok), message_(message) {}
+ void Run(bool ok, const CryptoHandshakeMessage& message) override {
+ *ok_ = ok;
+ *message_ = message;
+ }
+
+ private:
+ bool* ok_;
+ CryptoHandshakeMessage* message_;
+ };
+
+ // Note: relies on the callback being invoked synchronously
+ bool ok = false;
+ crypto_config.BuildServerConfigUpdateMessage(
+ session_->connection()->transport_version(), stream()->chlo_hash(),
+ tokens, QuicSocketAddress(QuicIpAddress::Loopback6(), 1234),
+ QuicIpAddress::Loopback6(), connection_->clock(),
+ QuicRandom::GetInstance(), &cache, stream()->crypto_negotiated_params(),
+ &network_params,
+ std::unique_ptr<BuildServerConfigUpdateMessageResultCallback>(
+ new Callback(&ok, &server_config_update)));
+ EXPECT_TRUE(ok);
+
+ EXPECT_CALL(*session_, OnProofValid(testing::_));
+ crypto_test_utils::SendHandshakeMessageToStream(
+ stream(), server_config_update, Perspective::IS_SERVER);
+
+ // Recreate connection with the new config and verify a 0-RTT attempt.
+ CreateConnection();
+
+ EXPECT_CALL(*connection_, OnCanWrite());
+ EXPECT_CALL(*session_, OnProofValid(testing::_));
+ EXPECT_CALL(*session_, OnProofVerifyDetailsAvailable(testing::_))
+ .Times(testing::AnyNumber());
+ stream()->CryptoConnect();
+ EXPECT_TRUE(session_->IsEncryptionEstablished());
+}
+
+TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdateBeforeHandshake) {
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE, _, _));
+ CryptoHandshakeMessage server_config_update;
+ server_config_update.set_tag(kSCUP);
+ crypto_test_utils::SendHandshakeMessageToStream(
+ stream(), server_config_update, Perspective::IS_SERVER);
+}
+
+TEST_F(QuicCryptoClientStreamTest, NoChannelID) {
+ crypto_config_.SetChannelIDSource(nullptr);
+
+ CompleteCryptoHandshake();
+ EXPECT_FALSE(stream()->WasChannelIDSent());
+ EXPECT_FALSE(stream()->WasChannelIDSourceCallbackRun());
+}
+
+TEST_F(QuicCryptoClientStreamTest, PreferredVersion) {
+ // This mimics the case where client receives version negotiation packet, such
+ // that, the preferred version is different from the packets' version.
+ connection_ = new PacketSavingConnection(
+ &client_helper_, &alarm_factory_, Perspective::IS_CLIENT,
+ ParsedVersionOfIndex(supported_versions_, 1));
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+
+ session_ = QuicMakeUnique<TestQuicSpdyClientSession>(
+ connection_, DefaultQuicConfig(), supported_versions_, server_id_,
+ &crypto_config_);
+ CompleteCryptoHandshake();
+ // 2 CHLOs are sent.
+ ASSERT_EQ(2u, session_->sent_crypto_handshake_messages().size());
+ // Verify preferred version is the highest version that session supports, and
+ // is different from connection's version.
+ QuicVersionLabel client_version_label;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ session_->sent_crypto_handshake_messages()[0].GetVersionLabel(
+ kVER, &client_version_label));
+ EXPECT_EQ(CreateQuicVersionLabel(supported_versions_[0]),
+ client_version_label);
+ EXPECT_EQ(QUIC_NO_ERROR,
+ session_->sent_crypto_handshake_messages()[1].GetVersionLabel(
+ kVER, &client_version_label));
+ EXPECT_EQ(CreateQuicVersionLabel(supported_versions_[0]),
+ client_version_label);
+ EXPECT_NE(CreateQuicVersionLabel(connection_->version()),
+ client_version_label);
+}
+
+class QuicCryptoClientStreamStatelessTest : public QuicTest {
+ public:
+ QuicCryptoClientStreamStatelessTest()
+ : client_crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx()),
+ server_crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ server_compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ server_id_(kServerHostname, kServerPort, false) {
+ TestQuicSpdyClientSession* client_session = nullptr;
+ CreateClientSessionForTest(server_id_,
+ /* supports_stateless_rejects= */ true,
+ QuicTime::Delta::FromSeconds(100000),
+ AllSupportedVersions(), &helper_,
+ &alarm_factory_, &client_crypto_config_,
+ &client_connection_, &client_session);
+ CHECK(client_session);
+ client_session_.reset(client_session);
+ }
+
+ QuicCryptoServerStream* server_stream() {
+ return server_session_->GetMutableCryptoStream();
+ }
+
+ void AdvanceHandshakeWithFakeServer() {
+ client_session_->GetMutableCryptoStream()->CryptoConnect();
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_session_->helper(), GenerateConnectionIdForReject(_, _))
+ .Times(testing::AnyNumber());
+ crypto_test_utils::AdvanceHandshake(
+ client_connection_, client_session_->GetMutableCryptoStream(), 0,
+ server_connection_, server_stream(), 0);
+ }
+
+ // Initializes the server_stream_ for stateless rejects.
+ void InitializeFakeStatelessRejectServer() {
+ TestQuicSpdyServerSession* server_session = nullptr;
+ CreateServerSessionForTest(
+ server_id_, QuicTime::Delta::FromSeconds(100000),
+ ParsedVersionOfIndex(AllSupportedVersions(), 0), &helper_,
+ &alarm_factory_, &server_crypto_config_,
+ &server_compressed_certs_cache_, &server_connection_, &server_session);
+ CHECK(server_session);
+ server_session_.reset(server_session);
+ server_session_->OnSuccessfulVersionNegotiation(AllSupportedVersions()[0]);
+ crypto_test_utils::FakeServerOptions options;
+ crypto_test_utils::SetupCryptoServerConfigForTest(
+ server_connection_->clock(), server_connection_->random_generator(),
+ &server_crypto_config_, options);
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+
+ // Client crypto stream state
+ PacketSavingConnection* client_connection_;
+ std::unique_ptr<TestQuicSpdyClientSession> client_session_;
+ QuicCryptoClientConfig client_crypto_config_;
+
+ // Server crypto stream state
+ PacketSavingConnection* server_connection_;
+ std::unique_ptr<TestQuicSpdyServerSession> server_session_;
+ QuicCryptoServerConfig server_crypto_config_;
+ QuicCompressedCertsCache server_compressed_certs_cache_;
+ QuicServerId server_id_;
+};
+
+TEST_F(QuicCryptoClientStreamStatelessTest, StatelessReject) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+
+ QuicCryptoClientConfig::CachedState* client_state =
+ client_crypto_config_.LookupOrCreate(server_id_);
+
+ EXPECT_FALSE(client_state->has_server_designated_connection_id());
+ EXPECT_CALL(*client_session_, OnProofValid(testing::_));
+
+ InitializeFakeStatelessRejectServer();
+ EXPECT_CALL(*client_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ AdvanceHandshakeWithFakeServer();
+
+ EXPECT_EQ(1, server_stream()->NumHandshakeMessages());
+ EXPECT_EQ(0, server_stream()->NumHandshakeMessagesWithServerNonces());
+
+ EXPECT_FALSE(client_session_->IsEncryptionEstablished());
+ EXPECT_FALSE(client_session_->IsCryptoHandshakeConfirmed());
+ // Even though the handshake was not complete, the cached client_state is
+ // complete, and can be used for a subsequent successful handshake.
+ EXPECT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+
+ ASSERT_TRUE(client_state->has_server_nonce());
+ ASSERT_FALSE(client_state->GetNextServerNonce().empty());
+ ASSERT_TRUE(client_state->has_server_designated_connection_id());
+ QuicConnectionId server_designated_id =
+ client_state->GetNextServerDesignatedConnectionId();
+ QuicConnectionId expected_id = QuicUtils::CreateRandomConnectionId(
+ server_session_->connection()->random_generator());
+ EXPECT_EQ(expected_id, server_designated_id);
+ EXPECT_FALSE(client_state->has_server_designated_connection_id());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..fa0f78a51c9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+
+namespace quic {
+
+#define ENDPOINT \
+ (session()->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
+
+QuicCryptoHandshaker::QuicCryptoHandshaker(QuicCryptoStream* stream,
+ QuicSession* session)
+ : stream_(stream), session_(session), last_sent_handshake_message_tag_(0) {
+ crypto_framer_.set_visitor(this);
+}
+
+QuicCryptoHandshaker::~QuicCryptoHandshaker() {}
+
+void QuicCryptoHandshaker::SendHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ QUIC_DVLOG(1) << ENDPOINT << "Sending " << message.DebugString();
+ session()->NeuterUnencryptedData();
+ session()->OnCryptoHandshakeMessageSent(message);
+ last_sent_handshake_message_tag_ = message.tag();
+ const QuicData& data = message.GetSerialized();
+ stream_->WriteCryptoData(session_->connection()->encryption_level(),
+ data.AsStringPiece());
+}
+
+void QuicCryptoHandshaker::OnError(CryptoFramer* framer) {
+ QUIC_DLOG(WARNING) << "Error processing crypto data: "
+ << QuicErrorCodeToString(framer->error());
+}
+
+void QuicCryptoHandshaker::OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ QUIC_DVLOG(1) << ENDPOINT << "Received " << message.DebugString();
+ session()->OnCryptoHandshakeMessageReceived(message);
+}
+
+CryptoMessageParser* QuicCryptoHandshaker::crypto_message_parser() {
+ return &crypto_framer_;
+}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h
new file mode 100644
index 00000000000..231acfcdb30
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CRYPTO_HANDSHAKER_H_
+#define QUICHE_QUIC_CORE_QUIC_CRYPTO_HANDSHAKER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicCryptoHandshaker
+ : public CryptoFramerVisitorInterface {
+ public:
+ QuicCryptoHandshaker(QuicCryptoStream* stream, QuicSession* session);
+ QuicCryptoHandshaker(const QuicCryptoHandshaker&) = delete;
+ QuicCryptoHandshaker& operator=(const QuicCryptoHandshaker&) = delete;
+
+ ~QuicCryptoHandshaker() override;
+
+ // Sends |message| to the peer.
+ // TODO(wtc): return a success/failure status.
+ void SendHandshakeMessage(const CryptoHandshakeMessage& message);
+
+ void OnError(CryptoFramer* framer) override;
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
+
+ CryptoMessageParser* crypto_message_parser();
+
+ protected:
+ QuicTag last_sent_handshake_message_tag() const {
+ return last_sent_handshake_message_tag_;
+ }
+
+ private:
+ QuicSession* session() { return session_; }
+
+ QuicCryptoStream* stream_;
+ QuicSession* session_;
+
+ CryptoFramer crypto_framer_;
+
+ // Records last sent crypto handshake message tag.
+ QuicTag last_sent_handshake_message_tag_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_HANDSHAKER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.cc
new file mode 100644
index 00000000000..c0e61efb21f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.cc
@@ -0,0 +1,485 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h"
+
+#include <memory>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+class QuicCryptoServerHandshaker::ProcessClientHelloCallback
+ : public ProcessClientHelloResultCallback {
+ public:
+ ProcessClientHelloCallback(
+ QuicCryptoServerHandshaker* parent,
+ const QuicReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>& result)
+ : parent_(parent), result_(result) {}
+
+ void Run(
+ QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) override {
+ if (parent_ == nullptr) {
+ return;
+ }
+
+ parent_->FinishProcessingHandshakeMessageAfterProcessClientHello(
+ *result_, error, error_details, std::move(message),
+ std::move(diversification_nonce), std::move(proof_source_details));
+ }
+
+ void Cancel() { parent_ = nullptr; }
+
+ private:
+ QuicCryptoServerHandshaker* parent_;
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result_;
+};
+
+QuicCryptoServerHandshaker::QuicCryptoServerHandshaker(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCryptoServerStream* stream,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSession* session,
+ QuicCryptoServerStream::Helper* helper)
+ : QuicCryptoHandshaker(stream, session),
+ stream_(stream),
+ session_(session),
+ crypto_config_(crypto_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ signed_config_(new QuicSignedServerConfig),
+ helper_(helper),
+ num_handshake_messages_(0),
+ num_handshake_messages_with_server_nonces_(0),
+ send_server_config_update_cb_(nullptr),
+ num_server_config_update_messages_sent_(0),
+ zero_rtt_attempted_(false),
+ chlo_packet_size_(0),
+ validate_client_hello_cb_(nullptr),
+ process_client_hello_cb_(nullptr),
+ encryption_established_(false),
+ handshake_confirmed_(false),
+ crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {}
+
+QuicCryptoServerHandshaker::~QuicCryptoServerHandshaker() {
+ CancelOutstandingCallbacks();
+}
+
+void QuicCryptoServerHandshaker::CancelOutstandingCallbacks() {
+ // Detach from the validation callback. Calling this multiple times is safe.
+ if (validate_client_hello_cb_ != nullptr) {
+ validate_client_hello_cb_->Cancel();
+ validate_client_hello_cb_ = nullptr;
+ }
+ if (send_server_config_update_cb_ != nullptr) {
+ send_server_config_update_cb_->Cancel();
+ send_server_config_update_cb_ = nullptr;
+ }
+ if (process_client_hello_cb_ != nullptr) {
+ process_client_hello_cb_->Cancel();
+ process_client_hello_cb_ = nullptr;
+ }
+}
+
+void QuicCryptoServerHandshaker::OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ QuicCryptoHandshaker::OnHandshakeMessage(message);
+ ++num_handshake_messages_;
+ chlo_packet_size_ = session()->connection()->GetCurrentPacket().length();
+
+ // Do not process handshake messages after the handshake is confirmed.
+ if (handshake_confirmed_) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE,
+ "Unexpected handshake message from client");
+ return;
+ }
+
+ if (message.tag() != kCHLO) {
+ stream_->CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
+ "Handshake packet not CHLO");
+ return;
+ }
+
+ if (validate_client_hello_cb_ != nullptr ||
+ process_client_hello_cb_ != nullptr) {
+ // Already processing some other handshake message. The protocol
+ // does not allow for clients to send multiple handshake messages
+ // before the server has a chance to respond.
+ stream_->CloseConnectionWithDetails(
+ QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO,
+ "Unexpected handshake message while processing CHLO");
+ return;
+ }
+
+ chlo_hash_ =
+ CryptoUtils::HashHandshakeMessage(message, Perspective::IS_SERVER);
+
+ std::unique_ptr<ValidateCallback> cb(new ValidateCallback(this));
+ DCHECK(validate_client_hello_cb_ == nullptr);
+ DCHECK(process_client_hello_cb_ == nullptr);
+ validate_client_hello_cb_ = cb.get();
+ crypto_config_->ValidateClientHello(
+ message, GetClientAddress().host(),
+ session()->connection()->self_address(), transport_version(),
+ session()->connection()->clock(), signed_config_, std::move(cb));
+}
+
+void QuicCryptoServerHandshaker::FinishProcessingHandshakeMessage(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result,
+ std::unique_ptr<ProofSource::Details> details) {
+ const CryptoHandshakeMessage& message = result->client_hello;
+
+ // Clear the callback that got us here.
+ DCHECK(validate_client_hello_cb_ != nullptr);
+ DCHECK(process_client_hello_cb_ == nullptr);
+ validate_client_hello_cb_ = nullptr;
+
+ if (stream_->UseStatelessRejectsIfPeerSupported()) {
+ stream_->SetPeerSupportsStatelessRejects(
+ QuicCryptoServerStreamBase::DoesPeerSupportStatelessRejects(message));
+ }
+
+ std::unique_ptr<ProcessClientHelloCallback> cb(
+ new ProcessClientHelloCallback(this, result));
+ process_client_hello_cb_ = cb.get();
+ ProcessClientHello(result, std::move(details), std::move(cb));
+}
+
+void QuicCryptoServerHandshaker::
+ FinishProcessingHandshakeMessageAfterProcessClientHello(
+ const ValidateClientHelloResultCallback::Result& result,
+ QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> reply,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) {
+ // Clear the callback that got us here.
+ DCHECK(process_client_hello_cb_ != nullptr);
+ DCHECK(validate_client_hello_cb_ == nullptr);
+ process_client_hello_cb_ = nullptr;
+
+ const CryptoHandshakeMessage& message = result.client_hello;
+ if (error != QUIC_NO_ERROR) {
+ stream_->CloseConnectionWithDetails(error, error_details);
+ return;
+ }
+
+ if (reply->tag() != kSHLO) {
+ if (reply->tag() == kSREJ) {
+ DCHECK(stream_->UseStatelessRejectsIfPeerSupported());
+ DCHECK(stream_->PeerSupportsStatelessRejects());
+ // Before sending the SREJ, cause the connection to save crypto packets
+ // so that they can be added to the time wait list manager and
+ // retransmitted.
+ session()->connection()->EnableSavingCryptoPackets();
+ }
+ session()->connection()->set_fully_pad_crypto_hadshake_packets(
+ crypto_config_->pad_rej());
+ SendHandshakeMessage(*reply);
+
+ if (reply->tag() == kSREJ) {
+ DCHECK(stream_->UseStatelessRejectsIfPeerSupported());
+ DCHECK(stream_->PeerSupportsStatelessRejects());
+ DCHECK(!handshake_confirmed());
+ QUIC_DLOG(INFO) << "Closing connection "
+ << session()->connection()->connection_id()
+ << " because of a stateless reject.";
+ session()->connection()->CloseConnection(
+ QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ }
+ return;
+ }
+
+ // If we are returning a SHLO then we accepted the handshake. Now
+ // process the negotiated configuration options as part of the
+ // session config.
+ QuicConfig* config = session()->config();
+ OverrideQuicConfigDefaults(config);
+ std::string process_error_details;
+ const QuicErrorCode process_error =
+ config->ProcessPeerHello(message, CLIENT, &process_error_details);
+ if (process_error != QUIC_NO_ERROR) {
+ stream_->CloseConnectionWithDetails(process_error, process_error_details);
+ return;
+ }
+
+ session()->OnConfigNegotiated();
+
+ config->ToHandshakeMessage(reply.get());
+
+ // Receiving a full CHLO implies the client is prepared to decrypt with
+ // the new server write key. We can start to encrypt with the new server
+ // write key.
+ //
+ // NOTE: the SHLO will be encrypted with the new server write key.
+ session()->connection()->SetEncrypter(
+ ENCRYPTION_ZERO_RTT,
+ std::move(crypto_negotiated_params_->initial_crypters.encrypter));
+ session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ // Set the decrypter immediately so that we no longer accept unencrypted
+ // packets.
+ if (session()->connection()->version().KnowsWhichDecrypterToUse()) {
+ session()->connection()->InstallDecrypter(
+ ENCRYPTION_ZERO_RTT,
+ std::move(crypto_negotiated_params_->initial_crypters.decrypter));
+ session()->connection()->RemoveDecrypter(ENCRYPTION_INITIAL);
+ } else {
+ session()->connection()->SetDecrypter(
+ ENCRYPTION_ZERO_RTT,
+ std::move(crypto_negotiated_params_->initial_crypters.decrypter));
+ }
+ session()->connection()->SetDiversificationNonce(*diversification_nonce);
+
+ session()->connection()->set_fully_pad_crypto_hadshake_packets(
+ crypto_config_->pad_shlo());
+ SendHandshakeMessage(*reply);
+
+ session()->connection()->SetEncrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ std::move(crypto_negotiated_params_->forward_secure_crypters.encrypter));
+ session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+
+ if (session()->connection()->version().KnowsWhichDecrypterToUse()) {
+ session()->connection()->InstallDecrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ std::move(
+ crypto_negotiated_params_->forward_secure_crypters.decrypter));
+ } else {
+ session()->connection()->SetAlternativeDecrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ std::move(crypto_negotiated_params_->forward_secure_crypters.decrypter),
+ false /* don't latch */);
+ }
+
+ encryption_established_ = true;
+ handshake_confirmed_ = true;
+ session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+}
+
+void QuicCryptoServerHandshaker::SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) {
+ if (!handshake_confirmed_) {
+ return;
+ }
+
+ if (send_server_config_update_cb_ != nullptr) {
+ QUIC_DVLOG(1)
+ << "Skipped server config update since one is already in progress";
+ return;
+ }
+
+ std::unique_ptr<SendServerConfigUpdateCallback> cb(
+ new SendServerConfigUpdateCallback(this));
+ send_server_config_update_cb_ = cb.get();
+
+ crypto_config_->BuildServerConfigUpdateMessage(
+ session()->connection()->transport_version(), chlo_hash_,
+ previous_source_address_tokens_, session()->connection()->self_address(),
+ GetClientAddress().host(), session()->connection()->clock(),
+ session()->connection()->random_generator(), compressed_certs_cache_,
+ *crypto_negotiated_params_, cached_network_params, std::move(cb));
+}
+
+QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::
+ SendServerConfigUpdateCallback(QuicCryptoServerHandshaker* parent)
+ : parent_(parent) {}
+
+void QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::Cancel() {
+ parent_ = nullptr;
+}
+
+// From BuildServerConfigUpdateMessageResultCallback
+void QuicCryptoServerHandshaker::SendServerConfigUpdateCallback::Run(
+ bool ok,
+ const CryptoHandshakeMessage& message) {
+ if (parent_ == nullptr) {
+ return;
+ }
+ parent_->FinishSendServerConfigUpdate(ok, message);
+}
+
+void QuicCryptoServerHandshaker::FinishSendServerConfigUpdate(
+ bool ok,
+ const CryptoHandshakeMessage& message) {
+ // Clear the callback that got us here.
+ DCHECK(send_server_config_update_cb_ != nullptr);
+ send_server_config_update_cb_ = nullptr;
+
+ if (!ok) {
+ QUIC_DVLOG(1) << "Server: Failed to build server config update (SCUP)!";
+ return;
+ }
+
+ QUIC_DVLOG(1) << "Server: Sending server config update: "
+ << message.DebugString();
+ if (!QuicVersionUsesCryptoFrames(transport_version())) {
+ const QuicData& data = message.GetSerialized();
+ stream_->WriteOrBufferData(QuicStringPiece(data.data(), data.length()),
+ false, nullptr);
+ } else {
+ SendHandshakeMessage(message);
+ }
+
+ ++num_server_config_update_messages_sent_;
+}
+
+uint8_t QuicCryptoServerHandshaker::NumHandshakeMessages() const {
+ return num_handshake_messages_;
+}
+
+uint8_t QuicCryptoServerHandshaker::NumHandshakeMessagesWithServerNonces()
+ const {
+ return num_handshake_messages_with_server_nonces_;
+}
+
+int QuicCryptoServerHandshaker::NumServerConfigUpdateMessagesSent() const {
+ return num_server_config_update_messages_sent_;
+}
+
+const CachedNetworkParameters*
+QuicCryptoServerHandshaker::PreviousCachedNetworkParams() const {
+ return previous_cached_network_params_.get();
+}
+
+bool QuicCryptoServerHandshaker::ZeroRttAttempted() const {
+ return zero_rtt_attempted_;
+}
+
+void QuicCryptoServerHandshaker::SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) {
+ previous_cached_network_params_.reset(
+ new CachedNetworkParameters(cached_network_params));
+}
+
+bool QuicCryptoServerHandshaker::ShouldSendExpectCTHeader() const {
+ return signed_config_->proof.send_expect_ct_header;
+}
+
+bool QuicCryptoServerHandshaker::GetBase64SHA256ClientChannelID(
+ std::string* output) const {
+ if (!encryption_established() ||
+ crypto_negotiated_params_->channel_id.empty()) {
+ return false;
+ }
+
+ const std::string& channel_id(crypto_negotiated_params_->channel_id);
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(channel_id.data()), channel_id.size(),
+ digest);
+
+ QuicTextUtils::Base64Encode(digest, QUIC_ARRAYSIZE(digest), output);
+ return true;
+}
+
+bool QuicCryptoServerHandshaker::encryption_established() const {
+ return encryption_established_;
+}
+
+bool QuicCryptoServerHandshaker::handshake_confirmed() const {
+ return handshake_confirmed_;
+}
+
+const QuicCryptoNegotiatedParameters&
+QuicCryptoServerHandshaker::crypto_negotiated_params() const {
+ return *crypto_negotiated_params_;
+}
+
+CryptoMessageParser* QuicCryptoServerHandshaker::crypto_message_parser() {
+ return QuicCryptoHandshaker::crypto_message_parser();
+}
+
+void QuicCryptoServerHandshaker::ProcessClientHello(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb) {
+ const CryptoHandshakeMessage& message = result->client_hello;
+ std::string error_details;
+ if (!helper_->CanAcceptClientHello(
+ message, GetClientAddress(), session()->connection()->peer_address(),
+ session()->connection()->self_address(), &error_details)) {
+ done_cb->Run(QUIC_HANDSHAKE_FAILED, error_details, nullptr, nullptr,
+ nullptr);
+ return;
+ }
+ if (!result->info.server_nonce.empty()) {
+ ++num_handshake_messages_with_server_nonces_;
+ }
+
+ if (num_handshake_messages_ == 1) {
+ // Client attempts zero RTT handshake by sending a non-inchoate CHLO.
+ QuicStringPiece public_value;
+ zero_rtt_attempted_ = message.GetStringPiece(kPUBS, &public_value);
+ }
+
+ // Store the bandwidth estimate from the client.
+ if (result->cached_network_params.bandwidth_estimate_bytes_per_second() > 0) {
+ previous_cached_network_params_.reset(
+ new CachedNetworkParameters(result->cached_network_params));
+ }
+ previous_source_address_tokens_ = result->info.source_address_tokens;
+
+ const bool use_stateless_rejects_in_crypto_config =
+ stream_->UseStatelessRejectsIfPeerSupported() &&
+ stream_->PeerSupportsStatelessRejects();
+ QuicConnection* connection = session()->connection();
+ const QuicConnectionId server_designated_connection_id =
+ GenerateConnectionIdForReject(use_stateless_rejects_in_crypto_config);
+ crypto_config_->ProcessClientHello(
+ result, /*reject_only=*/false, connection->connection_id(),
+ connection->self_address(), GetClientAddress(), connection->version(),
+ session()->supported_versions(), use_stateless_rejects_in_crypto_config,
+ server_designated_connection_id, connection->clock(),
+ connection->random_generator(), compressed_certs_cache_,
+ crypto_negotiated_params_, signed_config_,
+ QuicCryptoStream::CryptoMessageFramingOverhead(
+ transport_version(), connection->connection_id()),
+ chlo_packet_size_, std::move(done_cb));
+}
+
+void QuicCryptoServerHandshaker::OverrideQuicConfigDefaults(
+ QuicConfig* config) {}
+
+QuicCryptoServerHandshaker::ValidateCallback::ValidateCallback(
+ QuicCryptoServerHandshaker* parent)
+ : parent_(parent) {}
+
+void QuicCryptoServerHandshaker::ValidateCallback::Cancel() {
+ parent_ = nullptr;
+}
+
+void QuicCryptoServerHandshaker::ValidateCallback::Run(
+ QuicReferenceCountedPointer<Result> result,
+ std::unique_ptr<ProofSource::Details> details) {
+ if (parent_ != nullptr) {
+ parent_->FinishProcessingHandshakeMessage(std::move(result),
+ std::move(details));
+ }
+}
+
+QuicConnectionId QuicCryptoServerHandshaker::GenerateConnectionIdForReject(
+ bool use_stateless_rejects) {
+ if (!use_stateless_rejects) {
+ return EmptyQuicConnectionId();
+ }
+ return helper_->GenerateConnectionIdForReject(
+ transport_version(), session()->connection()->connection_id());
+}
+
+const QuicSocketAddress QuicCryptoServerHandshaker::GetClientAddress() {
+ return session()->connection()->peer_address();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h
new file mode 100644
index 00000000000..99207122b90
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h
@@ -0,0 +1,239 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_HANDSHAKER_H_
+#define QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_HANDSHAKER_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/proto/source_address_token.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class QuicCryptoServerStreamPeer;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE QuicCryptoServerHandshaker
+ : public QuicCryptoServerStream::HandshakerDelegate,
+ public QuicCryptoHandshaker {
+ public:
+ // |crypto_config| must outlive the stream.
+ // |session| must outlive the stream.
+ // |helper| must outlive the stream.
+ QuicCryptoServerHandshaker(const QuicCryptoServerConfig* crypto_config,
+ QuicCryptoServerStream* stream,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSession* session,
+ QuicCryptoServerStream::Helper* helper);
+ QuicCryptoServerHandshaker(const QuicCryptoServerHandshaker&) = delete;
+ QuicCryptoServerHandshaker& operator=(const QuicCryptoServerHandshaker&) =
+ delete;
+
+ ~QuicCryptoServerHandshaker() override;
+
+ // From HandshakerDelegate
+ void CancelOutstandingCallbacks() override;
+ bool GetBase64SHA256ClientChannelID(std::string* output) const override;
+ void SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) override;
+ uint8_t NumHandshakeMessages() const override;
+ uint8_t NumHandshakeMessagesWithServerNonces() const override;
+ int NumServerConfigUpdateMessagesSent() const override;
+ const CachedNetworkParameters* PreviousCachedNetworkParams() const override;
+ bool ZeroRttAttempted() const override;
+ void SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) override;
+ bool ShouldSendExpectCTHeader() const override;
+
+ // From QuicCryptoStream
+ bool encryption_established() const override;
+ bool handshake_confirmed() const override;
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override;
+ CryptoMessageParser* crypto_message_parser() override;
+
+ // From QuicCryptoHandshaker
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
+
+ protected:
+ virtual void ProcessClientHello(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result,
+ std::unique_ptr<ProofSource::Details> proof_source_details,
+ std::unique_ptr<ProcessClientHelloResultCallback> done_cb);
+
+ // Hook that allows the server to set QuicConfig defaults just
+ // before going through the parameter negotiation step.
+ virtual void OverrideQuicConfigDefaults(QuicConfig* config);
+
+ // Returns client address used to generate and validate source address token.
+ virtual const QuicSocketAddress GetClientAddress();
+
+ // Returns the QuicSession that this stream belongs to.
+ QuicSession* session() const { return session_; }
+
+ void set_encryption_established(bool encryption_established) {
+ encryption_established_ = encryption_established;
+ }
+
+ void set_handshake_confirmed(bool handshake_confirmed) {
+ handshake_confirmed_ = handshake_confirmed;
+ }
+
+ private:
+ friend class test::QuicCryptoServerStreamPeer;
+
+ class ValidateCallback : public ValidateClientHelloResultCallback {
+ public:
+ explicit ValidateCallback(QuicCryptoServerHandshaker* parent);
+ ValidateCallback(const ValidateCallback&) = delete;
+ ValidateCallback& operator=(const ValidateCallback&) = delete;
+ // To allow the parent to detach itself from the callback before deletion.
+ void Cancel();
+
+ // From ValidateClientHelloResultCallback
+ void Run(QuicReferenceCountedPointer<Result> result,
+ std::unique_ptr<ProofSource::Details> details) override;
+
+ private:
+ QuicCryptoServerHandshaker* parent_;
+ };
+
+ class SendServerConfigUpdateCallback
+ : public BuildServerConfigUpdateMessageResultCallback {
+ public:
+ explicit SendServerConfigUpdateCallback(QuicCryptoServerHandshaker* parent);
+ SendServerConfigUpdateCallback(const SendServerConfigUpdateCallback&) =
+ delete;
+ void operator=(const SendServerConfigUpdateCallback&) = delete;
+
+ // To allow the parent to detach itself from the callback before deletion.
+ void Cancel();
+
+ // From BuildServerConfigUpdateMessageResultCallback
+ void Run(bool ok, const CryptoHandshakeMessage& message) override;
+
+ private:
+ QuicCryptoServerHandshaker* parent_;
+ };
+
+ // Invoked by ValidateCallback::RunImpl once initial validation of
+ // the client hello is complete. Finishes processing of the client
+ // hello message and handles handshake success/failure.
+ void FinishProcessingHandshakeMessage(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result,
+ std::unique_ptr<ProofSource::Details> details);
+
+ class ProcessClientHelloCallback;
+ friend class ProcessClientHelloCallback;
+
+ // Portion of FinishProcessingHandshakeMessage which executes after
+ // ProcessClientHello has been called.
+ void FinishProcessingHandshakeMessageAfterProcessClientHello(
+ const ValidateClientHelloResultCallback::Result& result,
+ QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> reply,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details);
+
+ // Invoked by SendServerConfigUpdateCallback::RunImpl once the proof has been
+ // received. |ok| indicates whether or not the proof was successfully
+ // acquired, and |message| holds the partially-constructed message from
+ // SendServerConfigUpdate.
+ void FinishSendServerConfigUpdate(bool ok,
+ const CryptoHandshakeMessage& message);
+
+ // Returns a new ConnectionId to be used for statelessly rejected connections
+ // if |use_stateless_rejects| is true. Returns 0 otherwise.
+ QuicConnectionId GenerateConnectionIdForReject(bool use_stateless_rejects);
+
+ // Returns the QuicTransportVersion of the connection.
+ QuicTransportVersion transport_version() const {
+ return session_->connection()->transport_version();
+ }
+
+ QuicCryptoServerStream* stream_;
+
+ QuicSession* session_;
+
+ // crypto_config_ contains crypto parameters for the handshake.
+ const QuicCryptoServerConfig* crypto_config_;
+
+ // compressed_certs_cache_ contains a set of most recently compressed certs.
+ // Owned by QuicDispatcher.
+ QuicCompressedCertsCache* compressed_certs_cache_;
+
+ // Server's certificate chain and signature of the server config, as provided
+ // by ProofSource::GetProof.
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+
+ // Hash of the last received CHLO message which can be used for generating
+ // server config update messages.
+ std::string chlo_hash_;
+
+ // Pointer to the helper for this crypto stream. Must outlive this stream.
+ QuicCryptoServerStream::Helper* helper_;
+
+ // Number of handshake messages received by this stream.
+ uint8_t num_handshake_messages_;
+
+ // Number of handshake messages received by this stream that contain
+ // server nonces (indicating that this is a non-zero-RTT handshake
+ // attempt).
+ uint8_t num_handshake_messages_with_server_nonces_;
+
+ // Pointer to the active callback that will receive the result of
+ // BuildServerConfigUpdateMessage and forward it to
+ // FinishSendServerConfigUpdate. nullptr if no update message is currently
+ // being built.
+ SendServerConfigUpdateCallback* send_server_config_update_cb_;
+
+ // Number of server config update (SCUP) messages sent by this stream.
+ int num_server_config_update_messages_sent_;
+
+ // If the client provides CachedNetworkParameters in the STK in the CHLO, then
+ // store here, and send back in future STKs if we have no better bandwidth
+ // estimate to send.
+ std::unique_ptr<CachedNetworkParameters> previous_cached_network_params_;
+
+ // Contains any source address tokens which were present in the CHLO.
+ SourceAddressTokens previous_source_address_tokens_;
+
+ // True if client attempts 0-rtt handshake (which can succeed or fail). If
+ // stateless rejects are used, this variable will be false for the stateless
+ // rejected connection and true for subsequent connections.
+ bool zero_rtt_attempted_;
+
+ // Size of the packet containing the most recently received CHLO.
+ QuicByteCount chlo_packet_size_;
+
+ // Pointer to the active callback that will receive the result of the client
+ // hello validation request and forward it to FinishProcessingHandshakeMessage
+ // for processing. nullptr if no handshake message is being validated. Note
+ // that this field is mutually exclusive with process_client_hello_cb_.
+ ValidateCallback* validate_client_hello_cb_;
+
+ // Pointer to the active callback which will receive the results of
+ // ProcessClientHello and forward it to
+ // FinishProcessingHandshakeMessageAfterProcessClientHello. Note that this
+ // field is mutually exclusive with validate_client_hello_cb_.
+ ProcessClientHelloCallback* process_client_hello_cb_;
+
+ bool encryption_established_;
+ bool handshake_confirmed_;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ crypto_negotiated_params_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_HANDSHAKER_H_
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
new file mode 100644
index 00000000000..010ac361ea9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QuicCryptoServerStreamBase::QuicCryptoServerStreamBase(QuicSession* session)
+ : QuicCryptoStream(session) {}
+
+// TODO(jokulik): Once stateless rejects support is inherent in the version
+// number, this function will likely go away entirely.
+// static
+bool QuicCryptoServerStreamBase::DoesPeerSupportStatelessRejects(
+ const CryptoHandshakeMessage& message) {
+ QuicTagVector received_tags;
+ QuicErrorCode error = message.GetTaglist(kCOPT, &received_tags);
+ if (error != QUIC_NO_ERROR) {
+ return false;
+ }
+ for (const QuicTag tag : received_tags) {
+ if (tag == kSREJ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicCryptoServerStream::QuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ bool use_stateless_rejects_if_peer_supported,
+ QuicSession* session,
+ Helper* helper)
+ : QuicCryptoServerStreamBase(session),
+ use_stateless_rejects_if_peer_supported_(
+ use_stateless_rejects_if_peer_supported),
+ peer_supports_stateless_rejects_(false),
+ crypto_config_(crypto_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ helper_(helper) {
+ DCHECK_EQ(Perspective::IS_SERVER, session->connection()->perspective());
+}
+
+QuicCryptoServerStream::~QuicCryptoServerStream() {}
+
+void QuicCryptoServerStream::CancelOutstandingCallbacks() {
+ if (handshaker()) {
+ handshaker()->CancelOutstandingCallbacks();
+ }
+}
+
+bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID(
+ std::string* output) const {
+ return handshaker()->GetBase64SHA256ClientChannelID(output);
+}
+
+void QuicCryptoServerStream::SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) {
+ handshaker()->SendServerConfigUpdate(cached_network_params);
+}
+
+uint8_t QuicCryptoServerStream::NumHandshakeMessages() const {
+ return handshaker()->NumHandshakeMessages();
+}
+
+uint8_t QuicCryptoServerStream::NumHandshakeMessagesWithServerNonces() const {
+ return handshaker()->NumHandshakeMessagesWithServerNonces();
+}
+
+int QuicCryptoServerStream::NumServerConfigUpdateMessagesSent() const {
+ return handshaker()->NumServerConfigUpdateMessagesSent();
+}
+
+const CachedNetworkParameters*
+QuicCryptoServerStream::PreviousCachedNetworkParams() const {
+ return handshaker()->PreviousCachedNetworkParams();
+}
+
+bool QuicCryptoServerStream::UseStatelessRejectsIfPeerSupported() const {
+ return use_stateless_rejects_if_peer_supported_;
+}
+
+bool QuicCryptoServerStream::PeerSupportsStatelessRejects() const {
+ return peer_supports_stateless_rejects_;
+}
+
+bool QuicCryptoServerStream::ZeroRttAttempted() const {
+ return handshaker()->ZeroRttAttempted();
+}
+
+void QuicCryptoServerStream::SetPeerSupportsStatelessRejects(
+ bool peer_supports_stateless_rejects) {
+ peer_supports_stateless_rejects_ = peer_supports_stateless_rejects;
+}
+
+void QuicCryptoServerStream::SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) {
+ handshaker()->SetPreviousCachedNetworkParams(cached_network_params);
+}
+
+bool QuicCryptoServerStream::ShouldSendExpectCTHeader() const {
+ return handshaker()->ShouldSendExpectCTHeader();
+}
+
+bool QuicCryptoServerStream::encryption_established() const {
+ if (!handshaker()) {
+ return false;
+ }
+ return handshaker()->encryption_established();
+}
+
+bool QuicCryptoServerStream::handshake_confirmed() const {
+ if (!handshaker()) {
+ return false;
+ }
+ return handshaker()->handshake_confirmed();
+}
+
+const QuicCryptoNegotiatedParameters&
+QuicCryptoServerStream::crypto_negotiated_params() const {
+ return handshaker()->crypto_negotiated_params();
+}
+
+CryptoMessageParser* QuicCryptoServerStream::crypto_message_parser() {
+ return handshaker()->crypto_message_parser();
+}
+
+void QuicCryptoServerStream::OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) {
+ DCHECK_EQ(version, session()->connection()->version());
+ CHECK(!handshaker_);
+ switch (session()->connection()->version().handshake_protocol) {
+ case PROTOCOL_QUIC_CRYPTO:
+ handshaker_ = QuicMakeUnique<QuicCryptoServerHandshaker>(
+ crypto_config_, this, compressed_certs_cache_, session(), helper_);
+ break;
+ case PROTOCOL_TLS1_3:
+ handshaker_ = QuicMakeUnique<TlsServerHandshaker>(
+ this, session(), crypto_config_->ssl_ctx(),
+ crypto_config_->proof_source());
+ break;
+ case PROTOCOL_UNSUPPORTED:
+ QUIC_BUG << "Attempting to create QuicCryptoServerStream for unknown "
+ "handshake protocol";
+ }
+}
+
+QuicCryptoServerStream::HandshakerDelegate* QuicCryptoServerStream::handshaker()
+ const {
+ return handshaker_.get();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h
new file mode 100644
index 00000000000..a00302779d9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h
@@ -0,0 +1,224 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_STREAM_H_
+#define QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_STREAM_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class CachedNetworkParameters;
+class CryptoHandshakeMessage;
+class QuicCryptoServerConfig;
+class QuicCryptoServerStreamBase;
+
+// TODO(alyssar) see what can be moved out of QuicCryptoServerStream with
+// various code and test refactoring.
+class QUIC_EXPORT_PRIVATE QuicCryptoServerStreamBase : public QuicCryptoStream {
+ public:
+ explicit QuicCryptoServerStreamBase(QuicSession* session);
+
+ ~QuicCryptoServerStreamBase() override {}
+
+ // Cancel any outstanding callbacks, such as asynchronous validation of client
+ // hello.
+ virtual void CancelOutstandingCallbacks() = 0;
+
+ // GetBase64SHA256ClientChannelID sets |*output| to the base64 encoded,
+ // SHA-256 hash of the client's ChannelID key and returns true, if the client
+ // presented a ChannelID. Otherwise it returns false.
+ virtual bool GetBase64SHA256ClientChannelID(std::string* output) const = 0;
+
+ virtual int NumServerConfigUpdateMessagesSent() const = 0;
+
+ // Sends the latest server config and source-address token to the client.
+ virtual void SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) = 0;
+
+ // These are all accessors and setters to their respective counters.
+ virtual uint8_t NumHandshakeMessages() const = 0;
+ virtual uint8_t NumHandshakeMessagesWithServerNonces() const = 0;
+ virtual bool UseStatelessRejectsIfPeerSupported() const = 0;
+ virtual bool PeerSupportsStatelessRejects() const = 0;
+ virtual bool ZeroRttAttempted() const = 0;
+ virtual void SetPeerSupportsStatelessRejects(bool set) = 0;
+ virtual const CachedNetworkParameters* PreviousCachedNetworkParams()
+ const = 0;
+ virtual void SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) = 0;
+
+ // Checks the options on the handshake-message to see whether the
+ // peer supports stateless-rejects.
+ static bool DoesPeerSupportStatelessRejects(
+ const CryptoHandshakeMessage& message);
+};
+
+class QUIC_EXPORT_PRIVATE QuicCryptoServerStream
+ : public QuicCryptoServerStreamBase {
+ public:
+ // QuicCryptoServerStream creates a HandshakerDelegate at construction time
+ // based on the QuicTransportVersion of the connection. Different
+ // HandshakerDelegates provide implementations of different crypto handshake
+ // protocols. Currently QUIC crypto is the only protocol implemented; a future
+ // HandshakerDelegate will use TLS as the handshake protocol.
+ // QuicCryptoServerStream delegates all of its public methods to its
+ // HandshakerDelegate.
+ //
+ // This setup of the crypto stream delegating its implementation to the
+ // handshaker results in the handshaker reading and writing bytes on the
+ // crypto stream, instead of the handshake rpassing the stream bytes to send.
+ class QUIC_EXPORT_PRIVATE HandshakerDelegate {
+ public:
+ virtual ~HandshakerDelegate() {}
+
+ // Cancel any outstanding callbacks, such as asynchronous validation of
+ // client hello.
+ virtual void CancelOutstandingCallbacks() = 0;
+
+ // GetBase64SHA256ClientChannelID sets |*output| to the base64 encoded,
+ // SHA-256 hash of the client's ChannelID key and returns true, if the
+ // client presented a ChannelID. Otherwise it returns false.
+ virtual bool GetBase64SHA256ClientChannelID(std::string* output) const = 0;
+
+ // Sends the latest server config and source-address token to the client.
+ virtual void SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) = 0;
+
+ // These are all accessors and setters to their respective counters.
+ virtual uint8_t NumHandshakeMessages() const = 0;
+ virtual uint8_t NumHandshakeMessagesWithServerNonces() const = 0;
+ virtual int NumServerConfigUpdateMessagesSent() const = 0;
+ virtual const CachedNetworkParameters* PreviousCachedNetworkParams()
+ const = 0;
+ virtual bool ZeroRttAttempted() const = 0;
+ virtual void SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) = 0;
+
+ // NOTE: Indicating that the Expect-CT header should be sent here presents a
+ // layering violation to some extent. The Expect-CT header only applies to
+ // HTTP connections, while this class can be used for non-HTTP applications.
+ // However, it is exposed here because that is the only place where the
+ // configuration for the certificate used in the connection is accessible.
+ virtual bool ShouldSendExpectCTHeader() const = 0;
+
+ // Returns true once any encrypter (initial/0RTT or final/1RTT) has been set
+ // for the connection.
+ virtual bool encryption_established() const = 0;
+
+ // Returns true once the crypto handshake has completed.
+ virtual bool handshake_confirmed() const = 0;
+
+ // Returns the parameters negotiated in the crypto handshake.
+ virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const = 0;
+
+ // Used by QuicCryptoStream to parse data received on this stream.
+ virtual CryptoMessageParser* crypto_message_parser() = 0;
+ };
+
+ class Helper {
+ public:
+ virtual ~Helper() {}
+
+ // Given the current connection_id, generates a new ConnectionId to
+ // be returned with a stateless reject.
+ virtual QuicConnectionId GenerateConnectionIdForReject(
+ QuicTransportVersion version,
+ QuicConnectionId connection_id) const = 0;
+
+ // Returns true if |message|, which was received on |self_address| is
+ // acceptable according to the visitor's policy. Otherwise, returns false
+ // and populates |error_details|.
+ virtual bool CanAcceptClientHello(const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const = 0;
+ };
+
+ // |crypto_config| must outlive the stream.
+ // |session| must outlive the stream.
+ // |helper| must outlive the stream.
+ QuicCryptoServerStream(const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ bool use_stateless_rejects_if_peer_supported,
+ QuicSession* session,
+ Helper* helper);
+ QuicCryptoServerStream(const QuicCryptoServerStream&) = delete;
+ QuicCryptoServerStream& operator=(const QuicCryptoServerStream&) = delete;
+
+ ~QuicCryptoServerStream() override;
+
+ // From QuicCryptoServerStreamBase
+ void CancelOutstandingCallbacks() override;
+ bool GetBase64SHA256ClientChannelID(std::string* output) const override;
+ void SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) override;
+ uint8_t NumHandshakeMessages() const override;
+ uint8_t NumHandshakeMessagesWithServerNonces() const override;
+ int NumServerConfigUpdateMessagesSent() const override;
+ const CachedNetworkParameters* PreviousCachedNetworkParams() const override;
+ bool UseStatelessRejectsIfPeerSupported() const override;
+ bool PeerSupportsStatelessRejects() const override;
+ bool ZeroRttAttempted() const override;
+ void SetPeerSupportsStatelessRejects(
+ bool peer_supports_stateless_rejects) override;
+ void SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) override;
+
+ // NOTE: Indicating that the Expect-CT header should be sent here presents
+ // a layering violation to some extent. The Expect-CT header only applies to
+ // HTTP connections, while this class can be used for non-HTTP applications.
+ // However, it is exposed here because that is the only place where the
+ // configuration for the certificate used in the connection is accessible.
+ bool ShouldSendExpectCTHeader() const;
+
+ bool encryption_established() const override;
+ bool handshake_confirmed() const override;
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override;
+ CryptoMessageParser* crypto_message_parser() override;
+ void OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) override;
+
+ protected:
+ // Provided so that subclasses can provide their own handshaker.
+ virtual HandshakerDelegate* handshaker() const;
+
+ private:
+ std::unique_ptr<HandshakerDelegate> handshaker_;
+
+ // If true, the server should use stateless rejects, so long as the
+ // client supports them, as indicated by
+ // peer_supports_stateless_rejects_.
+ bool use_stateless_rejects_if_peer_supported_;
+
+ // Set to true, once the server has received information from the
+ // client that it supports stateless reject.
+ // TODO(jokulik): Remove once client stateless reject support
+ // becomes the default.
+ bool peer_supports_stateless_rejects_;
+
+ // Arguments from QuicCryptoServerStream constructor that might need to be
+ // passed to the HandshakerDelegate constructor in its late construction.
+ const QuicCryptoServerConfig* crypto_config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+ Helper* helper_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_SERVER_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc
new file mode 100644
index 00000000000..660c588377b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_server_stream_test.cc
@@ -0,0 +1,573 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/failing_proof_source.h"
+#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+class QuicConnection;
+class QuicStream;
+} // namespace quic
+
+using testing::_;
+using testing::NiceMock;
+
+namespace quic {
+namespace test {
+
+class QuicCryptoServerStreamPeer {
+ public:
+ static bool DoesPeerSupportStatelessRejects(
+ const CryptoHandshakeMessage& message) {
+ return QuicCryptoServerStream::DoesPeerSupportStatelessRejects(message);
+ }
+};
+
+namespace {
+
+const char kServerHostname[] = "test.example.com";
+const uint16_t kServerPort = 443;
+
+class QuicCryptoServerStreamTest : public QuicTestWithParam<bool> {
+ public:
+ QuicCryptoServerStreamTest()
+ : QuicCryptoServerStreamTest(crypto_test_utils::ProofSourceForTesting()) {
+ }
+
+ explicit QuicCryptoServerStreamTest(std::unique_ptr<ProofSource> proof_source)
+ : server_crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ std::move(proof_source),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ server_compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ server_id_(kServerHostname, kServerPort, false),
+ client_crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx()) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, false);
+ }
+
+ void Initialize() { InitializeServer(); }
+
+ ~QuicCryptoServerStreamTest() override {
+ // Ensure that anything that might reference |helpers_| is destroyed before
+ // |helpers_| is destroyed.
+ server_session_.reset();
+ client_session_.reset();
+ helpers_.clear();
+ alarm_factories_.clear();
+ }
+
+ // Initializes the crypto server stream state for testing. May be
+ // called multiple times.
+ void InitializeServer() {
+ TestQuicSpdyServerSession* server_session = nullptr;
+ helpers_.push_back(QuicMakeUnique<NiceMock<MockQuicConnectionHelper>>());
+ alarm_factories_.push_back(QuicMakeUnique<MockAlarmFactory>());
+ CreateServerSessionForTest(
+ server_id_, QuicTime::Delta::FromSeconds(100000), supported_versions_,
+ helpers_.back().get(), alarm_factories_.back().get(),
+ &server_crypto_config_, &server_compressed_certs_cache_,
+ &server_connection_, &server_session);
+ CHECK(server_session);
+ server_session_.reset(server_session);
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_session_->helper(), GenerateConnectionIdForReject(_, _))
+ .Times(testing::AnyNumber());
+ crypto_test_utils::FakeServerOptions options;
+ options.token_binding_params = QuicTagVector{kTB10};
+ crypto_test_utils::SetupCryptoServerConfigForTest(
+ server_connection_->clock(), server_connection_->random_generator(),
+ &server_crypto_config_, options);
+ server_session_->GetMutableCryptoStream()->OnSuccessfulVersionNegotiation(
+ supported_versions_.front());
+ }
+
+ QuicCryptoServerStream* server_stream() {
+ return server_session_->GetMutableCryptoStream();
+ }
+
+ QuicCryptoClientStream* client_stream() {
+ return client_session_->GetMutableCryptoStream();
+ }
+
+ // Initializes a fake client, and all its associated state, for
+ // testing. May be called multiple times.
+ void InitializeFakeClient(bool supports_stateless_rejects) {
+ TestQuicSpdyClientSession* client_session = nullptr;
+ helpers_.push_back(QuicMakeUnique<NiceMock<MockQuicConnectionHelper>>());
+ alarm_factories_.push_back(QuicMakeUnique<MockAlarmFactory>());
+ CreateClientSessionForTest(
+ server_id_, supports_stateless_rejects,
+ QuicTime::Delta::FromSeconds(100000), supported_versions_,
+ helpers_.back().get(), alarm_factories_.back().get(),
+ &client_crypto_config_, &client_connection_, &client_session);
+ CHECK(client_session);
+ client_session_.reset(client_session);
+ }
+
+ int CompleteCryptoHandshake() {
+ CHECK(server_connection_);
+ CHECK(server_session_ != nullptr);
+
+ return crypto_test_utils::HandshakeWithFakeClient(
+ helpers_.back().get(), alarm_factories_.back().get(),
+ server_connection_, server_stream(), server_id_, client_options_);
+ }
+
+ // Performs a single round of handshake message-exchange between the
+ // client and server.
+ void AdvanceHandshakeWithFakeClient() {
+ CHECK(server_connection_);
+ CHECK(client_session_ != nullptr);
+
+ EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber());
+ EXPECT_CALL(*server_connection_, OnCanWrite()).Times(testing::AnyNumber());
+ client_stream()->CryptoConnect();
+ crypto_test_utils::AdvanceHandshake(client_connection_, client_stream(), 0,
+ server_connection_, server_stream(), 0);
+ }
+
+ protected:
+ // Every connection gets its own MockQuicConnectionHelper and
+ // MockAlarmFactory, tracked separately from the server and client state so
+ // their lifetimes persist through the whole test.
+ std::vector<std::unique_ptr<MockQuicConnectionHelper>> helpers_;
+ std::vector<std::unique_ptr<MockAlarmFactory>> alarm_factories_;
+
+ // Server state.
+ PacketSavingConnection* server_connection_;
+ std::unique_ptr<TestQuicSpdyServerSession> server_session_;
+ QuicCryptoServerConfig server_crypto_config_;
+ QuicCompressedCertsCache server_compressed_certs_cache_;
+ QuicServerId server_id_;
+
+ // Client state.
+ PacketSavingConnection* client_connection_;
+ QuicCryptoClientConfig client_crypto_config_;
+ std::unique_ptr<TestQuicSpdyClientSession> client_session_;
+
+ CryptoHandshakeMessage message_;
+ crypto_test_utils::FakeClientOptions client_options_;
+
+ // Which QUIC versions the client and server support.
+ ParsedQuicVersionVector supported_versions_ = AllSupportedVersions();
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests, QuicCryptoServerStreamTest, testing::Bool());
+
+TEST_P(QuicCryptoServerStreamTest, NotInitiallyConected) {
+ Initialize();
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, NotInitiallySendingStatelessRejects) {
+ Initialize();
+ EXPECT_FALSE(server_stream()->UseStatelessRejectsIfPeerSupported());
+ EXPECT_FALSE(server_stream()->PeerSupportsStatelessRejects());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) {
+ // CompleteCryptoHandshake returns the number of client hellos sent. This
+ // test should send:
+ // * One to get a source-address token and certificates.
+ // * One to complete the handshake.
+ Initialize();
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ConnectedAfterTlsHandshake) {
+ FLAGS_quic_supports_tls_handshake = true;
+ client_options_.only_tls_versions = true;
+ supported_versions_.clear();
+ for (QuicTransportVersion transport_version :
+ AllSupportedTransportVersions()) {
+ supported_versions_.push_back(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, transport_version));
+ }
+ Initialize();
+ CompleteCryptoHandshake();
+ EXPECT_EQ(PROTOCOL_TLS1_3, server_stream()->handshake_protocol());
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ForwardSecureAfterCHLO) {
+ Initialize();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ // Do a first handshake in order to prime the client config with the server's
+ // information.
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+
+ // Now do another handshake, with the blocking SHLO connection option.
+ InitializeServer();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE,
+ server_session_->connection()->encryption_level());
+}
+
+TEST_P(QuicCryptoServerStreamTest, StatelessRejectAfterCHLO) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ Initialize();
+
+ InitializeFakeClient(/* supports_stateless_rejects= */ true);
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ EXPECT_CALL(*client_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ AdvanceHandshakeWithFakeClient();
+
+ // Check the server to make the sure the handshake did not succeed.
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+
+ // Check the client state to make sure that it received a server-designated
+ // connection id.
+ QuicCryptoClientConfig::CachedState* client_state =
+ client_crypto_config_.LookupOrCreate(server_id_);
+
+ ASSERT_TRUE(client_state->has_server_nonce());
+ ASSERT_FALSE(client_state->GetNextServerNonce().empty());
+ ASSERT_FALSE(client_state->has_server_nonce());
+
+ ASSERT_TRUE(client_state->has_server_designated_connection_id());
+ const QuicConnectionId server_designated_connection_id =
+ client_state->GetNextServerDesignatedConnectionId();
+ const QuicConnectionId expected_id = QuicUtils::CreateRandomConnectionId(
+ server_connection_->random_generator());
+ EXPECT_EQ(expected_id, server_designated_connection_id);
+ EXPECT_FALSE(client_state->has_server_designated_connection_id());
+ ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST_P(QuicCryptoServerStreamTest, ConnectedAfterStatelessHandshake) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ Initialize();
+
+ InitializeFakeClient(/* supports_stateless_rejects= */ true);
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ EXPECT_CALL(*client_connection_,
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ AdvanceHandshakeWithFakeClient();
+
+ // On the first round, encryption will not be established.
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+ EXPECT_EQ(1, server_stream()->NumHandshakeMessages());
+ EXPECT_EQ(0, server_stream()->NumHandshakeMessagesWithServerNonces());
+
+ // Now check the client state.
+ QuicCryptoClientConfig::CachedState* client_state =
+ client_crypto_config_.LookupOrCreate(server_id_);
+
+ ASSERT_TRUE(client_state->has_server_designated_connection_id());
+ const QuicConnectionId server_designated_connection_id =
+ client_state->GetNextServerDesignatedConnectionId();
+ const QuicConnectionId expected_id = QuicUtils::CreateRandomConnectionId(
+ server_connection_->random_generator());
+ EXPECT_EQ(expected_id, server_designated_connection_id);
+ EXPECT_FALSE(client_state->has_server_designated_connection_id());
+ ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+
+ // Now create new client and server streams with the existing config
+ // and try the handshake again (0-RTT handshake).
+ InitializeServer();
+
+ InitializeFakeClient(/* supports_stateless_rejects= */ true);
+ // In the stateless case, the second handshake contains a server-nonce, so the
+ // AsyncStrikeRegisterVerification() case will still succeed (unlike a 0-RTT
+ // handshake).
+ AdvanceHandshakeWithFakeClient();
+
+ // On the second round, encryption will be established.
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+ EXPECT_EQ(1, server_stream()->NumHandshakeMessages());
+ EXPECT_EQ(1, server_stream()->NumHandshakeMessagesWithServerNonces());
+}
+
+TEST_P(QuicCryptoServerStreamTest, NoStatelessRejectIfNoClientSupport) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ Initialize();
+
+ // The server is configured to use stateless rejects, but the client does not
+ // support it.
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+ AdvanceHandshakeWithFakeClient();
+
+ // Check the server to make the sure the handshake did not succeed.
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+
+ // Check the client state to make sure that it did not receive a
+ // server-designated connection id.
+ QuicCryptoClientConfig::CachedState* client_state =
+ client_crypto_config_.LookupOrCreate(server_id_);
+
+ ASSERT_FALSE(client_state->has_server_designated_connection_id());
+ ASSERT_TRUE(client_state->IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
+ Initialize();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ // Do a first handshake in order to prime the client config with the server's
+ // information.
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_FALSE(server_stream()->ZeroRttAttempted());
+
+ // Now do another handshake, hopefully in 0-RTT.
+ QUIC_LOG(INFO) << "Resetting for 0-RTT handshake attempt";
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+ InitializeServer();
+
+ EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber());
+ client_stream()->CryptoConnect();
+
+ EXPECT_CALL(*client_session_, OnProofValid(_)).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_session_, OnProofVerifyDetailsAvailable(_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_connection_, OnCanWrite()).Times(testing::AnyNumber());
+ crypto_test_utils::CommunicateHandshakeMessages(
+ client_connection_, client_stream(), server_connection_, server_stream());
+
+ EXPECT_EQ(1, client_stream()->num_sent_client_hellos());
+ EXPECT_TRUE(server_stream()->ZeroRttAttempted());
+}
+
+TEST_P(QuicCryptoServerStreamTest, FailByPolicy) {
+ Initialize();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .WillOnce(testing::Return(false));
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
+
+ AdvanceHandshakeWithFakeClient();
+}
+
+TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) {
+ Initialize();
+ CompleteCryptoHandshake();
+ EXPECT_CALL(
+ *server_connection_,
+ CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, _, _));
+ message_.set_tag(kCHLO);
+ crypto_test_utils::SendHandshakeMessageToStream(server_stream(), message_,
+ Perspective::IS_CLIENT);
+}
+
+TEST_P(QuicCryptoServerStreamTest, BadMessageType) {
+ Initialize();
+
+ message_.set_tag(kSHLO);
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, _, _));
+ crypto_test_utils::SendHandshakeMessageToStream(server_stream(), message_,
+ Perspective::IS_SERVER);
+}
+
+TEST_P(QuicCryptoServerStreamTest, ChannelID) {
+ Initialize();
+
+ client_options_.channel_id_enabled = true;
+ client_options_.channel_id_source_async = false;
+ // CompleteCryptoHandshake verifies
+ // server_stream()->crypto_negotiated_params().channel_id is correct.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ChannelIDAsync) {
+ Initialize();
+
+ client_options_.channel_id_enabled = true;
+ client_options_.channel_id_source_async = true;
+ // CompleteCryptoHandshake verifies
+ // server_stream()->crypto_negotiated_params().channel_id is correct.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(server_stream()->encryption_established());
+ EXPECT_TRUE(server_stream()->handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, OnlySendSCUPAfterHandshakeComplete) {
+ // An attempt to send a SCUP before completing handshake should fail.
+ Initialize();
+
+ server_stream()->SendServerConfigUpdate(nullptr);
+ EXPECT_EQ(0, server_stream()->NumServerConfigUpdateMessagesSent());
+}
+
+TEST_P(QuicCryptoServerStreamTest, SendSCUPAfterHandshakeComplete) {
+ Initialize();
+
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ // Do a first handshake in order to prime the client config with the server's
+ // information.
+ AdvanceHandshakeWithFakeClient();
+
+ // Now do another handshake, with the blocking SHLO connection option.
+ InitializeServer();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+ AdvanceHandshakeWithFakeClient();
+
+ // Send a SCUP message and ensure that the client was able to verify it.
+ EXPECT_CALL(*client_connection_, CloseConnection(_, _, _)).Times(0);
+ server_stream()->SendServerConfigUpdate(nullptr);
+ crypto_test_utils::AdvanceHandshake(client_connection_, client_stream(), 1,
+ server_connection_, server_stream(), 1);
+
+ EXPECT_EQ(1, server_stream()->NumServerConfigUpdateMessagesSent());
+ EXPECT_EQ(1, client_stream()->num_scup_messages_received());
+}
+
+TEST_P(QuicCryptoServerStreamTest, DoesPeerSupportStatelessRejects) {
+ Initialize();
+
+ QuicConfig stateless_reject_config = DefaultQuicConfigStatelessRejects();
+ stateless_reject_config.ToHandshakeMessage(&message_);
+ EXPECT_TRUE(
+ QuicCryptoServerStreamPeer::DoesPeerSupportStatelessRejects(message_));
+
+ message_.Clear();
+ QuicConfig stateful_reject_config = DefaultQuicConfig();
+ stateful_reject_config.ToHandshakeMessage(&message_);
+ EXPECT_FALSE(
+ QuicCryptoServerStreamPeer::DoesPeerSupportStatelessRejects(message_));
+}
+
+class QuicCryptoServerStreamTestWithFailingProofSource
+ : public QuicCryptoServerStreamTest {
+ public:
+ QuicCryptoServerStreamTestWithFailingProofSource()
+ : QuicCryptoServerStreamTest(
+ std::unique_ptr<FailingProofSource>(new FailingProofSource)) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(MoreTests,
+ QuicCryptoServerStreamTestWithFailingProofSource,
+ testing::Bool());
+
+TEST_P(QuicCryptoServerStreamTestWithFailingProofSource, Test) {
+ Initialize();
+ InitializeFakeClient(/* supports_stateless_rejects= */ false);
+
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(*server_connection_,
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "Failed to get proof", _));
+ // Regression test for b/31521252, in which a crash would happen here.
+ AdvanceHandshakeWithFakeClient();
+ EXPECT_FALSE(server_stream()->encryption_established());
+ EXPECT_FALSE(server_stream()->handshake_confirmed());
+}
+
+class QuicCryptoServerStreamTestWithFakeProofSource
+ : public QuicCryptoServerStreamTest {
+ public:
+ QuicCryptoServerStreamTestWithFakeProofSource()
+ : QuicCryptoServerStreamTest(
+ std::unique_ptr<FakeProofSource>(new FakeProofSource)),
+ crypto_config_peer_(&server_crypto_config_) {}
+
+ FakeProofSource* GetFakeProofSource() const {
+ return static_cast<FakeProofSource*>(crypto_config_peer_.GetProofSource());
+ }
+
+ protected:
+ QuicCryptoServerConfigPeer crypto_config_peer_;
+};
+
+INSTANTIATE_TEST_SUITE_P(YetMoreTests,
+ QuicCryptoServerStreamTestWithFakeProofSource,
+ testing::Bool());
+
+// Regression test for b/35422225, in which multiple CHLOs arriving on the same
+// connection in close succession could cause a crash, especially when the use
+// of Mentat signing meant that it took a while for each CHLO to be processed.
+TEST_P(QuicCryptoServerStreamTestWithFakeProofSource, MultipleChlo) {
+ Initialize();
+ GetFakeProofSource()->Activate();
+ EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
+ .WillOnce(testing::Return(true));
+
+ // Create a minimal CHLO
+ MockClock clock;
+ QuicTransportVersion version = AllSupportedTransportVersions().front();
+ CryptoHandshakeMessage chlo = crypto_test_utils::GenerateDefaultInchoateCHLO(
+ &clock, version, &server_crypto_config_);
+
+ // Send in the CHLO, and check that a callback is now pending in the
+ // ProofSource.
+ crypto_test_utils::SendHandshakeMessageToStream(server_stream(), chlo,
+ Perspective::IS_CLIENT);
+ EXPECT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Send in a second CHLO while processing of the first is still pending.
+ // Verify that the server closes the connection rather than crashing. Note
+ // that the crash is a use-after-free, so it may only show up consistently in
+ // ASAN tests.
+ EXPECT_CALL(
+ *server_connection_,
+ CloseConnection(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO,
+ "Unexpected handshake message while processing CHLO", _));
+ crypto_test_utils::SendHandshakeMessageToStream(server_stream(), chlo,
+ Perspective::IS_CLIENT);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..836fbac67ab
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.cc
@@ -0,0 +1,435 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+#define ENDPOINT \
+ (session()->perspective() == Perspective::IS_SERVER ? "Server: " \
+ : "Client:" \
+ " ")
+
+QuicCryptoStream::QuicCryptoStream(QuicSession* session)
+ : QuicStream(QuicUtils::GetCryptoStreamId(
+ session->connection()->transport_version()),
+ session,
+ /*is_static=*/true,
+ BIDIRECTIONAL),
+ substreams_{{this, ENCRYPTION_INITIAL},
+ {this, ENCRYPTION_HANDSHAKE},
+ {this, ENCRYPTION_ZERO_RTT},
+ {this, ENCRYPTION_FORWARD_SECURE}} {
+ // The crypto stream is exempt from connection level flow control.
+ DisableConnectionFlowControlForThisStream();
+}
+
+QuicCryptoStream::~QuicCryptoStream() {}
+
+// static
+QuicByteCount QuicCryptoStream::CryptoMessageFramingOverhead(
+ QuicTransportVersion version,
+ QuicConnectionId connection_id) {
+ DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id, version));
+ return QuicPacketCreator::StreamFramePacketOverhead(
+ version, static_cast<QuicConnectionIdLength>(connection_id.length()),
+ PACKET_0BYTE_CONNECTION_ID,
+ /*include_version=*/true,
+ /*include_diversification_nonce=*/true,
+ version > QUIC_VERSION_43 ? PACKET_4BYTE_PACKET_NUMBER
+ : PACKET_1BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_1, VARIABLE_LENGTH_INTEGER_LENGTH_2,
+ /*offset=*/0);
+}
+
+void QuicCryptoStream::OnCryptoFrame(const QuicCryptoFrame& frame) {
+ QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version()))
+ << "Versions less than 47 shouldn't receive CRYPTO frames";
+ EncryptionLevel level = session()->connection()->last_decrypted_level();
+ substreams_[level].sequencer.OnCryptoFrame(frame);
+}
+
+void QuicCryptoStream::OnStreamFrame(const QuicStreamFrame& frame) {
+ if (QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ QUIC_PEER_BUG
+ << "Crypto data received in stream frame instead of crypto frame";
+ CloseConnectionWithDetails(QUIC_INVALID_STREAM_DATA,
+ "Unexpected stream frame");
+ }
+ QuicStream::OnStreamFrame(frame);
+}
+
+void QuicCryptoStream::OnDataAvailable() {
+ EncryptionLevel level = session()->connection()->last_decrypted_level();
+ if (!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ // Versions less than 47 only support QUIC crypto, which ignores the
+ // EncryptionLevel passed into CryptoMessageParser::ProcessInput (and
+ // OnDataAvailableInSequencer).
+ OnDataAvailableInSequencer(sequencer(), level);
+ return;
+ }
+ OnDataAvailableInSequencer(&substreams_[level].sequencer, level);
+}
+
+void QuicCryptoStream::OnDataAvailableInSequencer(
+ QuicStreamSequencer* sequencer,
+ EncryptionLevel level) {
+ struct iovec iov;
+ while (sequencer->GetReadableRegion(&iov)) {
+ QuicStringPiece data(static_cast<char*>(iov.iov_base), iov.iov_len);
+ if (!crypto_message_parser()->ProcessInput(data, level)) {
+ CloseConnectionWithDetails(crypto_message_parser()->error(),
+ crypto_message_parser()->error_detail());
+ return;
+ }
+ sequencer->MarkConsumed(iov.iov_len);
+ if (handshake_confirmed() &&
+ crypto_message_parser()->InputBytesRemaining() == 0) {
+ // If the handshake is complete and the current message has been fully
+ // processed then no more handshake messages are likely to arrive soon
+ // so release the memory in the stream sequencer.
+ sequencer->ReleaseBufferIfEmpty();
+ }
+ }
+}
+
+bool QuicCryptoStream::ExportKeyingMaterial(QuicStringPiece label,
+ QuicStringPiece context,
+ size_t result_len,
+ std::string* result) const {
+ if (!handshake_confirmed()) {
+ QUIC_DLOG(ERROR) << "ExportKeyingMaterial was called before forward-secure"
+ << "encryption was established.";
+ return false;
+ }
+ return CryptoUtils::ExportKeyingMaterial(
+ crypto_negotiated_params().subkey_secret, label, context, result_len,
+ result);
+}
+
+void QuicCryptoStream::WriteCryptoData(EncryptionLevel level,
+ QuicStringPiece data) {
+ if (!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ // 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);
+ return;
+ }
+ if (data.empty()) {
+ QUIC_BUG << "Empty crypto data being written";
+ return;
+ }
+ // Append |data| to the send buffer for this encryption level.
+ struct iovec iov(QuicUtils::MakeIovec(data));
+ QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+ QuicStreamOffset offset = send_buffer->stream_offset();
+ send_buffer->SaveStreamData(&iov, /*iov_count=*/1, /*iov_offset=*/0,
+ data.length());
+ if (kMaxStreamLength - offset < data.length()) {
+ QUIC_BUG << "Writing too much crypto handshake data";
+ // TODO(nharper): Switch this to an IETF QUIC error code, possibly
+ // INTERNAL_ERROR?
+ CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW,
+ "Writing too much crypto handshake data");
+ }
+
+ EncryptionLevel current_level = session()->connection()->encryption_level();
+ session()->connection()->SetDefaultEncryptionLevel(level);
+ size_t bytes_consumed =
+ session()->connection()->SendCryptoData(level, data.length(), offset);
+ session()->connection()->SetDefaultEncryptionLevel(current_level);
+
+ send_buffer->OnStreamDataConsumed(bytes_consumed);
+}
+
+void QuicCryptoStream::OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) {}
+
+bool QuicCryptoStream::OnCryptoFrameAcked(const QuicCryptoFrame& frame,
+ QuicTime::Delta ack_delay_time) {
+ QuicByteCount newly_acked_length = 0;
+ if (!substreams_[frame.level].send_buffer.OnStreamDataAcked(
+ frame.offset, frame.data_length, &newly_acked_length)) {
+ CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+ "Trying to ack unsent crypto data.");
+ return false;
+ }
+ return newly_acked_length > 0;
+}
+
+void QuicCryptoStream::NeuterUnencryptedStreamData() {
+ if (!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ for (const auto& interval : bytes_consumed_[ENCRYPTION_INITIAL]) {
+ QuicByteCount newly_acked_length = 0;
+ send_buffer().OnStreamDataAcked(
+ interval.min(), interval.max() - interval.min(), &newly_acked_length);
+ }
+ return;
+ }
+ QuicStreamSendBuffer* send_buffer =
+ &substreams_[ENCRYPTION_INITIAL].send_buffer;
+ // TODO(nharper): Consider adding a Clear() method to QuicStreamSendBuffer to
+ // replace the following code.
+ QuicIntervalSet<QuicStreamOffset> to_ack = send_buffer->bytes_acked();
+ to_ack.Complement(0, send_buffer->stream_offset());
+ for (const auto& interval : to_ack) {
+ QuicByteCount newly_acked_length = 0;
+ send_buffer->OnStreamDataAcked(
+ interval.min(), interval.max() - interval.min(), &newly_acked_length);
+ }
+}
+
+void QuicCryptoStream::OnStreamDataConsumed(size_t bytes_consumed) {
+ if (QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ QUIC_BUG << "Stream data consumed when CRYPTO frames should be in use";
+ }
+ if (bytes_consumed > 0) {
+ bytes_consumed_[session()->connection()->encryption_level()].Add(
+ stream_bytes_written(), stream_bytes_written() + bytes_consumed);
+ }
+ QuicStream::OnStreamDataConsumed(bytes_consumed);
+}
+
+bool QuicCryptoStream::HasPendingCryptoRetransmission() {
+ if (!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ return false;
+ }
+ for (EncryptionLevel level :
+ {ENCRYPTION_INITIAL, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+ if (substreams_[level].send_buffer.HasPendingRetransmission()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void QuicCryptoStream::WritePendingCryptoRetransmission() {
+ QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version()))
+ << "Versions less than 47 don't write CRYPTO frames";
+ EncryptionLevel current_encryption_level =
+ session()->connection()->encryption_level();
+ for (EncryptionLevel level :
+ {ENCRYPTION_INITIAL, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+ QuicStreamSendBuffer* send_buffer = &substreams_[level].send_buffer;
+ session()->connection()->SetDefaultEncryptionLevel(level);
+ while (send_buffer->HasPendingRetransmission()) {
+ auto pending = send_buffer->NextPendingRetransmission();
+ size_t bytes_consumed = session()->connection()->SendCryptoData(
+ level, pending.length, pending.offset);
+ send_buffer->OnStreamDataRetransmitted(pending.offset, bytes_consumed);
+ }
+ }
+ session()->connection()->SetDefaultEncryptionLevel(current_encryption_level);
+}
+
+void QuicCryptoStream::WritePendingRetransmission() {
+ while (HasPendingRetransmission()) {
+ StreamPendingRetransmission pending =
+ send_buffer().NextPendingRetransmission();
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ pending.offset, pending.offset + pending.length);
+ EncryptionLevel retransmission_encryption_level = ENCRYPTION_INITIAL;
+ // Determine the encryption level to write the retransmission
+ // at. The retransmission should be written at the same encryption level
+ // as the original transmission.
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ if (retransmission.Intersects(bytes_consumed_[i])) {
+ retransmission_encryption_level = static_cast<EncryptionLevel>(i);
+ retransmission.Intersection(bytes_consumed_[i]);
+ break;
+ }
+ }
+ pending.offset = retransmission.begin()->min();
+ pending.length =
+ retransmission.begin()->max() - retransmission.begin()->min();
+ EncryptionLevel current_encryption_level =
+ session()->connection()->encryption_level();
+ // Set appropriate encryption level.
+ session()->connection()->SetDefaultEncryptionLevel(
+ retransmission_encryption_level);
+ QuicConsumedData consumed = session()->WritevData(
+ this, id(), pending.length, pending.offset, NO_FIN);
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
+ << " tries to retransmit stream data [" << pending.offset
+ << ", " << pending.offset + pending.length
+ << ") with encryption level: "
+ << retransmission_encryption_level
+ << ", consumed: " << consumed;
+ OnStreamFrameRetransmitted(pending.offset, consumed.bytes_consumed,
+ consumed.fin_consumed);
+ // Restore encryption level.
+ session()->connection()->SetDefaultEncryptionLevel(
+ current_encryption_level);
+ if (consumed.bytes_consumed < pending.length) {
+ // The connection is write blocked.
+ break;
+ }
+ }
+}
+
+bool QuicCryptoStream::RetransmitStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool /*fin*/) {
+ QuicIntervalSet<QuicStreamOffset> retransmission(offset,
+ offset + data_length);
+ // Determine the encryption level to send data. This only needs to be once as
+ // [offset, offset + data_length) is guaranteed to be in the same packet.
+ EncryptionLevel send_encryption_level = ENCRYPTION_INITIAL;
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ if (retransmission.Intersects(bytes_consumed_[i])) {
+ send_encryption_level = static_cast<EncryptionLevel>(i);
+ break;
+ }
+ }
+ retransmission.Difference(bytes_acked());
+ EncryptionLevel current_encryption_level =
+ session()->connection()->encryption_level();
+ for (const auto& interval : retransmission) {
+ QuicStreamOffset retransmission_offset = interval.min();
+ QuicByteCount retransmission_length = interval.max() - interval.min();
+ // Set appropriate encryption level.
+ session()->connection()->SetDefaultEncryptionLevel(send_encryption_level);
+ QuicConsumedData consumed = session()->WritevData(
+ this, id(), retransmission_length, retransmission_offset, NO_FIN);
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id()
+ << " is forced to retransmit stream data ["
+ << retransmission_offset << ", "
+ << retransmission_offset + retransmission_length
+ << "), with encryption level: " << send_encryption_level
+ << ", consumed: " << consumed;
+ OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed,
+ consumed.fin_consumed);
+ // Restore encryption level.
+ session()->connection()->SetDefaultEncryptionLevel(
+ current_encryption_level);
+ if (consumed.bytes_consumed < retransmission_length) {
+ // The connection is write blocked.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+uint64_t QuicCryptoStream::crypto_bytes_read() const {
+ if (!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ return stream_bytes_read();
+ }
+ return substreams_[ENCRYPTION_INITIAL].sequencer.NumBytesConsumed() +
+ substreams_[ENCRYPTION_ZERO_RTT].sequencer.NumBytesConsumed() +
+ substreams_[ENCRYPTION_FORWARD_SECURE].sequencer.NumBytesConsumed();
+}
+
+uint64_t QuicCryptoStream::BytesReadOnLevel(EncryptionLevel level) const {
+ return substreams_[level].sequencer.NumBytesConsumed();
+}
+
+bool QuicCryptoStream::WriteCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version()))
+ << "Versions less than 47 don't write CRYPTO frames (2)";
+ return substreams_[level].send_buffer.WriteStreamData(offset, data_length,
+ writer);
+}
+
+void QuicCryptoStream::OnCryptoFrameLost(QuicCryptoFrame* crypto_frame) {
+ QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version()))
+ << "Versions less than 47 don't lose CRYPTO frames";
+ substreams_[crypto_frame->level].send_buffer.OnStreamDataLost(
+ crypto_frame->offset, crypto_frame->data_length);
+}
+
+void QuicCryptoStream::RetransmitData(QuicCryptoFrame* crypto_frame) {
+ QUIC_BUG_IF(!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version()))
+ << "Versions less than 47 don't retransmit CRYPTO frames";
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ crypto_frame->offset, crypto_frame->offset + crypto_frame->data_length);
+ QuicStreamSendBuffer* send_buffer =
+ &substreams_[crypto_frame->level].send_buffer;
+ retransmission.Difference(send_buffer->bytes_acked());
+ if (retransmission.Empty()) {
+ return;
+ }
+ EncryptionLevel current_encryption_level =
+ session()->connection()->encryption_level();
+ for (const auto& interval : retransmission) {
+ size_t retransmission_offset = interval.min();
+ size_t retransmission_length = interval.max() - interval.min();
+ session()->connection()->SetDefaultEncryptionLevel(crypto_frame->level);
+ size_t bytes_consumed = session()->connection()->SendCryptoData(
+ crypto_frame->level, retransmission_length, retransmission_offset);
+ send_buffer->OnStreamDataRetransmitted(retransmission_offset,
+ bytes_consumed);
+ }
+ session()->connection()->SetDefaultEncryptionLevel(current_encryption_level);
+}
+
+bool QuicCryptoStream::IsFrameOutstanding(EncryptionLevel level,
+ size_t offset,
+ size_t length) const {
+ if (!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ // This only happens if a client was originally configured for a version
+ // greater than 45, but received a version negotiation packet and is
+ // attempting to retransmit for a version less than 47. Outside of tests,
+ // this is a misconfiguration of the client, and this connection will be
+ // doomed. Return false here to avoid trying to retransmit CRYPTO frames on
+ // the wrong transport version.
+ return false;
+ }
+ return substreams_[level].send_buffer.IsStreamDataOutstanding(offset, length);
+}
+
+bool QuicCryptoStream::IsWaitingForAcks() const {
+ if (!QuicVersionUsesCryptoFrames(
+ session()->connection()->transport_version())) {
+ return QuicStream::IsWaitingForAcks();
+ }
+ for (EncryptionLevel level :
+ {ENCRYPTION_INITIAL, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+ if (substreams_[level].send_buffer.stream_bytes_outstanding()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicCryptoStream::CryptoSubstream::CryptoSubstream(
+ QuicCryptoStream* crypto_stream,
+ EncryptionLevel)
+ : sequencer(crypto_stream),
+ send_buffer(crypto_stream->session()
+ ->connection()
+ ->helper()
+ ->GetStreamSendBufferAllocator()) {}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h
new file mode 100644
index 00000000000..0c6d08aa607
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream.h
@@ -0,0 +1,173 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CRYPTO_STREAM_H_
+#define QUICHE_QUIC_CORE_QUIC_CRYPTO_STREAM_H_
+
+#include <cstddef>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicSession;
+
+// Crypto handshake messages in QUIC take place over a reserved stream with the
+// id 1. Each endpoint (client and server) will allocate an instance of a
+// subclass of QuicCryptoStream to send and receive handshake messages. (In the
+// normal 1-RTT handshake, the client will send a client hello, CHLO, message.
+// The server will receive this message and respond with a server hello message,
+// SHLO. At this point both sides will have established a crypto context they
+// can use to send encrypted messages.
+//
+// For more details:
+// https://docs.google.com/document/d/1g5nIXAIkN_Y-7XJW5K45IblHd_L2f5LTaDUDwvZ5L6g/edit?usp=sharing
+class QUIC_EXPORT_PRIVATE QuicCryptoStream : public QuicStream {
+ public:
+ explicit QuicCryptoStream(QuicSession* session);
+ QuicCryptoStream(const QuicCryptoStream&) = delete;
+ QuicCryptoStream& operator=(const QuicCryptoStream&) = delete;
+
+ ~QuicCryptoStream() override;
+
+ // Returns the per-packet framing overhead associated with sending a
+ // handshake message for |version|.
+ static QuicByteCount CryptoMessageFramingOverhead(
+ QuicTransportVersion version,
+ QuicConnectionId connection_id);
+
+ // QuicStream implementation
+ void OnStreamFrame(const QuicStreamFrame& frame) override;
+ void OnDataAvailable() override;
+
+ // Called when a CRYPTO frame is received.
+ void OnCryptoFrame(const QuicCryptoFrame& frame);
+
+ // Called when a CRYPTO frame is ACKed.
+ bool OnCryptoFrameAcked(const QuicCryptoFrame& frame,
+ QuicTime::Delta ack_delay_time);
+
+ // Performs key extraction to derive a new secret of |result_len| bytes
+ // dependent on |label|, |context|, and the stream's negotiated subkey secret.
+ // Returns false if the handshake has not been confirmed or the parameters are
+ // invalid (e.g. |label| contains null bytes); returns true on success.
+ bool ExportKeyingMaterial(QuicStringPiece label,
+ QuicStringPiece context,
+ size_t result_len,
+ std::string* result) const;
+
+ // Writes |data| to the QuicStream at level |level|.
+ virtual void WriteCryptoData(EncryptionLevel level, QuicStringPiece data);
+
+ // Returns true once an encrypter has been set for the connection.
+ virtual bool encryption_established() const = 0;
+
+ // Returns true once the crypto handshake has completed.
+ virtual bool handshake_confirmed() const = 0;
+
+ // Returns the parameters negotiated in the crypto handshake.
+ virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const = 0;
+
+ // Provides the message parser to use when data is received on this stream.
+ virtual CryptoMessageParser* crypto_message_parser() = 0;
+
+ // Called when the underlying QuicConnection has agreed upon a QUIC version to
+ // use.
+ virtual void OnSuccessfulVersionNegotiation(const ParsedQuicVersion& version);
+
+ // Called to cancel retransmission of unencrypted crypto stream data.
+ void NeuterUnencryptedStreamData();
+
+ // Override to record the encryption level of consumed data.
+ void OnStreamDataConsumed(size_t bytes_consumed) override;
+
+ // Returns whether there are any bytes pending retransmission in CRYPTO
+ // frames.
+ virtual bool HasPendingCryptoRetransmission();
+
+ // Writes any pending CRYPTO frame retransmissions.
+ void WritePendingCryptoRetransmission();
+
+ // Override to retransmit lost crypto data with the appropriate encryption
+ // level.
+ void WritePendingRetransmission() override;
+
+ // Override to send unacked crypto data with the appropriate encryption level.
+ bool RetransmitStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin) override;
+
+ // Returns the number of bytes of handshake data that have been received from
+ // the peer in either CRYPTO or STREAM frames.
+ uint64_t crypto_bytes_read() const;
+
+ // Returns the number of bytes of handshake data that have been received from
+ // the peer in CRYPTO frames at a particular encryption level.
+ QuicByteCount BytesReadOnLevel(EncryptionLevel level) const;
+
+ // Writes |data_length| of data of a crypto frame to |writer|. The data
+ // written is from the send buffer for encryption level |level| and starts at
+ // |offset|.
+ bool WriteCryptoFrame(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer);
+
+ // Called when data from a CRYPTO frame is considered lost. The lost data is
+ // identified by the encryption level, offset, and length in |crypto_frame|.
+ void OnCryptoFrameLost(QuicCryptoFrame* crypto_frame);
+
+ // Called to retransmit any outstanding data in the range indicated by the
+ // encryption level, offset, and length in |crypto_frame|.
+ void RetransmitData(QuicCryptoFrame* crypto_frame);
+
+ // Returns true if any portion of the data at encryption level |level|
+ // starting at |offset| for |length| bytes is outstanding.
+ bool IsFrameOutstanding(EncryptionLevel level,
+ size_t offset,
+ size_t length) const;
+
+ // Returns true if the crypto handshake is still waiting for acks of sent
+ // data, and false if all data has been acked.
+ bool IsWaitingForAcks() const;
+
+ private:
+ // Data sent and received in CRYPTO frames is sent at multiple encryption
+ // levels. Some of the state for the single logical crypto stream is split
+ // across encryption levels, and a CryptoSubstream is used to manage that
+ // state for a particular encryption level.
+ struct CryptoSubstream {
+ CryptoSubstream(QuicCryptoStream* crypto_stream, EncryptionLevel);
+
+ QuicStreamSequencer sequencer;
+ QuicStreamSendBuffer send_buffer;
+ };
+
+ // Helper method for OnDataAvailable. Calls CryptoMessageParser::ProcessInput
+ // with the data available in |sequencer| and |level|, and marks the data
+ // passed to ProcessInput as consumed.
+ void OnDataAvailableInSequencer(QuicStreamSequencer* sequencer,
+ EncryptionLevel level);
+
+ // Consumed data according to encryption levels.
+ // TODO(fayang): This is not needed once switching from QUIC crypto to
+ // TLS 1.3, which never encrypts crypto data.
+ QuicIntervalSet<QuicStreamOffset> bytes_consumed_[NUM_ENCRYPTION_LEVELS];
+
+ // Keeps state for data sent/received in CRYPTO frames at each encryption
+ // level.
+ CryptoSubstream substreams_[NUM_ENCRYPTION_LEVELS];
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_CRYPTO_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc
new file mode 100644
index 00000000000..f5ab4130b11
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_crypto_stream_test.cc
@@ -0,0 +1,518 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::InSequence;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockQuicCryptoStream : public QuicCryptoStream,
+ public QuicCryptoHandshaker {
+ public:
+ explicit MockQuicCryptoStream(QuicSession* session)
+ : QuicCryptoStream(session),
+ QuicCryptoHandshaker(this, session),
+ params_(new QuicCryptoNegotiatedParameters) {}
+ MockQuicCryptoStream(const MockQuicCryptoStream&) = delete;
+ MockQuicCryptoStream& operator=(const MockQuicCryptoStream&) = delete;
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ messages_.push_back(message);
+ }
+
+ std::vector<CryptoHandshakeMessage>* messages() { return &messages_; }
+
+ bool encryption_established() const override { return false; }
+ bool handshake_confirmed() const override { return false; }
+
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override {
+ return *params_;
+ }
+ CryptoMessageParser* crypto_message_parser() override {
+ return QuicCryptoHandshaker::crypto_message_parser();
+ }
+
+ private:
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ std::vector<CryptoHandshakeMessage> messages_;
+};
+
+class QuicCryptoStreamTest : public QuicTest {
+ public:
+ QuicCryptoStreamTest()
+ : connection_(new MockQuicConnection(&helper_,
+ &alarm_factory_,
+ Perspective::IS_CLIENT)),
+ session_(connection_, /*create_mock_crypto_stream=*/false) {
+ stream_ = new MockQuicCryptoStream(&session_);
+ session_.SetCryptoStream(stream_);
+ session_.Initialize();
+ message_.set_tag(kSHLO);
+ message_.SetStringPiece(1, "abc");
+ message_.SetStringPiece(2, "def");
+ ConstructHandshakeMessage();
+ }
+ QuicCryptoStreamTest(const QuicCryptoStreamTest&) = delete;
+ QuicCryptoStreamTest& operator=(const QuicCryptoStreamTest&) = delete;
+
+ void ConstructHandshakeMessage() {
+ CryptoFramer framer;
+ message_data_ = framer.ConstructHandshakeMessage(message_);
+ }
+
+ protected:
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnection* connection_;
+ MockQuicSpdySession session_;
+ MockQuicCryptoStream* stream_;
+ CryptoHandshakeMessage message_;
+ std::unique_ptr<QuicData> message_data_;
+};
+
+TEST_F(QuicCryptoStreamTest, NotInitiallyConected) {
+ EXPECT_FALSE(stream_->encryption_established());
+ EXPECT_FALSE(stream_->handshake_confirmed());
+}
+
+TEST_F(QuicCryptoStreamTest, ProcessRawData) {
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ stream_->OnStreamFrame(QuicStreamFrame(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ /*fin=*/false,
+ /*offset=*/0, message_data_->AsStringPiece()));
+ } else {
+ stream_->OnCryptoFrame(QuicCryptoFrame(ENCRYPTION_INITIAL, /*offset*/ 0,
+ message_data_->AsStringPiece()));
+ }
+ ASSERT_EQ(1u, stream_->messages()->size());
+ const CryptoHandshakeMessage& message = (*stream_->messages())[0];
+ EXPECT_EQ(kSHLO, message.tag());
+ EXPECT_EQ(2u, message.tag_value_map().size());
+ EXPECT_EQ("abc", crypto_test_utils::GetValueForTag(message, 1));
+ EXPECT_EQ("def", crypto_test_utils::GetValueForTag(message, 2));
+}
+
+TEST_F(QuicCryptoStreamTest, ProcessBadData) {
+ std::string bad(message_data_->data(), message_data_->length());
+ const int kFirstTagIndex = sizeof(uint32_t) + // message tag
+ sizeof(uint16_t) + // number of tag-value pairs
+ sizeof(uint16_t); // padding
+ EXPECT_EQ(1, bad[kFirstTagIndex]);
+ bad[kFirstTagIndex] = 0x7F; // out of order tag
+
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_CRYPTO_TAGS_OUT_OF_ORDER,
+ testing::_, testing::_));
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ stream_->OnStreamFrame(QuicStreamFrame(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ /*fin=*/false, /*offset=*/0, bad));
+ } else {
+ stream_->OnCryptoFrame(
+ QuicCryptoFrame(ENCRYPTION_INITIAL, /*offset*/ 0, bad));
+ }
+}
+
+TEST_F(QuicCryptoStreamTest, NoConnectionLevelFlowControl) {
+ EXPECT_FALSE(
+ QuicStreamPeer::StreamContributesToConnectionFlowControl(stream_));
+}
+
+TEST_F(QuicCryptoStreamTest, RetransmitCryptoData) {
+ if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
+ InSequence s;
+ // Send [0, 1350) in ENCRYPTION_INITIAL.
+ EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+ std::string data(1350, 'a');
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1350, 0, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1350, 1350, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+ // Lost [0, 1000).
+ stream_->OnStreamFrameLost(0, 1000, false);
+ EXPECT_TRUE(stream_->HasPendingRetransmission());
+ // Lost [1200, 2000).
+ stream_->OnStreamFrameLost(1200, 800, false);
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1000, 0, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ // Verify [1200, 2000) are sent in [1200, 1350) and [1350, 2000) because of
+ // they are in different encryption levels.
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 150, 1200, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 650, 1350, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnCanWrite();
+ EXPECT_FALSE(stream_->HasPendingRetransmission());
+ // Verify connection's encryption level has restored.
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+}
+
+TEST_F(QuicCryptoStreamTest, RetransmitCryptoDataInCryptoFrames) {
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
+ EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0);
+ InSequence s;
+ // Send [0, 1350) in ENCRYPTION_INITIAL.
+ EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+ std::string data(1350, 'a');
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ stream_->WriteCryptoData(ENCRYPTION_INITIAL, data);
+ // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ std::unique_ptr<NullEncrypter> encrypter =
+ QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT);
+ connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter));
+ EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+ // Lost [0, 1000).
+ QuicCryptoFrame lost_frame(ENCRYPTION_INITIAL, 0, 1000);
+ stream_->OnCryptoFrameLost(&lost_frame);
+ EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
+ // Lost [1200, 2000).
+ lost_frame = QuicCryptoFrame(ENCRYPTION_INITIAL, 1200, 150);
+ stream_->OnCryptoFrameLost(&lost_frame);
+ lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650);
+ stream_->OnCryptoFrameLost(&lost_frame);
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1000, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ // Verify [1200, 2000) are sent in [1200, 1350) and [1350, 2000) because of
+ // they are in different encryption levels.
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 150, 1200))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ stream_->WritePendingCryptoRetransmission();
+ EXPECT_FALSE(stream_->HasPendingCryptoRetransmission());
+ // Verify connection's encryption level has restored.
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+}
+
+TEST_F(QuicCryptoStreamTest, NeuterUnencryptedStreamData) {
+ if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
+ // Send [0, 1350) in ENCRYPTION_INITIAL.
+ EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+ std::string data(1350, 'a');
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1350, 0, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1350, 1350, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(data, false, nullptr);
+
+ // Lost [0, 1350).
+ stream_->OnStreamFrameLost(0, 1350, false);
+ EXPECT_TRUE(stream_->HasPendingRetransmission());
+ // Neuters [0, 1350).
+ stream_->NeuterUnencryptedStreamData();
+ EXPECT_FALSE(stream_->HasPendingRetransmission());
+ // Lost [0, 1350) again.
+ stream_->OnStreamFrameLost(0, 1350, false);
+ EXPECT_FALSE(stream_->HasPendingRetransmission());
+
+ // Lost [1350, 2000).
+ stream_->OnStreamFrameLost(1350, 650, false);
+ EXPECT_TRUE(stream_->HasPendingRetransmission());
+ stream_->NeuterUnencryptedStreamData();
+ EXPECT_TRUE(stream_->HasPendingRetransmission());
+}
+
+TEST_F(QuicCryptoStreamTest, NeuterUnencryptedCryptoData) {
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
+ // Send [0, 1350) in ENCRYPTION_INITIAL.
+ EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+ std::string data(1350, 'a');
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ stream_->WriteCryptoData(ENCRYPTION_INITIAL, data);
+ // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ std::unique_ptr<NullEncrypter> encrypter =
+ QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT);
+ connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter));
+ EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+ EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0);
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+
+ // Lost [0, 1350).
+ QuicCryptoFrame lost_frame(ENCRYPTION_INITIAL, 0, 1350);
+ stream_->OnCryptoFrameLost(&lost_frame);
+ EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
+ // Neuters [0, 1350).
+ stream_->NeuterUnencryptedStreamData();
+ EXPECT_FALSE(stream_->HasPendingCryptoRetransmission());
+ // Lost [0, 1350) again.
+ stream_->OnCryptoFrameLost(&lost_frame);
+ EXPECT_FALSE(stream_->HasPendingCryptoRetransmission());
+
+ // Lost [1350, 2000), which starts at offset 0 at the ENCRYPTION_ZERO_RTT
+ // level.
+ lost_frame = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 650);
+ stream_->OnCryptoFrameLost(&lost_frame);
+ EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
+ stream_->NeuterUnencryptedStreamData();
+ EXPECT_TRUE(stream_->HasPendingCryptoRetransmission());
+}
+
+TEST_F(QuicCryptoStreamTest, RetransmitStreamData) {
+ if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
+ InSequence s;
+ // Send [0, 1350) in ENCRYPTION_INITIAL.
+ EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+ std::string data(1350, 'a');
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1350, 0, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1350, 1350, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+ // Ack [2000, 2500).
+ QuicByteCount newly_acked_length = 0;
+ stream_->OnStreamFrameAcked(2000, 500, false, QuicTime::Delta::Zero(),
+ &newly_acked_length);
+ EXPECT_EQ(500u, newly_acked_length);
+
+ // Force crypto stream to send [1350, 2700) and only [1350, 1500) is consumed.
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 650, 1350, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(
+ stream_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()), 150,
+ 1350, NO_FIN);
+ }));
+
+ EXPECT_FALSE(stream_->RetransmitStreamData(1350, 1350, false));
+ // Verify connection's encryption level has restored.
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+ // Force session to send [1350, 1500) again and all data is consumed.
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 650, 1350, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 200, 2500, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_TRUE(stream_->RetransmitStreamData(1350, 1350, false));
+ // Verify connection's encryption level has restored.
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _)).Times(0);
+ // Force to send an empty frame.
+ EXPECT_TRUE(stream_->RetransmitStreamData(0, 0, false));
+}
+
+TEST_F(QuicCryptoStreamTest, RetransmitStreamDataWithCryptoFrames) {
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
+ InSequence s;
+ // Send [0, 1350) in ENCRYPTION_INITIAL.
+ EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+ std::string data(1350, 'a');
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ stream_->WriteCryptoData(ENCRYPTION_INITIAL, data);
+ // Send [1350, 2700) in ENCRYPTION_ZERO_RTT.
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ std::unique_ptr<NullEncrypter> encrypter =
+ QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT);
+ connection_->SetEncrypter(ENCRYPTION_ZERO_RTT, std::move(encrypter));
+ EXPECT_EQ(ENCRYPTION_ZERO_RTT, connection_->encryption_level());
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 1350, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ stream_->WriteCryptoData(ENCRYPTION_ZERO_RTT, data);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+ // Ack [2000, 2500).
+ QuicCryptoFrame acked_frame(ENCRYPTION_ZERO_RTT, 650, 500);
+ EXPECT_TRUE(
+ stream_->OnCryptoFrameAcked(acked_frame, QuicTime::Delta::Zero()));
+
+ // Retransmit only [1350, 1500).
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 150, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ QuicCryptoFrame frame_to_retransmit(ENCRYPTION_ZERO_RTT, 0, 150);
+ stream_->RetransmitData(&frame_to_retransmit);
+
+ // Verify connection's encryption level has restored.
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+ // Retransmit [1350, 2700) again and all data is sent.
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 650, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_ZERO_RTT, 200, 1150))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ frame_to_retransmit = QuicCryptoFrame(ENCRYPTION_ZERO_RTT, 0, 1350);
+ stream_->RetransmitData(&frame_to_retransmit);
+ // Verify connection's encryption level has restored.
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, connection_->encryption_level());
+
+ EXPECT_CALL(*connection_, SendCryptoData(_, _, _)).Times(0);
+ // Force to send an empty frame.
+ QuicCryptoFrame empty_frame(ENCRYPTION_FORWARD_SECURE, 0, 0);
+ stream_->RetransmitData(&empty_frame);
+}
+
+// Regression test for b/115926584.
+TEST_F(QuicCryptoStreamTest, HasUnackedCryptoData) {
+ if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
+ std::string data(1350, 'a');
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1350, 0, _))
+ .WillOnce(testing::Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ // Although there is no outstanding data, verify session has pending crypto
+ // data.
+ EXPECT_EQ(GetQuicReloadableFlag(quic_fix_has_pending_crypto_data),
+ session_.HasUnackedCryptoData());
+
+ EXPECT_CALL(
+ session_,
+ WritevData(_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ 1350, 0, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnCanWrite();
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_TRUE(session_.HasUnackedCryptoData());
+}
+
+TEST_F(QuicCryptoStreamTest, HasUnackedCryptoDataWithCryptoFrames) {
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ return;
+ }
+ // Send [0, 1350) in ENCRYPTION_INITIAL.
+ EXPECT_EQ(ENCRYPTION_INITIAL, connection_->encryption_level());
+ std::string data(1350, 'a');
+ EXPECT_CALL(*connection_, SendCryptoData(ENCRYPTION_INITIAL, 1350, 0))
+ .WillOnce(Invoke(connection_,
+ &MockQuicConnection::QuicConnection_SendCryptoData));
+ stream_->WriteCryptoData(ENCRYPTION_INITIAL, data);
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_TRUE(session_.HasUnackedCryptoData());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.cc b/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.cc
new file mode 100644
index 00000000000..0488fd39d00
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.cc
@@ -0,0 +1,317 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+QuicDataReader::QuicDataReader(const char* data, const size_t len)
+ : QuicDataReader(data, len, NETWORK_BYTE_ORDER) {}
+
+QuicDataReader::QuicDataReader(const char* data,
+ const size_t len,
+ Endianness endianness)
+ : data_(data), len_(len), pos_(0), endianness_(endianness) {}
+
+bool QuicDataReader::ReadUInt8(uint8_t* result) {
+ return ReadBytes(result, sizeof(*result));
+}
+
+bool QuicDataReader::ReadUInt16(uint16_t* result) {
+ if (!ReadBytes(result, sizeof(*result))) {
+ return false;
+ }
+ if (endianness_ == NETWORK_BYTE_ORDER) {
+ *result = QuicEndian::NetToHost16(*result);
+ }
+ return true;
+}
+
+bool QuicDataReader::ReadUInt32(uint32_t* result) {
+ if (!ReadBytes(result, sizeof(*result))) {
+ return false;
+ }
+ if (endianness_ == NETWORK_BYTE_ORDER) {
+ *result = QuicEndian::NetToHost32(*result);
+ }
+ return true;
+}
+
+bool QuicDataReader::ReadUInt64(uint64_t* result) {
+ if (!ReadBytes(result, sizeof(*result))) {
+ return false;
+ }
+ if (endianness_ == NETWORK_BYTE_ORDER) {
+ *result = QuicEndian::NetToHost64(*result);
+ }
+ return true;
+}
+
+bool QuicDataReader::ReadBytesToUInt64(size_t num_bytes, uint64_t* result) {
+ *result = 0u;
+ if (num_bytes > sizeof(*result)) {
+ return false;
+ }
+ if (endianness_ == HOST_BYTE_ORDER) {
+ return ReadBytes(result, num_bytes);
+ }
+
+ if (!ReadBytes(reinterpret_cast<char*>(result) + sizeof(*result) - num_bytes,
+ num_bytes)) {
+ return false;
+ }
+ *result = QuicEndian::NetToHost64(*result);
+ return true;
+}
+
+bool QuicDataReader::ReadUFloat16(uint64_t* result) {
+ uint16_t value;
+ if (!ReadUInt16(&value)) {
+ return false;
+ }
+
+ *result = value;
+ if (*result < (1 << kUFloat16MantissaEffectiveBits)) {
+ // Fast path: either the value is denormalized (no hidden bit), or
+ // normalized (hidden bit set, exponent offset by one) with exponent zero.
+ // Zero exponent offset by one sets the bit exactly where the hidden bit is.
+ // So in both cases the value encodes itself.
+ return true;
+ }
+
+ uint16_t exponent =
+ value >> kUFloat16MantissaBits; // No sign extend on uint!
+ // After the fast pass, the exponent is at least one (offset by one).
+ // Un-offset the exponent.
+ --exponent;
+ DCHECK_GE(exponent, 1);
+ DCHECK_LE(exponent, kUFloat16MaxExponent);
+ // Here we need to clear the exponent and set the hidden bit. We have already
+ // decremented the exponent, so when we subtract it, it leaves behind the
+ // hidden bit.
+ *result -= exponent << kUFloat16MantissaBits;
+ *result <<= exponent;
+ DCHECK_GE(*result,
+ static_cast<uint64_t>(1 << kUFloat16MantissaEffectiveBits));
+ DCHECK_LE(*result, kUFloat16MaxValue);
+ return true;
+}
+
+bool QuicDataReader::ReadStringPiece16(QuicStringPiece* result) {
+ // Read resultant length.
+ uint16_t result_len;
+ if (!ReadUInt16(&result_len)) {
+ // OnFailure() already called.
+ return false;
+ }
+
+ return ReadStringPiece(result, result_len);
+}
+
+bool QuicDataReader::ReadStringPiece(QuicStringPiece* result, size_t size) {
+ // Make sure that we have enough data to read.
+ if (!CanRead(size)) {
+ OnFailure();
+ return false;
+ }
+
+ // Set result.
+ *result = QuicStringPiece(data_ + pos_, size);
+
+ // Iterate.
+ pos_ += size;
+
+ return true;
+}
+
+bool QuicDataReader::ReadConnectionId(QuicConnectionId* connection_id,
+ uint8_t length) {
+ if (length > kQuicMaxConnectionIdLength) {
+ QUIC_BUG << "Attempted to read connection ID with length too high "
+ << static_cast<int>(length);
+ return false;
+ }
+ if (length == 0) {
+ connection_id->set_length(0);
+ return true;
+ }
+
+ const bool ok = ReadBytes(connection_id->mutable_data(), length);
+ if (ok) {
+ connection_id->set_length(length);
+ }
+ return ok;
+}
+
+bool QuicDataReader::ReadTag(uint32_t* tag) {
+ return ReadBytes(tag, sizeof(*tag));
+}
+
+QuicStringPiece QuicDataReader::ReadRemainingPayload() {
+ QuicStringPiece payload = PeekRemainingPayload();
+ pos_ = len_;
+ return payload;
+}
+
+QuicStringPiece QuicDataReader::PeekRemainingPayload() const {
+ return QuicStringPiece(data_ + pos_, len_ - pos_);
+}
+
+bool QuicDataReader::ReadBytes(void* result, size_t size) {
+ // Make sure that we have enough data to read.
+ if (!CanRead(size)) {
+ OnFailure();
+ return false;
+ }
+
+ // Read into result.
+ memcpy(result, data_ + pos_, size);
+
+ // Iterate.
+ pos_ += size;
+
+ return true;
+}
+
+bool QuicDataReader::IsDoneReading() const {
+ return len_ == pos_;
+}
+
+QuicVariableLengthIntegerLength QuicDataReader::PeekVarInt62Length() {
+ DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER);
+ const unsigned char* next =
+ reinterpret_cast<const unsigned char*>(data_ + pos_);
+ if (BytesRemaining() == 0) {
+ return VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ }
+ return static_cast<QuicVariableLengthIntegerLength>(
+ 1 << ((*next & 0b11000000) >> 6));
+}
+
+size_t QuicDataReader::BytesRemaining() const {
+ return len_ - pos_;
+}
+
+bool QuicDataReader::TruncateRemaining(size_t truncation_length) {
+ if (truncation_length > BytesRemaining()) {
+ return false;
+ }
+ len_ = pos_ + truncation_length;
+ return true;
+}
+
+bool QuicDataReader::CanRead(size_t bytes) const {
+ return bytes <= (len_ - pos_);
+}
+
+void QuicDataReader::OnFailure() {
+ // Set our iterator to the end of the buffer so that further reads fail
+ // immediately.
+ pos_ = len_;
+}
+
+uint8_t QuicDataReader::PeekByte() const {
+ if (pos_ >= len_) {
+ QUIC_BUG << "Reading is done, cannot peek next byte. Tried to read pos = "
+ << pos_ << " buffer length = " << len_;
+ return 0;
+ }
+ return data_[pos_];
+}
+
+// Read an IETF/QUIC formatted 62-bit Variable Length Integer.
+//
+// Performance notes
+//
+// Measurements and experiments showed that unrolling the four cases
+// like this and dereferencing next_ as we do (*(next_+n) --- and then
+// doing a single pos_+=x at the end) gains about 10% over making a
+// loop and dereferencing next_ such as *(next_++)
+//
+// Using a register for pos_ was not helpful.
+//
+// Branches are ordered to increase the likelihood of the first being
+// taken.
+//
+// Low-level optimization is useful here because this function will be
+// called frequently, leading to outsize benefits.
+bool QuicDataReader::ReadVarInt62(uint64_t* result) {
+ DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER);
+
+ size_t remaining = BytesRemaining();
+ const unsigned char* next =
+ reinterpret_cast<const unsigned char*>(data_ + pos_);
+ if (remaining != 0) {
+ switch (*next & 0xc0) {
+ case 0xc0:
+ // Leading 0b11...... is 8 byte encoding
+ if (remaining >= 8) {
+ *result = (static_cast<uint64_t>((*(next)) & 0x3f) << 56) +
+ (static_cast<uint64_t>(*(next + 1)) << 48) +
+ (static_cast<uint64_t>(*(next + 2)) << 40) +
+ (static_cast<uint64_t>(*(next + 3)) << 32) +
+ (static_cast<uint64_t>(*(next + 4)) << 24) +
+ (static_cast<uint64_t>(*(next + 5)) << 16) +
+ (static_cast<uint64_t>(*(next + 6)) << 8) +
+ (static_cast<uint64_t>(*(next + 7)) << 0);
+ pos_ += 8;
+ return true;
+ }
+ return false;
+
+ case 0x80:
+ // Leading 0b10...... is 4 byte encoding
+ if (remaining >= 4) {
+ *result = (((*(next)) & 0x3f) << 24) + (((*(next + 1)) << 16)) +
+ (((*(next + 2)) << 8)) + (((*(next + 3)) << 0));
+ pos_ += 4;
+ return true;
+ }
+ return false;
+
+ case 0x40:
+ // Leading 0b01...... is 2 byte encoding
+ if (remaining >= 2) {
+ *result = (((*(next)) & 0x3f) << 8) + (*(next + 1));
+ pos_ += 2;
+ return true;
+ }
+ return false;
+
+ case 0x00:
+ // Leading 0b00...... is 1 byte encoding
+ *result = (*next) & 0x3f;
+ pos_++;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuicDataReader::ReadVarIntStreamId(QuicStreamId* result) {
+ uint64_t temp_uint64;
+ // TODO(fkastenholz): We should disambiguate read-errors from
+ // value errors.
+ if (!this->ReadVarInt62(&temp_uint64)) {
+ return false;
+ }
+ if (temp_uint64 > kMaxQuicStreamId) {
+ return false;
+ }
+ *result = static_cast<QuicStreamId>(temp_uint64);
+ return true;
+}
+
+std::string QuicDataReader::DebugString() const {
+ return QuicStrCat(" { length: ", len_, ", position: ", pos_, " }");
+}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.h b/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.h
new file mode 100644
index 00000000000..3426e48efc6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_reader.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_DATA_READER_H_
+#define QUICHE_QUIC_CORE_QUIC_DATA_READER_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Used for reading QUIC data. Though there isn't really anything terribly
+// QUIC-specific here, it's a helper class that's useful when doing QUIC
+// framing.
+//
+// To use, simply construct a QuicDataReader using the underlying buffer that
+// you'd like to read fields from, then call one of the Read*() methods to
+// actually do some reading.
+//
+// This class keeps an internal iterator to keep track of what's already been
+// read and each successive Read*() call automatically increments said iterator
+// on success. On failure, internal state of the QuicDataReader should not be
+// trusted and it is up to the caller to throw away the failed instance and
+// handle the error as appropriate. None of the Read*() methods should ever be
+// called after failure, as they will also fail immediately.
+class QUIC_EXPORT_PRIVATE QuicDataReader {
+ public:
+ // Constructs a reader using NETWORK_BYTE_ORDER endianness.
+ // Caller must provide an underlying buffer to work on.
+ QuicDataReader(const char* data, const size_t len);
+ // Constructs a reader using the specified endianness.
+ // Caller must provide an underlying buffer to work on.
+ QuicDataReader(const char* data, const size_t len, Endianness endianness);
+ QuicDataReader(const QuicDataReader&) = delete;
+ QuicDataReader& operator=(const QuicDataReader&) = delete;
+
+ // Empty destructor.
+ ~QuicDataReader() {}
+
+ // Reads an 8/16/32/64-bit unsigned integer into the given output
+ // parameter. Forwards the internal iterator on success. Returns true on
+ // success, false otherwise.
+ bool ReadUInt8(uint8_t* result);
+ bool ReadUInt16(uint16_t* result);
+ bool ReadUInt32(uint32_t* result);
+ bool ReadUInt64(uint64_t* result);
+
+ // Set |result| to 0, then read |num_bytes| bytes in the correct byte order
+ // into least significant bytes of |result|.
+ bool ReadBytesToUInt64(size_t num_bytes, uint64_t* result);
+
+ // Reads a 16-bit unsigned float into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadUFloat16(uint64_t* result);
+
+ // Reads a string prefixed with 16-bit length into the given output parameter.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadStringPiece16(QuicStringPiece* result);
+
+ // Reads a given number of bytes into the given buffer. The buffer
+ // must be of adequate size.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadStringPiece(QuicStringPiece* result, size_t len);
+
+ // Reads connection ID into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadConnectionId(QuicConnectionId* connection_id, uint8_t length);
+
+ // Reads tag represented as 32-bit unsigned integer into given output
+ // parameter. Tags are in big endian on the wire (e.g., CHLO is
+ // 'C','H','L','O') and are read in byte order, so tags in memory are in big
+ // endian.
+ bool ReadTag(uint32_t* tag);
+
+ // Returns the remaining payload as a QuicStringPiece.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // Forwards the internal iterator.
+ QuicStringPiece ReadRemainingPayload();
+
+ // Returns the remaining payload as a QuicStringPiece.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // DOES NOT forward the internal iterator.
+ QuicStringPiece PeekRemainingPayload() const;
+
+ // Reads a given number of bytes into the given buffer. The buffer
+ // must be of adequate size.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadBytes(void* result, size_t size);
+
+ // Returns true if the entirety of the underlying buffer has been read via
+ // Read*() calls.
+ bool IsDoneReading() const;
+
+ // Returns the length in bytes of a variable length integer based on the next
+ // two bits available. Returns 1, 2, 4, or 8 on success, and 0 on failure.
+ QuicVariableLengthIntegerLength PeekVarInt62Length();
+
+ // Returns the number of bytes remaining to be read.
+ size_t BytesRemaining() const;
+
+ // Truncates the reader down by reducing its internal length.
+ // If called immediately after calling this, BytesRemaining will
+ // return |truncation_length|. If truncation_length is less than the
+ // current value of BytesRemaining, this does nothing and returns false.
+ bool TruncateRemaining(size_t truncation_length);
+
+ // Returns the next byte that to be read. Must not be called when there are no
+ // bytes to be read.
+ //
+ // DOES NOT forward the internal iterator.
+ uint8_t PeekByte() const;
+
+ void set_endianness(Endianness endianness) { endianness_ = endianness; }
+
+ // Read an IETF-encoded Variable Length Integer and place the result
+ // in |*result|.
+ // Returns true if it works, false if not. The only error is that
+ // there is not enough in the buffer to read the number.
+ // If there is an error, |*result| is not altered.
+ // Numbers are encoded per the rules in draft-ietf-quic-transport-10.txt
+ // and that the integers in the range 0 ... (2^62)-1.
+ bool ReadVarInt62(uint64_t* result);
+
+ // Convenience method that reads a StreamId.
+ // Atempts to read a Stream ID into |result| using ReadVarInt62 and
+ // returns false if there is a read error or if the value is
+ // greater than (2^32)-1.
+ bool ReadVarIntStreamId(QuicStreamId* result);
+
+ std::string DebugString() const;
+
+ private:
+ // Returns true if the underlying buffer has enough room to read the given
+ // amount of bytes.
+ bool CanRead(size_t bytes) const;
+
+ // To be called when a read fails for any reason.
+ void OnFailure();
+
+ // TODO(fkastenholz, b/73004262) change buffer_, et al, to be uint8_t, not
+ // char. The data buffer that we're reading from.
+ const char* data_;
+
+ // The length of the data buffer that we're reading from.
+ size_t len_;
+
+ // The location of the next read from our data buffer.
+ size_t pos_;
+
+ // The endianness to read integers and floating numbers.
+ Endianness endianness_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_DATA_READER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.cc
new file mode 100644
index 00000000000..7116853e77d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.cc
@@ -0,0 +1,356 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+QuicDataWriter::QuicDataWriter(size_t size, char* buffer)
+ : QuicDataWriter(size, buffer, NETWORK_BYTE_ORDER) {}
+
+QuicDataWriter::QuicDataWriter(size_t size, char* buffer, Endianness endianness)
+ : buffer_(buffer), capacity_(size), length_(0), endianness_(endianness) {}
+
+QuicDataWriter::~QuicDataWriter() {}
+
+char* QuicDataWriter::data() {
+ return buffer_;
+}
+
+bool QuicDataWriter::WriteUInt8(uint8_t value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicDataWriter::WriteUInt16(uint16_t value) {
+ if (endianness_ == NETWORK_BYTE_ORDER) {
+ value = QuicEndian::HostToNet16(value);
+ }
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicDataWriter::WriteUInt32(uint32_t value) {
+ if (endianness_ == NETWORK_BYTE_ORDER) {
+ value = QuicEndian::HostToNet32(value);
+ }
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicDataWriter::WriteUInt64(uint64_t value) {
+ if (endianness_ == NETWORK_BYTE_ORDER) {
+ value = QuicEndian::HostToNet64(value);
+ }
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicDataWriter::WriteBytesToUInt64(size_t num_bytes, uint64_t value) {
+ if (num_bytes > sizeof(value)) {
+ return false;
+ }
+ if (endianness_ == HOST_BYTE_ORDER) {
+ return WriteBytes(&value, num_bytes);
+ }
+
+ value = QuicEndian::HostToNet64(value);
+ return WriteBytes(reinterpret_cast<char*>(&value) + sizeof(value) - num_bytes,
+ num_bytes);
+}
+
+bool QuicDataWriter::WriteUFloat16(uint64_t value) {
+ uint16_t result;
+ if (value < (UINT64_C(1) << kUFloat16MantissaEffectiveBits)) {
+ // Fast path: either the value is denormalized, or has exponent zero.
+ // Both cases are represented by the value itself.
+ result = static_cast<uint16_t>(value);
+ } else if (value >= kUFloat16MaxValue) {
+ // Value is out of range; clamp it to the maximum representable.
+ result = std::numeric_limits<uint16_t>::max();
+ } else {
+ // The highest bit is between position 13 and 42 (zero-based), which
+ // corresponds to exponent 1-30. In the output, mantissa is from 0 to 10,
+ // hidden bit is 11 and exponent is 11 to 15. Shift the highest bit to 11
+ // and count the shifts.
+ uint16_t exponent = 0;
+ for (uint16_t offset = 16; offset > 0; offset /= 2) {
+ // Right-shift the value until the highest bit is in position 11.
+ // For offset of 16, 8, 4, 2 and 1 (binary search over 1-30),
+ // shift if the bit is at or above 11 + offset.
+ if (value >= (UINT64_C(1) << (kUFloat16MantissaBits + offset))) {
+ exponent += offset;
+ value >>= offset;
+ }
+ }
+
+ DCHECK_GE(exponent, 1);
+ DCHECK_LE(exponent, kUFloat16MaxExponent);
+ DCHECK_GE(value, UINT64_C(1) << kUFloat16MantissaBits);
+ DCHECK_LT(value, UINT64_C(1) << kUFloat16MantissaEffectiveBits);
+
+ // Hidden bit (position 11) is set. We should remove it and increment the
+ // exponent. Equivalently, we just add it to the exponent.
+ // This hides the bit.
+ result = static_cast<uint16_t>(value + (exponent << kUFloat16MantissaBits));
+ }
+
+ if (endianness_ == NETWORK_BYTE_ORDER) {
+ result = QuicEndian::HostToNet16(result);
+ }
+ return WriteBytes(&result, sizeof(result));
+}
+
+bool QuicDataWriter::WriteStringPiece16(QuicStringPiece val) {
+ if (val.size() > std::numeric_limits<uint16_t>::max()) {
+ return false;
+ }
+ if (!WriteUInt16(static_cast<uint16_t>(val.size()))) {
+ return false;
+ }
+ return WriteBytes(val.data(), val.size());
+}
+
+bool QuicDataWriter::WriteStringPiece(QuicStringPiece val) {
+ return WriteBytes(val.data(), val.size());
+}
+
+char* QuicDataWriter::BeginWrite(size_t length) {
+ if (length_ > capacity_) {
+ return nullptr;
+ }
+
+ if (capacity_ - length_ < length) {
+ return nullptr;
+ }
+
+#ifdef ARCH_CPU_64_BITS
+ DCHECK_LE(length, std::numeric_limits<uint32_t>::max());
+#endif
+
+ return buffer_ + length_;
+}
+
+bool QuicDataWriter::WriteBytes(const void* data, size_t data_len) {
+ char* dest = BeginWrite(data_len);
+ if (!dest) {
+ return false;
+ }
+
+ memcpy(dest, data, data_len);
+
+ length_ += data_len;
+ return true;
+}
+
+bool QuicDataWriter::WriteRepeatedByte(uint8_t byte, size_t count) {
+ char* dest = BeginWrite(count);
+ if (!dest) {
+ return false;
+ }
+
+ memset(dest, byte, count);
+
+ length_ += count;
+ return true;
+}
+
+void QuicDataWriter::WritePadding() {
+ DCHECK_LE(length_, capacity_);
+ if (length_ > capacity_) {
+ return;
+ }
+ memset(buffer_ + length_, 0x00, capacity_ - length_);
+ length_ = capacity_;
+}
+
+bool QuicDataWriter::WritePaddingBytes(size_t count) {
+ return WriteRepeatedByte(0x00, count);
+}
+
+bool QuicDataWriter::WriteConnectionId(QuicConnectionId connection_id) {
+ if (connection_id.IsEmpty()) {
+ return true;
+ }
+ return WriteBytes(connection_id.data(), connection_id.length());
+}
+
+bool QuicDataWriter::WriteTag(uint32_t tag) {
+ return WriteBytes(&tag, sizeof(tag));
+}
+
+bool QuicDataWriter::WriteRandomBytes(QuicRandom* random, size_t length) {
+ char* dest = BeginWrite(length);
+ if (!dest) {
+ return false;
+ }
+
+ random->RandBytes(dest, length);
+ length_ += length;
+ return true;
+}
+
+// Converts a uint64_t into an IETF/Quic formatted Variable Length
+// Integer. IETF Variable Length Integers have 62 significant bits, so
+// the value to write must be in the range of 0..(2^62)-1.
+//
+// Performance notes
+//
+// Measurements and experiments showed that unrolling the four cases
+// like this and dereferencing next_ as we do (*(next_+n)) gains about
+// 10% over making a loop and dereferencing it as *(next_++)
+//
+// Using a register for next didn't help.
+//
+// Branches are ordered to increase the likelihood of the first being
+// taken.
+//
+// Low-level optimization is useful here because this function will be
+// called frequently, leading to outsize benefits.
+bool QuicDataWriter::WriteVarInt62(uint64_t value) {
+ DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER);
+
+ size_t remaining = capacity_ - length_;
+ char* next = buffer_ + length_;
+
+ if ((value & kVarInt62ErrorMask) == 0) {
+ // We know the high 2 bits are 0 so |value| is legal.
+ // We can do the encoding.
+ if ((value & kVarInt62Mask8Bytes) != 0) {
+ // Someplace in the high-4 bytes is a 1-bit. Do an 8-byte
+ // encoding.
+ if (remaining >= 8) {
+ *(next + 0) = ((value >> 56) & 0x3f) + 0xc0;
+ *(next + 1) = (value >> 48) & 0xff;
+ *(next + 2) = (value >> 40) & 0xff;
+ *(next + 3) = (value >> 32) & 0xff;
+ *(next + 4) = (value >> 24) & 0xff;
+ *(next + 5) = (value >> 16) & 0xff;
+ *(next + 6) = (value >> 8) & 0xff;
+ *(next + 7) = value & 0xff;
+ length_ += 8;
+ return true;
+ }
+ return false;
+ }
+ // The high-order-4 bytes are all 0, check for a 1, 2, or 4-byte
+ // encoding
+ if ((value & kVarInt62Mask4Bytes) != 0) {
+ // The encoding will not fit into 2 bytes, Do a 4-byte
+ // encoding.
+ if (remaining >= 4) {
+ *(next + 0) = ((value >> 24) & 0x3f) + 0x80;
+ *(next + 1) = (value >> 16) & 0xff;
+ *(next + 2) = (value >> 8) & 0xff;
+ *(next + 3) = value & 0xff;
+ length_ += 4;
+ return true;
+ }
+ return false;
+ }
+ // The high-order bits are all 0. Check to see if the number
+ // can be encoded as one or two bytes. One byte encoding has
+ // only 6 significant bits (bits 0xffffffff ffffffc0 are all 0).
+ // Two byte encoding has more than 6, but 14 or less significant
+ // bits (bits 0xffffffff ffffc000 are 0 and 0x00000000 00003fc0
+ // are not 0)
+ if ((value & kVarInt62Mask2Bytes) != 0) {
+ // Do 2-byte encoding
+ if (remaining >= 2) {
+ *(next + 0) = ((value >> 8) & 0x3f) + 0x40;
+ *(next + 1) = (value)&0xff;
+ length_ += 2;
+ return true;
+ }
+ return false;
+ }
+ if (remaining >= 1) {
+ // Do 1-byte encoding
+ *next = (value & 0x3f);
+ length_ += 1;
+ return true;
+ }
+ return false;
+ }
+ // Can not encode, high 2 bits not 0
+ return false;
+}
+
+bool QuicDataWriter::WriteVarInt62(
+ uint64_t value,
+ QuicVariableLengthIntegerLength write_length) {
+ DCHECK_EQ(endianness_, NETWORK_BYTE_ORDER);
+
+ size_t remaining = capacity_ - length_;
+ if (remaining < write_length) {
+ return false;
+ }
+
+ const QuicVariableLengthIntegerLength min_length = GetVarInt62Len(value);
+ if (write_length < min_length) {
+ QUIC_BUG << "Cannot write value " << value << " with write_length "
+ << write_length;
+ return false;
+ }
+ if (write_length == min_length) {
+ return WriteVarInt62(value);
+ }
+
+ if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_2) {
+ return WriteUInt8(0b01000000) && WriteUInt8(value);
+ }
+ if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_4) {
+ return WriteUInt8(0b10000000) && WriteUInt8(0) && WriteUInt16(value);
+ }
+ if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_8) {
+ return WriteUInt8(0b11000000) && WriteUInt8(0) && WriteUInt16(0) &&
+ WriteUInt32(value);
+ }
+
+ QUIC_BUG << "Invalid write_length " << static_cast<int>(write_length);
+ return false;
+}
+
+// static
+QuicVariableLengthIntegerLength QuicDataWriter::GetVarInt62Len(uint64_t value) {
+ if ((value & kVarInt62ErrorMask) != 0) {
+ QUIC_BUG << "Attempted to encode a value, " << value
+ << ", that is too big for VarInt62";
+ return VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ }
+ if ((value & kVarInt62Mask8Bytes) != 0) {
+ return VARIABLE_LENGTH_INTEGER_LENGTH_8;
+ }
+ if ((value & kVarInt62Mask4Bytes) != 0) {
+ return VARIABLE_LENGTH_INTEGER_LENGTH_4;
+ }
+ if ((value & kVarInt62Mask2Bytes) != 0) {
+ return VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+ return VARIABLE_LENGTH_INTEGER_LENGTH_1;
+}
+
+bool QuicDataWriter::WriteStringPieceVarInt62(
+ const QuicStringPiece& string_piece) {
+ if (!WriteVarInt62(string_piece.size())) {
+ return false;
+ }
+ if (!string_piece.empty()) {
+ if (!WriteBytes(string_piece.data(), string_piece.size())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+std::string QuicDataWriter::DebugString() const {
+ return QuicStrCat(" { capacity: ", capacity_, ", length: ", length_, " }");
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.h b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.h
new file mode 100644
index 00000000000..bd1ded6c6da
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_DATA_WRITER_H_
+#define QUICHE_QUIC_CORE_QUIC_DATA_WRITER_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicRandom;
+
+// Maximum value that can be properly encoded using VarInt62 coding.
+const uint64_t kVarInt62MaxValue = UINT64_C(0x3fffffffffffffff);
+
+// VarInt62 encoding masks
+// If a uint64_t anded with a mask is not 0 then the value is encoded
+// using that length (or is too big, in the case of kVarInt62ErrorMask).
+// Values must be checked in order (error, 8-, 4-, and then 2- bytes)
+// and if none are non-0, the value is encoded in 1 byte.
+const uint64_t kVarInt62ErrorMask = UINT64_C(0xc000000000000000);
+const uint64_t kVarInt62Mask8Bytes = UINT64_C(0x3fffffffc0000000);
+const uint64_t kVarInt62Mask4Bytes = UINT64_C(0x000000003fffc000);
+const uint64_t kVarInt62Mask2Bytes = UINT64_C(0x0000000000003fc0);
+
+// This class provides facilities for packing QUIC data.
+//
+// The QuicDataWriter supports appending primitive values (int, string, etc)
+// to a frame instance. The internal memory buffer is exposed as the "data"
+// of the QuicDataWriter.
+class QUIC_EXPORT_PRIVATE QuicDataWriter {
+ public:
+ // Creates a QuicDataWriter where |buffer| is not owned
+ // using NETWORK_BYTE_ORDER endianness.
+ QuicDataWriter(size_t size, char* buffer);
+ // Creates a QuicDataWriter where |buffer| is not owned
+ // using the specified endianness.
+ QuicDataWriter(size_t size, char* buffer, Endianness endianness);
+ QuicDataWriter(const QuicDataWriter&) = delete;
+ QuicDataWriter& operator=(const QuicDataWriter&) = delete;
+
+ ~QuicDataWriter();
+
+ // Returns the size of the QuicDataWriter's data.
+ size_t length() const { return length_; }
+
+ // Retrieves the buffer from the QuicDataWriter without changing ownership.
+ char* data();
+
+ // Methods for adding to the payload. These values are appended to the end
+ // of the QuicDataWriter payload.
+
+ // Writes 8/16/32/64-bit unsigned integers.
+ bool WriteUInt8(uint8_t value);
+ bool WriteUInt16(uint16_t value);
+ bool WriteUInt32(uint32_t value);
+ bool WriteUInt64(uint64_t value);
+
+ // Write an unsigned-integer value per the IETF QUIC/Variable Length
+ // Integer encoding rules (see draft-ietf-quic-transport-08.txt).
+ // IETF Variable Length Integers have 62 significant bits, so the
+ // value to write must be in the range of 0...(2^62)-1. Returns
+ // false if the value is out of range or if there is no room in the
+ // buffer.
+ bool WriteVarInt62(uint64_t value);
+
+ // Same as WriteVarInt62(uint64_t), but forces an encoding size to write to.
+ // This is not as optimized as WriteVarInt62(uint64_t).
+ // Returns false if the value does not fit in the specified write_length or if
+ // there is no room in the buffer.
+ bool WriteVarInt62(uint64_t value,
+ QuicVariableLengthIntegerLength write_length);
+
+ // Writes a string piece as a consecutive length/content pair. The
+ // length is VarInt62 encoded.
+ bool WriteStringPieceVarInt62(const QuicStringPiece& string_piece);
+
+ // Utility function to return the number of bytes needed to encode
+ // the given value using IETF VarInt62 encoding. Returns the number
+ // of bytes required to encode the given integer or 0 if the value
+ // is too large to encode.
+ static QuicVariableLengthIntegerLength GetVarInt62Len(uint64_t value);
+
+ // Writes least significant |num_bytes| of a 64-bit unsigned integer in the
+ // correct byte order.
+ bool WriteBytesToUInt64(size_t num_bytes, uint64_t value);
+
+ // Write unsigned floating point corresponding to the value. Large values are
+ // clamped to the maximum representable (kUFloat16MaxValue). Values that can
+ // not be represented directly are rounded down.
+ bool WriteUFloat16(uint64_t value);
+ bool WriteStringPiece(QuicStringPiece val);
+ bool WriteStringPiece16(QuicStringPiece val);
+ bool WriteBytes(const void* data, size_t data_len);
+ bool WriteRepeatedByte(uint8_t byte, size_t count);
+ // Fills the remaining buffer with null characters.
+ void WritePadding();
+ // Write padding of |count| bytes.
+ bool WritePaddingBytes(size_t count);
+
+ // Write connection ID to the payload.
+ bool WriteConnectionId(QuicConnectionId connection_id);
+
+ // Write tag as a 32-bit unsigned integer to the payload. As tags are already
+ // converted to big endian (e.g., CHLO is 'C','H','L','O') in memory by TAG or
+ // MakeQuicTag and tags are written in byte order, so tags on the wire are
+ // in big endian.
+ bool WriteTag(uint32_t tag);
+
+ // Write |length| random bytes generated by |random|.
+ bool WriteRandomBytes(QuicRandom* random, size_t length);
+
+ size_t capacity() const { return capacity_; }
+
+ size_t remaining() const { return capacity_ - length_; }
+
+ std::string DebugString() const;
+
+ private:
+ // Returns the location that the data should be written at, or nullptr if
+ // there is not enough room. Call EndWrite with the returned offset and the
+ // given length to pad out for the next write.
+ char* BeginWrite(size_t length);
+
+ // TODO(fkastenholz, b/73004262) change buffer_, et al, to be uint8_t, not
+ // char.
+ char* buffer_;
+ size_t capacity_; // Allocation size of payload (or -1 if buffer is const).
+ size_t length_; // Current length of the buffer.
+
+ // The endianness to write integers and floating numbers.
+ Endianness endianness_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_DATA_WRITER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_data_writer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer_test.cc
new file mode 100644
index 00000000000..031620df4e8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_data_writer_test.cc
@@ -0,0 +1,1119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+char* AsChars(unsigned char* data) {
+ return reinterpret_cast<char*>(data);
+}
+
+struct TestParams {
+ explicit TestParams(Endianness endianness) : endianness(endianness) {}
+
+ Endianness endianness;
+};
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ for (Endianness endianness : {NETWORK_BYTE_ORDER, HOST_BYTE_ORDER}) {
+ params.push_back(TestParams(endianness));
+ }
+ return params;
+}
+
+class QuicDataWriterTest : public QuicTestWithParam<TestParams> {};
+
+INSTANTIATE_TEST_SUITE_P(QuicDataWriterTests,
+ QuicDataWriterTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicDataWriterTest, SanityCheckUFloat16Consts) {
+ // Check the arithmetic on the constants - otherwise the values below make
+ // no sense.
+ EXPECT_EQ(30, kUFloat16MaxExponent);
+ EXPECT_EQ(11, kUFloat16MantissaBits);
+ EXPECT_EQ(12, kUFloat16MantissaEffectiveBits);
+ EXPECT_EQ(UINT64_C(0x3FFC0000000), kUFloat16MaxValue);
+}
+
+TEST_P(QuicDataWriterTest, WriteUFloat16) {
+ struct TestCase {
+ uint64_t decoded;
+ uint16_t encoded;
+ };
+ TestCase test_cases[] = {
+ // Small numbers represent themselves.
+ {0, 0},
+ {1, 1},
+ {2, 2},
+ {3, 3},
+ {4, 4},
+ {5, 5},
+ {6, 6},
+ {7, 7},
+ {15, 15},
+ {31, 31},
+ {42, 42},
+ {123, 123},
+ {1234, 1234},
+ // Check transition through 2^11.
+ {2046, 2046},
+ {2047, 2047},
+ {2048, 2048},
+ {2049, 2049},
+ // Running out of mantissa at 2^12.
+ {4094, 4094},
+ {4095, 4095},
+ {4096, 4096},
+ {4097, 4096},
+ {4098, 4097},
+ {4099, 4097},
+ {4100, 4098},
+ {4101, 4098},
+ // Check transition through 2^13.
+ {8190, 6143},
+ {8191, 6143},
+ {8192, 6144},
+ {8193, 6144},
+ {8194, 6144},
+ {8195, 6144},
+ {8196, 6145},
+ {8197, 6145},
+ // Half-way through the exponents.
+ {0x7FF8000, 0x87FF},
+ {0x7FFFFFF, 0x87FF},
+ {0x8000000, 0x8800},
+ {0xFFF0000, 0x8FFF},
+ {0xFFFFFFF, 0x8FFF},
+ {0x10000000, 0x9000},
+ // Transition into the largest exponent.
+ {0x1FFFFFFFFFE, 0xF7FF},
+ {0x1FFFFFFFFFF, 0xF7FF},
+ {0x20000000000, 0xF800},
+ {0x20000000001, 0xF800},
+ {0x2003FFFFFFE, 0xF800},
+ {0x2003FFFFFFF, 0xF800},
+ {0x20040000000, 0xF801},
+ {0x20040000001, 0xF801},
+ // Transition into the max value and clamping.
+ {0x3FF80000000, 0xFFFE},
+ {0x3FFBFFFFFFF, 0xFFFE},
+ {0x3FFC0000000, 0xFFFF},
+ {0x3FFC0000001, 0xFFFF},
+ {0x3FFFFFFFFFF, 0xFFFF},
+ {0x40000000000, 0xFFFF},
+ {0xFFFFFFFFFFFFFFFF, 0xFFFF},
+ };
+ int num_test_cases = sizeof(test_cases) / sizeof(test_cases[0]);
+
+ for (int i = 0; i < num_test_cases; ++i) {
+ char buffer[2];
+ QuicDataWriter writer(2, buffer, GetParam().endianness);
+ EXPECT_TRUE(writer.WriteUFloat16(test_cases[i].decoded));
+ uint16_t result = *reinterpret_cast<uint16_t*>(writer.data());
+ if (GetParam().endianness == NETWORK_BYTE_ORDER) {
+ result = QuicEndian::HostToNet16(result);
+ }
+ EXPECT_EQ(test_cases[i].encoded, result);
+ }
+}
+
+TEST_P(QuicDataWriterTest, ReadUFloat16) {
+ struct TestCase {
+ uint64_t decoded;
+ uint16_t encoded;
+ };
+ TestCase test_cases[] = {
+ // There are fewer decoding test cases because encoding truncates, and
+ // decoding returns the smallest expansion.
+ // Small numbers represent themselves.
+ {0, 0},
+ {1, 1},
+ {2, 2},
+ {3, 3},
+ {4, 4},
+ {5, 5},
+ {6, 6},
+ {7, 7},
+ {15, 15},
+ {31, 31},
+ {42, 42},
+ {123, 123},
+ {1234, 1234},
+ // Check transition through 2^11.
+ {2046, 2046},
+ {2047, 2047},
+ {2048, 2048},
+ {2049, 2049},
+ // Running out of mantissa at 2^12.
+ {4094, 4094},
+ {4095, 4095},
+ {4096, 4096},
+ {4098, 4097},
+ {4100, 4098},
+ // Check transition through 2^13.
+ {8190, 6143},
+ {8192, 6144},
+ {8196, 6145},
+ // Half-way through the exponents.
+ {0x7FF8000, 0x87FF},
+ {0x8000000, 0x8800},
+ {0xFFF0000, 0x8FFF},
+ {0x10000000, 0x9000},
+ // Transition into the largest exponent.
+ {0x1FFE0000000, 0xF7FF},
+ {0x20000000000, 0xF800},
+ {0x20040000000, 0xF801},
+ // Transition into the max value.
+ {0x3FF80000000, 0xFFFE},
+ {0x3FFC0000000, 0xFFFF},
+ };
+ int num_test_cases = sizeof(test_cases) / sizeof(test_cases[0]);
+
+ for (int i = 0; i < num_test_cases; ++i) {
+ uint16_t encoded_ufloat = test_cases[i].encoded;
+ if (GetParam().endianness == NETWORK_BYTE_ORDER) {
+ encoded_ufloat = QuicEndian::HostToNet16(encoded_ufloat);
+ }
+ QuicDataReader reader(reinterpret_cast<char*>(&encoded_ufloat), 2,
+ GetParam().endianness);
+ uint64_t value;
+ EXPECT_TRUE(reader.ReadUFloat16(&value));
+ EXPECT_EQ(test_cases[i].decoded, value);
+ }
+}
+
+TEST_P(QuicDataWriterTest, RoundTripUFloat16) {
+ // Just test all 16-bit encoded values. 0 and max already tested above.
+ uint64_t previous_value = 0;
+ for (uint16_t i = 1; i < 0xFFFF; ++i) {
+ // Read the two bytes.
+ uint16_t read_number = i;
+ if (GetParam().endianness == NETWORK_BYTE_ORDER) {
+ read_number = QuicEndian::HostToNet16(read_number);
+ }
+ QuicDataReader reader(reinterpret_cast<char*>(&read_number), 2,
+ GetParam().endianness);
+ uint64_t value;
+ // All values must be decodable.
+ EXPECT_TRUE(reader.ReadUFloat16(&value));
+ // Check that small numbers represent themselves
+ if (i < 4097) {
+ EXPECT_EQ(i, value);
+ }
+ // Check there's monotonic growth.
+ EXPECT_LT(previous_value, value);
+ // Check that precision is within 0.5% away from the denormals.
+ if (i > 2000) {
+ EXPECT_GT(previous_value * 1005, value * 1000);
+ }
+ // Check we're always within the promised range.
+ EXPECT_LT(value, UINT64_C(0x3FFC0000000));
+ previous_value = value;
+ char buffer[6];
+ QuicDataWriter writer(6, buffer, GetParam().endianness);
+ EXPECT_TRUE(writer.WriteUFloat16(value - 1));
+ EXPECT_TRUE(writer.WriteUFloat16(value));
+ EXPECT_TRUE(writer.WriteUFloat16(value + 1));
+ // Check minimal decoding (previous decoding has previous encoding).
+ uint16_t encoded1 = *reinterpret_cast<uint16_t*>(writer.data());
+ uint16_t encoded2 = *reinterpret_cast<uint16_t*>(writer.data() + 2);
+ uint16_t encoded3 = *reinterpret_cast<uint16_t*>(writer.data() + 4);
+ if (GetParam().endianness == NETWORK_BYTE_ORDER) {
+ encoded1 = QuicEndian::NetToHost16(encoded1);
+ encoded2 = QuicEndian::NetToHost16(encoded2);
+ encoded3 = QuicEndian::NetToHost16(encoded3);
+ }
+ EXPECT_EQ(i - 1, encoded1);
+ // Check roundtrip.
+ EXPECT_EQ(i, encoded2);
+ // Check next decoding.
+ EXPECT_EQ(i < 4096 ? i + 1 : i, encoded3);
+ }
+}
+
+TEST_P(QuicDataWriterTest, WriteConnectionId) {
+ QuicConnectionId connection_id =
+ TestConnectionId(UINT64_C(0x0011223344556677));
+ char big_endian[] = {
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ };
+ EXPECT_EQ(connection_id.length(), QUIC_ARRAYSIZE(big_endian));
+ ASSERT_LE(connection_id.length(), kQuicMaxConnectionIdLength);
+ char buffer[kQuicMaxConnectionIdLength];
+ QuicDataWriter writer(connection_id.length(), buffer, GetParam().endianness);
+ EXPECT_TRUE(writer.WriteConnectionId(connection_id));
+ test::CompareCharArraysWithHexError("connection_id", buffer,
+ connection_id.length(), big_endian,
+ connection_id.length());
+
+ QuicConnectionId read_connection_id;
+ QuicDataReader reader(buffer, connection_id.length(), GetParam().endianness);
+ EXPECT_TRUE(
+ reader.ReadConnectionId(&read_connection_id, QUIC_ARRAYSIZE(big_endian)));
+ EXPECT_EQ(connection_id, read_connection_id);
+}
+
+TEST_P(QuicDataWriterTest, EmptyConnectionIds) {
+ QuicConnectionId empty_connection_id = EmptyQuicConnectionId();
+ char buffer[2];
+ QuicDataWriter writer(QUIC_ARRAYSIZE(buffer), buffer, GetParam().endianness);
+ EXPECT_TRUE(writer.WriteConnectionId(empty_connection_id));
+ EXPECT_TRUE(writer.WriteUInt8(1));
+ EXPECT_TRUE(writer.WriteConnectionId(empty_connection_id));
+ EXPECT_TRUE(writer.WriteUInt8(2));
+ EXPECT_TRUE(writer.WriteConnectionId(empty_connection_id));
+ EXPECT_FALSE(writer.WriteUInt8(3));
+
+ EXPECT_EQ(buffer[0], 1);
+ EXPECT_EQ(buffer[1], 2);
+
+ QuicConnectionId read_connection_id = TestConnectionId();
+ uint8_t read_byte;
+ QuicDataReader reader(buffer, QUIC_ARRAYSIZE(buffer), GetParam().endianness);
+ EXPECT_TRUE(reader.ReadConnectionId(&read_connection_id, 0));
+ EXPECT_EQ(read_connection_id, empty_connection_id);
+ EXPECT_TRUE(reader.ReadUInt8(&read_byte));
+ EXPECT_EQ(read_byte, 1);
+ // Reset read_connection_id to something else to verify that
+ // ReadConnectionId properly sets it back to empty.
+ read_connection_id = TestConnectionId();
+ EXPECT_TRUE(reader.ReadConnectionId(&read_connection_id, 0));
+ EXPECT_EQ(read_connection_id, empty_connection_id);
+ EXPECT_TRUE(reader.ReadUInt8(&read_byte));
+ EXPECT_EQ(read_byte, 2);
+ read_connection_id = TestConnectionId();
+ EXPECT_TRUE(reader.ReadConnectionId(&read_connection_id, 0));
+ EXPECT_EQ(read_connection_id, empty_connection_id);
+ EXPECT_FALSE(reader.ReadUInt8(&read_byte));
+}
+
+TEST_P(QuicDataWriterTest, WriteTag) {
+ char CHLO[] = {
+ 'C',
+ 'H',
+ 'L',
+ 'O',
+ };
+ const int kBufferLength = sizeof(QuicTag);
+ char buffer[kBufferLength];
+ QuicDataWriter writer(kBufferLength, buffer, GetParam().endianness);
+ writer.WriteTag(kCHLO);
+ test::CompareCharArraysWithHexError("CHLO", buffer, kBufferLength, CHLO,
+ kBufferLength);
+
+ QuicTag read_chlo;
+ QuicDataReader reader(buffer, kBufferLength, GetParam().endianness);
+ reader.ReadTag(&read_chlo);
+ EXPECT_EQ(kCHLO, read_chlo);
+}
+
+TEST_P(QuicDataWriterTest, Write16BitUnsignedIntegers) {
+ char little_endian16[] = {0x22, 0x11};
+ char big_endian16[] = {0x11, 0x22};
+ char buffer16[2];
+ {
+ uint16_t in_memory16 = 0x1122;
+ QuicDataWriter writer(2, buffer16, GetParam().endianness);
+ writer.WriteUInt16(in_memory16);
+ test::CompareCharArraysWithHexError(
+ "uint16_t", buffer16, 2,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian16
+ : little_endian16,
+ 2);
+
+ uint16_t read_number16;
+ QuicDataReader reader(buffer16, 2, GetParam().endianness);
+ reader.ReadUInt16(&read_number16);
+ EXPECT_EQ(in_memory16, read_number16);
+ }
+
+ {
+ uint64_t in_memory16 = 0x0000000000001122;
+ QuicDataWriter writer(2, buffer16, GetParam().endianness);
+ writer.WriteBytesToUInt64(2, in_memory16);
+ test::CompareCharArraysWithHexError(
+ "uint16_t", buffer16, 2,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian16
+ : little_endian16,
+ 2);
+
+ uint64_t read_number16;
+ QuicDataReader reader(buffer16, 2, GetParam().endianness);
+ reader.ReadBytesToUInt64(2, &read_number16);
+ EXPECT_EQ(in_memory16, read_number16);
+ }
+}
+
+TEST_P(QuicDataWriterTest, Write24BitUnsignedIntegers) {
+ char little_endian24[] = {0x33, 0x22, 0x11};
+ char big_endian24[] = {0x11, 0x22, 0x33};
+ char buffer24[3];
+ uint64_t in_memory24 = 0x0000000000112233;
+ QuicDataWriter writer(3, buffer24, GetParam().endianness);
+ writer.WriteBytesToUInt64(3, in_memory24);
+ test::CompareCharArraysWithHexError(
+ "uint24", buffer24, 3,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian24
+ : little_endian24,
+ 3);
+
+ uint64_t read_number24;
+ QuicDataReader reader(buffer24, 3, GetParam().endianness);
+ reader.ReadBytesToUInt64(3, &read_number24);
+ EXPECT_EQ(in_memory24, read_number24);
+}
+
+TEST_P(QuicDataWriterTest, Write32BitUnsignedIntegers) {
+ char little_endian32[] = {0x44, 0x33, 0x22, 0x11};
+ char big_endian32[] = {0x11, 0x22, 0x33, 0x44};
+ char buffer32[4];
+ {
+ uint32_t in_memory32 = 0x11223344;
+ QuicDataWriter writer(4, buffer32, GetParam().endianness);
+ writer.WriteUInt32(in_memory32);
+ test::CompareCharArraysWithHexError(
+ "uint32_t", buffer32, 4,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian32
+ : little_endian32,
+ 4);
+
+ uint32_t read_number32;
+ QuicDataReader reader(buffer32, 4, GetParam().endianness);
+ reader.ReadUInt32(&read_number32);
+ EXPECT_EQ(in_memory32, read_number32);
+ }
+
+ {
+ uint64_t in_memory32 = 0x11223344;
+ QuicDataWriter writer(4, buffer32, GetParam().endianness);
+ writer.WriteBytesToUInt64(4, in_memory32);
+ test::CompareCharArraysWithHexError(
+ "uint32_t", buffer32, 4,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian32
+ : little_endian32,
+ 4);
+
+ uint64_t read_number32;
+ QuicDataReader reader(buffer32, 4, GetParam().endianness);
+ reader.ReadBytesToUInt64(4, &read_number32);
+ EXPECT_EQ(in_memory32, read_number32);
+ }
+}
+
+TEST_P(QuicDataWriterTest, Write40BitUnsignedIntegers) {
+ uint64_t in_memory40 = 0x0000001122334455;
+ char little_endian40[] = {0x55, 0x44, 0x33, 0x22, 0x11};
+ char big_endian40[] = {0x11, 0x22, 0x33, 0x44, 0x55};
+ char buffer40[5];
+ QuicDataWriter writer(5, buffer40, GetParam().endianness);
+ writer.WriteBytesToUInt64(5, in_memory40);
+ test::CompareCharArraysWithHexError(
+ "uint40", buffer40, 5,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian40
+ : little_endian40,
+ 5);
+
+ uint64_t read_number40;
+ QuicDataReader reader(buffer40, 5, GetParam().endianness);
+ reader.ReadBytesToUInt64(5, &read_number40);
+ EXPECT_EQ(in_memory40, read_number40);
+}
+
+TEST_P(QuicDataWriterTest, Write48BitUnsignedIntegers) {
+ uint64_t in_memory48 = 0x0000112233445566;
+ char little_endian48[] = {0x66, 0x55, 0x44, 0x33, 0x22, 0x11};
+ char big_endian48[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char buffer48[6];
+ QuicDataWriter writer(6, buffer48, GetParam().endianness);
+ writer.WriteBytesToUInt64(6, in_memory48);
+ test::CompareCharArraysWithHexError(
+ "uint48", buffer48, 6,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian48
+ : little_endian48,
+ 6);
+
+ uint64_t read_number48;
+ QuicDataReader reader(buffer48, 6, GetParam().endianness);
+ reader.ReadBytesToUInt64(6., &read_number48);
+ EXPECT_EQ(in_memory48, read_number48);
+}
+
+TEST_P(QuicDataWriterTest, Write56BitUnsignedIntegers) {
+ uint64_t in_memory56 = 0x0011223344556677;
+ char little_endian56[] = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11};
+ char big_endian56[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ char buffer56[7];
+ QuicDataWriter writer(7, buffer56, GetParam().endianness);
+ writer.WriteBytesToUInt64(7, in_memory56);
+ test::CompareCharArraysWithHexError(
+ "uint56", buffer56, 7,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? big_endian56
+ : little_endian56,
+ 7);
+
+ uint64_t read_number56;
+ QuicDataReader reader(buffer56, 7, GetParam().endianness);
+ reader.ReadBytesToUInt64(7, &read_number56);
+ EXPECT_EQ(in_memory56, read_number56);
+}
+
+TEST_P(QuicDataWriterTest, Write64BitUnsignedIntegers) {
+ uint64_t in_memory64 = 0x1122334455667788;
+ unsigned char little_endian64[] = {0x88, 0x77, 0x66, 0x55,
+ 0x44, 0x33, 0x22, 0x11};
+ unsigned char big_endian64[] = {0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88};
+ char buffer64[8];
+ QuicDataWriter writer(8, buffer64, GetParam().endianness);
+ writer.WriteBytesToUInt64(8, in_memory64);
+ test::CompareCharArraysWithHexError(
+ "uint64_t", buffer64, 8,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? AsChars(big_endian64)
+ : AsChars(little_endian64),
+ 8);
+
+ uint64_t read_number64;
+ QuicDataReader reader(buffer64, 8, GetParam().endianness);
+ reader.ReadBytesToUInt64(8, &read_number64);
+ EXPECT_EQ(in_memory64, read_number64);
+
+ QuicDataWriter writer2(8, buffer64, GetParam().endianness);
+ writer2.WriteUInt64(in_memory64);
+ test::CompareCharArraysWithHexError(
+ "uint64_t", buffer64, 8,
+ GetParam().endianness == NETWORK_BYTE_ORDER ? AsChars(big_endian64)
+ : AsChars(little_endian64),
+ 8);
+ read_number64 = 0u;
+ QuicDataReader reader2(buffer64, 8, GetParam().endianness);
+ reader2.ReadUInt64(&read_number64);
+ EXPECT_EQ(in_memory64, read_number64);
+}
+
+TEST_P(QuicDataWriterTest, WriteIntegers) {
+ char buf[43];
+ uint8_t i8 = 0x01;
+ uint16_t i16 = 0x0123;
+ uint32_t i32 = 0x01234567;
+ uint64_t i64 = 0x0123456789ABCDEF;
+ QuicDataWriter writer(46, buf, GetParam().endianness);
+ for (size_t i = 0; i < 10; ++i) {
+ switch (i) {
+ case 0u:
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 1u:
+ EXPECT_TRUE(writer.WriteUInt8(i8));
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 2u:
+ EXPECT_TRUE(writer.WriteUInt16(i16));
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 3u:
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 4u:
+ EXPECT_TRUE(writer.WriteUInt32(i32));
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ case 5u:
+ case 6u:
+ case 7u:
+ case 8u:
+ EXPECT_TRUE(writer.WriteBytesToUInt64(i, i64));
+ break;
+ default:
+ EXPECT_FALSE(writer.WriteBytesToUInt64(i, i64));
+ }
+ }
+
+ QuicDataReader reader(buf, 46, GetParam().endianness);
+ for (size_t i = 0; i < 10; ++i) {
+ uint8_t read8;
+ uint16_t read16;
+ uint32_t read32;
+ uint64_t read64;
+ switch (i) {
+ case 0u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0u, read64);
+ break;
+ case 1u:
+ EXPECT_TRUE(reader.ReadUInt8(&read8));
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(i8, read8);
+ EXPECT_EQ(0xEFu, read64);
+ break;
+ case 2u:
+ EXPECT_TRUE(reader.ReadUInt16(&read16));
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(i16, read16);
+ EXPECT_EQ(0xCDEFu, read64);
+ break;
+ case 3u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0xABCDEFu, read64);
+ break;
+ case 4u:
+ EXPECT_TRUE(reader.ReadUInt32(&read32));
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(i32, read32);
+ EXPECT_EQ(0x89ABCDEFu, read64);
+ break;
+ case 5u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0x6789ABCDEFu, read64);
+ break;
+ case 6u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0x456789ABCDEFu, read64);
+ break;
+ case 7u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0x23456789ABCDEFu, read64);
+ break;
+ case 8u:
+ EXPECT_TRUE(reader.ReadBytesToUInt64(i, &read64));
+ EXPECT_EQ(0x0123456789ABCDEFu, read64);
+ break;
+ default:
+ EXPECT_FALSE(reader.ReadBytesToUInt64(i, &read64));
+ }
+ }
+}
+
+TEST_P(QuicDataWriterTest, WriteBytes) {
+ char bytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+ char buf[QUIC_ARRAYSIZE(bytes)];
+ QuicDataWriter writer(QUIC_ARRAYSIZE(buf), buf, GetParam().endianness);
+ EXPECT_TRUE(writer.WriteBytes(bytes, QUIC_ARRAYSIZE(bytes)));
+ for (unsigned int i = 0; i < QUIC_ARRAYSIZE(bytes); ++i) {
+ EXPECT_EQ(bytes[i], buf[i]);
+ }
+}
+
+const int kVarIntBufferLength = 1024;
+
+// Encodes and then decodes a specified value, checks that the
+// value that was encoded is the same as the decoded value, the length
+// is correct, and that after decoding, all data in the buffer has
+// been consumed..
+// Returns true if everything works, false if not.
+bool EncodeDecodeValue(uint64_t value_in, char* buffer, size_t size_of_buffer) {
+ // Init the buffer to all 0, just for cleanliness. Makes for better
+ // output if, in debugging, we need to dump out the buffer.
+ memset(buffer, 0, size_of_buffer);
+ // make a writer. Note that for IETF encoding
+ // we do not care about endianness... It's always big-endian,
+ // but the c'tor expects to be told what endianness is in force...
+ QuicDataWriter writer(size_of_buffer, buffer, Endianness::NETWORK_BYTE_ORDER);
+
+ // Try to write the value.
+ if (writer.WriteVarInt62(value_in) != true) {
+ return false;
+ }
+ // Look at the value we encoded. Determine how much should have been
+ // used based on the value, and then check the state of the writer
+ // to see that it matches.
+ size_t expected_length = 0;
+ if (value_in <= 0x3f) {
+ expected_length = 1;
+ } else if (value_in <= 0x3fff) {
+ expected_length = 2;
+ } else if (value_in <= 0x3fffffff) {
+ expected_length = 4;
+ } else {
+ expected_length = 8;
+ }
+ if (writer.length() != expected_length) {
+ return false;
+ }
+
+ // set up a reader, just the length we've used, no more, no less.
+ QuicDataReader reader(buffer, expected_length,
+ Endianness::NETWORK_BYTE_ORDER);
+ uint64_t value_out;
+
+ if (reader.ReadVarInt62(&value_out) == false) {
+ return false;
+ }
+ if (value_in != value_out) {
+ return false;
+ }
+ // We only write one value so there had better be nothing left to read
+ return reader.IsDoneReading();
+}
+
+// Test that 8-byte-encoded Variable Length Integers are properly laid
+// out in the buffer.
+TEST_P(QuicDataWriterTest, VarInt8Layout) {
+ char buffer[1024];
+
+ // Check that the layout of bytes in the buffer is correct. Bytes
+ // are always encoded big endian...
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x3142f3e4d5c6b7a8)));
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 0)),
+ (0x31 + 0xc0)); // 0xc0 for encoding
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 1)), 0x42);
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 2)), 0xf3);
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 3)), 0xe4);
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 4)), 0xd5);
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 5)), 0xc6);
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 6)), 0xb7);
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 7)), 0xa8);
+}
+
+// Test that 4-byte-encoded Variable Length Integers are properly laid
+// out in the buffer.
+TEST_P(QuicDataWriterTest, VarInt4Layout) {
+ char buffer[1024];
+
+ // Check that the layout of bytes in the buffer is correct. Bytes
+ // are always encoded big endian...
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer.WriteVarInt62(0x3243f4e5));
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 0)),
+ (0x32 + 0x80)); // 0x80 for encoding
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 1)), 0x43);
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 2)), 0xf4);
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 3)), 0xe5);
+}
+
+// Test that 2-byte-encoded Variable Length Integers are properly laid
+// out in the buffer.
+TEST_P(QuicDataWriterTest, VarInt2Layout) {
+ char buffer[1024];
+
+ // Check that the layout of bytes in the buffer is correct. Bytes
+ // are always encoded big endian...
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer.WriteVarInt62(0x3647));
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 0)),
+ (0x36 + 0x40)); // 0x40 for encoding
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 1)), 0x47);
+}
+
+// Test that 1-byte-encoded Variable Length Integers are properly laid
+// out in the buffer.
+TEST_P(QuicDataWriterTest, VarInt1Layout) {
+ char buffer[1024];
+
+ // Check that the layout of bytes in the buffer
+ // is correct. Bytes are always encoded big endian...
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer.WriteVarInt62(0x3f));
+ EXPECT_EQ(static_cast<unsigned char>(*(writer.data() + 0)), 0x3f);
+}
+
+// Test certain, targeted, values that are expected to succeed:
+// 0, 1,
+// 0x3e, 0x3f, 0x40, 0x41 (around the 1-2 byte transitions)
+// 0x3ffe, 0x3fff, 0x4000, 0x4001 (the 2-4 byte transition)
+// 0x3ffffffe, 0x3fffffff, 0x40000000, 0x40000001 (the 4-8 byte
+// transition)
+// 0x3ffffffffffffffe, 0x3fffffffffffffff, (the highest valid values)
+// 0xfe, 0xff, 0x100, 0x101,
+// 0xfffe, 0xffff, 0x10000, 0x10001,
+// 0xfffffe, 0xffffff, 0x1000000, 0x1000001,
+// 0xfffffffe, 0xffffffff, 0x100000000, 0x100000001,
+// 0xfffffffffe, 0xffffffffff, 0x10000000000, 0x10000000001,
+// 0xfffffffffffe, 0xffffffffffff, 0x1000000000000, 0x1000000000001,
+// 0xfffffffffffffe, 0xffffffffffffff, 0x100000000000000, 0x100000000000001,
+TEST_P(QuicDataWriterTest, VarIntGoodTargetedValues) {
+ char buffer[kVarIntBufferLength];
+ uint64_t passing_values[] = {
+ 0,
+ 1,
+ 0x3e,
+ 0x3f,
+ 0x40,
+ 0x41,
+ 0x3ffe,
+ 0x3fff,
+ 0x4000,
+ 0x4001,
+ 0x3ffffffe,
+ 0x3fffffff,
+ 0x40000000,
+ 0x40000001,
+ 0x3ffffffffffffffe,
+ 0x3fffffffffffffff,
+ 0xfe,
+ 0xff,
+ 0x100,
+ 0x101,
+ 0xfffe,
+ 0xffff,
+ 0x10000,
+ 0x10001,
+ 0xfffffe,
+ 0xffffff,
+ 0x1000000,
+ 0x1000001,
+ 0xfffffffe,
+ 0xffffffff,
+ 0x100000000,
+ 0x100000001,
+ 0xfffffffffe,
+ 0xffffffffff,
+ 0x10000000000,
+ 0x10000000001,
+ 0xfffffffffffe,
+ 0xffffffffffff,
+ 0x1000000000000,
+ 0x1000000000001,
+ 0xfffffffffffffe,
+ 0xffffffffffffff,
+ 0x100000000000000,
+ 0x100000000000001,
+ };
+ for (uint64_t test_val : passing_values) {
+ EXPECT_TRUE(
+ EncodeDecodeValue(test_val, static_cast<char*>(buffer), sizeof(buffer)))
+ << " encode/decode of " << test_val << " failed";
+ }
+}
+//
+// Test certain, targeted, values where failure is expected (the
+// values are invalid w.r.t. IETF VarInt encoding):
+// 0x4000000000000000, 0x4000000000000001, ( Just above max allowed value)
+// 0xfffffffffffffffe, 0xffffffffffffffff, (should fail)
+TEST_P(QuicDataWriterTest, VarIntBadTargetedValues) {
+ char buffer[kVarIntBufferLength];
+ uint64_t failing_values[] = {
+ 0x4000000000000000,
+ 0x4000000000000001,
+ 0xfffffffffffffffe,
+ 0xffffffffffffffff,
+ };
+ for (uint64_t test_val : failing_values) {
+ EXPECT_FALSE(
+ EncodeDecodeValue(test_val, static_cast<char*>(buffer), sizeof(buffer)))
+ << " encode/decode of " << test_val << " succeeded, but was an "
+ << "invalid value";
+ }
+}
+
+// Following tests all try to fill the buffer with multiple values,
+// go one value more than the buffer can accommodate, then read
+// the successfully encoded values, and try to read the unsuccessfully
+// encoded value. The following is the number of values to encode.
+const int kMultiVarCount = 1000;
+
+// Test writing & reading multiple 8-byte-encoded varints
+TEST_P(QuicDataWriterTest, MultiVarInt8) {
+ uint64_t test_val;
+ char buffer[8 * kMultiVarCount];
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ // Put N values into the buffer. Adding i to the value ensures that
+ // each value is different so we can detect if we overwrite values,
+ // or read the same value over and over.
+ for (int i = 0; i < kMultiVarCount; i++) {
+ EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x3142f3e4d5c6b7a8) + i));
+ }
+ EXPECT_EQ(writer.length(), 8u * kMultiVarCount);
+
+ // N+1st should fail, the buffer is full.
+ EXPECT_FALSE(writer.WriteVarInt62(UINT64_C(0x3142f3e4d5c6b7a8)));
+
+ // Now we should be able to read out the N values that were
+ // successfully encoded.
+ QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER);
+ for (int i = 0; i < kMultiVarCount; i++) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, (UINT64_C(0x3142f3e4d5c6b7a8) + i));
+ }
+ // And the N+1st should fail.
+ EXPECT_FALSE(reader.ReadVarInt62(&test_val));
+}
+
+// Test writing & reading multiple 4-byte-encoded varints
+TEST_P(QuicDataWriterTest, MultiVarInt4) {
+ uint64_t test_val;
+ char buffer[4 * kMultiVarCount];
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ // Put N values into the buffer. Adding i to the value ensures that
+ // each value is different so we can detect if we overwrite values,
+ // or read the same value over and over.
+ for (int i = 0; i < kMultiVarCount; i++) {
+ EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x3142f3e4) + i));
+ }
+ EXPECT_EQ(writer.length(), 4u * kMultiVarCount);
+
+ // N+1st should fail, the buffer is full.
+ EXPECT_FALSE(writer.WriteVarInt62(UINT64_C(0x3142f3e4)));
+
+ // Now we should be able to read out the N values that were
+ // successfully encoded.
+ QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER);
+ for (int i = 0; i < kMultiVarCount; i++) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, (UINT64_C(0x3142f3e4) + i));
+ }
+ // And the N+1st should fail.
+ EXPECT_FALSE(reader.ReadVarInt62(&test_val));
+}
+
+// Test writing & reading multiple 2-byte-encoded varints
+TEST_P(QuicDataWriterTest, MultiVarInt2) {
+ uint64_t test_val;
+ char buffer[2 * kMultiVarCount];
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ // Put N values into the buffer. Adding i to the value ensures that
+ // each value is different so we can detect if we overwrite values,
+ // or read the same value over and over.
+ for (int i = 0; i < kMultiVarCount; i++) {
+ EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x3142) + i));
+ }
+ EXPECT_EQ(writer.length(), 2u * kMultiVarCount);
+
+ // N+1st should fail, the buffer is full.
+ EXPECT_FALSE(writer.WriteVarInt62(UINT64_C(0x3142)));
+
+ // Now we should be able to read out the N values that were
+ // successfully encoded.
+ QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER);
+ for (int i = 0; i < kMultiVarCount; i++) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, (UINT64_C(0x3142) + i));
+ }
+ // And the N+1st should fail.
+ EXPECT_FALSE(reader.ReadVarInt62(&test_val));
+}
+
+// Test writing & reading multiple 1-byte-encoded varints
+TEST_P(QuicDataWriterTest, MultiVarInt1) {
+ uint64_t test_val;
+ char buffer[1 * kMultiVarCount];
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ // Put N values into the buffer. Adding i to the value ensures that
+ // each value is different so we can detect if we overwrite values,
+ // or read the same value over and over. &0xf ensures we do not
+ // overflow the max value for single-byte encoding.
+ for (int i = 0; i < kMultiVarCount; i++) {
+ EXPECT_TRUE(writer.WriteVarInt62(UINT64_C(0x30) + (i & 0xf)));
+ }
+ EXPECT_EQ(writer.length(), 1u * kMultiVarCount);
+
+ // N+1st should fail, the buffer is full.
+ EXPECT_FALSE(writer.WriteVarInt62(UINT64_C(0x31)));
+
+ // Now we should be able to read out the N values that were
+ // successfully encoded.
+ QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER);
+ for (int i = 0; i < kMultiVarCount; i++) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, (UINT64_C(0x30) + (i & 0xf)));
+ }
+ // And the N+1st should fail.
+ EXPECT_FALSE(reader.ReadVarInt62(&test_val));
+}
+
+// Test writing varints with a forced length.
+TEST_P(QuicDataWriterTest, VarIntFixedLength) {
+ char buffer[90];
+ memset(buffer, 0, sizeof(buffer));
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+
+ writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_1);
+ writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_2);
+ writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+ writer.WriteVarInt62(1, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+ writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_1);
+ writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_2);
+ writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+ writer.WriteVarInt62(63, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+ writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_2);
+ writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+ writer.WriteVarInt62(64, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+ writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_2);
+ writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+ writer.WriteVarInt62(16383, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+ writer.WriteVarInt62(16384, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+ writer.WriteVarInt62(16384, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+ writer.WriteVarInt62(1073741823, VARIABLE_LENGTH_INTEGER_LENGTH_4);
+ writer.WriteVarInt62(1073741823, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+ writer.WriteVarInt62(1073741824, VARIABLE_LENGTH_INTEGER_LENGTH_8);
+
+ QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER);
+
+ uint64_t test_val = 0;
+ for (int i = 0; i < 4; ++i) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, 1u);
+ }
+ for (int i = 0; i < 4; ++i) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, 63u);
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, 64u);
+ }
+ for (int i = 0; i < 3; ++i) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, 16383u);
+ }
+
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, 16384u);
+ }
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, 1073741823u);
+ }
+
+ EXPECT_TRUE(reader.ReadVarInt62(&test_val));
+ EXPECT_EQ(test_val, 1073741824u);
+
+ // We are at the end of the buffer so this should fail.
+ EXPECT_FALSE(reader.ReadVarInt62(&test_val));
+}
+
+// Test encoding/decoding stream-id values.
+void EncodeDecodeStreamId(uint64_t value_in, bool expected_decode_result) {
+ char buffer[1 * kMultiVarCount];
+ memset(buffer, 0, sizeof(buffer));
+
+ // Encode the given Stream ID.
+ QuicDataWriter writer(sizeof(buffer), static_cast<char*>(buffer),
+ Endianness::NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer.WriteVarInt62(value_in));
+
+ QuicDataReader reader(buffer, sizeof(buffer), Endianness::NETWORK_BYTE_ORDER);
+ QuicStreamId received_stream_id;
+ bool read_result = reader.ReadVarIntStreamId(&received_stream_id);
+ EXPECT_EQ(expected_decode_result, read_result);
+ if (read_result) {
+ EXPECT_EQ(value_in, received_stream_id);
+ }
+}
+
+// Test writing & reading stream-ids of various value.
+TEST_P(QuicDataWriterTest, StreamId1) {
+ // Check a 1-byte QuicStreamId, should work
+ EncodeDecodeStreamId(UINT64_C(0x15), true);
+
+ // Check a 2-byte QuicStream ID. It should work.
+ EncodeDecodeStreamId(UINT64_C(0x1567), true);
+
+ // Check a QuicStreamId that requires 4 bytes of encoding
+ // This should work.
+ EncodeDecodeStreamId(UINT64_C(0x34567890), true);
+
+ // Check a QuicStreamId that requires 8 bytes of encoding
+ // but whose value is in the acceptable range.
+ // This should work.
+ EncodeDecodeStreamId(UINT64_C(0xf4567890), true);
+
+ // Check QuicStreamIds that require 8 bytes of encoding
+ // and whose value is not acceptable.
+ // This should fail.
+ EncodeDecodeStreamId(UINT64_C(0x100000000), false);
+ EncodeDecodeStreamId(UINT64_C(0x3fffffffffffffff), false);
+}
+
+TEST_P(QuicDataWriterTest, WriteRandomBytes) {
+ char buffer[20];
+ char expected[20];
+ for (size_t i = 0; i < 20; ++i) {
+ expected[i] = 'r';
+ }
+ MockRandom random;
+ QuicDataWriter writer(20, buffer, GetParam().endianness);
+ EXPECT_FALSE(writer.WriteRandomBytes(&random, 30));
+
+ EXPECT_TRUE(writer.WriteRandomBytes(&random, 20));
+ test::CompareCharArraysWithHexError("random", buffer, 20, expected, 20);
+}
+
+TEST_P(QuicDataWriterTest, PeekVarInt62Length) {
+ // In range [0, 63], variable length should be 1 byte.
+ char buffer[20];
+ QuicDataWriter writer(20, buffer, NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer.WriteVarInt62(50));
+ QuicDataReader reader(buffer, 20, NETWORK_BYTE_ORDER);
+ EXPECT_EQ(1, reader.PeekVarInt62Length());
+ // In range (63-16383], variable length should be 2 byte2.
+ char buffer2[20];
+ QuicDataWriter writer2(20, buffer2, NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer2.WriteVarInt62(100));
+ QuicDataReader reader2(buffer2, 20, NETWORK_BYTE_ORDER);
+ EXPECT_EQ(2, reader2.PeekVarInt62Length());
+ // In range (16383, 1073741823], variable length should be 4 bytes.
+ char buffer3[20];
+ QuicDataWriter writer3(20, buffer3, NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer3.WriteVarInt62(20000));
+ QuicDataReader reader3(buffer3, 20, NETWORK_BYTE_ORDER);
+ EXPECT_EQ(4, reader3.PeekVarInt62Length());
+ // In range (1073741823, 4611686018427387903], variable length should be 8
+ // bytes.
+ char buffer4[20];
+ QuicDataWriter writer4(20, buffer4, NETWORK_BYTE_ORDER);
+ EXPECT_TRUE(writer4.WriteVarInt62(2000000000));
+ QuicDataReader reader4(buffer4, 20, NETWORK_BYTE_ORDER);
+ EXPECT_EQ(8, reader4.PeekVarInt62Length());
+}
+
+TEST_P(QuicDataWriterTest, InvalidConnectionIdLengthRead) {
+ static const uint8_t bad_connection_id_length = 19;
+ static_assert(bad_connection_id_length > kQuicMaxConnectionIdLength,
+ "bad lengths");
+ char buffer[20] = {};
+ QuicDataReader reader(buffer, 20);
+ QuicConnectionId connection_id;
+ bool ok;
+ EXPECT_QUIC_BUG(
+ ok = reader.ReadConnectionId(&connection_id, bad_connection_id_length),
+ QuicStrCat("Attempted to read connection ID with length too high ",
+ static_cast<int>(bad_connection_id_length)));
+ EXPECT_FALSE(ok);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.cc
new file mode 100644
index 00000000000..a9147c07442
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.cc
@@ -0,0 +1,68 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h"
+
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+namespace quic {
+
+QuicDefaultPacketWriter::QuicDefaultPacketWriter(int fd)
+ : fd_(fd), write_blocked_(false) {}
+
+QuicDefaultPacketWriter::~QuicDefaultPacketWriter() = default;
+
+WriteResult QuicDefaultPacketWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ DCHECK(!write_blocked_);
+ DCHECK(nullptr == options)
+ << "QuicDefaultPacketWriter does not accept any options.";
+ WriteResult result = QuicSocketUtils::WritePacket(fd_, buffer, buf_len,
+ self_address, peer_address);
+ if (IsWriteBlockedStatus(result.status)) {
+ write_blocked_ = true;
+ }
+ return result;
+}
+
+bool QuicDefaultPacketWriter::IsWriteBlocked() const {
+ return write_blocked_;
+}
+
+void QuicDefaultPacketWriter::SetWritable() {
+ write_blocked_ = false;
+}
+
+QuicByteCount QuicDefaultPacketWriter::GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const {
+ return kMaxOutgoingPacketSize;
+}
+
+bool QuicDefaultPacketWriter::SupportsReleaseTime() const {
+ return false;
+}
+
+bool QuicDefaultPacketWriter::IsBatchMode() const {
+ return false;
+}
+
+char* QuicDefaultPacketWriter::GetNextWriteLocation(
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ return nullptr;
+}
+
+WriteResult QuicDefaultPacketWriter::Flush() {
+ return WriteResult(WRITE_STATUS_OK, 0);
+}
+
+void QuicDefaultPacketWriter::set_write_blocked(bool is_blocked) {
+ write_blocked_ = is_blocked;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.h b/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.h
new file mode 100644
index 00000000000..36d4d8aa267
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_default_packet_writer.h
@@ -0,0 +1,55 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_DEFAULT_PACKET_WRITER_H_
+#define QUICHE_QUIC_CORE_QUIC_DEFAULT_PACKET_WRITER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+struct WriteResult;
+
+// Default packet writer which wraps QuicSocketUtils WritePacket.
+class QUIC_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter {
+ public:
+ explicit QuicDefaultPacketWriter(int fd);
+ QuicDefaultPacketWriter(const QuicDefaultPacketWriter&) = delete;
+ QuicDefaultPacketWriter& operator=(const QuicDefaultPacketWriter&) = delete;
+ ~QuicDefaultPacketWriter() override;
+
+ // QuicPacketWriter
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+ bool IsWriteBlocked() const override;
+ void SetWritable() override;
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const override;
+ bool SupportsReleaseTime() const override;
+ bool IsBatchMode() const override;
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override;
+ WriteResult Flush() override;
+
+ void set_fd(int fd) { fd_ = fd; }
+
+ protected:
+ void set_write_blocked(bool is_blocked);
+ int fd() { return fd_; }
+
+ private:
+ int fd_;
+ bool write_blocked_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_DEFAULT_PACKET_WRITER_H_
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
new file mode 100644
index 00000000000..75b3c30ffc6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.cc
@@ -0,0 +1,1405 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/chlo_extractor.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/stateless_rejector.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+typedef QuicBufferedPacketStore::BufferedPacket BufferedPacket;
+typedef QuicBufferedPacketStore::BufferedPacketList BufferedPacketList;
+typedef QuicBufferedPacketStore::EnqueuePacketResult EnqueuePacketResult;
+
+namespace {
+
+// An alarm that informs the QuicDispatcher to delete old sessions.
+class DeleteSessionsAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {}
+ DeleteSessionsAlarm(const DeleteSessionsAlarm&) = delete;
+ DeleteSessionsAlarm& operator=(const DeleteSessionsAlarm&) = delete;
+
+ void OnAlarm() override { dispatcher_->DeleteSessions(); }
+
+ private:
+ // Not owned.
+ QuicDispatcher* dispatcher_;
+};
+
+// Collects packets serialized by a QuicPacketCreator in order
+// to be handed off to the time wait list manager.
+class PacketCollector : public QuicPacketCreator::DelegateInterface,
+ public QuicStreamFrameDataProducer {
+ public:
+ explicit PacketCollector(QuicBufferAllocator* allocator)
+ : send_buffer_(allocator) {}
+ ~PacketCollector() override = default;
+
+ // QuicPacketCreator::DelegateInterface methods:
+ void OnSerializedPacket(SerializedPacket* serialized_packet) override {
+ // Make a copy of the serialized packet to send later.
+ packets_.emplace_back(
+ new QuicEncryptedPacket(CopyBuffer(*serialized_packet),
+ serialized_packet->encrypted_length, true));
+ serialized_packet->encrypted_buffer = nullptr;
+ DeleteFrames(&(serialized_packet->retransmittable_frames));
+ serialized_packet->retransmittable_frames.clear();
+ }
+
+ char* GetPacketBuffer() override {
+ // Let QuicPacketCreator to serialize packets on stack buffer.
+ return nullptr;
+ }
+
+ void OnUnrecoverableError(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) override {}
+
+ void SaveStatelessRejectFrameData(QuicStringPiece reject) {
+ struct iovec iovec;
+ iovec.iov_base = const_cast<char*>(reject.data());
+ iovec.iov_len = reject.length();
+ send_buffer_.SaveStreamData(&iovec, 1, 0, iovec.iov_len);
+ }
+
+ // QuicStreamFrameDataProducer
+ WriteStreamDataResult WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override {
+ if (send_buffer_.WriteStreamData(offset, data_length, writer)) {
+ return WRITE_SUCCESS;
+ }
+ return WRITE_FAILED;
+ }
+ bool WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override {
+ return send_buffer_.WriteStreamData(offset, data_length, writer);
+ }
+
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets() {
+ return &packets_;
+ }
+
+ private:
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> packets_;
+ // This is only needed until the packets are encrypted. Once packets are
+ // encrypted, the stream data is no longer required.
+ QuicStreamSendBuffer send_buffer_;
+};
+
+// Helper for statelessly closing connections by generating the
+// correct termination packets and adding the connection to the time wait
+// list manager.
+class StatelessConnectionTerminator {
+ public:
+ StatelessConnectionTerminator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicConnectionHelperInterface* helper,
+ QuicTimeWaitListManager* time_wait_list_manager)
+ : connection_id_(connection_id),
+ framer_(framer),
+ collector_(helper->GetStreamSendBufferAllocator()),
+ creator_(connection_id, framer, &collector_),
+ time_wait_list_manager_(time_wait_list_manager) {
+ framer_->set_data_producer(&collector_);
+ }
+
+ ~StatelessConnectionTerminator() {
+ // Clear framer's producer.
+ framer_->set_data_producer(nullptr);
+ }
+
+ // Generates a packet containing a CONNECTION_CLOSE frame specifying
+ // |error_code| and |error_details| and add the connection to time wait.
+ void CloseConnection(QuicErrorCode error_code,
+ const std::string& error_details,
+ bool ietf_quic) {
+ QuicConnectionCloseFrame* frame =
+ new QuicConnectionCloseFrame(error_code, error_details);
+ // TODO(fkastenholz): The framer version may be incorrect in some cases.
+ if (framer_->transport_version() == QUIC_VERSION_99) {
+ frame->close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ }
+
+ if (!creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION)) {
+ QUIC_BUG << "Unable to add frame to an empty packet";
+ delete frame;
+ return;
+ }
+ creator_.Flush();
+ DCHECK_EQ(1u, collector_.packets()->size());
+ time_wait_list_manager_->AddConnectionIdToTimeWait(
+ connection_id_, ietf_quic,
+ QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
+ quic::ENCRYPTION_INITIAL, collector_.packets());
+ }
+
+ // Generates a series of termination packets containing the crypto handshake
+ // message |reject|. Adds the connection to time wait list with the
+ // generated packets.
+ void RejectConnection(QuicStringPiece reject, bool ietf_quic) {
+ QuicStreamOffset offset = 0;
+ collector_.SaveStatelessRejectFrameData(reject);
+ while (offset < reject.length()) {
+ QuicFrame frame;
+ if (!QuicVersionUsesCryptoFrames(framer_->transport_version())) {
+ if (!creator_.ConsumeData(
+ QuicUtils::GetCryptoStreamId(framer_->transport_version()),
+ reject.length() - offset, offset,
+ /*fin=*/false,
+ /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)) {
+ QUIC_BUG << "Unable to consume data into an empty packet.";
+ return;
+ }
+ offset += frame.stream_frame.data_length;
+ } else {
+ if (!creator_.ConsumeCryptoData(ENCRYPTION_INITIAL,
+ reject.length() - offset, offset,
+ NOT_RETRANSMISSION, &frame)) {
+ QUIC_BUG << "Unable to consume crypto data into an empty packet.";
+ return;
+ }
+ offset += frame.crypto_frame->data_length;
+ }
+ if (offset < reject.length()) {
+ DCHECK(!creator_.HasRoomForStreamFrame(
+ QuicUtils::GetCryptoStreamId(framer_->transport_version()), offset,
+ frame.stream_frame.data_length));
+ }
+ creator_.Flush();
+ }
+ time_wait_list_manager_->AddConnectionIdToTimeWait(
+ connection_id_, ietf_quic,
+ QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, ENCRYPTION_INITIAL,
+ collector_.packets());
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id_));
+ }
+
+ private:
+ QuicConnectionId connection_id_;
+ QuicFramer* framer_; // Unowned.
+ // Set as the visitor of |creator_| to collect any generated packets.
+ PacketCollector collector_;
+ QuicPacketCreator creator_;
+ QuicTimeWaitListManager* time_wait_list_manager_;
+};
+
+// Class which extracts the ALPN from a CHLO packet.
+class ChloAlpnExtractor : public ChloExtractor::Delegate {
+ public:
+ void OnChlo(QuicTransportVersion version,
+ QuicConnectionId connection_id,
+ const CryptoHandshakeMessage& chlo) override {
+ QuicStringPiece alpn_value;
+ if (chlo.GetStringPiece(kALPN, &alpn_value)) {
+ alpn_ = std::string(alpn_value);
+ }
+ }
+
+ std::string&& ConsumeAlpn() { return std::move(alpn_); }
+
+ private:
+ std::string alpn_;
+};
+
+// Class which sits between the ChloExtractor and the StatelessRejector
+// to give the QuicDispatcher a chance to apply policy checks to the CHLO.
+class ChloValidator : public ChloAlpnExtractor {
+ public:
+ ChloValidator(QuicCryptoServerStream::Helper* helper,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ StatelessRejector* rejector)
+ : helper_(helper),
+ client_address_(client_address),
+ peer_address_(peer_address),
+ self_address_(self_address),
+ rejector_(rejector),
+ can_accept_(false),
+ error_details_("CHLO not processed") {}
+
+ // ChloExtractor::Delegate implementation.
+ void OnChlo(QuicTransportVersion version,
+ QuicConnectionId connection_id,
+ const CryptoHandshakeMessage& chlo) override {
+ // Extract the ALPN
+ ChloAlpnExtractor::OnChlo(version, connection_id, chlo);
+ if (helper_->CanAcceptClientHello(chlo, client_address_, peer_address_,
+ self_address_, &error_details_)) {
+ can_accept_ = true;
+ rejector_->OnChlo(
+ version, connection_id,
+ helper_->GenerateConnectionIdForReject(version, connection_id), chlo);
+ }
+ }
+
+ bool can_accept() const { return can_accept_; }
+
+ const std::string& error_details() const { return error_details_; }
+
+ private:
+ QuicCryptoServerStream::Helper* helper_; // Unowned.
+ // client_address_ and peer_address_ could be different values for proxy
+ // connections.
+ QuicSocketAddress client_address_;
+ QuicSocketAddress peer_address_;
+ QuicSocketAddress self_address_;
+ StatelessRejector* rejector_; // Unowned.
+ bool can_accept_;
+ std::string error_details_;
+};
+
+} // namespace
+
+QuicDispatcher::QuicDispatcher(
+ const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ uint8_t expected_connection_id_length)
+ : config_(config),
+ crypto_config_(crypto_config),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ helper_(std::move(helper)),
+ session_helper_(std::move(session_helper)),
+ alarm_factory_(std::move(alarm_factory)),
+ delete_sessions_alarm_(
+ alarm_factory_->CreateAlarm(new DeleteSessionsAlarm(this))),
+ buffered_packets_(this, helper_->GetClock(), alarm_factory_.get()),
+ current_packet_(nullptr),
+ version_manager_(version_manager),
+ framer_(GetSupportedVersions(),
+ /*unused*/ QuicTime::Zero(),
+ Perspective::IS_SERVER,
+ expected_connection_id_length),
+ last_error_(QUIC_NO_ERROR),
+ new_sessions_allowed_per_event_loop_(0u),
+ accept_new_connections_(true),
+ allow_short_initial_connection_ids_(false) {
+ framer_.set_visitor(this);
+}
+
+QuicDispatcher::~QuicDispatcher() {
+ session_map_.clear();
+ closed_session_list_.clear();
+}
+
+void QuicDispatcher::InitializeWithWriter(QuicPacketWriter* writer) {
+ DCHECK(writer_ == nullptr);
+ writer_.reset(writer);
+ time_wait_list_manager_.reset(CreateQuicTimeWaitListManager());
+}
+
+void QuicDispatcher::ProcessPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) {
+ current_self_address_ = self_address;
+ current_peer_address_ = peer_address;
+ // GetClientAddress must be called after current_peer_address_ is set.
+ current_client_address_ = GetClientAddress();
+ current_packet_ = &packet;
+ // ProcessPacket will cause the packet to be dispatched in
+ // OnUnauthenticatedPublicHeader, or sent to the time wait list manager
+ // in OnUnauthenticatedHeader.
+ framer_.ProcessPacket(packet);
+ // TODO(rjshade): Return a status describing if/why a packet was dropped,
+ // and log somehow. Maybe expose as a varz.
+ // TODO(wub): Consider invalidate the current_* variables so processing of the
+ // next packet does not use them incorrectly.
+}
+
+QuicConnectionId QuicDispatcher::MaybeReplaceConnectionId(
+ QuicConnectionId connection_id,
+ ParsedQuicVersion version) {
+ const uint8_t expected_connection_id_length =
+ framer_.GetExpectedConnectionIdLength();
+ if (connection_id.length() == expected_connection_id_length) {
+ return connection_id;
+ }
+ DCHECK(QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ version.transport_version));
+ auto it = connection_id_map_.find(connection_id);
+ if (it != connection_id_map_.end()) {
+ return it->second;
+ }
+ QuicConnectionId new_connection_id =
+ session_helper_->GenerateConnectionIdForReject(version.transport_version,
+ connection_id);
+ DCHECK_EQ(expected_connection_id_length, new_connection_id.length());
+ connection_id_map_.insert(std::make_pair(connection_id, new_connection_id));
+ QUIC_DLOG(INFO) << "Replacing incoming connection ID " << connection_id
+ << " with " << new_connection_id;
+ return new_connection_id;
+}
+
+bool QuicDispatcher::OnUnauthenticatedPublicHeader(
+ const QuicPacketHeader& header) {
+ current_connection_id_ = header.destination_connection_id;
+
+ // Port zero is only allowed for unidirectional UDP, so is disallowed by QUIC.
+ // Given that we can't even send a reply rejecting the packet, just drop the
+ // packet.
+ if (current_peer_address_.port() == 0) {
+ return false;
+ }
+
+ // The dispatcher requires the connection ID to be present in order to
+ // look up the matching QuicConnection, so we error out if it is absent.
+ if (header.destination_connection_id_included != CONNECTION_ID_PRESENT) {
+ return false;
+ }
+ QuicConnectionId connection_id = header.destination_connection_id;
+
+ // The IETF spec requires the client to generate an initial server
+ // connection ID that is at least 64 bits long. After that initial
+ // connection ID, the dispatcher picks a new one of its expected length.
+ // Therefore we should never receive a connection ID that is smaller
+ // than 64 bits and smaller than what we expect.
+ if (connection_id.length() < kQuicMinimumInitialConnectionIdLength &&
+ connection_id.length() < framer_.GetExpectedConnectionIdLength() &&
+ !allow_short_initial_connection_ids_) {
+ DCHECK(header.version_flag);
+ DCHECK(QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ header.version.transport_version));
+ QUIC_DLOG(INFO) << "Packet with short destination connection ID "
+ << connection_id << " expected "
+ << static_cast<int>(
+ framer_.GetExpectedConnectionIdLength());
+ ProcessUnauthenticatedHeaderFate(kFateTimeWait, connection_id, header.form,
+ header.version);
+ return false;
+ }
+
+ // Packets with connection IDs for active connections are processed
+ // immediately.
+ auto it = session_map_.find(connection_id);
+ if (it != session_map_.end()) {
+ DCHECK(!buffered_packets_.HasBufferedPackets(connection_id));
+ it->second->ProcessUdpPacket(current_self_address_, current_peer_address_,
+ *current_packet_);
+ return false;
+ }
+
+ if (buffered_packets_.HasChloForConnection(connection_id)) {
+ BufferEarlyPacket(connection_id, header.form != GOOGLE_QUIC_PACKET,
+ header.version);
+ return false;
+ }
+
+ // Check if we are buffering packets for this connection ID
+ if (temporarily_buffered_connections_.find(connection_id) !=
+ temporarily_buffered_connections_.end()) {
+ // This packet was received while the a CHLO for the same connection ID was
+ // being processed. Buffer it.
+ BufferEarlyPacket(connection_id, header.form != GOOGLE_QUIC_PACKET,
+ header.version);
+ return false;
+ }
+
+ if (!OnUnauthenticatedUnknownPublicHeader(header)) {
+ return false;
+ }
+
+ // If the packet is a public reset for a connection ID that is not active,
+ // there is nothing we must do or can do.
+ if (header.reset_flag) {
+ return false;
+ }
+
+ if (time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) {
+ // This connection ID is already in time-wait state.
+ time_wait_list_manager_->ProcessPacket(
+ current_self_address_, current_peer_address_,
+ header.destination_connection_id, header.form, GetPerPacketContext());
+ return false;
+ }
+
+ // The packet has an unknown connection ID.
+
+ // Unless the packet provides a version, assume that we can continue
+ // processing using our preferred version.
+ ParsedQuicVersion version = GetSupportedVersions().front();
+ if (header.version_flag) {
+ ParsedQuicVersion packet_version = header.version;
+ if (framer_.supported_versions() != GetSupportedVersions()) {
+ // Reset framer's version if version flags change in flight.
+ framer_.SetSupportedVersions(GetSupportedVersions());
+ }
+ if (!framer_.IsSupportedVersion(packet_version)) {
+ if (ShouldCreateSessionForUnknownVersion(framer_.last_version_label())) {
+ return true;
+ }
+ if (!crypto_config()->validate_chlo_size() ||
+ current_packet_->length() >= kMinPacketSizeForVersionNegotiation) {
+ // Since the version is not supported, send a version negotiation
+ // packet and stop processing the current packet.
+ time_wait_list_manager()->SendVersionNegotiationPacket(
+ connection_id, header.form != GOOGLE_QUIC_PACKET,
+ GetSupportedVersions(), current_self_address_,
+ current_peer_address_, GetPerPacketContext());
+ }
+ return false;
+ }
+ version = packet_version;
+ }
+ // Set the framer's version and continue processing.
+ framer_.set_version(version);
+ return true;
+}
+
+bool QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
+ QuicConnectionId connection_id = header.destination_connection_id;
+ // Packet's connection ID is unknown. Apply the validity checks.
+ QuicPacketFate fate = ValidityChecks(header);
+ if (fate == kFateProcess) {
+ // Execute stateless rejection logic to determine the packet fate, then
+ // invoke ProcessUnauthenticatedHeaderFate.
+ MaybeRejectStatelessly(connection_id, header.form, header.version);
+ } else {
+ // If the fate is already known, process it without executing stateless
+ // rejection logic.
+ ProcessUnauthenticatedHeaderFate(fate, connection_id, header.form,
+ header.version);
+ }
+
+ return false;
+}
+
+void QuicDispatcher::ProcessUnauthenticatedHeaderFate(
+ QuicPacketFate fate,
+ QuicConnectionId connection_id,
+ PacketHeaderFormat form,
+ ParsedQuicVersion version) {
+ switch (fate) {
+ case kFateProcess: {
+ ProcessChlo(form, version);
+ break;
+ }
+ case kFateTimeWait:
+ // MaybeRejectStatelessly or OnExpiredPackets might have already added the
+ // connection to time wait, in which case it should not be added again.
+ if (!GetQuicReloadableFlag(quic_use_cheap_stateless_rejects) ||
+ !time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) {
+ // Add this connection_id to the time-wait state, to safely reject
+ // future packets.
+ QUIC_DLOG(INFO) << "Adding connection ID " << connection_id
+ << " to time-wait list.";
+ QUIC_CODE_COUNT(quic_reject_fate_time_wait);
+ StatelesslyTerminateConnection(
+ connection_id, form, version, QUIC_HANDSHAKE_FAILED,
+ "Reject connection",
+ quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
+ }
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+ time_wait_list_manager_->ProcessPacket(
+ current_self_address_, current_peer_address_, connection_id, form,
+ GetPerPacketContext());
+
+ // Any packets which were buffered while the stateless rejector logic was
+ // running should be discarded. Do not inform the time wait list manager,
+ // which should already have a made a decision about sending a reject
+ // based on the CHLO alone.
+ buffered_packets_.DiscardPackets(connection_id);
+ break;
+ case kFateBuffer:
+ // This packet is a non-CHLO packet which has arrived before the
+ // corresponding CHLO, *or* this packet was received while the
+ // corresponding CHLO was being processed. Buffer it.
+ BufferEarlyPacket(connection_id, form != GOOGLE_QUIC_PACKET, version);
+ break;
+ case kFateDrop:
+ // Do nothing with the packet.
+ break;
+ }
+}
+
+QuicDispatcher::QuicPacketFate QuicDispatcher::ValidityChecks(
+ const QuicPacketHeader& header) {
+ // To have all the checks work properly without tears, insert any new check
+ // into the framework of this method in the section for checks that return the
+ // check's fate value. The sections for checks must be ordered with the
+ // highest priority fate first.
+
+ // Checks that return kFateDrop.
+
+ // Checks that return kFateTimeWait.
+
+ // All packets within a connection sent by a client before receiving a
+ // response from the server are required to have the version negotiation flag
+ // set. Since this may be a client continuing a connection we lost track of
+ // via server restart, send a rejection to fast-fail the connection.
+ if (!header.version_flag) {
+ QUIC_DLOG(INFO)
+ << "Packet without version arrived for unknown connection ID "
+ << header.destination_connection_id;
+ return kFateTimeWait;
+ }
+
+ // initial packet number of 0 is always invalid.
+ if (!header.packet_number.IsInitialized()) {
+ return kFateTimeWait;
+ }
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ QUIC_RESTART_FLAG_COUNT_N(quic_enable_accept_random_ipn, 1, 2);
+ // Accepting Initial Packet Numbers in 1...((2^31)-1) range... check
+ // maximum accordingly.
+ if (header.packet_number > MaxRandomInitialPacketNumber()) {
+ return kFateTimeWait;
+ }
+ } else {
+ // Count those that would have been accepted if FLAGS..random_ipn
+ // were true -- to detect/diagnose potential issues prior to
+ // enabling the flag.
+ if ((header.packet_number >
+ QuicPacketNumber(kMaxReasonableInitialPacketNumber)) &&
+ (header.packet_number <= MaxRandomInitialPacketNumber())) {
+ QUIC_CODE_COUNT_N(had_possibly_random_ipn, 1, 2);
+ }
+ // Check that the sequence number is within the range that the client is
+ // expected to send before receiving a response from the server.
+ if (header.packet_number >
+ QuicPacketNumber(kMaxReasonableInitialPacketNumber)) {
+ return kFateTimeWait;
+ }
+ }
+ return kFateProcess;
+}
+
+void QuicDispatcher::CleanUpSession(SessionMap::iterator it,
+ QuicConnection* connection,
+ bool should_close_statelessly,
+ ConnectionCloseSource source) {
+ write_blocked_list_.erase(connection);
+ if (should_close_statelessly) {
+ DCHECK(connection->termination_packets() != nullptr &&
+ !connection->termination_packets()->empty());
+ }
+ QuicTimeWaitListManager::TimeWaitAction action =
+ QuicTimeWaitListManager::SEND_STATELESS_RESET;
+ if (connection->termination_packets() != nullptr &&
+ !connection->termination_packets()->empty()) {
+ action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS;
+ } else if (connection->transport_version() > QUIC_VERSION_43) {
+ if (!connection->IsHandshakeConfirmed()) {
+ QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_handshake_failed);
+ action = QuicTimeWaitListManager::SEND_TERMINATION_PACKETS;
+ // This serializes a connection close termination packet with error code
+ // QUIC_HANDSHAKE_FAILED and adds the connection to the time wait list.
+ StatelesslyTerminateConnection(
+ connection->connection_id(), IETF_QUIC_LONG_HEADER_PACKET,
+ connection->version(), QUIC_HANDSHAKE_FAILED,
+ "Connection is closed by server before handshake confirmed",
+ // Although it is our intention to send termination packets, the
+ // |action| argument is not used by this call to
+ // StatelesslyTerminateConnection().
+ action);
+ session_map_.erase(it);
+ return;
+ }
+ QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_stateless_reset);
+ }
+ time_wait_list_manager_->AddConnectionIdToTimeWait(
+ it->first, connection->transport_version() > QUIC_VERSION_43, action,
+ connection->encryption_level(), connection->termination_packets());
+ session_map_.erase(it);
+}
+
+void QuicDispatcher::StopAcceptingNewConnections() {
+ accept_new_connections_ = false;
+}
+
+std::unique_ptr<QuicPerPacketContext> QuicDispatcher::GetPerPacketContext()
+ const {
+ return nullptr;
+}
+
+void QuicDispatcher::DeleteSessions() {
+ if (!write_blocked_list_.empty()) {
+ for (const std::unique_ptr<QuicSession>& session : closed_session_list_) {
+ if (write_blocked_list_.erase(session->connection()) != 0) {
+ QUIC_BUG << "QuicConnection was in WriteBlockedList before destruction";
+ }
+ }
+ }
+ closed_session_list_.clear();
+}
+
+void QuicDispatcher::OnCanWrite() {
+ // The socket is now writable.
+ writer_->SetWritable();
+
+ // Move every blocked writer in |write_blocked_list_| to a temporary list.
+ const size_t num_blocked_writers_before = write_blocked_list_.size();
+ WriteBlockedList temp_list;
+ temp_list.swap(write_blocked_list_);
+ DCHECK(write_blocked_list_.empty());
+
+ // Give each blocked writer a chance to write what they indended to write.
+ // If they are blocked again, they will call |OnWriteBlocked| to add
+ // themselves back into |write_blocked_list_|.
+ while (!temp_list.empty()) {
+ QuicBlockedWriterInterface* blocked_writer = temp_list.begin()->first;
+ temp_list.erase(temp_list.begin());
+ blocked_writer->OnBlockedWriterCanWrite();
+ }
+ const size_t num_blocked_writers_after = write_blocked_list_.size();
+ if (num_blocked_writers_after != 0) {
+ if (num_blocked_writers_before == num_blocked_writers_after) {
+ QUIC_CODE_COUNT(quic_zero_progress_on_can_write);
+ } else {
+ QUIC_CODE_COUNT(quic_blocked_again_on_can_write);
+ }
+ }
+}
+
+bool QuicDispatcher::HasPendingWrites() const {
+ return !write_blocked_list_.empty();
+}
+
+void QuicDispatcher::Shutdown() {
+ while (!session_map_.empty()) {
+ QuicSession* session = session_map_.begin()->second.get();
+ session->connection()->CloseConnection(
+ QUIC_PEER_GOING_AWAY, "Server shutdown imminent",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ // Validate that the session removes itself from the session map on close.
+ DCHECK(session_map_.empty() ||
+ session_map_.begin()->second.get() != session);
+ }
+ DeleteSessions();
+}
+
+void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {
+ auto it = session_map_.find(connection_id);
+ if (it == session_map_.end()) {
+ QUIC_BUG << "ConnectionId " << connection_id
+ << " does not exist in the session map. Error: "
+ << QuicErrorCodeToString(error);
+ QUIC_BUG << QuicStackTrace();
+ return;
+ }
+
+ QUIC_DLOG_IF(INFO, error != QUIC_NO_ERROR)
+ << "Closing connection (" << connection_id
+ << ") due to error: " << QuicErrorCodeToString(error)
+ << ", with details: " << error_details;
+
+ QuicConnection* connection = it->second->connection();
+ if (ShouldDestroySessionAsynchronously()) {
+ // Set up alarm to fire immediately to bring destruction of this session
+ // out of current call stack.
+ if (closed_session_list_.empty()) {
+ delete_sessions_alarm_->Update(helper()->GetClock()->ApproximateNow(),
+ QuicTime::Delta::Zero());
+ }
+ closed_session_list_.push_back(std::move(it->second));
+ }
+ const bool should_close_statelessly =
+ (error == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT);
+ CleanUpSession(it, connection, should_close_statelessly, source);
+}
+
+void QuicDispatcher::OnWriteBlocked(
+ QuicBlockedWriterInterface* blocked_writer) {
+ if (!blocked_writer->IsWriterBlocked()) {
+ // It is a programming error if this ever happens. When we are sure it is
+ // not happening, replace it with a DCHECK.
+ QUIC_BUG
+ << "Tried to add writer into blocked list when it shouldn't be added";
+ // Return without adding the connection to the blocked list, to avoid
+ // infinite loops in OnCanWrite.
+ return;
+ }
+
+ write_blocked_list_.insert(std::make_pair(blocked_writer, true));
+}
+
+void QuicDispatcher::OnRstStreamReceived(const QuicRstStreamFrame& frame) {}
+
+void QuicDispatcher::OnStopSendingReceived(const QuicStopSendingFrame& frame) {}
+
+void QuicDispatcher::OnConnectionAddedToTimeWaitList(
+ QuicConnectionId connection_id) {
+ QUIC_DLOG(INFO) << "Connection " << connection_id
+ << " added to time wait list.";
+}
+
+void QuicDispatcher::StatelesslyTerminateConnection(
+ QuicConnectionId connection_id,
+ PacketHeaderFormat format,
+ ParsedQuicVersion version,
+ QuicErrorCode error_code,
+ const std::string& error_details,
+ QuicTimeWaitListManager::TimeWaitAction action) {
+ if (format != IETF_QUIC_LONG_HEADER_PACKET) {
+ QUIC_DVLOG(1) << "Statelessly terminating " << connection_id
+ << " based on a non-ietf-long packet, action:" << action
+ << ", error_code:" << error_code
+ << ", error_details:" << error_details;
+ time_wait_list_manager_->AddConnectionIdToTimeWait(
+ connection_id, format != GOOGLE_QUIC_PACKET, action, ENCRYPTION_INITIAL,
+ nullptr);
+ return;
+ }
+
+ // If the version is known and supported by framer, send a connection close.
+ if (framer_.IsSupportedVersion(version)) {
+ QUIC_DVLOG(1)
+ << "Statelessly terminating " << connection_id
+ << " based on an ietf-long packet, which has a supported version:"
+ << version << ", error_code:" << error_code
+ << ", error_details:" << error_details;
+ // Set framer_ to the packet's version such that the connection close can be
+ // processed by the client.
+ ParsedQuicVersion original_version = framer_.version();
+ framer_.set_version(version);
+
+ StatelessConnectionTerminator terminator(
+ connection_id, &framer_, helper_.get(), time_wait_list_manager_.get());
+ // This also adds the connection to time wait list.
+ terminator.CloseConnection(error_code, error_details, true);
+
+ // Restore framer_ to the original version, as if nothing changed in it.
+ framer_.set_version(original_version);
+ return;
+ }
+
+ QUIC_DVLOG(1)
+ << "Statelessly terminating " << connection_id
+ << " based on an ietf-long packet, which has an unsupported version:"
+ << version << ", error_code:" << error_code
+ << ", error_details:" << error_details;
+ // Version is unknown or unsupported by framer, send a version negotiation
+ // with an empty version list, which can be understood by the client.
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
+ termination_packets.push_back(QuicFramer::BuildVersionNegotiationPacket(
+ connection_id, /*ietf_quic=*/true,
+ ParsedQuicVersionVector{UnsupportedQuicVersion()}));
+ time_wait_list_manager()->AddConnectionIdToTimeWait(
+ connection_id, /*ietf_quic=*/true,
+ QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, ENCRYPTION_INITIAL,
+ &termination_packets);
+}
+
+void QuicDispatcher::OnPacket() {}
+
+void QuicDispatcher::OnError(QuicFramer* framer) {
+ QuicErrorCode error = framer->error();
+ SetLastError(error);
+ QUIC_DLOG(INFO) << QuicErrorCodeToString(error);
+}
+
+bool QuicDispatcher::ShouldCreateSessionForUnknownVersion(
+ QuicVersionLabel /*version_label*/) {
+ return false;
+}
+
+bool QuicDispatcher::OnProtocolVersionMismatch(
+ ParsedQuicVersion /*received_version*/,
+ PacketHeaderFormat /*form*/) {
+ QUIC_BUG_IF(
+ !time_wait_list_manager_->IsConnectionIdInTimeWait(
+ current_connection_id_) &&
+ !ShouldCreateSessionForUnknownVersion(framer_.last_version_label()))
+ << "Unexpected version mismatch: "
+ << QuicVersionLabelToString(framer_.last_version_label());
+
+ // Keep processing after protocol mismatch - this will be dealt with by the
+ // time wait list or connection that we will create.
+ return true;
+}
+
+void QuicDispatcher::OnPublicResetPacket(
+ const QuicPublicResetPacket& /*packet*/) {
+ DCHECK(false);
+}
+
+void QuicDispatcher::OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& /*packet*/) {
+ DCHECK(false);
+}
+
+void QuicDispatcher::OnDecryptedPacket(EncryptionLevel level) {
+ DCHECK(false);
+}
+
+bool QuicDispatcher::OnPacketHeader(const QuicPacketHeader& /*header*/) {
+ DCHECK(false);
+ return false;
+}
+
+void QuicDispatcher::OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) {
+ DCHECK(false);
+}
+
+bool QuicDispatcher::OnStreamFrame(const QuicStreamFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnCryptoFrame(const QuicCryptoFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnAckFrameStart(QuicPacketNumber /*largest_acked*/,
+ QuicTime::Delta /*ack_delay_time*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnAckRange(QuicPacketNumber /*start*/,
+ QuicPacketNumber /*end*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnAckTimestamp(QuicPacketNumber /*packet_number*/,
+ QuicTime /*timestamp*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnAckFrameEnd(QuicPacketNumber /*start*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnStopWaitingFrame(const QuicStopWaitingFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnPaddingFrame(const QuicPaddingFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnPingFrame(const QuicPingFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) {
+ return true;
+}
+
+bool QuicDispatcher::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ return true;
+}
+
+bool QuicDispatcher::OnStopSendingFrame(const QuicStopSendingFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnPathChallengeFrame(
+ const QuicPathChallengeFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnPathResponseFrame(
+ const QuicPathResponseFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& /*frame*/) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnNewConnectionIdFrame(
+ const QuicNewConnectionIdFrame& frame) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+ DCHECK(false);
+ return false;
+}
+
+bool QuicDispatcher::OnMessageFrame(const QuicMessageFrame& frame) {
+ DCHECK(false);
+ return false;
+}
+
+void QuicDispatcher::OnPacketComplete() {
+ DCHECK(false);
+}
+
+bool QuicDispatcher::IsValidStatelessResetToken(QuicUint128 token) const {
+ DCHECK(false);
+ return false;
+}
+
+void QuicDispatcher::OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) {
+ DCHECK(false);
+}
+
+void QuicDispatcher::OnExpiredPackets(
+ QuicConnectionId connection_id,
+ BufferedPacketList early_arrived_packets) {
+ QUIC_CODE_COUNT(quic_reject_buffered_packets_expired);
+ StatelesslyTerminateConnection(
+ connection_id,
+ early_arrived_packets.ietf_quic ? IETF_QUIC_LONG_HEADER_PACKET
+ : GOOGLE_QUIC_PACKET,
+ early_arrived_packets.version, QUIC_HANDSHAKE_FAILED,
+ "Packets buffered for too long",
+ quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
+}
+
+void QuicDispatcher::ProcessBufferedChlos(size_t max_connections_to_create) {
+ // Reset the counter before starting creating connections.
+ new_sessions_allowed_per_event_loop_ = max_connections_to_create;
+ for (; new_sessions_allowed_per_event_loop_ > 0;
+ --new_sessions_allowed_per_event_loop_) {
+ QuicConnectionId connection_id;
+ BufferedPacketList packet_list =
+ buffered_packets_.DeliverPacketsForNextConnection(&connection_id);
+ const std::list<BufferedPacket>& packets = packet_list.buffered_packets;
+ if (packets.empty()) {
+ return;
+ }
+ QuicConnectionId original_connection_id = connection_id;
+ connection_id =
+ MaybeReplaceConnectionId(connection_id, packet_list.version);
+ QuicSession* session =
+ CreateQuicSession(connection_id, packets.front().peer_address,
+ packet_list.alpn, packet_list.version);
+ if (original_connection_id != connection_id) {
+ session->connection()->AddIncomingConnectionId(original_connection_id);
+ }
+ QUIC_DLOG(INFO) << "Created new session for " << connection_id;
+ session_map_.insert(std::make_pair(connection_id, QuicWrapUnique(session)));
+ DeliverPacketsToSession(packets, session);
+ }
+}
+
+bool QuicDispatcher::HasChlosBuffered() const {
+ return buffered_packets_.HasChlosBuffered();
+}
+
+bool QuicDispatcher::ShouldCreateOrBufferPacketForConnection(
+ QuicConnectionId connection_id,
+ bool ietf_quic) {
+ VLOG(1) << "Received packet from new connection " << connection_id;
+ return true;
+}
+
+// Return true if there is any packet buffered in the store.
+bool QuicDispatcher::HasBufferedPackets(QuicConnectionId connection_id) {
+ return buffered_packets_.HasBufferedPackets(connection_id);
+}
+
+void QuicDispatcher::OnBufferPacketFailure(EnqueuePacketResult result,
+ QuicConnectionId connection_id) {
+ QUIC_DLOG(INFO) << "Fail to buffer packet on connection " << connection_id
+ << " because of " << result;
+}
+
+bool QuicDispatcher::ShouldAttemptCheapStatelessRejection() {
+ return true;
+}
+
+QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() {
+ return new QuicTimeWaitListManager(writer_.get(), this, helper_->GetClock(),
+ alarm_factory_.get());
+}
+
+void QuicDispatcher::BufferEarlyPacket(QuicConnectionId connection_id,
+ bool ietf_quic,
+ ParsedQuicVersion version) {
+ bool is_new_connection = !buffered_packets_.HasBufferedPackets(connection_id);
+ if (is_new_connection &&
+ !ShouldCreateOrBufferPacketForConnection(connection_id, ietf_quic)) {
+ return;
+ }
+
+ EnqueuePacketResult rs = buffered_packets_.EnqueuePacket(
+ connection_id, ietf_quic, *current_packet_, current_self_address_,
+ current_peer_address_, /*is_chlo=*/false,
+ /*alpn=*/"", version);
+ if (rs != EnqueuePacketResult::SUCCESS) {
+ OnBufferPacketFailure(rs, connection_id);
+ }
+}
+
+void QuicDispatcher::ProcessChlo(PacketHeaderFormat form,
+ ParsedQuicVersion version) {
+ if (!accept_new_connections_) {
+ // Don't any create new connection.
+ QUIC_CODE_COUNT(quic_reject_stop_accepting_new_connections);
+ StatelesslyTerminateConnection(
+ current_connection_id(), form, version, QUIC_HANDSHAKE_FAILED,
+ "Stop accepting new connections",
+ quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
+ // Time wait list will reject the packet correspondingly.
+ time_wait_list_manager()->ProcessPacket(
+ current_self_address(), current_peer_address(), current_connection_id(),
+ form, GetPerPacketContext());
+ return;
+ }
+ if (!buffered_packets_.HasBufferedPackets(current_connection_id_) &&
+ !ShouldCreateOrBufferPacketForConnection(current_connection_id_,
+ form != GOOGLE_QUIC_PACKET)) {
+ return;
+ }
+ if (FLAGS_quic_allow_chlo_buffering &&
+ new_sessions_allowed_per_event_loop_ <= 0) {
+ // Can't create new session any more. Wait till next event loop.
+ QUIC_BUG_IF(buffered_packets_.HasChloForConnection(current_connection_id_));
+ EnqueuePacketResult rs = buffered_packets_.EnqueuePacket(
+ current_connection_id_, form != GOOGLE_QUIC_PACKET, *current_packet_,
+ current_self_address_, current_peer_address_,
+ /*is_chlo=*/true, current_alpn_, framer_.version());
+ if (rs != EnqueuePacketResult::SUCCESS) {
+ OnBufferPacketFailure(rs, current_connection_id_);
+ }
+ return;
+ }
+
+ QuicConnectionId original_connection_id = current_connection_id_;
+ current_connection_id_ =
+ MaybeReplaceConnectionId(current_connection_id_, framer_.version());
+ // Creates a new session and process all buffered packets for this connection.
+ QuicSession* session =
+ CreateQuicSession(current_connection_id_, current_peer_address_,
+ current_alpn_, framer_.version());
+ if (original_connection_id != current_connection_id_) {
+ session->connection()->AddIncomingConnectionId(original_connection_id);
+ }
+ QUIC_DLOG(INFO) << "Created new session for " << current_connection_id_;
+ session_map_.insert(
+ std::make_pair(current_connection_id_, QuicWrapUnique(session)));
+ std::list<BufferedPacket> packets =
+ buffered_packets_.DeliverPackets(current_connection_id_).buffered_packets;
+ // Process CHLO at first.
+ session->ProcessUdpPacket(current_self_address_, current_peer_address_,
+ *current_packet_);
+ // Deliver queued-up packets in the same order as they arrived.
+ // Do this even when flag is off because there might be still some packets
+ // buffered in the store before flag is turned off.
+ DeliverPacketsToSession(packets, session);
+ --new_sessions_allowed_per_event_loop_;
+}
+
+const QuicSocketAddress QuicDispatcher::GetClientAddress() const {
+ return current_peer_address_;
+}
+
+bool QuicDispatcher::ShouldDestroySessionAsynchronously() {
+ return true;
+}
+
+void QuicDispatcher::SetLastError(QuicErrorCode error) {
+ last_error_ = error;
+}
+
+bool QuicDispatcher::OnUnauthenticatedUnknownPublicHeader(
+ const QuicPacketHeader& header) {
+ return true;
+}
+
+class StatelessRejectorProcessDoneCallback
+ : public StatelessRejector::ProcessDoneCallback {
+ public:
+ StatelessRejectorProcessDoneCallback(QuicDispatcher* dispatcher,
+ ParsedQuicVersion first_version,
+ PacketHeaderFormat form)
+ : dispatcher_(dispatcher),
+ current_client_address_(dispatcher->current_client_address_),
+ current_peer_address_(dispatcher->current_peer_address_),
+ current_self_address_(dispatcher->current_self_address_),
+ additional_context_(dispatcher->GetPerPacketContext()),
+ current_packet_(
+ dispatcher->current_packet_->Clone()), // Note: copies the packet
+ first_version_(first_version),
+ current_packet_format_(form) {}
+
+ void Run(std::unique_ptr<StatelessRejector> rejector) override {
+ if (additional_context_ != nullptr) {
+ dispatcher_->RestorePerPacketContext(std::move(additional_context_));
+ }
+ dispatcher_->OnStatelessRejectorProcessDone(
+ std::move(rejector), current_client_address_, current_peer_address_,
+ current_self_address_, std::move(current_packet_), first_version_,
+ current_packet_format_);
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+ QuicSocketAddress current_client_address_;
+ QuicSocketAddress current_peer_address_;
+ QuicSocketAddress current_self_address_;
+ // TODO(wub): Wrap all current_* variables into PerPacketContext. And rename
+ // |additional_context_| to |context_|.
+ std::unique_ptr<QuicPerPacketContext> additional_context_;
+ std::unique_ptr<QuicReceivedPacket> current_packet_;
+ ParsedQuicVersion first_version_;
+ const PacketHeaderFormat current_packet_format_;
+};
+
+void QuicDispatcher::MaybeRejectStatelessly(QuicConnectionId connection_id,
+
+ PacketHeaderFormat form,
+ ParsedQuicVersion version) {
+ if (version.handshake_protocol == PROTOCOL_TLS1_3) {
+ ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, form,
+ version);
+ return;
+ // TODO(nharper): Support buffering non-ClientHello packets when using TLS.
+ }
+ // TODO(rch): This logic should probably live completely inside the rejector.
+ if (!FLAGS_quic_allow_chlo_buffering ||
+ !GetQuicReloadableFlag(quic_use_cheap_stateless_rejects) ||
+ !GetQuicReloadableFlag(enable_quic_stateless_reject_support) ||
+ !ShouldAttemptCheapStatelessRejection()) {
+ // Not use cheap stateless reject.
+ ChloAlpnExtractor alpn_extractor;
+ if (FLAGS_quic_allow_chlo_buffering &&
+ !ChloExtractor::Extract(*current_packet_, GetSupportedVersions(),
+ config_->create_session_tag_indicators(),
+ &alpn_extractor, connection_id.length())) {
+ // Buffer non-CHLO packets.
+ ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form,
+ version);
+ return;
+ }
+ current_alpn_ = alpn_extractor.ConsumeAlpn();
+ ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, form,
+ version);
+ return;
+ }
+
+ std::unique_ptr<StatelessRejector> rejector(new StatelessRejector(
+ version, GetSupportedVersions(), crypto_config_, &compressed_certs_cache_,
+ helper()->GetClock(), helper()->GetRandomGenerator(),
+ current_packet_->length(), current_client_address_,
+ current_self_address_));
+ ChloValidator validator(session_helper_.get(), current_client_address_,
+ current_peer_address_, current_self_address_,
+ rejector.get());
+ if (!ChloExtractor::Extract(*current_packet_, GetSupportedVersions(),
+ config_->create_session_tag_indicators(),
+ &validator, connection_id.length())) {
+ ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form, version);
+ return;
+ }
+ current_alpn_ = validator.ConsumeAlpn();
+
+ if (!validator.can_accept()) {
+ // This CHLO is prohibited by policy.
+ QUIC_CODE_COUNT(quic_reject_cant_accept_chlo);
+ StatelessConnectionTerminator terminator(connection_id, &framer_, helper(),
+ time_wait_list_manager_.get());
+ terminator.CloseConnection(QUIC_HANDSHAKE_FAILED, validator.error_details(),
+ form != GOOGLE_QUIC_PACKET);
+ QuicSession::RecordConnectionCloseAtServer(
+ QUIC_HANDSHAKE_FAILED, ConnectionCloseSource::FROM_SELF);
+ ProcessUnauthenticatedHeaderFate(kFateTimeWait, connection_id, form,
+ version);
+ return;
+ }
+
+ // If we were able to make a decision about this CHLO based purely on the
+ // information available in OnChlo, just invoke the done callback immediately.
+ if (rejector->state() != StatelessRejector::UNKNOWN) {
+ ProcessStatelessRejectorState(std::move(rejector),
+ version.transport_version, form);
+ return;
+ }
+
+ // Insert into set of connection IDs to buffer
+ const bool ok =
+ temporarily_buffered_connections_.insert(connection_id).second;
+ QUIC_BUG_IF(!ok)
+ << "Processing multiple stateless rejections for connection ID "
+ << connection_id;
+
+ // Continue stateless rejector processing
+ std::unique_ptr<StatelessRejectorProcessDoneCallback> cb(
+ new StatelessRejectorProcessDoneCallback(this, version, form));
+ StatelessRejector::Process(std::move(rejector), std::move(cb));
+}
+
+void QuicDispatcher::OnStatelessRejectorProcessDone(
+ std::unique_ptr<StatelessRejector> rejector,
+ const QuicSocketAddress& current_client_address,
+ const QuicSocketAddress& current_peer_address,
+ const QuicSocketAddress& current_self_address,
+ std::unique_ptr<QuicReceivedPacket> current_packet,
+ ParsedQuicVersion first_version,
+ PacketHeaderFormat current_packet_format) {
+ // Reset current_* to correspond to the packet which initiated the stateless
+ // reject logic.
+ current_client_address_ = current_client_address;
+ current_peer_address_ = current_peer_address;
+ current_self_address_ = current_self_address;
+ current_packet_ = current_packet.get();
+ current_connection_id_ = rejector->connection_id();
+ framer_.set_version(first_version);
+
+ // Stop buffering packets on this connection
+ const auto num_erased =
+ temporarily_buffered_connections_.erase(rejector->connection_id());
+ QUIC_BUG_IF(num_erased != 1) << "Completing stateless rejection logic for "
+ "non-buffered connection ID "
+ << rejector->connection_id();
+
+ // If this connection has gone into time-wait during the async processing,
+ // don't proceed.
+ if (time_wait_list_manager_->IsConnectionIdInTimeWait(
+ rejector->connection_id())) {
+ time_wait_list_manager_->ProcessPacket(
+ current_self_address, current_peer_address, rejector->connection_id(),
+ current_packet_format, GetPerPacketContext());
+ return;
+ }
+
+ ProcessStatelessRejectorState(std::move(rejector),
+ first_version.transport_version,
+ current_packet_format);
+}
+
+void QuicDispatcher::ProcessStatelessRejectorState(
+ std::unique_ptr<StatelessRejector> rejector,
+ QuicTransportVersion first_version,
+ PacketHeaderFormat form) {
+ QuicPacketFate fate;
+ switch (rejector->state()) {
+ case StatelessRejector::FAILED: {
+ // There was an error processing the client hello.
+ QUIC_CODE_COUNT(quic_reject_error_processing_chlo);
+ StatelessConnectionTerminator terminator(rejector->connection_id(),
+ &framer_, helper(),
+ time_wait_list_manager_.get());
+ terminator.CloseConnection(rejector->error(), rejector->error_details(),
+ form != GOOGLE_QUIC_PACKET);
+ fate = kFateTimeWait;
+ break;
+ }
+
+ case StatelessRejector::UNSUPPORTED:
+ // Cheap stateless rejects are not supported so process the packet.
+ fate = kFateProcess;
+ break;
+
+ case StatelessRejector::ACCEPTED:
+ // Contains a valid CHLO, so process the packet and create a connection.
+ fate = kFateProcess;
+ break;
+
+ case StatelessRejector::REJECTED: {
+ QUIC_BUG_IF(first_version != framer_.transport_version())
+ << "SREJ: Client's version: " << QuicVersionToString(first_version)
+ << " is different from current dispatcher framer's version: "
+ << QuicVersionToString(framer_.transport_version());
+ StatelessConnectionTerminator terminator(rejector->connection_id(),
+ &framer_, helper(),
+ time_wait_list_manager_.get());
+ terminator.RejectConnection(
+ rejector->reply().GetSerialized().AsStringPiece(),
+ form != GOOGLE_QUIC_PACKET);
+ QuicSession::RecordConnectionCloseAtServer(
+ QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT,
+ ConnectionCloseSource::FROM_SELF);
+ OnConnectionRejectedStatelessly();
+ fate = kFateTimeWait;
+ break;
+ }
+
+ default:
+ QUIC_BUG << "Rejector has invalid state " << rejector->state();
+ fate = kFateDrop;
+ break;
+ }
+ ProcessUnauthenticatedHeaderFate(fate, rejector->connection_id(), form,
+ rejector->version());
+}
+
+const QuicTransportVersionVector&
+QuicDispatcher::GetSupportedTransportVersions() {
+ return version_manager_->GetSupportedTransportVersions();
+}
+
+const ParsedQuicVersionVector& QuicDispatcher::GetSupportedVersions() {
+ return version_manager_->GetSupportedVersions();
+}
+
+void QuicDispatcher::DeliverPacketsToSession(
+ const std::list<BufferedPacket>& packets,
+ QuicSession* session) {
+ for (const BufferedPacket& packet : packets) {
+ session->ProcessUdpPacket(packet.self_address, packet.peer_address,
+ *(packet.packet));
+ }
+}
+
+void QuicDispatcher::DisableFlagValidation() {
+ framer_.set_validate_flags(false);
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..a6d7e37ca21
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher.h
@@ -0,0 +1,509 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A server side dispatcher which dispatches a given client's data to their
+// stream.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_DISPATCHER_H_
+#define QUICHE_QUIC_CORE_QUIC_DISPATCHER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/core/stateless_rejector.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+namespace test {
+class QuicDispatcherPeer;
+} // namespace test
+
+class QuicConfig;
+class QuicCryptoServerConfig;
+
+class QuicDispatcher : public QuicTimeWaitListManager::Visitor,
+ public ProcessPacketInterface,
+ public QuicFramerVisitorInterface,
+ public QuicBufferedPacketStore::VisitorInterface {
+ public:
+ // Ideally we'd have a linked_hash_set: the boolean is unused.
+ typedef QuicLinkedHashMap<QuicBlockedWriterInterface*, bool> WriteBlockedList;
+
+ QuicDispatcher(const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ uint8_t expected_connection_id_length);
+ QuicDispatcher(const QuicDispatcher&) = delete;
+ QuicDispatcher& operator=(const QuicDispatcher&) = delete;
+
+ ~QuicDispatcher() override;
+
+ // Takes ownership of |writer|.
+ void InitializeWithWriter(QuicPacketWriter* writer);
+
+ // Process the incoming packet by creating a new session, passing it to
+ // an existing session, or passing it to the time wait list.
+ void ProcessPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) override;
+
+ // Called when the socket becomes writable to allow queued writes to happen.
+ virtual void OnCanWrite();
+
+ // Returns true if there's anything in the blocked writer list.
+ virtual bool HasPendingWrites() const;
+
+ // Sends ConnectionClose frames to all connected clients.
+ void Shutdown();
+
+ // QuicSession::Visitor interface implementation (via inheritance of
+ // QuicTimeWaitListManager::Visitor):
+ // Ensure that the closed connection is cleaned up asynchronously.
+ void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) override;
+
+ // QuicSession::Visitor interface implementation (via inheritance of
+ // QuicTimeWaitListManager::Visitor):
+ // Queues the blocked writer for later resumption.
+ void OnWriteBlocked(QuicBlockedWriterInterface* blocked_writer) override;
+
+ // QuicSession::Visitor interface implementation (via inheritance of
+ // QuicTimeWaitListManager::Visitor):
+ // Collects reset error code received on streams.
+ void OnRstStreamReceived(const QuicRstStreamFrame& frame) override;
+
+ // QuicSession::Visitor interface implementation (via inheritance of
+ // QuicTimeWaitListManager::Visitor):
+ // Collects reset error code received on streams.
+ void OnStopSendingReceived(const QuicStopSendingFrame& frame) override;
+
+ // QuicTimeWaitListManager::Visitor interface implementation
+ // Called whenever the time wait list manager adds a new connection to the
+ // time-wait list.
+ void OnConnectionAddedToTimeWaitList(QuicConnectionId connection_id) override;
+
+ using SessionMap = QuicUnorderedMap<QuicConnectionId,
+ std::unique_ptr<QuicSession>,
+ QuicConnectionIdHash>;
+
+ const SessionMap& session_map() const { return session_map_; }
+
+ // Deletes all sessions on the closed session list and clears the list.
+ virtual void DeleteSessions();
+
+ using ConnectionIdMap = QuicUnorderedMap<QuicConnectionId,
+ QuicConnectionId,
+ QuicConnectionIdHash>;
+
+ const ConnectionIdMap& connection_id_map() const {
+ return connection_id_map_;
+ }
+
+ // The largest packet number we expect to receive with a connection
+ // ID for a connection that is not established yet. The current design will
+ // send a handshake and then up to 50 or so data packets, and then it may
+ // resend the handshake packet up to 10 times. (Retransmitted packets are
+ // sent with unique packet numbers.)
+ static const uint64_t kMaxReasonableInitialPacketNumber = 100;
+ static_assert(kMaxReasonableInitialPacketNumber >=
+ kInitialCongestionWindow + 10,
+ "kMaxReasonableInitialPacketNumber is unreasonably small "
+ "relative to kInitialCongestionWindow.");
+
+ // QuicFramerVisitorInterface implementation. Not expected to be called
+ // outside of this class.
+ void OnPacket() override;
+ // Called when the public header has been parsed.
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
+ // Called when the private header has been parsed of a data packet that is
+ // destined for the time wait manager.
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
+ void OnError(QuicFramer* framer) override;
+ bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
+ PacketHeaderFormat form) override;
+
+ // The following methods should never get called because
+ // OnUnauthenticatedPublicHeader() or OnUnauthenticatedHeader() (whichever
+ // was called last), will return false and prevent a subsequent invocation
+ // of these methods. Thus, the payload of the packet is never processed in
+ // the dispatcher.
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override;
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override;
+ void OnDecryptedPacket(EncryptionLevel level) override;
+ bool OnPacketHeader(const QuicPacketHeader& header) override;
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
+ bool OnStreamFrame(const QuicStreamFrame& frame) override;
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override;
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override;
+ bool OnAckFrameEnd(QuicPacketNumber start) override;
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
+ bool OnPingFrame(const QuicPingFrame& frame) override;
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override;
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override;
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override;
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override;
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override;
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override;
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override;
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override;
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override;
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
+ bool OnMessageFrame(const QuicMessageFrame& frame) override;
+ void OnPacketComplete() override;
+ bool IsValidStatelessResetToken(QuicUint128 token) const override;
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override;
+
+ // QuicBufferedPacketStore::VisitorInterface implementation.
+ void OnExpiredPackets(QuicConnectionId connection_id,
+ QuicBufferedPacketStore::BufferedPacketList
+ early_arrived_packets) override;
+
+ // Create connections for previously buffered CHLOs as many as allowed.
+ virtual void ProcessBufferedChlos(size_t max_connections_to_create);
+
+ // Return true if there is CHLO buffered.
+ virtual bool HasChlosBuffered() const;
+
+ protected:
+ virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id,
+ const QuicSocketAddress& peer_address,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) = 0;
+
+ // Called when a connection is rejected statelessly.
+ virtual void OnConnectionRejectedStatelessly() {}
+
+ // Returns true if cheap stateless rejection should be attempted.
+ virtual bool ShouldAttemptCheapStatelessRejection();
+
+ // Values to be returned by ValidityChecks() to indicate what should be done
+ // with a packet. Fates with greater values are considered to be higher
+ // priority, in that if one validity check indicates a lower-valued fate and
+ // another validity check indicates a higher-valued fate, the higher-valued
+ // fate should be obeyed.
+ enum QuicPacketFate {
+ // Process the packet normally, which is usually to establish a connection.
+ kFateProcess,
+ // Put the connection ID into time-wait state and send a public reset.
+ kFateTimeWait,
+ // Buffer the packet.
+ kFateBuffer,
+ // Drop the packet (ignore and give no response).
+ kFateDrop,
+ };
+
+ // This method is called by OnUnauthenticatedHeader on packets not associated
+ // with a known connection ID. It applies validity checks and returns a
+ // QuicPacketFate to tell what should be done with the packet.
+ virtual QuicPacketFate ValidityChecks(const QuicPacketHeader& header);
+
+ // Create and return the time wait list manager for this dispatcher, which
+ // will be owned by the dispatcher as time_wait_list_manager_
+ virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager();
+
+ // Called when |connection_id| doesn't have an open connection yet, to buffer
+ // |current_packet_| until it can be delivered to the connection.
+ void BufferEarlyPacket(QuicConnectionId connection_id,
+ bool ietf_quic,
+ ParsedQuicVersion version);
+
+ // Called when |current_packet_| is a CHLO packet. Creates a new connection
+ // and delivers any buffered packets for that connection id.
+ void ProcessChlo(PacketHeaderFormat form, ParsedQuicVersion version);
+
+ // Returns the actual client address of the current packet.
+ // This function should only be called once per packet at the very beginning
+ // of ProcessPacket(), its result is saved to |current_client_address_|, which
+ // is guaranteed to be valid even in the stateless rejector's callback(i.e.
+ // OnStatelessRejectorProcessDone).
+ // By default, this function returns |current_peer_address_|, subclasses have
+ // the option to override this function to return a different address.
+ virtual const QuicSocketAddress GetClientAddress() const;
+
+ // Return true if dispatcher wants to destroy session outside of
+ // OnConnectionClosed() call stack.
+ virtual bool ShouldDestroySessionAsynchronously();
+
+ QuicTimeWaitListManager* time_wait_list_manager() {
+ return time_wait_list_manager_.get();
+ }
+
+ const QuicTransportVersionVector& GetSupportedTransportVersions();
+
+ const ParsedQuicVersionVector& GetSupportedVersions();
+
+ QuicConnectionId current_connection_id() const {
+ return current_connection_id_;
+ }
+ const QuicSocketAddress& current_self_address() const {
+ return current_self_address_;
+ }
+ const QuicSocketAddress& current_peer_address() const {
+ return current_peer_address_;
+ }
+ const QuicSocketAddress& current_client_address() const {
+ return current_client_address_;
+ }
+ const QuicReceivedPacket& current_packet() const { return *current_packet_; }
+
+ const QuicConfig& config() const { return *config_; }
+
+ const QuicCryptoServerConfig* crypto_config() const { return crypto_config_; }
+
+ QuicCompressedCertsCache* compressed_certs_cache() {
+ return &compressed_certs_cache_;
+ }
+
+ QuicConnectionHelperInterface* helper() { return helper_.get(); }
+
+ QuicCryptoServerStream::Helper* session_helper() {
+ return session_helper_.get();
+ }
+
+ QuicAlarmFactory* alarm_factory() { return alarm_factory_.get(); }
+
+ QuicPacketWriter* writer() { return writer_.get(); }
+
+ // Returns true if a session should be created for a connection with an
+ // unknown version identified by |version_label|.
+ virtual bool ShouldCreateSessionForUnknownVersion(
+ QuicVersionLabel version_label);
+
+ void SetLastError(QuicErrorCode error);
+
+ // Called when the public header has been parsed and the session has been
+ // looked up, and the session was not found in the active list of sessions.
+ // Returns false if processing should stop after this call.
+ virtual bool OnUnauthenticatedUnknownPublicHeader(
+ const QuicPacketHeader& header);
+
+ // Called when a new connection starts to be handled by this dispatcher.
+ // Either this connection is created or its packets is buffered while waiting
+ // for CHLO. Returns true if a new connection should be created or its packets
+ // should be buffered, false otherwise.
+ virtual bool ShouldCreateOrBufferPacketForConnection(
+ QuicConnectionId connection_id,
+ bool ietf_quic);
+
+ bool HasBufferedPackets(QuicConnectionId connection_id);
+
+ // Called when BufferEarlyPacket() fail to buffer the packet.
+ virtual void OnBufferPacketFailure(
+ QuicBufferedPacketStore::EnqueuePacketResult result,
+ QuicConnectionId connection_id);
+
+ // Removes the session from the session map and write blocked list, and adds
+ // the ConnectionId to the time-wait list. If |session_closed_statelessly| is
+ // true, any future packets for the ConnectionId will be black-holed.
+ virtual void CleanUpSession(SessionMap::iterator it,
+ QuicConnection* connection,
+ bool session_closed_statelessly,
+ ConnectionCloseSource source);
+
+ void StopAcceptingNewConnections();
+
+ // Called to terminate a connection statelessly. Depending on |format|, either
+ // 1) send connection close with |error_code| and |error_details| and add
+ // connection to time wait list or 2) directly add connection to time wait
+ // list with |action|.
+ void StatelesslyTerminateConnection(
+ QuicConnectionId connection_id,
+ PacketHeaderFormat format,
+ ParsedQuicVersion version,
+ QuicErrorCode error_code,
+ const std::string& error_details,
+ QuicTimeWaitListManager::TimeWaitAction action);
+
+ // Save/Restore per packet context. Used by async stateless rejector.
+ virtual std::unique_ptr<QuicPerPacketContext> GetPerPacketContext() const;
+ virtual void RestorePerPacketContext(
+ std::unique_ptr<QuicPerPacketContext> /*context*/) {}
+
+ // Skip validating that the public flags are set to legal values.
+ void DisableFlagValidation();
+
+ // If true, our framer will change its expected connection ID length
+ // to the received destination connection ID length of all IETF long headers.
+ void SetShouldUpdateExpectedConnectionIdLength(
+ bool should_update_expected_connection_id_length) {
+ framer_.SetShouldUpdateExpectedConnectionIdLength(
+ should_update_expected_connection_id_length);
+ }
+
+ // If true, the dispatcher will allow incoming initial packets that have
+ // connection IDs shorter than 64 bits.
+ void SetAllowShortInitialConnectionIds(
+ bool allow_short_initial_connection_ids) {
+ allow_short_initial_connection_ids_ = allow_short_initial_connection_ids;
+ }
+
+ private:
+ friend class test::QuicDispatcherPeer;
+ friend class StatelessRejectorProcessDoneCallback;
+
+ typedef QuicUnorderedSet<QuicConnectionId, QuicConnectionIdHash>
+ QuicConnectionIdSet;
+
+ // Attempts to reject the connection statelessly, if stateless rejects are
+ // possible and if the current packet contains a CHLO message. Determines a
+ // fate which describes what subsequent processing should be performed on the
+ // packets, like ValidityChecks, and invokes ProcessUnauthenticatedHeaderFate.
+ void MaybeRejectStatelessly(QuicConnectionId connection_id,
+ PacketHeaderFormat form,
+ ParsedQuicVersion version);
+
+ // Deliver |packets| to |session| for further processing.
+ void DeliverPacketsToSession(
+ const std::list<QuicBufferedPacketStore::BufferedPacket>& packets,
+ QuicSession* session);
+
+ // Perform the appropriate actions on the current packet based on |fate| -
+ // either process, buffer, or drop it.
+ void ProcessUnauthenticatedHeaderFate(QuicPacketFate fate,
+ QuicConnectionId connection_id,
+ PacketHeaderFormat form,
+ ParsedQuicVersion version);
+
+ // Invoked when StatelessRejector::Process completes. |first_version| is the
+ // version of the packet which initiated the stateless reject.
+ // WARNING: This function can be called when a async proof returns, i.e. not
+ // from a stack traceable to ProcessPacket().
+ // TODO(fayang): maybe consider not using callback when there is no crypto
+ // involved.
+ void OnStatelessRejectorProcessDone(
+ std::unique_ptr<StatelessRejector> rejector,
+ const QuicSocketAddress& current_client_address,
+ const QuicSocketAddress& current_peer_address,
+ const QuicSocketAddress& current_self_address,
+ std::unique_ptr<QuicReceivedPacket> current_packet,
+ ParsedQuicVersion first_version,
+ PacketHeaderFormat current_packet_format);
+
+ // Examine the state of the rejector and decide what to do with the current
+ // packet.
+ void ProcessStatelessRejectorState(
+ std::unique_ptr<StatelessRejector> rejector,
+ QuicTransportVersion first_version,
+ PacketHeaderFormat form);
+
+ // If the connection ID length is different from what the dispatcher expects,
+ // replace the connection ID with a random one of the right length,
+ // and save it to make sure the mapping is persistent.
+ QuicConnectionId MaybeReplaceConnectionId(QuicConnectionId connection_id,
+ ParsedQuicVersion version);
+
+ void set_new_sessions_allowed_per_event_loop(
+ int16_t new_sessions_allowed_per_event_loop) {
+ new_sessions_allowed_per_event_loop_ = new_sessions_allowed_per_event_loop;
+ }
+
+ const QuicConfig* config_;
+
+ const QuicCryptoServerConfig* crypto_config_;
+
+ // The cache for most recently compressed certs.
+ QuicCompressedCertsCache compressed_certs_cache_;
+
+ // The list of connections waiting to write.
+ WriteBlockedList write_blocked_list_;
+
+ SessionMap session_map_;
+
+ // Map of connection IDs with bad lengths to their replacements.
+ ConnectionIdMap connection_id_map_;
+
+ // Entity that manages connection_ids in time wait state.
+ std::unique_ptr<QuicTimeWaitListManager> time_wait_list_manager_;
+
+ // The list of closed but not-yet-deleted sessions.
+ std::vector<std::unique_ptr<QuicSession>> closed_session_list_;
+
+ // The helper used for all connections.
+ std::unique_ptr<QuicConnectionHelperInterface> helper_;
+
+ // The helper used for all sessions.
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper_;
+
+ // Creates alarms.
+ std::unique_ptr<QuicAlarmFactory> alarm_factory_;
+
+ // An alarm which deletes closed sessions.
+ std::unique_ptr<QuicAlarm> delete_sessions_alarm_;
+
+ // The writer to write to the socket with.
+ std::unique_ptr<QuicPacketWriter> writer_;
+
+ // Packets which are buffered until a connection can be created to handle
+ // them.
+ QuicBufferedPacketStore buffered_packets_;
+
+ // Set of connection IDs for which asynchronous CHLO processing is in
+ // progress, making it necessary to buffer any other packets which arrive on
+ // that connection until CHLO processing is complete.
+ QuicConnectionIdSet temporarily_buffered_connections_;
+
+ // Information about the packet currently being handled.
+
+ // Used for stateless rejector to generate and validate source address token.
+ QuicSocketAddress current_client_address_;
+ QuicSocketAddress current_peer_address_;
+ QuicSocketAddress current_self_address_;
+ const QuicReceivedPacket* current_packet_;
+ // If |current_packet_| is a CHLO packet, the extracted alpn.
+ std::string current_alpn_;
+ QuicConnectionId current_connection_id_;
+
+ // Used to get the supported versions based on flag. Does not own.
+ QuicVersionManager* version_manager_;
+
+ QuicFramer framer_;
+
+ // The last error set by SetLastError(), which is called by
+ // framer_visitor_->OnError().
+ QuicErrorCode last_error_;
+
+ // A backward counter of how many new sessions can be create within current
+ // event loop. When reaches 0, it means can't create sessions for now.
+ int16_t new_sessions_allowed_per_event_loop_;
+
+ // True if this dispatcher is not draining.
+ bool accept_new_connections_;
+
+ // If false, the dispatcher follows the IETF spec and rejects packets with
+ // invalid connection IDs lengths below 64 bits. If true they are allowed.
+ bool allow_short_initial_connection_ids_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_DISPATCHER_H_
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
new file mode 100644
index 00000000000..1e646af3445
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_dispatcher_test.cc
@@ -0,0 +1,2885 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+
+#include <memory>
+#include <ostream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/chlo_extractor.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/stateless_rejector.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+
+using testing::_;
+using testing::InSequence;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+using testing::WithArg;
+using testing::WithoutArgs;
+
+static const size_t kDefaultMaxConnectionsInStore = 100;
+static const size_t kMaxConnectionsWithoutCHLO =
+ kDefaultMaxConnectionsInStore / 2;
+static const int16_t kMaxNumSessionsToCreate = 16;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestQuicSpdyServerSession : public QuicServerSessionBase {
+ public:
+ TestQuicSpdyServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache)
+ : QuicServerSessionBase(config,
+ CurrentSupportedVersions(),
+ connection,
+ nullptr,
+ nullptr,
+ crypto_config,
+ compressed_certs_cache),
+ crypto_stream_(QuicServerSessionBase::GetMutableCryptoStream()) {}
+ TestQuicSpdyServerSession(const TestQuicSpdyServerSession&) = delete;
+ TestQuicSpdyServerSession& operator=(const TestQuicSpdyServerSession&) =
+ delete;
+
+ ~TestQuicSpdyServerSession() override { delete connection(); }
+
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream pending));
+ MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+
+ QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) override {
+ return new QuicCryptoServerStream(
+ crypto_config, compressed_certs_cache,
+ GetQuicReloadableFlag(enable_quic_stateless_reject_support), this,
+ stream_helper());
+ }
+
+ void SetCryptoStream(QuicCryptoServerStream* crypto_stream) {
+ crypto_stream_ = crypto_stream;
+ }
+
+ QuicCryptoServerStreamBase* GetMutableCryptoStream() override {
+ return crypto_stream_;
+ }
+
+ const QuicCryptoServerStreamBase* GetCryptoStream() const override {
+ return crypto_stream_;
+ }
+
+ QuicCryptoServerStream::Helper* stream_helper() {
+ return QuicServerSessionBase::stream_helper();
+ }
+
+ private:
+ QuicCryptoServerStreamBase* crypto_stream_;
+};
+
+class TestDispatcher : public QuicDispatcher {
+ public:
+ TestDispatcher(const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ QuicRandom* random)
+ : QuicDispatcher(config,
+ crypto_config,
+ version_manager,
+ QuicMakeUnique<MockQuicConnectionHelper>(),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(random)),
+ QuicMakeUnique<MockAlarmFactory>(),
+ kQuicDefaultConnectionIdLength) {}
+
+ MOCK_METHOD4(CreateQuicSession,
+ QuicServerSessionBase*(QuicConnectionId connection_id,
+ const QuicSocketAddress& peer_address,
+ QuicStringPiece alpn,
+ const quic::ParsedQuicVersion& version));
+
+ MOCK_METHOD2(ShouldCreateOrBufferPacketForConnection,
+ bool(QuicConnectionId connection_id, bool ietf_quic));
+
+ struct TestQuicPerPacketContext : public QuicPerPacketContext {
+ std::string custom_packet_context;
+ };
+
+ std::unique_ptr<QuicPerPacketContext> GetPerPacketContext() const override {
+ auto test_context = QuicMakeUnique<TestQuicPerPacketContext>();
+ test_context->custom_packet_context = custom_packet_context_;
+ return std::move(test_context);
+ }
+
+ void RestorePerPacketContext(
+ std::unique_ptr<QuicPerPacketContext> context) override {
+ TestQuicPerPacketContext* test_context =
+ static_cast<TestQuicPerPacketContext*>(context.get());
+ custom_packet_context_ = test_context->custom_packet_context;
+ }
+
+ std::string custom_packet_context_;
+
+ using QuicDispatcher::current_client_address;
+ using QuicDispatcher::current_peer_address;
+ using QuicDispatcher::current_self_address;
+ using QuicDispatcher::SetAllowShortInitialConnectionIds;
+ using QuicDispatcher::writer;
+};
+
+// A Connection class which unregisters the session from the dispatcher when
+// sending connection close.
+// It'd be slightly more realistic to do this from the Session but it would
+// involve a lot more mocking.
+class MockServerConnection : public MockQuicConnection {
+ public:
+ MockServerConnection(QuicConnectionId connection_id,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicDispatcher* dispatcher)
+ : MockQuicConnection(connection_id,
+ helper,
+ alarm_factory,
+ Perspective::IS_SERVER),
+ dispatcher_(dispatcher) {}
+
+ void UnregisterOnConnectionClosed() {
+ QUIC_LOG(ERROR) << "Unregistering " << connection_id();
+ dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR,
+ "Unregistering.",
+ ConnectionCloseSource::FROM_SELF);
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+};
+
+class QuicDispatcherTest : public QuicTest {
+ public:
+ QuicDispatcherTest()
+ : QuicDispatcherTest(crypto_test_utils::ProofSourceForTesting()) {}
+
+ ParsedQuicVersionVector AllSupportedVersionsIncludingTls() {
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ return AllSupportedVersions();
+ }
+
+ explicit QuicDispatcherTest(std::unique_ptr<ProofSource> proof_source)
+ :
+
+ version_manager_(AllSupportedVersionsIncludingTls()),
+ crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ std::move(proof_source),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ server_address_(QuicIpAddress::Any4(), 5),
+ dispatcher_(
+ new NiceMock<TestDispatcher>(&config_,
+ &crypto_config_,
+ &version_manager_,
+ mock_helper_.GetRandomGenerator())),
+ time_wait_list_manager_(nullptr),
+ session1_(nullptr),
+ session2_(nullptr),
+ store_(nullptr) {}
+
+ void SetUp() override {
+ dispatcher_->InitializeWithWriter(new MockPacketWriter());
+ // Set the counter to some value to start with.
+ QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(
+ dispatcher_.get(), kMaxNumSessionsToCreate);
+ ON_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(_, _))
+ .WillByDefault(Return(true));
+ }
+
+ MockQuicConnection* connection1() {
+ if (session1_ == nullptr) {
+ return nullptr;
+ }
+ return reinterpret_cast<MockQuicConnection*>(session1_->connection());
+ }
+
+ MockQuicConnection* connection2() {
+ if (session2_ == nullptr) {
+ return nullptr;
+ }
+ return reinterpret_cast<MockQuicConnection*>(session2_->connection());
+ }
+
+ // Process a packet with an 8 byte connection id,
+ // 6 byte packet number, default path id, and packet number 1,
+ // using the first supported version.
+ void ProcessPacket(QuicSocketAddress peer_address,
+ QuicConnectionId connection_id,
+ bool has_version_flag,
+ const std::string& data) {
+ ProcessPacket(peer_address, connection_id, has_version_flag, data,
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER);
+ }
+
+ // Process a packet with a default path id, and packet number 1,
+ // using the first supported version.
+ void ProcessPacket(QuicSocketAddress peer_address,
+ QuicConnectionId connection_id,
+ bool has_version_flag,
+ const std::string& data,
+ QuicConnectionIdIncluded connection_id_included,
+ QuicPacketNumberLength packet_number_length) {
+ ProcessPacket(peer_address, connection_id, has_version_flag, data,
+ connection_id_included, packet_number_length, 1);
+ }
+
+ // Process a packet using the first supported version.
+ void ProcessPacket(QuicSocketAddress peer_address,
+ QuicConnectionId connection_id,
+ bool has_version_flag,
+ const std::string& data,
+ QuicConnectionIdIncluded connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ uint64_t packet_number) {
+ ProcessPacket(peer_address, connection_id, has_version_flag,
+ CurrentSupportedVersions().front(), data,
+ connection_id_included, packet_number_length, packet_number);
+ }
+
+ // Processes a packet.
+ void ProcessPacket(QuicSocketAddress peer_address,
+ QuicConnectionId connection_id,
+ bool has_version_flag,
+ ParsedQuicVersion version,
+ const std::string& data,
+ QuicConnectionIdIncluded connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ uint64_t packet_number) {
+ ParsedQuicVersionVector versions(SupportedVersions(version));
+ std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
+ connection_id, EmptyQuicConnectionId(), has_version_flag, false,
+ packet_number, data, connection_id_included, CONNECTION_ID_ABSENT,
+ packet_number_length, &versions));
+ std::unique_ptr<QuicReceivedPacket> received_packet(
+ ConstructReceivedPacket(*packet, mock_helper_.GetClock()->Now()));
+
+ if (ChloExtractor::Extract(*packet, versions, {}, nullptr,
+ connection_id.length())) {
+ // Add CHLO packet to the beginning to be verified first, because it is
+ // also processed first by new session.
+ data_connection_map_[connection_id].push_front(
+ std::string(packet->data(), packet->length()));
+ } else {
+ // For non-CHLO, always append to last.
+ data_connection_map_[connection_id].push_back(
+ std::string(packet->data(), packet->length()));
+ }
+ dispatcher_->ProcessPacket(server_address_, peer_address, *received_packet);
+ }
+
+ void ValidatePacket(QuicConnectionId conn_id,
+ const QuicEncryptedPacket& packet) {
+ EXPECT_EQ(data_connection_map_[conn_id].front().length(),
+ packet.AsStringPiece().length());
+ EXPECT_EQ(data_connection_map_[conn_id].front(), packet.AsStringPiece());
+ data_connection_map_[conn_id].pop_front();
+ }
+
+ QuicServerSessionBase* CreateSession(
+ TestDispatcher* dispatcher,
+ const QuicConfig& config,
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& peer_address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ TestQuicSpdyServerSession** session) {
+ MockServerConnection* connection = new MockServerConnection(
+ connection_id, helper, alarm_factory, dispatcher);
+ connection->SetQuicPacketWriter(dispatcher->writer(),
+ /*owns_writer=*/false);
+ *session = new TestQuicSpdyServerSession(config, connection, crypto_config,
+ compressed_certs_cache);
+ connection->set_visitor(*session);
+ ON_CALL(*connection, CloseConnection(_, _, _))
+ .WillByDefault(WithoutArgs(Invoke(
+ connection, &MockServerConnection::UnregisterOnConnectionClosed)));
+ return *session;
+ }
+
+ void CreateTimeWaitListManager() {
+ time_wait_list_manager_ = new MockTimeWaitListManager(
+ QuicDispatcherPeer::GetWriter(dispatcher_.get()), dispatcher_.get(),
+ mock_helper_.GetClock(), &mock_alarm_factory_);
+ // dispatcher_ takes the ownership of time_wait_list_manager_.
+ QuicDispatcherPeer::SetTimeWaitListManager(dispatcher_.get(),
+ time_wait_list_manager_);
+ }
+
+ std::string SerializeCHLO() {
+ CryptoHandshakeMessage client_hello;
+ client_hello.set_tag(kCHLO);
+ client_hello.SetStringPiece(kALPN, "hq");
+ return std::string(client_hello.GetSerialized().AsStringPiece());
+ }
+
+ std::string SerializeTlsClientHello() { return ""; }
+
+ void MarkSession1Deleted() { session1_ = nullptr; }
+
+ MockQuicConnectionHelper mock_helper_;
+ MockAlarmFactory mock_alarm_factory_;
+ QuicConfig config_;
+ QuicVersionManager version_manager_;
+ QuicCryptoServerConfig crypto_config_;
+ QuicSocketAddress server_address_;
+ std::unique_ptr<NiceMock<TestDispatcher>> dispatcher_;
+ MockTimeWaitListManager* time_wait_list_manager_;
+ TestQuicSpdyServerSession* session1_;
+ TestQuicSpdyServerSession* session2_;
+ std::map<QuicConnectionId, std::list<std::string>> data_connection_map_;
+ QuicBufferedPacketStore* store_;
+};
+
+TEST_F(QuicDispatcherTest, TlsClientHelloCreatesSession) {
+ if (!QuicVersionUsesCryptoFrames(
+ CurrentSupportedVersions().front().transport_version)) {
+ // TLS is only supported in versions 47 and greater.
+ return;
+ }
+ FLAGS_quic_supports_tls_handshake = true;
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ QuicStringPiece(""), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _));
+ ProcessPacket(
+ client_address, TestConnectionId(1), true,
+ ParsedQuicVersion(PROTOCOL_TLS1_3,
+ CurrentSupportedVersions().front().transport_version),
+ SerializeCHLO(), CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+ EXPECT_EQ(client_address, dispatcher_->current_peer_address());
+ EXPECT_EQ(server_address_, dispatcher_->current_self_address());
+}
+
+TEST_F(QuicDispatcherTest, ProcessPackets) {
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _));
+ ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
+ EXPECT_EQ(client_address, dispatcher_->current_peer_address());
+ EXPECT_EQ(server_address_, dispatcher_->current_self_address());
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(2), client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(2), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(2), packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(TestConnectionId(2), _));
+ ProcessPacket(client_address, TestConnectionId(2), true, SerializeCHLO());
+
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(1)
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ ProcessPacket(client_address, TestConnectionId(1), false, "data");
+}
+
+// Regression test of b/93325907.
+TEST_F(QuicDispatcherTest, DispatcherDoesNotRejectPacketNumberZero) {
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ // Verify both packets 1 and 2 are processed by connection 1.
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(2)
+ .WillRepeatedly(
+ WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _));
+ ProcessPacket(
+ client_address, TestConnectionId(1), true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO,
+ CurrentSupportedVersions().front().transport_version),
+ SerializeCHLO(), CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+ // Packet number 256 with packet number length 1 would be considered as 0 in
+ // dispatcher.
+ ProcessPacket(
+ client_address, TestConnectionId(1), false,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO,
+ CurrentSupportedVersions().front().transport_version),
+ "", CONNECTION_ID_PRESENT, PACKET_1BYTE_PACKET_NUMBER, 256);
+ EXPECT_EQ(client_address, dispatcher_->current_peer_address());
+ EXPECT_EQ(server_address_, dispatcher_->current_self_address());
+}
+
+TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) {
+ CreateTimeWaitListManager();
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ SendVersionNegotiationPacket(_, _, _, _, _, _))
+ .Times(1);
+ QuicTransportVersion version =
+ static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1);
+ ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version);
+ // Pad the CHLO message with enough data to make the packet large enough
+ // to trigger version negotiation.
+ std::string chlo = SerializeCHLO() + std::string(1200, 'a');
+ DCHECK_LE(1200u, chlo.length());
+ ProcessPacket(client_address, TestConnectionId(1), true, parsed_version, chlo,
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+}
+
+TEST_F(QuicDispatcherTest, NoVersionNegotiationWithSmallPacket) {
+ CreateTimeWaitListManager();
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ SendVersionNegotiationPacket(_, _, _, _, _, _))
+ .Times(0);
+ QuicTransportVersion version =
+ static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1);
+ ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version);
+ std::string chlo = SerializeCHLO() + std::string(1200, 'a');
+ // Truncate to 1100 bytes of payload which results in a packet just
+ // under 1200 bytes after framing, packet, and encryption overhead.
+ DCHECK_LE(1200u, chlo.length());
+ std::string truncated_chlo = chlo.substr(0, 1100);
+ DCHECK_EQ(1100u, truncated_chlo.length());
+ ProcessPacket(client_address, TestConnectionId(1), true, parsed_version,
+ truncated_chlo, CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+}
+
+// Disabling CHLO size validation allows the dispatcher to send version
+// negotiation packets in response to a CHLO that is otherwise too small.
+TEST_F(QuicDispatcherTest, VersionNegotiationWithoutChloSizeValidation) {
+ crypto_config_.set_validate_chlo_size(false);
+
+ CreateTimeWaitListManager();
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ SendVersionNegotiationPacket(_, _, _, _, _, _))
+ .Times(1);
+ QuicTransportVersion version =
+ static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1);
+ ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version);
+ std::string chlo = SerializeCHLO() + std::string(1200, 'a');
+ // Truncate to 1100 bytes of payload which results in a packet just
+ // under 1200 bytes after framing, packet, and encryption overhead.
+ DCHECK_LE(1200u, chlo.length());
+ std::string truncated_chlo = chlo.substr(0, 1100);
+ DCHECK_EQ(1100u, truncated_chlo.length());
+ ProcessPacket(client_address, TestConnectionId(1), true, parsed_version,
+ truncated_chlo, CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+}
+
+TEST_F(QuicDispatcherTest, Shutdown) {
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(_, client_address, QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _));
+ ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
+
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
+
+ dispatcher_->Shutdown();
+}
+
+TEST_F(QuicDispatcherTest, TimeWaitListManager) {
+ CreateTimeWaitListManager();
+
+ // Create a new session.
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId connection_id = TestConnectionId(1);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _));
+ ProcessPacket(client_address, connection_id, true, SerializeCHLO());
+
+ // Now close the connection, which should add it to the time wait list.
+ session1_->connection()->CloseConnection(
+ QUIC_INVALID_VERSION,
+ "Server: Packet 2 without version flag before version negotiated.",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+
+ // Dispatcher forwards subsequent packets for this connection_id to the time
+ // wait list manager.
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, _, connection_id, _, _))
+ .Times(1);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(0);
+ ProcessPacket(client_address, connection_id, true, "data");
+}
+
+TEST_F(QuicDispatcherTest, NoVersionPacketToTimeWaitListManager) {
+ CreateTimeWaitListManager();
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId connection_id = TestConnectionId(1);
+ // Dispatcher forwards all packets for this connection_id to the time wait
+ // list manager.
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, _, connection_id, _, _))
+ .Times(1);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(1);
+ ProcessPacket(client_address, connection_id, false, SerializeCHLO());
+}
+
+// Makes sure nine-byte connection IDs are replaced by 8-byte ones.
+TEST_F(QuicDispatcherTest, LongConnectionIdLengthReplaced) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ CurrentSupportedVersions()[0].transport_version)) {
+ // When variable length connection IDs are not supported, the connection
+ // fails. See StrayPacketTruncatedConnectionId.
+ return;
+ }
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ QuicConnectionId bad_connection_id = TestConnectionIdNineBytesLong(2);
+ QuicConnectionId fixed_connection_id =
+ QuicUtils::CreateRandomConnectionId(mock_helper_.GetRandomGenerator());
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(fixed_connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, fixed_connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, bad_connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(bad_connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(bad_connection_id, _));
+ ProcessPacket(client_address, bad_connection_id, true, SerializeCHLO());
+ EXPECT_EQ(client_address, dispatcher_->current_peer_address());
+ EXPECT_EQ(server_address_, dispatcher_->current_self_address());
+}
+
+// Makes sure zero-byte connection IDs are replaced by 8-byte ones.
+TEST_F(QuicDispatcherTest, InvalidShortConnectionIdLengthReplaced) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ CurrentSupportedVersions()[0].transport_version)) {
+ // When variable length connection IDs are not supported, the connection
+ // fails. See StrayPacketTruncatedConnectionId.
+ return;
+ }
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ QuicConnectionId bad_connection_id = EmptyQuicConnectionId();
+ QuicConnectionId fixed_connection_id =
+ QuicUtils::CreateRandomConnectionId(mock_helper_.GetRandomGenerator());
+
+ // Disable validation of invalid short connection IDs.
+ dispatcher_->SetAllowShortInitialConnectionIds(true);
+ // Note that StrayPacketTruncatedConnectionId covers the case where the
+ // validation is still enabled.
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(fixed_connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, fixed_connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, bad_connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(bad_connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(bad_connection_id, _));
+ ProcessPacket(client_address, bad_connection_id, true, SerializeCHLO());
+ EXPECT_EQ(client_address, dispatcher_->current_peer_address());
+ EXPECT_EQ(server_address_, dispatcher_->current_self_address());
+}
+
+// Makes sure TestConnectionId(1) creates a new connection and
+// TestConnectionIdNineBytesLong(2) gets replaced.
+TEST_F(QuicDispatcherTest, MixGoodAndBadConnectionIdLengthPackets) {
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ CurrentSupportedVersions()[0].transport_version)) {
+ return;
+ }
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId bad_connection_id = TestConnectionIdNineBytesLong(2);
+ QuicConnectionId fixed_connection_id =
+ QuicUtils::CreateRandomConnectionId(mock_helper_.GetRandomGenerator());
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(TestConnectionId(1), _));
+ ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
+ EXPECT_EQ(client_address, dispatcher_->current_peer_address());
+ EXPECT_EQ(server_address_, dispatcher_->current_self_address());
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(fixed_connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, fixed_connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, bad_connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(bad_connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(bad_connection_id, _));
+ ProcessPacket(client_address, bad_connection_id, true, SerializeCHLO());
+
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(1)
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ ProcessPacket(client_address, TestConnectionId(1), false, "data");
+}
+
+TEST_F(QuicDispatcherTest, ProcessPacketWithZeroPort) {
+ CreateTimeWaitListManager();
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 0);
+
+ // dispatcher_ should drop this packet.
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ QuicStringPiece("hq"), _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(0);
+ ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
+}
+
+TEST_F(QuicDispatcherTest, OKSeqNoPacketProcessed) {
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId connection_id = TestConnectionId(1);
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(1), client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+
+ // A packet whose packet number is the largest that is allowed to start a
+ // connection.
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _));
+ ProcessPacket(client_address, connection_id, true, SerializeCHLO(),
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+ QuicDispatcher::kMaxReasonableInitialPacketNumber);
+ EXPECT_EQ(client_address, dispatcher_->current_peer_address());
+ EXPECT_EQ(server_address_, dispatcher_->current_self_address());
+}
+
+TEST_F(QuicDispatcherTest, TooBigSeqNoPacketToTimeWaitListManager) {
+ CreateTimeWaitListManager();
+ SetQuicRestartFlag(quic_enable_accept_random_ipn, false);
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId connection_id = TestConnectionId(1);
+
+ // Dispatcher forwards this packet for this connection_id to the time wait
+ // list manager.
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, _, TestConnectionId(1), _, _))
+ .Times(1);
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, _, TestConnectionId(2), _, _))
+ .Times(1);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(2);
+ // A packet whose packet number is one to large to be allowed to start a
+ // connection.
+ ProcessPacket(client_address, connection_id, true, SerializeCHLO(),
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+ QuicDispatcher::kMaxReasonableInitialPacketNumber + 1);
+ connection_id = TestConnectionId(2);
+ SetQuicRestartFlag(quic_enable_accept_random_ipn, true);
+ ProcessPacket(client_address, connection_id, true, SerializeCHLO(),
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+ MaxRandomInitialPacketNumber().ToUint64() +
+ QuicDispatcher::kMaxReasonableInitialPacketNumber + 1);
+}
+
+TEST_F(QuicDispatcherTest, SupportedTransportVersionsChangeInFlight) {
+ static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u,
+ "Supported versions out of sync");
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_47, true);
+ SetQuicReloadableFlag(quic_enable_version_99, true);
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ uint64_t conn_id = 1;
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .Times(0);
+ ParsedQuicVersion version(
+ PROTOCOL_QUIC_CRYPTO,
+ static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1));
+ ProcessPacket(client_address, connection_id, true, version, SerializeCHLO(),
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER, 1);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _));
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO,
+ QuicVersionMin().transport_version),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _));
+ ProcessPacket(client_address, connection_id, true, QuicVersionMax(),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn off version 47.
+ SetQuicReloadableFlag(quic_enable_version_47, false);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .Times(0);
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn on version 47.
+ SetQuicReloadableFlag(quic_enable_version_47, true);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _));
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn off version 46.
+ SetQuicReloadableFlag(quic_enable_version_46, false);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .Times(0);
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn on version 46.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _));
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn off version 44.
+ SetQuicReloadableFlag(quic_enable_version_44, false);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .Times(0);
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn on version 44.
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _));
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn off version 43.
+ SetQuicReloadableFlag(quic_enable_version_43, false);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .Times(0);
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn on version 43.
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _));
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn off version 39.
+ SetQuicReloadableFlag(quic_disable_version_39, true);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .Times(0);
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Turn on version 39.
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ connection_id = TestConnectionId(++conn_id);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _));
+ ProcessPacket(client_address, connection_id, true,
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+}
+
+// Enables mocking of the handshake-confirmation for stateless rejects.
+class MockQuicCryptoServerStream : public QuicCryptoServerStream {
+ public:
+ MockQuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicServerSessionBase* session,
+ QuicCryptoServerStream::Helper* helper)
+ : QuicCryptoServerStream(
+ &crypto_config,
+ compressed_certs_cache,
+ GetQuicReloadableFlag(enable_quic_stateless_reject_support),
+ session,
+ helper),
+ handshake_confirmed_(false) {}
+ MockQuicCryptoServerStream(const MockQuicCryptoServerStream&) = delete;
+ MockQuicCryptoServerStream& operator=(const MockQuicCryptoServerStream&) =
+ delete;
+
+ void set_handshake_confirmed_for_testing(bool handshake_confirmed) {
+ handshake_confirmed_ = handshake_confirmed;
+ }
+
+ bool handshake_confirmed() const override { return handshake_confirmed_; }
+
+ private:
+ bool handshake_confirmed_;
+};
+
+struct StatelessRejectTestParams {
+ StatelessRejectTestParams(bool enable_stateless_rejects_via_flag,
+ bool client_supports_statelesss_rejects,
+ bool crypto_handshake_successful)
+ : enable_stateless_rejects_via_flag(enable_stateless_rejects_via_flag),
+ client_supports_statelesss_rejects(client_supports_statelesss_rejects),
+ crypto_handshake_successful(crypto_handshake_successful) {}
+
+ friend std::ostream& operator<<(std::ostream& os,
+ const StatelessRejectTestParams& p) {
+ os << "{ enable_stateless_rejects_via_flag: "
+ << p.enable_stateless_rejects_via_flag << std::endl;
+ os << " client_supports_statelesss_rejects: "
+ << p.client_supports_statelesss_rejects << std::endl;
+ os << " crypto_handshake_successful: " << p.crypto_handshake_successful
+ << " }";
+ return os;
+ }
+
+ // This only enables the stateless reject feature via the feature-flag.
+ // This should be a no-op if the peer does not support them.
+ bool enable_stateless_rejects_via_flag;
+ // Whether or not the client supports stateless rejects.
+ bool client_supports_statelesss_rejects;
+ // Should the initial crypto handshake succeed or not.
+ bool crypto_handshake_successful;
+};
+
+// Constructs various test permutations for stateless rejects.
+std::vector<StatelessRejectTestParams> GetStatelessRejectTestParams() {
+ std::vector<StatelessRejectTestParams> params;
+ for (bool enable_stateless_rejects_via_flag : {true, false}) {
+ for (bool client_supports_statelesss_rejects : {true, false}) {
+ for (bool crypto_handshake_successful : {true, false}) {
+ params.push_back(StatelessRejectTestParams(
+ enable_stateless_rejects_via_flag,
+ client_supports_statelesss_rejects, crypto_handshake_successful));
+ }
+ }
+ }
+ return params;
+}
+
+class QuicDispatcherStatelessRejectTest
+ : public QuicDispatcherTest,
+ public testing::WithParamInterface<StatelessRejectTestParams> {
+ public:
+ QuicDispatcherStatelessRejectTest()
+ : QuicDispatcherTest(), crypto_stream1_(nullptr) {}
+
+ ~QuicDispatcherStatelessRejectTest() override {
+ if (crypto_stream1_) {
+ delete crypto_stream1_;
+ }
+ }
+
+ // This test setup assumes that all testing will be done using
+ // crypto_stream1_.
+ void SetUp() override {
+ QuicDispatcherTest::SetUp();
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support,
+ GetParam().enable_stateless_rejects_via_flag);
+ }
+
+ // Returns true or false, depending on whether the server will emit
+ // a stateless reject, depending upon the parameters of the test.
+ bool ExpectStatelessReject() {
+ return GetParam().enable_stateless_rejects_via_flag &&
+ !GetParam().crypto_handshake_successful &&
+ GetParam().client_supports_statelesss_rejects;
+ }
+
+ // Sets up dispatcher_, session1_, and crypto_stream1_ based on
+ // the test parameters.
+ QuicServerSessionBase* CreateSessionBasedOnTestParams(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address) {
+ CreateSession(dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_);
+
+ crypto_stream1_ = new MockQuicCryptoServerStream(
+ crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ session1_, session1_->stream_helper());
+ session1_->SetCryptoStream(crypto_stream1_);
+ crypto_stream1_->set_handshake_confirmed_for_testing(
+ GetParam().crypto_handshake_successful);
+ crypto_stream1_->SetPeerSupportsStatelessRejects(
+ GetParam().client_supports_statelesss_rejects);
+ return session1_;
+ }
+
+ MockQuicCryptoServerStream* crypto_stream1_;
+};
+
+// Parameterized test for stateless rejects. Should test all
+// combinations of enabling/disabling, reject/no-reject for stateless
+// rejects.
+INSTANTIATE_TEST_SUITE_P(QuicDispatcherStatelessRejectTests,
+ QuicDispatcherStatelessRejectTest,
+ ::testing::ValuesIn(GetStatelessRejectTestParams()));
+
+TEST_P(QuicDispatcherStatelessRejectTest, ParameterizedBasicTest) {
+ CreateTimeWaitListManager();
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId connection_id = TestConnectionId(1);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(
+ CreateSessionBasedOnTestParams(connection_id, client_address)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _))
+ .Times(1);
+
+ // Process the first packet for the connection.
+ ProcessPacket(client_address, connection_id, true, SerializeCHLO());
+ if (ExpectStatelessReject()) {
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ CloseConnection(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, _, _));
+ // If this is a stateless reject, the crypto stream will close the
+ // connection.
+ session1_->connection()->CloseConnection(
+ QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT, "stateless reject",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ }
+
+ // Send a second packet and check the results. If this is a stateless reject,
+ // the existing connection_id will go on the time-wait list.
+ EXPECT_EQ(ExpectStatelessReject(),
+ time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+ if (ExpectStatelessReject()) {
+ // The second packet will be processed on the time-wait list.
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, _, connection_id, _, _))
+ .Times(1);
+ } else {
+ // The second packet will trigger a packet-validation
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(1)
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ }
+ ProcessPacket(client_address, connection_id, true, "data");
+}
+
+TEST_P(QuicDispatcherStatelessRejectTest, CheapRejects) {
+ SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true);
+ CreateTimeWaitListManager();
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId connection_id = TestConnectionId(1);
+ if (GetParam().enable_stateless_rejects_via_flag) {
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(connection_id, client_address, _, _))
+ .Times(0);
+ } else {
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("h2"), _))
+ .WillOnce(testing::Return(
+ CreateSessionBasedOnTestParams(connection_id, client_address)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ }
+
+ QUIC_LOG(INFO) << "ExpectStatelessReject: " << ExpectStatelessReject();
+ QUIC_LOG(INFO) << "Params: " << GetParam();
+ // Process the first packet for the connection.
+ CryptoHandshakeMessage client_hello =
+ crypto_test_utils::CreateCHLO({{"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"COPT", "SREJ"},
+ {"NONC", "1234567890123456789012"},
+ {"ALPN", "h2"},
+ {"VER\0", "Q025"}},
+ kClientHelloMinimumSize);
+
+ if (GetParam().enable_stateless_rejects_via_flag) {
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, _, connection_id, _, _))
+ .Times(1);
+ } else {
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _))
+ .Times(1);
+ }
+ ProcessPacket(client_address, connection_id, true,
+ std::string(client_hello.GetSerialized().AsStringPiece()));
+
+ if (GetParam().enable_stateless_rejects_via_flag) {
+ EXPECT_EQ(true,
+ time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+ }
+}
+
+TEST_P(QuicDispatcherStatelessRejectTest, BufferNonChlo) {
+ SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true);
+ CreateTimeWaitListManager();
+
+ const QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ const QuicConnectionId connection_id = TestConnectionId(1);
+
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(connection_id, _))
+ .Times(1);
+ ProcessPacket(client_address, connection_id, true, "NOT DATA FOR A CHLO");
+
+ // Process the first packet for the connection.
+ CryptoHandshakeMessage client_hello =
+ crypto_test_utils::CreateCHLO({{"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"NONC", "1234567890123456789012"},
+ {"ALPN", "h3"},
+ {"VER\0", "Q025"}},
+ kClientHelloMinimumSize);
+
+ // If stateless rejects are enabled then a connection will be created now
+ // and the buffered packet will be processed
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(connection_id, client_address,
+ QuicStringPiece("h3"), _))
+ .WillOnce(testing::Return(
+ CreateSessionBasedOnTestParams(connection_id, client_address)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, client_address, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })));
+ // Expect both packets to be passed to ProcessUdpPacket(). And one of them
+ // is already expected in CreateSessionBasedOnTestParams().
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, client_address, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(connection_id, packet);
+ })))
+ .RetiresOnSaturation();
+ ProcessPacket(client_address, connection_id, true,
+ std::string(client_hello.GetSerialized().AsStringPiece()));
+ EXPECT_FALSE(
+ time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+}
+
+// Verify the stopgap test: Packets with truncated connection IDs should be
+// dropped.
+class QuicDispatcherTestStrayPacketConnectionId : public QuicDispatcherTest {};
+
+// Packets with truncated connection IDs should be dropped.
+TEST_F(QuicDispatcherTestStrayPacketConnectionId,
+ StrayPacketTruncatedConnectionId) {
+ CreateTimeWaitListManager();
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId connection_id = TestConnectionId(1);
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, QuicStringPiece("hq"), _))
+ .Times(0);
+ if (CurrentSupportedVersions()[0].transport_version > QUIC_VERSION_43 &&
+ !QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ CurrentSupportedVersions()[0].transport_version)) {
+ // This IETF packet has invalid connection ID length.
+ EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(0);
+ } else {
+ // This is either:
+ // - a GQUIC packet considered as IETF QUIC packet with short header
+ // with unacceptable packet number or
+ // - an IETF QUIC packet with bad connection ID length which is rejected.
+ EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _))
+ .Times(1);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(1);
+ }
+ ProcessPacket(client_address, connection_id, true, "data",
+ CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER);
+}
+
+class BlockingWriter : public QuicPacketWriterWrapper {
+ public:
+ BlockingWriter() : write_blocked_(false) {}
+
+ bool IsWriteBlocked() const override { return write_blocked_; }
+ void SetWritable() override { write_blocked_ = false; }
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_client_address,
+ const QuicSocketAddress& peer_client_address,
+ PerPacketOptions* options) override {
+ // It would be quite possible to actually implement this method here with
+ // the fake blocked status, but it would be significantly more work in
+ // Chromium, and since it's not called anyway, don't bother.
+ QUIC_LOG(DFATAL) << "Not supported";
+ return WriteResult();
+ }
+
+ bool write_blocked_;
+};
+
+class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest {
+ public:
+ void SetUp() override {
+ QuicDispatcherTest::SetUp();
+ writer_ = new BlockingWriter;
+ QuicDispatcherPeer::UseWriter(dispatcher_.get(), writer_);
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(_, client_address, QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(1), client_address,
+ &helper_, &alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(1), packet);
+ })));
+ EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(
+ TestConnectionId(1), _));
+ ProcessPacket(client_address, TestConnectionId(1), true, SerializeCHLO());
+
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(_, client_address, QuicStringPiece("hq"), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(2), client_address,
+ &helper_, &alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session2_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session2_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(2), packet);
+ })));
+ EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(
+ TestConnectionId(2), _));
+ ProcessPacket(client_address, TestConnectionId(2), true, SerializeCHLO());
+
+ blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(dispatcher_.get());
+ }
+
+ void TearDown() override {
+ if (connection1() != nullptr) {
+ EXPECT_CALL(*connection1(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
+ }
+
+ if (connection2() != nullptr) {
+ EXPECT_CALL(*connection2(), CloseConnection(QUIC_PEER_GOING_AWAY, _, _));
+ }
+ dispatcher_->Shutdown();
+ }
+
+ // Set the dispatcher's writer to be blocked. By default, all connections use
+ // the same writer as the dispatcher in this test.
+ void SetBlocked() {
+ QUIC_LOG(INFO) << "set writer " << writer_ << " to blocked";
+ writer_->write_blocked_ = true;
+ }
+
+ // Simulate what happens when connection1 gets blocked when writing.
+ void BlockConnection1() {
+ Connection1Writer()->write_blocked_ = true;
+ dispatcher_->OnWriteBlocked(connection1());
+ }
+
+ BlockingWriter* Connection1Writer() {
+ return static_cast<BlockingWriter*>(connection1()->writer());
+ }
+
+ // Simulate what happens when connection2 gets blocked when writing.
+ void BlockConnection2() {
+ Connection2Writer()->write_blocked_ = true;
+ dispatcher_->OnWriteBlocked(connection2());
+ }
+
+ BlockingWriter* Connection2Writer() {
+ return static_cast<BlockingWriter*>(connection2()->writer());
+ }
+
+ protected:
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ BlockingWriter* writer_;
+ QuicDispatcher::WriteBlockedList* blocked_list_;
+};
+
+TEST_F(QuicDispatcherWriteBlockedListTest, BasicOnCanWrite) {
+ // No OnCanWrite calls because no connections are blocked.
+ dispatcher_->OnCanWrite();
+
+ // Register connection 1 for events, and make sure it's notified.
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ dispatcher_->OnCanWrite();
+
+ // It should get only one notification.
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
+ dispatcher_->OnCanWrite();
+ EXPECT_FALSE(dispatcher_->HasPendingWrites());
+}
+
+TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteOrder) {
+ // Make sure we handle events in order.
+ InSequence s;
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ dispatcher_->OnWriteBlocked(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_->OnCanWrite();
+
+ // Check the other ordering.
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection2());
+ dispatcher_->OnWriteBlocked(connection1());
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ dispatcher_->OnCanWrite();
+}
+
+TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteRemove) {
+ // Add and remove one connction.
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ blocked_list_->erase(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
+ dispatcher_->OnCanWrite();
+
+ // Add and remove one connction and make sure it doesn't affect others.
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ dispatcher_->OnWriteBlocked(connection2());
+ blocked_list_->erase(connection1());
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_->OnCanWrite();
+
+ // Add it, remove it, and add it back and make sure things are OK.
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ blocked_list_->erase(connection1());
+ dispatcher_->OnWriteBlocked(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
+ dispatcher_->OnCanWrite();
+}
+
+TEST_F(QuicDispatcherWriteBlockedListTest, DoubleAdd) {
+ // Make sure a double add does not necessitate a double remove.
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ dispatcher_->OnWriteBlocked(connection1());
+ blocked_list_->erase(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
+ dispatcher_->OnCanWrite();
+
+ // Make sure a double add does not result in two OnCanWrite calls.
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ dispatcher_->OnWriteBlocked(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
+ dispatcher_->OnCanWrite();
+}
+
+TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlockConnection1) {
+ // If the 1st blocked writer gets blocked in OnCanWrite, it will be added back
+ // into the write blocked list.
+ InSequence s;
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ dispatcher_->OnWriteBlocked(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite())
+ .WillOnce(
+ Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection1));
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_->OnCanWrite();
+
+ // connection1 should be still in the write blocked list.
+ EXPECT_TRUE(dispatcher_->HasPendingWrites());
+
+ // Now call OnCanWrite again, connection1 should get its second chance.
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
+ dispatcher_->OnCanWrite();
+ EXPECT_FALSE(dispatcher_->HasPendingWrites());
+}
+
+TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlockConnection2) {
+ // If the 2nd blocked writer gets blocked in OnCanWrite, it will be added back
+ // into the write blocked list.
+ InSequence s;
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ dispatcher_->OnWriteBlocked(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ EXPECT_CALL(*connection2(), OnCanWrite())
+ .WillOnce(
+ Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2));
+ dispatcher_->OnCanWrite();
+
+ // connection2 should be still in the write blocked list.
+ EXPECT_TRUE(dispatcher_->HasPendingWrites());
+
+ // Now call OnCanWrite again, connection2 should get its second chance.
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_->OnCanWrite();
+ EXPECT_FALSE(dispatcher_->HasPendingWrites());
+}
+
+TEST_F(QuicDispatcherWriteBlockedListTest,
+ OnCanWriteHandleBlockBothConnections) {
+ // Both connections get blocked in OnCanWrite, and added back into the write
+ // blocked list.
+ InSequence s;
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ dispatcher_->OnWriteBlocked(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite())
+ .WillOnce(
+ Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection1));
+ EXPECT_CALL(*connection2(), OnCanWrite())
+ .WillOnce(
+ Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2));
+ dispatcher_->OnCanWrite();
+
+ // Both connections should be still in the write blocked list.
+ EXPECT_TRUE(dispatcher_->HasPendingWrites());
+
+ // Now call OnCanWrite again, both connections should get its second chance.
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_->OnCanWrite();
+ EXPECT_FALSE(dispatcher_->HasPendingWrites());
+}
+
+TEST_F(QuicDispatcherWriteBlockedListTest, PerConnectionWriterBlocked) {
+ // By default, all connections share the same packet writer with the
+ // dispatcher.
+ EXPECT_EQ(dispatcher_->writer(), connection1()->writer());
+ EXPECT_EQ(dispatcher_->writer(), connection2()->writer());
+
+ // Test the case where connection1 shares the same packet writer as the
+ // dispatcher, whereas connection2 owns it's packet writer.
+ // Change connection2's writer.
+ connection2()->SetQuicPacketWriter(new BlockingWriter, /*owns_writer=*/true);
+ EXPECT_NE(dispatcher_->writer(), connection2()->writer());
+
+ BlockConnection2();
+ EXPECT_TRUE(dispatcher_->HasPendingWrites());
+
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_->OnCanWrite();
+ EXPECT_FALSE(dispatcher_->HasPendingWrites());
+}
+
+TEST_F(QuicDispatcherWriteBlockedListTest,
+ RemoveConnectionFromWriteBlockedListWhenDeletingSessions) {
+ dispatcher_->OnConnectionClosed(connection1()->connection_id(),
+ QUIC_PACKET_WRITE_ERROR, "Closed by test.",
+ ConnectionCloseSource::FROM_SELF);
+
+ SetBlocked();
+
+ ASSERT_FALSE(dispatcher_->HasPendingWrites());
+ SetBlocked();
+ dispatcher_->OnWriteBlocked(connection1());
+ ASSERT_TRUE(dispatcher_->HasPendingWrites());
+
+ EXPECT_QUIC_BUG(dispatcher_->DeleteSessions(),
+ "QuicConnection was in WriteBlockedList before destruction");
+ MarkSession1Deleted();
+}
+
+// Tests that bufferring packets works in stateful reject, expensive stateless
+// reject and cheap stateless reject.
+struct BufferedPacketStoreTestParams {
+ BufferedPacketStoreTestParams(bool enable_stateless_rejects_via_flag,
+ bool support_cheap_stateless_reject)
+ : enable_stateless_rejects_via_flag(enable_stateless_rejects_via_flag),
+ support_cheap_stateless_reject(support_cheap_stateless_reject) {}
+
+ friend std::ostream& operator<<(std::ostream& os,
+ const BufferedPacketStoreTestParams& p) {
+ os << "{ enable_stateless_rejects_via_flag: "
+ << p.enable_stateless_rejects_via_flag << std::endl;
+ os << " support_cheap_stateless_reject: "
+ << p.support_cheap_stateless_reject << " }";
+ return os;
+ }
+
+ // This only enables the stateless reject feature via the feature-flag.
+ // This should be a no-op if the peer does not support them.
+ bool enable_stateless_rejects_via_flag;
+ // Whether to do cheap stateless or not.
+ bool support_cheap_stateless_reject;
+};
+
+std::vector<BufferedPacketStoreTestParams> GetBufferedPacketStoreTestParams() {
+ std::vector<BufferedPacketStoreTestParams> params;
+ for (bool enable_stateless_rejects_via_flag : {true, false}) {
+ for (bool support_cheap_stateless_reject : {true, false}) {
+ params.push_back(BufferedPacketStoreTestParams(
+ enable_stateless_rejects_via_flag, support_cheap_stateless_reject));
+ }
+ }
+ return params;
+}
+
+// A dispatcher whose stateless rejector will always ACCEPTs CHLO.
+class BufferedPacketStoreTest
+ : public QuicDispatcherTest,
+ public testing::WithParamInterface<BufferedPacketStoreTestParams> {
+ public:
+ BufferedPacketStoreTest()
+ : QuicDispatcherTest(),
+ server_addr_(QuicSocketAddress(QuicIpAddress::Any4(), 5)),
+ client_addr_(QuicIpAddress::Loopback4(), 1234),
+ signed_config_(new QuicSignedServerConfig) {
+ SetQuicReloadableFlag(quic_use_cheap_stateless_rejects,
+ GetParam().support_cheap_stateless_reject);
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support,
+ GetParam().enable_stateless_rejects_via_flag);
+ }
+
+ void SetUp() override {
+ QuicDispatcherTest::SetUp();
+ clock_ = QuicDispatcherPeer::GetHelper(dispatcher_.get())->GetClock();
+
+ QuicTransportVersion version = AllSupportedTransportVersions().front();
+ CryptoHandshakeMessage chlo =
+ crypto_test_utils::GenerateDefaultInchoateCHLO(clock_, version,
+ &crypto_config_);
+ chlo.SetVector(kCOPT, QuicTagVector{kSREJ});
+ // Pass an inchoate CHLO.
+ crypto_test_utils::GenerateFullCHLO(
+ chlo, &crypto_config_, server_addr_, client_addr_, version, clock_,
+ signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ &full_chlo_);
+ }
+
+ std::string SerializeFullCHLO() {
+ return std::string(full_chlo_.GetSerialized().AsStringPiece());
+ }
+
+ protected:
+ QuicSocketAddress server_addr_;
+ QuicSocketAddress client_addr_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ const QuicClock* clock_;
+ CryptoHandshakeMessage full_chlo_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ BufferedPacketStoreTests,
+ BufferedPacketStoreTest,
+ ::testing::ValuesIn(GetBufferedPacketStoreTestParams()));
+
+TEST_P(BufferedPacketStoreTest, ProcessNonChloPacketsUptoLimitAndProcessChlo) {
+ InSequence s;
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId conn_id = TestConnectionId(1);
+ // A bunch of non-CHLO should be buffered upon arrival, and the first one
+ // should trigger ShouldCreateOrBufferPacketForConnection().
+ EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(conn_id, _))
+ .Times(1);
+ for (size_t i = 1; i <= kDefaultMaxUndecryptablePackets + 1; ++i) {
+ ProcessPacket(client_address, conn_id, true,
+ QuicStrCat("data packet ", i + 1), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, /*packet_number=*/i + 1);
+ }
+ EXPECT_EQ(0u, dispatcher_->session_map().size())
+ << "No session should be created before CHLO arrives.";
+
+ // Pop out the last packet as it is also be dropped by the store.
+ data_connection_map_[conn_id].pop_back();
+ // When CHLO arrives, a new session should be created, and all packets
+ // buffered should be delivered to the session.
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(conn_id, client_address, QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, conn_id, client_address, &mock_helper_,
+ &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+
+ // Only |kDefaultMaxUndecryptablePackets| packets were buffered, and they
+ // should be delivered in arrival order.
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(kDefaultMaxUndecryptablePackets + 1) // + 1 for CHLO.
+ .WillRepeatedly(
+ WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id, packet);
+ })));
+ ProcessPacket(client_address, conn_id, true, SerializeFullCHLO());
+}
+
+TEST_P(BufferedPacketStoreTest,
+ ProcessNonChloPacketsForDifferentConnectionsUptoLimit) {
+ InSequence s;
+ // A bunch of non-CHLO should be buffered upon arrival.
+ size_t kNumConnections = kMaxConnectionsWithoutCHLO + 1;
+ for (size_t i = 1; i <= kNumConnections; ++i) {
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), i);
+ QuicConnectionId conn_id = TestConnectionId(i);
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id, _));
+ ProcessPacket(client_address, conn_id, true,
+ QuicStrCat("data packet on connection ", i),
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+ /*packet_number=*/2);
+ }
+
+ // Pop out the packet on last connection as it shouldn't be enqueued in store
+ // as well.
+ data_connection_map_[TestConnectionId(kNumConnections)].pop_front();
+
+ // Reset session creation counter to ensure processing CHLO can always
+ // create session.
+ QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(dispatcher_.get(),
+ kNumConnections);
+ // Process CHLOs to create session for these connections.
+ for (size_t i = 1; i <= kNumConnections; ++i) {
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), i);
+ QuicConnectionId conn_id = TestConnectionId(i);
+ if (i == kNumConnections) {
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id, _));
+ }
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_address,
+ QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, conn_id, client_address, &mock_helper_,
+ &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ // First |kNumConnections| - 1 connections should have buffered
+ // a packet in store. The rest should have been dropped.
+ size_t num_packet_to_process = i <= kMaxConnectionsWithoutCHLO ? 2u : 1u;
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, client_address, _))
+ .Times(num_packet_to_process)
+ .WillRepeatedly(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id, packet);
+ })));
+
+ ProcessPacket(client_address, conn_id, true, SerializeFullCHLO());
+ }
+}
+
+// Tests that store delivers empty packet list if CHLO arrives firstly.
+TEST_P(BufferedPacketStoreTest, DeliverEmptyPackets) {
+ QuicConnectionId conn_id = TestConnectionId(1);
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id, _));
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(conn_id, client_address, QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, conn_id, client_address, &mock_helper_,
+ &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, client_address, _));
+ ProcessPacket(client_address, conn_id, true, SerializeFullCHLO());
+}
+
+// Tests that a retransmitted CHLO arrives after a connection for the
+// CHLO has been created.
+TEST_P(BufferedPacketStoreTest, ReceiveRetransmittedCHLO) {
+ InSequence s;
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId conn_id = TestConnectionId(1);
+ ProcessPacket(client_address, conn_id, true, QuicStrCat("data packet ", 2),
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+ /*packet_number=*/2);
+
+ // When CHLO arrives, a new session should be created, and all packets
+ // buffered should be delivered to the session.
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(conn_id, client_address, QuicStringPiece(), _))
+ .Times(1) // Only triggered by 1st CHLO.
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, conn_id, client_address, &mock_helper_,
+ &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(3) // Triggered by 1 data packet and 2 CHLOs.
+ .WillRepeatedly(
+ WithArg<2>(Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id, packet);
+ })));
+ ProcessPacket(client_address, conn_id, true, SerializeFullCHLO());
+
+ ProcessPacket(client_address, conn_id, true, SerializeFullCHLO());
+}
+
+// Tests that expiration of a connection add connection id to time wait list.
+TEST_P(BufferedPacketStoreTest, ReceiveCHLOAfterExpiration) {
+ InSequence s;
+ CreateTimeWaitListManager();
+ QuicBufferedPacketStore* store =
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
+ QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock());
+
+ QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+ QuicConnectionId conn_id = TestConnectionId(1);
+ ProcessPacket(client_address, conn_id, true, QuicStrCat("data packet ", 2),
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+ /*packet_number=*/2);
+
+ mock_helper_.AdvanceTime(
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs));
+ QuicAlarm* alarm = QuicBufferedPacketStorePeer::expiration_alarm(store);
+ // Cancel alarm as if it had been fired.
+ alarm->Cancel();
+ store->OnExpirationTimeout();
+ // New arrived CHLO will be dropped because this connection is in time wait
+ // list.
+ ASSERT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
+ EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _));
+ ProcessPacket(client_address, conn_id, true, SerializeFullCHLO());
+}
+
+TEST_P(BufferedPacketStoreTest, ProcessCHLOsUptoLimitAndBufferTheRest) {
+ // Process more than (|kMaxNumSessionsToCreate| +
+ // |kDefaultMaxConnectionsInStore|) CHLOs,
+ // the first |kMaxNumSessionsToCreate| should create connections immediately,
+ // the next |kDefaultMaxConnectionsInStore| should be buffered,
+ // the rest should be dropped.
+ QuicBufferedPacketStore* store =
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
+ const size_t kNumCHLOs =
+ kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore + 1;
+ for (uint64_t conn_id = 1; conn_id <= kNumCHLOs; ++conn_id) {
+ EXPECT_CALL(*dispatcher_, ShouldCreateOrBufferPacketForConnection(
+ TestConnectionId(conn_id), _));
+ if (conn_id <= kMaxNumSessionsToCreate) {
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(conn_id), client_addr_,
+ QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(conn_id),
+ client_addr_, &mock_helper_, &mock_alarm_factory_,
+ &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ &session1_)));
+ EXPECT_CALL(
+ *reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(conn_id), packet);
+ })));
+ }
+ ProcessPacket(client_addr_, TestConnectionId(conn_id), true,
+ SerializeFullCHLO());
+ if (conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore &&
+ conn_id > kMaxNumSessionsToCreate) {
+ EXPECT_TRUE(store->HasChloForConnection(TestConnectionId(conn_id)));
+ } else {
+ // First |kMaxNumSessionsToCreate| CHLOs should be passed to new
+ // connections immediately, and the last CHLO should be dropped as the
+ // store is full.
+ EXPECT_FALSE(store->HasChloForConnection(TestConnectionId(conn_id)));
+ }
+ }
+
+ // Graduately consume buffered CHLOs. The buffered connections should be
+ // created but the dropped one shouldn't.
+ for (uint64_t conn_id = kMaxNumSessionsToCreate + 1;
+ conn_id <= kMaxNumSessionsToCreate + kDefaultMaxConnectionsInStore;
+ ++conn_id) {
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(conn_id), client_addr_,
+ QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(conn_id), packet);
+ })));
+ }
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(kNumCHLOs), client_addr_,
+ QuicStringPiece(), _))
+ .Times(0);
+
+ while (store->HasChlosBuffered()) {
+ dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate);
+ }
+
+ EXPECT_EQ(TestConnectionId(static_cast<size_t>(kMaxNumSessionsToCreate) +
+ kDefaultMaxConnectionsInStore),
+ session1_->connection_id());
+}
+
+// Duplicated CHLO shouldn't be buffered.
+TEST_P(BufferedPacketStoreTest, BufferDuplicatedCHLO) {
+ for (uint64_t conn_id = 1; conn_id <= kMaxNumSessionsToCreate + 1;
+ ++conn_id) {
+ // Last CHLO will be buffered. Others will create connection right away.
+ if (conn_id <= kMaxNumSessionsToCreate) {
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(conn_id), client_addr_,
+ QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(conn_id),
+ client_addr_, &mock_helper_, &mock_alarm_factory_,
+ &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ &session1_)));
+ EXPECT_CALL(
+ *reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(conn_id), packet);
+ })));
+ }
+ ProcessPacket(client_addr_, TestConnectionId(conn_id), true,
+ SerializeFullCHLO());
+ }
+ // Retransmit CHLO on last connection should be dropped.
+ QuicConnectionId last_connection =
+ TestConnectionId(kMaxNumSessionsToCreate + 1);
+ ProcessPacket(client_addr_, last_connection, true, SerializeFullCHLO());
+
+ size_t packets_buffered = 2;
+
+ // Reset counter and process buffered CHLO.
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection, client_addr_,
+ QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, last_connection, client_addr_,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ // Only one packet(CHLO) should be process.
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(packets_buffered)
+ .WillRepeatedly(WithArg<2>(
+ Invoke([this, last_connection](const QuicEncryptedPacket& packet) {
+ ValidatePacket(last_connection, packet);
+ })));
+ dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate);
+}
+
+TEST_P(BufferedPacketStoreTest, BufferNonChloPacketsUptoLimitWithChloBuffered) {
+ uint64_t last_conn_id = kMaxNumSessionsToCreate + 1;
+ QuicConnectionId last_connection_id = TestConnectionId(last_conn_id);
+ for (uint64_t conn_id = 1; conn_id <= last_conn_id; ++conn_id) {
+ // Last CHLO will be buffered. Others will create connection right away.
+ if (conn_id <= kMaxNumSessionsToCreate) {
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(conn_id), client_addr_,
+ QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(conn_id),
+ client_addr_, &mock_helper_, &mock_alarm_factory_,
+ &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ &session1_)));
+ EXPECT_CALL(
+ *reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillRepeatedly(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(conn_id), packet);
+ })));
+ }
+ ProcessPacket(client_addr_, TestConnectionId(conn_id), true,
+ SerializeFullCHLO());
+ }
+
+ // Process another |kDefaultMaxUndecryptablePackets| + 1 data packets. The
+ // last one should be dropped.
+ for (uint64_t packet_number = 2;
+ packet_number <= kDefaultMaxUndecryptablePackets + 2; ++packet_number) {
+ ProcessPacket(client_addr_, last_connection_id, true, "data packet");
+ }
+
+ // Reset counter and process buffered CHLO.
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(last_connection_id, client_addr_,
+ QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, last_connection_id, client_addr_,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ // Only CHLO and following |kDefaultMaxUndecryptablePackets| data packets
+ // should be process.
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .Times(kDefaultMaxUndecryptablePackets + 1)
+ .WillRepeatedly(WithArg<2>(
+ Invoke([this, last_connection_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(last_connection_id, packet);
+ })));
+ dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate);
+}
+
+// Tests that when dispatcher's packet buffer is full, a CHLO on connection
+// which doesn't have buffered CHLO should be buffered.
+TEST_P(BufferedPacketStoreTest, ReceiveCHLOForBufferedConnection) {
+ QuicBufferedPacketStore* store =
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
+
+ uint64_t conn_id = 1;
+ ProcessPacket(client_addr_, TestConnectionId(conn_id), true, "data packet",
+ CONNECTION_ID_PRESENT, PACKET_4BYTE_PACKET_NUMBER,
+ /*packet_number=*/1);
+ // Fill packet buffer to full with CHLOs on other connections. Need to feed
+ // extra CHLOs because the first |kMaxNumSessionsToCreate| are going to create
+ // session directly.
+ for (conn_id = 2;
+ conn_id <= kDefaultMaxConnectionsInStore + kMaxNumSessionsToCreate;
+ ++conn_id) {
+ if (conn_id <= kMaxNumSessionsToCreate + 1) {
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(conn_id), client_addr_,
+ QuicStringPiece(), _))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(conn_id),
+ client_addr_, &mock_helper_, &mock_alarm_factory_,
+ &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ &session1_)));
+ EXPECT_CALL(
+ *reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(conn_id), packet);
+ })));
+ }
+ ProcessPacket(client_addr_, TestConnectionId(conn_id), true,
+ SerializeFullCHLO());
+ }
+ EXPECT_FALSE(store->HasChloForConnection(
+ /*connection_id=*/TestConnectionId(1)));
+
+ // CHLO on connection 1 should still be buffered.
+ ProcessPacket(client_addr_, /*connection_id=*/TestConnectionId(1), true,
+ SerializeFullCHLO());
+ EXPECT_TRUE(store->HasChloForConnection(
+ /*connection_id=*/TestConnectionId(1)));
+}
+
+// Regression test for b/117874922.
+TEST_P(BufferedPacketStoreTest, ProcessBufferedChloWithDifferentVersion) {
+ // Turn off version 99, such that the preferred version is not supported by
+ // the server.
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ uint64_t last_connection_id = kMaxNumSessionsToCreate + 5;
+ ParsedQuicVersionVector supported_versions = CurrentSupportedVersions();
+ for (uint64_t conn_id = 1; conn_id <= last_connection_id; ++conn_id) {
+ // Last 5 CHLOs will be buffered. Others will create connection right away.
+ ParsedQuicVersion version =
+ supported_versions[(conn_id - 1) % supported_versions.size()];
+ if (conn_id <= kMaxNumSessionsToCreate) {
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(conn_id), client_addr_,
+ QuicStringPiece(), version))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(conn_id),
+ client_addr_, &mock_helper_, &mock_alarm_factory_,
+ &crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ &session1_)));
+ EXPECT_CALL(
+ *reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillRepeatedly(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(conn_id), packet);
+ })));
+ }
+ ProcessPacket(client_addr_, TestConnectionId(conn_id), true, version,
+ SerializeFullCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+ }
+
+ // Process buffered CHLOs. Verify the version is correct.
+ for (uint64_t conn_id = kMaxNumSessionsToCreate + 1;
+ conn_id <= last_connection_id; ++conn_id) {
+ ParsedQuicVersion version =
+ supported_versions[(conn_id - 1) % supported_versions.size()];
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(TestConnectionId(conn_id), client_addr_,
+ QuicStringPiece(), version))
+ .WillOnce(testing::Return(CreateSession(
+ dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session1_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillRepeatedly(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(TestConnectionId(conn_id), packet);
+ })));
+ }
+ dispatcher_->ProcessBufferedChlos(kMaxNumSessionsToCreate);
+}
+
+// Test which exercises the async GetProof codepaths, especially in the context
+// of stateless rejection.
+class AsyncGetProofTest : public QuicDispatcherTest {
+ public:
+ AsyncGetProofTest()
+ : QuicDispatcherTest(
+ std::unique_ptr<FakeProofSource>(new FakeProofSource())),
+ client_addr_(QuicIpAddress::Loopback4(), 1234),
+ client_addr_2_(QuicIpAddress::Loopback4(), 1357),
+ crypto_config_peer_(&crypto_config_),
+ server_addr_(QuicIpAddress::Any4(), 5),
+ signed_config_(new QuicSignedServerConfig) {
+ SetQuicReloadableFlag(enable_quic_stateless_reject_support, true);
+ SetQuicReloadableFlag(quic_use_cheap_stateless_rejects, true);
+ }
+
+ void SetUp() override {
+ QuicDispatcherTest::SetUp();
+
+ clock_ = QuicDispatcherPeer::GetHelper(dispatcher_.get())->GetClock();
+ QuicTransportVersion version = AllSupportedTransportVersions().front();
+ chlo_ = crypto_test_utils::GenerateDefaultInchoateCHLO(clock_, version,
+ &crypto_config_);
+ chlo_.SetVector(kCOPT, QuicTagVector{kSREJ});
+ chlo_.SetStringPiece(kALPN, "HTTP/1");
+ // Pass an inchoate CHLO.
+ crypto_test_utils::GenerateFullCHLO(
+ chlo_, &crypto_config_, server_addr_, client_addr_, version, clock_,
+ signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ &full_chlo_);
+
+ crypto_test_utils::GenerateFullCHLO(
+ chlo_, &crypto_config_, server_addr_, client_addr_2_, version, clock_,
+ signed_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ &full_chlo_2_);
+
+ GetFakeProofSource()->Activate();
+ }
+
+ FakeProofSource* GetFakeProofSource() const {
+ return static_cast<FakeProofSource*>(crypto_config_peer_.GetProofSource());
+ }
+
+ std::string SerializeFullCHLO() {
+ return std::string(full_chlo_.GetSerialized().AsStringPiece());
+ }
+
+ std::string SerializeFullCHLOForClient2() {
+ return std::string(full_chlo_2_.GetSerialized().AsStringPiece());
+ }
+
+ std::string SerializeCHLO() {
+ return std::string(chlo_.GetSerialized().AsStringPiece());
+ }
+
+ // Sets up a session, and crypto stream based on the test parameters.
+ QuicServerSessionBase* GetSession(QuicConnectionId connection_id,
+ QuicSocketAddress client_address) {
+ auto it = sessions_.find(connection_id);
+ if (it != sessions_.end()) {
+ return it->second.session;
+ }
+
+ TestQuicSpdyServerSession* session;
+ CreateSession(dispatcher_.get(), config_, connection_id, client_address,
+ &mock_helper_, &mock_alarm_factory_, &crypto_config_,
+ QuicDispatcherPeer::GetCache(dispatcher_.get()), &session);
+
+ std::unique_ptr<MockQuicCryptoServerStream> crypto_stream(
+ new MockQuicCryptoServerStream(
+ crypto_config_, QuicDispatcherPeer::GetCache(dispatcher_.get()),
+ session, session->stream_helper()));
+ session->SetCryptoStream(crypto_stream.get());
+ crypto_stream->SetPeerSupportsStatelessRejects(true);
+ const bool ok =
+ sessions_
+ .insert(std::make_pair(
+ connection_id, SessionInfo{session, std::move(crypto_stream)}))
+ .second;
+ CHECK(ok);
+ return session;
+ }
+
+ protected:
+ const QuicSocketAddress client_addr_;
+ const QuicSocketAddress client_addr_2_;
+ CryptoHandshakeMessage chlo_;
+
+ private:
+ QuicCryptoServerConfigPeer crypto_config_peer_;
+ QuicSocketAddress server_addr_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ const QuicClock* clock_;
+ CryptoHandshakeMessage full_chlo_; // CHLO for client_addr_
+ CryptoHandshakeMessage full_chlo_2_; // CHLO for client_addr_2_
+
+ struct SessionInfo {
+ TestQuicSpdyServerSession* session;
+ std::unique_ptr<MockQuicCryptoServerStream> crypto_stream;
+ };
+ std::map<QuicConnectionId, SessionInfo> sessions_;
+};
+
+// Test a simple situation of connections which the StatelessRejector will
+// accept.
+TEST_F(AsyncGetProofTest, BasicAccept) {
+ QuicConnectionId conn_id = TestConnectionId(1);
+
+ testing::MockFunction<void(int check_point)> check;
+ {
+ InSequence s;
+
+ EXPECT_CALL(check, Call(1));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id, _));
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_,
+ QuicStringPiece("HTTP/1"), _))
+ .WillOnce(testing::Return(GetSession(conn_id, client_addr_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(
+ GetSession(conn_id, client_addr_)->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id, packet);
+ })));
+
+ EXPECT_CALL(check, Call(2));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(
+ GetSession(conn_id, client_addr_)->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id, packet);
+ })));
+ }
+
+ // Send a CHLO that the StatelessRejector will accept.
+ ProcessPacket(client_addr_, conn_id, true, SerializeFullCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ check.Call(1);
+ // Complete the ProofSource::GetProof call and verify that a session is
+ // created.
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+
+ check.Call(2);
+ // Verify that a data packet gets processed immediately.
+ ProcessPacket(client_addr_, conn_id, true, "My name is Data");
+}
+
+TEST_F(AsyncGetProofTest, RestorePacketContext) {
+ QuicConnectionId conn_id_1 = TestConnectionId(1);
+ QuicConnectionId conn_id_2 = TestConnectionId(2);
+
+ testing::MockFunction<void(int check_point)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(check, Call(1));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id_1, _));
+
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_1, client_addr_,
+ QuicStringPiece("HTTP/1"), _))
+ .WillOnce(testing::Return(GetSession(conn_id_1, client_addr_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(
+ GetSession(conn_id_1, client_addr_)->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillRepeatedly(WithArg<2>(
+ Invoke([this, conn_id_1](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id_1, packet);
+ })));
+
+ EXPECT_CALL(check, Call(2));
+
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id_2, _));
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_2_,
+ QuicStringPiece("HTTP/1"), _))
+ .WillOnce(testing::Return(GetSession(conn_id_2, client_addr_2_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(
+ GetSession(conn_id_2, client_addr_2_)->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id_2](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id_2, packet);
+ })));
+ }
+
+ // Send a CHLO that the StatelessRejector will accept.
+ dispatcher_->custom_packet_context_ = "connection 1";
+ ProcessPacket(client_addr_, conn_id_1, true, SerializeFullCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Send another CHLO that the StatelessRejector will accept.
+ dispatcher_->custom_packet_context_ = "connection 2";
+ ProcessPacket(client_addr_2_, conn_id_2, true, SerializeFullCHLOForClient2());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
+
+ // Complete the first ProofSource::GetProof call and verify that a session is
+ // created.
+ check.Call(1);
+
+ EXPECT_EQ(client_addr_2_, dispatcher_->current_client_address());
+ EXPECT_EQ(client_addr_2_, dispatcher_->current_peer_address());
+ EXPECT_EQ("connection 2", dispatcher_->custom_packet_context_);
+
+ // Runs the async proof callback for conn_id_1 from client_addr_.
+ GetFakeProofSource()->InvokePendingCallback(0);
+
+ EXPECT_EQ(client_addr_, dispatcher_->current_client_address());
+ EXPECT_EQ(client_addr_, dispatcher_->current_peer_address());
+ EXPECT_EQ("connection 1", dispatcher_->custom_packet_context_);
+
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Complete the second ProofSource::GetProof call and verify that a session is
+ // created.
+ check.Call(2);
+
+ EXPECT_EQ(client_addr_, dispatcher_->current_client_address());
+ EXPECT_EQ(client_addr_, dispatcher_->current_peer_address());
+ EXPECT_EQ("connection 1", dispatcher_->custom_packet_context_);
+
+ // Runs the async proof callback for conn_id_2 from client_addr_2_.
+ GetFakeProofSource()->InvokePendingCallback(0);
+
+ EXPECT_EQ(client_addr_2_, dispatcher_->current_client_address());
+ EXPECT_EQ(client_addr_2_, dispatcher_->current_peer_address());
+ EXPECT_EQ("connection 2", dispatcher_->custom_packet_context_);
+
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+}
+
+// Test a simple situation of connections which the StatelessRejector will
+// reject.
+TEST_F(AsyncGetProofTest, BasicReject) {
+ CreateTimeWaitListManager();
+
+ QuicConnectionId conn_id = TestConnectionId(1);
+
+ testing::MockFunction<void(int check_point)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(check, Call(1));
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(conn_id, _, _, _, _));
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, client_addr_, conn_id, _, _));
+
+ EXPECT_CALL(check, Call(2));
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_,
+ QuicStringPiece("hq"), _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, client_addr_, conn_id, _, _));
+ }
+
+ // Send a CHLO that the StatelessRejector will reject.
+ ProcessPacket(client_addr_, conn_id, true, SerializeCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Complete the ProofSource::GetProof call and verify that the connection and
+ // packet are processed by the time wait list manager.
+ check.Call(1);
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+
+ // Verify that a data packet is passed to the time wait list manager.
+ check.Call(2);
+ ProcessPacket(client_addr_, conn_id, true, "My name is Data");
+}
+
+// Test a situation with multiple interleaved connections which the
+// StatelessRejector will accept.
+TEST_F(AsyncGetProofTest, MultipleAccept) {
+ QuicConnectionId conn_id_1 = TestConnectionId(1);
+ QuicConnectionId conn_id_2 = TestConnectionId(2);
+ QuicBufferedPacketStore* store =
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
+
+ testing::MockFunction<void(int check_point)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(check, Call(1));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id_2, _));
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_,
+ QuicStringPiece("HTTP/1"), _))
+ .WillOnce(testing::Return(GetSession(conn_id_2, client_addr_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(
+ GetSession(conn_id_2, client_addr_)->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id_2](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id_2, packet);
+ })));
+
+ EXPECT_CALL(check, Call(2));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(
+ GetSession(conn_id_2, client_addr_)->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id_2](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id_2, packet);
+ })));
+
+ EXPECT_CALL(check, Call(3));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id_1, _));
+
+ EXPECT_CALL(check, Call(4));
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_1, client_addr_,
+ QuicStringPiece("HTTP/1"), _))
+ .WillOnce(testing::Return(GetSession(conn_id_1, client_addr_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(
+ GetSession(conn_id_1, client_addr_)->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillRepeatedly(WithArg<2>(
+ Invoke([this, conn_id_1](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id_1, packet);
+ })));
+ }
+
+ // Send a CHLO that the StatelessRejector will accept.
+ ProcessPacket(client_addr_, conn_id_1, true, SerializeFullCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Send another CHLO that the StatelessRejector will accept.
+ ProcessPacket(client_addr_, conn_id_2, true, SerializeFullCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
+
+ // Complete the second ProofSource::GetProof call and verify that a session is
+ // created.
+ check.Call(1);
+ GetFakeProofSource()->InvokePendingCallback(1);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Verify that a data packet on that connection gets processed immediately.
+ check.Call(2);
+ ProcessPacket(client_addr_, conn_id_2, true, "My name is Data");
+
+ // Verify that a data packet on the other connection does not get processed
+ // yet.
+ check.Call(3);
+ ProcessPacket(client_addr_, conn_id_1, true, "My name is Data");
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id_1));
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_2));
+
+ // Complete the first ProofSource::GetProof call and verify that a session is
+ // created and the buffered packet is processed.
+ check.Call(4);
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+}
+
+// Test a situation with multiple interleaved connections which the
+// StatelessRejector will reject.
+TEST_F(AsyncGetProofTest, MultipleReject) {
+ CreateTimeWaitListManager();
+
+ QuicConnectionId conn_id_1 = TestConnectionId(1);
+ QuicConnectionId conn_id_2 = TestConnectionId(2);
+ QuicBufferedPacketStore* store =
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
+
+ testing::MockFunction<void(int check_point)> check;
+ {
+ InSequence s;
+
+ EXPECT_CALL(check, Call(1));
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_2, client_addr_, _, _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(conn_id_2, _, _, _, _));
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, client_addr_, conn_id_2, _, _));
+
+ EXPECT_CALL(check, Call(2));
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, client_addr_, conn_id_2, _, _));
+
+ EXPECT_CALL(check, Call(3));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id_1, _));
+
+ EXPECT_CALL(check, Call(4));
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(conn_id_1, _, _, _, _));
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, client_addr_, conn_id_1, _, _));
+ }
+
+ // Send a CHLO that the StatelessRejector will reject.
+ ProcessPacket(client_addr_, conn_id_1, true, SerializeCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Send another CHLO that the StatelessRejector will reject.
+ ProcessPacket(client_addr_, conn_id_2, true, SerializeCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
+
+ // Complete the second ProofSource::GetProof call and verify that the
+ // connection and packet are processed by the time wait manager.
+ check.Call(1);
+ GetFakeProofSource()->InvokePendingCallback(1);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Verify that a data packet on that connection gets processed immediately by
+ // the time wait manager.
+ check.Call(2);
+ ProcessPacket(client_addr_, conn_id_2, true, "My name is Data");
+
+ // Verify that a data packet on the first connection gets buffered.
+ check.Call(3);
+ ProcessPacket(client_addr_, conn_id_1, true, "My name is Data");
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id_1));
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_2));
+
+ // Complete the first ProofSource::GetProof call and verify that the CHLO is
+ // processed by the time wait manager and the remaining packets are discarded.
+ check.Call(4);
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_1));
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_2));
+}
+
+// Test a situation with multiple identical CHLOs which the StatelessRejector
+// will reject.
+TEST_F(AsyncGetProofTest, MultipleIdenticalReject) {
+ CreateTimeWaitListManager();
+
+ QuicConnectionId conn_id_1 = TestConnectionId(1);
+ QuicBufferedPacketStore* store =
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
+
+ testing::MockFunction<void(int check_point)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(check, Call(1));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id_1, _));
+
+ EXPECT_CALL(check, Call(2));
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id_1, client_addr_,
+ QuicStringPiece(), _))
+ .Times(0);
+ EXPECT_CALL(*time_wait_list_manager_,
+ AddConnectionIdToTimeWait(conn_id_1, _, _, _, _));
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, client_addr_, conn_id_1, _, _));
+ }
+
+ // Send a CHLO that the StatelessRejector will reject.
+ ProcessPacket(client_addr_, conn_id_1, true, SerializeCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_1));
+
+ // Send an identical CHLO which should get buffered.
+ check.Call(1);
+ ProcessPacket(client_addr_, conn_id_1, true, SerializeCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id_1));
+
+ // Complete the ProofSource::GetProof call and verify that the CHLO is
+ // rejected and the copy is discarded.
+ check.Call(2);
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id_1));
+}
+
+// Test dispatcher behavior when packets time out of the buffer while CHLO
+// validation is still pending.
+TEST_F(AsyncGetProofTest, BufferTimeout) {
+ CreateTimeWaitListManager();
+
+ QuicConnectionId conn_id = TestConnectionId(1);
+ QuicBufferedPacketStore* store =
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
+ QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock());
+
+ testing::MockFunction<void(int check_point)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(check, Call(1));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id, _));
+
+ EXPECT_CALL(check, Call(2));
+ EXPECT_CALL(*time_wait_list_manager_,
+ ProcessPacket(_, client_addr_, conn_id, _, _));
+ EXPECT_CALL(*dispatcher_,
+ CreateQuicSession(conn_id, client_addr_, QuicStringPiece(), _))
+ .Times(0);
+ }
+
+ // Send a CHLO that the StatelessRejector will accept.
+ ProcessPacket(client_addr_, conn_id, true, SerializeFullCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id));
+
+ // Send a data packet that will get buffered
+ check.Call(1);
+ ProcessPacket(client_addr_, conn_id, true, "My name is Data");
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id));
+
+ // Pretend that enough time has gone by for the packets to get expired out of
+ // the buffer
+ mock_helper_.AdvanceTime(
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs));
+ QuicBufferedPacketStorePeer::expiration_alarm(store)->Cancel();
+ store->OnExpirationTimeout();
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id));
+ EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
+
+ // Now allow the CHLO validation to complete, and verify that no connection
+ // gets created.
+ check.Call(2);
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id));
+ EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
+}
+
+// Test behavior when packets time out of the buffer *and* the connection times
+// out of the time wait manager while CHLO validation is still pending. This
+// *should* be impossible, but anything can happen with timing conditions.
+TEST_F(AsyncGetProofTest, TimeWaitTimeout) {
+ QuicConnectionId conn_id = TestConnectionId(1);
+ QuicBufferedPacketStore* store =
+ QuicDispatcherPeer::GetBufferedPackets(dispatcher_.get());
+ QuicBufferedPacketStorePeer::set_clock(store, mock_helper_.GetClock());
+ CreateTimeWaitListManager();
+ QuicTimeWaitListManagerPeer::set_clock(time_wait_list_manager_,
+ mock_helper_.GetClock());
+
+ testing::MockFunction<void(int check_point)> check;
+ {
+ InSequence s;
+ EXPECT_CALL(check, Call(1));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id, _));
+
+ EXPECT_CALL(check, Call(2));
+ EXPECT_CALL(*dispatcher_,
+ ShouldCreateOrBufferPacketForConnection(conn_id, _));
+ EXPECT_CALL(*dispatcher_, CreateQuicSession(conn_id, client_addr_,
+ QuicStringPiece("HTTP/1"), _))
+ .WillOnce(testing::Return(GetSession(conn_id, client_addr_)));
+ EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(
+ GetSession(conn_id, client_addr_)->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(WithArg<2>(
+ Invoke([this, conn_id](const QuicEncryptedPacket& packet) {
+ ValidatePacket(conn_id, packet);
+ })));
+ }
+
+ // Send a CHLO that the StatelessRejector will accept.
+ ProcessPacket(client_addr_, conn_id, true, SerializeFullCHLO());
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id));
+
+ // Send a data packet that will get buffered
+ check.Call(1);
+ ProcessPacket(client_addr_, conn_id, true, "My name is Data");
+ EXPECT_TRUE(store->HasBufferedPackets(conn_id));
+
+ // Pretend that enough time has gone by for the packets to get expired out of
+ // the buffer
+ mock_helper_.AdvanceTime(
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs));
+ QuicBufferedPacketStorePeer::expiration_alarm(store)->Cancel();
+ store->OnExpirationTimeout();
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id));
+ EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
+
+ // Pretend that enough time has gone by for the connection ID to be removed
+ // from the time wait manager
+ mock_helper_.AdvanceTime(
+ QuicTimeWaitListManagerPeer::time_wait_period(time_wait_list_manager_));
+ QuicTimeWaitListManagerPeer::expiration_alarm(time_wait_list_manager_)
+ ->Cancel();
+ time_wait_list_manager_->CleanUpOldConnectionIds();
+ EXPECT_FALSE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
+
+ // Now allow the CHLO validation to complete. Expect that a connection is
+ // indeed created, since QUIC has forgotten that this connection ever existed.
+ // This is a miniscule corner case which should never happen in the wild, so
+ // really we are just verifying that the dispatcher does not explode in this
+ // situation.
+ check.Call(2);
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+ EXPECT_FALSE(store->HasBufferedPackets(conn_id));
+ EXPECT_FALSE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
+}
+
+// Regression test for
+// https://bugs.chromium.org/p/chromium/issues/detail?id=748289
+TEST_F(AsyncGetProofTest, DispatcherFailedToPickUpVersionForAsyncProof) {
+ // This test mimics the scenario that dispatcher's framer can have different
+ // version when async proof returns.
+ // When dispatcher sends SREJ, the SREJ frame can be serialized in
+ // different endianness which causes the client to close the connection
+ // because of QUIC_INVALID_STREAM_DATA.
+
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ ParsedQuicVersion chlo_version(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43);
+ chlo_.SetVersion(kVER, chlo_version);
+ // Send a CHLO with v43. Dispatcher framer's version is set to v43.
+ ProcessPacket(client_addr_, TestConnectionId(1), true, chlo_version,
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Send another CHLO with v39. Dispatcher framer's version is set to v39.
+ chlo_version.transport_version = QUIC_VERSION_39;
+ chlo_.SetVersion(kVER, chlo_version);
+ // Invalidate the cached serialized form.
+ chlo_.MarkDirty();
+ ProcessPacket(client_addr_, TestConnectionId(2), true, chlo_version,
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
+
+ // Complete the ProofSource::GetProof call for v43. This would cause the
+ // version mismatch between the CHLO packet and the dispatcher.
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+}
+
+// Regression test for b/116200989.
+TEST_F(AsyncGetProofTest, DispatcherHasWrongLastPacketIsIetfQuic) {
+ // Process a packet of v44.
+ ParsedQuicVersion chlo_version(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44);
+ chlo_.SetVersion(kVER, chlo_version);
+ ProcessPacket(client_addr_, TestConnectionId(1), true, chlo_version,
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+
+ // Process another packet of v43.
+ chlo_version.transport_version = QUIC_VERSION_43;
+ chlo_.SetVersion(kVER, chlo_version);
+ // Invalidate the cached serialized form.
+ chlo_.MarkDirty();
+ ProcessPacket(client_addr_, TestConnectionId(2), true, chlo_version,
+ SerializeCHLO(), CONNECTION_ID_PRESENT,
+ PACKET_4BYTE_PACKET_NUMBER, 1);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 2);
+
+ // Complete the ProofSource::GetProof call for v44.
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 1);
+
+ // Complete the ProofSource::GetProof call for v43.
+ GetFakeProofSource()->InvokePendingCallback(0);
+ ASSERT_EQ(GetFakeProofSource()->NumPendingCallbacks(), 0);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.cc b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.cc
new file mode 100644
index 00000000000..75b375ff0ed
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+
+#include <type_traits>
+
+#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h"
+
+namespace quic {
+namespace {
+
+class QuicEpollAlarm : public QuicAlarm {
+ public:
+ QuicEpollAlarm(QuicEpollServer* epoll_server,
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
+ : QuicAlarm(std::move(delegate)),
+ epoll_server_(epoll_server),
+ epoll_alarm_impl_(this) {}
+
+ protected:
+ void SetImpl() override {
+ DCHECK(deadline().IsInitialized());
+ epoll_server_->RegisterAlarm(
+ (deadline() - QuicTime::Zero()).ToMicroseconds(), &epoll_alarm_impl_);
+ }
+
+ void CancelImpl() override {
+ DCHECK(!deadline().IsInitialized());
+ epoll_alarm_impl_.UnregisterIfRegistered();
+ }
+
+ void UpdateImpl() override {
+ DCHECK(deadline().IsInitialized());
+ int64_t epoll_deadline = (deadline() - QuicTime::Zero()).ToMicroseconds();
+ if (epoll_alarm_impl_.registered()) {
+ epoll_alarm_impl_.ReregisterAlarm(epoll_deadline);
+ } else {
+ epoll_server_->RegisterAlarm(epoll_deadline, &epoll_alarm_impl_);
+ }
+ }
+
+ private:
+ class EpollAlarmImpl : public QuicEpollAlarmBase {
+ public:
+ using int64_epoll = decltype(QuicEpollAlarmBase().OnAlarm());
+
+ explicit EpollAlarmImpl(QuicEpollAlarm* alarm) : alarm_(alarm) {}
+
+ // Use the same integer type as the base class.
+ int64_epoll OnAlarm() override {
+ QuicEpollAlarmBase::OnAlarm();
+ alarm_->Fire();
+ // Fire will take care of registering the alarm, if needed.
+ return 0;
+ }
+
+ private:
+ QuicEpollAlarm* alarm_;
+ };
+
+ QuicEpollServer* epoll_server_;
+ EpollAlarmImpl epoll_alarm_impl_;
+};
+
+} // namespace
+
+QuicEpollAlarmFactory::QuicEpollAlarmFactory(QuicEpollServer* epoll_server)
+ : epoll_server_(epoll_server) {}
+
+QuicEpollAlarmFactory::~QuicEpollAlarmFactory() = default;
+
+QuicAlarm* QuicEpollAlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) {
+ return new QuicEpollAlarm(epoll_server_,
+ QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
+}
+
+QuicArenaScopedPtr<QuicAlarm> QuicEpollAlarmFactory::CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) {
+ if (arena != nullptr) {
+ return arena->New<QuicEpollAlarm>(epoll_server_, std::move(delegate));
+ }
+ return QuicArenaScopedPtr<QuicAlarm>(
+ new QuicEpollAlarm(epoll_server_, std::move(delegate)));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h
new file mode 100644
index 00000000000..fc9b45c393f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_EPOLL_ALARM_FACTORY_H_
+#define QUICHE_QUIC_CORE_QUIC_EPOLL_ALARM_FACTORY_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+
+namespace quic {
+
+// Creates alarms that use the supplied EpollServer for timing and firing.
+class QuicEpollAlarmFactory : public QuicAlarmFactory {
+ public:
+ explicit QuicEpollAlarmFactory(QuicEpollServer* eps);
+ QuicEpollAlarmFactory(const QuicEpollAlarmFactory&) = delete;
+ QuicEpollAlarmFactory& operator=(const QuicEpollAlarmFactory&) = delete;
+ ~QuicEpollAlarmFactory() override;
+
+ // QuicAlarmFactory interface.
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override;
+
+ private:
+ QuicEpollServer* epoll_server_; // Not owned.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_EPOLL_ALARM_FACTORY_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory_test.cc
new file mode 100644
index 00000000000..ac950c6e669
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory_test.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll_test_tools.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/quic/platform/impl/quic_epoll_clock.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestDelegate : public QuicAlarm::Delegate {
+ public:
+ TestDelegate() : fired_(false) {}
+
+ void OnAlarm() override { fired_ = true; }
+
+ bool fired() const { return fired_; }
+
+ private:
+ bool fired_;
+};
+
+// The boolean parameter denotes whether or not to use an arena.
+class QuicEpollAlarmFactoryTest : public QuicTestWithParam<bool> {
+ protected:
+ QuicEpollAlarmFactoryTest()
+ : clock_(&epoll_server_), alarm_factory_(&epoll_server_) {}
+
+ QuicConnectionArena* GetArenaParam() {
+ return GetParam() ? &arena_ : nullptr;
+ }
+
+ const QuicEpollClock clock_;
+ QuicEpollAlarmFactory alarm_factory_;
+ QuicFakeEpollServer epoll_server_;
+ QuicConnectionArena arena_;
+};
+
+INSTANTIATE_TEST_SUITE_P(UseArena,
+ QuicEpollAlarmFactoryTest,
+ ::testing::ValuesIn({true, false}));
+
+TEST_P(QuicEpollAlarmFactoryTest, CreateAlarm) {
+ QuicArenaScopedPtr<TestDelegate> delegate =
+ QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
+ QuicArenaScopedPtr<QuicAlarm> alarm(
+ alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam()));
+
+ QuicTime start = clock_.Now();
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(start + delta);
+
+ epoll_server_.AdvanceByAndWaitForEventsAndExecuteCallbacks(
+ delta.ToMicroseconds());
+ EXPECT_EQ(start + delta, clock_.Now());
+}
+
+TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndCancel) {
+ QuicArenaScopedPtr<TestDelegate> delegate =
+ QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
+ TestDelegate* unowned_delegate = delegate.get();
+ QuicArenaScopedPtr<QuicAlarm> alarm(
+ alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam()));
+
+ QuicTime start = clock_.Now();
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(start + delta);
+ alarm->Cancel();
+
+ epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds());
+ EXPECT_EQ(start + delta, clock_.Now());
+ EXPECT_FALSE(unowned_delegate->fired());
+}
+
+TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndReset) {
+ QuicArenaScopedPtr<TestDelegate> delegate =
+ QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
+ TestDelegate* unowned_delegate = delegate.get();
+ QuicArenaScopedPtr<QuicAlarm> alarm(
+ alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam()));
+
+ QuicTime start = clock_.Now();
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(clock_.Now() + delta);
+ alarm->Cancel();
+ QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3);
+ alarm->Set(clock_.Now() + new_delta);
+
+ epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds());
+ EXPECT_EQ(start + delta, clock_.Now());
+ EXPECT_FALSE(unowned_delegate->fired());
+
+ epoll_server_.AdvanceByExactlyAndCallCallbacks(
+ (new_delta - delta).ToMicroseconds());
+ EXPECT_EQ(start + new_delta, clock_.Now());
+ EXPECT_TRUE(unowned_delegate->fired());
+}
+
+TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndUpdate) {
+ QuicArenaScopedPtr<TestDelegate> delegate =
+ QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
+ TestDelegate* unowned_delegate = delegate.get();
+ QuicArenaScopedPtr<QuicAlarm> alarm(
+ alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam()));
+
+ QuicTime start = clock_.Now();
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(clock_.Now() + delta);
+ QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3);
+ alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(1));
+
+ epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds());
+ EXPECT_EQ(start + delta, clock_.Now());
+ EXPECT_FALSE(unowned_delegate->fired());
+
+ // Move the alarm forward 1us and ensure it doesn't move forward.
+ alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(2));
+
+ epoll_server_.AdvanceByExactlyAndCallCallbacks(
+ (new_delta - delta).ToMicroseconds());
+ EXPECT_EQ(start + new_delta, clock_.Now());
+ EXPECT_TRUE(unowned_delegate->fired());
+
+ // Set the alarm via an update call.
+ new_delta = QuicTime::Delta::FromMicroseconds(5);
+ alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(1));
+ EXPECT_TRUE(alarm->IsSet());
+
+ // Update it with an uninitialized time and ensure it's cancelled.
+ alarm->Update(QuicTime::Zero(), QuicTime::Delta::FromMicroseconds(1));
+ EXPECT_FALSE(alarm->IsSet());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.cc b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.cc
new file mode 100644
index 00000000000..1f5fb87b9d3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+namespace quic {
+
+QuicEpollConnectionHelper::QuicEpollConnectionHelper(
+ QuicEpollServer* epoll_server,
+ QuicAllocator type)
+ : clock_(epoll_server),
+ random_generator_(QuicRandom::GetInstance()),
+ allocator_type_(type) {}
+
+QuicEpollConnectionHelper::~QuicEpollConnectionHelper() = default;
+
+const QuicClock* QuicEpollConnectionHelper::GetClock() const {
+ return &clock_;
+}
+
+QuicRandom* QuicEpollConnectionHelper::GetRandomGenerator() {
+ return random_generator_;
+}
+
+QuicBufferAllocator* QuicEpollConnectionHelper::GetStreamSendBufferAllocator() {
+ if (allocator_type_ == QuicAllocator::BUFFER_POOL) {
+ return &stream_buffer_allocator_;
+ } else {
+ DCHECK(allocator_type_ == QuicAllocator::SIMPLE);
+ return &simple_buffer_allocator_;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h
new file mode 100644
index 00000000000..7041454f907
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The epoll-specific helper for QuicConnection which uses
+// EpollAlarm for alarms, and used an int fd_ for writing data.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_EPOLL_CONNECTION_HELPER_H_
+#define QUICHE_QUIC_CORE_QUIC_EPOLL_CONNECTION_HELPER_H_
+
+#include <sys/types.h>
+#include <set>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_stream_buffer_allocator.h"
+#include "net/quic/platform/impl/quic_epoll_clock.h"
+
+namespace quic {
+
+class QuicRandom;
+
+enum class QuicAllocator { SIMPLE, BUFFER_POOL };
+
+class QuicEpollConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+ QuicEpollConnectionHelper(QuicEpollServer* eps, QuicAllocator allocator);
+ QuicEpollConnectionHelper(const QuicEpollConnectionHelper&) = delete;
+ QuicEpollConnectionHelper& operator=(const QuicEpollConnectionHelper&) =
+ delete;
+ ~QuicEpollConnectionHelper() override;
+
+ // QuicConnectionHelperInterface
+ const QuicClock* GetClock() const override;
+ QuicRandom* GetRandomGenerator() override;
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+
+ private:
+ const QuicEpollClock clock_;
+ QuicRandom* random_generator_;
+ // Set up allocators. They take up minimal memory before use.
+ // Allocator for stream send buffers.
+ QuicStreamBufferAllocator stream_buffer_allocator_;
+ SimpleBufferAllocator simple_buffer_allocator_;
+ QuicAllocator allocator_type_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_EPOLL_CONNECTION_HELPER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper_test.cc
new file mode 100644
index 00000000000..ea639480ec1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_epoll_connection_helper_test.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll_test_tools.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicEpollConnectionHelperTest : public QuicTest {
+ protected:
+ QuicEpollConnectionHelperTest()
+ : helper_(&epoll_server_, QuicAllocator::BUFFER_POOL) {}
+
+ QuicFakeEpollServer epoll_server_;
+ QuicEpollConnectionHelper helper_;
+};
+
+TEST_F(QuicEpollConnectionHelperTest, GetClock) {
+ const QuicClock* clock = helper_.GetClock();
+ QuicTime start = clock->Now();
+
+ QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(5);
+ epoll_server_.AdvanceBy(delta.ToMicroseconds());
+
+ EXPECT_EQ(start + delta, clock->Now());
+}
+
+TEST_F(QuicEpollConnectionHelperTest, GetRandomGenerator) {
+ QuicRandom* random = helper_.GetRandomGenerator();
+ EXPECT_EQ(QuicRandom::GetInstance(), random);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..e2a14ade998
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+
+namespace quic {
+
+#define RETURN_STRING_LITERAL(x) \
+ case x: \
+ return #x;
+
+const char* QuicRstStreamErrorCodeToString(QuicRstStreamErrorCode error) {
+ switch (error) {
+ RETURN_STRING_LITERAL(QUIC_STREAM_NO_ERROR);
+ RETURN_STRING_LITERAL(QUIC_STREAM_CONNECTION_ERROR);
+ RETURN_STRING_LITERAL(QUIC_ERROR_PROCESSING_STREAM);
+ RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS);
+ RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD);
+ RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY);
+ RETURN_STRING_LITERAL(QUIC_STREAM_CANCELLED);
+ RETURN_STRING_LITERAL(QUIC_RST_ACKNOWLEDGEMENT);
+ RETURN_STRING_LITERAL(QUIC_REFUSED_STREAM);
+ RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PROMISE_URL);
+ RETURN_STRING_LITERAL(QUIC_UNAUTHORIZED_PROMISE_URL);
+ RETURN_STRING_LITERAL(QUIC_DUPLICATE_PROMISE_URL);
+ RETURN_STRING_LITERAL(QUIC_PROMISE_VARY_MISMATCH);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PROMISE_METHOD);
+ RETURN_STRING_LITERAL(QUIC_PUSH_STREAM_TIMED_OUT);
+ RETURN_STRING_LITERAL(QUIC_HEADERS_TOO_LARGE);
+ RETURN_STRING_LITERAL(QUIC_STREAM_TTL_EXPIRED);
+ }
+ // Return a default value so that we return this when |error| doesn't match
+ // any of the QuicRstStreamErrorCodes. This can happen when the RstStream
+ // frame sent by the peer (attacker) has invalid error code.
+ return "INVALID_RST_STREAM_ERROR_CODE";
+}
+
+const char* QuicErrorCodeToString(QuicErrorCode error) {
+ switch (error) {
+ RETURN_STRING_LITERAL(QUIC_NO_ERROR);
+ RETURN_STRING_LITERAL(QUIC_INTERNAL_ERROR);
+ RETURN_STRING_LITERAL(QUIC_STREAM_DATA_AFTER_TERMINATION);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PACKET_HEADER);
+ RETURN_STRING_LITERAL(QUIC_INVALID_FRAME_DATA);
+ RETURN_STRING_LITERAL(QUIC_MISSING_PAYLOAD);
+ RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_OVERLAPPING_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_RST_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_WINDOW_UPDATE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_BLOCKED_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STOP_WAITING_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PATH_CLOSE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PUBLIC_RST_PACKET);
+ RETURN_STRING_LITERAL(QUIC_DECRYPTION_FAILURE);
+ RETURN_STRING_LITERAL(QUIC_ENCRYPTION_FAILURE);
+ RETURN_STRING_LITERAL(QUIC_PACKET_TOO_LARGE);
+ RETURN_STRING_LITERAL(QUIC_PEER_GOING_AWAY);
+ RETURN_STRING_LITERAL(QUIC_HANDSHAKE_FAILED);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_TAGS_OUT_OF_ORDER);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_ENTRIES);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_REJECTS);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH)
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_INTERNAL_ERROR);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_VERSION_NOT_SUPPORTED);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_NO_SUPPORT);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_TYPE);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND);
+ RETURN_STRING_LITERAL(QUIC_UNSUPPORTED_PROOF_DEMAND);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PRIORITY);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS);
+ RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET);
+ RETURN_STRING_LITERAL(QUIC_INVALID_VERSION);
+ RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID);
+ RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE);
+ RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE);
+ RETURN_STRING_LITERAL(QUIC_NETWORK_IDLE_TIMEOUT);
+ RETURN_STRING_LITERAL(QUIC_HANDSHAKE_TIMEOUT);
+ RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_ADDRESS);
+ RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_PORT);
+ RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR);
+ RETURN_STRING_LITERAL(QUIC_PACKET_READ_ERROR);
+ RETURN_STRING_LITERAL(QUIC_EMPTY_STREAM_FRAME_NO_FIN);
+ RETURN_STRING_LITERAL(QUIC_INVALID_HEADERS_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_IP_POOLED);
+ RETURN_STRING_LITERAL(QUIC_PROOF_INVALID);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_SERVER_CONFIG_EXPIRED);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CHANNEL_ID_SIGNATURE);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE);
+ RETURN_STRING_LITERAL(QUIC_VERSION_NEGOTIATION_MISMATCH);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_CANCELLED);
+ RETURN_STRING_LITERAL(QUIC_BAD_PACKET_LOSS_RATE);
+ RETURN_STRING_LITERAL(QUIC_PUBLIC_RESETS_POST_HANDSHAKE);
+ RETURN_STRING_LITERAL(QUIC_FAILED_TO_SERIALIZE_PACKET);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_AVAILABLE_STREAMS);
+ RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_FEC_DATA);
+ RETURN_STRING_LITERAL(QUIC_BAD_MULTIPATH_FLAG);
+ RETURN_STRING_LITERAL(QUIC_IP_ADDRESS_CHANGED);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_RTOS);
+ RETURN_STRING_LITERAL(QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_MAYBE_CORRUPTED_MEMORY);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_CHLO_TOO_LARGE);
+ RETURN_STRING_LITERAL(QUIC_MULTIPATH_PATH_DOES_NOT_EXIST);
+ RETURN_STRING_LITERAL(QUIC_MULTIPATH_PATH_NOT_ACTIVE);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_STREAM_DATA_INTERVALS);
+ RETURN_STRING_LITERAL(QUIC_STREAM_SEQUENCER_INVALID_STATE);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_SESSIONS_ON_SERVER);
+ RETURN_STRING_LITERAL(QUIC_STREAM_LENGTH_OVERFLOW);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR);
+ RETURN_STRING_LITERAL(QUIC_INVALID_MAX_DATA_FRAME_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_BLOCKED_DATA);
+ RETURN_STRING_LITERAL(QUIC_MAX_STREAM_ID_DATA);
+ RETURN_STRING_LITERAL(QUIC_STREAM_ID_BLOCKED_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_NEW_CONNECTION_ID_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_RETIRE_CONNECTION_ID_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STOP_SENDING_FRAME_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PATH_CHALLENGE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PATH_RESPONSE_DATA);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED);
+ RETURN_STRING_LITERAL(QUIC_INVALID_MESSAGE_DATA);
+ RETURN_STRING_LITERAL(IETF_QUIC_PROTOCOL_VIOLATION);
+ RETURN_STRING_LITERAL(QUIC_INVALID_NEW_TOKEN);
+ RETURN_STRING_LITERAL(QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM);
+ RETURN_STRING_LITERAL(QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM);
+ RETURN_STRING_LITERAL(QUIC_STREAM_ID_BLOCKED_ERROR);
+ RETURN_STRING_LITERAL(QUIC_MAX_STREAM_ID_ERROR);
+ RETURN_STRING_LITERAL(QUIC_HTTP_DECODER_ERROR);
+ RETURN_STRING_LITERAL(QUIC_STALE_CONNECTION_CANCELLED);
+ RETURN_STRING_LITERAL(QUIC_IETF_GQUIC_ERROR_MISSING);
+
+ RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
+ // Intentionally have no default case, so we'll break the build
+ // if we add errors and don't put them here.
+ }
+ // Return a default value so that we return this when |error| doesn't match
+ // any of the QuicErrorCodes. This can happen when the ConnectionClose
+ // frame sent by the peer (attacker) has invalid error code.
+ return "INVALID_ERROR_CODE";
+}
+
+#undef RETURN_STRING_LITERAL // undef for jumbo builds
+} // namespace quic
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
new file mode 100644
index 00000000000..b06cc7f57d3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes.h
@@ -0,0 +1,363 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_ERROR_CODES_H_
+#define QUICHE_QUIC_CORE_QUIC_ERROR_CODES_H_
+
+#include <cstdint>
+#include <limits>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+enum QuicRstStreamErrorCode {
+ // Complete response has been sent, sending a RST to ask the other endpoint
+ // to stop sending request data without discarding the response.
+ QUIC_STREAM_NO_ERROR = 0,
+
+ // There was some error which halted stream processing.
+ QUIC_ERROR_PROCESSING_STREAM,
+ // We got two fin or reset offsets which did not match.
+ QUIC_MULTIPLE_TERMINATION_OFFSETS,
+ // We got bad payload and can not respond to it at the protocol level.
+ QUIC_BAD_APPLICATION_PAYLOAD,
+ // Stream closed due to connection error. No reset frame is sent when this
+ // happens.
+ QUIC_STREAM_CONNECTION_ERROR,
+ // GoAway frame sent. No more stream can be created.
+ QUIC_STREAM_PEER_GOING_AWAY,
+ // The stream has been cancelled.
+ QUIC_STREAM_CANCELLED,
+ // Closing stream locally, sending a RST to allow for proper flow control
+ // accounting. Sent in response to a RST from the peer.
+ QUIC_RST_ACKNOWLEDGEMENT,
+ // Receiver refused to create the stream (because its limit on open streams
+ // has been reached). The sender should retry the request later (using
+ // another stream).
+ QUIC_REFUSED_STREAM,
+ // Invalid URL in PUSH_PROMISE request header.
+ QUIC_INVALID_PROMISE_URL,
+ // Server is not authoritative for this URL.
+ QUIC_UNAUTHORIZED_PROMISE_URL,
+ // Can't have more than one active PUSH_PROMISE per URL.
+ QUIC_DUPLICATE_PROMISE_URL,
+ // Vary check failed.
+ QUIC_PROMISE_VARY_MISMATCH,
+ // Only GET and HEAD methods allowed.
+ QUIC_INVALID_PROMISE_METHOD,
+ // The push stream is unclaimed and timed out.
+ QUIC_PUSH_STREAM_TIMED_OUT,
+ // Received headers were too large.
+ QUIC_HEADERS_TOO_LARGE,
+ // The data is not likely arrive in time.
+ QUIC_STREAM_TTL_EXPIRED,
+ // No error. Used as bound while iterating.
+ QUIC_STREAM_LAST_ERROR,
+};
+// QuicRstStreamErrorCode is encoded as a single octet on-the-wire.
+static_assert(static_cast<int>(QUIC_STREAM_LAST_ERROR) <=
+ std::numeric_limits<uint8_t>::max(),
+ "QuicRstStreamErrorCode exceeds single octet");
+
+// These values must remain stable as they are uploaded to UMA histograms.
+// To add a new error code, use the current value of QUIC_LAST_ERROR and
+// increment QUIC_LAST_ERROR.
+enum QuicErrorCode {
+ QUIC_NO_ERROR = 0,
+
+ // Connection has reached an invalid state.
+ QUIC_INTERNAL_ERROR = 1,
+ // There were data frames after the a fin or reset.
+ QUIC_STREAM_DATA_AFTER_TERMINATION = 2,
+ // Control frame is malformed.
+ QUIC_INVALID_PACKET_HEADER = 3,
+ // Frame data is malformed.
+ QUIC_INVALID_FRAME_DATA = 4,
+ // The packet contained no payload.
+ QUIC_MISSING_PAYLOAD = 48,
+ // FEC data is malformed.
+ QUIC_INVALID_FEC_DATA = 5,
+ // STREAM frame data is malformed.
+ QUIC_INVALID_STREAM_DATA = 46,
+ // STREAM frame data overlaps with buffered data.
+ QUIC_OVERLAPPING_STREAM_DATA = 87,
+ // Received STREAM frame data is not encrypted.
+ QUIC_UNENCRYPTED_STREAM_DATA = 61,
+ // Attempt to send unencrypted STREAM frame.
+ QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA = 88,
+ // Received a frame which is likely the result of memory corruption.
+ QUIC_MAYBE_CORRUPTED_MEMORY = 89,
+ // FEC frame data is not encrypted.
+ QUIC_UNENCRYPTED_FEC_DATA = 77,
+ // RST_STREAM frame data is malformed.
+ QUIC_INVALID_RST_STREAM_DATA = 6,
+ // CONNECTION_CLOSE frame data is malformed.
+ QUIC_INVALID_CONNECTION_CLOSE_DATA = 7,
+ // GOAWAY frame data is malformed.
+ QUIC_INVALID_GOAWAY_DATA = 8,
+ // WINDOW_UPDATE frame data is malformed.
+ QUIC_INVALID_WINDOW_UPDATE_DATA = 57,
+ // BLOCKED frame data is malformed.
+ QUIC_INVALID_BLOCKED_DATA = 58,
+ // STOP_WAITING frame data is malformed.
+ QUIC_INVALID_STOP_WAITING_DATA = 60,
+ // PATH_CLOSE frame data is malformed.
+ QUIC_INVALID_PATH_CLOSE_DATA = 78,
+ // ACK frame data is malformed.
+ QUIC_INVALID_ACK_DATA = 9,
+ // Message frame data is malformed.
+ QUIC_INVALID_MESSAGE_DATA = 112,
+
+ // Version negotiation packet is malformed.
+ QUIC_INVALID_VERSION_NEGOTIATION_PACKET = 10,
+ // Public RST packet is malformed.
+ QUIC_INVALID_PUBLIC_RST_PACKET = 11,
+ // There was an error decrypting.
+ QUIC_DECRYPTION_FAILURE = 12,
+ // There was an error encrypting.
+ QUIC_ENCRYPTION_FAILURE = 13,
+ // The packet exceeded kMaxOutgoingPacketSize.
+ QUIC_PACKET_TOO_LARGE = 14,
+ // The peer is going away. May be a client or server.
+ QUIC_PEER_GOING_AWAY = 16,
+ // A stream ID was invalid.
+ QUIC_INVALID_STREAM_ID = 17,
+ // A priority was invalid.
+ QUIC_INVALID_PRIORITY = 49,
+ // Too many streams already open.
+ QUIC_TOO_MANY_OPEN_STREAMS = 18,
+ // The peer created too many available streams.
+ QUIC_TOO_MANY_AVAILABLE_STREAMS = 76,
+ // Received public reset for this connection.
+ QUIC_PUBLIC_RESET = 19,
+ // Invalid protocol version.
+ QUIC_INVALID_VERSION = 20,
+
+ // The Header ID for a stream was too far from the previous.
+ QUIC_INVALID_HEADER_ID = 22,
+ // Negotiable parameter received during handshake had invalid value.
+ QUIC_INVALID_NEGOTIATED_VALUE = 23,
+ // There was an error decompressing data.
+ QUIC_DECOMPRESSION_FAILURE = 24,
+ // The connection timed out due to no network activity.
+ QUIC_NETWORK_IDLE_TIMEOUT = 25,
+ // The connection timed out waiting for the handshake to complete.
+ QUIC_HANDSHAKE_TIMEOUT = 67,
+ // There was an error encountered migrating addresses.
+ QUIC_ERROR_MIGRATING_ADDRESS = 26,
+ // There was an error encountered migrating port only.
+ QUIC_ERROR_MIGRATING_PORT = 86,
+ // There was an error while writing to the socket.
+ QUIC_PACKET_WRITE_ERROR = 27,
+ // There was an error while reading from the socket.
+ QUIC_PACKET_READ_ERROR = 51,
+ // We received a STREAM_FRAME with no data and no fin flag set.
+ QUIC_EMPTY_STREAM_FRAME_NO_FIN = 50,
+ // We received invalid data on the headers stream.
+ QUIC_INVALID_HEADERS_STREAM_DATA = 56,
+ // Invalid data on the headers stream received because of decompression
+ // failure.
+ QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE = 97,
+ // The peer received too much data, violating flow control.
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA = 59,
+ // The peer sent too much data, violating flow control.
+ QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA = 63,
+ // The peer received an invalid flow control window.
+ QUIC_FLOW_CONTROL_INVALID_WINDOW = 64,
+ // The connection has been IP pooled into an existing connection.
+ QUIC_CONNECTION_IP_POOLED = 62,
+ // The connection has too many outstanding sent packets.
+ QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS = 68,
+ // The connection has too many outstanding received packets.
+ QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS = 69,
+ // The quic connection has been cancelled.
+ QUIC_CONNECTION_CANCELLED = 70,
+ // Disabled QUIC because of high packet loss rate.
+ QUIC_BAD_PACKET_LOSS_RATE = 71,
+ // Disabled QUIC because of too many PUBLIC_RESETs post handshake.
+ QUIC_PUBLIC_RESETS_POST_HANDSHAKE = 73,
+ // Closed because we failed to serialize a packet.
+ QUIC_FAILED_TO_SERIALIZE_PACKET = 75,
+ // QUIC timed out after too many RTOs.
+ QUIC_TOO_MANY_RTOS = 85,
+
+ // Crypto errors.
+
+ // Handshake failed.
+ QUIC_HANDSHAKE_FAILED = 28,
+ // Handshake message contained out of order tags.
+ QUIC_CRYPTO_TAGS_OUT_OF_ORDER = 29,
+ // Handshake message contained too many entries.
+ QUIC_CRYPTO_TOO_MANY_ENTRIES = 30,
+ // Handshake message contained an invalid value length.
+ QUIC_CRYPTO_INVALID_VALUE_LENGTH = 31,
+ // A crypto message was received after the handshake was complete.
+ QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE = 32,
+ // A crypto message was received with an illegal message tag.
+ QUIC_INVALID_CRYPTO_MESSAGE_TYPE = 33,
+ // A crypto message was received with an illegal parameter.
+ QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER = 34,
+ // An invalid channel id signature was supplied.
+ QUIC_INVALID_CHANNEL_ID_SIGNATURE = 52,
+ // A crypto message was received with a mandatory parameter missing.
+ QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND = 35,
+ // A crypto message was received with a parameter that has no overlap
+ // with the local parameter.
+ QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP = 36,
+ // A crypto message was received that contained a parameter with too few
+ // values.
+ QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND = 37,
+ // A demand for an unsupport proof type was received.
+ QUIC_UNSUPPORTED_PROOF_DEMAND = 94,
+ // An internal error occurred in crypto processing.
+ QUIC_CRYPTO_INTERNAL_ERROR = 38,
+ // A crypto handshake message specified an unsupported version.
+ QUIC_CRYPTO_VERSION_NOT_SUPPORTED = 39,
+ // A crypto handshake message resulted in a stateless reject.
+ QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT = 72,
+ // There was no intersection between the crypto primitives supported by the
+ // peer and ourselves.
+ QUIC_CRYPTO_NO_SUPPORT = 40,
+ // The server rejected our client hello messages too many times.
+ QUIC_CRYPTO_TOO_MANY_REJECTS = 41,
+ // The client rejected the server's certificate chain or signature.
+ QUIC_PROOF_INVALID = 42,
+ // A crypto message was received with a duplicate tag.
+ QUIC_CRYPTO_DUPLICATE_TAG = 43,
+ // A crypto message was received with the wrong encryption level (i.e. it
+ // should have been encrypted but was not.)
+ QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT = 44,
+ // The server config for a server has expired.
+ QUIC_CRYPTO_SERVER_CONFIG_EXPIRED = 45,
+ // We failed to setup the symmetric keys for a connection.
+ QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED = 53,
+ // A handshake message arrived, but we are still validating the
+ // previous handshake message.
+ QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO = 54,
+ // A server config update arrived before the handshake is complete.
+ QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE = 65,
+ // CHLO cannot fit in one packet.
+ QUIC_CRYPTO_CHLO_TOO_LARGE = 90,
+ // This connection involved a version negotiation which appears to have been
+ // tampered with.
+ QUIC_VERSION_NEGOTIATION_MISMATCH = 55,
+
+ // Multipath errors.
+ // Multipath is not enabled, but a packet with multipath flag on is received.
+ QUIC_BAD_MULTIPATH_FLAG = 79,
+ // A path is supposed to exist but does not.
+ QUIC_MULTIPATH_PATH_DOES_NOT_EXIST = 91,
+ // A path is supposed to be active but is not.
+ QUIC_MULTIPATH_PATH_NOT_ACTIVE = 92,
+
+ // IP address changed causing connection close.
+ QUIC_IP_ADDRESS_CHANGED = 80,
+
+ // Connection migration errors.
+ // Network changed, but connection had no migratable streams.
+ QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS = 81,
+ // Connection changed networks too many times.
+ QUIC_CONNECTION_MIGRATION_TOO_MANY_CHANGES = 82,
+ // Connection migration was attempted, but there was no new network to
+ // migrate to.
+ QUIC_CONNECTION_MIGRATION_NO_NEW_NETWORK = 83,
+ // Network changed, but connection had one or more non-migratable streams.
+ QUIC_CONNECTION_MIGRATION_NON_MIGRATABLE_STREAM = 84,
+ // Network changed, but connection migration was disabled by config.
+ QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG = 99,
+ // Network changed, but error was encountered on the alternative network.
+ QUIC_CONNECTION_MIGRATION_INTERNAL_ERROR = 100,
+ // Network changed, but handshake is not confirmed yet.
+ QUIC_CONNECTION_MIGRATION_HANDSHAKE_UNCONFIRMED = 111,
+
+ // Stream frames arrived too discontiguously so that stream sequencer buffer
+ // maintains too many intervals.
+ QUIC_TOO_MANY_STREAM_DATA_INTERVALS = 93,
+
+ // Sequencer buffer get into weird state where continuing read/write will lead
+ // to crash.
+ QUIC_STREAM_SEQUENCER_INVALID_STATE = 95,
+
+ // Connection closed because of server hits max number of sessions allowed.
+ QUIC_TOO_MANY_SESSIONS_ON_SERVER = 96,
+
+ // Receive a RST_STREAM with offset larger than kMaxStreamLength.
+ QUIC_STREAM_LENGTH_OVERFLOW = 98,
+ // Received a MAX DATA frame with errors.
+ QUIC_INVALID_MAX_DATA_FRAME_DATA = 102,
+ // Received a MAX STREAM DATA frame with errors.
+ QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA = 103,
+ // Received a MAX_STREAM_ID frame with bad data
+ QUIC_MAX_STREAM_ID_DATA = 104,
+ // Received a STREAM_ID_BLOCKED frame with bad data
+ QUIC_STREAM_ID_BLOCKED_DATA = 105,
+ // Error deframing a STREAM BLOCKED frame.
+ QUIC_INVALID_STREAM_BLOCKED_DATA = 106,
+ // NEW CONNECTION ID frame data is malformed.
+ QUIC_INVALID_NEW_CONNECTION_ID_DATA = 107,
+ // Received a MAX STREAM DATA frame with errors.
+ QUIC_INVALID_STOP_SENDING_FRAME_DATA = 108,
+ // Error deframing PATH CHALLENGE or PATH RESPONSE frames.
+ QUIC_INVALID_PATH_CHALLENGE_DATA = 109,
+ QUIC_INVALID_PATH_RESPONSE_DATA = 110,
+ // This is used to indicate an IETF QUIC PROTOCOL VIOLATION
+ // transport error within Google (pre-v99) QUIC.
+ IETF_QUIC_PROTOCOL_VIOLATION = 113,
+ QUIC_INVALID_NEW_TOKEN = 114,
+
+ // Received stream data on a WRITE_UNIDIRECTIONAL stream.
+ QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM = 115,
+ // Try to send stream data on a READ_UNIDIRECTIONAL stream.
+ QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM = 116,
+
+ // RETIRE CONNECTION ID frame data is malformed.
+ QUIC_INVALID_RETIRE_CONNECTION_ID_DATA = 117,
+ //
+ // Error in a received STREAM ID BLOCKED frame. -- the stream ID is not
+ // consistent with the state of the endpoint.
+ QUIC_STREAM_ID_BLOCKED_ERROR = 118,
+ // Error in a received MAX STREAM ID frame -- the stream ID is not
+ // consistent with the state of the endpoint.
+ QUIC_MAX_STREAM_ID_ERROR = 119,
+ // Error in Http decoder
+ QUIC_HTTP_DECODER_ERROR = 120,
+ // Connection from stale host needs to be cancelled.
+ QUIC_STALE_CONNECTION_CANCELLED = 121,
+
+ // A pseudo error, used as an extended error reason code in the error_details
+ // of IETF-QUIC CONNECTION_CLOSE frames. It is used in
+ // OnConnectionClosed upcalls to indicate that extended error information was
+ // not available in a received CONNECTION_CLOSE frame.
+ QUIC_IETF_GQUIC_ERROR_MISSING = 122,
+
+ // No error. Used as bound while iterating.
+ QUIC_LAST_ERROR = 123,
+};
+// QuicErrorCodes is encoded as a single octet on-the-wire.
+static_assert(static_cast<int>(QUIC_LAST_ERROR) <=
+ std::numeric_limits<uint8_t>::max(),
+ "QuicErrorCode exceeds single octet");
+
+// Returns the name of the QuicRstStreamErrorCode as a char*
+QUIC_EXPORT_PRIVATE const char* QuicRstStreamErrorCodeToString(
+ QuicRstStreamErrorCode error);
+
+// Returns the name of the QuicErrorCode as a char*
+QUIC_EXPORT const char* QuicErrorCodeToString(QuicErrorCode error);
+
+QUIC_EXPORT_PRIVATE inline std::string HistogramEnumString(
+ QuicErrorCode enum_value) {
+ return QuicErrorCodeToString(enum_value);
+}
+
+QUIC_EXPORT_PRIVATE inline std::string HistogramEnumDescription(
+ QuicErrorCode dummy) {
+ return "cause";
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_ERROR_CODES_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_error_codes_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes_test.cc
new file mode 100644
index 00000000000..f9928bd2b39
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_error_codes_test.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicErrorCodesTest : public QuicTest {};
+
+TEST_F(QuicErrorCodesTest, QuicRstStreamErrorCodeToString) {
+ EXPECT_STREQ("QUIC_BAD_APPLICATION_PAYLOAD",
+ QuicRstStreamErrorCodeToString(QUIC_BAD_APPLICATION_PAYLOAD));
+}
+
+TEST_F(QuicErrorCodesTest, QuicErrorCodeToString) {
+ EXPECT_STREQ("QUIC_NO_ERROR", QuicErrorCodeToString(QUIC_NO_ERROR));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.cc b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.cc
new file mode 100644
index 00000000000..3fb5d754ee7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.cc
@@ -0,0 +1,308 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+#define ENDPOINT \
+ (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ")
+
+QuicFlowController::QuicFlowController(
+ QuicSession* session,
+ QuicStreamId id,
+ bool is_connection_flow_controller,
+ QuicStreamOffset send_window_offset,
+ QuicStreamOffset receive_window_offset,
+ QuicByteCount receive_window_size_limit,
+ bool should_auto_tune_receive_window,
+ QuicFlowControllerInterface* session_flow_controller)
+ : session_(session),
+ connection_(session->connection()),
+ id_(id),
+ is_connection_flow_controller_(is_connection_flow_controller),
+ perspective_(session->perspective()),
+ bytes_sent_(0),
+ send_window_offset_(send_window_offset),
+ bytes_consumed_(0),
+ highest_received_byte_offset_(0),
+ receive_window_offset_(receive_window_offset),
+ receive_window_size_(receive_window_offset),
+ receive_window_size_limit_(receive_window_size_limit),
+ auto_tune_receive_window_(should_auto_tune_receive_window),
+ session_flow_controller_(session_flow_controller),
+ last_blocked_send_window_offset_(0),
+ prev_window_update_time_(QuicTime::Zero()) {
+ DCHECK_LE(receive_window_size_, receive_window_size_limit_);
+ DCHECK_EQ(is_connection_flow_controller_,
+ QuicUtils::GetInvalidStreamId(
+ session_->connection()->transport_version()) == id_);
+
+ QUIC_DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_
+ << ", setting initial receive window offset to: "
+ << receive_window_offset_
+ << ", max receive window to: " << receive_window_size_
+ << ", max receive window limit to: "
+ << receive_window_size_limit_
+ << ", setting send window offset to: " << send_window_offset_;
+}
+
+void QuicFlowController::AddBytesConsumed(QuicByteCount bytes_consumed) {
+ bytes_consumed_ += bytes_consumed;
+ QUIC_DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed "
+ << bytes_consumed_ << " bytes.";
+
+ MaybeSendWindowUpdate();
+}
+
+bool QuicFlowController::UpdateHighestReceivedOffset(
+ QuicStreamOffset new_offset) {
+ // Only update if offset has increased.
+ if (new_offset <= highest_received_byte_offset_) {
+ return false;
+ }
+
+ QUIC_DVLOG(1) << ENDPOINT << "Stream " << id_
+ << " highest byte offset increased from "
+ << highest_received_byte_offset_ << " to " << new_offset;
+ highest_received_byte_offset_ = new_offset;
+ return true;
+}
+
+void QuicFlowController::AddBytesSent(QuicByteCount bytes_sent) {
+ if (bytes_sent_ + bytes_sent > send_window_offset_) {
+ QUIC_BUG << ENDPOINT << "Stream " << id_ << " Trying to send an extra "
+ << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_
+ << ", and send_window_offset_ = " << send_window_offset_;
+ bytes_sent_ = send_window_offset_;
+
+ // This is an error on our side, close the connection as soon as possible.
+ connection_->CloseConnection(
+ QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA,
+ QuicStrCat(send_window_offset_ - (bytes_sent_ + bytes_sent),
+ "bytes over send window offset"),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ bytes_sent_ += bytes_sent;
+ QUIC_DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent " << bytes_sent_
+ << " bytes.";
+}
+
+bool QuicFlowController::FlowControlViolation() {
+ if (highest_received_byte_offset_ > receive_window_offset_) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Flow control violation on stream " << id_
+ << ", receive window offset: " << receive_window_offset_
+ << ", highest received byte offset: "
+ << highest_received_byte_offset_;
+ return true;
+ }
+ return false;
+}
+
+void QuicFlowController::MaybeIncreaseMaxWindowSize() {
+ // Core of receive window auto tuning. This method should be called before a
+ // WINDOW_UPDATE frame is sent. Ideally, window updates should occur close to
+ // once per RTT. If a window update happens much faster than RTT, it implies
+ // that the flow control window is imposing a bottleneck. To prevent this,
+ // this method will increase the receive window size (subject to a reasonable
+ // upper bound). For simplicity this algorithm is deliberately asymmetric, in
+ // that it may increase window size but never decreases.
+
+ // Keep track of timing between successive window updates.
+ QuicTime now = connection_->clock()->ApproximateNow();
+ QuicTime prev = prev_window_update_time_;
+ prev_window_update_time_ = now;
+ if (!prev.IsInitialized()) {
+ QUIC_DVLOG(1) << ENDPOINT << "first window update for stream " << id_;
+ return;
+ }
+
+ if (!auto_tune_receive_window_) {
+ return;
+ }
+
+ // Get outbound RTT.
+ QuicTime::Delta rtt =
+ connection_->sent_packet_manager().GetRttStats()->smoothed_rtt();
+ if (rtt.IsZero()) {
+ QUIC_DVLOG(1) << ENDPOINT << "rtt zero for stream " << id_;
+ return;
+ }
+
+ // Now we can compare timing of window updates with RTT.
+ QuicTime::Delta since_last = now - prev;
+ QuicTime::Delta two_rtt = 2 * rtt;
+
+ if (since_last >= two_rtt) {
+ // If interval between window updates is sufficiently large, there
+ // is no need to increase receive_window_size_.
+ return;
+ }
+ QuicByteCount old_window = receive_window_size_;
+ IncreaseWindowSize();
+
+ if (receive_window_size_ > old_window) {
+ QUIC_DVLOG(1) << ENDPOINT << "New max window increase for stream " << id_
+ << " after " << since_last.ToMicroseconds()
+ << " us, and RTT is " << rtt.ToMicroseconds()
+ << "us. max wndw: " << receive_window_size_;
+ if (session_flow_controller_ != nullptr) {
+ session_flow_controller_->EnsureWindowAtLeast(
+ kSessionFlowControlMultiplier * receive_window_size_);
+ }
+ } else {
+ // TODO(ckrasic) - add a varz to track this (?).
+ QUIC_LOG_FIRST_N(INFO, 1)
+ << ENDPOINT << "Max window at limit for stream " << id_ << " after "
+ << since_last.ToMicroseconds() << " us, and RTT is "
+ << rtt.ToMicroseconds() << "us. Limit size: " << receive_window_size_;
+ }
+}
+
+void QuicFlowController::IncreaseWindowSize() {
+ receive_window_size_ *= 2;
+ receive_window_size_ =
+ std::min(receive_window_size_, receive_window_size_limit_);
+}
+
+QuicByteCount QuicFlowController::WindowUpdateThreshold() {
+ return receive_window_size_ / 2;
+}
+
+void QuicFlowController::MaybeSendWindowUpdate() {
+ // Send WindowUpdate to increase receive window if
+ // (receive window offset - consumed bytes) < (max window / 2).
+ // This is behaviour copied from SPDY.
+ DCHECK_LE(bytes_consumed_, receive_window_offset_);
+ QuicStreamOffset available_window = receive_window_offset_ - bytes_consumed_;
+ QuicByteCount threshold = WindowUpdateThreshold();
+
+ if (!prev_window_update_time_.IsInitialized()) {
+ // Treat the initial window as if it is a window update, so if 1/2 the
+ // window is used in less than 2 RTTs, the window is increased.
+ prev_window_update_time_ = connection_->clock()->ApproximateNow();
+ }
+
+ if (available_window >= threshold) {
+ QUIC_DVLOG(1) << ENDPOINT << "Not sending WindowUpdate for stream " << id_
+ << ", available window: " << available_window
+ << " >= threshold: " << threshold;
+ return;
+ }
+
+ MaybeIncreaseMaxWindowSize();
+ UpdateReceiveWindowOffsetAndSendWindowUpdate(available_window);
+}
+
+void QuicFlowController::UpdateReceiveWindowOffsetAndSendWindowUpdate(
+ QuicStreamOffset available_window) {
+ // Update our receive window.
+ receive_window_offset_ += (receive_window_size_ - available_window);
+
+ QUIC_DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_
+ << ", consumed bytes: " << bytes_consumed_
+ << ", available window: " << available_window
+ << ", and threshold: " << WindowUpdateThreshold()
+ << ", and receive window size: " << receive_window_size_
+ << ". New receive window offset is: " << receive_window_offset_;
+
+ SendWindowUpdate();
+}
+
+bool QuicFlowController::ShouldSendBlocked() {
+ if (SendWindowSize() != 0 ||
+ last_blocked_send_window_offset_ >= send_window_offset_) {
+ return false;
+ }
+ QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id_
+ << " is flow control blocked. "
+ << "Send window: " << SendWindowSize()
+ << ", bytes sent: " << bytes_sent_
+ << ", send limit: " << send_window_offset_;
+ // The entire send_window has been consumed, we are now flow control
+ // blocked.
+
+ // Keep track of when we last sent a BLOCKED frame so that we only send one
+ // at a given send offset.
+ last_blocked_send_window_offset_ = send_window_offset_;
+ return true;
+}
+
+bool QuicFlowController::UpdateSendWindowOffset(
+ QuicStreamOffset new_send_window_offset) {
+ // Only update if send window has increased.
+ if (new_send_window_offset <= send_window_offset_) {
+ return false;
+ }
+
+ QUIC_DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_
+ << " with new offset " << new_send_window_offset
+ << " current offset: " << send_window_offset_
+ << " bytes_sent: " << bytes_sent_;
+
+ // The flow is now unblocked but could have also been unblocked
+ // before. Return true iff this update caused a change from blocked
+ // to unblocked.
+ const bool was_previously_blocked = IsBlocked();
+ send_window_offset_ = new_send_window_offset;
+ return was_previously_blocked;
+}
+
+void QuicFlowController::EnsureWindowAtLeast(QuicByteCount window_size) {
+ if (receive_window_size_limit_ >= window_size) {
+ return;
+ }
+
+ QuicStreamOffset available_window = receive_window_offset_ - bytes_consumed_;
+ IncreaseWindowSize();
+ UpdateReceiveWindowOffsetAndSendWindowUpdate(available_window);
+}
+
+bool QuicFlowController::IsBlocked() const {
+ return SendWindowSize() == 0;
+}
+
+uint64_t QuicFlowController::SendWindowSize() const {
+ if (bytes_sent_ > send_window_offset_) {
+ return 0;
+ }
+ return send_window_offset_ - bytes_sent_;
+}
+
+void QuicFlowController::UpdateReceiveWindowSize(QuicStreamOffset size) {
+ DCHECK_LE(size, receive_window_size_limit_);
+ QUIC_DVLOG(1) << ENDPOINT << "UpdateReceiveWindowSize for stream " << id_
+ << ": " << size;
+ if (receive_window_size_ != receive_window_offset_) {
+ QUIC_BUG << "receive_window_size_:" << receive_window_size_
+ << " != receive_window_offset:" << receive_window_offset_;
+ return;
+ }
+ receive_window_size_ = size;
+ receive_window_offset_ = size;
+}
+
+void QuicFlowController::SendWindowUpdate() {
+ QuicStreamId id = id_;
+ if (is_connection_flow_controller_) {
+ id = QuicUtils::GetInvalidStreamId(connection_->transport_version());
+ }
+ session_->SendWindowUpdate(id, receive_window_offset_);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.h b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.h
new file mode 100644
index 00000000000..4de292cd5a6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller.h
@@ -0,0 +1,207 @@
+// Copyright 2014 The Chromium 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_FLOW_CONTROLLER_H_
+#define QUICHE_QUIC_CORE_QUIC_FLOW_CONTROLLER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class QuicFlowControllerPeer;
+} // namespace test
+
+class QuicConnection;
+class QuicSession;
+
+// How much larger the session flow control window needs to be relative to any
+// stream's flow control window.
+const float kSessionFlowControlMultiplier = 1.5;
+
+class QUIC_EXPORT_PRIVATE QuicFlowControllerInterface {
+ public:
+ virtual ~QuicFlowControllerInterface() {}
+
+ // Ensures the flow control window is at least |window_size| and send out an
+ // update frame if it is increased.
+ virtual void EnsureWindowAtLeast(QuicByteCount window_size) = 0;
+};
+
+// QuicFlowController allows a QUIC stream or connection to perform flow
+// control. The stream/connection owns a QuicFlowController which keeps track of
+// bytes sent/received, can tell the owner if it is flow control blocked, and
+// can send WINDOW_UPDATE or BLOCKED frames when needed.
+class QUIC_EXPORT_PRIVATE QuicFlowController
+ : public QuicFlowControllerInterface {
+ public:
+ QuicFlowController(QuicSession* session,
+ QuicStreamId id,
+ bool is_connection_flow_controller,
+ QuicStreamOffset send_window_offset,
+ QuicStreamOffset receive_window_offset,
+ QuicByteCount receive_window_size_limit,
+ bool should_auto_tune_receive_window,
+ QuicFlowControllerInterface* session_flow_controller);
+
+ QuicFlowController(const QuicFlowController&) = delete;
+ QuicFlowController(QuicFlowController&&) = default;
+ QuicFlowController& operator=(const QuicFlowController&) = delete;
+
+ ~QuicFlowController() override {}
+
+ // Called when we see a new highest received byte offset from the peer, either
+ // via a data frame or a RST.
+ // Returns true if this call changes highest_received_byte_offset_, and false
+ // in the case where |new_offset| is <= highest_received_byte_offset_.
+ bool UpdateHighestReceivedOffset(QuicStreamOffset new_offset);
+
+ // Called when bytes received from the peer are consumed locally. This may
+ // trigger the sending of a WINDOW_UPDATE frame using |connection|.
+ void AddBytesConsumed(QuicByteCount bytes_consumed);
+
+ // Called when bytes are sent to the peer.
+ void AddBytesSent(QuicByteCount bytes_sent);
+
+ // Increases |send_window_offset_| if |new_send_window_offset| is
+ // greater than the current value. Returns true if this increase
+ // also causes us to change from a blocked state to unblocked. In
+ // all other cases, returns false.
+ bool UpdateSendWindowOffset(QuicStreamOffset new_send_window_offset);
+
+ // QuicFlowControllerInterface.
+ void EnsureWindowAtLeast(QuicByteCount window_size) override;
+
+ // Returns the current available send window.
+ QuicByteCount SendWindowSize() const;
+
+ // Returns whether a BLOCKED frame should be sent.
+ bool ShouldSendBlocked();
+
+ // Returns true if flow control send limits have been reached.
+ bool IsBlocked() const;
+
+ // Returns true if flow control receive limits have been violated by the peer.
+ bool FlowControlViolation();
+
+ // Inform the peer of new receive window.
+ void SendWindowUpdate();
+
+ QuicByteCount bytes_consumed() const { return bytes_consumed_; }
+
+ QuicStreamOffset highest_received_byte_offset() const {
+ return highest_received_byte_offset_;
+ }
+
+ void set_receive_window_size_limit(QuicByteCount receive_window_size_limit) {
+ DCHECK_GE(receive_window_size_limit, receive_window_size_limit_);
+ receive_window_size_limit_ = receive_window_size_limit;
+ }
+
+ // Should only be called before any data is received.
+ void UpdateReceiveWindowSize(QuicStreamOffset size);
+
+ bool auto_tune_receive_window() { return auto_tune_receive_window_; }
+
+ private:
+ friend class test::QuicFlowControllerPeer;
+
+ // Send a WINDOW_UPDATE frame if appropriate.
+ void MaybeSendWindowUpdate();
+
+ // Auto-tune the max receive window size.
+ void MaybeIncreaseMaxWindowSize();
+
+ // Updates the current offset and sends a window update frame.
+ void UpdateReceiveWindowOffsetAndSendWindowUpdate(
+ QuicStreamOffset available_window);
+
+ // Double the window size as long as we haven't hit the max window size.
+ void IncreaseWindowSize();
+
+ // The parent session/connection, used to send connection close on flow
+ // control violation, and WINDOW_UPDATE and BLOCKED frames when appropriate.
+ // Not owned.
+ QuicSession* session_;
+ QuicConnection* connection_;
+
+ // ID of stream this flow controller belongs to. If
+ // |is_connection_flow_controller_| is false, this must be a valid stream ID.
+ QuicStreamId id_;
+
+ // Whether this flow controller is the connection level flow controller
+ // instead of the flow controller for a stream. If true, |id_| is ignored.
+ bool is_connection_flow_controller_;
+
+ // Tracks if this is owned by a server or a client.
+ Perspective perspective_;
+
+ // Tracks number of bytes sent to the peer.
+ QuicByteCount bytes_sent_;
+
+ // The absolute offset in the outgoing byte stream. If this offset is reached
+ // then we become flow control blocked until we receive a WINDOW_UPDATE.
+ QuicStreamOffset send_window_offset_;
+
+ // Overview of receive flow controller.
+ //
+ // 0=...===1=======2-------3 ...... FIN
+ // |<--- <= 4 --->|
+ //
+
+ // 1) bytes_consumed_ - moves forward when data is read out of the
+ // stream.
+ //
+ // 2) highest_received_byte_offset_ - moves when data is received
+ // from the peer.
+ //
+ // 3) receive_window_offset_ - moves when WINDOW_UPDATE is sent.
+ //
+ // 4) receive_window_size_ - maximum allowed unread data (3 - 1).
+ // This value may be increased by auto-tuning.
+ //
+ // 5) receive_window_size_limit_ - limit on receive_window_size_;
+ // auto-tuning will not increase window size beyond this limit.
+
+ // Track number of bytes received from the peer, which have been consumed
+ // locally.
+ QuicByteCount bytes_consumed_;
+
+ // The highest byte offset we have seen from the peer. This could be the
+ // highest offset in a data frame, or a final value in a RST.
+ QuicStreamOffset highest_received_byte_offset_;
+
+ // The absolute offset in the incoming byte stream. The peer should never send
+ // us bytes which are beyond this offset.
+ QuicStreamOffset receive_window_offset_;
+
+ // Largest size the receive window can grow to.
+ QuicByteCount receive_window_size_;
+
+ // Upper limit on receive_window_size_;
+ QuicByteCount receive_window_size_limit_;
+
+ // Used to dynamically enable receive window auto-tuning.
+ bool auto_tune_receive_window_;
+
+ // The session's flow controller. null if this is stream id 0.
+ // Not owned.
+ QuicFlowControllerInterface* session_flow_controller_;
+
+ // Send window update when receive window size drops below this.
+ QuicByteCount WindowUpdateThreshold();
+
+ // Keep track of the last time we sent a BLOCKED frame. We should only send
+ // another when the number of bytes we have sent has changed.
+ QuicStreamOffset last_blocked_send_window_offset_;
+
+ // Keep time of the last time a window update was sent. We use this
+ // as part of the receive window auto tuning.
+ QuicTime prev_window_update_time_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_FLOW_CONTROLLER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller_test.cc
new file mode 100644
index 00000000000..771b2e2bd91
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_flow_controller_test.cc
@@ -0,0 +1,408 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+
+namespace quic {
+namespace test {
+
+// Receive window auto-tuning uses RTT in its logic.
+const int64_t kRtt = 100;
+
+class MockFlowController : public QuicFlowControllerInterface {
+ public:
+ MockFlowController() {}
+ MockFlowController(const MockFlowController&) = delete;
+ MockFlowController& operator=(const MockFlowController&) = delete;
+ ~MockFlowController() override {}
+
+ MOCK_METHOD1(EnsureWindowAtLeast, void(QuicByteCount));
+};
+
+class QuicFlowControllerTest : public QuicTest {
+ public:
+ void Initialize() {
+ connection_ = new MockQuicConnection(&helper_, &alarm_factory_,
+ Perspective::IS_CLIENT);
+ session_ = QuicMakeUnique<MockQuicSession>(connection_);
+ flow_controller_ = QuicMakeUnique<QuicFlowController>(
+ session_.get(), stream_id_, /*is_connection_flow_controller*/ false,
+ send_window_, receive_window_, kStreamReceiveWindowLimit,
+ should_auto_tune_receive_window_, &session_flow_controller_);
+ }
+
+ bool ClearControlFrame(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ protected:
+ QuicStreamId stream_id_ = 1234;
+ QuicByteCount send_window_ = kInitialSessionFlowControlWindowForTest;
+ QuicByteCount receive_window_ = kInitialSessionFlowControlWindowForTest;
+ std::unique_ptr<QuicFlowController> flow_controller_;
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnection* connection_;
+ std::unique_ptr<MockQuicSession> session_;
+ MockFlowController session_flow_controller_;
+ bool should_auto_tune_receive_window_ = false;
+};
+
+TEST_F(QuicFlowControllerTest, SendingBytes) {
+ Initialize();
+
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Send some bytes, but not enough to block.
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize());
+
+ // Send enough bytes to block.
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent.
+ EXPECT_TRUE(flow_controller_->ShouldSendBlocked());
+
+ // Update the send window, and verify this has unblocked.
+ EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_));
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Updating with a smaller offset doesn't change anything.
+ EXPECT_FALSE(flow_controller_->UpdateSendWindowOffset(send_window_ / 10));
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Try to send more bytes, violating flow control.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, _, _));
+ EXPECT_QUIC_BUG(
+ flow_controller_->AddBytesSent(send_window_ * 10),
+ QuicStrCat("Trying to send an extra ", send_window_ * 10, " bytes"));
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+}
+
+TEST_F(QuicFlowControllerTest, ReceivingBytes) {
+ Initialize();
+
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Receive some bytes, updating highest received offset, but not enough to
+ // fill flow control receive window.
+ EXPECT_TRUE(
+ flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ((receive_window_ / 2) - 1,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Consume enough bytes to send a WINDOW_UPDATE frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+
+ flow_controller_->AddBytesConsumed(1 + receive_window_ / 2);
+
+ // Result is that once again we have a fully open receive window.
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+}
+
+TEST_F(QuicFlowControllerTest, Move) {
+ Initialize();
+
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize());
+
+ EXPECT_TRUE(
+ flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ((receive_window_ / 2) - 1,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ QuicFlowController flow_controller2(std::move(*flow_controller_));
+ EXPECT_EQ(send_window_ / 2, flow_controller2.SendWindowSize());
+ EXPECT_FALSE(flow_controller2.FlowControlViolation());
+ EXPECT_EQ((receive_window_ / 2) - 1,
+ QuicFlowControllerPeer::ReceiveWindowSize(&flow_controller2));
+}
+
+TEST_F(QuicFlowControllerTest, OnlySendBlockedFrameOncePerOffset) {
+ Initialize();
+
+ // Test that we don't send duplicate BLOCKED frames. We should only send one
+ // BLOCKED frame at a given send window offset.
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Send enough bytes to block.
+ flow_controller_->AddBytesSent(send_window_);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent.
+ EXPECT_TRUE(flow_controller_->ShouldSendBlocked());
+
+ // BLOCKED frame should not get sent again until our send offset changes.
+ EXPECT_FALSE(flow_controller_->ShouldSendBlocked());
+ EXPECT_FALSE(flow_controller_->ShouldSendBlocked());
+ EXPECT_FALSE(flow_controller_->ShouldSendBlocked());
+ EXPECT_FALSE(flow_controller_->ShouldSendBlocked());
+ EXPECT_FALSE(flow_controller_->ShouldSendBlocked());
+
+ // Update the send window, then send enough bytes to block again.
+ EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_));
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ flow_controller_->AddBytesSent(send_window_);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent as send offset has changed.
+ EXPECT_TRUE(flow_controller_->ShouldSendBlocked());
+}
+
+TEST_F(QuicFlowControllerTest, ReceivingBytesFastIncreasesFlowWindow) {
+ should_auto_tune_receive_window_ = true;
+ Initialize();
+ // This test will generate two WINDOW_UPDATE frames.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ EXPECT_TRUE(flow_controller_->auto_tune_receive_window());
+
+ // Make sure clock is inititialized.
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+
+ QuicSentPacketManager* manager =
+ QuicConnectionPeer::GetSentPacketManager(connection_);
+
+ RttStats* rtt_stats = const_cast<RttStats*>(manager->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ QuicByteCount threshold =
+ QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
+
+ QuicStreamOffset receive_offset = threshold + 1;
+ // Receive some bytes, updating highest received offset, but not enough to
+ // fill flow control receive window.
+ EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+ EXPECT_CALL(
+ session_flow_controller_,
+ EnsureWindowAtLeast(kInitialSessionFlowControlWindowForTest * 2 * 1.5));
+
+ // Consume enough bytes to send a WINDOW_UPDATE frame.
+ flow_controller_->AddBytesConsumed(threshold + 1);
+ // Result is that once again we have a fully open receive window.
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(2 * kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt - 1));
+ receive_offset += threshold + 1;
+ EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset));
+ flow_controller_->AddBytesConsumed(threshold + 1);
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ QuicByteCount new_threshold =
+ QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
+ EXPECT_GT(new_threshold, threshold);
+}
+
+TEST_F(QuicFlowControllerTest, ReceivingBytesFastNoAutoTune) {
+ Initialize();
+ // This test will generate two WINDOW_UPDATE frames.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(this, &QuicFlowControllerTest::ClearControlFrame));
+ EXPECT_FALSE(flow_controller_->auto_tune_receive_window());
+
+ // Make sure clock is inititialized.
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+
+ QuicSentPacketManager* manager =
+ QuicConnectionPeer::GetSentPacketManager(connection_);
+
+ RttStats* rtt_stats = const_cast<RttStats*>(manager->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ QuicByteCount threshold =
+ QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
+
+ QuicStreamOffset receive_offset = threshold + 1;
+ // Receive some bytes, updating highest received offset, but not enough to
+ // fill flow control receive window.
+ EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Consume enough bytes to send a WINDOW_UPDATE frame.
+ flow_controller_->AddBytesConsumed(threshold + 1);
+ // Result is that once again we have a fully open receive window.
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Move time forward, but by less than two RTTs. Then receive and consume
+ // some more, forcing a second WINDOW_UPDATE with an increased max window
+ // size.
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt - 1));
+ receive_offset += threshold + 1;
+ EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset));
+ flow_controller_->AddBytesConsumed(threshold + 1);
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ QuicByteCount new_threshold =
+ QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
+ EXPECT_EQ(new_threshold, threshold);
+}
+
+TEST_F(QuicFlowControllerTest, ReceivingBytesNormalStableFlowWindow) {
+ should_auto_tune_receive_window_ = true;
+ Initialize();
+ // This test will generate two WINDOW_UPDATE frames.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ EXPECT_TRUE(flow_controller_->auto_tune_receive_window());
+
+ // Make sure clock is inititialized.
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+
+ QuicSentPacketManager* manager =
+ QuicConnectionPeer::GetSentPacketManager(connection_);
+ RttStats* rtt_stats = const_cast<RttStats*>(manager->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ QuicByteCount threshold =
+ QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
+
+ QuicStreamOffset receive_offset = threshold + 1;
+ // Receive some bytes, updating highest received offset, but not enough to
+ // fill flow control receive window.
+ EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+ EXPECT_CALL(
+ session_flow_controller_,
+ EnsureWindowAtLeast(kInitialSessionFlowControlWindowForTest * 2 * 1.5));
+ flow_controller_->AddBytesConsumed(threshold + 1);
+
+ // Result is that once again we have a fully open receive window.
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(2 * kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Move time forward, but by more than two RTTs. Then receive and consume
+ // some more, forcing a second WINDOW_UPDATE with unchanged max window size.
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt + 1));
+
+ receive_offset += threshold + 1;
+ EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset));
+
+ flow_controller_->AddBytesConsumed(threshold + 1);
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+
+ QuicByteCount new_threshold =
+ QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
+ EXPECT_EQ(new_threshold, 2 * threshold);
+}
+
+TEST_F(QuicFlowControllerTest, ReceivingBytesNormalNoAutoTune) {
+ Initialize();
+ // This test will generate two WINDOW_UPDATE frames.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(this, &QuicFlowControllerTest::ClearControlFrame));
+ EXPECT_FALSE(flow_controller_->auto_tune_receive_window());
+
+ // Make sure clock is inititialized.
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+
+ QuicSentPacketManager* manager =
+ QuicConnectionPeer::GetSentPacketManager(connection_);
+ RttStats* rtt_stats = const_cast<RttStats*>(manager->GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRtt),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ QuicByteCount threshold =
+ QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
+
+ QuicStreamOffset receive_offset = threshold + 1;
+ // Receive some bytes, updating highest received offset, but not enough to
+ // fill flow control receive window.
+ EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest - receive_offset,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ flow_controller_->AddBytesConsumed(threshold + 1);
+
+ // Result is that once again we have a fully open receive window.
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Move time forward, but by more than two RTTs. Then receive and consume
+ // some more, forcing a second WINDOW_UPDATE with unchanged max window size.
+ connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(2 * kRtt + 1));
+
+ receive_offset += threshold + 1;
+ EXPECT_TRUE(flow_controller_->UpdateHighestReceivedOffset(receive_offset));
+
+ flow_controller_->AddBytesConsumed(threshold + 1);
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+
+ QuicByteCount new_threshold =
+ QuicFlowControllerPeer::WindowUpdateThreshold(flow_controller_.get());
+
+ EXPECT_EQ(new_threshold, threshold);
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..0c65dc226e4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer.cc
@@ -0,0 +1,5767 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_client_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+namespace {
+
+#define ENDPOINT \
+ (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ")
+
+// How much to shift the timestamp in the IETF Ack frame.
+// TODO(fkastenholz) when we get real IETF QUIC, need to get
+// the currect shift from the transport parameters.
+const int kIetfAckTimestampShift = 3;
+
+// Number of bits the packet number length bits are shifted from the right
+// edge of the header.
+const uint8_t kPublicHeaderSequenceNumberShift = 4;
+
+// There are two interpretations for the Frame Type byte in the QUIC protocol,
+// resulting in two Frame Types: Special Frame Types and Regular Frame Types.
+//
+// Regular Frame Types use the Frame Type byte simply. Currently defined
+// Regular Frame Types are:
+// Padding : 0b 00000000 (0x00)
+// ResetStream : 0b 00000001 (0x01)
+// ConnectionClose : 0b 00000010 (0x02)
+// GoAway : 0b 00000011 (0x03)
+// WindowUpdate : 0b 00000100 (0x04)
+// Blocked : 0b 00000101 (0x05)
+//
+// Special Frame Types encode both a Frame Type and corresponding flags
+// all in the Frame Type byte. Currently defined Special Frame Types
+// are:
+// Stream : 0b 1xxxxxxx
+// Ack : 0b 01xxxxxx
+//
+// Semantics of the flag bits above (the x bits) depends on the frame type.
+
+// Masks to determine if the frame type is a special use
+// and for specific special frame types.
+const uint8_t kQuicFrameTypeBrokenMask = 0xE0; // 0b 11100000
+const uint8_t kQuicFrameTypeSpecialMask = 0xC0; // 0b 11000000
+const uint8_t kQuicFrameTypeStreamMask = 0x80;
+const uint8_t kQuicFrameTypeAckMask = 0x40;
+static_assert(kQuicFrameTypeSpecialMask ==
+ (kQuicFrameTypeStreamMask | kQuicFrameTypeAckMask),
+ "Invalid kQuicFrameTypeSpecialMask");
+
+// The stream type format is 1FDOOOSS, where
+// F is the fin bit.
+// D is the data length bit (0 or 2 bytes).
+// OO/OOO are the size of the offset.
+// SS is the size of the stream ID.
+// Note that the stream encoding can not be determined by inspection. It can
+// be determined only by knowing the QUIC Version.
+// Stream frame relative shifts and masks for interpreting the stream flags.
+// StreamID may be 1, 2, 3, or 4 bytes.
+const uint8_t kQuicStreamIdShift = 2;
+const uint8_t kQuicStreamIDLengthMask = 0x03;
+
+// Offset may be 0, 2, 4, or 8 bytes.
+const uint8_t kQuicStreamShift = 3;
+const uint8_t kQuicStreamOffsetMask = 0x07;
+
+// Data length may be 0 or 2 bytes.
+const uint8_t kQuicStreamDataLengthShift = 1;
+const uint8_t kQuicStreamDataLengthMask = 0x01;
+
+// Fin bit may be set or not.
+const uint8_t kQuicStreamFinShift = 1;
+const uint8_t kQuicStreamFinMask = 0x01;
+
+// The format is 01M0LLOO, where
+// M if set, there are multiple ack blocks in the frame.
+// LL is the size of the largest ack field.
+// OO is the size of the ack blocks offset field.
+// packet number size shift used in AckFrames.
+const uint8_t kQuicSequenceNumberLengthNumBits = 2;
+const uint8_t kActBlockLengthOffset = 0;
+const uint8_t kLargestAckedOffset = 2;
+
+// Acks may have only one ack block.
+const uint8_t kQuicHasMultipleAckBlocksOffset = 5;
+
+// Timestamps are 4 bytes followed by 2 bytes.
+const uint8_t kQuicNumTimestampsLength = 1;
+const uint8_t kQuicFirstTimestampLength = 4;
+const uint8_t kQuicTimestampLength = 2;
+// Gaps between packet numbers are 1 byte.
+const uint8_t kQuicTimestampPacketNumberGapLength = 1;
+
+// Maximum length of encoded error strings.
+const int kMaxErrorStringLength = 256;
+
+const uint8_t kConnectionIdLengthAdjustment = 3;
+const uint8_t kDestinationConnectionIdLengthMask = 0xF0;
+const uint8_t kSourceConnectionIdLengthMask = 0x0F;
+
+// Returns the absolute value of the difference between |a| and |b|.
+uint64_t Delta(uint64_t a, uint64_t b) {
+ // Since these are unsigned numbers, we can't just return abs(a - b)
+ if (a < b) {
+ return b - a;
+ }
+ return a - b;
+}
+
+uint64_t ClosestTo(uint64_t target, uint64_t a, uint64_t b) {
+ return (Delta(target, a) < Delta(target, b)) ? a : b;
+}
+
+uint64_t PacketNumberIntervalLength(
+ const QuicInterval<QuicPacketNumber>& interval) {
+ if (interval.Empty()) {
+ return 0u;
+ }
+ return interval.max() - interval.min();
+}
+
+QuicPacketNumberLength ReadSequenceNumberLength(uint8_t flags) {
+ switch (flags & PACKET_FLAGS_8BYTE_PACKET) {
+ case PACKET_FLAGS_8BYTE_PACKET:
+ return PACKET_6BYTE_PACKET_NUMBER;
+ case PACKET_FLAGS_4BYTE_PACKET:
+ return PACKET_4BYTE_PACKET_NUMBER;
+ case PACKET_FLAGS_2BYTE_PACKET:
+ return PACKET_2BYTE_PACKET_NUMBER;
+ case PACKET_FLAGS_1BYTE_PACKET:
+ return PACKET_1BYTE_PACKET_NUMBER;
+ default:
+ QUIC_BUG << "Unreachable case statement.";
+ return PACKET_6BYTE_PACKET_NUMBER;
+ }
+}
+
+QuicPacketNumberLength ReadAckPacketNumberLength(QuicTransportVersion version,
+ uint8_t flags) {
+ switch (flags & PACKET_FLAGS_8BYTE_PACKET) {
+ case PACKET_FLAGS_8BYTE_PACKET:
+ return PACKET_6BYTE_PACKET_NUMBER;
+ case PACKET_FLAGS_4BYTE_PACKET:
+ return PACKET_4BYTE_PACKET_NUMBER;
+ case PACKET_FLAGS_2BYTE_PACKET:
+ return PACKET_2BYTE_PACKET_NUMBER;
+ case PACKET_FLAGS_1BYTE_PACKET:
+ return PACKET_1BYTE_PACKET_NUMBER;
+ default:
+ QUIC_BUG << "Unreachable case statement.";
+ return PACKET_6BYTE_PACKET_NUMBER;
+ }
+}
+
+uint8_t PacketNumberLengthToOnWireValue(
+ QuicTransportVersion version,
+ QuicPacketNumberLength packet_number_length) {
+ if (version > QUIC_VERSION_44) {
+ return packet_number_length - 1;
+ }
+ switch (packet_number_length) {
+ case PACKET_1BYTE_PACKET_NUMBER:
+ return 0;
+ case PACKET_2BYTE_PACKET_NUMBER:
+ return 1;
+ case PACKET_4BYTE_PACKET_NUMBER:
+ return 2;
+ default:
+ QUIC_BUG << "Invalid packet number length.";
+ return 0;
+ }
+}
+
+bool GetShortHeaderPacketNumberLength(
+ QuicTransportVersion version,
+ uint8_t type,
+ bool infer_packet_header_type_from_version,
+ QuicPacketNumberLength* packet_number_length) {
+ DCHECK(!(type & FLAGS_LONG_HEADER));
+ const bool two_bits_packet_number_length =
+ infer_packet_header_type_from_version ? version > QUIC_VERSION_44
+ : (type & FLAGS_FIXED_BIT);
+ if (two_bits_packet_number_length) {
+ *packet_number_length =
+ static_cast<QuicPacketNumberLength>((type & 0x03) + 1);
+ return true;
+ }
+ switch (type & 0x07) {
+ case 0:
+ *packet_number_length = PACKET_1BYTE_PACKET_NUMBER;
+ break;
+ case 1:
+ *packet_number_length = PACKET_2BYTE_PACKET_NUMBER;
+ break;
+ case 2:
+ *packet_number_length = PACKET_4BYTE_PACKET_NUMBER;
+ break;
+ default:
+ *packet_number_length = PACKET_6BYTE_PACKET_NUMBER;
+ return false;
+ }
+ return true;
+}
+
+uint8_t LongHeaderTypeToOnWireValue(QuicTransportVersion version,
+ QuicLongHeaderType type) {
+ switch (type) {
+ case INITIAL:
+ return version > QUIC_VERSION_44 ? 0 : 0x7F;
+ case ZERO_RTT_PROTECTED:
+ return version > QUIC_VERSION_44 ? 1 << 4 : 0x7C;
+ case HANDSHAKE:
+ return version > QUIC_VERSION_44 ? 2 << 4 : 0x7D;
+ case RETRY:
+ return version > QUIC_VERSION_44 ? 3 << 4 : 0x7E;
+ case VERSION_NEGOTIATION:
+ return 0xF0; // Value does not matter
+ default:
+ QUIC_BUG << "Invalid long header type: " << type;
+ return 0xFF;
+ }
+}
+
+bool GetLongHeaderType(QuicTransportVersion version,
+ uint8_t type,
+ QuicLongHeaderType* long_header_type) {
+ DCHECK((type & FLAGS_LONG_HEADER) && version != QUIC_VERSION_UNSUPPORTED);
+ if (version > QUIC_VERSION_44) {
+ switch ((type & 0x30) >> 4) {
+ case 0:
+ *long_header_type = INITIAL;
+ break;
+ case 1:
+ *long_header_type = ZERO_RTT_PROTECTED;
+ break;
+ case 2:
+ *long_header_type = HANDSHAKE;
+ break;
+ case 3:
+ *long_header_type = RETRY;
+ break;
+ default:
+ QUIC_BUG << "Unreachable statement";
+ *long_header_type = VERSION_NEGOTIATION;
+ return false;
+ }
+ return true;
+ }
+
+ switch (type & 0x7F) {
+ case 0x7F:
+ *long_header_type = INITIAL;
+ break;
+ case 0x7C:
+ *long_header_type = ZERO_RTT_PROTECTED;
+ break;
+ case 0x7D:
+ *long_header_type = HANDSHAKE;
+ break;
+ case 0x7E:
+ *long_header_type = RETRY;
+ break;
+ default:
+ // Invalid packet header type. Whether a packet is version negotiation is
+ // determined by the version field.
+ *long_header_type = INVALID_PACKET_TYPE;
+ return false;
+ }
+ return true;
+}
+
+QuicPacketNumberLength GetLongHeaderPacketNumberLength(
+ QuicTransportVersion version,
+ uint8_t type) {
+ if (version > QUIC_VERSION_44) {
+ return static_cast<QuicPacketNumberLength>((type & 0x03) + 1);
+ }
+ return PACKET_4BYTE_PACKET_NUMBER;
+}
+
+// Used to get packet number space before packet gets decrypted.
+PacketNumberSpace GetPacketNumberSpace(const QuicPacketHeader& header) {
+ switch (header.form) {
+ case GOOGLE_QUIC_PACKET:
+ QUIC_BUG << "Try to get packet number space of Google QUIC packet";
+ break;
+ case IETF_QUIC_SHORT_HEADER_PACKET:
+ return APPLICATION_DATA;
+ case IETF_QUIC_LONG_HEADER_PACKET:
+ switch (header.long_packet_type) {
+ case INITIAL:
+ return INITIAL_DATA;
+ case HANDSHAKE:
+ return HANDSHAKE_DATA;
+ case ZERO_RTT_PROTECTED:
+ return APPLICATION_DATA;
+ case VERSION_NEGOTIATION:
+ case RETRY:
+ case INVALID_PACKET_TYPE:
+ QUIC_BUG << "Try to get packet number space of long header type: "
+ << QuicUtils::QuicLongHeaderTypetoString(
+ header.long_packet_type);
+ break;
+ }
+ }
+
+ return NUM_PACKET_NUMBER_SPACES;
+}
+
+EncryptionLevel GetEncryptionLevel(const QuicPacketHeader& header) {
+ switch (header.form) {
+ case GOOGLE_QUIC_PACKET:
+ QUIC_BUG << "Cannot determine EncryptionLevel from Google QUIC header";
+ break;
+ case IETF_QUIC_SHORT_HEADER_PACKET:
+ return ENCRYPTION_FORWARD_SECURE;
+ case IETF_QUIC_LONG_HEADER_PACKET:
+ switch (header.long_packet_type) {
+ case INITIAL:
+ return ENCRYPTION_INITIAL;
+ case HANDSHAKE:
+ return ENCRYPTION_HANDSHAKE;
+ case ZERO_RTT_PROTECTED:
+ return ENCRYPTION_ZERO_RTT;
+ case VERSION_NEGOTIATION:
+ case RETRY:
+ case INVALID_PACKET_TYPE:
+ QUIC_BUG << "No encryption used with type "
+ << QuicUtils::QuicLongHeaderTypetoString(
+ header.long_packet_type);
+ }
+ }
+ return NUM_ENCRYPTION_LEVELS;
+}
+
+QuicStringPiece TruncateErrorString(QuicStringPiece error) {
+ if (error.length() <= kMaxErrorStringLength) {
+ return error;
+ }
+ return QuicStringPiece(error.data(), kMaxErrorStringLength);
+}
+
+size_t TruncatedErrorStringSize(const QuicStringPiece& error) {
+ if (error.length() < kMaxErrorStringLength) {
+ return error.length();
+ }
+ return kMaxErrorStringLength;
+}
+
+uint8_t GetConnectionIdLengthValue(QuicConnectionIdLength length) {
+ if (length == 0) {
+ return 0;
+ }
+ return static_cast<uint8_t>(length - kConnectionIdLengthAdjustment);
+}
+
+bool IsValidPacketNumberLength(QuicPacketNumberLength packet_number_length) {
+ size_t length = packet_number_length;
+ return length == 1 || length == 2 || length == 4 || length == 6 ||
+ length == 8;
+}
+
+bool IsValidFullPacketNumber(uint64_t full_packet_number,
+ QuicTransportVersion version) {
+ return full_packet_number > 0 || version == QUIC_VERSION_99;
+}
+
+// Convert a stream ID to a count of streams, for IETF QUIC/Version 99 only.
+// There is no need to take into account whether the ID is for uni- or
+// bi-directional streams, or whether it's server- or client- initiated. It
+// always returns a valid count.
+QuicStreamId StreamIdToCount(QuicTransportVersion version,
+ QuicStreamId stream_id) {
+ DCHECK_EQ(QUIC_VERSION_99, version);
+ if ((stream_id & 0x3) == 0) {
+ return (stream_id / QuicUtils::StreamIdDelta(version));
+ }
+ return (stream_id / QuicUtils::StreamIdDelta(version)) + 1;
+}
+
+// Returns the maximum value that a stream count may have, taking into account
+// the fact that bidirectional, client initiated, streams have one fewer stream
+// available than the others. This is because the old crypto streams, with ID ==
+// 0 are not included in the count.
+// The version is not included in the call, nor does the method take the version
+// into account, because this is called only from code used for IETF QUIC.
+// TODO(fkastenholz): Remove this method and replace calls to it with direct
+// references to kMaxQuicStreamIdCount when streamid 0 becomes a normal stream
+// id.
+QuicStreamId GetMaxStreamCount(bool unidirectional, Perspective perspective) {
+ if (!unidirectional && perspective == Perspective::IS_CLIENT) {
+ return kMaxQuicStreamId >> 2;
+ }
+ return (kMaxQuicStreamId >> 2) + 1;
+}
+
+// Convert a stream count to the maximum stream ID for that count.
+// Needs to know whether the resulting stream ID should be uni-directional,
+// bi-directional, server-initiated, or client-initiated.
+// Returns true if it works, false if not. The only error condition is that
+// the stream_count is too big and it would generate a stream id that is larger
+// than the implementation's maximum stream id value.
+bool StreamCountToId(QuicStreamId stream_count,
+ bool unidirectional,
+ Perspective perspective,
+ QuicTransportVersion version,
+ QuicStreamId* generated_stream_id) {
+ DCHECK_EQ(QUIC_VERSION_99, version);
+ // TODO(fkastenholz): when the MAX_STREAMS and STREAMS_BLOCKED frames
+ // are connected all the way up to the stream_id_manager, handle count==0
+ // properly (interpret it as "can open 0 streams") and the count being too
+ // large (close the connection).
+ if ((stream_count == 0) ||
+ (stream_count > GetMaxStreamCount(unidirectional, perspective))) {
+ return false;
+ }
+ *generated_stream_id =
+ ((unidirectional)
+ ? QuicUtils::GetFirstUnidirectionalStreamId(version, perspective)
+ : QuicUtils::GetFirstBidirectionalStreamId(version, perspective)) +
+ ((stream_count - 1) * QuicUtils::StreamIdDelta(version));
+ return true;
+}
+
+bool AppendIetfConnectionIdsNew(bool version_flag,
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ QuicDataWriter* writer) {
+ if (!version_flag) {
+ return writer->WriteConnectionId(destination_connection_id);
+ }
+
+ // Compute connection ID length byte.
+ uint8_t dcil = GetConnectionIdLengthValue(
+ static_cast<QuicConnectionIdLength>(destination_connection_id.length()));
+ uint8_t scil = GetConnectionIdLengthValue(
+ static_cast<QuicConnectionIdLength>(source_connection_id.length()));
+ uint8_t connection_id_length = dcil << 4 | scil;
+
+ return writer->WriteUInt8(connection_id_length) &&
+ writer->WriteConnectionId(destination_connection_id) &&
+ writer->WriteConnectionId(source_connection_id);
+}
+
+enum class DroppedPacketReason {
+ // General errors
+ INVALID_PUBLIC_HEADER,
+ VERSION_MISMATCH,
+ // Version negotiation packet errors
+ INVALID_VERSION_NEGOTIATION_PACKET,
+ // Public reset packet errors, pre-v44
+ INVALID_PUBLIC_RESET_PACKET,
+ // Data packet errors
+ INVALID_PACKET_NUMBER,
+ INVALID_DIVERSIFICATION_NONCE,
+ DECRYPTION_FAILURE,
+ NUM_REASONS,
+};
+
+void RecordDroppedPacketReason(DroppedPacketReason reason) {
+ QUIC_CLIENT_HISTOGRAM_ENUM("QuicDroppedPacketReason", reason,
+ DroppedPacketReason::NUM_REASONS,
+ "The reason a packet was not processed. Recorded "
+ "each time such a packet is dropped");
+}
+
+} // namespace
+
+QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions,
+ QuicTime creation_time,
+ Perspective perspective,
+ uint8_t expected_connection_id_length)
+ : visitor_(nullptr),
+ error_(QUIC_NO_ERROR),
+ last_serialized_connection_id_(EmptyQuicConnectionId()),
+ last_version_label_(0),
+ version_(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED),
+ supported_versions_(supported_versions),
+ decrypter_level_(ENCRYPTION_INITIAL),
+ alternative_decrypter_level_(NUM_ENCRYPTION_LEVELS),
+ alternative_decrypter_latch_(false),
+ perspective_(perspective),
+ validate_flags_(true),
+ process_timestamps_(false),
+ creation_time_(creation_time),
+ last_timestamp_(QuicTime::Delta::Zero()),
+ first_sending_packet_number_(FirstSendingPacketNumber()),
+ data_producer_(nullptr),
+ infer_packet_header_type_from_version_(perspective ==
+ Perspective::IS_CLIENT),
+ expected_connection_id_length_(expected_connection_id_length),
+ should_update_expected_connection_id_length_(false),
+ supports_multiple_packet_number_spaces_(false) {
+ DCHECK(!supported_versions.empty());
+ version_ = supported_versions_[0];
+ decrypter_[ENCRYPTION_INITIAL] = QuicMakeUnique<NullDecrypter>(perspective);
+ encrypter_[ENCRYPTION_INITIAL] = QuicMakeUnique<NullEncrypter>(perspective);
+}
+
+QuicFramer::~QuicFramer() {}
+
+// static
+size_t QuicFramer::GetMinStreamFrameSize(QuicTransportVersion version,
+ QuicStreamId stream_id,
+ QuicStreamOffset offset,
+ bool last_frame_in_packet,
+ QuicPacketLength data_length) {
+ if (version == QUIC_VERSION_99) {
+ return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(stream_id) +
+ (last_frame_in_packet
+ ? 0
+ : QuicDataWriter::GetVarInt62Len(data_length)) +
+ (offset != 0 ? QuicDataWriter::GetVarInt62Len(offset) : 0);
+ }
+ return kQuicFrameTypeSize + GetStreamIdSize(stream_id) +
+ GetStreamOffsetSize(version, offset) +
+ (last_frame_in_packet ? 0 : kQuicStreamPayloadLengthSize);
+}
+
+// static
+size_t QuicFramer::GetMinCryptoFrameSize(QuicStreamOffset offset,
+ QuicPacketLength data_length) {
+ return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(offset) +
+ QuicDataWriter::GetVarInt62Len(data_length);
+}
+
+// static
+size_t QuicFramer::GetMessageFrameSize(QuicTransportVersion version,
+ bool last_frame_in_packet,
+ QuicByteCount length) {
+ QUIC_BUG_IF(version <= QUIC_VERSION_44)
+ << "Try to serialize MESSAGE frame in " << version;
+ return kQuicFrameTypeSize +
+ (last_frame_in_packet ? 0 : QuicDataWriter::GetVarInt62Len(length)) +
+ length;
+}
+
+// static
+size_t QuicFramer::GetMinAckFrameSize(
+ QuicTransportVersion version,
+ QuicPacketNumberLength largest_observed_length) {
+ if (version == QUIC_VERSION_99) {
+ // The minimal ack frame consists of the following four fields: Largest
+ // Acknowledged, ACK Delay, ACK Block Count, and First ACK Block. Minimum
+ // size of each is 1 byte.
+ return kQuicFrameTypeSize + 4;
+ }
+ size_t min_size = kQuicFrameTypeSize + largest_observed_length +
+ kQuicDeltaTimeLargestObservedSize;
+ return min_size + kQuicNumTimestampsSize;
+}
+
+// static
+size_t QuicFramer::GetStopWaitingFrameSize(
+ QuicTransportVersion version,
+ QuicPacketNumberLength packet_number_length) {
+ size_t min_size = kQuicFrameTypeSize + packet_number_length;
+ return min_size;
+}
+
+// static
+size_t QuicFramer::GetRstStreamFrameSize(QuicTransportVersion version,
+ const QuicRstStreamFrame& frame) {
+ if (version == QUIC_VERSION_99) {
+ return QuicDataWriter::GetVarInt62Len(frame.stream_id) +
+ QuicDataWriter::GetVarInt62Len(frame.byte_offset) +
+ kQuicFrameTypeSize + kQuicIetfQuicErrorCodeSize;
+ }
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize +
+ kQuicErrorCodeSize;
+}
+
+// static
+size_t QuicFramer::GetMinConnectionCloseFrameSize(
+ QuicTransportVersion version,
+ const QuicConnectionCloseFrame& frame) {
+ if (version == QUIC_VERSION_99) {
+ // TODO(fkastenholz): For complete support of IETF QUIC CONNECTION_CLOSE,
+ // check if the frame is a Transport close and if the frame's
+ // extracted_error_code is not QUIC_IETF_GQUIC_ERROR_MISSING. If so,
+ // extend the error string to include " QuicErrorCode: #"
+ if (frame.close_type == IETF_QUIC_APPLICATION_CONNECTION_CLOSE) {
+ // Application close variant does not include the transport close frame
+ // type field.
+ return QuicDataWriter::GetVarInt62Len(
+ TruncatedErrorStringSize(frame.error_details)) +
+ kQuicFrameTypeSize + kQuicIetfQuicErrorCodeSize;
+ }
+ QUIC_BUG_IF(frame.close_type != IETF_QUIC_TRANSPORT_CONNECTION_CLOSE)
+ << "IETF QUIC Connection close and QuicConnectionCloseFrame type is "
+ "not IETF ConnectionClose";
+ return QuicDataWriter::GetVarInt62Len(
+ TruncatedErrorStringSize(frame.error_details)) +
+ QuicDataWriter::GetVarInt62Len(frame.transport_close_frame_type) +
+ kQuicFrameTypeSize + kQuicIetfQuicErrorCodeSize;
+ }
+ // Not version 99/IETF QUIC, return Google QUIC CONNECTION CLOSE frame size.
+ return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize;
+}
+
+// static
+size_t QuicFramer::GetMinGoAwayFrameSize() {
+ return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize +
+ kQuicMaxStreamIdSize;
+}
+
+// static
+size_t QuicFramer::GetWindowUpdateFrameSize(
+ QuicTransportVersion version,
+ const QuicWindowUpdateFrame& frame) {
+ if (version != QUIC_VERSION_99) {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+ }
+ if (frame.stream_id == QuicUtils::GetInvalidStreamId(version)) {
+ // Frame would be a MAX DATA frame, which has only a Maximum Data field.
+ return kQuicFrameTypeSize +
+ QuicDataWriter::GetVarInt62Len(frame.byte_offset);
+ }
+ // Frame would be MAX STREAM DATA, has Maximum Stream Data and Stream ID
+ // fields.
+ return kQuicFrameTypeSize +
+ QuicDataWriter::GetVarInt62Len(frame.byte_offset) +
+ QuicDataWriter::GetVarInt62Len(frame.stream_id);
+}
+
+// static
+size_t QuicFramer::GetMaxStreamsFrameSize(QuicTransportVersion version,
+ const QuicMaxStreamIdFrame& frame) {
+ if (version != QUIC_VERSION_99) {
+ QUIC_BUG << "In version " << version
+ << " - not 99 - and tried to serialize MaxStreamId Frame.";
+ }
+
+ // Convert from the stream id on which the connection is blocked to a count
+ QuicStreamId stream_count = StreamIdToCount(version, frame.max_stream_id);
+
+ return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(stream_count);
+}
+
+// static
+size_t QuicFramer::GetStreamsBlockedFrameSize(
+ QuicTransportVersion version,
+ const QuicStreamIdBlockedFrame& frame) {
+ if (version != QUIC_VERSION_99) {
+ QUIC_BUG << "In version " << version
+ << " - not 99 - and tried to serialize StreamIdBlocked Frame.";
+ }
+
+ // Convert from the stream id on which the connection is blocked to a count
+ QuicStreamId stream_count = StreamIdToCount(version, frame.stream_id);
+
+ return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(stream_count);
+}
+
+// static
+size_t QuicFramer::GetBlockedFrameSize(QuicTransportVersion version,
+ const QuicBlockedFrame& frame) {
+ if (version != QUIC_VERSION_99) {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize;
+ }
+ if (frame.stream_id == QuicUtils::GetInvalidStreamId(version)) {
+ // return size of IETF QUIC Blocked frame
+ return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.offset);
+ }
+ // return size of IETF QUIC Stream Blocked frame.
+ return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.offset) +
+ QuicDataWriter::GetVarInt62Len(frame.stream_id);
+}
+
+// static
+size_t QuicFramer::GetStopSendingFrameSize(const QuicStopSendingFrame& frame) {
+ return kQuicFrameTypeSize + QuicDataWriter::GetVarInt62Len(frame.stream_id) +
+ sizeof(QuicApplicationErrorCode);
+}
+
+// static
+size_t QuicFramer::GetPathChallengeFrameSize(
+ const QuicPathChallengeFrame& frame) {
+ return kQuicFrameTypeSize + sizeof(frame.data_buffer);
+}
+
+// static
+size_t QuicFramer::GetPathResponseFrameSize(
+ const QuicPathResponseFrame& frame) {
+ return kQuicFrameTypeSize + sizeof(frame.data_buffer);
+}
+
+// static
+size_t QuicFramer::GetRetransmittableControlFrameSize(
+ QuicTransportVersion version,
+ const QuicFrame& frame) {
+ switch (frame.type) {
+ case PING_FRAME:
+ // Ping has no payload.
+ return kQuicFrameTypeSize;
+ case RST_STREAM_FRAME:
+ return GetRstStreamFrameSize(version, *frame.rst_stream_frame);
+ case CONNECTION_CLOSE_FRAME:
+ return GetMinConnectionCloseFrameSize(version,
+ *frame.connection_close_frame) +
+ TruncatedErrorStringSize(
+ frame.connection_close_frame->error_details);
+ case GOAWAY_FRAME:
+ return GetMinGoAwayFrameSize() +
+ TruncatedErrorStringSize(frame.goaway_frame->reason_phrase);
+ case WINDOW_UPDATE_FRAME:
+ // For version 99, this could be either a MAX DATA or MAX STREAM DATA.
+ // GetWindowUpdateFrameSize figures this out and returns the correct
+ // length.
+ return GetWindowUpdateFrameSize(version, *frame.window_update_frame);
+ case BLOCKED_FRAME:
+ return GetBlockedFrameSize(version, *frame.blocked_frame);
+ case NEW_CONNECTION_ID_FRAME:
+ return GetNewConnectionIdFrameSize(*frame.new_connection_id_frame);
+ case RETIRE_CONNECTION_ID_FRAME:
+ return GetRetireConnectionIdFrameSize(*frame.retire_connection_id_frame);
+ case NEW_TOKEN_FRAME:
+ return GetNewTokenFrameSize(*frame.new_token_frame);
+ case MAX_STREAM_ID_FRAME:
+ return GetMaxStreamsFrameSize(version, frame.max_stream_id_frame);
+ case STREAM_ID_BLOCKED_FRAME:
+ return GetStreamsBlockedFrameSize(version, frame.stream_id_blocked_frame);
+ case PATH_RESPONSE_FRAME:
+ return GetPathResponseFrameSize(*frame.path_response_frame);
+ case PATH_CHALLENGE_FRAME:
+ return GetPathChallengeFrameSize(*frame.path_challenge_frame);
+ case STOP_SENDING_FRAME:
+ return GetStopSendingFrameSize(*frame.stop_sending_frame);
+
+ case STREAM_FRAME:
+ case ACK_FRAME:
+ case STOP_WAITING_FRAME:
+ case MTU_DISCOVERY_FRAME:
+ case PADDING_FRAME:
+ case MESSAGE_FRAME:
+ case CRYPTO_FRAME:
+ case NUM_FRAME_TYPES:
+ DCHECK(false);
+ return 0;
+ }
+
+ // Not reachable, but some Chrome compilers can't figure that out. *sigh*
+ DCHECK(false);
+ return 0;
+}
+
+// static
+size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) {
+ // Sizes are 1 through 4 bytes.
+ for (int i = 1; i <= 4; ++i) {
+ stream_id >>= 8;
+ if (stream_id == 0) {
+ return i;
+ }
+ }
+ QUIC_BUG << "Failed to determine StreamIDSize.";
+ return 4;
+}
+
+// static
+size_t QuicFramer::GetStreamOffsetSize(QuicTransportVersion version,
+ QuicStreamOffset offset) {
+ // 0 is a special case.
+ if (offset == 0) {
+ return 0;
+ }
+ // 2 through 8 are the remaining sizes.
+ offset >>= 8;
+ for (int i = 2; i <= 8; ++i) {
+ offset >>= 8;
+ if (offset == 0) {
+ return i;
+ }
+ }
+ QUIC_BUG << "Failed to determine StreamOffsetSize.";
+ return 8;
+}
+
+// static
+size_t QuicFramer::GetNewConnectionIdFrameSize(
+ const QuicNewConnectionIdFrame& frame) {
+ return kQuicFrameTypeSize +
+ QuicDataWriter::GetVarInt62Len(frame.sequence_number) +
+ kConnectionIdLengthSize + frame.connection_id.length() +
+ sizeof(frame.stateless_reset_token);
+}
+
+// static
+size_t QuicFramer::GetRetireConnectionIdFrameSize(
+ const QuicRetireConnectionIdFrame& frame) {
+ return kQuicFrameTypeSize +
+ QuicDataWriter::GetVarInt62Len(frame.sequence_number);
+}
+
+// static
+size_t QuicFramer::GetNewTokenFrameSize(const QuicNewTokenFrame& frame) {
+ return kQuicFrameTypeSize +
+ QuicDataWriter::GetVarInt62Len(frame.token.length()) +
+ frame.token.length();
+}
+
+// TODO(nharper): Change this method to take a ParsedQuicVersion.
+bool QuicFramer::IsSupportedTransportVersion(
+ const QuicTransportVersion version) const {
+ for (ParsedQuicVersion supported_version : supported_versions_) {
+ if (version == supported_version.transport_version) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuicFramer::IsSupportedVersion(const ParsedQuicVersion version) const {
+ for (const ParsedQuicVersion& supported_version : supported_versions_) {
+ if (version == supported_version) {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t QuicFramer::GetSerializedFrameLength(
+ const QuicFrame& frame,
+ size_t free_bytes,
+ bool first_frame,
+ bool last_frame,
+ QuicPacketNumberLength packet_number_length) {
+ // Prevent a rare crash reported in b/19458523.
+ if (frame.type == ACK_FRAME && frame.ack_frame == nullptr) {
+ QUIC_BUG << "Cannot compute the length of a null ack frame. free_bytes:"
+ << free_bytes << " first_frame:" << first_frame
+ << " last_frame:" << last_frame
+ << " seq num length:" << packet_number_length;
+ set_error(QUIC_INTERNAL_ERROR);
+ visitor_->OnError(this);
+ return 0;
+ }
+ if (frame.type == PADDING_FRAME) {
+ if (frame.padding_frame.num_padding_bytes == -1) {
+ // Full padding to the end of the packet.
+ return free_bytes;
+ } else {
+ // Lite padding.
+ return free_bytes <
+ static_cast<size_t>(frame.padding_frame.num_padding_bytes)
+ ? free_bytes
+ : frame.padding_frame.num_padding_bytes;
+ }
+ }
+
+ size_t frame_len =
+ ComputeFrameLength(frame, last_frame, packet_number_length);
+ if (frame_len <= free_bytes) {
+ // Frame fits within packet. Note that acks may be truncated.
+ return frame_len;
+ }
+ // Only truncate the first frame in a packet, so if subsequent ones go
+ // over, stop including more frames.
+ if (!first_frame) {
+ return 0;
+ }
+ bool can_truncate =
+ frame.type == ACK_FRAME &&
+ free_bytes >= GetMinAckFrameSize(version_.transport_version,
+ PACKET_6BYTE_PACKET_NUMBER);
+ if (can_truncate) {
+ // Truncate the frame so the packet will not exceed kMaxOutgoingPacketSize.
+ // Note that we may not use every byte of the writer in this case.
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Truncating large frame, free bytes: " << free_bytes;
+ return free_bytes;
+ }
+ return 0;
+}
+
+QuicFramer::AckFrameInfo::AckFrameInfo()
+ : max_block_length(0), first_block_length(0), num_ack_blocks(0) {}
+
+QuicFramer::AckFrameInfo::AckFrameInfo(const AckFrameInfo& other) = default;
+
+QuicFramer::AckFrameInfo::~AckFrameInfo() {}
+
+bool QuicFramer::WriteIetfLongHeaderLength(const QuicPacketHeader& header,
+ QuicDataWriter* writer,
+ size_t length_field_offset,
+ EncryptionLevel level) {
+ if (!QuicVersionHasLongHeaderLengths(transport_version()) ||
+ !header.version_flag || length_field_offset == 0) {
+ return true;
+ }
+ if (writer->length() < length_field_offset ||
+ writer->length() - length_field_offset <
+ kQuicDefaultLongHeaderLengthLength) {
+ set_detailed_error("Invalid length_field_offset.");
+ QUIC_BUG << "Invalid length_field_offset.";
+ return false;
+ }
+ size_t length_to_write = writer->length() - length_field_offset -
+ kQuicDefaultLongHeaderLengthLength;
+ // Add length of auth tag.
+ length_to_write = GetCiphertextSize(level, length_to_write);
+
+ QuicDataWriter length_writer(writer->length() - length_field_offset,
+ writer->data() + length_field_offset);
+ if (!length_writer.WriteVarInt62(length_to_write,
+ kQuicDefaultLongHeaderLengthLength)) {
+ set_detailed_error("Failed to overwrite long header length.");
+ QUIC_BUG << "Failed to overwrite long header length.";
+ return false;
+ }
+ return true;
+}
+
+size_t QuicFramer::BuildDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ char* buffer,
+ size_t packet_length,
+ EncryptionLevel level) {
+ QuicDataWriter writer(packet_length, buffer);
+ size_t length_field_offset = 0;
+ if (!AppendPacketHeader(header, &writer, &length_field_offset)) {
+ QUIC_BUG << "AppendPacketHeader failed";
+ return 0;
+ }
+
+ if (transport_version() == QUIC_VERSION_99) {
+ if (AppendIetfFrames(frames, &writer) == 0) {
+ return 0;
+ }
+ if (!WriteIetfLongHeaderLength(header, &writer, length_field_offset,
+ level)) {
+ return 0;
+ }
+ return writer.length();
+ }
+ // TODO(dschinazi) if we enable long header lengths before v99, we need to
+ // add support for fixing up lengths in QuicFramer::BuildDataPacket.
+ DCHECK(!QuicVersionHasLongHeaderLengths(transport_version()));
+
+ size_t i = 0;
+ for (const QuicFrame& frame : frames) {
+ // Determine if we should write stream frame length in header.
+ const bool last_frame_in_packet = i == frames.size() - 1;
+ if (!AppendTypeByte(frame, last_frame_in_packet, &writer)) {
+ QUIC_BUG << "AppendTypeByte failed";
+ return 0;
+ }
+
+ switch (frame.type) {
+ case PADDING_FRAME:
+ if (!AppendPaddingFrame(frame.padding_frame, &writer)) {
+ QUIC_BUG << "AppendPaddingFrame of "
+ << frame.padding_frame.num_padding_bytes << " failed";
+ return 0;
+ }
+ break;
+ case STREAM_FRAME:
+ if (!AppendStreamFrame(frame.stream_frame, last_frame_in_packet,
+ &writer)) {
+ QUIC_BUG << "AppendStreamFrame failed";
+ return 0;
+ }
+ break;
+ case ACK_FRAME:
+ if (!AppendAckFrameAndTypeByte(*frame.ack_frame, &writer)) {
+ QUIC_BUG << "AppendAckFrameAndTypeByte failed: " << detailed_error_;
+ return 0;
+ }
+ break;
+ case STOP_WAITING_FRAME:
+ if (!AppendStopWaitingFrame(header, frame.stop_waiting_frame,
+ &writer)) {
+ QUIC_BUG << "AppendStopWaitingFrame failed";
+ return 0;
+ }
+ break;
+ case MTU_DISCOVERY_FRAME:
+ // MTU discovery frames are serialized as ping frames.
+ QUIC_FALLTHROUGH_INTENDED;
+ case PING_FRAME:
+ // Ping has no payload.
+ break;
+ case RST_STREAM_FRAME:
+ if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) {
+ QUIC_BUG << "AppendRstStreamFrame failed";
+ return 0;
+ }
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ if (!AppendConnectionCloseFrame(*frame.connection_close_frame,
+ &writer)) {
+ QUIC_BUG << "AppendConnectionCloseFrame failed";
+ return 0;
+ }
+ break;
+ case GOAWAY_FRAME:
+ if (!AppendGoAwayFrame(*frame.goaway_frame, &writer)) {
+ QUIC_BUG << "AppendGoAwayFrame failed";
+ return 0;
+ }
+ break;
+ case WINDOW_UPDATE_FRAME:
+ if (!AppendWindowUpdateFrame(*frame.window_update_frame, &writer)) {
+ QUIC_BUG << "AppendWindowUpdateFrame failed";
+ return 0;
+ }
+ break;
+ case BLOCKED_FRAME:
+ if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) {
+ QUIC_BUG << "AppendBlockedFrame failed";
+ return 0;
+ }
+ break;
+ case NEW_CONNECTION_ID_FRAME:
+ set_detailed_error(
+ "Attempt to append NEW_CONNECTION_ID frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case RETIRE_CONNECTION_ID_FRAME:
+ set_detailed_error(
+ "Attempt to append RETIRE_CONNECTION_ID frame and not in version "
+ "99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case NEW_TOKEN_FRAME:
+ set_detailed_error(
+ "Attempt to append NEW_TOKEN_ID frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case MAX_STREAM_ID_FRAME:
+ set_detailed_error(
+ "Attempt to append MAX_STREAM_ID frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case STREAM_ID_BLOCKED_FRAME:
+ set_detailed_error(
+ "Attempt to append STREAM_ID_BLOCKED frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case PATH_RESPONSE_FRAME:
+ set_detailed_error(
+ "Attempt to append PATH_RESPONSE frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case PATH_CHALLENGE_FRAME:
+ set_detailed_error(
+ "Attempt to append PATH_CHALLENGE frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case STOP_SENDING_FRAME:
+ set_detailed_error(
+ "Attempt to append STOP_SENDING frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case MESSAGE_FRAME:
+ if (!AppendMessageFrameAndTypeByte(*frame.message_frame,
+ last_frame_in_packet, &writer)) {
+ QUIC_BUG << "AppendMessageFrame failed";
+ return 0;
+ }
+ break;
+ case CRYPTO_FRAME:
+ if (!QuicVersionUsesCryptoFrames(version_.transport_version)) {
+ set_detailed_error(
+ "Attempt to append CRYPTO frame in version prior to 47.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ }
+ if (!AppendCryptoFrame(*frame.crypto_frame, &writer)) {
+ QUIC_BUG << "AppendCryptoFrame failed";
+ return 0;
+ }
+ break;
+ default:
+ RaiseError(QUIC_INVALID_FRAME_DATA);
+ QUIC_BUG << "QUIC_INVALID_FRAME_DATA";
+ return 0;
+ }
+ ++i;
+ }
+
+ return writer.length();
+}
+
+size_t QuicFramer::AppendIetfFrames(const QuicFrames& frames,
+ QuicDataWriter* writer) {
+ size_t i = 0;
+ for (const QuicFrame& frame : frames) {
+ // Determine if we should write stream frame length in header.
+ const bool last_frame_in_packet = i == frames.size() - 1;
+ if (!AppendIetfTypeByte(frame, last_frame_in_packet, writer)) {
+ QUIC_BUG << "AppendIetfTypeByte failed: " << detailed_error();
+ return 0;
+ }
+
+ switch (frame.type) {
+ case PADDING_FRAME:
+ if (!AppendPaddingFrame(frame.padding_frame, writer)) {
+ QUIC_BUG << "AppendPaddingFrame of "
+ << frame.padding_frame.num_padding_bytes
+ << " failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case STREAM_FRAME:
+ if (!AppendStreamFrame(frame.stream_frame, last_frame_in_packet,
+ writer)) {
+ QUIC_BUG << "AppendStreamFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case ACK_FRAME:
+ if (!AppendIetfAckFrameAndTypeByte(*frame.ack_frame, writer)) {
+ QUIC_BUG << "AppendIetfAckFrameAndTypeByte failed: "
+ << detailed_error();
+ return 0;
+ }
+ break;
+ case STOP_WAITING_FRAME:
+ set_detailed_error(
+ "Attempt to append STOP WAITING frame in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case MTU_DISCOVERY_FRAME:
+ // MTU discovery frames are serialized as ping frames.
+ QUIC_FALLTHROUGH_INTENDED;
+ case PING_FRAME:
+ // Ping has no payload.
+ break;
+ case RST_STREAM_FRAME:
+ if (!AppendRstStreamFrame(*frame.rst_stream_frame, writer)) {
+ QUIC_BUG << "AppendRstStreamFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ if (!AppendIetfConnectionCloseFrame(*frame.connection_close_frame,
+ writer)) {
+ QUIC_BUG << "AppendIetfConnectionCloseFrame failed: "
+ << detailed_error();
+ return 0;
+ }
+ break;
+ case GOAWAY_FRAME:
+ set_detailed_error("Attempt to append GOAWAY frame in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case WINDOW_UPDATE_FRAME:
+ // Depending on whether there is a stream ID or not, will be either a
+ // MAX STREAM DATA frame or a MAX DATA frame.
+ if (frame.window_update_frame->stream_id ==
+ QuicUtils::GetInvalidStreamId(transport_version())) {
+ if (!AppendMaxDataFrame(*frame.window_update_frame, writer)) {
+ QUIC_BUG << "AppendMaxDataFrame failed: " << detailed_error();
+ return 0;
+ }
+ } else {
+ if (!AppendMaxStreamDataFrame(*frame.window_update_frame, writer)) {
+ QUIC_BUG << "AppendMaxStreamDataFrame failed: " << detailed_error();
+ return 0;
+ }
+ }
+ break;
+ case BLOCKED_FRAME:
+ if (!AppendBlockedFrame(*frame.blocked_frame, writer)) {
+ QUIC_BUG << "AppendBlockedFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case MAX_STREAM_ID_FRAME:
+ if (!AppendMaxStreamsFrame(frame.max_stream_id_frame, writer)) {
+ QUIC_BUG << "AppendMaxStreamsFrame failed" << detailed_error();
+ return 0;
+ }
+ break;
+ case STREAM_ID_BLOCKED_FRAME:
+ if (!AppendStreamsBlockedFrame(frame.stream_id_blocked_frame, writer)) {
+ QUIC_BUG << "AppendStreamsBlockedFrame failed" << detailed_error();
+ return 0;
+ }
+ break;
+ case NEW_CONNECTION_ID_FRAME:
+ if (!AppendNewConnectionIdFrame(*frame.new_connection_id_frame,
+ writer)) {
+ QUIC_BUG << "AppendNewConnectionIdFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case RETIRE_CONNECTION_ID_FRAME:
+ if (!AppendRetireConnectionIdFrame(*frame.retire_connection_id_frame,
+ writer)) {
+ QUIC_BUG << "AppendRetireConnectionIdFrame failed: "
+ << detailed_error();
+ return 0;
+ }
+ break;
+ case NEW_TOKEN_FRAME:
+ if (!AppendNewTokenFrame(*frame.new_token_frame, writer)) {
+ QUIC_BUG << "AppendNewTokenFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case STOP_SENDING_FRAME:
+ if (!AppendStopSendingFrame(*frame.stop_sending_frame, writer)) {
+ QUIC_BUG << "AppendStopSendingFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case PATH_CHALLENGE_FRAME:
+ if (!AppendPathChallengeFrame(*frame.path_challenge_frame, writer)) {
+ QUIC_BUG << "AppendPathChallengeFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case PATH_RESPONSE_FRAME:
+ if (!AppendPathResponseFrame(*frame.path_response_frame, writer)) {
+ QUIC_BUG << "AppendPathResponseFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case MESSAGE_FRAME:
+ if (!AppendMessageFrameAndTypeByte(*frame.message_frame,
+ last_frame_in_packet, writer)) {
+ QUIC_BUG << "AppendMessageFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ case CRYPTO_FRAME:
+ if (!AppendCryptoFrame(*frame.crypto_frame, writer)) {
+ QUIC_BUG << "AppendCryptoFrame failed: " << detailed_error();
+ return 0;
+ }
+ break;
+ default:
+ RaiseError(QUIC_INVALID_FRAME_DATA);
+ set_detailed_error("Tried to append unknown frame type.");
+ QUIC_BUG << "QUIC_INVALID_FRAME_DATA";
+ return 0;
+ }
+ ++i;
+ }
+
+ return writer->length();
+}
+
+size_t QuicFramer::BuildConnectivityProbingPacket(
+ const QuicPacketHeader& header,
+ char* buffer,
+ size_t packet_length,
+ EncryptionLevel level) {
+ QuicFrames frames;
+
+ // Write a PING frame, which has no data payload.
+ QuicPingFrame ping_frame;
+ frames.push_back(QuicFrame(ping_frame));
+
+ // Add padding to the rest of the packet.
+ QuicPaddingFrame padding_frame;
+ frames.push_back(QuicFrame(padding_frame));
+
+ return BuildDataPacket(header, frames, buffer, packet_length, level);
+}
+
+size_t QuicFramer::BuildPaddedPathChallengePacket(
+ const QuicPacketHeader& header,
+ char* buffer,
+ size_t packet_length,
+ QuicPathFrameBuffer* payload,
+ QuicRandom* randomizer,
+ EncryptionLevel level) {
+ if (version_.transport_version != QUIC_VERSION_99) {
+ QUIC_BUG << "Attempt to build a PATH_CHALLENGE Connectivity Probing "
+ "packet and not doing IETF QUIC";
+ return 0;
+ }
+ QuicFrames frames;
+
+ // Write a PATH_CHALLENGE frame, which has a random 8-byte payload
+ randomizer->RandBytes(payload->data(), payload->size());
+
+ QuicPathChallengeFrame path_challenge_frame(0, *payload);
+ frames.push_back(QuicFrame(&path_challenge_frame));
+
+ // Add padding to the rest of the packet in order to assess Path MTU
+ // characteristics.
+ QuicPaddingFrame padding_frame;
+ frames.push_back(QuicFrame(padding_frame));
+
+ return BuildDataPacket(header, frames, buffer, packet_length, level);
+}
+
+size_t QuicFramer::BuildPathResponsePacket(
+ const QuicPacketHeader& header,
+ char* buffer,
+ size_t packet_length,
+ const QuicDeque<QuicPathFrameBuffer>& payloads,
+ const bool is_padded,
+ EncryptionLevel level) {
+ if (payloads.empty()) {
+ QUIC_BUG
+ << "Attempt to generate connectivity response with no request payloads";
+ return 0;
+ }
+ if (version_.transport_version != QUIC_VERSION_99) {
+ QUIC_BUG << "Attempt to build a PATH_RESPONSE Connectivity Probing "
+ "packet and not doing IETF QUIC";
+ return 0;
+ }
+
+ std::vector<std::unique_ptr<QuicPathResponseFrame>> path_response_frames;
+ for (const QuicPathFrameBuffer& payload : payloads) {
+ // Note that the control frame ID can be 0 since this is not retransmitted.
+ path_response_frames.push_back(
+ QuicMakeUnique<QuicPathResponseFrame>(0, payload));
+ }
+
+ QuicFrames frames;
+ for (const std::unique_ptr<QuicPathResponseFrame>& path_response_frame :
+ path_response_frames) {
+ frames.push_back(QuicFrame(path_response_frame.get()));
+ }
+
+ if (is_padded) {
+ // Add padding to the rest of the packet in order to assess Path MTU
+ // characteristics.
+ QuicPaddingFrame padding_frame;
+ frames.push_back(QuicFrame(padding_frame));
+ }
+
+ return BuildDataPacket(header, frames, buffer, packet_length, level);
+}
+
+// static
+std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildPublicResetPacket(
+ const QuicPublicResetPacket& packet) {
+ CryptoHandshakeMessage reset;
+ reset.set_tag(kPRST);
+ reset.SetValue(kRNON, packet.nonce_proof);
+ if (packet.client_address.host().address_family() !=
+ IpAddressFamily::IP_UNSPEC) {
+ // packet.client_address is non-empty.
+ QuicSocketAddressCoder address_coder(packet.client_address);
+ std::string serialized_address = address_coder.Encode();
+ if (serialized_address.empty()) {
+ return nullptr;
+ }
+ reset.SetStringPiece(kCADR, serialized_address);
+ }
+ if (!packet.endpoint_id.empty()) {
+ reset.SetStringPiece(kEPID, packet.endpoint_id);
+ }
+ const QuicData& reset_serialized = reset.GetSerialized();
+
+ size_t len = kPublicFlagsSize + packet.connection_id.length() +
+ reset_serialized.length();
+ std::unique_ptr<char[]> buffer(new char[len]);
+ // Endianness is not a concern here, as writer is not going to write integers
+ // or floating numbers.
+ QuicDataWriter writer(len, buffer.get());
+
+ uint8_t flags = static_cast<uint8_t>(PACKET_PUBLIC_FLAGS_RST |
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID);
+ // This hack makes post-v33 public reset packet look like pre-v33 packets.
+ flags |= static_cast<uint8_t>(PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD);
+ if (!writer.WriteUInt8(flags)) {
+ return nullptr;
+ }
+
+ if (!writer.WriteConnectionId(packet.connection_id)) {
+ return nullptr;
+ }
+
+ if (!writer.WriteBytes(reset_serialized.data(), reset_serialized.length())) {
+ return nullptr;
+ }
+
+ return QuicMakeUnique<QuicEncryptedPacket>(buffer.release(), len, true);
+}
+
+// static
+std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket(
+ QuicConnectionId connection_id,
+ QuicUint128 stateless_reset_token) {
+ QUIC_DVLOG(1) << "Building IETF stateless reset packet.";
+ size_t len = kPacketHeaderTypeSize + kMinRandomBytesLengthInStatelessReset +
+ sizeof(stateless_reset_token);
+ std::unique_ptr<char[]> buffer(new char[len]);
+ QuicDataWriter writer(len, buffer.get());
+
+ uint8_t type = 0;
+ type |= FLAGS_FIXED_BIT;
+ type |= FLAGS_SHORT_HEADER_RESERVED_1;
+ type |= FLAGS_SHORT_HEADER_RESERVED_2;
+ type |= PacketNumberLengthToOnWireValue(QUIC_VERSION_UNSUPPORTED,
+ PACKET_1BYTE_PACKET_NUMBER);
+
+ // Append type byte.
+ if (!writer.WriteUInt8(type)) {
+ return nullptr;
+ }
+ // Append random bytes.
+ if (!writer.WriteRandomBytes(QuicRandom::GetInstance(),
+ kMinRandomBytesLengthInStatelessReset)) {
+ return nullptr;
+ }
+
+ // Append stateless reset token.
+ if (!writer.WriteBytes(&stateless_reset_token,
+ sizeof(stateless_reset_token))) {
+ return nullptr;
+ }
+ return QuicMakeUnique<QuicEncryptedPacket>(buffer.release(), len, true);
+}
+
+// static
+std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildVersionNegotiationPacket(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ const ParsedQuicVersionVector& versions) {
+ if (ietf_quic) {
+ return BuildIetfVersionNegotiationPacket(connection_id, versions);
+ }
+ DCHECK(!versions.empty());
+ size_t len = kPublicFlagsSize + connection_id.length() +
+ versions.size() * kQuicVersionSize;
+ std::unique_ptr<char[]> buffer(new char[len]);
+ // Endianness is not a concern here, version negotiation packet does not have
+ // integers or floating numbers.
+ QuicDataWriter writer(len, buffer.get());
+
+ uint8_t flags = static_cast<uint8_t>(
+ PACKET_PUBLIC_FLAGS_VERSION | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID |
+ // TODO(rch): Remove this QUIC_VERSION_32 is retired.
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD);
+ if (!writer.WriteUInt8(flags)) {
+ return nullptr;
+ }
+
+ if (!writer.WriteConnectionId(connection_id)) {
+ return nullptr;
+ }
+
+ for (const ParsedQuicVersion& version : versions) {
+ // TODO(rch): Use WriteUInt32() once QUIC_VERSION_35 is removed.
+ if (!writer.WriteTag(
+ QuicEndian::HostToNet32(CreateQuicVersionLabel(version)))) {
+ return nullptr;
+ }
+ }
+
+ return QuicMakeUnique<QuicEncryptedPacket>(buffer.release(), len, true);
+}
+
+// static
+std::unique_ptr<QuicEncryptedPacket>
+QuicFramer::BuildIetfVersionNegotiationPacket(
+ QuicConnectionId connection_id,
+ const ParsedQuicVersionVector& versions) {
+ QUIC_DVLOG(1) << "Building IETF version negotiation packet.";
+ DCHECK(!versions.empty());
+ size_t len = kPacketHeaderTypeSize + kConnectionIdLengthSize +
+ connection_id.length() +
+ (versions.size() + 1) * kQuicVersionSize;
+ std::unique_ptr<char[]> buffer(new char[len]);
+ QuicDataWriter writer(len, buffer.get());
+
+ // TODO(fayang): Randomly select a value for the type.
+ uint8_t type = static_cast<uint8_t>(FLAGS_LONG_HEADER | VERSION_NEGOTIATION);
+ if (!writer.WriteUInt8(type)) {
+ return nullptr;
+ }
+
+ if (!writer.WriteUInt32(0)) {
+ return nullptr;
+ }
+
+ if (!GetQuicReloadableFlag(quic_use_new_append_connection_id)) {
+ if (!AppendIetfConnectionId(true, EmptyQuicConnectionId(),
+ PACKET_0BYTE_CONNECTION_ID, connection_id,
+ PACKET_8BYTE_CONNECTION_ID, &writer)) {
+ return nullptr;
+ }
+ } else {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_new_append_connection_id, 1, 2);
+ if (!AppendIetfConnectionIdsNew(true, EmptyQuicConnectionId(),
+ connection_id, &writer)) {
+ return nullptr;
+ }
+ }
+
+ for (const ParsedQuicVersion& version : versions) {
+ // TODO(rch): Use WriteUInt32() once QUIC_VERSION_35 is removed.
+ if (!writer.WriteTag(
+ QuicEndian::HostToNet32(CreateQuicVersionLabel(version)))) {
+ return nullptr;
+ }
+ }
+
+ return QuicMakeUnique<QuicEncryptedPacket>(buffer.release(), len, true);
+}
+
+bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
+ QuicDataReader reader(packet.data(), packet.length());
+
+ bool packet_has_ietf_packet_header = false;
+ if (infer_packet_header_type_from_version_) {
+ packet_has_ietf_packet_header =
+ version_.transport_version > QUIC_VERSION_43;
+ } else if (!reader.IsDoneReading()) {
+ uint8_t type = reader.PeekByte();
+ packet_has_ietf_packet_header = QuicUtils::IsIetfPacketHeader(type);
+ }
+ if (packet_has_ietf_packet_header) {
+ QUIC_DVLOG(1) << ENDPOINT << "Processing IETF QUIC packet.";
+ }
+
+ visitor_->OnPacket();
+
+ QuicPacketHeader header;
+ if (!ProcessPublicHeader(&reader, packet_has_ietf_packet_header, &header)) {
+ DCHECK_NE("", detailed_error_);
+ QUIC_DVLOG(1) << ENDPOINT << "Unable to process public header. Error: "
+ << detailed_error_;
+ DCHECK_NE("", detailed_error_);
+ RecordDroppedPacketReason(DroppedPacketReason::INVALID_PUBLIC_HEADER);
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ if (!visitor_->OnUnauthenticatedPublicHeader(header)) {
+ // The visitor suppresses further processing of the packet.
+ return true;
+ }
+
+ if (perspective_ == Perspective::IS_SERVER && header.version_flag &&
+ header.version != version_) {
+ if (!visitor_->OnProtocolVersionMismatch(header.version, header.form)) {
+ RecordDroppedPacketReason(DroppedPacketReason::VERSION_MISMATCH);
+ return true;
+ }
+ }
+
+ bool rv;
+ if (IsVersionNegotiation(header, packet_has_ietf_packet_header)) {
+ QUIC_DVLOG(1) << ENDPOINT << "Received version negotiation packet";
+ rv = ProcessVersionNegotiationPacket(&reader, header);
+ } else if (header.reset_flag) {
+ rv = ProcessPublicResetPacket(&reader, header);
+ } else if (packet.length() <= kMaxIncomingPacketSize) {
+ // The optimized decryption algorithm implementations run faster when
+ // operating on aligned memory.
+ QUIC_CACHELINE_ALIGNED char buffer[kMaxIncomingPacketSize];
+ if (packet_has_ietf_packet_header) {
+ rv = ProcessIetfDataPacket(&reader, &header, packet, buffer,
+ QUIC_ARRAYSIZE(buffer));
+ } else {
+ rv = ProcessDataPacket(&reader, &header, packet, buffer,
+ QUIC_ARRAYSIZE(buffer));
+ }
+ } else {
+ std::unique_ptr<char[]> large_buffer(new char[packet.length()]);
+ if (packet_has_ietf_packet_header) {
+ rv = ProcessIetfDataPacket(&reader, &header, packet, large_buffer.get(),
+ packet.length());
+ } else {
+ rv = ProcessDataPacket(&reader, &header, packet, large_buffer.get(),
+ packet.length());
+ }
+ QUIC_BUG_IF(rv) << "QUIC should never successfully process packets larger"
+ << "than kMaxIncomingPacketSize. packet size:"
+ << packet.length();
+ }
+ return rv;
+}
+
+bool QuicFramer::ProcessVersionNegotiationPacket(
+ QuicDataReader* reader,
+ const QuicPacketHeader& header) {
+ DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
+
+ QuicVersionNegotiationPacket packet(header.destination_connection_id);
+ // Try reading at least once to raise error if the packet is invalid.
+ do {
+ QuicVersionLabel version_label;
+ if (!reader->ReadTag(&version_label)) {
+ set_detailed_error("Unable to read supported version in negotiation.");
+ RecordDroppedPacketReason(
+ DroppedPacketReason::INVALID_VERSION_NEGOTIATION_PACKET);
+ return RaiseError(QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
+ }
+ // TODO(rch): Use ReadUInt32() once QUIC_VERSION_35 is removed.
+ version_label = QuicEndian::NetToHost32(version_label);
+ packet.versions.push_back(ParseQuicVersionLabel(version_label));
+ } while (!reader->IsDoneReading());
+
+ visitor_->OnVersionNegotiationPacket(packet);
+ return true;
+}
+
+bool QuicFramer::MaybeProcessIetfInitialRetryToken(
+ QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header) {
+ if (!QuicVersionHasLongHeaderLengths(header->version.transport_version) ||
+ header->form != IETF_QUIC_LONG_HEADER_PACKET ||
+ header->long_packet_type != INITIAL) {
+ return true;
+ }
+ uint64_t retry_token_length = 0;
+ header->retry_token_length_length = encrypted_reader->PeekVarInt62Length();
+ if (!encrypted_reader->ReadVarInt62(&retry_token_length)) {
+ set_detailed_error("Unable to read INITIAL retry token length.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ header->retry_token = encrypted_reader->PeekRemainingPayload();
+ // Safety check to avoid spending ressources if malformed.
+ // At this point header->retry_token contains the rest of the packet
+ // so its length() is the amount of data remaining in the packet.
+ if (retry_token_length > header->retry_token.length()) {
+ set_detailed_error("INITIAL token length longer than packet.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ // Resize retry_token to make it only contain the retry token.
+ header->retry_token.remove_suffix(header->retry_token.length() -
+ retry_token_length);
+ // Advance encrypted_reader by retry_token_length.
+ uint8_t wasted_byte;
+ for (uint64_t i = 0; i < retry_token_length; ++i) {
+ if (!encrypted_reader->ReadUInt8(&wasted_byte)) {
+ set_detailed_error("Unable to read INITIAL retry token.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ }
+ return true;
+}
+
+// Seeks the current packet to check for a coalesced packet at the end.
+// If the IETF length field only spans part of the outer packet,
+// then there is a coalesced packet after this one.
+void QuicFramer::MaybeProcessCoalescedPacket(
+ const QuicDataReader& encrypted_reader,
+ uint64_t remaining_bytes_length,
+ const QuicPacketHeader& header) {
+ if (header.remaining_packet_length >= remaining_bytes_length) {
+ // There is no coalesced packet.
+ return;
+ }
+
+ QuicStringPiece remaining_data = encrypted_reader.PeekRemainingPayload();
+ DCHECK_EQ(remaining_data.length(), remaining_bytes_length);
+
+ const char* coalesced_data =
+ remaining_data.data() + header.remaining_packet_length;
+ uint64_t coalesced_data_length =
+ remaining_bytes_length - header.remaining_packet_length;
+ QuicDataReader coalesced_reader(coalesced_data, coalesced_data_length);
+
+ QuicPacketHeader coalesced_header;
+ if (!ProcessIetfPacketHeader(&coalesced_reader, &coalesced_header)) {
+ QUIC_PEER_BUG << ENDPOINT
+ << "Failed to parse received coalesced header of length "
+ << coalesced_data_length << ": "
+ << QuicTextUtils::HexEncode(coalesced_data,
+ coalesced_data_length)
+ << " previous header was " << header;
+ return;
+ }
+
+ if (coalesced_header.destination_connection_id !=
+ header.destination_connection_id ||
+ (coalesced_header.form != IETF_QUIC_SHORT_HEADER_PACKET &&
+ coalesced_header.version != header.version)) {
+ QUIC_PEER_BUG << ENDPOINT << "Received mismatched coalesced header "
+ << coalesced_header << " previous header was " << header;
+ return;
+ }
+
+ QuicEncryptedPacket coalesced_packet(coalesced_data, coalesced_data_length,
+ /*owns_buffer=*/false);
+ visitor_->OnCoalescedPacket(coalesced_packet);
+}
+
+bool QuicFramer::MaybeProcessIetfLength(QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header) {
+ if (!QuicVersionHasLongHeaderLengths(header->version.transport_version) ||
+ header->form != IETF_QUIC_LONG_HEADER_PACKET ||
+ (header->long_packet_type != INITIAL &&
+ header->long_packet_type != HANDSHAKE &&
+ header->long_packet_type != ZERO_RTT_PROTECTED)) {
+ return true;
+ }
+ header->length_length = encrypted_reader->PeekVarInt62Length();
+ if (!encrypted_reader->ReadVarInt62(&header->remaining_packet_length)) {
+ set_detailed_error("Unable to read long header payload length.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ uint64_t remaining_bytes_length = encrypted_reader->BytesRemaining();
+ if (header->remaining_packet_length > remaining_bytes_length) {
+ set_detailed_error("Long header payload length longer than packet.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ MaybeProcessCoalescedPacket(*encrypted_reader, remaining_bytes_length,
+ *header);
+
+ if (!encrypted_reader->TruncateRemaining(header->remaining_packet_length)) {
+ set_detailed_error("Length TruncateRemaining failed.");
+ QUIC_BUG << "Length TruncateRemaining failed.";
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessIetfDataPacket(QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header,
+ const QuicEncryptedPacket& packet,
+ char* decrypted_buffer,
+ size_t buffer_length) {
+ DCHECK_NE(GOOGLE_QUIC_PACKET, header->form);
+ DCHECK(!header->has_possible_stateless_reset_token);
+ header->retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ header->retry_token = QuicStringPiece();
+ header->length_length = VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ header->remaining_packet_length = 0;
+ if (header->form == IETF_QUIC_SHORT_HEADER_PACKET &&
+ perspective_ == Perspective::IS_CLIENT) {
+ // Peek possible stateless reset token. Will only be used on decryption
+ // failure.
+ QuicStringPiece remaining = encrypted_reader->PeekRemainingPayload();
+ if (remaining.length() >= sizeof(header->possible_stateless_reset_token)) {
+ header->has_possible_stateless_reset_token = true;
+ memcpy(&header->possible_stateless_reset_token,
+ &remaining.data()[remaining.length() -
+ sizeof(header->possible_stateless_reset_token)],
+ sizeof(header->possible_stateless_reset_token));
+ }
+ }
+
+ if (!MaybeProcessIetfInitialRetryToken(encrypted_reader, header)) {
+ return false;
+ }
+
+ if (!MaybeProcessIetfLength(encrypted_reader, header)) {
+ return false;
+ }
+
+ if (header->form == IETF_QUIC_SHORT_HEADER_PACKET ||
+ header->long_packet_type != VERSION_NEGOTIATION) {
+ // Process packet number.
+ QuicPacketNumber base_packet_number;
+ if (supports_multiple_packet_number_spaces_) {
+ base_packet_number =
+ largest_decrypted_packet_numbers_[GetPacketNumberSpace(*header)];
+ } else {
+ base_packet_number = largest_packet_number_;
+ }
+ uint64_t full_packet_number;
+ if (!ProcessAndCalculatePacketNumber(
+ encrypted_reader, header->packet_number_length, base_packet_number,
+ &full_packet_number)) {
+ set_detailed_error("Unable to read packet number.");
+ RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER);
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ if (!IsValidFullPacketNumber(full_packet_number, transport_version())) {
+ if (IsIetfStatelessResetPacket(*header)) {
+ // This is a stateless reset packet.
+ QuicIetfStatelessResetPacket packet(
+ *header, header->possible_stateless_reset_token);
+ visitor_->OnAuthenticatedIetfStatelessResetPacket(packet);
+ return true;
+ }
+ RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER);
+ set_detailed_error("packet numbers cannot be 0.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ header->packet_number = QuicPacketNumber(full_packet_number);
+ }
+
+ // A nonce should only present in SHLO from the server to the client when
+ // using QUIC crypto.
+ if (header->form == IETF_QUIC_LONG_HEADER_PACKET &&
+ header->long_packet_type == ZERO_RTT_PROTECTED &&
+ perspective_ == Perspective::IS_CLIENT &&
+ version_.handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
+ if (!encrypted_reader->ReadBytes(
+ reinterpret_cast<uint8_t*>(last_nonce_.data()),
+ last_nonce_.size())) {
+ set_detailed_error("Unable to read nonce.");
+ RecordDroppedPacketReason(
+ DroppedPacketReason::INVALID_DIVERSIFICATION_NONCE);
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ header->nonce = &last_nonce_;
+ } else {
+ header->nonce = nullptr;
+ }
+
+ if (!visitor_->OnUnauthenticatedHeader(*header)) {
+ set_detailed_error(
+ "Visitor asked to stop processing of unauthenticated header.");
+ return false;
+ }
+
+ QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload();
+ QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket(
+ version_.transport_version, packet,
+ GetIncludedDestinationConnectionIdLength(*header),
+ GetIncludedSourceConnectionIdLength(*header), header->version_flag,
+ header->nonce != nullptr, header->packet_number_length,
+ header->retry_token_length_length, header->retry_token.length(),
+ header->length_length);
+
+ size_t decrypted_length = 0;
+ EncryptionLevel decrypted_level;
+ if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer,
+ buffer_length, &decrypted_length, &decrypted_level)) {
+ if (IsIetfStatelessResetPacket(*header)) {
+ // This is a stateless reset packet.
+ QuicIetfStatelessResetPacket packet(
+ *header, header->possible_stateless_reset_token);
+ visitor_->OnAuthenticatedIetfStatelessResetPacket(packet);
+ return true;
+ }
+ set_detailed_error("Unable to decrypt payload.");
+ RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE);
+ return RaiseError(QUIC_DECRYPTION_FAILURE);
+ }
+ QuicDataReader reader(decrypted_buffer, decrypted_length);
+
+ // Update the largest packet number after we have decrypted the packet
+ // so we are confident is not attacker controlled.
+ if (supports_multiple_packet_number_spaces_) {
+ largest_decrypted_packet_numbers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_level)]
+ .UpdateMax(header->packet_number);
+ } else {
+ largest_packet_number_.UpdateMax(header->packet_number);
+ }
+
+ if (!visitor_->OnPacketHeader(*header)) {
+ RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER);
+ // The visitor suppresses further processing of the packet.
+ return true;
+ }
+
+ if (packet.length() > kMaxIncomingPacketSize) {
+ set_detailed_error("Packet too large.");
+ return RaiseError(QUIC_PACKET_TOO_LARGE);
+ }
+
+ // Handle the payload.
+ if (version_.transport_version == QUIC_VERSION_99) {
+ if (!ProcessIetfFrameData(&reader, *header)) {
+ DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessIetfFrameData sets the error.
+ DCHECK_NE("", detailed_error_);
+ QUIC_DLOG(WARNING) << ENDPOINT << "Unable to process frame data. Error: "
+ << detailed_error_;
+ return false;
+ }
+ } else {
+ if (!ProcessFrameData(&reader, *header)) {
+ DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error.
+ DCHECK_NE("", detailed_error_);
+ QUIC_DLOG(WARNING) << ENDPOINT << "Unable to process frame data. Error: "
+ << detailed_error_;
+ return false;
+ }
+ }
+
+ visitor_->OnPacketComplete();
+ return true;
+}
+
+bool QuicFramer::ProcessDataPacket(QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header,
+ const QuicEncryptedPacket& packet,
+ char* decrypted_buffer,
+ size_t buffer_length) {
+ if (!ProcessUnauthenticatedHeader(encrypted_reader, header)) {
+ DCHECK_NE("", detailed_error_);
+ QUIC_DVLOG(1)
+ << ENDPOINT
+ << "Unable to process packet header. Stopping parsing. Error: "
+ << detailed_error_;
+ RecordDroppedPacketReason(DroppedPacketReason::INVALID_PACKET_NUMBER);
+ return false;
+ }
+
+ QuicStringPiece encrypted = encrypted_reader->ReadRemainingPayload();
+ QuicStringPiece associated_data = GetAssociatedDataFromEncryptedPacket(
+ version_.transport_version, packet,
+ GetIncludedDestinationConnectionIdLength(*header),
+ GetIncludedSourceConnectionIdLength(*header), header->version_flag,
+ header->nonce != nullptr, header->packet_number_length,
+ header->retry_token_length_length, header->retry_token.length(),
+ header->length_length);
+
+ size_t decrypted_length = 0;
+ EncryptionLevel decrypted_level;
+ if (!DecryptPayload(encrypted, associated_data, *header, decrypted_buffer,
+ buffer_length, &decrypted_length, &decrypted_level)) {
+ RecordDroppedPacketReason(DroppedPacketReason::DECRYPTION_FAILURE);
+ set_detailed_error("Unable to decrypt payload.");
+ return RaiseError(QUIC_DECRYPTION_FAILURE);
+ }
+
+ QuicDataReader reader(decrypted_buffer, decrypted_length);
+
+ // Update the largest packet number after we have decrypted the packet
+ // so we are confident is not attacker controlled.
+ if (supports_multiple_packet_number_spaces_) {
+ largest_decrypted_packet_numbers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_level)]
+ .UpdateMax(header->packet_number);
+ } else {
+ largest_packet_number_.UpdateMax(header->packet_number);
+ }
+
+ if (!visitor_->OnPacketHeader(*header)) {
+ // The visitor suppresses further processing of the packet.
+ return true;
+ }
+
+ if (packet.length() > kMaxIncomingPacketSize) {
+ set_detailed_error("Packet too large.");
+ return RaiseError(QUIC_PACKET_TOO_LARGE);
+ }
+
+ // Handle the payload.
+ if (!ProcessFrameData(&reader, *header)) {
+ DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error.
+ DCHECK_NE("", detailed_error_);
+ QUIC_DLOG(WARNING) << ENDPOINT << "Unable to process frame data. Error: "
+ << detailed_error_;
+ return false;
+ }
+
+ visitor_->OnPacketComplete();
+ return true;
+}
+
+bool QuicFramer::ProcessPublicResetPacket(QuicDataReader* reader,
+ const QuicPacketHeader& header) {
+ QuicPublicResetPacket packet(header.destination_connection_id);
+
+ std::unique_ptr<CryptoHandshakeMessage> reset(
+ CryptoFramer::ParseMessage(reader->ReadRemainingPayload()));
+ if (!reset.get()) {
+ set_detailed_error("Unable to read reset message.");
+ RecordDroppedPacketReason(DroppedPacketReason::INVALID_PUBLIC_RESET_PACKET);
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ if (reset->tag() != kPRST) {
+ set_detailed_error("Incorrect message tag.");
+ RecordDroppedPacketReason(DroppedPacketReason::INVALID_PUBLIC_RESET_PACKET);
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+
+ if (reset->GetUint64(kRNON, &packet.nonce_proof) != QUIC_NO_ERROR) {
+ set_detailed_error("Unable to read nonce proof.");
+ RecordDroppedPacketReason(DroppedPacketReason::INVALID_PUBLIC_RESET_PACKET);
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ // TODO(satyamshekhar): validate nonce to protect against DoS.
+
+ QuicStringPiece address;
+ if (reset->GetStringPiece(kCADR, &address)) {
+ QuicSocketAddressCoder address_coder;
+ if (address_coder.Decode(address.data(), address.length())) {
+ packet.client_address =
+ QuicSocketAddress(address_coder.ip(), address_coder.port());
+ }
+ }
+
+ QuicStringPiece endpoint_id;
+ if (perspective_ == Perspective::IS_CLIENT &&
+ reset->GetStringPiece(kEPID, &endpoint_id)) {
+ packet.endpoint_id = std::string(endpoint_id);
+ packet.endpoint_id += '\0';
+ }
+
+ visitor_->OnPublicResetPacket(packet);
+ return true;
+}
+
+bool QuicFramer::IsIetfStatelessResetPacket(
+ const QuicPacketHeader& header) const {
+ QUIC_BUG_IF(header.has_possible_stateless_reset_token &&
+ perspective_ != Perspective::IS_CLIENT)
+ << "has_possible_stateless_reset_token can only be true at client side.";
+ return header.form == IETF_QUIC_SHORT_HEADER_PACKET &&
+ header.has_possible_stateless_reset_token &&
+ visitor_->IsValidStatelessResetToken(
+ header.possible_stateless_reset_token);
+}
+
+bool QuicFramer::HasEncrypterOfEncryptionLevel(EncryptionLevel level) const {
+ return encrypter_[level] != nullptr;
+}
+
+bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
+ QuicDataWriter* writer,
+ size_t* length_field_offset) {
+ if (transport_version() > QUIC_VERSION_43) {
+ return AppendIetfPacketHeader(header, writer, length_field_offset);
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Appending header: " << header;
+ uint8_t public_flags = 0;
+ if (header.reset_flag) {
+ public_flags |= PACKET_PUBLIC_FLAGS_RST;
+ }
+ if (header.version_flag) {
+ public_flags |= PACKET_PUBLIC_FLAGS_VERSION;
+ }
+
+ public_flags |= GetPacketNumberFlags(header.packet_number_length)
+ << kPublicHeaderSequenceNumberShift;
+
+ if (header.nonce != nullptr) {
+ DCHECK_EQ(Perspective::IS_SERVER, perspective_);
+ public_flags |= PACKET_PUBLIC_FLAGS_NONCE;
+ }
+ DCHECK_EQ(CONNECTION_ID_ABSENT, header.source_connection_id_included);
+ switch (header.destination_connection_id_included) {
+ case CONNECTION_ID_ABSENT:
+ if (!writer->WriteUInt8(public_flags |
+ PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID)) {
+ return false;
+ }
+ break;
+ case CONNECTION_ID_PRESENT:
+ QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(
+ header.destination_connection_id, transport_version()))
+ << "AppendPacketHeader: attempted to use connection ID "
+ << header.destination_connection_id
+ << " which is invalid with version "
+ << QuicVersionToString(transport_version());
+
+ public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID;
+ if (perspective_ == Perspective::IS_CLIENT) {
+ public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD;
+ }
+ if (!writer->WriteUInt8(public_flags) ||
+ !writer->WriteConnectionId(header.destination_connection_id)) {
+ return false;
+ }
+ break;
+ }
+ last_serialized_connection_id_ = header.destination_connection_id;
+
+ if (header.version_flag) {
+ DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
+ QuicVersionLabel version_label = CreateQuicVersionLabel(version_);
+ // TODO(rch): Use WriteUInt32() once QUIC_VERSION_35 is removed.
+ if (!writer->WriteTag(QuicEndian::NetToHost32(version_label))) {
+ return false;
+ }
+
+ QUIC_DVLOG(1) << ENDPOINT << "label = '"
+ << QuicVersionLabelToString(version_label) << "'";
+ }
+
+ if (header.nonce != nullptr &&
+ !writer->WriteBytes(header.nonce, kDiversificationNonceSize)) {
+ return false;
+ }
+
+ if (!AppendPacketNumber(header.packet_number_length, header.packet_number,
+ writer)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::AppendIetfHeaderTypeByte(const QuicPacketHeader& header,
+ QuicDataWriter* writer) {
+ uint8_t type = 0;
+ if (transport_version() > QUIC_VERSION_44) {
+ if (header.version_flag) {
+ type = static_cast<uint8_t>(
+ FLAGS_LONG_HEADER | FLAGS_FIXED_BIT |
+ LongHeaderTypeToOnWireValue(transport_version(),
+ header.long_packet_type) |
+ PacketNumberLengthToOnWireValue(transport_version(),
+ header.packet_number_length));
+ } else {
+ type = static_cast<uint8_t>(
+ FLAGS_FIXED_BIT |
+ PacketNumberLengthToOnWireValue(transport_version(),
+ header.packet_number_length));
+ }
+ return writer->WriteUInt8(type);
+ }
+
+ if (header.version_flag) {
+ type = static_cast<uint8_t>(
+ FLAGS_LONG_HEADER | LongHeaderTypeToOnWireValue(
+ transport_version(), header.long_packet_type));
+ DCHECK_EQ(PACKET_4BYTE_PACKET_NUMBER, header.packet_number_length);
+ } else {
+ type |= FLAGS_SHORT_HEADER_RESERVED_1;
+ type |= FLAGS_SHORT_HEADER_RESERVED_2;
+ DCHECK_GE(PACKET_4BYTE_PACKET_NUMBER, header.packet_number_length);
+ type |= PacketNumberLengthToOnWireValue(transport_version(),
+ header.packet_number_length);
+ }
+ return writer->WriteUInt8(type);
+}
+
+bool QuicFramer::AppendIetfPacketHeader(const QuicPacketHeader& header,
+ QuicDataWriter* writer,
+ size_t* length_field_offset) {
+ QUIC_DVLOG(1) << ENDPOINT << "Appending IETF header: " << header;
+ QUIC_BUG_IF(!QuicUtils::IsConnectionIdValidForVersion(
+ header.destination_connection_id, transport_version()))
+ << "AppendIetfPacketHeader: attempted to use connection ID "
+ << header.destination_connection_id << " which is invalid with version "
+ << QuicVersionToString(transport_version());
+ if (!AppendIetfHeaderTypeByte(header, writer)) {
+ return false;
+ }
+
+ if (header.version_flag) {
+ // Append version for long header.
+ QuicVersionLabel version_label = CreateQuicVersionLabel(version_);
+ // TODO(rch): Use WriteUInt32() once QUIC_VERSION_35 is removed.
+ if (!writer->WriteTag(QuicEndian::NetToHost32(version_label))) {
+ return false;
+ }
+ }
+
+ // Append connection ID.
+ if (!QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ transport_version()) &&
+ !GetQuicReloadableFlag(quic_use_new_append_connection_id)) {
+ if (!AppendIetfConnectionId(
+ header.version_flag, header.destination_connection_id,
+ GetIncludedDestinationConnectionIdLength(header),
+ header.source_connection_id,
+ GetIncludedSourceConnectionIdLength(header), writer)) {
+ return false;
+ }
+ } else {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_new_append_connection_id, 2, 2);
+ if (!AppendIetfConnectionIdsNew(
+ header.version_flag,
+ header.destination_connection_id_included != CONNECTION_ID_ABSENT
+ ? header.destination_connection_id
+ : EmptyQuicConnectionId(),
+ header.source_connection_id_included != CONNECTION_ID_ABSENT
+ ? header.source_connection_id
+ : EmptyQuicConnectionId(),
+ writer)) {
+ return false;
+ }
+ }
+ last_serialized_connection_id_ = header.destination_connection_id;
+
+ if (QuicVersionHasLongHeaderLengths(transport_version()) &&
+ header.version_flag) {
+ if (header.long_packet_type == INITIAL) {
+ // Write retry token length.
+ if (!writer->WriteVarInt62(header.retry_token.length(),
+ header.retry_token_length_length)) {
+ return false;
+ }
+ // Write retry token.
+ if (!header.retry_token.empty() &&
+ !writer->WriteStringPiece(header.retry_token)) {
+ return false;
+ }
+ }
+ if (length_field_offset != nullptr) {
+ *length_field_offset = writer->length();
+ }
+ // Add fake length to reserve two bytes to add length in later.
+ writer->WriteVarInt62(256);
+ } else if (length_field_offset != nullptr) {
+ *length_field_offset = 0;
+ }
+
+ // Append packet number.
+ if (!AppendPacketNumber(header.packet_number_length, header.packet_number,
+ writer)) {
+ return false;
+ }
+
+ if (!header.version_flag) {
+ return true;
+ }
+
+ if (header.nonce != nullptr) {
+ DCHECK(header.version_flag);
+ DCHECK_EQ(ZERO_RTT_PROTECTED, header.long_packet_type);
+ DCHECK_EQ(Perspective::IS_SERVER, perspective_);
+ if (!writer->WriteBytes(header.nonce, kDiversificationNonceSize)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+const QuicTime::Delta QuicFramer::CalculateTimestampFromWire(
+ uint32_t time_delta_us) {
+ // The new time_delta might have wrapped to the next epoch, or it
+ // might have reverse wrapped to the previous epoch, or it might
+ // remain in the same epoch. Select the time closest to the previous
+ // time.
+ //
+ // epoch_delta is the delta between epochs. A delta is 4 bytes of
+ // microseconds.
+ const uint64_t epoch_delta = UINT64_C(1) << 32;
+ uint64_t epoch = last_timestamp_.ToMicroseconds() & ~(epoch_delta - 1);
+ // Wrapping is safe here because a wrapped value will not be ClosestTo below.
+ uint64_t prev_epoch = epoch - epoch_delta;
+ uint64_t next_epoch = epoch + epoch_delta;
+
+ uint64_t time = ClosestTo(
+ last_timestamp_.ToMicroseconds(), epoch + time_delta_us,
+ ClosestTo(last_timestamp_.ToMicroseconds(), prev_epoch + time_delta_us,
+ next_epoch + time_delta_us));
+
+ return QuicTime::Delta::FromMicroseconds(time);
+}
+
+uint64_t QuicFramer::CalculatePacketNumberFromWire(
+ QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber base_packet_number,
+ uint64_t packet_number) const {
+ // The new packet number might have wrapped to the next epoch, or
+ // it might have reverse wrapped to the previous epoch, or it might
+ // remain in the same epoch. Select the packet number closest to the
+ // next expected packet number, the previous packet number plus 1.
+
+ // epoch_delta is the delta between epochs the packet number was serialized
+ // with, so the correct value is likely the same epoch as the last sequence
+ // number or an adjacent epoch.
+ if (!base_packet_number.IsInitialized()) {
+ return packet_number;
+ }
+ const uint64_t epoch_delta = UINT64_C(1) << (8 * packet_number_length);
+ uint64_t next_packet_number = base_packet_number.ToUint64() + 1;
+ uint64_t epoch = base_packet_number.ToUint64() & ~(epoch_delta - 1);
+ uint64_t prev_epoch = epoch - epoch_delta;
+ uint64_t next_epoch = epoch + epoch_delta;
+
+ return ClosestTo(next_packet_number, epoch + packet_number,
+ ClosestTo(next_packet_number, prev_epoch + packet_number,
+ next_epoch + packet_number));
+}
+
+bool QuicFramer::ProcessPublicHeader(QuicDataReader* reader,
+ bool packet_has_ietf_packet_header,
+ QuicPacketHeader* header) {
+ if (packet_has_ietf_packet_header) {
+ return ProcessIetfPacketHeader(reader, header);
+ }
+ uint8_t public_flags;
+ if (!reader->ReadBytes(&public_flags, 1)) {
+ set_detailed_error("Unable to read public flags.");
+ return false;
+ }
+
+ header->reset_flag = (public_flags & PACKET_PUBLIC_FLAGS_RST) != 0;
+ header->version_flag = (public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0;
+
+ if (validate_flags_ && !header->version_flag &&
+ public_flags > PACKET_PUBLIC_FLAGS_MAX) {
+ set_detailed_error("Illegal public flags value.");
+ return false;
+ }
+
+ if (header->reset_flag && header->version_flag) {
+ set_detailed_error("Got version flag in reset packet");
+ return false;
+ }
+
+ switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) {
+ case PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID:
+ if (!reader->ReadConnectionId(&header->destination_connection_id,
+ kQuicDefaultConnectionIdLength)) {
+ set_detailed_error("Unable to read ConnectionId.");
+ return false;
+ }
+ header->destination_connection_id_included = CONNECTION_ID_PRESENT;
+ break;
+ case PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID:
+ header->destination_connection_id_included = CONNECTION_ID_ABSENT;
+ header->destination_connection_id = last_serialized_connection_id_;
+ break;
+ }
+
+ header->packet_number_length = ReadSequenceNumberLength(
+ public_flags >> kPublicHeaderSequenceNumberShift);
+
+ // Read the version only if the packet is from the client.
+ // version flag from the server means version negotiation packet.
+ if (header->version_flag && perspective_ == Perspective::IS_SERVER) {
+ QuicVersionLabel version_label;
+ if (!reader->ReadTag(&version_label)) {
+ set_detailed_error("Unable to read protocol version.");
+ return false;
+ }
+ // TODO(rch): Use ReadUInt32() once QUIC_VERSION_35 is removed.
+ version_label = QuicEndian::NetToHost32(version_label);
+
+ // If the version from the new packet is the same as the version of this
+ // framer, then the public flags should be set to something we understand.
+ // If not, this raises an error.
+ last_version_label_ = version_label;
+ ParsedQuicVersion version = ParseQuicVersionLabel(version_label);
+ if (version == version_ && public_flags > PACKET_PUBLIC_FLAGS_MAX) {
+ set_detailed_error("Illegal public flags value.");
+ return false;
+ }
+ header->version = version;
+ }
+
+ // A nonce should only be present in packets from the server to the client,
+ // which are neither version negotiation nor public reset packets.
+ if (public_flags & PACKET_PUBLIC_FLAGS_NONCE &&
+ !(public_flags & PACKET_PUBLIC_FLAGS_VERSION) &&
+ !(public_flags & PACKET_PUBLIC_FLAGS_RST) &&
+ // The nonce flag from a client is ignored and is assumed to be an older
+ // client indicating an eight-byte connection ID.
+ perspective_ == Perspective::IS_CLIENT) {
+ if (!reader->ReadBytes(reinterpret_cast<uint8_t*>(last_nonce_.data()),
+ last_nonce_.size())) {
+ set_detailed_error("Unable to read nonce.");
+ return false;
+ }
+ header->nonce = &last_nonce_;
+ } else {
+ header->nonce = nullptr;
+ }
+
+ return true;
+}
+
+// static
+QuicPacketNumberLength QuicFramer::GetMinPacketNumberLength(
+ QuicTransportVersion version,
+ QuicPacketNumber packet_number) {
+ DCHECK(packet_number.IsInitialized());
+ if (packet_number < QuicPacketNumber(1 << (PACKET_1BYTE_PACKET_NUMBER * 8))) {
+ return PACKET_1BYTE_PACKET_NUMBER;
+ } else if (packet_number <
+ QuicPacketNumber(1 << (PACKET_2BYTE_PACKET_NUMBER * 8))) {
+ return PACKET_2BYTE_PACKET_NUMBER;
+ } else if (packet_number <
+ QuicPacketNumber(UINT64_C(1)
+ << (PACKET_4BYTE_PACKET_NUMBER * 8))) {
+ return PACKET_4BYTE_PACKET_NUMBER;
+ } else {
+ return PACKET_6BYTE_PACKET_NUMBER;
+ }
+}
+
+// static
+uint8_t QuicFramer::GetPacketNumberFlags(
+ QuicPacketNumberLength packet_number_length) {
+ switch (packet_number_length) {
+ case PACKET_1BYTE_PACKET_NUMBER:
+ return PACKET_FLAGS_1BYTE_PACKET;
+ case PACKET_2BYTE_PACKET_NUMBER:
+ return PACKET_FLAGS_2BYTE_PACKET;
+ case PACKET_4BYTE_PACKET_NUMBER:
+ return PACKET_FLAGS_4BYTE_PACKET;
+ case PACKET_6BYTE_PACKET_NUMBER:
+ case PACKET_8BYTE_PACKET_NUMBER:
+ return PACKET_FLAGS_8BYTE_PACKET;
+ default:
+ QUIC_BUG << "Unreachable case statement.";
+ return PACKET_FLAGS_8BYTE_PACKET;
+ }
+}
+
+// static
+QuicFramer::AckFrameInfo QuicFramer::GetAckFrameInfo(
+ const QuicAckFrame& frame) {
+ AckFrameInfo new_ack_info;
+ if (frame.packets.Empty()) {
+ return new_ack_info;
+ }
+ // The first block is the last interval. It isn't encoded with the gap-length
+ // encoding, so skip it.
+ new_ack_info.first_block_length = frame.packets.LastIntervalLength();
+ auto itr = frame.packets.rbegin();
+ QuicPacketNumber previous_start = itr->min();
+ new_ack_info.max_block_length = PacketNumberIntervalLength(*itr);
+ ++itr;
+
+ // Don't do any more work after getting information for 256 ACK blocks; any
+ // more can't be encoded anyway.
+ for (; itr != frame.packets.rend() &&
+ new_ack_info.num_ack_blocks < std::numeric_limits<uint8_t>::max();
+ previous_start = itr->min(), ++itr) {
+ const auto& interval = *itr;
+ const QuicPacketCount total_gap = previous_start - interval.max();
+ new_ack_info.num_ack_blocks +=
+ (total_gap + std::numeric_limits<uint8_t>::max() - 1) /
+ std::numeric_limits<uint8_t>::max();
+ new_ack_info.max_block_length = std::max(
+ new_ack_info.max_block_length, PacketNumberIntervalLength(interval));
+ }
+ return new_ack_info;
+}
+
+bool QuicFramer::ProcessUnauthenticatedHeader(QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header) {
+ QuicPacketNumber base_packet_number;
+ if (supports_multiple_packet_number_spaces_) {
+ base_packet_number =
+ largest_decrypted_packet_numbers_[GetPacketNumberSpace(*header)];
+ } else {
+ base_packet_number = largest_packet_number_;
+ }
+ uint64_t full_packet_number;
+ if (!ProcessAndCalculatePacketNumber(
+ encrypted_reader, header->packet_number_length, base_packet_number,
+ &full_packet_number)) {
+ set_detailed_error("Unable to read packet number.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ if (!IsValidFullPacketNumber(full_packet_number, transport_version())) {
+ set_detailed_error("packet numbers cannot be 0.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ header->packet_number = QuicPacketNumber(full_packet_number);
+
+ if (!visitor_->OnUnauthenticatedHeader(*header)) {
+ set_detailed_error(
+ "Visitor asked to stop processing of unauthenticated header.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessIetfHeaderTypeByte(QuicDataReader* reader,
+ QuicPacketHeader* header) {
+ uint8_t type;
+ if (!reader->ReadBytes(&type, 1)) {
+ set_detailed_error("Unable to read type.");
+ return false;
+ }
+ // Determine whether this is a long or short header.
+ header->form = type & FLAGS_LONG_HEADER ? IETF_QUIC_LONG_HEADER_PACKET
+ : IETF_QUIC_SHORT_HEADER_PACKET;
+ if (header->form == IETF_QUIC_LONG_HEADER_PACKET) {
+ // Version is always present in long headers.
+ header->version_flag = true;
+ // Long header packets received by client must include 8-byte source
+ // connection ID, and those received by server must include 8-byte
+ // destination connection ID.
+ header->destination_connection_id_included =
+ perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_ABSENT
+ : CONNECTION_ID_PRESENT;
+ header->source_connection_id_included =
+ perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_PRESENT
+ : CONNECTION_ID_ABSENT;
+ // Read version tag.
+ QuicVersionLabel version_label;
+ if (!reader->ReadTag(&version_label)) {
+ set_detailed_error("Unable to read protocol version.");
+ return false;
+ }
+ // TODO(rch): Use ReadUInt32() once QUIC_VERSION_35 is removed.
+ version_label = QuicEndian::NetToHost32(version_label);
+ if (!version_label) {
+ // Version label is 0 indicating this is a version negotiation packet.
+ header->long_packet_type = VERSION_NEGOTIATION;
+ } else {
+ header->version = ParseQuicVersionLabel(version_label);
+ if (header->version.transport_version != QUIC_VERSION_UNSUPPORTED) {
+ if (header->version.transport_version > QUIC_VERSION_44 &&
+ !(type & FLAGS_FIXED_BIT)) {
+ set_detailed_error("Fixed bit is 0 in long header.");
+ return false;
+ }
+ if (!GetLongHeaderType(header->version.transport_version, type,
+ &header->long_packet_type)) {
+ set_detailed_error("Illegal long header type value.");
+ return false;
+ }
+ header->packet_number_length = GetLongHeaderPacketNumberLength(
+ header->version.transport_version, type);
+ }
+ }
+ if (header->long_packet_type != VERSION_NEGOTIATION) {
+ // Do not save version of version negotiation packet.
+ last_version_label_ = version_label;
+ }
+
+ QUIC_DVLOG(1) << ENDPOINT << "Received IETF long header: "
+ << QuicUtils::QuicLongHeaderTypetoString(
+ header->long_packet_type);
+ return true;
+ }
+
+ QUIC_DVLOG(1) << ENDPOINT << "Received IETF short header";
+ // Version is not present in short headers.
+ header->version_flag = false;
+ // Connection ID length depends on the perspective. Client does not expect
+ // destination connection ID, and server expects destination connection ID.
+ header->destination_connection_id_included =
+ perspective_ == Perspective::IS_CLIENT ? CONNECTION_ID_ABSENT
+ : CONNECTION_ID_PRESENT;
+ header->source_connection_id_included = CONNECTION_ID_ABSENT;
+ if (infer_packet_header_type_from_version_ &&
+ transport_version() > QUIC_VERSION_44 && !(type & FLAGS_FIXED_BIT)) {
+ set_detailed_error("Fixed bit is 0 in short header.");
+ return false;
+ }
+ if (!GetShortHeaderPacketNumberLength(transport_version(), type,
+ infer_packet_header_type_from_version_,
+ &header->packet_number_length)) {
+ set_detailed_error("Illegal short header type value.");
+ return false;
+ }
+ QUIC_DVLOG(1) << "packet_number_length = " << header->packet_number_length;
+ return true;
+}
+
+bool QuicFramer::ProcessIetfPacketHeader(QuicDataReader* reader,
+ QuicPacketHeader* header) {
+ if (!ProcessIetfHeaderTypeByte(reader, header)) {
+ return false;
+ }
+
+ uint8_t destination_connection_id_length =
+ header->destination_connection_id_included == CONNECTION_ID_PRESENT
+ ? expected_connection_id_length_
+ : 0;
+ uint8_t source_connection_id_length =
+ header->source_connection_id_included == CONNECTION_ID_PRESENT
+ ? expected_connection_id_length_
+ : 0;
+ if (header->form == IETF_QUIC_LONG_HEADER_PACKET) {
+ // Read and validate connection ID length.
+ uint8_t connection_id_lengths_byte;
+ if (!reader->ReadBytes(&connection_id_lengths_byte, 1)) {
+ set_detailed_error("Unable to read ConnectionId length.");
+ return false;
+ }
+ uint8_t dcil =
+ (connection_id_lengths_byte & kDestinationConnectionIdLengthMask) >> 4;
+ if (dcil != 0) {
+ dcil += kConnectionIdLengthAdjustment;
+ }
+ if (should_update_expected_connection_id_length_ &&
+ expected_connection_id_length_ != dcil) {
+ QUIC_DVLOG(1) << ENDPOINT << "Updating expected_connection_id_length: "
+ << static_cast<int>(expected_connection_id_length_)
+ << " -> " << static_cast<int>(dcil);
+ expected_connection_id_length_ = dcil;
+ }
+ uint8_t scil = connection_id_lengths_byte & kSourceConnectionIdLengthMask;
+ if (scil != 0) {
+ scil += kConnectionIdLengthAdjustment;
+ }
+ if ((dcil != destination_connection_id_length ||
+ scil != source_connection_id_length) &&
+ !should_update_expected_connection_id_length_ &&
+ !QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ header->version.transport_version)) {
+ // TODO(dschinazi): use the framer's version once the
+ // OnProtocolVersionMismatch call is moved to before this is run.
+ QUIC_DVLOG(1) << "dcil: " << static_cast<uint32_t>(dcil)
+ << ", scil: " << static_cast<uint32_t>(scil);
+ set_detailed_error("Invalid ConnectionId length.");
+ return false;
+ }
+ destination_connection_id_length = dcil;
+ source_connection_id_length = scil;
+ }
+
+ DCHECK_LE(destination_connection_id_length, kQuicMaxConnectionIdLength);
+ DCHECK_LE(source_connection_id_length, kQuicMaxConnectionIdLength);
+
+ // Read connection ID.
+ if (!reader->ReadConnectionId(&header->destination_connection_id,
+ destination_connection_id_length)) {
+ set_detailed_error("Unable to read Destination ConnectionId.");
+ return false;
+ }
+
+ if (!reader->ReadConnectionId(&header->source_connection_id,
+ source_connection_id_length)) {
+ set_detailed_error("Unable to read Source ConnectionId.");
+ return false;
+ }
+
+ if (header->source_connection_id_included == CONNECTION_ID_PRESENT) {
+ // Set destination connection ID to source connection ID.
+ DCHECK_EQ(EmptyQuicConnectionId(), header->destination_connection_id);
+ header->destination_connection_id = header->source_connection_id;
+ } else if (header->destination_connection_id_included ==
+ CONNECTION_ID_ABSENT) {
+ header->destination_connection_id = last_serialized_connection_id_;
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessAndCalculatePacketNumber(
+ QuicDataReader* reader,
+ QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber base_packet_number,
+ uint64_t* packet_number) {
+ uint64_t wire_packet_number;
+ if (!reader->ReadBytesToUInt64(packet_number_length, &wire_packet_number)) {
+ return false;
+ }
+
+ // TODO(ianswett): Explore the usefulness of trying multiple packet numbers
+ // in case the first guess is incorrect.
+ *packet_number = CalculatePacketNumberFromWire(
+ packet_number_length, base_packet_number, wire_packet_number);
+ return true;
+}
+
+bool QuicFramer::ProcessFrameData(QuicDataReader* reader,
+ const QuicPacketHeader& header) {
+ DCHECK_NE(QUIC_VERSION_99, version_.transport_version)
+ << "Version 99 negotiated, but not processing frames as version 99.";
+ if (reader->IsDoneReading()) {
+ set_detailed_error("Packet has no frames.");
+ return RaiseError(QUIC_MISSING_PAYLOAD);
+ }
+ while (!reader->IsDoneReading()) {
+ uint8_t frame_type;
+ if (!reader->ReadBytes(&frame_type, 1)) {
+ set_detailed_error("Unable to read frame type.");
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ const uint8_t special_mask = transport_version() <= QUIC_VERSION_44
+ ? kQuicFrameTypeBrokenMask
+ : kQuicFrameTypeSpecialMask;
+ if (frame_type & special_mask) {
+ // Stream Frame
+ if (frame_type & kQuicFrameTypeStreamMask) {
+ QuicStreamFrame frame;
+ if (!ProcessStreamFrame(reader, frame_type, &frame)) {
+ return RaiseError(QUIC_INVALID_STREAM_DATA);
+ }
+ if (!visitor_->OnStreamFrame(frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ // Ack Frame
+ if (frame_type & kQuicFrameTypeAckMask) {
+ if (!ProcessAckFrame(reader, frame_type)) {
+ return RaiseError(QUIC_INVALID_ACK_DATA);
+ }
+ continue;
+ }
+
+ // This was a special frame type that did not match any
+ // of the known ones. Error.
+ set_detailed_error("Illegal frame type.");
+ QUIC_DLOG(WARNING) << ENDPOINT << "Illegal frame type: "
+ << static_cast<int>(frame_type);
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+
+ switch (frame_type) {
+ case PADDING_FRAME: {
+ QuicPaddingFrame frame;
+ ProcessPaddingFrame(reader, &frame);
+ if (!visitor_->OnPaddingFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case RST_STREAM_FRAME: {
+ QuicRstStreamFrame frame;
+ if (!ProcessRstStreamFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_RST_STREAM_DATA);
+ }
+ if (!visitor_->OnRstStreamFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case CONNECTION_CLOSE_FRAME: {
+ QuicConnectionCloseFrame frame;
+ if (!ProcessConnectionCloseFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA);
+ }
+
+ if (!visitor_->OnConnectionCloseFrame(frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case GOAWAY_FRAME: {
+ QuicGoAwayFrame goaway_frame;
+ if (!ProcessGoAwayFrame(reader, &goaway_frame)) {
+ return RaiseError(QUIC_INVALID_GOAWAY_DATA);
+ }
+ if (!visitor_->OnGoAwayFrame(goaway_frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case WINDOW_UPDATE_FRAME: {
+ QuicWindowUpdateFrame window_update_frame;
+ if (!ProcessWindowUpdateFrame(reader, &window_update_frame)) {
+ return RaiseError(QUIC_INVALID_WINDOW_UPDATE_DATA);
+ }
+ if (!visitor_->OnWindowUpdateFrame(window_update_frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case BLOCKED_FRAME: {
+ QuicBlockedFrame blocked_frame;
+ if (!ProcessBlockedFrame(reader, &blocked_frame)) {
+ return RaiseError(QUIC_INVALID_BLOCKED_DATA);
+ }
+ if (!visitor_->OnBlockedFrame(blocked_frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case STOP_WAITING_FRAME: {
+ QuicStopWaitingFrame stop_waiting_frame;
+ if (!ProcessStopWaitingFrame(reader, header, &stop_waiting_frame)) {
+ return RaiseError(QUIC_INVALID_STOP_WAITING_DATA);
+ }
+ if (!visitor_->OnStopWaitingFrame(stop_waiting_frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+ case PING_FRAME: {
+ // Ping has no payload.
+ QuicPingFrame ping_frame;
+ if (!visitor_->OnPingFrame(ping_frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+ case IETF_EXTENSION_MESSAGE_NO_LENGTH:
+ QUIC_FALLTHROUGH_INTENDED;
+ case IETF_EXTENSION_MESSAGE: {
+ QuicMessageFrame message_frame;
+ if (!ProcessMessageFrame(reader,
+ frame_type == IETF_EXTENSION_MESSAGE_NO_LENGTH,
+ &message_frame)) {
+ return RaiseError(QUIC_INVALID_MESSAGE_DATA);
+ }
+ if (!visitor_->OnMessageFrame(message_frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case CRYPTO_FRAME: {
+ if (!QuicVersionUsesCryptoFrames(version_.transport_version)) {
+ set_detailed_error("Illegal frame type.");
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ QuicCryptoFrame frame;
+ if (!ProcessCryptoFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ if (!visitor_->OnCryptoFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+
+ default:
+ set_detailed_error("Illegal frame type.");
+ QUIC_DLOG(WARNING) << ENDPOINT << "Illegal frame type: "
+ << static_cast<int>(frame_type);
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessIetfFrameData(QuicDataReader* reader,
+ const QuicPacketHeader& header) {
+ DCHECK_EQ(QUIC_VERSION_99, version_.transport_version)
+ << "Attempt to process frames as IETF frames but version is "
+ << version_.transport_version << ", not 99.";
+ if (reader->IsDoneReading()) {
+ set_detailed_error("Packet has no frames.");
+ return RaiseError(QUIC_MISSING_PAYLOAD);
+ }
+ while (!reader->IsDoneReading()) {
+ uint64_t frame_type;
+ // Will be the number of bytes into which frame_type was encoded.
+ size_t encoded_bytes = reader->BytesRemaining();
+ if (!reader->ReadVarInt62(&frame_type)) {
+ set_detailed_error("Unable to read frame type.");
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+
+ // Is now the number of bytes into which the frame type was encoded.
+ encoded_bytes -= reader->BytesRemaining();
+
+ // Check that the frame type is minimally encoded.
+ if (encoded_bytes !=
+ static_cast<size_t>(QuicDataWriter::GetVarInt62Len(frame_type))) {
+ // The frame type was not minimally encoded.
+ set_detailed_error("Frame type not minimally encoded.");
+ return RaiseError(IETF_QUIC_PROTOCOL_VIOLATION);
+ }
+
+ if (IS_IETF_STREAM_FRAME(frame_type)) {
+ QuicStreamFrame frame;
+ if (!ProcessIetfStreamFrame(reader, frame_type, &frame)) {
+ return RaiseError(QUIC_INVALID_STREAM_DATA);
+ }
+ if (!visitor_->OnStreamFrame(frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ } else {
+ switch (frame_type) {
+ case IETF_PADDING: {
+ QuicPaddingFrame frame;
+ ProcessPaddingFrame(reader, &frame);
+ if (!visitor_->OnPaddingFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_RST_STREAM: {
+ QuicRstStreamFrame frame;
+ if (!ProcessIetfResetStreamFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_RST_STREAM_DATA);
+ }
+ if (!visitor_->OnRstStreamFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_APPLICATION_CLOSE:
+ case IETF_CONNECTION_CLOSE: {
+ QuicConnectionCloseFrame frame;
+ if (!ProcessIetfConnectionCloseFrame(
+ reader,
+ (frame_type == IETF_CONNECTION_CLOSE)
+ ? IETF_QUIC_TRANSPORT_CONNECTION_CLOSE
+ : IETF_QUIC_APPLICATION_CONNECTION_CLOSE,
+ &frame)) {
+ return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA);
+ }
+ if (!visitor_->OnConnectionCloseFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_MAX_DATA: {
+ QuicWindowUpdateFrame frame;
+ if (!ProcessMaxDataFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_MAX_DATA_FRAME_DATA);
+ }
+ // TODO(fkastenholz): Or should we create a new visitor function,
+ // OnMaxDataFrame()?
+ if (!visitor_->OnWindowUpdateFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_MAX_STREAM_DATA: {
+ QuicWindowUpdateFrame frame;
+ if (!ProcessMaxStreamDataFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA);
+ }
+ // TODO(fkastenholz): Or should we create a new visitor function,
+ // OnMaxStreamDataFrame()?
+ if (!visitor_->OnWindowUpdateFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_MAX_STREAMS_BIDIRECTIONAL:
+ case IETF_MAX_STREAMS_UNIDIRECTIONAL: {
+ QuicMaxStreamIdFrame frame;
+ if (!ProcessMaxStreamsFrame(reader, &frame, frame_type)) {
+ return RaiseError(QUIC_MAX_STREAM_ID_DATA);
+ }
+ QUIC_CODE_COUNT_N(max_stream_id_received, 1, 2);
+ if (!visitor_->OnMaxStreamIdFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_PING: {
+ // Ping has no payload.
+ QuicPingFrame ping_frame;
+ if (!visitor_->OnPingFrame(ping_frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_BLOCKED: {
+ QuicBlockedFrame frame;
+ if (!ProcessIetfBlockedFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_BLOCKED_DATA);
+ }
+ if (!visitor_->OnBlockedFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_STREAM_BLOCKED: {
+ QuicBlockedFrame frame;
+ if (!ProcessStreamBlockedFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_STREAM_BLOCKED_DATA);
+ }
+ if (!visitor_->OnBlockedFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_STREAMS_BLOCKED_UNIDIRECTIONAL:
+ case IETF_STREAMS_BLOCKED_BIDIRECTIONAL: {
+ QuicStreamIdBlockedFrame frame;
+ if (!ProcessStreamsBlockedFrame(reader, &frame, frame_type)) {
+ return RaiseError(QUIC_STREAM_ID_BLOCKED_DATA);
+ }
+ QUIC_CODE_COUNT_N(stream_id_blocked_received, 1, 2);
+ if (!visitor_->OnStreamIdBlockedFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_NEW_CONNECTION_ID: {
+ QuicNewConnectionIdFrame frame;
+ if (!ProcessNewConnectionIdFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_NEW_CONNECTION_ID_DATA);
+ }
+ if (!visitor_->OnNewConnectionIdFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_RETIRE_CONNECTION_ID: {
+ QuicRetireConnectionIdFrame frame;
+ if (!ProcessRetireConnectionIdFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_RETIRE_CONNECTION_ID_DATA);
+ }
+ if (!visitor_->OnRetireConnectionIdFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_NEW_TOKEN: {
+ QuicNewTokenFrame frame;
+ if (!ProcessNewTokenFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_NEW_TOKEN);
+ }
+ if (!visitor_->OnNewTokenFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_STOP_SENDING: {
+ QuicStopSendingFrame frame;
+ if (!ProcessStopSendingFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_STOP_SENDING_FRAME_DATA);
+ }
+ if (!visitor_->OnStopSendingFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_ACK_ECN:
+ case IETF_ACK: {
+ QuicAckFrame frame;
+ if (!ProcessIetfAckFrame(reader, frame_type, &frame)) {
+ return RaiseError(QUIC_INVALID_ACK_DATA);
+ }
+ break;
+ }
+ case IETF_PATH_CHALLENGE: {
+ QuicPathChallengeFrame frame;
+ if (!ProcessPathChallengeFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_PATH_CHALLENGE_DATA);
+ }
+ if (!visitor_->OnPathChallengeFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_PATH_RESPONSE: {
+ QuicPathResponseFrame frame;
+ if (!ProcessPathResponseFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_PATH_RESPONSE_DATA);
+ }
+ if (!visitor_->OnPathResponseFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_EXTENSION_MESSAGE_NO_LENGTH:
+ QUIC_FALLTHROUGH_INTENDED;
+ case IETF_EXTENSION_MESSAGE: {
+ QuicMessageFrame message_frame;
+ if (!ProcessMessageFrame(
+ reader, frame_type == IETF_EXTENSION_MESSAGE_NO_LENGTH,
+ &message_frame)) {
+ return RaiseError(QUIC_INVALID_MESSAGE_DATA);
+ }
+ if (!visitor_->OnMessageFrame(message_frame)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+ case IETF_CRYPTO: {
+ QuicCryptoFrame frame;
+ if (!ProcessCryptoFrame(reader, &frame)) {
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ if (!visitor_->OnCryptoFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
+
+ default:
+ set_detailed_error("Illegal frame type.");
+ QUIC_DLOG(WARNING)
+ << ENDPOINT
+ << "Illegal frame type: " << static_cast<int>(frame_type);
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ }
+ }
+ return true;
+}
+
+namespace {
+// Create a mask that sets the last |num_bits| to 1 and the rest to 0.
+inline uint8_t GetMaskFromNumBits(uint8_t num_bits) {
+ return (1u << num_bits) - 1;
+}
+
+// Extract |num_bits| from |flags| offset by |offset|.
+uint8_t ExtractBits(uint8_t flags, uint8_t num_bits, uint8_t offset) {
+ return (flags >> offset) & GetMaskFromNumBits(num_bits);
+}
+
+// Extract the bit at position |offset| from |flags| as a bool.
+bool ExtractBit(uint8_t flags, uint8_t offset) {
+ return ((flags >> offset) & GetMaskFromNumBits(1)) != 0;
+}
+
+// Set |num_bits|, offset by |offset| to |val| in |flags|.
+void SetBits(uint8_t* flags, uint8_t val, uint8_t num_bits, uint8_t offset) {
+ DCHECK_LE(val, GetMaskFromNumBits(num_bits));
+ *flags |= val << offset;
+}
+
+// Set the bit at position |offset| to |val| in |flags|.
+void SetBit(uint8_t* flags, bool val, uint8_t offset) {
+ SetBits(flags, val ? 1 : 0, 1, offset);
+}
+} // namespace
+
+bool QuicFramer::ProcessStreamFrame(QuicDataReader* reader,
+ uint8_t frame_type,
+ QuicStreamFrame* frame) {
+ uint8_t stream_flags = frame_type;
+
+ uint8_t stream_id_length = 0;
+ uint8_t offset_length = 4;
+ bool has_data_length = true;
+ stream_flags &= ~kQuicFrameTypeStreamMask;
+
+ // Read from right to left: StreamID, Offset, Data Length, Fin.
+ stream_id_length = (stream_flags & kQuicStreamIDLengthMask) + 1;
+ stream_flags >>= kQuicStreamIdShift;
+
+ offset_length = (stream_flags & kQuicStreamOffsetMask);
+ // There is no encoding for 1 byte, only 0 and 2 through 8.
+ if (offset_length > 0) {
+ offset_length += 1;
+ }
+ stream_flags >>= kQuicStreamShift;
+
+ has_data_length =
+ (stream_flags & kQuicStreamDataLengthMask) == kQuicStreamDataLengthMask;
+ stream_flags >>= kQuicStreamDataLengthShift;
+
+ frame->fin = (stream_flags & kQuicStreamFinMask) == kQuicStreamFinShift;
+
+ uint64_t stream_id;
+ if (!reader->ReadBytesToUInt64(stream_id_length, &stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+ frame->stream_id = static_cast<QuicStreamId>(stream_id);
+
+ if (!reader->ReadBytesToUInt64(offset_length, &frame->offset)) {
+ set_detailed_error("Unable to read offset.");
+ return false;
+ }
+
+ // TODO(ianswett): Don't use QuicStringPiece as an intermediary.
+ QuicStringPiece data;
+ if (has_data_length) {
+ if (!reader->ReadStringPiece16(&data)) {
+ set_detailed_error("Unable to read frame data.");
+ return false;
+ }
+ } else {
+ if (!reader->ReadStringPiece(&data, reader->BytesRemaining())) {
+ set_detailed_error("Unable to read frame data.");
+ return false;
+ }
+ }
+ frame->data_buffer = data.data();
+ frame->data_length = static_cast<uint16_t>(data.length());
+
+ return true;
+}
+
+bool QuicFramer::ProcessIetfStreamFrame(QuicDataReader* reader,
+ uint8_t frame_type,
+ QuicStreamFrame* frame) {
+ // Read stream id from the frame. It's always present.
+ if (!reader->ReadVarIntStreamId(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ // If we have a data offset, read it. If not, set to 0.
+ if (frame_type & IETF_STREAM_FRAME_OFF_BIT) {
+ if (!reader->ReadVarInt62(&frame->offset)) {
+ set_detailed_error("Unable to read stream data offset.");
+ return false;
+ }
+ } else {
+ // no offset in the frame, ensure it's 0 in the Frame.
+ frame->offset = 0;
+ }
+
+ // If we have a data length, read it. If not, set to 0.
+ if (frame_type & IETF_STREAM_FRAME_LEN_BIT) {
+ QuicIetfStreamDataLength length;
+ if (!reader->ReadVarInt62(&length)) {
+ set_detailed_error("Unable to read stream data length.");
+ return false;
+ }
+ if (length > 0xffff) {
+ set_detailed_error("Stream data length is too large.");
+ return false;
+ }
+ frame->data_length = length;
+ } else {
+ // no length in the frame, it is the number of bytes remaining in the
+ // packet.
+ frame->data_length = reader->BytesRemaining();
+ }
+
+ if (frame_type & IETF_STREAM_FRAME_FIN_BIT) {
+ frame->fin = true;
+ } else {
+ frame->fin = false;
+ }
+
+ // TODO(ianswett): Don't use QuicStringPiece as an intermediary.
+ QuicStringPiece data;
+ if (!reader->ReadStringPiece(&data, frame->data_length)) {
+ set_detailed_error("Unable to read frame data.");
+ return false;
+ }
+ frame->data_buffer = data.data();
+ frame->data_length = static_cast<QuicIetfStreamDataLength>(data.length());
+
+ return true;
+}
+
+bool QuicFramer::ProcessCryptoFrame(QuicDataReader* reader,
+ QuicCryptoFrame* frame) {
+ if (!reader->ReadVarInt62(&frame->offset)) {
+ set_detailed_error("Unable to read crypto data offset.");
+ return false;
+ }
+ uint64_t len;
+ if (!reader->ReadVarInt62(&len) ||
+ len > std::numeric_limits<QuicPacketLength>::max()) {
+ set_detailed_error("Invalid data length.");
+ return false;
+ }
+ frame->data_length = len;
+
+ // TODO(ianswett): Don't use QuicStringPiece as an intermediary.
+ QuicStringPiece data;
+ if (!reader->ReadStringPiece(&data, frame->data_length)) {
+ set_detailed_error("Unable to read frame data.");
+ return false;
+ }
+ frame->data_buffer = data.data();
+ return true;
+}
+
+bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type) {
+ const bool has_ack_blocks =
+ ExtractBit(frame_type, kQuicHasMultipleAckBlocksOffset);
+ uint8_t num_ack_blocks = 0;
+ uint8_t num_received_packets = 0;
+
+ // Determine the two lengths from the frame type: largest acked length,
+ // ack block length.
+ const QuicPacketNumberLength ack_block_length = ReadAckPacketNumberLength(
+ version_.transport_version,
+ ExtractBits(frame_type, kQuicSequenceNumberLengthNumBits,
+ kActBlockLengthOffset));
+ const QuicPacketNumberLength largest_acked_length = ReadAckPacketNumberLength(
+ version_.transport_version,
+ ExtractBits(frame_type, kQuicSequenceNumberLengthNumBits,
+ kLargestAckedOffset));
+
+ uint64_t largest_acked;
+ if (!reader->ReadBytesToUInt64(largest_acked_length, &largest_acked)) {
+ set_detailed_error("Unable to read largest acked.");
+ return false;
+ }
+
+ if (largest_acked < first_sending_packet_number_.ToUint64()) {
+ // Connection always sends packet starting from kFirstSendingPacketNumber >
+ // 0, peer has observed an unsent packet.
+ set_detailed_error("Largest acked is 0.");
+ return false;
+ }
+
+ uint64_t ack_delay_time_us;
+ if (!reader->ReadUFloat16(&ack_delay_time_us)) {
+ set_detailed_error("Unable to read ack delay time.");
+ return false;
+ }
+
+ if (!visitor_->OnAckFrameStart(
+ QuicPacketNumber(largest_acked),
+ ack_delay_time_us == kUFloat16MaxValue
+ ? QuicTime::Delta::Infinite()
+ : QuicTime::Delta::FromMicroseconds(ack_delay_time_us))) {
+ // The visitor suppresses further processing of the packet. Although this is
+ // not a parsing error, returns false as this is in middle of processing an
+ // ack frame,
+ set_detailed_error("Visitor suppresses further processing of ack frame.");
+ return false;
+ }
+
+ if (has_ack_blocks && !reader->ReadUInt8(&num_ack_blocks)) {
+ set_detailed_error("Unable to read num of ack blocks.");
+ return false;
+ }
+
+ uint64_t first_block_length;
+ if (!reader->ReadBytesToUInt64(ack_block_length, &first_block_length)) {
+ set_detailed_error("Unable to read first ack block length.");
+ return false;
+ }
+
+ if (first_block_length == 0) {
+ set_detailed_error("First block length is zero.");
+ return false;
+ }
+ bool first_ack_block_underflow = first_block_length > largest_acked + 1;
+ if (first_block_length + first_sending_packet_number_.ToUint64() >
+ largest_acked + 1) {
+ first_ack_block_underflow = true;
+ }
+ if (first_ack_block_underflow) {
+ set_detailed_error(QuicStrCat("Underflow with first ack block length ",
+ first_block_length, " largest acked is ",
+ largest_acked, ".")
+ .c_str());
+ return false;
+ }
+
+ uint64_t first_received = largest_acked + 1 - first_block_length;
+ if (!visitor_->OnAckRange(QuicPacketNumber(first_received),
+ QuicPacketNumber(largest_acked + 1))) {
+ // The visitor suppresses further processing of the packet. Although
+ // this is not a parsing error, returns false as this is in middle
+ // of processing an ack frame,
+ set_detailed_error("Visitor suppresses further processing of ack frame.");
+ return false;
+ }
+
+ if (num_ack_blocks > 0) {
+ for (size_t i = 0; i < num_ack_blocks; ++i) {
+ uint8_t gap = 0;
+ if (!reader->ReadUInt8(&gap)) {
+ set_detailed_error("Unable to read gap to next ack block.");
+ return false;
+ }
+ uint64_t current_block_length;
+ if (!reader->ReadBytesToUInt64(ack_block_length, &current_block_length)) {
+ set_detailed_error("Unable to ack block length.");
+ return false;
+ }
+ bool ack_block_underflow = first_received < gap + current_block_length;
+ if (first_received < gap + current_block_length +
+ first_sending_packet_number_.ToUint64()) {
+ ack_block_underflow = true;
+ }
+ if (ack_block_underflow) {
+ set_detailed_error(
+ QuicStrCat("Underflow with ack block length ", current_block_length,
+ ", end of block is ", first_received - gap, ".")
+ .c_str());
+ return false;
+ }
+
+ first_received -= (gap + current_block_length);
+ if (current_block_length > 0) {
+ if (!visitor_->OnAckRange(
+ QuicPacketNumber(first_received),
+ QuicPacketNumber(first_received) + current_block_length)) {
+ // The visitor suppresses further processing of the packet. Although
+ // this is not a parsing error, returns false as this is in middle
+ // of processing an ack frame,
+ set_detailed_error(
+ "Visitor suppresses further processing of ack frame.");
+ return false;
+ }
+ }
+ }
+ }
+
+ if (!reader->ReadUInt8(&num_received_packets)) {
+ set_detailed_error("Unable to read num received packets.");
+ return false;
+ }
+
+ if (!ProcessTimestampsInAckFrame(num_received_packets,
+ QuicPacketNumber(largest_acked), reader)) {
+ return false;
+ }
+
+ // Done processing the ACK frame.
+ return visitor_->OnAckFrameEnd(QuicPacketNumber(first_received));
+}
+
+bool QuicFramer::ProcessTimestampsInAckFrame(uint8_t num_received_packets,
+ QuicPacketNumber largest_acked,
+ QuicDataReader* reader) {
+ if (num_received_packets == 0) {
+ return true;
+ }
+ uint8_t delta_from_largest_observed;
+ if (!reader->ReadUInt8(&delta_from_largest_observed)) {
+ set_detailed_error("Unable to read sequence delta in received packets.");
+ return false;
+ }
+
+ if (largest_acked.ToUint64() <= delta_from_largest_observed) {
+ set_detailed_error(QuicStrCat("delta_from_largest_observed too high: ",
+ delta_from_largest_observed,
+ ", largest_acked: ", largest_acked.ToUint64())
+ .c_str());
+ return false;
+ }
+
+ // Time delta from the framer creation.
+ uint32_t time_delta_us;
+ if (!reader->ReadUInt32(&time_delta_us)) {
+ set_detailed_error("Unable to read time delta in received packets.");
+ return false;
+ }
+
+ QuicPacketNumber seq_num = largest_acked - delta_from_largest_observed;
+ if (process_timestamps_) {
+ last_timestamp_ = CalculateTimestampFromWire(time_delta_us);
+
+ visitor_->OnAckTimestamp(seq_num, creation_time_ + last_timestamp_);
+ }
+
+ for (uint8_t i = 1; i < num_received_packets; ++i) {
+ if (!reader->ReadUInt8(&delta_from_largest_observed)) {
+ set_detailed_error("Unable to read sequence delta in received packets.");
+ return false;
+ }
+ if (largest_acked.ToUint64() <= delta_from_largest_observed) {
+ set_detailed_error(
+ QuicStrCat("delta_from_largest_observed too high: ",
+ delta_from_largest_observed,
+ ", largest_acked: ", largest_acked.ToUint64())
+ .c_str());
+ return false;
+ }
+ seq_num = largest_acked - delta_from_largest_observed;
+
+ // Time delta from the previous timestamp.
+ uint64_t incremental_time_delta_us;
+ if (!reader->ReadUFloat16(&incremental_time_delta_us)) {
+ set_detailed_error(
+ "Unable to read incremental time delta in received packets.");
+ return false;
+ }
+
+ if (process_timestamps_) {
+ last_timestamp_ = last_timestamp_ + QuicTime::Delta::FromMicroseconds(
+ incremental_time_delta_us);
+ visitor_->OnAckTimestamp(seq_num, creation_time_ + last_timestamp_);
+ }
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessIetfAckFrame(QuicDataReader* reader,
+ uint64_t frame_type,
+ QuicAckFrame* ack_frame) {
+ uint64_t largest_acked;
+ if (!reader->ReadVarInt62(&largest_acked)) {
+ set_detailed_error("Unable to read largest acked.");
+ return false;
+ }
+ if (largest_acked < first_sending_packet_number_.ToUint64()) {
+ // Connection always sends packet starting from kFirstSendingPacketNumber >
+ // 0, peer has observed an unsent packet.
+ set_detailed_error("Largest acked is 0.");
+ return false;
+ }
+ ack_frame->largest_acked = static_cast<QuicPacketNumber>(largest_acked);
+ uint64_t ack_delay_time_in_us;
+ if (!reader->ReadVarInt62(&ack_delay_time_in_us)) {
+ set_detailed_error("Unable to read ack delay time.");
+ return false;
+ }
+
+ // TODO(fkastenholz) when we get real IETF QUIC, need to get
+ // the currect shift from the transport parameters.
+ if (ack_delay_time_in_us == kVarInt62MaxValue) {
+ ack_frame->ack_delay_time = QuicTime::Delta::Infinite();
+ } else {
+ ack_delay_time_in_us = (ack_delay_time_in_us << kIetfAckTimestampShift);
+ ack_frame->ack_delay_time =
+ QuicTime::Delta::FromMicroseconds(ack_delay_time_in_us);
+ }
+ if (frame_type == IETF_ACK_ECN) {
+ ack_frame->ecn_counters_populated = true;
+ if (!reader->ReadVarInt62(&ack_frame->ect_0_count)) {
+ set_detailed_error("Unable to read ack ect_0_count.");
+ return false;
+ }
+ if (!reader->ReadVarInt62(&ack_frame->ect_1_count)) {
+ set_detailed_error("Unable to read ack ect_1_count.");
+ return false;
+ }
+ if (!reader->ReadVarInt62(&ack_frame->ecn_ce_count)) {
+ set_detailed_error("Unable to read ack ecn_ce_count.");
+ return false;
+ }
+ } else {
+ ack_frame->ecn_counters_populated = false;
+ ack_frame->ect_0_count = 0;
+ ack_frame->ect_1_count = 0;
+ ack_frame->ecn_ce_count = 0;
+ }
+ if (!visitor_->OnAckFrameStart(QuicPacketNumber(largest_acked),
+ ack_frame->ack_delay_time)) {
+ // The visitor suppresses further processing of the packet. Although this is
+ // not a parsing error, returns false as this is in middle of processing an
+ // ACK frame.
+ set_detailed_error("Visitor suppresses further processing of ACK frame.");
+ return false;
+ }
+
+ // Get number of ACK blocks from the packet.
+ uint64_t ack_block_count;
+ if (!reader->ReadVarInt62(&ack_block_count)) {
+ set_detailed_error("Unable to read ack block count.");
+ return false;
+ }
+ // There always is a first ACK block, which is the (number of packets being
+ // acked)-1, up to and including the packet at largest_acked. Therefore if the
+ // value is 0, then only largest is acked. If it is 1, then largest-1,
+ // largest] are acked, etc
+ uint64_t ack_block_value;
+ if (!reader->ReadVarInt62(&ack_block_value)) {
+ set_detailed_error("Unable to read first ack block length.");
+ return false;
+ }
+ // Calculate the packets being acked in the first block.
+ // +1 because AddRange implementation requires [low,high)
+ uint64_t block_high = largest_acked + 1;
+ uint64_t block_low = largest_acked - ack_block_value;
+
+ // ack_block_value is the number of packets preceding the
+ // largest_acked packet which are in the block being acked. Thus,
+ // its maximum value is largest_acked-1. Test this, reporting an
+ // error if the value is wrong.
+ if (ack_block_value + first_sending_packet_number_.ToUint64() >
+ largest_acked) {
+ set_detailed_error(QuicStrCat("Underflow with first ack block length ",
+ ack_block_value + 1, " largest acked is ",
+ largest_acked, ".")
+ .c_str());
+ return false;
+ }
+
+ if (!visitor_->OnAckRange(QuicPacketNumber(block_low),
+ QuicPacketNumber(block_high))) {
+ // The visitor suppresses further processing of the packet. Although
+ // this is not a parsing error, returns false as this is in middle
+ // of processing an ACK frame.
+ set_detailed_error("Visitor suppresses further processing of ACK frame.");
+ return false;
+ }
+
+ while (ack_block_count != 0) {
+ uint64_t gap_block_value;
+ // Get the sizes of the gap and ack blocks,
+ if (!reader->ReadVarInt62(&gap_block_value)) {
+ set_detailed_error("Unable to read gap block value.");
+ return false;
+ }
+ // It's an error if the gap is larger than the space from packet
+ // number 0 to the start of the block that's just been acked, PLUS
+ // there must be space for at least 1 packet to be acked. For
+ // example, if block_low is 10 and gap_block_value is 9, it means
+ // the gap block is 10 packets long, leaving no room for a packet
+ // to be acked. Thus, gap_block_value+2 can not be larger than
+ // block_low.
+ // The test is written this way to detect wrap-arounds.
+ if ((gap_block_value + 2) > block_low) {
+ set_detailed_error(
+ QuicStrCat("Underflow with gap block length ", gap_block_value + 1,
+ " previous ack block start is ", block_low, ".")
+ .c_str());
+ return false;
+ }
+
+ // Adjust block_high to be the top of the next ack block.
+ // There is a gap of |gap_block_value| packets between the bottom
+ // of ack block N and top of block N+1. Note that gap_block_value
+ // is he size of the gap minus 1 (per the QUIC protocol), and
+ // block_high is the packet number of the first packet of the gap
+ // (per the implementation of OnAckRange/AddAckRange, below).
+ block_high = block_low - 1 - gap_block_value;
+
+ if (!reader->ReadVarInt62(&ack_block_value)) {
+ set_detailed_error("Unable to read ack block value.");
+ return false;
+ }
+ if (ack_block_value + first_sending_packet_number_.ToUint64() >
+ (block_high - 1)) {
+ set_detailed_error(
+ QuicStrCat("Underflow with ack block length ", ack_block_value + 1,
+ " latest ack block end is ", block_high - 1, ".")
+ .c_str());
+ return false;
+ }
+ // Calculate the low end of the new nth ack block. The +1 is
+ // because the encoded value is the blocksize-1.
+ block_low = block_high - 1 - ack_block_value;
+ if (!visitor_->OnAckRange(QuicPacketNumber(block_low),
+ QuicPacketNumber(block_high))) {
+ // The visitor suppresses further processing of the packet. Although
+ // this is not a parsing error, returns false as this is in middle
+ // of processing an ACK frame.
+ set_detailed_error("Visitor suppresses further processing of ACK frame.");
+ return false;
+ }
+
+ // Another one done.
+ ack_block_count--;
+ }
+
+ return visitor_->OnAckFrameEnd(QuicPacketNumber(block_low));
+}
+
+bool QuicFramer::ProcessStopWaitingFrame(QuicDataReader* reader,
+ const QuicPacketHeader& header,
+ QuicStopWaitingFrame* stop_waiting) {
+ uint64_t least_unacked_delta;
+ if (!reader->ReadBytesToUInt64(header.packet_number_length,
+ &least_unacked_delta)) {
+ set_detailed_error("Unable to read least unacked delta.");
+ return false;
+ }
+ if (header.packet_number.ToUint64() <= least_unacked_delta) {
+ set_detailed_error("Invalid unacked delta.");
+ return false;
+ }
+ stop_waiting->least_unacked = header.packet_number - least_unacked_delta;
+
+ return true;
+}
+
+bool QuicFramer::ProcessRstStreamFrame(QuicDataReader* reader,
+ QuicRstStreamFrame* frame) {
+ if (!reader->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ if (!reader->ReadUInt64(&frame->byte_offset)) {
+ set_detailed_error("Unable to read rst stream sent byte offset.");
+ return false;
+ }
+
+ uint32_t error_code;
+ if (!reader->ReadUInt32(&error_code)) {
+ set_detailed_error("Unable to read rst stream error code.");
+ return false;
+ }
+
+ if (error_code >= QUIC_STREAM_LAST_ERROR) {
+ // Ignore invalid stream error code if any.
+ error_code = QUIC_STREAM_LAST_ERROR;
+ }
+
+ frame->error_code = static_cast<QuicRstStreamErrorCode>(error_code);
+
+ return true;
+}
+
+bool QuicFramer::ProcessConnectionCloseFrame(QuicDataReader* reader,
+ QuicConnectionCloseFrame* frame) {
+ uint32_t error_code;
+ frame->close_type = GOOGLE_QUIC_CONNECTION_CLOSE;
+
+ if (!reader->ReadUInt32(&error_code)) {
+ set_detailed_error("Unable to read connection close error code.");
+ return false;
+ }
+
+ if (error_code >= QUIC_LAST_ERROR) {
+ // Ignore invalid QUIC error code if any.
+ error_code = QUIC_LAST_ERROR;
+ }
+
+ frame->quic_error_code = static_cast<QuicErrorCode>(error_code);
+
+ QuicStringPiece error_details;
+ if (!reader->ReadStringPiece16(&error_details)) {
+ set_detailed_error("Unable to read connection close error details.");
+ return false;
+ }
+ frame->error_details = std::string(error_details);
+
+ return true;
+}
+
+bool QuicFramer::ProcessGoAwayFrame(QuicDataReader* reader,
+ QuicGoAwayFrame* frame) {
+ uint32_t error_code;
+ if (!reader->ReadUInt32(&error_code)) {
+ set_detailed_error("Unable to read go away error code.");
+ return false;
+ }
+
+ if (error_code >= QUIC_LAST_ERROR) {
+ // Ignore invalid QUIC error code if any.
+ error_code = QUIC_LAST_ERROR;
+ }
+ frame->error_code = static_cast<QuicErrorCode>(error_code);
+
+ uint32_t stream_id;
+ if (!reader->ReadUInt32(&stream_id)) {
+ set_detailed_error("Unable to read last good stream id.");
+ return false;
+ }
+ frame->last_good_stream_id = static_cast<QuicStreamId>(stream_id);
+
+ QuicStringPiece reason_phrase;
+ if (!reader->ReadStringPiece16(&reason_phrase)) {
+ set_detailed_error("Unable to read goaway reason.");
+ return false;
+ }
+ frame->reason_phrase = std::string(reason_phrase);
+
+ return true;
+}
+
+bool QuicFramer::ProcessWindowUpdateFrame(QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame) {
+ if (!reader->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ if (!reader->ReadUInt64(&frame->byte_offset)) {
+ set_detailed_error("Unable to read window byte_offset.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessBlockedFrame(QuicDataReader* reader,
+ QuicBlockedFrame* frame) {
+ DCHECK_NE(QUIC_VERSION_99, version_.transport_version)
+ << "Attempt to process non-IETF frames but version is 99";
+
+ if (!reader->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ return true;
+}
+
+void QuicFramer::ProcessPaddingFrame(QuicDataReader* reader,
+ QuicPaddingFrame* frame) {
+ // Type byte has been read.
+ frame->num_padding_bytes = 1;
+ uint8_t next_byte;
+ while (!reader->IsDoneReading() && reader->PeekByte() == 0x00) {
+ reader->ReadBytes(&next_byte, 1);
+ DCHECK_EQ(0x00, next_byte);
+ ++frame->num_padding_bytes;
+ }
+}
+
+bool QuicFramer::ProcessMessageFrame(QuicDataReader* reader,
+ bool no_message_length,
+ QuicMessageFrame* frame) {
+ if (no_message_length) {
+ QuicStringPiece remaining(reader->ReadRemainingPayload());
+ frame->data = remaining.data();
+ frame->message_length = remaining.length();
+ return true;
+ }
+
+ uint64_t message_length;
+ if (!reader->ReadVarInt62(&message_length)) {
+ set_detailed_error("Unable to read message length");
+ return false;
+ }
+
+ QuicStringPiece message_piece;
+ if (!reader->ReadStringPiece(&message_piece, message_length)) {
+ set_detailed_error("Unable to read message data");
+ return false;
+ }
+
+ frame->data = message_piece.data();
+ frame->message_length = message_length;
+
+ return true;
+}
+
+// static
+QuicStringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket(
+ QuicTransportVersion version,
+ const QuicEncryptedPacket& encrypted,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool includes_version,
+ bool includes_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ uint64_t retry_token_length,
+ QuicVariableLengthIntegerLength length_length) {
+ // TODO(ianswett): This is identical to QuicData::AssociatedData.
+ return QuicStringPiece(
+ encrypted.data(),
+ GetStartOfEncryptedData(version, destination_connection_id_length,
+ source_connection_id_length, includes_version,
+ includes_diversification_nonce,
+ packet_number_length, retry_token_length_length,
+ retry_token_length, length_length));
+}
+
+void QuicFramer::SetDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter) {
+ DCHECK_EQ(alternative_decrypter_level_, NUM_ENCRYPTION_LEVELS);
+ DCHECK_GE(level, decrypter_level_);
+ DCHECK(!version_.KnowsWhichDecrypterToUse());
+ decrypter_[decrypter_level_] = nullptr;
+ decrypter_[level] = std::move(decrypter);
+ decrypter_level_ = level;
+}
+
+void QuicFramer::SetAlternativeDecrypter(
+ EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter,
+ bool latch_once_used) {
+ DCHECK_NE(level, decrypter_level_);
+ DCHECK(!version_.KnowsWhichDecrypterToUse());
+ if (alternative_decrypter_level_ != NUM_ENCRYPTION_LEVELS) {
+ decrypter_[alternative_decrypter_level_] = nullptr;
+ }
+ decrypter_[level] = std::move(decrypter);
+ alternative_decrypter_level_ = level;
+ alternative_decrypter_latch_ = latch_once_used;
+}
+
+void QuicFramer::InstallDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter) {
+ DCHECK(version_.KnowsWhichDecrypterToUse());
+ decrypter_[level] = std::move(decrypter);
+}
+
+void QuicFramer::RemoveDecrypter(EncryptionLevel level) {
+ DCHECK(version_.KnowsWhichDecrypterToUse());
+ decrypter_[level] = nullptr;
+}
+
+const QuicDecrypter* QuicFramer::GetDecrypter(EncryptionLevel level) const {
+ DCHECK(version_.KnowsWhichDecrypterToUse());
+ return decrypter_[level].get();
+}
+
+const QuicDecrypter* QuicFramer::decrypter() const {
+ return decrypter_[decrypter_level_].get();
+}
+
+const QuicDecrypter* QuicFramer::alternative_decrypter() const {
+ if (alternative_decrypter_level_ == NUM_ENCRYPTION_LEVELS) {
+ return nullptr;
+ }
+ return decrypter_[alternative_decrypter_level_].get();
+}
+
+void QuicFramer::SetEncrypter(EncryptionLevel level,
+ std::unique_ptr<QuicEncrypter> encrypter) {
+ DCHECK_GE(level, 0);
+ DCHECK_LT(level, NUM_ENCRYPTION_LEVELS);
+ encrypter_[level] = std::move(encrypter);
+}
+
+size_t QuicFramer::EncryptInPlace(EncryptionLevel level,
+ QuicPacketNumber packet_number,
+ size_t ad_len,
+ size_t total_len,
+ size_t buffer_len,
+ char* buffer) {
+ DCHECK(packet_number.IsInitialized());
+ if (encrypter_[level] == nullptr) {
+ QUIC_BUG << ENDPOINT
+ << "Attempted to encrypt in place without encrypter at level "
+ << QuicUtils::EncryptionLevelToString(level);
+ RaiseError(QUIC_ENCRYPTION_FAILURE);
+ return 0;
+ }
+
+ size_t output_length = 0;
+ if (!encrypter_[level]->EncryptPacket(
+ packet_number.ToUint64(),
+ QuicStringPiece(buffer, ad_len), // Associated data
+ QuicStringPiece(buffer + ad_len, total_len - ad_len), // Plaintext
+ buffer + ad_len, // Destination buffer
+ &output_length, buffer_len - ad_len)) {
+ RaiseError(QUIC_ENCRYPTION_FAILURE);
+ return 0;
+ }
+
+ return ad_len + output_length;
+}
+
+size_t QuicFramer::EncryptPayload(EncryptionLevel level,
+ QuicPacketNumber packet_number,
+ const QuicPacket& packet,
+ char* buffer,
+ size_t buffer_len) {
+ DCHECK(packet_number.IsInitialized());
+ if (encrypter_[level] == nullptr) {
+ QUIC_BUG << ENDPOINT << "Attempted to encrypt without encrypter at level "
+ << QuicUtils::EncryptionLevelToString(level);
+ RaiseError(QUIC_ENCRYPTION_FAILURE);
+ return 0;
+ }
+
+ QuicStringPiece associated_data =
+ packet.AssociatedData(version_.transport_version);
+ // Copy in the header, because the encrypter only populates the encrypted
+ // plaintext content.
+ const size_t ad_len = associated_data.length();
+ memmove(buffer, associated_data.data(), ad_len);
+ // Encrypt the plaintext into the buffer.
+ size_t output_length = 0;
+ if (!encrypter_[level]->EncryptPacket(
+ packet_number.ToUint64(), associated_data,
+ packet.Plaintext(version_.transport_version), buffer + ad_len,
+ &output_length, buffer_len - ad_len)) {
+ RaiseError(QUIC_ENCRYPTION_FAILURE);
+ return 0;
+ }
+
+ return ad_len + output_length;
+}
+
+size_t QuicFramer::GetCiphertextSize(EncryptionLevel level,
+ size_t plaintext_size) const {
+ return encrypter_[level]->GetCiphertextSize(plaintext_size);
+}
+
+size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) {
+ // In order to keep the code simple, we don't have the current encryption
+ // level to hand. Both the NullEncrypter and AES-GCM have a tag length of 12.
+ size_t min_plaintext_size = ciphertext_size;
+
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; i++) {
+ if (encrypter_[i] != nullptr) {
+ size_t size = encrypter_[i]->GetMaxPlaintextSize(ciphertext_size);
+ if (size < min_plaintext_size) {
+ min_plaintext_size = size;
+ }
+ }
+ }
+
+ return min_plaintext_size;
+}
+
+bool QuicFramer::DecryptPayload(QuicStringPiece encrypted,
+ QuicStringPiece associated_data,
+ const QuicPacketHeader& header,
+ char* decrypted_buffer,
+ size_t buffer_length,
+ size_t* decrypted_length,
+ EncryptionLevel* decrypted_level) {
+ EncryptionLevel level = decrypter_level_;
+ QuicDecrypter* decrypter = decrypter_[level].get();
+ QuicDecrypter* alternative_decrypter = nullptr;
+ if (version().KnowsWhichDecrypterToUse()) {
+ level = GetEncryptionLevel(header);
+ decrypter = decrypter_[level].get();
+ if (decrypter == nullptr) {
+ return false;
+ }
+ if (level == ENCRYPTION_ZERO_RTT &&
+ perspective_ == Perspective::IS_CLIENT && header.nonce != nullptr) {
+ decrypter->SetDiversificationNonce(*header.nonce);
+ }
+ } else if (alternative_decrypter_level_ != NUM_ENCRYPTION_LEVELS) {
+ alternative_decrypter = decrypter_[alternative_decrypter_level_].get();
+ }
+
+ DCHECK(decrypter != nullptr);
+
+ bool success = decrypter->DecryptPacket(
+ header.packet_number.ToUint64(), associated_data, encrypted,
+ decrypted_buffer, decrypted_length, buffer_length);
+ if (success) {
+ visitor_->OnDecryptedPacket(level);
+ *decrypted_level = level;
+ } else if (alternative_decrypter != nullptr) {
+ if (header.nonce != nullptr) {
+ DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
+ alternative_decrypter->SetDiversificationNonce(*header.nonce);
+ }
+ bool try_alternative_decryption = true;
+ if (alternative_decrypter_level_ == ENCRYPTION_ZERO_RTT) {
+ if (perspective_ == Perspective::IS_CLIENT) {
+ if (header.nonce == nullptr) {
+ // Can not use INITIAL decryption without a diversification nonce.
+ try_alternative_decryption = false;
+ }
+ } else {
+ DCHECK(header.nonce == nullptr);
+ }
+ }
+
+ if (try_alternative_decryption) {
+ success = alternative_decrypter->DecryptPacket(
+ header.packet_number.ToUint64(), associated_data, encrypted,
+ decrypted_buffer, decrypted_length, buffer_length);
+ }
+ if (success) {
+ visitor_->OnDecryptedPacket(alternative_decrypter_level_);
+ *decrypted_level = decrypter_level_;
+ if (alternative_decrypter_latch_) {
+ // Switch to the alternative decrypter and latch so that we cannot
+ // switch back.
+ decrypter_level_ = alternative_decrypter_level_;
+ decrypter_[decrypter_level_] =
+ std::move(decrypter_[alternative_decrypter_level_]);
+ alternative_decrypter_level_ = NUM_ENCRYPTION_LEVELS;
+ } else {
+ // Switch the alternative decrypter so that we use it first next time.
+ EncryptionLevel level = alternative_decrypter_level_;
+ alternative_decrypter_level_ = decrypter_level_;
+ decrypter_level_ = level;
+ }
+ }
+ }
+
+ if (!success) {
+ QUIC_DVLOG(1) << ENDPOINT << "DecryptPacket failed for packet_number:"
+ << header.packet_number;
+ return false;
+ }
+
+ return true;
+}
+
+size_t QuicFramer::GetIetfAckFrameSize(const QuicAckFrame& frame) {
+ // Type byte, largest_acked, and delay_time are straight-forward.
+ size_t ack_frame_size = kQuicFrameTypeSize;
+ QuicPacketNumber largest_acked = LargestAcked(frame);
+ ack_frame_size += QuicDataWriter::GetVarInt62Len(largest_acked.ToUint64());
+ uint64_t ack_delay_time_us;
+ ack_delay_time_us = frame.ack_delay_time.ToMicroseconds();
+ ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift;
+ ack_frame_size += QuicDataWriter::GetVarInt62Len(ack_delay_time_us);
+
+ // If |ecn_counters_populated| is true and any of the ecn counters is non-0
+ // then the ecn counters are included...
+ if (frame.ecn_counters_populated &&
+ (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) {
+ ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_0_count);
+ ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_1_count);
+ ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ecn_ce_count);
+ }
+
+ // The rest (ack_block_count, first_ack_block, and additional ack
+ // blocks, if any) depends:
+ uint64_t ack_block_count = frame.packets.NumIntervals();
+ if (ack_block_count == 0) {
+ // If the QuicAckFrame has no Intervals, then it is interpreted
+ // as an ack of a single packet at QuicAckFrame.largest_acked.
+ // The resulting ack will consist of only the frame's
+ // largest_ack & first_ack_block fields. The first ack block will be 0
+ // (indicating a single packet) and the ack block_count will be 0.
+ // Each 0 takes 1 byte when VarInt62 encoded.
+ ack_frame_size += 2;
+ return ack_frame_size;
+ }
+
+ auto itr = frame.packets.rbegin();
+ QuicPacketNumber ack_block_largest = largest_acked;
+ QuicPacketNumber ack_block_smallest;
+ if ((itr->max() - 1) == largest_acked) {
+ // If largest_acked + 1 is equal to the Max() of the first Interval
+ // in the QuicAckFrame then the first Interval is the first ack block of the
+ // frame; remaining Intervals are additional ack blocks. The QuicAckFrame's
+ // first Interval is encoded in the frame's largest_acked/first_ack_block,
+ // the remaining Intervals are encoded in additional ack blocks in the
+ // frame, and the packet's ack_block_count is the number of QuicAckFrame
+ // Intervals - 1.
+ ack_block_smallest = itr->min();
+ itr++;
+ ack_block_count--;
+ } else {
+ // If QuicAckFrame.largest_acked is NOT equal to the Max() of
+ // the first Interval then it is interpreted as acking a single
+ // packet at QuicAckFrame.largest_acked, with additional
+ // Intervals indicating additional ack blocks. The encoding is
+ // a) The packet's largest_acked is the QuicAckFrame's largest
+ // acked,
+ // b) the first ack block size is 0,
+ // c) The packet's ack_block_count is the number of QuicAckFrame
+ // Intervals, and
+ // d) The QuicAckFrame Intervals are encoded in additional ack
+ // blocks in the packet.
+ ack_block_smallest = largest_acked;
+ }
+ size_t ack_block_count_size = QuicDataWriter::GetVarInt62Len(ack_block_count);
+ ack_frame_size += ack_block_count_size;
+
+ uint64_t first_ack_block = ack_block_largest - ack_block_smallest;
+ size_t first_ack_block_size = QuicDataWriter::GetVarInt62Len(first_ack_block);
+ ack_frame_size += first_ack_block_size;
+
+ // Account for the remaining Intervals, if any.
+ while (ack_block_count != 0) {
+ uint64_t gap_size = ack_block_smallest - itr->max();
+ // Decrement per the protocol specification
+ size_t size_of_gap_size = QuicDataWriter::GetVarInt62Len(gap_size - 1);
+ ack_frame_size += size_of_gap_size;
+
+ uint64_t block_size = itr->max() - itr->min();
+ // Decrement per the protocol specification
+ size_t size_of_block_size = QuicDataWriter::GetVarInt62Len(block_size - 1);
+ ack_frame_size += size_of_block_size;
+
+ ack_block_smallest = itr->min();
+ itr++;
+ ack_block_count--;
+ }
+
+ return ack_frame_size;
+}
+
+size_t QuicFramer::GetAckFrameSize(
+ const QuicAckFrame& ack,
+ QuicPacketNumberLength packet_number_length) {
+ DCHECK(!ack.packets.Empty());
+ size_t ack_size = 0;
+
+ if (version_.transport_version == QUIC_VERSION_99) {
+ return GetIetfAckFrameSize(ack);
+ }
+ AckFrameInfo ack_info = GetAckFrameInfo(ack);
+ QuicPacketNumberLength largest_acked_length =
+ GetMinPacketNumberLength(version_.transport_version, LargestAcked(ack));
+ QuicPacketNumberLength ack_block_length = GetMinPacketNumberLength(
+ version_.transport_version, QuicPacketNumber(ack_info.max_block_length));
+
+ ack_size =
+ GetMinAckFrameSize(version_.transport_version, largest_acked_length);
+ // First ack block length.
+ ack_size += ack_block_length;
+ if (ack_info.num_ack_blocks != 0) {
+ ack_size += kNumberOfAckBlocksSize;
+ ack_size += std::min(ack_info.num_ack_blocks, kMaxAckBlocks) *
+ (ack_block_length + PACKET_1BYTE_PACKET_NUMBER);
+ }
+
+ // Include timestamps.
+ if (process_timestamps_) {
+ ack_size += GetAckFrameTimeStampSize(ack);
+ }
+
+ return ack_size;
+}
+
+size_t QuicFramer::GetAckFrameTimeStampSize(const QuicAckFrame& ack) {
+ if (ack.received_packet_times.empty()) {
+ return 0;
+ }
+
+ return kQuicNumTimestampsLength + kQuicFirstTimestampLength +
+ (kQuicTimestampLength + kQuicTimestampPacketNumberGapLength) *
+ (ack.received_packet_times.size() - 1);
+}
+
+size_t QuicFramer::ComputeFrameLength(
+ const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicPacketNumberLength packet_number_length) {
+ switch (frame.type) {
+ case STREAM_FRAME:
+ return GetMinStreamFrameSize(
+ version_.transport_version, frame.stream_frame.stream_id,
+ frame.stream_frame.offset, last_frame_in_packet,
+ frame.stream_frame.data_length) +
+ frame.stream_frame.data_length;
+ case CRYPTO_FRAME:
+ return GetMinCryptoFrameSize(frame.crypto_frame->offset,
+ frame.crypto_frame->data_length) +
+ frame.crypto_frame->data_length;
+ case ACK_FRAME: {
+ return GetAckFrameSize(*frame.ack_frame, packet_number_length);
+ }
+ case STOP_WAITING_FRAME:
+ return GetStopWaitingFrameSize(version_.transport_version,
+ packet_number_length);
+ case MTU_DISCOVERY_FRAME:
+ // MTU discovery frames are serialized as ping frames.
+ return kQuicFrameTypeSize;
+ case MESSAGE_FRAME:
+ return GetMessageFrameSize(version_.transport_version,
+ last_frame_in_packet,
+ frame.message_frame->message_length);
+ case PADDING_FRAME:
+ DCHECK(false);
+ return 0;
+ default:
+ return GetRetransmittableControlFrameSize(version_.transport_version,
+ frame);
+ }
+}
+
+bool QuicFramer::AppendTypeByte(const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer) {
+ if (version_.transport_version == QUIC_VERSION_99) {
+ return AppendIetfTypeByte(frame, last_frame_in_packet, writer);
+ }
+ uint8_t type_byte = 0;
+ switch (frame.type) {
+ case STREAM_FRAME:
+ type_byte =
+ GetStreamFrameTypeByte(frame.stream_frame, last_frame_in_packet);
+ break;
+ case ACK_FRAME:
+ return true;
+ case MTU_DISCOVERY_FRAME:
+ type_byte = static_cast<uint8_t>(PING_FRAME);
+ break;
+ case NEW_CONNECTION_ID_FRAME:
+ set_detailed_error(
+ "Attempt to append NEW_CONNECTION_ID frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case RETIRE_CONNECTION_ID_FRAME:
+ set_detailed_error(
+ "Attempt to append RETIRE_CONNECTION_ID frame and not in version "
+ "99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case NEW_TOKEN_FRAME:
+ set_detailed_error(
+ "Attempt to append NEW_TOKEN frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case MAX_STREAM_ID_FRAME:
+ set_detailed_error(
+ "Attempt to append MAX_STREAM_ID frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case STREAM_ID_BLOCKED_FRAME:
+ set_detailed_error(
+ "Attempt to append STREAM_ID_BLOCKED frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case PATH_RESPONSE_FRAME:
+ set_detailed_error(
+ "Attempt to append PATH_RESPONSE frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case PATH_CHALLENGE_FRAME:
+ set_detailed_error(
+ "Attempt to append PATH_CHALLENGE frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case STOP_SENDING_FRAME:
+ set_detailed_error(
+ "Attempt to append STOP_SENDING frame and not in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case MESSAGE_FRAME:
+ return true;
+
+ default:
+ type_byte = static_cast<uint8_t>(frame.type);
+ break;
+ }
+
+ return writer->WriteUInt8(type_byte);
+}
+
+bool QuicFramer::AppendIetfTypeByte(const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer) {
+ uint8_t type_byte = 0;
+ switch (frame.type) {
+ case PADDING_FRAME:
+ type_byte = IETF_PADDING;
+ break;
+ case RST_STREAM_FRAME:
+ type_byte = IETF_RST_STREAM;
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ switch (frame.connection_close_frame->close_type) {
+ case IETF_QUIC_APPLICATION_CONNECTION_CLOSE:
+ type_byte = IETF_APPLICATION_CLOSE;
+ break;
+ case IETF_QUIC_TRANSPORT_CONNECTION_CLOSE:
+ type_byte = IETF_CONNECTION_CLOSE;
+ break;
+ default:
+ set_detailed_error("Invalid QuicConnectionCloseFrame type.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ }
+ break;
+ case GOAWAY_FRAME:
+ set_detailed_error(
+ "Attempt to create non-version-99 GOAWAY frame in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case WINDOW_UPDATE_FRAME:
+ // Depending on whether there is a stream ID or not, will be either a
+ // MAX_STREAM_DATA frame or a MAX_DATA frame.
+ if (frame.window_update_frame->stream_id ==
+ QuicUtils::GetInvalidStreamId(transport_version())) {
+ type_byte = IETF_MAX_DATA;
+ } else {
+ type_byte = IETF_MAX_STREAM_DATA;
+ }
+ break;
+ case BLOCKED_FRAME:
+ if (frame.blocked_frame->stream_id ==
+ QuicUtils::GetInvalidStreamId(transport_version())) {
+ type_byte = IETF_BLOCKED;
+ } else {
+ type_byte = IETF_STREAM_BLOCKED;
+ }
+ break;
+ case STOP_WAITING_FRAME:
+ set_detailed_error(
+ "Attempt to append type byte of STOP WAITING frame in version 99.");
+ return RaiseError(QUIC_INTERNAL_ERROR);
+ case PING_FRAME:
+ type_byte = IETF_PING;
+ break;
+ case STREAM_FRAME:
+ type_byte =
+ GetStreamFrameTypeByte(frame.stream_frame, last_frame_in_packet);
+ break;
+ case ACK_FRAME:
+ // Do nothing here, AppendIetfAckFrameAndTypeByte() will put the type byte
+ // in the buffer.
+ return true;
+ case MTU_DISCOVERY_FRAME:
+ // The path MTU discovery frame is encoded as a PING frame on the wire.
+ type_byte = IETF_PING;
+ break;
+ case NEW_CONNECTION_ID_FRAME:
+ type_byte = IETF_NEW_CONNECTION_ID;
+ break;
+ case RETIRE_CONNECTION_ID_FRAME:
+ type_byte = IETF_RETIRE_CONNECTION_ID;
+ break;
+ case NEW_TOKEN_FRAME:
+ type_byte = IETF_NEW_TOKEN;
+ break;
+ case MAX_STREAM_ID_FRAME:
+ if (QuicUtils::IsBidirectionalStreamId(
+ frame.max_stream_id_frame.max_stream_id)) {
+ type_byte = IETF_MAX_STREAMS_BIDIRECTIONAL;
+ } else {
+ type_byte = IETF_MAX_STREAMS_UNIDIRECTIONAL;
+ }
+ break;
+ case STREAM_ID_BLOCKED_FRAME:
+ if (QuicUtils::IsBidirectionalStreamId(
+ frame.max_stream_id_frame.max_stream_id)) {
+ type_byte = IETF_STREAMS_BLOCKED_BIDIRECTIONAL;
+ } else {
+ type_byte = IETF_STREAMS_BLOCKED_UNIDIRECTIONAL;
+ }
+ break;
+ case PATH_RESPONSE_FRAME:
+ type_byte = IETF_PATH_RESPONSE;
+ break;
+ case PATH_CHALLENGE_FRAME:
+ type_byte = IETF_PATH_CHALLENGE;
+ break;
+ case STOP_SENDING_FRAME:
+ type_byte = IETF_STOP_SENDING;
+ break;
+ case MESSAGE_FRAME:
+ return true;
+ case CRYPTO_FRAME:
+ type_byte = IETF_CRYPTO;
+ break;
+ default:
+ QUIC_BUG << "Attempt to generate a frame type for an unsupported value: "
+ << frame.type;
+ return false;
+ }
+ return writer->WriteUInt8(type_byte);
+}
+
+// static
+bool QuicFramer::AppendPacketNumber(QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber packet_number,
+ QuicDataWriter* writer) {
+ DCHECK(packet_number.IsInitialized());
+ if (!IsValidPacketNumberLength(packet_number_length)) {
+ QUIC_BUG << "Invalid packet_number_length: " << packet_number_length;
+ return false;
+ }
+ return writer->WriteBytesToUInt64(packet_number_length,
+ packet_number.ToUint64());
+}
+
+// static
+bool QuicFramer::AppendStreamId(size_t stream_id_length,
+ QuicStreamId stream_id,
+ QuicDataWriter* writer) {
+ if (stream_id_length == 0 || stream_id_length > 4) {
+ QUIC_BUG << "Invalid stream_id_length: " << stream_id_length;
+ return false;
+ }
+ return writer->WriteBytesToUInt64(stream_id_length, stream_id);
+}
+
+// static
+bool QuicFramer::AppendStreamOffset(size_t offset_length,
+ QuicStreamOffset offset,
+ QuicDataWriter* writer) {
+ if (offset_length == 1 || offset_length > 8) {
+ QUIC_BUG << "Invalid stream_offset_length: " << offset_length;
+ return false;
+ }
+
+ return writer->WriteBytesToUInt64(offset_length, offset);
+}
+
+// static
+bool QuicFramer::AppendAckBlock(uint8_t gap,
+ QuicPacketNumberLength length_length,
+ uint64_t length,
+ QuicDataWriter* writer) {
+ if (length == 0) {
+ if (!IsValidPacketNumberLength(length_length)) {
+ QUIC_BUG << "Invalid packet_number_length: " << length_length;
+ return false;
+ }
+ return writer->WriteUInt8(gap) &&
+ writer->WriteBytesToUInt64(length_length, length);
+ }
+ return writer->WriteUInt8(gap) &&
+ AppendPacketNumber(length_length, QuicPacketNumber(length), writer);
+}
+
+bool QuicFramer::AppendStreamFrame(const QuicStreamFrame& frame,
+ bool no_stream_frame_length,
+ QuicDataWriter* writer) {
+ if (version_.transport_version == QUIC_VERSION_99) {
+ return AppendIetfStreamFrame(frame, no_stream_frame_length, writer);
+ }
+ if (!AppendStreamId(GetStreamIdSize(frame.stream_id), frame.stream_id,
+ writer)) {
+ QUIC_BUG << "Writing stream id size failed.";
+ return false;
+ }
+ if (!AppendStreamOffset(
+ GetStreamOffsetSize(version_.transport_version, frame.offset),
+ frame.offset, writer)) {
+ QUIC_BUG << "Writing offset size failed.";
+ return false;
+ }
+ if (!no_stream_frame_length) {
+ if ((frame.data_length > std::numeric_limits<uint16_t>::max()) ||
+ !writer->WriteUInt16(static_cast<uint16_t>(frame.data_length))) {
+ QUIC_BUG << "Writing stream frame length failed";
+ return false;
+ }
+ }
+
+ if (data_producer_ != nullptr) {
+ DCHECK_EQ(nullptr, frame.data_buffer);
+ if (frame.data_length == 0) {
+ return true;
+ }
+ if (data_producer_->WriteStreamData(frame.stream_id, frame.offset,
+ frame.data_length,
+ writer) != WRITE_SUCCESS) {
+ QUIC_BUG << "Writing frame data failed.";
+ return false;
+ }
+ return true;
+ }
+
+ if (!writer->WriteBytes(frame.data_buffer, frame.data_length)) {
+ QUIC_BUG << "Writing frame data failed.";
+ return false;
+ }
+ return true;
+}
+
+// static
+bool QuicFramer::AppendIetfConnectionId(
+ bool version_flag,
+ QuicConnectionId destination_connection_id,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionId source_connection_id,
+ QuicConnectionIdLength source_connection_id_length,
+ QuicDataWriter* writer) {
+ if (version_flag) {
+ // Append connection ID length byte.
+ uint8_t dcil = GetConnectionIdLengthValue(destination_connection_id_length);
+ uint8_t scil = GetConnectionIdLengthValue(source_connection_id_length);
+ uint8_t connection_id_length = dcil << 4 | scil;
+ if (!writer->WriteBytes(&connection_id_length, 1)) {
+ return false;
+ }
+ }
+ if (destination_connection_id_length == PACKET_8BYTE_CONNECTION_ID &&
+ !writer->WriteConnectionId(destination_connection_id)) {
+ return false;
+ }
+ if (source_connection_id_length == PACKET_8BYTE_CONNECTION_ID &&
+ !writer->WriteConnectionId(source_connection_id)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendNewTokenFrame(const QuicNewTokenFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.token.length()))) {
+ set_detailed_error("Writing token length failed.");
+ return false;
+ }
+ if (!writer->WriteBytes(frame.token.data(), frame.token.length())) {
+ set_detailed_error("Writing token buffer failed.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessNewTokenFrame(QuicDataReader* reader,
+ QuicNewTokenFrame* frame) {
+ uint64_t length;
+ if (!reader->ReadVarInt62(&length)) {
+ set_detailed_error("Unable to read new token length.");
+ return false;
+ }
+ if (length > kMaxNewTokenTokenLength) {
+ set_detailed_error("Token length larger than maximum.");
+ return false;
+ }
+
+ // TODO(ianswett): Don't use QuicStringPiece as an intermediary.
+ QuicStringPiece data;
+ if (!reader->ReadStringPiece(&data, length)) {
+ set_detailed_error("Unable to read new token data.");
+ return false;
+ }
+ frame->token = std::string(data);
+ return true;
+}
+
+// Add a new ietf-format stream frame.
+// Bits controlling whether there is a frame-length and frame-offset
+// are in the QuicStreamFrame.
+bool QuicFramer::AppendIetfStreamFrame(const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.stream_id))) {
+ set_detailed_error("Writing stream id failed.");
+ return false;
+ }
+
+ if (frame.offset != 0) {
+ if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.offset))) {
+ set_detailed_error("Writing data offset failed.");
+ return false;
+ }
+ }
+
+ if (!last_frame_in_packet) {
+ if (!writer->WriteVarInt62(frame.data_length)) {
+ set_detailed_error("Writing data length failed.");
+ return false;
+ }
+ }
+
+ if (frame.data_length == 0) {
+ return true;
+ }
+ if (data_producer_ == nullptr) {
+ if (!writer->WriteBytes(frame.data_buffer, frame.data_length)) {
+ set_detailed_error("Writing frame data failed.");
+ return false;
+ }
+ } else {
+ DCHECK_EQ(nullptr, frame.data_buffer);
+
+ if (data_producer_->WriteStreamData(frame.stream_id, frame.offset,
+ frame.data_length,
+ writer) != WRITE_SUCCESS) {
+ set_detailed_error("Writing frame data failed.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicFramer::AppendCryptoFrame(const QuicCryptoFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.offset))) {
+ set_detailed_error("Writing data offset failed.");
+ return false;
+ }
+ if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.data_length))) {
+ set_detailed_error("Writing data length failed.");
+ return false;
+ }
+ if (data_producer_ == nullptr) {
+ if (frame.data_buffer == nullptr ||
+ !writer->WriteBytes(frame.data_buffer, frame.data_length)) {
+ set_detailed_error("Writing frame data failed.");
+ return false;
+ }
+ } else {
+ DCHECK_EQ(nullptr, frame.data_buffer);
+ if (!data_producer_->WriteCryptoData(frame.level, frame.offset,
+ frame.data_length, writer)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void QuicFramer::set_version(const ParsedQuicVersion version) {
+ DCHECK(IsSupportedVersion(version)) << ParsedQuicVersionToString(version);
+ version_ = version;
+}
+
+bool QuicFramer::AppendAckFrameAndTypeByte(const QuicAckFrame& frame,
+ QuicDataWriter* writer) {
+ if (transport_version() == QUIC_VERSION_99) {
+ return AppendIetfAckFrameAndTypeByte(frame, writer);
+ }
+
+ const AckFrameInfo new_ack_info = GetAckFrameInfo(frame);
+ QuicPacketNumber largest_acked = LargestAcked(frame);
+ QuicPacketNumberLength largest_acked_length =
+ GetMinPacketNumberLength(version_.transport_version, largest_acked);
+ QuicPacketNumberLength ack_block_length =
+ GetMinPacketNumberLength(version_.transport_version,
+ QuicPacketNumber(new_ack_info.max_block_length));
+ // Calculate available bytes for timestamps and ack blocks.
+ int32_t available_timestamp_and_ack_block_bytes =
+ writer->capacity() - writer->length() - ack_block_length -
+ GetMinAckFrameSize(version_.transport_version, largest_acked_length) -
+ (new_ack_info.num_ack_blocks != 0 ? kNumberOfAckBlocksSize : 0);
+ DCHECK_LE(0, available_timestamp_and_ack_block_bytes);
+
+ // Write out the type byte by setting the low order bits and doing shifts
+ // to make room for the next bit flags to be set.
+ // Whether there are multiple ack blocks.
+ uint8_t type_byte = 0;
+ SetBit(&type_byte, new_ack_info.num_ack_blocks != 0,
+ kQuicHasMultipleAckBlocksOffset);
+
+ SetBits(&type_byte, GetPacketNumberFlags(largest_acked_length),
+ kQuicSequenceNumberLengthNumBits, kLargestAckedOffset);
+
+ SetBits(&type_byte, GetPacketNumberFlags(ack_block_length),
+ kQuicSequenceNumberLengthNumBits, kActBlockLengthOffset);
+
+ type_byte |= kQuicFrameTypeAckMask;
+
+ if (!writer->WriteUInt8(type_byte)) {
+ return false;
+ }
+
+ size_t max_num_ack_blocks = available_timestamp_and_ack_block_bytes /
+ (ack_block_length + PACKET_1BYTE_PACKET_NUMBER);
+
+ // Number of ack blocks.
+ size_t num_ack_blocks =
+ std::min(new_ack_info.num_ack_blocks, max_num_ack_blocks);
+ if (num_ack_blocks > std::numeric_limits<uint8_t>::max()) {
+ num_ack_blocks = std::numeric_limits<uint8_t>::max();
+ }
+
+ // Largest acked.
+ if (!AppendPacketNumber(largest_acked_length, largest_acked, writer)) {
+ return false;
+ }
+
+ // Largest acked delta time.
+ uint64_t ack_delay_time_us = kUFloat16MaxValue;
+ if (!frame.ack_delay_time.IsInfinite()) {
+ DCHECK_LE(0u, frame.ack_delay_time.ToMicroseconds());
+ ack_delay_time_us = frame.ack_delay_time.ToMicroseconds();
+ }
+ if (!writer->WriteUFloat16(ack_delay_time_us)) {
+ return false;
+ }
+
+ if (num_ack_blocks > 0) {
+ if (!writer->WriteBytes(&num_ack_blocks, 1)) {
+ return false;
+ }
+ }
+
+ // First ack block length.
+ if (!AppendPacketNumber(ack_block_length,
+ QuicPacketNumber(new_ack_info.first_block_length),
+ writer)) {
+ return false;
+ }
+
+ // Ack blocks.
+ if (num_ack_blocks > 0) {
+ size_t num_ack_blocks_written = 0;
+ // Append, in descending order from the largest ACKed packet, a series of
+ // ACK blocks that represents the successfully acknoweldged packets. Each
+ // appended gap/block length represents a descending delta from the previous
+ // block. i.e.:
+ // |--- length ---|--- gap ---|--- length ---|--- gap ---|--- largest ---|
+ // For gaps larger than can be represented by a single encoded gap, a 0
+ // length gap of the maximum is used, i.e.:
+ // |--- length ---|--- gap ---|- 0 -|--- gap ---|--- largest ---|
+ auto itr = frame.packets.rbegin();
+ QuicPacketNumber previous_start = itr->min();
+ ++itr;
+
+ for (;
+ itr != frame.packets.rend() && num_ack_blocks_written < num_ack_blocks;
+ previous_start = itr->min(), ++itr) {
+ const auto& interval = *itr;
+ const uint64_t total_gap = previous_start - interval.max();
+ const size_t num_encoded_gaps =
+ (total_gap + std::numeric_limits<uint8_t>::max() - 1) /
+ std::numeric_limits<uint8_t>::max();
+ DCHECK_LE(0u, num_encoded_gaps);
+
+ // Append empty ACK blocks because the gap is longer than a single gap.
+ for (size_t i = 1;
+ i < num_encoded_gaps && num_ack_blocks_written < num_ack_blocks;
+ ++i) {
+ if (!AppendAckBlock(std::numeric_limits<uint8_t>::max(),
+ ack_block_length, 0, writer)) {
+ return false;
+ }
+ ++num_ack_blocks_written;
+ }
+ if (num_ack_blocks_written >= num_ack_blocks) {
+ if (QUIC_PREDICT_FALSE(num_ack_blocks_written != num_ack_blocks)) {
+ QUIC_BUG << "Wrote " << num_ack_blocks_written
+ << ", expected to write " << num_ack_blocks;
+ }
+ break;
+ }
+
+ const uint8_t last_gap =
+ total_gap -
+ (num_encoded_gaps - 1) * std::numeric_limits<uint8_t>::max();
+ // Append the final ACK block with a non-empty size.
+ if (!AppendAckBlock(last_gap, ack_block_length,
+ PacketNumberIntervalLength(interval), writer)) {
+ return false;
+ }
+ ++num_ack_blocks_written;
+ }
+ DCHECK_EQ(num_ack_blocks, num_ack_blocks_written);
+ }
+ // Timestamps.
+ // If we don't process timestamps or if we don't have enough available space
+ // to append all the timestamps, don't append any of them.
+ if (process_timestamps_ && writer->capacity() - writer->length() >=
+ GetAckFrameTimeStampSize(frame)) {
+ if (!AppendTimestampsToAckFrame(frame, writer)) {
+ return false;
+ }
+ } else {
+ uint8_t num_received_packets = 0;
+ if (!writer->WriteBytes(&num_received_packets, 1)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool QuicFramer::AppendTimestampsToAckFrame(const QuicAckFrame& frame,
+ QuicDataWriter* writer) {
+ DCHECK_GE(std::numeric_limits<uint8_t>::max(),
+ frame.received_packet_times.size());
+ // num_received_packets is only 1 byte.
+ if (frame.received_packet_times.size() >
+ std::numeric_limits<uint8_t>::max()) {
+ return false;
+ }
+
+ uint8_t num_received_packets = frame.received_packet_times.size();
+ if (!writer->WriteBytes(&num_received_packets, 1)) {
+ return false;
+ }
+ if (num_received_packets == 0) {
+ return true;
+ }
+
+ auto it = frame.received_packet_times.begin();
+ QuicPacketNumber packet_number = it->first;
+ uint64_t delta_from_largest_observed = LargestAcked(frame) - packet_number;
+
+ DCHECK_GE(std::numeric_limits<uint8_t>::max(), delta_from_largest_observed);
+ if (delta_from_largest_observed > std::numeric_limits<uint8_t>::max()) {
+ return false;
+ }
+
+ if (!writer->WriteUInt8(delta_from_largest_observed)) {
+ return false;
+ }
+
+ // Use the lowest 4 bytes of the time delta from the creation_time_.
+ const uint64_t time_epoch_delta_us = UINT64_C(1) << 32;
+ uint32_t time_delta_us =
+ static_cast<uint32_t>((it->second - creation_time_).ToMicroseconds() &
+ (time_epoch_delta_us - 1));
+ if (!writer->WriteUInt32(time_delta_us)) {
+ return false;
+ }
+
+ QuicTime prev_time = it->second;
+
+ for (++it; it != frame.received_packet_times.end(); ++it) {
+ packet_number = it->first;
+ delta_from_largest_observed = LargestAcked(frame) - packet_number;
+
+ if (delta_from_largest_observed > std::numeric_limits<uint8_t>::max()) {
+ return false;
+ }
+
+ if (!writer->WriteUInt8(delta_from_largest_observed)) {
+ return false;
+ }
+
+ uint64_t frame_time_delta_us = (it->second - prev_time).ToMicroseconds();
+ prev_time = it->second;
+ if (!writer->WriteUFloat16(frame_time_delta_us)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicFramer::AppendStopWaitingFrame(const QuicPacketHeader& header,
+ const QuicStopWaitingFrame& frame,
+ QuicDataWriter* writer) {
+ DCHECK_GE(QUIC_VERSION_43, version_.transport_version);
+ DCHECK(frame.least_unacked.IsInitialized() &&
+ header.packet_number >= frame.least_unacked);
+ const uint64_t least_unacked_delta =
+ header.packet_number - frame.least_unacked;
+ const uint64_t length_shift = header.packet_number_length * 8;
+
+ if (least_unacked_delta >> length_shift > 0) {
+ QUIC_BUG << "packet_number_length " << header.packet_number_length
+ << " is too small for least_unacked_delta: " << least_unacked_delta
+ << " packet_number:" << header.packet_number
+ << " least_unacked:" << frame.least_unacked
+ << " version:" << version_.transport_version;
+ return false;
+ }
+ if (least_unacked_delta == 0) {
+ return writer->WriteBytesToUInt64(header.packet_number_length,
+ least_unacked_delta);
+ }
+ if (!AppendPacketNumber(header.packet_number_length,
+ QuicPacketNumber(least_unacked_delta), writer)) {
+ QUIC_BUG << " seq failed: " << header.packet_number_length;
+ return false;
+ }
+
+ return true;
+}
+
+int QuicFramer::CalculateIetfAckBlockCount(const QuicAckFrame& frame,
+ QuicDataWriter* writer,
+ size_t available_space) {
+ // Number of blocks requested in the frame
+ uint64_t ack_block_count = frame.packets.NumIntervals();
+
+ auto itr = frame.packets.rbegin();
+
+ int actual_block_count = 1;
+ uint64_t block_length = itr->max() - itr->min();
+ size_t encoded_size = QuicDataWriter::GetVarInt62Len(block_length);
+ if (encoded_size > available_space) {
+ return 0;
+ }
+ available_space -= encoded_size;
+ QuicPacketNumber previous_ack_end = itr->min();
+ ack_block_count--;
+
+ while (ack_block_count) {
+ // Each block is a gap followed by another ACK. Calculate each value,
+ // determine the encoded lengths, and check against the available space.
+ itr++;
+ size_t gap = previous_ack_end - itr->max() - 1;
+ encoded_size = QuicDataWriter::GetVarInt62Len(gap);
+
+ // Add the ACK block.
+ block_length = itr->max() - itr->min();
+ encoded_size += QuicDataWriter::GetVarInt62Len(block_length);
+
+ if (encoded_size > available_space) {
+ // No room for this block, so what we've
+ // done up to now is all that can be done.
+ return actual_block_count;
+ }
+ available_space -= encoded_size;
+ actual_block_count++;
+ previous_ack_end = itr->min();
+ ack_block_count--;
+ }
+ // Ran through the whole thing! We can do all blocks.
+ return actual_block_count;
+}
+
+bool QuicFramer::AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame,
+ QuicDataWriter* writer) {
+ // Assume frame is an IETF_ACK frame. If |ecn_counters_populated| is true and
+ // any of the ECN counters is non-0 then turn it into an IETF_ACK+ECN frame.
+ uint8_t type = IETF_ACK;
+ if (frame.ecn_counters_populated &&
+ (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) {
+ type = IETF_ACK_ECN;
+ }
+
+ if (!writer->WriteUInt8(type)) {
+ set_detailed_error("No room for frame-type");
+ return false;
+ }
+
+ QuicPacketNumber largest_acked = LargestAcked(frame);
+ if (!writer->WriteVarInt62(largest_acked.ToUint64())) {
+ set_detailed_error("No room for largest-acked in ack frame");
+ return false;
+ }
+
+ uint64_t ack_delay_time_us = kVarInt62MaxValue;
+ if (!frame.ack_delay_time.IsInfinite()) {
+ DCHECK_LE(0u, frame.ack_delay_time.ToMicroseconds());
+ ack_delay_time_us = frame.ack_delay_time.ToMicroseconds();
+ // TODO(fkastenholz): Use the shift from TLS transport parameters.
+ ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift;
+ }
+
+ if (!writer->WriteVarInt62(ack_delay_time_us)) {
+ set_detailed_error("No room for ack-delay in ack frame");
+ return false;
+ }
+ if (type == IETF_ACK_ECN) {
+ // Encode the ACK ECN fields
+ if (!writer->WriteVarInt62(frame.ect_0_count)) {
+ set_detailed_error("No room for ect_0_count in ack frame");
+ return false;
+ }
+ if (!writer->WriteVarInt62(frame.ect_1_count)) {
+ set_detailed_error("No room for ect_1_count in ack frame");
+ return false;
+ }
+ if (!writer->WriteVarInt62(frame.ecn_ce_count)) {
+ set_detailed_error("No room for ecn_ce_count in ack frame");
+ return false;
+ }
+ }
+
+ uint64_t ack_block_count = frame.packets.NumIntervals();
+ if (ack_block_count == 0) {
+ // If the QuicAckFrame has no Intervals, then it is interpreted
+ // as an ack of a single packet at QuicAckFrame.largest_acked.
+ // The resulting ack will consist of only the frame's
+ // largest_ack & first_ack_block fields. The first ack block will be 0
+ // (indicating a single packet) and the ack block_count will be 0.
+ if (!writer->WriteVarInt62(0)) {
+ set_detailed_error("No room for ack block count in ack frame");
+ return false;
+ }
+ // size of the first block is 1 packet
+ if (!writer->WriteVarInt62(0)) {
+ set_detailed_error("No room for first ack block in ack frame");
+ return false;
+ }
+ return true;
+ }
+ // Case 2 or 3
+ auto itr = frame.packets.rbegin();
+
+ QuicPacketNumber ack_block_largest(largest_acked);
+ QuicPacketNumber ack_block_smallest;
+ if ((itr->max() - 1) == QuicPacketNumber(largest_acked)) {
+ // If largest_acked + 1 is equal to the Max() of the first Interval
+ // in the QuicAckFrame then the first Interval is the first ack block of the
+ // frame; remaining Intervals are additional ack blocks. The QuicAckFrame's
+ // first Interval is encoded in the frame's largest_acked/first_ack_block,
+ // the remaining Intervals are encoded in additional ack blocks in the
+ // frame, and the packet's ack_block_count is the number of QuicAckFrame
+ // Intervals - 1.
+ ack_block_smallest = itr->min();
+ itr++;
+ ack_block_count--;
+ } else {
+ // If QuicAckFrame.largest_acked is NOT equal to the Max() of
+ // the first Interval then it is interpreted as acking a single
+ // packet at QuicAckFrame.largest_acked, with additional
+ // Intervals indicating additional ack blocks. The encoding is
+ // a) The packet's largest_acked is the QuicAckFrame's largest
+ // acked,
+ // b) the first ack block size is 0,
+ // c) The packet's ack_block_count is the number of QuicAckFrame
+ // Intervals, and
+ // d) The QuicAckFrame Intervals are encoded in additional ack
+ // blocks in the packet.
+ ack_block_smallest = largest_acked;
+ }
+
+ if (!writer->WriteVarInt62(ack_block_count)) {
+ set_detailed_error("No room for ack block count in ack frame");
+ return false;
+ }
+
+ uint64_t first_ack_block = ack_block_largest - ack_block_smallest;
+ if (!writer->WriteVarInt62(first_ack_block)) {
+ set_detailed_error("No room for first ack block in ack frame");
+ return false;
+ }
+
+ // For the remaining QuicAckFrame Intervals, if any
+ while (ack_block_count != 0) {
+ uint64_t gap_size = ack_block_smallest - itr->max();
+ if (!writer->WriteVarInt62(gap_size - 1)) {
+ set_detailed_error("No room for gap block in ack frame");
+ return false;
+ }
+
+ uint64_t block_size = itr->max() - itr->min();
+ if (!writer->WriteVarInt62(block_size - 1)) {
+ set_detailed_error("No room for nth ack block in ack frame");
+ return false;
+ }
+
+ ack_block_smallest = itr->min();
+ itr++;
+ ack_block_count--;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendRstStreamFrame(const QuicRstStreamFrame& frame,
+ QuicDataWriter* writer) {
+ if (version_.transport_version == QUIC_VERSION_99) {
+ return AppendIetfResetStreamFrame(frame, writer);
+ }
+ if (!writer->WriteUInt32(frame.stream_id)) {
+ return false;
+ }
+
+ if (!writer->WriteUInt64(frame.byte_offset)) {
+ return false;
+ }
+
+ uint32_t error_code = static_cast<uint32_t>(frame.error_code);
+ if (!writer->WriteUInt32(error_code)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::AppendConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* writer) {
+ if (version_.transport_version == QUIC_VERSION_99) {
+ return AppendIetfConnectionCloseFrame(frame, writer);
+ }
+ uint32_t error_code = static_cast<uint32_t>(frame.quic_error_code);
+ if (!writer->WriteUInt32(error_code)) {
+ return false;
+ }
+ if (!writer->WriteStringPiece16(TruncateErrorString(frame.error_details))) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendGoAwayFrame(const QuicGoAwayFrame& frame,
+ QuicDataWriter* writer) {
+ uint32_t error_code = static_cast<uint32_t>(frame.error_code);
+ if (!writer->WriteUInt32(error_code)) {
+ return false;
+ }
+ uint32_t stream_id = static_cast<uint32_t>(frame.last_good_stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ if (!writer->WriteStringPiece16(TruncateErrorString(frame.reason_phrase))) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ uint32_t stream_id = static_cast<uint32_t>(frame.stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ if (!writer->WriteUInt64(frame.byte_offset)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ if (version_.transport_version == QUIC_VERSION_99) {
+ if (frame.stream_id == QuicUtils::GetInvalidStreamId(transport_version())) {
+ return AppendIetfBlockedFrame(frame, writer);
+ }
+ return AppendStreamBlockedFrame(frame, writer);
+ }
+ uint32_t stream_id = static_cast<uint32_t>(frame.stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendPaddingFrame(const QuicPaddingFrame& frame,
+ QuicDataWriter* writer) {
+ if (frame.num_padding_bytes == 0) {
+ return false;
+ }
+ if (frame.num_padding_bytes < 0) {
+ QUIC_BUG_IF(frame.num_padding_bytes != -1);
+ writer->WritePadding();
+ return true;
+ }
+ // Please note, num_padding_bytes includes type byte which has been written.
+ return writer->WritePaddingBytes(frame.num_padding_bytes - 1);
+}
+
+bool QuicFramer::AppendMessageFrameAndTypeByte(const QuicMessageFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer) {
+ uint8_t type_byte = last_frame_in_packet ? IETF_EXTENSION_MESSAGE_NO_LENGTH
+ : IETF_EXTENSION_MESSAGE;
+ if (!writer->WriteUInt8(type_byte)) {
+ return false;
+ }
+ if (!last_frame_in_packet && !writer->WriteVarInt62(frame.message_length)) {
+ return false;
+ }
+ for (const auto& slice : frame.message_data) {
+ if (!writer->WriteBytes(slice.data(), slice.length())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicFramer::RaiseError(QuicErrorCode error) {
+ QUIC_DLOG(INFO) << ENDPOINT << "Error: " << QuicErrorCodeToString(error)
+ << " detail: " << detailed_error_;
+ set_error(error);
+ visitor_->OnError(this);
+ return false;
+}
+
+bool QuicFramer::IsVersionNegotiation(
+ const QuicPacketHeader& header,
+ bool packet_has_ietf_packet_header) const {
+ if (perspective_ == Perspective::IS_SERVER) {
+ return false;
+ }
+ if (!packet_has_ietf_packet_header) {
+ return header.version_flag;
+ }
+ if (header.form == IETF_QUIC_SHORT_HEADER_PACKET) {
+ return false;
+ }
+ return header.long_packet_type == VERSION_NEGOTIATION;
+}
+
+bool QuicFramer::AppendIetfConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* writer) {
+ if (frame.close_type != IETF_QUIC_TRANSPORT_CONNECTION_CLOSE &&
+ frame.close_type != IETF_QUIC_APPLICATION_CONNECTION_CLOSE) {
+ QUIC_BUG << "Invalid close_type for writing IETF CONNECTION CLOSE.";
+ set_detailed_error("Invalid close_type for writing IETF CONNECTION CLOSE.");
+ return false;
+ }
+
+ if (!writer->WriteUInt16(frame.application_error_code)) {
+ set_detailed_error("Can not write connection close frame error code");
+ return false;
+ }
+
+ if (frame.close_type == IETF_QUIC_TRANSPORT_CONNECTION_CLOSE) {
+ // Write the frame-type of the frame causing the error only
+ // if it's a CONNECTION_CLOSE/Transport.
+ if (!writer->WriteVarInt62(frame.transport_close_frame_type)) {
+ set_detailed_error("Writing frame type failed.");
+ return false;
+ }
+ }
+
+ // TODO(fkastenholz): For full IETF CONNECTION CLOSE support,
+ // if this is a Transport CONNECTION_CLOSE and the extended
+ // error is not QUIC_IETF_GQUIC_ERROR_MISSING then append the extended
+ // "QuicErrorCode: #" string to the phrase.
+ if (!writer->WriteStringPieceVarInt62(
+ TruncateErrorString(frame.error_details))) {
+ set_detailed_error("Can not write connection close phrase");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessIetfConnectionCloseFrame(
+ QuicDataReader* reader,
+ QuicConnectionCloseType type,
+ QuicConnectionCloseFrame* frame) {
+ frame->close_type = type;
+ uint16_t code;
+ if (!reader->ReadUInt16(&code)) {
+ set_detailed_error("Unable to read connection close error code.");
+ return false;
+ }
+ frame->transport_error_code = static_cast<QuicIetfTransportErrorCodes>(code);
+
+ if (type == IETF_QUIC_TRANSPORT_CONNECTION_CLOSE) {
+ // The frame-type of the frame causing the error is present only
+ // if it's a CONNECTION_CLOSE/Transport.
+ if (!reader->ReadVarInt62(&frame->transport_close_frame_type)) {
+ set_detailed_error("Unable to read connection close frame type.");
+ return false;
+ }
+ }
+
+ uint64_t phrase_length;
+ if (!reader->ReadVarInt62(&phrase_length)) {
+ set_detailed_error("Unable to read connection close error details.");
+ return false;
+ }
+ QuicStringPiece phrase;
+ if (!reader->ReadStringPiece(&phrase, static_cast<size_t>(phrase_length))) {
+ set_detailed_error("Unable to read connection close error details.");
+ return false;
+ }
+ // TODO(fkastenholz): when full support is done, add code here
+ // to extract the extended error code from the reason phrase
+ // and set it into frame->extracted_error_code.
+ frame->error_details = std::string(phrase);
+
+ return true;
+}
+
+// IETF Quic Path Challenge/Response frames.
+bool QuicFramer::ProcessPathChallengeFrame(QuicDataReader* reader,
+ QuicPathChallengeFrame* frame) {
+ if (!reader->ReadBytes(frame->data_buffer.data(),
+ frame->data_buffer.size())) {
+ set_detailed_error("Can not read path challenge data.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessPathResponseFrame(QuicDataReader* reader,
+ QuicPathResponseFrame* frame) {
+ if (!reader->ReadBytes(frame->data_buffer.data(),
+ frame->data_buffer.size())) {
+ set_detailed_error("Can not read path response data.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendPathChallengeFrame(const QuicPathChallengeFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteBytes(frame.data_buffer.data(), frame.data_buffer.size())) {
+ set_detailed_error("Writing Path Challenge data failed.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendPathResponseFrame(const QuicPathResponseFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteBytes(frame.data_buffer.data(), frame.data_buffer.size())) {
+ set_detailed_error("Writing Path Response data failed.");
+ return false;
+ }
+ return true;
+}
+
+// Add a new ietf-format stream reset frame.
+// General format is
+// stream id
+// application error code
+// final offset
+bool QuicFramer::AppendIetfResetStreamFrame(const QuicRstStreamFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.stream_id))) {
+ set_detailed_error("Writing reset-stream stream id failed.");
+ return false;
+ }
+ if (!writer->WriteUInt16(frame.ietf_error_code)) {
+ set_detailed_error("Writing reset-stream error code failed.");
+ return false;
+ }
+ if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.byte_offset))) {
+ set_detailed_error("Writing reset-stream final-offset failed.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessIetfResetStreamFrame(QuicDataReader* reader,
+ QuicRstStreamFrame* frame) {
+ // Get Stream ID from frame. ReadVarIntStreamID returns false
+ // if either A) there is a read error or B) the resulting value of
+ // the Stream ID is larger than the maximum allowed value.
+ if (!reader->ReadVarIntStreamId(&frame->stream_id)) {
+ set_detailed_error("Unable to read rst stream stream id.");
+ return false;
+ }
+
+ if (!reader->ReadUInt16(&frame->ietf_error_code)) {
+ set_detailed_error("Unable to read rst stream error code.");
+ return false;
+ }
+
+ if (!reader->ReadVarInt62(&frame->byte_offset)) {
+ set_detailed_error("Unable to read rst stream sent byte offset.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessStopSendingFrame(
+ QuicDataReader* reader,
+ QuicStopSendingFrame* stop_sending_frame) {
+ if (!reader->ReadVarIntStreamId(&stop_sending_frame->stream_id)) {
+ set_detailed_error("Unable to read stop sending stream id.");
+ return false;
+ }
+
+ if (!reader->ReadUInt16(&stop_sending_frame->application_error_code)) {
+ set_detailed_error("Unable to read stop sending application error code.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendStopSendingFrame(
+ const QuicStopSendingFrame& stop_sending_frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(stop_sending_frame.stream_id)) {
+ set_detailed_error("Can not write stop sending stream id");
+ return false;
+ }
+ if (!writer->WriteUInt16(stop_sending_frame.application_error_code)) {
+ set_detailed_error("Can not write application error code");
+ return false;
+ }
+ return true;
+}
+
+// Append/process IETF-Format MAX_DATA Frame
+bool QuicFramer::AppendMaxDataFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(frame.byte_offset)) {
+ set_detailed_error("Can not write MAX_DATA byte-offset");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessMaxDataFrame(QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame) {
+ frame->stream_id = QuicUtils::GetInvalidStreamId(transport_version());
+ if (!reader->ReadVarInt62(&frame->byte_offset)) {
+ set_detailed_error("Can not read MAX_DATA byte-offset");
+ return false;
+ }
+ return true;
+}
+
+// Append/process IETF-Format MAX_STREAM_DATA Frame
+bool QuicFramer::AppendMaxStreamDataFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(frame.stream_id)) {
+ set_detailed_error("Can not write MAX_STREAM_DATA stream id");
+ return false;
+ }
+ if (!writer->WriteVarInt62(frame.byte_offset)) {
+ set_detailed_error("Can not write MAX_STREAM_DATA byte-offset");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessMaxStreamDataFrame(QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame) {
+ if (!reader->ReadVarIntStreamId(&frame->stream_id)) {
+ set_detailed_error("Can not read MAX_STREAM_DATA stream id");
+ return false;
+ }
+ if (!reader->ReadVarInt62(&frame->byte_offset)) {
+ set_detailed_error("Can not read MAX_STREAM_DATA byte-count");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendMaxStreamsFrame(const QuicMaxStreamIdFrame& frame,
+ QuicDataWriter* writer) {
+ // Convert from the stream id on which the connection is blocked to a count
+ QuicStreamId stream_count =
+ StreamIdToCount(version_.transport_version, frame.max_stream_id);
+
+ if (!writer->WriteVarInt62(stream_count)) {
+ set_detailed_error("Can not write MAX_STREAMS stream count");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessMaxStreamsFrame(QuicDataReader* reader,
+ QuicMaxStreamIdFrame* frame,
+ uint64_t frame_type) {
+ QuicStreamId received_stream_count;
+ if (!reader->ReadVarIntStreamId(&received_stream_count)) {
+ set_detailed_error("Can not read MAX_STREAMS stream count.");
+ return false;
+ }
+ // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED
+ // frame is implemented and passed up to the stream ID manager.
+ if (received_stream_count == 0) {
+ set_detailed_error("MAX_STREAMS stream count of 0 not supported.");
+ return false;
+ }
+ // Note that this code assumes that the only possible error that
+ // StreamCountToId can detect is that the stream count is too big or is 0.
+ // Too big is prevented by passing in the minimum of the received count
+ // and the maximum supported count, ensuring that the stream ID is
+ // pegged at the maximum allowed ID.
+ // count==0 is handled above, so that detailed_error_ may be set
+ // properly.
+ return StreamCountToId(
+ std::min(
+ received_stream_count,
+ GetMaxStreamCount((frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL),
+ perspective_)),
+ /*unidirectional=*/(frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL),
+ perspective_, version_.transport_version, &frame->max_stream_id);
+}
+
+bool QuicFramer::AppendIetfBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(frame.offset)) {
+ set_detailed_error("Can not write blocked offset.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessIetfBlockedFrame(QuicDataReader* reader,
+ QuicBlockedFrame* frame) {
+ // Indicates that it is a BLOCKED frame (as opposed to STREAM_BLOCKED).
+ frame->stream_id = QuicUtils::GetInvalidStreamId(transport_version());
+ if (!reader->ReadVarInt62(&frame->offset)) {
+ set_detailed_error("Can not read blocked offset.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendStreamBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(frame.stream_id)) {
+ set_detailed_error("Can not write stream blocked stream id.");
+ return false;
+ }
+ if (!writer->WriteVarInt62(frame.offset)) {
+ set_detailed_error("Can not write stream blocked offset.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessStreamBlockedFrame(QuicDataReader* reader,
+ QuicBlockedFrame* frame) {
+ if (!reader->ReadVarIntStreamId(&frame->stream_id)) {
+ set_detailed_error("Can not read stream blocked stream id.");
+ return false;
+ }
+ if (!reader->ReadVarInt62(&frame->offset)) {
+ set_detailed_error("Can not read stream blocked offset.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendStreamsBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ // Convert from the stream id on which the connection is blocked to a count
+ QuicStreamId stream_count =
+ StreamIdToCount(version_.transport_version, frame.stream_id);
+
+ if (!writer->WriteVarInt62(stream_count)) {
+ set_detailed_error("Can not write STREAMS_BLOCKED stream count");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessStreamsBlockedFrame(QuicDataReader* reader,
+ QuicStreamIdBlockedFrame* frame,
+ uint64_t frame_type) {
+ QuicStreamId received_stream_count;
+ if (!reader->ReadVarIntStreamId(&received_stream_count)) {
+ set_detailed_error("Can not read STREAMS_BLOCKED stream id.");
+ return false;
+ }
+ // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED
+ // frame is implemented and passed up to the stream ID manager.
+ if (received_stream_count == 0) {
+ set_detailed_error("STREAMS_BLOCKED stream count 0 not supported.");
+ return false;
+ }
+ // TODO(fkastenholz): handle properly when the STREAMS_BLOCKED
+ // frame is implemented and passed up to the stream ID manager.
+ if (received_stream_count >
+ GetMaxStreamCount((frame_type == IETF_MAX_STREAMS_UNIDIRECTIONAL),
+ ((perspective_ == Perspective::IS_CLIENT)
+ ? Perspective::IS_SERVER
+ : Perspective::IS_CLIENT))) {
+ // If stream count is such that the resulting stream ID would exceed our
+ // implementation limit, generate an error.
+ set_detailed_error(
+ "STREAMS_BLOCKED stream count exceeds implementation limit.");
+ return false;
+ }
+ // Convert the stream count to an ID that can be used.
+ // The STREAMS_BLOCKED frame is a request for more streams
+ // that the peer will initiate. If this node is a client, it
+ // means that the peer is a server, and wants server-initiated
+ // stream IDs.
+ return StreamCountToId(
+ received_stream_count,
+ /*unidirectional=*/(frame_type == IETF_STREAMS_BLOCKED_UNIDIRECTIONAL),
+ (perspective_ == Perspective::IS_CLIENT) ? Perspective::IS_SERVER
+ : Perspective::IS_CLIENT,
+ version_.transport_version, &frame->stream_id);
+}
+
+bool QuicFramer::AppendNewConnectionIdFrame(
+ const QuicNewConnectionIdFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(frame.sequence_number)) {
+ set_detailed_error("Can not write New Connection ID sequence number");
+ return false;
+ }
+ if (!writer->WriteUInt8(frame.connection_id.length())) {
+ set_detailed_error(
+ "Can not write New Connection ID frame connection ID Length");
+ return false;
+ }
+ if (!writer->WriteConnectionId(frame.connection_id)) {
+ set_detailed_error("Can not write New Connection ID frame connection ID");
+ return false;
+ }
+
+ if (!writer->WriteBytes(
+ static_cast<const void*>(&frame.stateless_reset_token),
+ sizeof(frame.stateless_reset_token))) {
+ set_detailed_error("Can not write New Connection ID Reset Token");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessNewConnectionIdFrame(QuicDataReader* reader,
+ QuicNewConnectionIdFrame* frame) {
+ if (!reader->ReadVarInt62(&frame->sequence_number)) {
+ set_detailed_error(
+ "Unable to read new connection ID frame sequence number.");
+ return false;
+ }
+
+ uint8_t connection_id_length;
+ if (!reader->ReadUInt8(&connection_id_length)) {
+ set_detailed_error(
+ "Unable to read new connection ID frame connection id length.");
+ return false;
+ }
+
+ if (connection_id_length > kQuicMaxConnectionIdLength) {
+ set_detailed_error("New connection ID length too high.");
+ return false;
+ }
+
+ if (connection_id_length != kQuicDefaultConnectionIdLength &&
+ !QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ transport_version())) {
+ set_detailed_error("Invalid new connection ID length for version.");
+ return false;
+ }
+
+ if (!reader->ReadConnectionId(&frame->connection_id, connection_id_length)) {
+ set_detailed_error("Unable to read new connection ID frame connection id.");
+ return false;
+ }
+
+ if (!reader->ReadBytes(&frame->stateless_reset_token,
+ sizeof(frame->stateless_reset_token))) {
+ set_detailed_error("Can not read new connection ID frame reset token.");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteVarInt62(frame.sequence_number)) {
+ set_detailed_error("Can not write Retire Connection ID sequence number");
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessRetireConnectionIdFrame(
+ QuicDataReader* reader,
+ QuicRetireConnectionIdFrame* frame) {
+ if (!reader->ReadVarInt62(&frame->sequence_number)) {
+ set_detailed_error(
+ "Unable to read retire connection ID frame sequence number.");
+ return false;
+ }
+ return true;
+}
+
+uint8_t QuicFramer::GetStreamFrameTypeByte(const QuicStreamFrame& frame,
+ bool last_frame_in_packet) const {
+ if (version_.transport_version == QUIC_VERSION_99) {
+ return GetIetfStreamFrameTypeByte(frame, last_frame_in_packet);
+ }
+ uint8_t type_byte = 0;
+ // Fin bit.
+ type_byte |= frame.fin ? kQuicStreamFinMask : 0;
+
+ // Data Length bit.
+ type_byte <<= kQuicStreamDataLengthShift;
+ type_byte |= last_frame_in_packet ? 0 : kQuicStreamDataLengthMask;
+
+ // Offset 3 bits.
+ type_byte <<= kQuicStreamShift;
+ const size_t offset_len =
+ GetStreamOffsetSize(version_.transport_version, frame.offset);
+ if (offset_len > 0) {
+ type_byte |= offset_len - 1;
+ }
+
+ // stream id 2 bits.
+ type_byte <<= kQuicStreamIdShift;
+ type_byte |= GetStreamIdSize(frame.stream_id) - 1;
+ type_byte |= kQuicFrameTypeStreamMask; // Set Stream Frame Type to 1.
+
+ return type_byte;
+}
+
+uint8_t QuicFramer::GetIetfStreamFrameTypeByte(
+ const QuicStreamFrame& frame,
+ bool last_frame_in_packet) const {
+ DCHECK_EQ(QUIC_VERSION_99, version_.transport_version);
+ uint8_t type_byte = IETF_STREAM;
+ if (!last_frame_in_packet) {
+ type_byte |= IETF_STREAM_FRAME_LEN_BIT;
+ }
+ if (frame.offset != 0) {
+ type_byte |= IETF_STREAM_FRAME_OFF_BIT;
+ }
+ if (frame.fin) {
+ type_byte |= IETF_STREAM_FRAME_FIN_BIT;
+ }
+ return type_byte;
+}
+
+void QuicFramer::InferPacketHeaderTypeFromVersion() {
+ // This function should only be called when server connection negotiates the
+ // version.
+ DCHECK(perspective_ == Perspective::IS_SERVER &&
+ !infer_packet_header_type_from_version_);
+ infer_packet_header_type_from_version_ = true;
+}
+
+void QuicFramer::EnableMultiplePacketNumberSpacesSupport() {
+ if (supports_multiple_packet_number_spaces_) {
+ QUIC_BUG << "Multiple packet number spaces has already been enabled";
+ return;
+ }
+ if (largest_packet_number_.IsInitialized()) {
+ QUIC_BUG << "Try to enable multiple packet number spaces support after any "
+ "packet has been received.";
+ return;
+ }
+
+ supports_multiple_packet_number_spaces_ = true;
+}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic
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
new file mode 100644
index 00000000000..90ef99815f1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer.h
@@ -0,0 +1,939 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_FRAMER_H_
+#define QUICHE_QUIC_CORE_QUIC_FRAMER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+namespace test {
+class QuicFramerPeer;
+} // namespace test
+
+class QuicDataReader;
+class QuicDataWriter;
+class QuicFramer;
+class QuicStreamFrameDataProducer;
+
+// Number of bytes reserved for the frame type preceding each frame.
+const size_t kQuicFrameTypeSize = 1;
+// Number of bytes reserved for error code.
+const size_t kQuicErrorCodeSize = 4;
+// Number of bytes reserved to denote the length of error details field.
+const size_t kQuicErrorDetailsLengthSize = 2;
+
+// Maximum number of bytes reserved for stream id.
+const size_t kQuicMaxStreamIdSize = 4;
+// Maximum number of bytes reserved for byte offset in stream frame.
+const size_t kQuicMaxStreamOffsetSize = 8;
+// Number of bytes reserved to store payload length in stream frame.
+const size_t kQuicStreamPayloadLengthSize = 2;
+// Number of bytes to reserve for IQ Error codes (for the Connection Close,
+// Application Close, and Reset Stream frames).
+const size_t kQuicIetfQuicErrorCodeSize = 2;
+// Minimum size of the IETF QUIC Error Phrase's length field
+const size_t kIetfQuicMinErrorPhraseLengthSize = 1;
+
+// Size in bytes reserved for the delta time of the largest observed
+// packet number in ack frames.
+const size_t kQuicDeltaTimeLargestObservedSize = 2;
+// Size in bytes reserved for the number of received packets with timestamps.
+const size_t kQuicNumTimestampsSize = 1;
+// Size in bytes reserved for the number of missing packets in ack frames.
+const size_t kNumberOfNackRangesSize = 1;
+// Size in bytes reserved for the number of ack blocks in ack frames.
+const size_t kNumberOfAckBlocksSize = 1;
+// Maximum number of missing packet ranges that can fit within an ack frame.
+const size_t kMaxNackRanges = (1 << (kNumberOfNackRangesSize * 8)) - 1;
+// Maximum number of ack blocks that can fit within an ack frame.
+const size_t kMaxAckBlocks = (1 << (kNumberOfAckBlocksSize * 8)) - 1;
+
+// This class receives callbacks from the framer when packets
+// are processed.
+class QUIC_EXPORT_PRIVATE QuicFramerVisitorInterface {
+ public:
+ virtual ~QuicFramerVisitorInterface() {}
+
+ // Called if an error is detected in the QUIC protocol.
+ virtual void OnError(QuicFramer* framer) = 0;
+
+ // Called only when |perspective_| is IS_SERVER and the framer gets a
+ // packet with version flag true and the version on the packet doesn't match
+ // |quic_version_|. The visitor should return true after it updates the
+ // version of the |framer_| to |received_version| or false to stop processing
+ // this packet.
+ virtual bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
+ PacketHeaderFormat form) = 0;
+
+ // Called when a new packet has been received, before it
+ // has been validated or processed.
+ virtual void OnPacket() = 0;
+
+ // Called when a public reset packet has been parsed but has not yet
+ // been validated.
+ virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) = 0;
+
+ // Called only when |perspective_| is IS_CLIENT and a version negotiation
+ // packet has been parsed.
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) = 0;
+
+ // Called when all fields except packet number has been parsed, but has not
+ // been authenticated. If it returns false, framing for this packet will
+ // cease.
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketHeader& header) = 0;
+
+ // Called when the unauthenticated portion of the header has been parsed.
+ // If OnUnauthenticatedHeader returns false, framing for this packet will
+ // cease.
+ virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) = 0;
+
+ // Called when a packet has been decrypted. |level| is the encryption level
+ // of the packet.
+ virtual void OnDecryptedPacket(EncryptionLevel level) = 0;
+
+ // Called when the complete header of a packet had been parsed.
+ // If OnPacketHeader returns false, framing for this packet will cease.
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0;
+
+ // Called when the packet being processed contains multiple IETF QUIC packets,
+ // which is due to there being more data after what is covered by the length
+ // field. |packet| contains the remaining data which can be processed.
+ // Note that this is called when the framer parses the length field, before
+ // it attempts to decrypt the first payload. It is the visitor's
+ // responsibility to buffer the packet and call ProcessPacket on it
+ // after the framer is done parsing the current payload. |packet| does not
+ // own its internal buffer, the visitor should make a copy of it.
+ virtual void OnCoalescedPacket(const QuicEncryptedPacket& packet) = 0;
+
+ // Called when a StreamFrame has been parsed.
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) = 0;
+
+ // Called when a CRYPTO frame has been parsed.
+ virtual bool OnCryptoFrame(const QuicCryptoFrame& frame) = 0;
+
+ // Called when largest acked of an AckFrame has been parsed.
+ virtual bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) = 0;
+
+ // Called when ack range [start, end) of an AckFrame has been parsed.
+ virtual bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) = 0;
+
+ // Called when a timestamp in the AckFrame has been parsed.
+ virtual bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) = 0;
+
+ // Called after the last ack range in an AckFrame has been parsed.
+ // |start| is the starting value of the last ack range.
+ virtual bool OnAckFrameEnd(QuicPacketNumber start) = 0;
+
+ // Called when a StopWaitingFrame has been parsed.
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) = 0;
+
+ // Called when a QuicPaddingFrame has been parsed.
+ virtual bool OnPaddingFrame(const QuicPaddingFrame& frame) = 0;
+
+ // Called when a PingFrame has been parsed.
+ virtual bool OnPingFrame(const QuicPingFrame& frame) = 0;
+
+ // Called when a RstStreamFrame has been parsed.
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0;
+
+ // Called when a ConnectionCloseFrame, of any type, has been parsed.
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) = 0;
+
+ // Called when a StopSendingFrame has been parsed.
+ virtual bool OnStopSendingFrame(const QuicStopSendingFrame& frame) = 0;
+
+ // Called when a PathChallengeFrame has been parsed.
+ virtual bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) = 0;
+
+ // Called when a PathResponseFrame has been parsed.
+ virtual bool OnPathResponseFrame(const QuicPathResponseFrame& frame) = 0;
+
+ // Called when a GoAwayFrame has been parsed.
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) = 0;
+
+ // Called when a WindowUpdateFrame has been parsed.
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0;
+
+ // Called when a BlockedFrame has been parsed.
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) = 0;
+
+ // Called when a NewConnectionIdFrame has been parsed.
+ virtual bool OnNewConnectionIdFrame(
+ const QuicNewConnectionIdFrame& frame) = 0;
+
+ // Called when a RetireConnectionIdFrame has been parsed.
+ virtual bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) = 0;
+
+ // Called when a NewTokenFrame has been parsed.
+ virtual bool OnNewTokenFrame(const QuicNewTokenFrame& frame) = 0;
+
+ // Called when a message frame has been parsed.
+ virtual bool OnMessageFrame(const QuicMessageFrame& frame) = 0;
+
+ // Called when a packet has been completely processed.
+ virtual void OnPacketComplete() = 0;
+
+ // Called to check whether |token| is a valid stateless reset token.
+ virtual bool IsValidStatelessResetToken(QuicUint128 token) const = 0;
+
+ // Called when an IETF stateless reset packet has been parsed and validated
+ // with the stateless reset token.
+ virtual void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) = 0;
+
+ // Called when an IETF MaxStreamId frame has been parsed.
+ virtual bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) = 0;
+
+ // Called when an IETF StreamIdBlocked frame has been parsed.
+ virtual bool OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) = 0;
+};
+
+// Class for parsing and constructing QUIC packets. It has a
+// QuicFramerVisitorInterface that is called when packets are parsed.
+class QUIC_EXPORT_PRIVATE QuicFramer {
+ public:
+ // Constructs a new framer that installs a kNULL QuicEncrypter and
+ // QuicDecrypter for level ENCRYPTION_INITIAL. |supported_versions| specifies
+ // the list of supported QUIC versions. |quic_version_| is set to the maximum
+ // version in |supported_versions|.
+ QuicFramer(const ParsedQuicVersionVector& supported_versions,
+ QuicTime creation_time,
+ Perspective perspective,
+ uint8_t expected_connection_id_length);
+ QuicFramer(const QuicFramer&) = delete;
+ QuicFramer& operator=(const QuicFramer&) = delete;
+
+ virtual ~QuicFramer();
+
+ // Returns true if |version| is a supported transport version.
+ bool IsSupportedTransportVersion(const QuicTransportVersion version) const;
+
+ // Returns true if |version| is a supported protocol version.
+ bool IsSupportedVersion(const ParsedQuicVersion version) const;
+
+ // Set callbacks to be called from the framer. A visitor must be set, or
+ // else the framer will likely crash. It is acceptable for the visitor
+ // to do nothing. If this is called multiple times, only the last visitor
+ // will be used.
+ void set_visitor(QuicFramerVisitorInterface* visitor) { visitor_ = visitor; }
+
+ const ParsedQuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+
+ QuicTransportVersion transport_version() const {
+ return version_.transport_version;
+ }
+
+ ParsedQuicVersion version() const { return version_; }
+
+ void set_version(const ParsedQuicVersion version);
+
+ // Does not DCHECK for supported version. Used by tests to set unsupported
+ // version to trigger version negotiation.
+ void set_version_for_tests(const ParsedQuicVersion version) {
+ version_ = version;
+ }
+
+ QuicErrorCode error() const { return error_; }
+
+ // Allows enabling or disabling of timestamp processing and serialization.
+ void set_process_timestamps(bool process_timestamps) {
+ process_timestamps_ = process_timestamps;
+ }
+
+ // Pass a UDP packet into the framer for parsing.
+ // Return true if the packet was processed succesfully. |packet| must be a
+ // single, complete UDP packet (not a frame of a packet). This packet
+ // might be null padded past the end of the payload, which will be correctly
+ // ignored.
+ bool ProcessPacket(const QuicEncryptedPacket& packet);
+
+ // Largest size in bytes of all stream frame fields without the payload.
+ static size_t GetMinStreamFrameSize(QuicTransportVersion version,
+ QuicStreamId stream_id,
+ QuicStreamOffset offset,
+ bool last_frame_in_packet,
+ QuicPacketLength data_length);
+ // Returns the overhead of framing a CRYPTO frame with the specific offset and
+ // data length provided, but not counting the size of the data payload.
+ static size_t GetMinCryptoFrameSize(QuicStreamOffset offset,
+ QuicPacketLength data_length);
+ static size_t GetMessageFrameSize(QuicTransportVersion version,
+ bool last_frame_in_packet,
+ QuicByteCount length);
+ // Size in bytes of all ack frame fields without the missing packets or ack
+ // blocks.
+ static size_t GetMinAckFrameSize(
+ QuicTransportVersion version,
+ QuicPacketNumberLength largest_observed_length);
+ // Size in bytes of a stop waiting frame.
+ static size_t GetStopWaitingFrameSize(
+ QuicTransportVersion version,
+ QuicPacketNumberLength packet_number_length);
+ // Size in bytes of all reset stream frame fields.
+ static size_t GetRstStreamFrameSize(QuicTransportVersion version,
+ const QuicRstStreamFrame& frame);
+ // Size in bytes of all connection close frame fields without the error
+ // details and the missing packets from the enclosed ack frame.
+ static size_t GetMinConnectionCloseFrameSize(
+ QuicTransportVersion version,
+ const QuicConnectionCloseFrame& frame);
+ // Size in bytes of all GoAway frame fields without the reason phrase.
+ static size_t GetMinGoAwayFrameSize();
+ // Size in bytes of all WindowUpdate frame fields.
+ // For version 99, determines whether a MAX DATA or MAX STREAM DATA frame will
+ // be generated and calculates the appropriate size.
+ static size_t GetWindowUpdateFrameSize(QuicTransportVersion version,
+ const QuicWindowUpdateFrame& frame);
+ // Size in bytes of all MaxStreams frame fields.
+ static size_t GetMaxStreamsFrameSize(QuicTransportVersion version,
+ const QuicMaxStreamIdFrame& frame);
+ // Size in bytes of all StreamsBlocked frame fields.
+ static size_t GetStreamsBlockedFrameSize(
+ QuicTransportVersion version,
+ const QuicStreamIdBlockedFrame& frame);
+ // Size in bytes of all Blocked frame fields.
+ static size_t GetBlockedFrameSize(QuicTransportVersion version,
+ const QuicBlockedFrame& frame);
+ // Size in bytes of PathChallenge frame.
+ static size_t GetPathChallengeFrameSize(const QuicPathChallengeFrame& frame);
+ // Size in bytes of PathResponse frame.
+ static size_t GetPathResponseFrameSize(const QuicPathResponseFrame& frame);
+ // Size in bytes required to serialize the stream id.
+ static size_t GetStreamIdSize(QuicStreamId stream_id);
+ // Size in bytes required to serialize the stream offset.
+ static size_t GetStreamOffsetSize(QuicTransportVersion version,
+ QuicStreamOffset offset);
+ // Size in bytes for a serialized new connection id frame
+ static size_t GetNewConnectionIdFrameSize(
+ const QuicNewConnectionIdFrame& frame);
+
+ // Size in bytes for a serialized retire connection id frame
+ static size_t GetRetireConnectionIdFrameSize(
+ const QuicRetireConnectionIdFrame& frame);
+
+ // Size in bytes for a serialized new token frame
+ static size_t GetNewTokenFrameSize(const QuicNewTokenFrame& frame);
+
+ // Size in bytes required for a serialized stop sending frame.
+ static size_t GetStopSendingFrameSize(const QuicStopSendingFrame& frame);
+
+ // Size in bytes required for a serialized retransmittable control |frame|.
+ static size_t GetRetransmittableControlFrameSize(QuicTransportVersion version,
+ const QuicFrame& frame);
+
+ // Returns the number of bytes added to the packet for the specified frame,
+ // and 0 if the frame doesn't fit. Includes the header size for the first
+ // frame.
+ size_t GetSerializedFrameLength(const QuicFrame& frame,
+ size_t free_bytes,
+ bool first_frame_in_packet,
+ bool last_frame_in_packet,
+ QuicPacketNumberLength packet_number_length);
+
+ // Returns the associated data from the encrypted packet |encrypted| as a
+ // stringpiece.
+ static QuicStringPiece GetAssociatedDataFromEncryptedPacket(
+ QuicTransportVersion version,
+ const QuicEncryptedPacket& encrypted,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool includes_version,
+ bool includes_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ uint64_t retry_token_length,
+ QuicVariableLengthIntegerLength length_length);
+
+ // Serializes a packet containing |frames| into |buffer|.
+ // Returns the length of the packet, which must not be longer than
+ // |packet_length|. Returns 0 if it fails to serialize.
+ size_t BuildDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ char* buffer,
+ size_t packet_length,
+ EncryptionLevel level);
+
+ // Serializes a probing packet, which is a padded PING packet. Returns the
+ // length of the packet. Returns 0 if it fails to serialize.
+ size_t BuildConnectivityProbingPacket(const QuicPacketHeader& header,
+ char* buffer,
+ size_t packet_length,
+ EncryptionLevel level);
+
+ // Serialize a probing packet that uses IETF QUIC's PATH CHALLENGE frame. Also
+ // fills the packet with padding.
+ size_t BuildPaddedPathChallengePacket(const QuicPacketHeader& header,
+ char* buffer,
+ size_t packet_length,
+ QuicPathFrameBuffer* payload,
+ QuicRandom* randomizer,
+ EncryptionLevel level);
+
+ // Serialize a probing response packet that uses IETF QUIC's PATH RESPONSE
+ // frame. Also fills the packet with padding if |is_padded| is
+ // true. |payloads| is always emptied, even if the packet can not be
+ // successfully built.
+ size_t BuildPathResponsePacket(const QuicPacketHeader& header,
+ char* buffer,
+ size_t packet_length,
+ const QuicDeque<QuicPathFrameBuffer>& payloads,
+ const bool is_padded,
+ EncryptionLevel level);
+
+ // Returns a new public reset packet.
+ static std::unique_ptr<QuicEncryptedPacket> BuildPublicResetPacket(
+ const QuicPublicResetPacket& packet);
+
+ // Returns a new IETF stateless reset packet.
+ static std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket(
+ QuicConnectionId connection_id,
+ QuicUint128 stateless_reset_token);
+
+ // Returns a new version negotiation packet.
+ static std::unique_ptr<QuicEncryptedPacket> BuildVersionNegotiationPacket(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ const ParsedQuicVersionVector& versions);
+
+ // Returns a new IETF version negotiation packet.
+ static std::unique_ptr<QuicEncryptedPacket> BuildIetfVersionNegotiationPacket(
+ QuicConnectionId connection_id,
+ const ParsedQuicVersionVector& versions);
+
+ // If header.version_flag is set, the version in the
+ // packet will be set -- but it will be set from version_ not
+ // header.versions.
+ bool AppendPacketHeader(const QuicPacketHeader& header,
+ QuicDataWriter* writer,
+ size_t* length_field_offset);
+ bool AppendIetfHeaderTypeByte(const QuicPacketHeader& header,
+ QuicDataWriter* writer);
+ bool AppendIetfPacketHeader(const QuicPacketHeader& header,
+ QuicDataWriter* writer,
+ size_t* length_field_offset);
+ bool WriteIetfLongHeaderLength(const QuicPacketHeader& header,
+ QuicDataWriter* writer,
+ size_t length_field_offset,
+ EncryptionLevel level);
+ bool AppendTypeByte(const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer);
+ bool AppendIetfTypeByte(const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer);
+ size_t AppendIetfFrames(const QuicFrames& frames, QuicDataWriter* writer);
+ bool AppendStreamFrame(const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer);
+ bool AppendCryptoFrame(const QuicCryptoFrame& frame, QuicDataWriter* writer);
+
+ // SetDecrypter sets the primary decrypter, replacing any that already exists.
+ // If an alternative decrypter is in place then the function DCHECKs. This is
+ // intended for cases where one knows that future packets will be using the
+ // new decrypter and the previous decrypter is now obsolete. |level| indicates
+ // the encryption level of the new decrypter.
+ void SetDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter);
+
+ // SetAlternativeDecrypter sets a decrypter that may be used to decrypt
+ // future packets. |level| indicates the encryption level of the decrypter. If
+ // |latch_once_used| is true, then the first time that the decrypter is
+ // successful it will replace the primary decrypter. Otherwise both
+ // decrypters will remain active and the primary decrypter will be the one
+ // last used.
+ void SetAlternativeDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter,
+ bool latch_once_used);
+
+ void InstallDecrypter(EncryptionLevel level,
+ std::unique_ptr<QuicDecrypter> decrypter);
+ void RemoveDecrypter(EncryptionLevel level);
+
+ const QuicDecrypter* GetDecrypter(EncryptionLevel level) const;
+ const QuicDecrypter* decrypter() const;
+ const QuicDecrypter* alternative_decrypter() const;
+
+ // Changes the encrypter used for level |level| to |encrypter|.
+ void SetEncrypter(EncryptionLevel level,
+ std::unique_ptr<QuicEncrypter> encrypter);
+
+ // Encrypts a payload in |buffer|. |ad_len| is the length of the associated
+ // data. |total_len| is the length of the associated data plus plaintext.
+ // |buffer_len| is the full length of the allocated buffer.
+ size_t EncryptInPlace(EncryptionLevel level,
+ QuicPacketNumber packet_number,
+ size_t ad_len,
+ size_t total_len,
+ size_t buffer_len,
+ char* buffer);
+
+ // Returns the length of the data encrypted into |buffer| if |buffer_len| is
+ // long enough, and otherwise 0.
+ size_t EncryptPayload(EncryptionLevel level,
+ QuicPacketNumber packet_number,
+ const QuicPacket& packet,
+ char* buffer,
+ size_t buffer_len);
+
+ // Returns the length of the ciphertext that would be generated by encrypting
+ // to plaintext of size |plaintext_size| at the given level.
+ size_t GetCiphertextSize(EncryptionLevel level, size_t plaintext_size) const;
+
+ // Returns the maximum length of plaintext that can be encrypted
+ // to ciphertext no larger than |ciphertext_size|.
+ size_t GetMaxPlaintextSize(size_t ciphertext_size);
+
+ const std::string& detailed_error() { return detailed_error_; }
+
+ // The minimum packet number length required to represent |packet_number|.
+ static QuicPacketNumberLength GetMinPacketNumberLength(
+ QuicTransportVersion version,
+ QuicPacketNumber packet_number);
+
+ void SetSupportedVersions(const ParsedQuicVersionVector& versions) {
+ supported_versions_ = versions;
+ version_ = versions[0];
+ }
+
+ // Tell framer to infer packet header type from version_.
+ void InferPacketHeaderTypeFromVersion();
+
+ // Returns true if |header| is considered as an stateless reset packet.
+ bool IsIetfStatelessResetPacket(const QuicPacketHeader& header) const;
+
+ // Returns true if encrypter of |level| is available.
+ bool HasEncrypterOfEncryptionLevel(EncryptionLevel level) const;
+
+ void set_validate_flags(bool value) { validate_flags_ = value; }
+
+ Perspective perspective() const { return perspective_; }
+
+ QuicVersionLabel last_version_label() const { return last_version_label_; }
+
+ void set_data_producer(QuicStreamFrameDataProducer* data_producer) {
+ data_producer_ = data_producer;
+ }
+
+ // Returns true if we are doing IETF-formatted packets.
+ // In the future this could encompass a wide variety of
+ // versions. Doing the test by name ("ietf format") rather
+ // than version number localizes the version/ietf-ness binding
+ // to this method.
+ bool is_ietf_format() {
+ return version_.transport_version == QUIC_VERSION_99;
+ }
+
+ QuicTime creation_time() const { return creation_time_; }
+
+ QuicPacketNumber first_sending_packet_number() const {
+ return first_sending_packet_number_;
+ }
+
+ // If true, QuicFramer will change its expected connection ID length
+ // to the received destination connection ID length of all IETF long headers.
+ void SetShouldUpdateExpectedConnectionIdLength(
+ bool should_update_expected_connection_id_length) {
+ should_update_expected_connection_id_length_ =
+ should_update_expected_connection_id_length;
+ }
+
+ // The connection ID length the framer expects on incoming IETF short headers.
+ uint8_t GetExpectedConnectionIdLength() {
+ return expected_connection_id_length_;
+ }
+
+ void EnableMultiplePacketNumberSpacesSupport();
+
+ private:
+ friend class test::QuicFramerPeer;
+
+ typedef std::map<QuicPacketNumber, uint8_t> NackRangeMap;
+
+ struct AckFrameInfo {
+ AckFrameInfo();
+ AckFrameInfo(const AckFrameInfo& other);
+ ~AckFrameInfo();
+
+ // The maximum ack block length.
+ QuicPacketCount max_block_length;
+ // Length of first ack block.
+ QuicPacketCount first_block_length;
+ // Number of ACK blocks needed for the ACK frame.
+ size_t num_ack_blocks;
+ };
+
+ bool ProcessDataPacket(QuicDataReader* reader,
+ QuicPacketHeader* header,
+ const QuicEncryptedPacket& packet,
+ char* decrypted_buffer,
+ size_t buffer_length);
+
+ bool ProcessIetfDataPacket(QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header,
+ const QuicEncryptedPacket& packet,
+ char* decrypted_buffer,
+ size_t buffer_length);
+
+ bool ProcessPublicResetPacket(QuicDataReader* reader,
+ const QuicPacketHeader& header);
+
+ bool ProcessVersionNegotiationPacket(QuicDataReader* reader,
+ const QuicPacketHeader& header);
+
+ bool MaybeProcessIetfInitialRetryToken(QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header);
+
+ void MaybeProcessCoalescedPacket(const QuicDataReader& encrypted_reader,
+ uint64_t remaining_bytes_length,
+ const QuicPacketHeader& header);
+
+ bool MaybeProcessIetfLength(QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header);
+
+ bool ProcessPublicHeader(QuicDataReader* reader,
+ bool packet_has_ietf_packet_header,
+ QuicPacketHeader* header);
+
+ // Processes the unauthenticated portion of the header into |header| from
+ // the current QuicDataReader. Returns true on success, false on failure.
+ bool ProcessUnauthenticatedHeader(QuicDataReader* encrypted_reader,
+ QuicPacketHeader* header);
+
+ bool ProcessIetfHeaderTypeByte(QuicDataReader* reader,
+ QuicPacketHeader* header);
+ bool ProcessIetfPacketHeader(QuicDataReader* reader,
+ QuicPacketHeader* header);
+
+ // First processes possibly truncated packet number. Calculates the full
+ // packet number from the truncated one and the last seen packet number, and
+ // stores it to |packet_number|.
+ bool ProcessAndCalculatePacketNumber(
+ QuicDataReader* reader,
+ QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber base_packet_number,
+ uint64_t* packet_number);
+ bool ProcessFrameData(QuicDataReader* reader, const QuicPacketHeader& header);
+ bool ProcessIetfFrameData(QuicDataReader* reader,
+ const QuicPacketHeader& header);
+ bool ProcessStreamFrame(QuicDataReader* reader,
+ uint8_t frame_type,
+ QuicStreamFrame* frame);
+ bool ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type);
+ bool ProcessTimestampsInAckFrame(uint8_t num_received_packets,
+ QuicPacketNumber largest_acked,
+ QuicDataReader* reader);
+ bool ProcessIetfAckFrame(QuicDataReader* reader,
+ uint64_t frame_type,
+ QuicAckFrame* ack_frame);
+ bool ProcessStopWaitingFrame(QuicDataReader* reader,
+ const QuicPacketHeader& header,
+ QuicStopWaitingFrame* stop_waiting);
+ bool ProcessRstStreamFrame(QuicDataReader* reader, QuicRstStreamFrame* frame);
+ bool ProcessConnectionCloseFrame(QuicDataReader* reader,
+ QuicConnectionCloseFrame* frame);
+ bool ProcessGoAwayFrame(QuicDataReader* reader, QuicGoAwayFrame* frame);
+ bool ProcessWindowUpdateFrame(QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame);
+ bool ProcessBlockedFrame(QuicDataReader* reader, QuicBlockedFrame* frame);
+ void ProcessPaddingFrame(QuicDataReader* reader, QuicPaddingFrame* frame);
+ bool ProcessMessageFrame(QuicDataReader* reader,
+ bool no_message_length,
+ QuicMessageFrame* frame);
+
+ bool DecryptPayload(QuicStringPiece encrypted,
+ QuicStringPiece associated_data,
+ const QuicPacketHeader& header,
+ char* decrypted_buffer,
+ size_t buffer_length,
+ size_t* decrypted_length,
+ EncryptionLevel* decrypted_level);
+
+ // Returns the full packet number from the truncated
+ // wire format version and the last seen packet number.
+ uint64_t CalculatePacketNumberFromWire(
+ QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber base_packet_number,
+ uint64_t packet_number) const;
+
+ // Returns the QuicTime::Delta corresponding to the time from when the framer
+ // was created.
+ const QuicTime::Delta CalculateTimestampFromWire(uint32_t time_delta_us);
+
+ // Computes the wire size in bytes of time stamps in |ack|.
+ size_t GetAckFrameTimeStampSize(const QuicAckFrame& ack);
+
+ // Computes the wire size in bytes of the |ack| frame.
+ size_t GetAckFrameSize(const QuicAckFrame& ack,
+ QuicPacketNumberLength packet_number_length);
+ // Computes the wire-size, in bytes, of the |frame| ack frame, for IETF Quic.
+ size_t GetIetfAckFrameSize(const QuicAckFrame& frame);
+
+ // Computes the wire size in bytes of the |ack| frame.
+ size_t GetAckFrameSize(const QuicAckFrame& ack);
+
+ // Computes the wire size in bytes of the payload of |frame|.
+ size_t ComputeFrameLength(const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicPacketNumberLength packet_number_length);
+
+ static bool AppendPacketNumber(QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber packet_number,
+ QuicDataWriter* writer);
+ static bool AppendStreamId(size_t stream_id_length,
+ QuicStreamId stream_id,
+ QuicDataWriter* writer);
+ static bool AppendStreamOffset(size_t offset_length,
+ QuicStreamOffset offset,
+ QuicDataWriter* writer);
+
+ // Appends a single ACK block to |writer| and returns true if the block was
+ // successfully appended.
+ static bool AppendAckBlock(uint8_t gap,
+ QuicPacketNumberLength length_length,
+ uint64_t length,
+ QuicDataWriter* writer);
+
+ static uint8_t GetPacketNumberFlags(
+ QuicPacketNumberLength packet_number_length);
+
+ static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame);
+
+ static bool AppendIetfConnectionId(
+ bool version_flag,
+ QuicConnectionId destination_connection_id,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionId source_connection_id,
+ QuicConnectionIdLength source_connection_id_length,
+ QuicDataWriter* writer);
+
+ // The Append* methods attempt to write the provided header or frame using the
+ // |writer|, and return true if successful.
+
+ bool AppendAckFrameAndTypeByte(const QuicAckFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendTimestampsToAckFrame(const QuicAckFrame& frame,
+ QuicDataWriter* writer);
+
+ // Append IETF format ACK frame.
+ //
+ // AppendIetfAckFrameAndTypeByte adds the IETF type byte and the body
+ // of the frame.
+ bool AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame,
+ QuicDataWriter* writer);
+
+ // Used by AppendIetfAckFrameAndTypeByte to figure out how many ack
+ // blocks can be included.
+ int CalculateIetfAckBlockCount(const QuicAckFrame& frame,
+ QuicDataWriter* writer,
+ size_t available_space);
+ bool AppendStopWaitingFrame(const QuicPacketHeader& header,
+ const QuicStopWaitingFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendRstStreamFrame(const QuicRstStreamFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendConnectionCloseFrame(const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendGoAwayFrame(const QuicGoAwayFrame& frame, QuicDataWriter* writer);
+ bool AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendPaddingFrame(const QuicPaddingFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendMessageFrameAndTypeByte(const QuicMessageFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer);
+
+ // IETF frame processing methods.
+ bool ProcessIetfStreamFrame(QuicDataReader* reader,
+ uint8_t frame_type,
+ QuicStreamFrame* frame);
+ bool ProcessIetfConnectionCloseFrame(QuicDataReader* reader,
+ QuicConnectionCloseType type,
+ QuicConnectionCloseFrame* frame);
+ bool ProcessPathChallengeFrame(QuicDataReader* reader,
+ QuicPathChallengeFrame* frame);
+ bool ProcessPathResponseFrame(QuicDataReader* reader,
+ QuicPathResponseFrame* frame);
+ bool ProcessIetfResetStreamFrame(QuicDataReader* reader,
+ QuicRstStreamFrame* frame);
+ bool ProcessStopSendingFrame(QuicDataReader* reader,
+ QuicStopSendingFrame* stop_sending_frame);
+ bool ProcessCryptoFrame(QuicDataReader* reader, QuicCryptoFrame* frame);
+
+ // IETF frame appending methods. All methods append the type byte as well.
+ bool AppendIetfStreamFrame(const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer);
+ bool AppendIetfConnectionCloseFrame(const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendPathChallengeFrame(const QuicPathChallengeFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendPathResponseFrame(const QuicPathResponseFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendIetfResetStreamFrame(const QuicRstStreamFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendStopSendingFrame(const QuicStopSendingFrame& stop_sending_frame,
+ QuicDataWriter* writer);
+
+ // Append/consume IETF-Format MAX_DATA and MAX_STREAM_DATA frames
+ bool AppendMaxDataFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendMaxStreamDataFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ bool ProcessMaxDataFrame(QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame);
+ bool ProcessMaxStreamDataFrame(QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame);
+
+ bool AppendMaxStreamsFrame(const QuicMaxStreamIdFrame& frame,
+ QuicDataWriter* writer);
+ bool ProcessMaxStreamsFrame(QuicDataReader* reader,
+ QuicMaxStreamIdFrame* frame,
+ uint64_t frame_type);
+
+ bool AppendIetfBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+ bool ProcessIetfBlockedFrame(QuicDataReader* reader, QuicBlockedFrame* frame);
+
+ bool AppendStreamBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+ bool ProcessStreamBlockedFrame(QuicDataReader* reader,
+ QuicBlockedFrame* frame);
+
+ bool AppendStreamsBlockedFrame(const QuicStreamIdBlockedFrame& frame,
+ QuicDataWriter* writer);
+ bool ProcessStreamsBlockedFrame(QuicDataReader* reader,
+ QuicStreamIdBlockedFrame* frame,
+ uint64_t frame_type);
+
+ bool AppendNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame,
+ QuicDataWriter* writer);
+ bool ProcessNewConnectionIdFrame(QuicDataReader* reader,
+ QuicNewConnectionIdFrame* frame);
+ bool AppendRetireConnectionIdFrame(const QuicRetireConnectionIdFrame& frame,
+ QuicDataWriter* writer);
+ bool ProcessRetireConnectionIdFrame(QuicDataReader* reader,
+ QuicRetireConnectionIdFrame* frame);
+
+ bool AppendNewTokenFrame(const QuicNewTokenFrame& frame,
+ QuicDataWriter* writer);
+ bool ProcessNewTokenFrame(QuicDataReader* reader, QuicNewTokenFrame* frame);
+
+ bool RaiseError(QuicErrorCode error);
+
+ // Returns true if |header| indicates a version negotiation packet.
+ bool IsVersionNegotiation(const QuicPacketHeader& header,
+ bool packet_has_ietf_packet_header) const;
+
+ // Calculates and returns type byte of stream frame.
+ uint8_t GetStreamFrameTypeByte(const QuicStreamFrame& frame,
+ bool last_frame_in_packet) const;
+ uint8_t GetIetfStreamFrameTypeByte(const QuicStreamFrame& frame,
+ bool last_frame_in_packet) const;
+
+ void set_error(QuicErrorCode error) { error_ = error; }
+
+ void set_detailed_error(const char* error) { detailed_error_ = error; }
+
+ std::string detailed_error_;
+ QuicFramerVisitorInterface* visitor_;
+ QuicErrorCode error_;
+ // Updated by ProcessPacketHeader when it succeeds decrypting a larger packet.
+ QuicPacketNumber largest_packet_number_;
+ // Largest successfully decrypted packet number per packet number space. Only
+ // used when supports_multiple_packet_number_spaces_ is true.
+ QuicPacketNumber largest_decrypted_packet_numbers_[NUM_PACKET_NUMBER_SPACES];
+ // Updated by WritePacketHeader.
+ QuicConnectionId last_serialized_connection_id_;
+ // The last QUIC version label received.
+ QuicVersionLabel last_version_label_;
+ // Version of the protocol being used.
+ ParsedQuicVersion version_;
+ // This vector contains QUIC versions which we currently support.
+ // This should be ordered such that the highest supported version is the first
+ // element, with subsequent elements in descending order (versions can be
+ // skipped as necessary).
+ ParsedQuicVersionVector supported_versions_;
+ // Decrypters used to decrypt packets during parsing.
+ std::unique_ptr<QuicDecrypter> decrypter_[NUM_ENCRYPTION_LEVELS];
+ // The encryption level of the primary decrypter to use in |decrypter_|.
+ EncryptionLevel decrypter_level_;
+ // The encryption level of the alternative decrypter to use in |decrypter_|.
+ // When set to NUM_ENCRYPTION_LEVELS, indicates that there is no alternative
+ // decrypter.
+ EncryptionLevel alternative_decrypter_level_;
+ // |alternative_decrypter_latch_| is true if, when the decrypter at
+ // |alternative_decrypter_level_| successfully decrypts a packet, we should
+ // install it as the only decrypter.
+ bool alternative_decrypter_latch_;
+ // Encrypters used to encrypt packets via EncryptPayload().
+ std::unique_ptr<QuicEncrypter> encrypter_[NUM_ENCRYPTION_LEVELS];
+ // Tracks if the framer is being used by the entity that received the
+ // connection or the entity that initiated it.
+ Perspective perspective_;
+ // If false, skip validation that the public flags are set to legal values.
+ bool validate_flags_;
+ // The diversification nonce from the last received packet.
+ DiversificationNonce last_nonce_;
+ // If true, send and process timestamps in the ACK frame.
+ bool process_timestamps_;
+ // The creation time of the connection, used to calculate timestamps.
+ QuicTime creation_time_;
+ // The last timestamp received if process_timestamps_ is true.
+ QuicTime::Delta last_timestamp_;
+
+ // If this is a framer of a connection, this is the packet number of first
+ // sending packet. If this is a framer of a framer of dispatcher, this is the
+ // packet number of sent packets (for those which have packet number).
+ const QuicPacketNumber first_sending_packet_number_;
+
+ // If not null, framer asks data_producer_ to write stream frame data. Not
+ // owned. TODO(fayang): Consider add data producer to framer's constructor.
+ QuicStreamFrameDataProducer* data_producer_;
+
+ // If true, framer infers packet header type (IETF/GQUIC) from version_.
+ // Otherwise, framer infers packet header type from first byte of a received
+ // packet.
+ bool infer_packet_header_type_from_version_;
+
+ // IETF short headers contain a destination connection ID but do not
+ // encode its length. This variable contains the length we expect to read.
+ // This is also used to validate the long header connection ID lengths in
+ // older versions of QUIC.
+ uint8_t expected_connection_id_length_;
+
+ // When this is true, QuicFramer will change expected_connection_id_length_
+ // to the received destination connection ID length of all IETF long headers.
+ bool should_update_expected_connection_id_length_;
+
+ // Indicates whether this framer supports multiple packet number spaces.
+ bool supports_multiple_packet_number_spaces_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_FRAMER_H_
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
new file mode 100644
index 00000000000..2ffe54e02d2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_framer_test.cc
@@ -0,0 +1,13346 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
+
+using testing::_;
+using testing::Return;
+using testing::Truly;
+
+namespace quic {
+namespace test {
+namespace {
+
+const uint64_t kEpoch = UINT64_C(1) << 32;
+const uint64_t kMask = kEpoch - 1;
+
+const QuicUint128 kTestStatelessResetToken = 1010101; // 0x0F69B5
+
+// Use fields in which each byte is distinct to ensure that every byte is
+// framed correctly. The values are otherwise arbitrary.
+QuicConnectionId FramerTestConnectionId() {
+ return TestConnectionId(UINT64_C(0xFEDCBA9876543210));
+}
+
+QuicConnectionId FramerTestConnectionIdPlusOne() {
+ return TestConnectionId(UINT64_C(0xFEDCBA9876543211));
+}
+
+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));
+}
+
+const QuicPacketNumber kPacketNumber = QuicPacketNumber(UINT64_C(0x12345678));
+const QuicPacketNumber kSmallLargestObserved =
+ QuicPacketNumber(UINT16_C(0x1234));
+const QuicPacketNumber kSmallMissingPacket = QuicPacketNumber(UINT16_C(0x1233));
+const QuicPacketNumber kLeastUnacked = QuicPacketNumber(UINT64_C(0x012345670));
+const QuicStreamId kStreamId = UINT64_C(0x01020304);
+// Note that the high 4 bits of the stream offset must be less than 0x40
+// in order to ensure that the value can be encoded using VarInt62 encoding.
+const QuicStreamOffset kStreamOffset = UINT64_C(0x3A98FEDC32107654);
+const QuicPublicResetNonceProof kNonceProof = UINT64_C(0xABCDEF0123456789);
+
+// In testing that we can ack the full range of packets...
+// This is the largest packet number that can be represented in IETF QUIC
+// varint62 format.
+const QuicPacketNumber kLargestIetfLargestObserved =
+ QuicPacketNumber(UINT64_C(0x3fffffffffffffff));
+// Encodings for the two bits in a VarInt62 that
+// describe the length of the VarInt61. For binary packet
+// formats in this file, the convention is to code the
+// first byte as
+// kVarInt62FourBytes + 0x<value_in_that_byte>
+const uint8_t kVarInt62OneByte = 0x00;
+const uint8_t kVarInt62TwoBytes = 0x40;
+const uint8_t kVarInt62FourBytes = 0x80;
+const uint8_t kVarInt62EightBytes = 0xc0;
+
+class TestEncrypter : public QuicEncrypter {
+ public:
+ ~TestEncrypter() override {}
+ bool SetKey(QuicStringPiece key) override { return true; }
+ bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; }
+ bool SetIV(QuicStringPiece iv) override { return true; }
+ bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; }
+ bool EncryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece plaintext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) override {
+ packet_number_ = QuicPacketNumber(packet_number);
+ associated_data_ = std::string(associated_data);
+ plaintext_ = std::string(plaintext);
+ memcpy(output, plaintext.data(), plaintext.length());
+ *output_length = plaintext.length();
+ return true;
+ }
+ std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override {
+ return std::string(5, 0);
+ }
+ size_t GetKeySize() const override { return 0; }
+ size_t GetNoncePrefixSize() const override { return 0; }
+ size_t GetIVSize() const override { return 0; }
+ size_t GetMaxPlaintextSize(size_t ciphertext_size) const override {
+ return ciphertext_size;
+ }
+ size_t GetCiphertextSize(size_t plaintext_size) const override {
+ return plaintext_size;
+ }
+ QuicStringPiece GetKey() const override { return QuicStringPiece(); }
+ QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); }
+
+ QuicPacketNumber packet_number_;
+ std::string associated_data_;
+ std::string plaintext_;
+};
+
+class TestDecrypter : public QuicDecrypter {
+ public:
+ ~TestDecrypter() override {}
+ bool SetKey(QuicStringPiece key) override { return true; }
+ bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; }
+ bool SetIV(QuicStringPiece iv) override { return true; }
+ bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; }
+ bool SetPreliminaryKey(QuicStringPiece key) override {
+ QUIC_BUG << "should not be called";
+ return false;
+ }
+ bool SetDiversificationNonce(const DiversificationNonce& key) override {
+ return true;
+ }
+ bool DecryptPacket(uint64_t packet_number,
+ QuicStringPiece associated_data,
+ QuicStringPiece ciphertext,
+ char* output,
+ size_t* output_length,
+ size_t max_output_length) override {
+ packet_number_ = QuicPacketNumber(packet_number);
+ associated_data_ = std::string(associated_data);
+ ciphertext_ = std::string(ciphertext);
+ memcpy(output, ciphertext.data(), ciphertext.length());
+ *output_length = ciphertext.length();
+ return true;
+ }
+ std::string GenerateHeaderProtectionMask(
+ QuicDataReader* sample_reader) override {
+ return std::string(5, 0);
+ }
+ size_t GetKeySize() const override { return 0; }
+ size_t GetIVSize() const override { return 0; }
+ QuicStringPiece GetKey() const override { return QuicStringPiece(); }
+ QuicStringPiece GetNoncePrefix() const override { return QuicStringPiece(); }
+ // Use a distinct value starting with 0xFFFFFF, which is never used by TLS.
+ uint32_t cipher_id() const override { return 0xFFFFFFF2; }
+ QuicPacketNumber packet_number_;
+ std::string associated_data_;
+ std::string ciphertext_;
+};
+
+class TestQuicVisitor : public QuicFramerVisitorInterface {
+ public:
+ TestQuicVisitor()
+ : error_count_(0),
+ version_mismatch_(0),
+ packet_count_(0),
+ frame_count_(0),
+ complete_packets_(0),
+ accept_packet_(true),
+ accept_public_header_(true) {}
+
+ ~TestQuicVisitor() override {}
+
+ void OnError(QuicFramer* f) override {
+ QUIC_DLOG(INFO) << "QuicFramer Error: " << QuicErrorCodeToString(f->error())
+ << " (" << f->error() << ")";
+ ++error_count_;
+ }
+
+ void OnPacket() override {}
+
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {
+ public_reset_packet_ = QuicMakeUnique<QuicPublicResetPacket>((packet));
+ }
+
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {
+ version_negotiation_packet_ =
+ QuicMakeUnique<QuicVersionNegotiationPacket>((packet));
+ }
+
+ bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
+ PacketHeaderFormat /*form*/) override {
+ QUIC_DLOG(INFO) << "QuicFramer Version Mismatch, version: "
+ << received_version;
+ ++version_mismatch_;
+ return true;
+ }
+
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override {
+ header_ = QuicMakeUnique<QuicPacketHeader>((header));
+ return accept_public_header_;
+ }
+
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override {
+ return true;
+ }
+
+ void OnDecryptedPacket(EncryptionLevel level) override {}
+
+ bool OnPacketHeader(const QuicPacketHeader& header) override {
+ ++packet_count_;
+ header_ = QuicMakeUnique<QuicPacketHeader>((header));
+ return accept_packet_;
+ }
+
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {
+ size_t coalesced_data_length = packet.length();
+ char* coalesced_data = new char[coalesced_data_length];
+ memcpy(coalesced_data, packet.data(), coalesced_data_length);
+ coalesced_packets_.push_back(QuicMakeUnique<QuicEncryptedPacket>(
+ coalesced_data, coalesced_data_length,
+ /*owns_buffer=*/true));
+ }
+
+ bool OnStreamFrame(const QuicStreamFrame& frame) override {
+ ++frame_count_;
+ // Save a copy of the data so it is valid after the packet is processed.
+ std::string* string_data =
+ new std::string(frame.data_buffer, frame.data_length);
+ stream_data_.push_back(QuicWrapUnique(string_data));
+ stream_frames_.push_back(QuicMakeUnique<QuicStreamFrame>(
+ frame.stream_id, frame.fin, frame.offset, *string_data));
+ return true;
+ }
+
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+ ++frame_count_;
+ // Save a copy of the data so it is valid after the packet is processed.
+ std::string* string_data =
+ new std::string(frame.data_buffer, frame.data_length);
+ crypto_data_.push_back(QuicWrapUnique(string_data));
+ crypto_frames_.push_back(QuicMakeUnique<QuicCryptoFrame>(
+ ENCRYPTION_INITIAL, frame.offset, *string_data));
+ return true;
+ }
+
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override {
+ ++frame_count_;
+ QuicAckFrame ack_frame;
+ ack_frame.largest_acked = largest_acked;
+ ack_frame.ack_delay_time = ack_delay_time;
+ ack_frames_.push_back(QuicMakeUnique<QuicAckFrame>(ack_frame));
+ return true;
+ }
+
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
+ DCHECK(!ack_frames_.empty());
+ ack_frames_[ack_frames_.size() - 1]->packets.AddRange(start, end);
+ return true;
+ }
+
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override {
+ ack_frames_[ack_frames_.size() - 1]->received_packet_times.push_back(
+ std::make_pair(packet_number, timestamp));
+ return true;
+ }
+
+ bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; }
+
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
+ ++frame_count_;
+ stop_waiting_frames_.push_back(QuicMakeUnique<QuicStopWaitingFrame>(frame));
+ return true;
+ }
+
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override {
+ padding_frames_.push_back(QuicMakeUnique<QuicPaddingFrame>(frame));
+ return true;
+ }
+
+ bool OnPingFrame(const QuicPingFrame& frame) override {
+ ++frame_count_;
+ ping_frames_.push_back(QuicMakeUnique<QuicPingFrame>(frame));
+ return true;
+ }
+
+ bool OnMessageFrame(const QuicMessageFrame& frame) override {
+ ++frame_count_;
+ message_frames_.push_back(
+ QuicMakeUnique<QuicMessageFrame>(frame.data, frame.message_length));
+ return true;
+ }
+
+ void OnPacketComplete() override { ++complete_packets_; }
+
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
+ rst_stream_frame_ = frame;
+ return true;
+ }
+
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override {
+ connection_close_frame_ = frame;
+ return true;
+ }
+
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+ stop_sending_frame_ = frame;
+ return true;
+ }
+
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override {
+ path_challenge_frame_ = frame;
+ return true;
+ }
+
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override {
+ path_response_frame_ = frame;
+ return true;
+ }
+
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override {
+ goaway_frame_ = frame;
+ return true;
+ }
+
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
+ max_stream_id_frame_ = frame;
+ return true;
+ }
+
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
+ stream_id_blocked_frame_ = frame;
+ return true;
+ }
+
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {
+ window_update_frame_ = frame;
+ return true;
+ }
+
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override {
+ blocked_frame_ = frame;
+ return true;
+ }
+
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override {
+ new_connection_id_ = frame;
+ return true;
+ }
+
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override {
+ retire_connection_id_ = frame;
+ return true;
+ }
+
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+ new_token_ = frame;
+ return true;
+ }
+
+ bool IsValidStatelessResetToken(QuicUint128 token) const override {
+ return token == kTestStatelessResetToken;
+ }
+
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {
+ stateless_reset_packet_ =
+ QuicMakeUnique<QuicIetfStatelessResetPacket>(packet);
+ }
+
+ // Counters from the visitor_ callbacks.
+ int error_count_;
+ int version_mismatch_;
+ int packet_count_;
+ int frame_count_;
+ int complete_packets_;
+ bool accept_packet_;
+ bool accept_public_header_;
+
+ std::unique_ptr<QuicPacketHeader> header_;
+ std::unique_ptr<QuicPublicResetPacket> public_reset_packet_;
+ std::unique_ptr<QuicIetfStatelessResetPacket> stateless_reset_packet_;
+ std::unique_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
+ std::vector<std::unique_ptr<QuicStreamFrame>> stream_frames_;
+ std::vector<std::unique_ptr<QuicCryptoFrame>> crypto_frames_;
+ std::vector<std::unique_ptr<QuicAckFrame>> ack_frames_;
+ std::vector<std::unique_ptr<QuicStopWaitingFrame>> stop_waiting_frames_;
+ std::vector<std::unique_ptr<QuicPaddingFrame>> padding_frames_;
+ std::vector<std::unique_ptr<QuicPingFrame>> ping_frames_;
+ std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_;
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_;
+ QuicRstStreamFrame rst_stream_frame_;
+ QuicConnectionCloseFrame connection_close_frame_;
+ QuicStopSendingFrame stop_sending_frame_;
+ QuicGoAwayFrame goaway_frame_;
+ QuicPathChallengeFrame path_challenge_frame_;
+ QuicPathResponseFrame path_response_frame_;
+ QuicWindowUpdateFrame window_update_frame_;
+ QuicBlockedFrame blocked_frame_;
+ QuicStreamIdBlockedFrame stream_id_blocked_frame_;
+ QuicMaxStreamIdFrame max_stream_id_frame_;
+ QuicNewConnectionIdFrame new_connection_id_;
+ QuicRetireConnectionIdFrame retire_connection_id_;
+ QuicNewTokenFrame new_token_;
+ std::vector<std::unique_ptr<std::string>> stream_data_;
+ std::vector<std::unique_ptr<std::string>> crypto_data_;
+};
+
+// Simple struct for defining a packet's content, and associated
+// parse error.
+struct PacketFragment {
+ std::string error_if_missing;
+ std::vector<unsigned char> fragment;
+};
+
+using PacketFragments = std::vector<struct PacketFragment>;
+
+ParsedQuicVersionVector AllSupportedVersionsIncludingTls() {
+ QuicFlagSaver flags;
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ return AllSupportedVersions();
+}
+
+class QuicFramerTest : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ QuicFramerTest()
+ : encrypter_(new test::TestEncrypter()),
+ decrypter_(new test::TestDecrypter()),
+ version_(GetParam()),
+ start_(QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(0x10)),
+ framer_(AllSupportedVersionsIncludingTls(),
+ start_,
+ Perspective::IS_SERVER,
+ kQuicDefaultConnectionIdLength) {
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ framer_.set_version(version_);
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_INITIAL,
+ std::unique_ptr<QuicDecrypter>(decrypter_));
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_INITIAL,
+ std::unique_ptr<QuicDecrypter>(decrypter_));
+ }
+ framer_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::unique_ptr<QuicEncrypter>(encrypter_));
+
+ framer_.set_visitor(&visitor_);
+ framer_.InferPacketHeaderTypeFromVersion();
+ }
+
+ void SetDecrypterLevel(EncryptionLevel level) {
+ if (!framer_.version().KnowsWhichDecrypterToUse()) {
+ return;
+ }
+ decrypter_ = new TestDecrypter();
+ framer_.InstallDecrypter(level, std::unique_ptr<QuicDecrypter>(decrypter_));
+ }
+
+ // Helper function to get unsigned char representation of the handshake
+ // protocol byte of the current QUIC version number.
+ unsigned char GetQuicVersionProtocolByte() {
+ return (CreateQuicVersionLabel(version_) >> 24) & 0xff;
+ }
+
+ // Helper function to get unsigned char representation of digit in the
+ // units place of the current QUIC version number.
+ unsigned char GetQuicVersionDigitOnes() {
+ return CreateQuicVersionLabel(version_) & 0xff;
+ }
+
+ // Helper function to get unsigned char representation of digit in the
+ // tens place of the current QUIC version number.
+ unsigned char GetQuicVersionDigitTens() {
+ return (CreateQuicVersionLabel(version_) >> 8) & 0xff;
+ }
+
+ bool CheckEncryption(QuicPacketNumber packet_number, QuicPacket* packet) {
+ if (packet_number != encrypter_->packet_number_) {
+ QUIC_LOG(ERROR) << "Encrypted incorrect packet number. expected "
+ << packet_number
+ << " actual: " << encrypter_->packet_number_;
+ return false;
+ }
+ if (packet->AssociatedData(framer_.transport_version()) !=
+ encrypter_->associated_data_) {
+ QUIC_LOG(ERROR) << "Encrypted incorrect associated data. expected "
+ << packet->AssociatedData(framer_.transport_version())
+ << " actual: " << encrypter_->associated_data_;
+ return false;
+ }
+ if (packet->Plaintext(framer_.transport_version()) !=
+ encrypter_->plaintext_) {
+ QUIC_LOG(ERROR) << "Encrypted incorrect plaintext data. expected "
+ << packet->Plaintext(framer_.transport_version())
+ << " actual: " << encrypter_->plaintext_;
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckDecryption(const QuicEncryptedPacket& encrypted,
+ bool includes_version,
+ bool includes_diversification_nonce,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length) {
+ return CheckDecryption(
+ encrypted, includes_version, includes_diversification_nonce,
+ destination_connection_id_length, source_connection_id_length,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
+ }
+
+ bool CheckDecryption(
+ const QuicEncryptedPacket& encrypted,
+ bool includes_version,
+ bool includes_diversification_nonce,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ size_t retry_token_length,
+ QuicVariableLengthIntegerLength length_length) {
+ if (visitor_.header_->packet_number != decrypter_->packet_number_) {
+ QUIC_LOG(ERROR) << "Decrypted incorrect packet number. expected "
+ << visitor_.header_->packet_number
+ << " actual: " << decrypter_->packet_number_;
+ return false;
+ }
+ QuicStringPiece associated_data =
+ QuicFramer::GetAssociatedDataFromEncryptedPacket(
+ framer_.transport_version(), encrypted,
+ destination_connection_id_length, source_connection_id_length,
+ includes_version, includes_diversification_nonce,
+ PACKET_4BYTE_PACKET_NUMBER, retry_token_length_length,
+ retry_token_length, length_length);
+ if (associated_data != decrypter_->associated_data_) {
+ QUIC_LOG(ERROR) << "Decrypted incorrect associated data. expected "
+ << QuicTextUtils::HexEncode(associated_data)
+ << " actual: "
+ << QuicTextUtils::HexEncode(decrypter_->associated_data_);
+ return false;
+ }
+ QuicStringPiece ciphertext(
+ encrypted.AsStringPiece().substr(GetStartOfEncryptedData(
+ framer_.transport_version(), destination_connection_id_length,
+ source_connection_id_length, includes_version,
+ includes_diversification_nonce, PACKET_4BYTE_PACKET_NUMBER,
+ retry_token_length_length, retry_token_length, length_length)));
+ if (ciphertext != decrypter_->ciphertext_) {
+ QUIC_LOG(ERROR) << "Decrypted incorrect ciphertext data. expected "
+ << QuicTextUtils::HexEncode(ciphertext) << " actual: "
+ << QuicTextUtils::HexEncode(decrypter_->ciphertext_)
+ << " associated data: "
+ << QuicTextUtils::HexEncode(associated_data);
+ return false;
+ }
+ return true;
+ }
+
+ char* AsChars(unsigned char* data) { return reinterpret_cast<char*>(data); }
+
+ // Creates a new QuicEncryptedPacket by concatenating the various
+ // packet fragments in |fragments|.
+ std::unique_ptr<QuicEncryptedPacket> AssemblePacketFromFragments(
+ const PacketFragments& fragments) {
+ char* buffer = new char[kMaxOutgoingPacketSize + 1];
+ size_t len = 0;
+ for (const auto& fragment : fragments) {
+ memcpy(buffer + len, fragment.fragment.data(), fragment.fragment.size());
+ len += fragment.fragment.size();
+ }
+ return QuicMakeUnique<QuicEncryptedPacket>(buffer, len, true);
+ }
+
+ void CheckFramingBoundaries(const PacketFragments& fragments,
+ QuicErrorCode error_code) {
+ std::unique_ptr<QuicEncryptedPacket> packet(
+ AssemblePacketFromFragments(fragments));
+ // Check all the various prefixes of |packet| for the expected
+ // parse error and error code.
+ for (size_t i = 0; i < packet->length(); ++i) {
+ std::string expected_error;
+ size_t len = 0;
+ for (const auto& fragment : fragments) {
+ len += fragment.fragment.size();
+ if (i < len) {
+ expected_error = fragment.error_if_missing;
+ break;
+ }
+ }
+
+ if (expected_error.empty())
+ continue;
+
+ CheckProcessingFails(*packet, i, expected_error, error_code);
+ }
+ }
+
+ void CheckProcessingFails(const QuicEncryptedPacket& packet,
+ size_t len,
+ std::string expected_error,
+ QuicErrorCode error_code) {
+ QuicEncryptedPacket encrypted(packet.data(), len, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted)) << "len: " << len;
+ EXPECT_EQ(expected_error, framer_.detailed_error()) << "len: " << len;
+ EXPECT_EQ(error_code, framer_.error()) << "len: " << len;
+ }
+
+ void CheckProcessingFails(unsigned char* packet,
+ size_t len,
+ std::string expected_error,
+ QuicErrorCode error_code) {
+ QuicEncryptedPacket encrypted(AsChars(packet), len, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted)) << "len: " << len;
+ EXPECT_EQ(expected_error, framer_.detailed_error()) << "len: " << len;
+ EXPECT_EQ(error_code, framer_.error()) << "len: " << len;
+ }
+
+ // Checks if the supplied string matches data in the supplied StreamFrame.
+ void CheckStreamFrameData(std::string str, QuicStreamFrame* frame) {
+ EXPECT_EQ(str, std::string(frame->data_buffer, frame->data_length));
+ }
+
+ void CheckCalculatePacketNumber(uint64_t expected_packet_number,
+ QuicPacketNumber last_packet_number) {
+ uint64_t wire_packet_number = expected_packet_number & kMask;
+ EXPECT_EQ(expected_packet_number,
+ QuicFramerPeer::CalculatePacketNumberFromWire(
+ &framer_, PACKET_4BYTE_PACKET_NUMBER, last_packet_number,
+ wire_packet_number))
+ << "last_packet_number: " << last_packet_number
+ << " wire_packet_number: " << wire_packet_number;
+ }
+
+ std::unique_ptr<QuicPacket> BuildDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ return BuildUnsizedDataPacket(&framer_, header, frames);
+ }
+
+ std::unique_ptr<QuicPacket> BuildDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size) {
+ return BuildUnsizedDataPacket(&framer_, header, frames, packet_size);
+ }
+
+ // N starts at 1.
+ QuicStreamId GetNthStreamid(QuicTransportVersion transport_version,
+ Perspective perspective,
+ bool bidirectional,
+ int n) {
+ if (bidirectional) {
+ return QuicUtils::GetFirstBidirectionalStreamId(transport_version,
+ perspective) +
+ ((n - 1) * QuicUtils::StreamIdDelta(transport_version));
+ }
+ // Unidirectional
+ return QuicUtils::GetFirstUnidirectionalStreamId(transport_version,
+ perspective) +
+ ((n - 1) * QuicUtils::StreamIdDelta(transport_version));
+ }
+
+ test::TestEncrypter* encrypter_;
+ test::TestDecrypter* decrypter_;
+ ParsedQuicVersion version_;
+ QuicTime start_;
+ QuicFramer framer_;
+ test::TestQuicVisitor visitor_;
+ SimpleBufferAllocator allocator_;
+};
+
+// Multiple test cases of QuicFramerTest use byte arrays to define packets for
+// testing, and these byte arrays contain the QUIC version. This macro explodes
+// the 32-bit version into four bytes in network order. Since it uses methods of
+// QuicFramerTest, it is only valid to use this in a QuicFramerTest.
+#define QUIC_VERSION_BYTES \
+ GetQuicVersionProtocolByte(), '0', GetQuicVersionDigitTens(), \
+ GetQuicVersionDigitOnes()
+
+// Run all framer tests with all supported versions of QUIC.
+INSTANTIATE_TEST_SUITE_P(
+ QuicFramerTests,
+ QuicFramerTest,
+ ::testing::ValuesIn(AllSupportedVersionsIncludingTls()));
+
+TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearEpochStart) {
+ // A few quick manual sanity checks.
+ CheckCalculatePacketNumber(UINT64_C(1), QuicPacketNumber());
+ CheckCalculatePacketNumber(kEpoch + 1, QuicPacketNumber(kMask));
+ CheckCalculatePacketNumber(kEpoch, QuicPacketNumber(kMask));
+ for (uint64_t j = 0; j < 10; j++) {
+ CheckCalculatePacketNumber(j, QuicPacketNumber());
+ CheckCalculatePacketNumber(kEpoch - 1 - j, QuicPacketNumber());
+ }
+
+ // Cases where the last number was close to the start of the range.
+ for (QuicPacketNumber last = QuicPacketNumber(1); last < QuicPacketNumber(10);
+ last++) {
+ // Small numbers should not wrap (even if they're out of order).
+ for (uint64_t j = 0; j < 10; j++) {
+ CheckCalculatePacketNumber(j, last);
+ }
+
+ // Large numbers should not wrap either (because we're near 0 already).
+ for (uint64_t j = 0; j < 10; j++) {
+ CheckCalculatePacketNumber(kEpoch - 1 - j, last);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearEpochEnd) {
+ // Cases where the last number was close to the end of the range
+ for (uint64_t i = 0; i < 10; i++) {
+ QuicPacketNumber last = QuicPacketNumber(kEpoch - i);
+
+ // Small numbers should wrap.
+ for (uint64_t j = 0; j < 10; j++) {
+ CheckCalculatePacketNumber(kEpoch + j, last);
+ }
+
+ // Large numbers should not (even if they're out of order).
+ for (uint64_t j = 0; j < 10; j++) {
+ CheckCalculatePacketNumber(kEpoch - 1 - j, last);
+ }
+ }
+}
+
+// Next check where we're in a non-zero epoch to verify we handle
+// reverse wrapping, too.
+TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearPrevEpoch) {
+ const uint64_t prev_epoch = 1 * kEpoch;
+ const uint64_t cur_epoch = 2 * kEpoch;
+ // Cases where the last number was close to the start of the range
+ for (uint64_t i = 0; i < 10; i++) {
+ QuicPacketNumber last = QuicPacketNumber(cur_epoch + i);
+ // Small number should not wrap (even if they're out of order).
+ for (uint64_t j = 0; j < 10; j++) {
+ CheckCalculatePacketNumber(cur_epoch + j, last);
+ }
+
+ // But large numbers should reverse wrap.
+ for (uint64_t j = 0; j < 10; j++) {
+ uint64_t num = kEpoch - 1 - j;
+ CheckCalculatePacketNumber(prev_epoch + num, last);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearNextEpoch) {
+ const uint64_t cur_epoch = 2 * kEpoch;
+ const uint64_t next_epoch = 3 * kEpoch;
+ // Cases where the last number was close to the end of the range
+ for (uint64_t i = 0; i < 10; i++) {
+ QuicPacketNumber last = QuicPacketNumber(next_epoch - 1 - i);
+
+ // Small numbers should wrap.
+ for (uint64_t j = 0; j < 10; j++) {
+ CheckCalculatePacketNumber(next_epoch + j, last);
+ }
+
+ // but large numbers should not (even if they're out of order).
+ for (uint64_t j = 0; j < 10; j++) {
+ uint64_t num = kEpoch - 1 - j;
+ CheckCalculatePacketNumber(cur_epoch + num, last);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, CalculatePacketNumberFromWireNearNextMax) {
+ const uint64_t max_number = std::numeric_limits<uint64_t>::max();
+ const uint64_t max_epoch = max_number & ~kMask;
+
+ // Cases where the last number was close to the end of the range
+ for (uint64_t i = 0; i < 10; i++) {
+ // Subtract 1, because the expected next packet number is 1 more than the
+ // last packet number.
+ QuicPacketNumber last = QuicPacketNumber(max_number - i - 1);
+
+ // Small numbers should not wrap, because they have nowhere to go.
+ for (uint64_t j = 0; j < 10; j++) {
+ CheckCalculatePacketNumber(max_epoch + j, last);
+ }
+
+ // Large numbers should not wrap either.
+ for (uint64_t j = 0; j < 10; j++) {
+ uint64_t num = kEpoch - 1 - j;
+ CheckCalculatePacketNumber(max_epoch + num, last);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, EmptyPacket) {
+ char packet[] = {0x00};
+ QuicEncryptedPacket encrypted(packet, 0, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
+}
+
+TEST_P(QuicFramerTest, LargePacket) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[kMaxIncomingPacketSize + 1] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x78, 0x56, 0x34, 0x12,
+ // private flags
+ 0x00,
+ };
+ unsigned char packet44[kMaxIncomingPacketSize + 1] = {
+ // type (short header 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x78, 0x56, 0x34, 0x12,
+ };
+ unsigned char packet46[kMaxIncomingPacketSize + 1] = {
+ // type (short header 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x78, 0x56, 0x34, 0x12,
+ };
+ // clang-format on
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ const size_t header_size = GetPacketHeaderSize(
+ framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
+
+ memset(p + header_size, 0, kMaxIncomingPacketSize - header_size);
+
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+
+ ASSERT_TRUE(visitor_.header_.get());
+ // Make sure we've parsed the packet header, so we can send an error.
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ // Make sure the correct error is propagated.
+ EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error());
+ EXPECT_EQ("Packet too large.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, PacketHeader) {
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"Unable to read public flags.",
+ {0x28}},
+ // connection_id
+ {"Unable to read ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments = packet;
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_FALSE(visitor_.header_->reset_flag);
+ EXPECT_FALSE(visitor_.header_->version_flag);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER);
+}
+
+TEST_P(QuicFramerTest, LongPacketHeader) {
+ // clang-format off
+ PacketFragments packet44 = {
+ // type (long header with packet type INITIAL)
+ {"Unable to read type.",
+ {0xFF}},
+ // version tag
+ {"Unable to read protocol version.",
+ {QUIC_VERSION_BYTES}},
+ // connection_id length
+ {"Unable to read ConnectionId length.",
+ {0x50}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+ PacketFragments packet46 = {
+ // type (long header with packet type INITIAL)
+ {"Unable to read type.",
+ {0xC3}},
+ // version tag
+ {"Unable to read protocol version.",
+ {QUIC_VERSION_BYTES}},
+ // connection_id length
+ {"Unable to read ConnectionId length.",
+ {0x50}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+ // clang-format on
+
+ if (framer_.transport_version() <= QUIC_VERSION_43 ||
+ QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ return;
+ }
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44;
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_FALSE(visitor_.header_->reset_flag);
+ EXPECT_TRUE(visitor_.header_->version_flag);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ CheckFramingBoundaries(
+ framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet44,
+ QUIC_INVALID_PACKET_HEADER);
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicFramerPeer::SetLastSerializedConnectionId(&framer_,
+ FramerTestConnectionId());
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (0 byte connection_id)
+ {"Unable to read public flags.",
+ {0x20}},
+ // connection_id
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"Unable to read type.",
+ {0x32}},
+ // connection_id
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"Unable to read type.",
+ {0x43}},
+ // connection_id
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_FALSE(visitor_.header_->reset_flag);
+ EXPECT_FALSE(visitor_.header_->version_flag);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER);
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) {
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (0 byte connection_id)
+ {"Unable to read public flags.",
+ {0x29}},
+ // connection_id
+ {"Unable to read ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // version tag
+ {"Unable to read protocol version.",
+ {QUIC_VERSION_BYTES}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+
+ PacketFragments packet44 = {
+ // type (long header with packet type ZERO_RTT_PROTECTED)
+ {"Unable to read type.",
+ {0xFC}},
+ // version tag
+ {"Unable to read protocol version.",
+ {QUIC_VERSION_BYTES}},
+ // connection_id length
+ {"Unable to read ConnectionId length.",
+ {0x50}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+
+ PacketFragments packet46 = {
+ // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes
+ // packet number)
+ {"Unable to read type.",
+ {0xD3}},
+ // version tag
+ {"Unable to read protocol version.",
+ {QUIC_VERSION_BYTES}},
+ // connection_id length
+ {"Unable to read ConnectionId length.",
+ {0x50}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+
+ PacketFragments packet99 = {
+ // type (long header with packet type ZERO_RTT_PROTECTED and 4 bytes
+ // packet number)
+ {"Unable to read type.",
+ {0xD3}},
+ // version tag
+ {"Unable to read protocol version.",
+ {QUIC_VERSION_BYTES}},
+ // connection_id length
+ {"Unable to read ConnectionId length.",
+ {0x50}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // long header packet length
+ {"Unable to read long header payload length.",
+ {0x04}},
+ // packet number
+ {"Long header payload length longer than packet.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet);
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_FALSE(visitor_.header_->reset_flag);
+ EXPECT_TRUE(visitor_.header_->version_flag);
+ EXPECT_EQ(GetParam(), visitor_.header_->version);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER);
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith4BytePacketNumber) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2);
+
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id and 4 byte packet number)
+ {"Unable to read public flags.",
+ {0x28}},
+ // connection_id
+ {"Unable to read ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"Unable to read type.",
+ {0x32}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"Unable to read type.",
+ {0x43}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x12, 0x34, 0x56, 0x78}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_FALSE(visitor_.header_->reset_flag);
+ EXPECT_FALSE(visitor_.header_->version_flag);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER);
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith2BytePacketNumber) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2);
+
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id and 2 byte packet number)
+ {"Unable to read public flags.",
+ {0x18}},
+ // connection_id
+ {"Unable to read ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x56, 0x78}},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 2 byte packet number)
+ {"Unable to read type.",
+ {0x31}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x56, 0x78}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 2 byte packet number)
+ {"Unable to read type.",
+ {0x41}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x56, 0x78}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_FALSE(visitor_.header_->reset_flag);
+ EXPECT_FALSE(visitor_.header_->version_flag);
+ EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER);
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith1BytePacketNumber) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2);
+
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id and 1 byte packet number)
+ {"Unable to read public flags.",
+ {0x08}},
+ // connection_id
+ {"Unable to read ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x78}},
+ };
+
+ PacketFragments packet44 = {
+ // type (8 byte connection_id and 1 byte packet number)
+ {"Unable to read type.",
+ {0x30}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x78}},
+ };
+
+ PacketFragments packet46 = {
+ // type (8 byte connection_id and 1 byte packet number)
+ {"Unable to read type.",
+ {0x40}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x78}},
+ };
+
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_FALSE(visitor_.header_->reset_flag);
+ EXPECT_FALSE(visitor_.header_->version_flag);
+ EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_PACKET_HEADER);
+}
+
+TEST_P(QuicFramerTest, PacketNumberDecreasesThenIncreases) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // Test the case when a packet is received from the past and future packet
+ // numbers are still calculated relative to the largest received packet.
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber - 2;
+
+ QuicFrames frames = {QuicFrame(QuicPaddingFrame())};
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ QuicEncryptedPacket encrypted(data->data(), data->length(), false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length);
+ EXPECT_EQ(kPacketNumber - 2, visitor_.header_->packet_number);
+
+ // Receive a 1 byte packet number.
+ header.packet_number = kPacketNumber;
+ header.packet_number_length = PACKET_1BYTE_PACKET_NUMBER;
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ data = BuildDataPacket(header, frames);
+ QuicEncryptedPacket encrypted1(data->data(), data->length(), false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted1));
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ // Process a 2 byte packet number 256 packets ago.
+ header.packet_number = kPacketNumber - 256;
+ header.packet_number_length = PACKET_2BYTE_PACKET_NUMBER;
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ data = BuildDataPacket(header, frames);
+ QuicEncryptedPacket encrypted2(data->data(), data->length(), false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted2));
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length);
+ EXPECT_EQ(kPacketNumber - 256, visitor_.header_->packet_number);
+
+ // Process another 1 byte packet number and ensure it works.
+ header.packet_number = kPacketNumber - 1;
+ header.packet_number_length = PACKET_1BYTE_PACKET_NUMBER;
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ data = BuildDataPacket(header, frames);
+ QuicEncryptedPacket encrypted3(data->data(), data->length(), false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted3));
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.header_->destination_connection_id);
+ EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length);
+ EXPECT_EQ(kPacketNumber - 1, visitor_.header_->packet_number);
+}
+
+TEST_P(QuicFramerTest, PacketWithDiversificationNonce) {
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags: includes nonce flag
+ 0x2C,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // nonce
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet44[] = {
+ // type: Long header with packet type ZERO_RTT_PROTECTED
+ 0xFC,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // nonce
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+
+ // frame type (padding)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet46[] = {
+ // type: Long header with packet type ZERO_RTT_PROTECTED and 1 byte packet
+ // number.
+ 0xD0,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x78,
+ // nonce
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+
+ // frame type (padding)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet99[] = {
+ // type: Long header with packet type ZERO_RTT_PROTECTED and 1 byte packet
+ // number.
+ 0xD0,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x26,
+ // packet number
+ 0x78,
+ // nonce
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+
+ // frame type (padding)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ if (framer_.version().handshake_protocol != PROTOCOL_QUIC_CRYPTO) {
+ return;
+ }
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ ASSERT_TRUE(visitor_.header_->nonce != nullptr);
+ for (char i = 0; i < 32; ++i) {
+ EXPECT_EQ(i, (*visitor_.header_->nonce)[static_cast<size_t>(i)]);
+ }
+ EXPECT_EQ(1u, visitor_.padding_frames_.size());
+ EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes);
+}
+
+TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) {
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id, version flag and an unknown flag)
+ 0x29,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // version tag
+ 'Q', '0', '0', '0',
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet44[] = {
+ // type (long header with packet type ZERO_RTT_PROTECTED)
+ 0xFC,
+ // version tag
+ 'Q', '0', '0', '0',
+ // connection_id length
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(
+ AsChars(framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet),
+ false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(0, visitor_.frame_count_);
+ EXPECT_EQ(1, visitor_.version_mismatch_);
+ EXPECT_EQ(1u, visitor_.padding_frames_.size());
+ EXPECT_EQ(5, visitor_.padding_frames_[0]->num_padding_bytes);
+}
+
+TEST_P(QuicFramerTest, PaddingFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // paddings
+ 0x00, 0x00,
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // paddings
+ 0x00, 0x00,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // paddings
+ 0x00, 0x00,
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // paddings
+ 0x00, 0x00,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // paddings
+ 0x00, 0x00,
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // paddings
+ 0x00, 0x00,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // paddings
+ 0x00, 0x00,
+ // frame type - IETF_STREAM with FIN, LEN, and OFFSET bits set.
+ 0x08 | 0x01 | 0x02 | 0x04,
+
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // paddings
+ 0x00, 0x00,
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(2u, visitor_.padding_frames_.size());
+ EXPECT_EQ(2, visitor_.padding_frames_[0]->num_padding_bytes);
+ EXPECT_EQ(2, visitor_.padding_frames_[1]->num_padding_bytes);
+ EXPECT_EQ(kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+}
+
+TEST_P(QuicFramerTest, StreamFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFF}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFF}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFF}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type - IETF_STREAM with FIN, LEN, and OFFSET bits set.
+ {"",
+ { 0x08 | 0x01 | 0x02 | 0x04 }},
+ // stream id
+ {"Unable to read stream_id.",
+ {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read stream data offset.",
+ {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // data length
+ {"Unable to read stream data length.",
+ {kVarInt62OneByte + 0x0c}},
+ // data
+ {"Unable to read frame data.",
+ { 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA);
+}
+
+// Test an empty (no data) stream frame.
+TEST_P(QuicFramerTest, EmptyStreamFrame) {
+ // Only the IETF QUIC spec explicitly says that empty
+ // stream frames are supported.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type - IETF_STREAM with FIN, LEN, and OFFSET bits set.
+ {"",
+ { 0x08 | 0x01 | 0x02 | 0x04 }},
+ // stream id
+ {"Unable to read stream_id.",
+ {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read stream data offset.",
+ {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // data length
+ {"Unable to read stream data length.",
+ {kVarInt62OneByte + 0x00}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ EXPECT_EQ(visitor_.stream_frames_[0].get()->data_length, 0u);
+
+ CheckFramingBoundaries(packet, QUIC_INVALID_STREAM_DATA);
+}
+
+TEST_P(QuicFramerTest, MissingDiversificationNonce) {
+ if (framer_.version().handshake_protocol != PROTOCOL_QUIC_CRYPTO) {
+ // TLS does not use diversification nonces.
+ return;
+ }
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ decrypter_ = new test::TestDecrypter();
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_INITIAL, QuicMakeUnique<NullDecrypter>(
+ Perspective::IS_CLIENT));
+ framer_.InstallDecrypter(ENCRYPTION_ZERO_RTT,
+ std::unique_ptr<QuicDecrypter>(decrypter_));
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT));
+ framer_.SetAlternativeDecrypter(
+ ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false);
+ }
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+
+ unsigned char packet44[] = {
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+ 0xFC,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+
+ unsigned char packet46[] = {
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+ 0xD3,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+
+ unsigned char packet99[] = {
+ // type (long header, ZERO_RTT_PROTECTED, 4-byte packet number)
+ 0xD3,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
+ // IETF long header payload length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_length = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_length = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() >= QUIC_VERSION_46) {
+ p = packet46;
+ p_length = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() >= QUIC_VERSION_44) {
+ p = packet44;
+ p_length = QUIC_ARRAYSIZE(packet44);
+ }
+ QuicEncryptedPacket encrypted(AsChars(p), p_length, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ if (framer_.transport_version() >= QUIC_VERSION_44) {
+ // Cannot read diversification nonce.
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
+ EXPECT_EQ("Unable to read nonce.", framer_.detailed_error());
+ } else {
+ EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error());
+ }
+}
+
+TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) {
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ // This test is nonsensical for IETF Quic.
+ return;
+ }
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFE}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments = packet;
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ // Stream ID should be the last 3 bytes of kStreamId.
+ EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA);
+}
+
+TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFD}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFD}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFD}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_STREAM frame with LEN, FIN, and OFFSET bits set)
+ {"",
+ {0x08 | 0x01 | 0x02 | 0x04}},
+ // stream id
+ {"Unable to read stream_id.",
+ {kVarInt62TwoBytes + 0x03, 0x04}},
+ // offset
+ {"Unable to read stream data offset.",
+ {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // data length
+ {"Unable to read stream data length.",
+ {kVarInt62OneByte + 0x0c}},
+ // data
+ {"Unable to read frame data.",
+ { 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ // Stream ID should be the last 2 bytes of kStreamId.
+ EXPECT_EQ(0x0000FFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA);
+}
+
+TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFC}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFC}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFC}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_STREAM frame with LEN, FIN, and OFFSET bits set)
+ {"",
+ {0x08 | 0x01 | 0x02 | 0x04}},
+ // stream id
+ {"Unable to read stream_id.",
+ {kVarInt62OneByte + 0x04}},
+ // offset
+ {"Unable to read stream data offset.",
+ {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // data length
+ {"Unable to read stream data length.",
+ {kVarInt62OneByte + 0x0c}},
+ // data
+ {"Unable to read frame data.",
+ { 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ // Stream ID should be the last 1 byte of kStreamId.
+ EXPECT_EQ(0x000000FF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_DATA);
+}
+
+TEST_P(QuicFramerTest, StreamFrameWithVersion) {
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (version, 8 byte connection_id)
+ {"",
+ {0x29}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // version tag
+ {"",
+ {QUIC_VERSION_BYTES}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFE}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet44 = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED)
+ {"",
+ {0xFC}},
+ // version tag
+ {"",
+ {QUIC_VERSION_BYTES}},
+ // connection_id length
+ {"",
+ {0x50}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFE}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet46 = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ {"",
+ {0xD3}},
+ // version tag
+ {"",
+ {QUIC_VERSION_BYTES}},
+ // connection_id length
+ {"",
+ {0x50}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stream frame with fin)
+ {"",
+ {0xFE}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x02, 0x03, 0x04}},
+ // offset
+ {"Unable to read offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ {"Unable to read frame data.",
+ {
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+
+ PacketFragments packet99 = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ {"",
+ {0xD3}},
+ // version tag
+ {"",
+ {QUIC_VERSION_BYTES}},
+ // connection_id length
+ {"",
+ {0x50}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // long header packet length
+ {"",
+ {0x1E}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ {"",
+ {0x08 | 0x01 | 0x02 | 0x04}},
+ // stream id
+ {"Long header payload length longer than packet.",
+ {kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04}},
+ // offset
+ {"Long header payload length longer than packet.",
+ {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // data length
+ {"Long header payload length longer than packet.",
+ {kVarInt62OneByte + 0x0c}},
+ // data
+ {"Long header payload length longer than packet.",
+ { 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+ // clang-format on
+
+ QuicVariableLengthIntegerLength retry_token_length_length =
+ VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ size_t retry_token_length = 0;
+ QuicVariableLengthIntegerLength length_length =
+ QuicVersionHasLongHeaderLengths(framer_.transport_version())
+ ? VARIABLE_LENGTH_INTEGER_LENGTH_1
+ : VARIABLE_LENGTH_INTEGER_LENGTH_0;
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
+ retry_token_length_length, retry_token_length, length_length));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ // Stream ID should be the last 3 bytes of kStreamId.
+ EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ CheckFramingBoundaries(fragments,
+ framer_.transport_version() == QUIC_VERSION_99
+ ? QUIC_INVALID_PACKET_HEADER
+ : QUIC_INVALID_STREAM_DATA);
+}
+
+TEST_P(QuicFramerTest, RejectPacket) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ visitor_.accept_packet_ = false;
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (STREAM Frame with FIN, LEN, and OFFSET bits set)
+ 0x10 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (STREAM Frame with FIN, LEN, and OFFSET bits set)
+ 0x10 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+ QuicEncryptedPacket encrypted(AsChars(p),
+ framer_.transport_version() > QUIC_VERSION_43
+ ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet),
+ false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ ASSERT_EQ(0u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+}
+
+TEST_P(QuicFramerTest, RejectPublicHeader) {
+ visitor_.accept_public_header_ = false;
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 1 byte packet number)
+ 0x30,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x01,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 1 byte packet number)
+ 0x40,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x01,
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(
+ framer_.transport_version() > QUIC_VERSION_44
+ ? AsChars(packet46)
+ : (framer_.transport_version() > QUIC_VERSION_43 ? AsChars(packet44)
+ : AsChars(packet)),
+ framer_.transport_version() > QUIC_VERSION_44
+ ? QUIC_ARRAYSIZE(packet46)
+ : (framer_.transport_version() > QUIC_VERSION_43
+ ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet)),
+ false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_FALSE(visitor_.header_->packet_number.IsInitialized());
+}
+
+TEST_P(QuicFramerTest, AckFrameOneAckBlock) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x2C}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (ack frame)
+ // (one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ {0x45}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x12, 0x34}},
+ // num timestamps.
+ {"Unable to read num received packets.",
+ {0x00}}
+ };
+
+ PacketFragments packet44 = {
+ // type (short packet, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (ack frame)
+ // (one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ {0x45}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x12, 0x34}},
+ // num timestamps.
+ {"Unable to read num received packets.",
+ {0x00}}
+ };
+
+ PacketFragments packet46 = {
+ // type (short packet, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (ack frame)
+ // (one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ {0x45}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x12, 0x34}},
+ // num timestamps.
+ {"Unable to read num received packets.",
+ {0x00}}
+ };
+
+ PacketFragments packet99 = {
+ // type (short packet, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_ACK)
+ // (one ack block, 2 byte largest observed, 2 byte block length)
+ // IETF-Quic ignores the bit-fields in the ack type, all of
+ // that information is encoded elsewhere in the frame.
+ {"",
+ {0x02}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {kVarInt62TwoBytes + 0x12, 0x34}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {kVarInt62OneByte + 0x00}},
+ // Ack block count (0 -- no blocks after the first)
+ {"Unable to read ack block count.",
+ {kVarInt62OneByte + 0x00}},
+ // first ack block length - 1.
+ // IETF Quic defines the ack block's value as the "number of
+ // packets that preceed the largest packet number in the block"
+ // which for the 1st ack block is the largest acked field,
+ // above. This means that if we are acking just packet 0x1234
+ // then the 1st ack block will be 0.
+ {"Unable to read first ack block length.",
+ {kVarInt62TwoBytes + 0x12, 0x33}}
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(kSmallLargestObserved, LargestAcked(frame));
+ ASSERT_EQ(4660u, frame.packets.NumPacketsSlow());
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA);
+}
+
+// This test checks that the ack frame processor correctly identifies
+// and handles the case where the first ack block is larger than the
+// largest_acked packet.
+TEST_P(QuicFramerTest, FirstAckFrameUnderflow) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x2C}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (ack frame)
+ // (one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ {0x45}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x88, 0x88}},
+ // num timestamps.
+ {"Underflow with first ack block length 34952 largest acked is 4660.",
+ {0x00}}
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (ack frame)
+ // (one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ {0x45}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x88, 0x88}},
+ // num timestamps.
+ {"Underflow with first ack block length 34952 largest acked is 4660.",
+ {0x00}}
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (ack frame)
+ // (one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ {0x45}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x88, 0x88}},
+ // num timestamps.
+ {"Underflow with first ack block length 34952 largest acked is 4660.",
+ {0x00}}
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_ACK)
+ {"",
+ {0x02}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {kVarInt62TwoBytes + 0x12, 0x34}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {kVarInt62OneByte + 0x00}},
+ // Ack block count (0 -- no blocks after the first)
+ {"Unable to read ack block count.",
+ {kVarInt62OneByte + 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {kVarInt62TwoBytes + 0x28, 0x88}}
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA);
+}
+
+// This test checks that the ack frame processor correctly identifies
+// and handles the case where the third ack block's gap is larger than the
+// available space in the ack range.
+TEST_P(QuicFramerTest, ThirdAckBlockUnderflowGap) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // for now, only v99
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_ACK frame)
+ {"",
+ {0x02}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {kVarInt62OneByte + 63}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {kVarInt62OneByte + 0x00}},
+ // Ack block count (2 -- 2 blocks after the first)
+ {"Unable to read ack block count.",
+ {kVarInt62OneByte + 0x02}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {kVarInt62OneByte + 13}}, // Ack 14 packets, range 50..63 (inclusive)
+
+ {"Unable to read gap block value.",
+ {kVarInt62OneByte + 9}}, // Gap 10 packets, 40..49 (inclusive)
+ {"Unable to read ack block value.",
+ {kVarInt62OneByte + 9}}, // Ack 10 packets, 30..39 (inclusive)
+ {"Unable to read gap block value.",
+ {kVarInt62OneByte + 29}}, // A gap of 30 packets (0..29 inclusive)
+ // should be too big, leaving no room
+ // for the ack.
+ {"Underflow with gap block length 30 previous ack block start is 30.",
+ {kVarInt62OneByte + 10}}, // Don't care
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(
+ framer_.detailed_error(),
+ "Underflow with gap block length 30 previous ack block start is 30.");
+ CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA);
+}
+
+// This test checks that the ack frame processor correctly identifies
+// and handles the case where the third ack block's length is larger than the
+// available space in the ack range.
+TEST_P(QuicFramerTest, ThirdAckBlockUnderflowAck) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // for now, only v99
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_ACK frame)
+ {"",
+ {0x02}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {kVarInt62OneByte + 63}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {kVarInt62OneByte + 0x00}},
+ // Ack block count (2 -- 2 blocks after the first)
+ {"Unable to read ack block count.",
+ {kVarInt62OneByte + 0x02}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {kVarInt62OneByte + 13}}, // only 50 packet numbers "left"
+
+ {"Unable to read gap block value.",
+ {kVarInt62OneByte + 10}}, // Only 40 packet numbers left
+ {"Unable to read ack block value.",
+ {kVarInt62OneByte + 10}}, // only 30 packet numbers left.
+ {"Unable to read gap block value.",
+ {kVarInt62OneByte + 1}}, // Gap is OK, 29 packet numbers left
+ {"Unable to read ack block value.",
+ {kVarInt62OneByte + 30}}, // Use up all 30, should be an error
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(framer_.detailed_error(),
+ "Underflow with ack block length 31 latest ack block end is 25.");
+ CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA);
+}
+
+// Tests a variety of ack block wrap scenarios. For example, if the
+// N-1th block causes packet 0 to be acked, then a gap would wrap
+// around to 0x3fffffff ffffffff... Make sure we detect this
+// condition.
+TEST_P(QuicFramerTest, AckBlockUnderflowGapWrap) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // for now, only v99
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_ACK frame)
+ {"",
+ {0x02}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {kVarInt62OneByte + 10}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {kVarInt62OneByte + 0x00}},
+ // Ack block count (1 -- 1 blocks after the first)
+ {"Unable to read ack block count.",
+ {kVarInt62OneByte + 1}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {kVarInt62OneByte + 9}}, // Ack packets 1..10 (inclusive)
+
+ {"Unable to read gap block value.",
+ {kVarInt62OneByte + 1}}, // Gap of 2 packets (-1...0), should wrap
+ {"Underflow with gap block length 2 previous ack block start is 1.",
+ {kVarInt62OneByte + 9}}, // irrelevant
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(framer_.detailed_error(),
+ "Underflow with gap block length 2 previous ack block start is 1.");
+ CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA);
+}
+
+// As AckBlockUnderflowGapWrap, but in this test, it's the ack
+// component of the ack-block that causes the wrap, not the gap.
+TEST_P(QuicFramerTest, AckBlockUnderflowAckWrap) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // for now, only v99
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_ACK frame)
+ {"",
+ {0x02}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {kVarInt62OneByte + 10}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {kVarInt62OneByte + 0x00}},
+ // Ack block count (1 -- 1 blocks after the first)
+ {"Unable to read ack block count.",
+ {kVarInt62OneByte + 1}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {kVarInt62OneByte + 6}}, // Ack packets 4..10 (inclusive)
+
+ {"Unable to read gap block value.",
+ {kVarInt62OneByte + 1}}, // Gap of 2 packets (2..3)
+ {"Unable to read ack block value.",
+ {kVarInt62OneByte + 9}}, // Should wrap.
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(framer_.detailed_error(),
+ "Underflow with ack block length 10 latest ack block end is 1.");
+ CheckFramingBoundaries(packet99, QUIC_INVALID_ACK_DATA);
+}
+
+// An ack block that acks the entire range, 1...0x3fffffffffffffff
+TEST_P(QuicFramerTest, AckBlockAcksEverything) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // for now, only v99
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_ACK frame)
+ {"",
+ {0x02}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {kVarInt62EightBytes + 0x3f, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {kVarInt62OneByte + 0x00}},
+ // Ack block count No additional blocks
+ {"Unable to read ack block count.",
+ {kVarInt62OneByte + 0}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {kVarInt62EightBytes + 0x3f, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xfe}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(1u, frame.packets.NumIntervals());
+ EXPECT_EQ(kLargestIetfLargestObserved, LargestAcked(frame));
+ EXPECT_EQ(kLargestIetfLargestObserved.ToUint64(),
+ frame.packets.NumPacketsSlow());
+}
+
+// This test looks for a malformed ack where
+// - There is a largest-acked value (that is, the frame is acking
+// something,
+// - But the length of the first ack block is 0 saying that no frames
+// are being acked with the largest-acked value or there are no
+// additional ack blocks.
+//
+TEST_P(QuicFramerTest, AckFrameFirstAckBlockLengthZero) {
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // Not applicable to version 99 -- first ack block contains the
+ // number of packets that preceed the largest_acked packet.
+ // A value of 0 means no packets preceed --- that the block's
+ // length is 1. Therefore the condition that this test checks can
+ // not arise.
+ return;
+ }
+
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ { 0x2C }},
+ // connection_id
+ {"",
+ { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
+ // packet number
+ {"",
+ { 0x12, 0x34, 0x56, 0x78 }},
+
+ // frame type (ack frame)
+ // (more than one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ { 0x65 }},
+ // largest acked
+ {"Unable to read largest acked.",
+ { 0x12, 0x34 }},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ { 0x00, 0x00 }},
+ // num ack blocks ranges.
+ {"Unable to read num of ack blocks.",
+ { 0x01 }},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ { 0x00, 0x00 }},
+ // gap to next block.
+ { "First block length is zero.",
+ { 0x01 }},
+ // ack block length.
+ { "First block length is zero.",
+ { 0x0e, 0xaf }},
+ // Number of timestamps.
+ { "First block length is zero.",
+ { 0x00 }},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ { 0x32 }},
+ // connection_id
+ {"",
+ { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
+ // packet number
+ {"",
+ { 0x12, 0x34, 0x56, 0x78 }},
+
+ // frame type (ack frame)
+ // (more than one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ { 0x65 }},
+ // largest acked
+ {"Unable to read largest acked.",
+ { 0x12, 0x34 }},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ { 0x00, 0x00 }},
+ // num ack blocks ranges.
+ {"Unable to read num of ack blocks.",
+ { 0x01 }},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ { 0x00, 0x00 }},
+ // gap to next block.
+ { "First block length is zero.",
+ { 0x01 }},
+ // ack block length.
+ { "First block length is zero.",
+ { 0x0e, 0xaf }},
+ // Number of timestamps.
+ { "First block length is zero.",
+ { 0x00 }},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ { 0x43 }},
+ // connection_id
+ {"",
+ { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
+ // packet number
+ {"",
+ { 0x12, 0x34, 0x56, 0x78 }},
+
+ // frame type (ack frame)
+ // (more than one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ { 0x65 }},
+ // largest acked
+ {"Unable to read largest acked.",
+ { 0x12, 0x34 }},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ { 0x00, 0x00 }},
+ // num ack blocks ranges.
+ {"Unable to read num of ack blocks.",
+ { 0x01 }},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ { 0x00, 0x00 }},
+ // gap to next block.
+ { "First block length is zero.",
+ { 0x01 }},
+ // ack block length.
+ { "First block length is zero.",
+ { 0x0e, 0xaf }},
+ // Number of timestamps.
+ { "First block length is zero.",
+ { 0x00 }},
+ };
+
+ // clang-format on
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_INVALID_ACK_DATA, framer_.error());
+
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA);
+}
+
+TEST_P(QuicFramerTest, AckFrameOneAckBlockMaxLength) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x2C}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (ack frame)
+ // (one ack block, 4 byte largest observed, 2 byte block length)
+ {"",
+ {0x49}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34, 0x56, 0x78}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x12, 0x34}},
+ // num timestamps.
+ {"Unable to read num received packets.",
+ {0x00}}
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x56, 0x78, 0x9A, 0xBC}},
+ // frame type (ack frame)
+ // (one ack block, 4 byte largest observed, 2 byte block length)
+ {"",
+ {0x49}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34, 0x56, 0x78}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x12, 0x34}},
+ // num timestamps.
+ {"Unable to read num received packets.",
+ {0x00}}
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x56, 0x78, 0x9A, 0xBC}},
+ // frame type (ack frame)
+ // (one ack block, 4 byte largest observed, 2 byte block length)
+ {"",
+ {0x49}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {0x12, 0x34, 0x56, 0x78}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {0x00, 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {0x12, 0x34}},
+ // num timestamps.
+ {"Unable to read num received packets.",
+ {0x00}}
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x56, 0x78, 0x9A, 0xBC}},
+ // frame type (IETF_ACK frame)
+ {"",
+ {0x02}},
+ // largest acked
+ {"Unable to read largest acked.",
+ {kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x78}},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ {kVarInt62OneByte + 0x00}},
+ // Number of ack blocks after first
+ {"Unable to read ack block count.",
+ {kVarInt62OneByte + 0x00}},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ {kVarInt62TwoBytes + 0x12, 0x33}}
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(kPacketNumber, LargestAcked(frame));
+ ASSERT_EQ(4660u, frame.packets.NumPacketsSlow());
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA);
+}
+
+// Tests ability to handle multiple ackblocks after the first ack
+// block. Non-version-99 tests include multiple timestamps as well.
+TEST_P(QuicFramerTest, AckFrameTwoTimeStampsMultipleAckBlocks) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ { 0x2C }},
+ // connection_id
+ {"",
+ { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
+ // packet number
+ {"",
+ { 0x12, 0x34, 0x56, 0x78 }},
+
+ // frame type (ack frame)
+ // (more than one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ { 0x65 }},
+ // largest acked
+ {"Unable to read largest acked.",
+ { 0x12, 0x34 }},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ { 0x00, 0x00 }},
+ // num ack blocks ranges.
+ {"Unable to read num of ack blocks.",
+ { 0x04 }},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ { 0x00, 0x01 }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x01 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x0e, 0xaf }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0xff }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x00, 0x00 }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x91 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x01, 0xea }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x05 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x00, 0x04 }},
+ // Number of timestamps.
+ { "Unable to read num received packets.",
+ { 0x02 }},
+ // Delta from largest observed.
+ { "Unable to read sequence delta in received packets.",
+ { 0x01 }},
+ // Delta time.
+ { "Unable to read time delta in received packets.",
+ { 0x76, 0x54, 0x32, 0x10 }},
+ // Delta from largest observed.
+ { "Unable to read sequence delta in received packets.",
+ { 0x02 }},
+ // Delta time.
+ { "Unable to read incremental time delta in received packets.",
+ { 0x32, 0x10 }},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ { 0x32 }},
+ // connection_id
+ {"",
+ { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
+ // packet number
+ {"",
+ { 0x12, 0x34, 0x56, 0x78 }},
+
+ // frame type (ack frame)
+ // (more than one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ { 0x65 }},
+ // largest acked
+ {"Unable to read largest acked.",
+ { 0x12, 0x34 }},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ { 0x00, 0x00 }},
+ // num ack blocks ranges.
+ {"Unable to read num of ack blocks.",
+ { 0x04 }},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ { 0x00, 0x01 }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x01 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x0e, 0xaf }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0xff }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x00, 0x00 }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x91 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x01, 0xea }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x05 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x00, 0x04 }},
+ // Number of timestamps.
+ { "Unable to read num received packets.",
+ { 0x02 }},
+ // Delta from largest observed.
+ { "Unable to read sequence delta in received packets.",
+ { 0x01 }},
+ // Delta time.
+ { "Unable to read time delta in received packets.",
+ { 0x76, 0x54, 0x32, 0x10 }},
+ // Delta from largest observed.
+ { "Unable to read sequence delta in received packets.",
+ { 0x02 }},
+ // Delta time.
+ { "Unable to read incremental time delta in received packets.",
+ { 0x32, 0x10 }},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ { 0x43 }},
+ // connection_id
+ {"",
+ { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
+ // packet number
+ {"",
+ { 0x12, 0x34, 0x56, 0x78 }},
+
+ // frame type (ack frame)
+ // (more than one ack block, 2 byte largest observed, 2 byte block length)
+ {"",
+ { 0x65 }},
+ // largest acked
+ {"Unable to read largest acked.",
+ { 0x12, 0x34 }},
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ { 0x00, 0x00 }},
+ // num ack blocks ranges.
+ {"Unable to read num of ack blocks.",
+ { 0x04 }},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ { 0x00, 0x01 }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x01 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x0e, 0xaf }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0xff }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x00, 0x00 }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x91 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x01, 0xea }},
+ // gap to next block.
+ { "Unable to read gap to next ack block.",
+ { 0x05 }},
+ // ack block length.
+ { "Unable to ack block length.",
+ { 0x00, 0x04 }},
+ // Number of timestamps.
+ { "Unable to read num received packets.",
+ { 0x02 }},
+ // Delta from largest observed.
+ { "Unable to read sequence delta in received packets.",
+ { 0x01 }},
+ // Delta time.
+ { "Unable to read time delta in received packets.",
+ { 0x76, 0x54, 0x32, 0x10 }},
+ // Delta from largest observed.
+ { "Unable to read sequence delta in received packets.",
+ { 0x02 }},
+ // Delta time.
+ { "Unable to read incremental time delta in received packets.",
+ { 0x32, 0x10 }},
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ { 0x43 }},
+ // connection_id
+ {"",
+ { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 }},
+ // packet number
+ {"",
+ { 0x12, 0x34, 0x56, 0x78 }},
+
+ // frame type (IETF_ACK frame)
+ {"",
+ { 0x02 }},
+ // largest acked
+ {"Unable to read largest acked.",
+ { kVarInt62TwoBytes + 0x12, 0x34 }}, // = 4660
+ // Zero delta time.
+ {"Unable to read ack delay time.",
+ { kVarInt62OneByte + 0x00 }},
+ // number of additional ack blocks
+ {"Unable to read ack block count.",
+ { kVarInt62OneByte + 0x03 }},
+ // first ack block length.
+ {"Unable to read first ack block length.",
+ { kVarInt62OneByte + 0x00 }}, // 1st block length = 1
+
+ // Additional ACK Block #1
+ // gap to next block.
+ { "Unable to read gap block value.",
+ { kVarInt62OneByte + 0x00 }}, // gap of 1 packet
+ // ack block length.
+ { "Unable to read ack block value.",
+ { kVarInt62TwoBytes + 0x0e, 0xae }}, // 3759
+
+ // pre-version-99 test includes an ack block of 0 length. this
+ // can not happen in version 99. ergo the second block is not
+ // present in the v99 test and the gap length of the next block
+ // is the sum of the two gaps in the pre-version-99 tests.
+ // Additional ACK Block #2
+ // gap to next block.
+ { "Unable to read gap block value.",
+ { kVarInt62TwoBytes + 0x01, 0x8f }}, // Gap is 400 (0x190) pkts
+ // ack block length.
+ { "Unable to read ack block value.",
+ { kVarInt62TwoBytes + 0x01, 0xe9 }}, // block is 389 (x1ea) pkts
+
+ // Additional ACK Block #3
+ // gap to next block.
+ { "Unable to read gap block value.",
+ { kVarInt62OneByte + 0x04 }}, // Gap is 5 packets.
+ // ack block length.
+ { "Unable to read ack block value.",
+ { kVarInt62OneByte + 0x03 }}, // block is 3 packets.
+ };
+
+ // clang-format on
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+
+ framer_.set_process_timestamps(true);
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(kSmallLargestObserved, LargestAcked(frame));
+ ASSERT_EQ(4254u, frame.packets.NumPacketsSlow());
+ EXPECT_EQ(4u, frame.packets.NumIntervals());
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ EXPECT_EQ(0u, frame.received_packet_times.size());
+ } else {
+ EXPECT_EQ(2u, frame.received_packet_times.size());
+ }
+ CheckFramingBoundaries(fragments, QUIC_INVALID_ACK_DATA);
+}
+
+TEST_P(QuicFramerTest, AckFrameTimeStampDeltaTooHigh) {
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 1 byte largest observed, 1 byte block length)
+ 0x40,
+ // largest acked
+ 0x01,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x01,
+ // num timestamps.
+ 0x01,
+ // Delta from largest observed.
+ 0x01,
+ // Delta time.
+ 0x10, 0x32, 0x54, 0x76,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 1 byte largest observed, 1 byte block length)
+ 0x40,
+ // largest acked
+ 0x01,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x01,
+ // num timestamps.
+ 0x01,
+ // Delta from largest observed.
+ 0x01,
+ // Delta time.
+ 0x10, 0x32, 0x54, 0x76,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 1 byte largest observed, 1 byte block length)
+ 0x40,
+ // largest acked
+ 0x01,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x01,
+ // num timestamps.
+ 0x01,
+ // Delta from largest observed.
+ 0x01,
+ // Delta time.
+ 0x10, 0x32, 0x54, 0x76,
+ };
+ // clang-format on
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ return;
+ }
+ QuicEncryptedPacket encrypted(
+ AsChars(framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet)),
+ QUIC_ARRAYSIZE(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_TRUE(QuicTextUtils::StartsWith(
+ framer_.detailed_error(), "delta_from_largest_observed too high"));
+}
+
+TEST_P(QuicFramerTest, AckFrameTimeStampSecondDeltaTooHigh) {
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 1 byte largest observed, 1 byte block length)
+ 0x40,
+ // largest acked
+ 0x03,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x03,
+ // num timestamps.
+ 0x02,
+ // Delta from largest observed.
+ 0x01,
+ // Delta time.
+ 0x10, 0x32, 0x54, 0x76,
+ // Delta from largest observed.
+ 0x03,
+ // Delta time.
+ 0x10, 0x32,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 1 byte largest observed, 1 byte block length)
+ 0x40,
+ // largest acked
+ 0x03,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x03,
+ // num timestamps.
+ 0x02,
+ // Delta from largest observed.
+ 0x01,
+ // Delta time.
+ 0x10, 0x32, 0x54, 0x76,
+ // Delta from largest observed.
+ 0x03,
+ // Delta time.
+ 0x10, 0x32,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 1 byte largest observed, 1 byte block length)
+ 0x40,
+ // largest acked
+ 0x03,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x03,
+ // num timestamps.
+ 0x02,
+ // Delta from largest observed.
+ 0x01,
+ // Delta time.
+ 0x10, 0x32, 0x54, 0x76,
+ // Delta from largest observed.
+ 0x03,
+ // Delta time.
+ 0x10, 0x32,
+ };
+ // clang-format on
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ return;
+ }
+ QuicEncryptedPacket encrypted(
+ AsChars(framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet)),
+ QUIC_ARRAYSIZE(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_TRUE(QuicTextUtils::StartsWith(
+ framer_.detailed_error(), "delta_from_largest_observed too high"));
+}
+
+TEST_P(QuicFramerTest, NewStopWaitingFrame) {
+ if (version_.transport_version == QUIC_VERSION_99) {
+ return;
+ }
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x2C}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stop waiting frame)
+ {"",
+ {0x06}},
+ // least packet number awaiting an ack, delta from packet number.
+ {"Unable to read least unacked delta.",
+ {0x00, 0x00, 0x00, 0x08}}
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stop waiting frame)
+ {"",
+ {0x06}},
+ // least packet number awaiting an ack, delta from packet number.
+ {"Unable to read least unacked delta.",
+ {0x00, 0x00, 0x00, 0x08}}
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (stop waiting frame)
+ {"",
+ {0x06}},
+ // least packet number awaiting an ack, delta from packet number.
+ {"Unable to read least unacked delta.",
+ {0x00, 0x00, 0x00, 0x08}}
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.stop_waiting_frames_.size());
+ const QuicStopWaitingFrame& frame = *visitor_.stop_waiting_frames_[0];
+ EXPECT_EQ(kLeastUnacked, frame.least_unacked);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_STOP_WAITING_DATA);
+}
+
+TEST_P(QuicFramerTest, InvalidNewStopWaitingFrame) {
+ if (version_.transport_version == QUIC_VERSION_99) {
+ return;
+ }
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x2C,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (stop waiting frame)
+ 0x06,
+ // least packet number awaiting an ack, delta from packet number.
+ 0x13, 0x34, 0x56, 0x78,
+ 0x9A, 0xA8,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (stop waiting frame)
+ 0x06,
+ // least packet number awaiting an ack, delta from packet number.
+ 0x57, 0x78, 0x9A, 0xA8,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (stop waiting frame)
+ 0x06,
+ // least packet number awaiting an ack, delta from packet number.
+ 0x57, 0x78, 0x9A, 0xA8,
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(
+ AsChars(framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet)),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet),
+ false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_INVALID_STOP_WAITING_DATA, framer_.error());
+ EXPECT_EQ("Invalid unacked delta.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, RstStreamFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (rst stream frame)
+ {"",
+ {0x01}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // sent byte offset
+ {"Unable to read rst stream sent byte offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // error code
+ {"Unable to read rst stream error code.",
+ {0x00, 0x00, 0x00, 0x01}}
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (rst stream frame)
+ {"",
+ {0x01}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // sent byte offset
+ {"Unable to read rst stream sent byte offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // error code
+ {"Unable to read rst stream error code.",
+ {0x00, 0x00, 0x00, 0x01}}
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (rst stream frame)
+ {"",
+ {0x01}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // sent byte offset
+ {"Unable to read rst stream sent byte offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // error code
+ {"Unable to read rst stream error code.",
+ {0x00, 0x00, 0x00, 0x01}}
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_RST_STREAM frame)
+ {"",
+ {0x04}},
+ // stream id
+ {"Unable to read rst stream stream id.",
+ {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}},
+ // application error code
+ {"Unable to read rst stream error code.",
+ {0x00, 0x01}}, // Not varint62 encoded
+ // Final Offset
+ {"Unable to read rst stream sent byte offset.",
+ {kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54}}
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(kStreamId, visitor_.rst_stream_frame_.stream_id);
+ EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code);
+ EXPECT_EQ(kStreamOffset, visitor_.rst_stream_frame_.byte_offset);
+ CheckFramingBoundaries(fragments, QUIC_INVALID_RST_STREAM_DATA);
+}
+
+TEST_P(QuicFramerTest, ConnectionCloseFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (connection close frame)
+ {"",
+ {0x02}},
+ // error code
+ {"Unable to read connection close error code.",
+ {0x00, 0x00, 0x00, 0x11}},
+ {"Unable to read connection close error details.",
+ {
+ // error details length
+ 0x0, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n'}
+ }
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (connection close frame)
+ {"",
+ {0x02}},
+ // error code
+ {"Unable to read connection close error code.",
+ {0x00, 0x00, 0x00, 0x11}},
+ {"Unable to read connection close error details.",
+ {
+ // error details length
+ 0x0, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n'}
+ }
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (connection close frame)
+ {"",
+ {0x02}},
+ // error code
+ {"Unable to read connection close error code.",
+ {0x00, 0x00, 0x00, 0x11}},
+ {"Unable to read connection close error details.",
+ {
+ // error details length
+ 0x0, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n'}
+ }
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_CONNECTION_CLOSE frame)
+ {"",
+ {0x1c}},
+ // error code
+ {"Unable to read connection close error code.",
+ {0x00, 0x11}},
+ {"Unable to read connection close frame type.",
+ {kVarInt62TwoBytes + 0x12, 0x34 }},
+ {"Unable to read connection close error details.",
+ {
+ // error details length
+ kVarInt62OneByte + 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n'}
+ }
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0x11u, static_cast<unsigned>(
+ visitor_.connection_close_frame_.quic_error_code));
+ EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ EXPECT_EQ(0x1234u,
+ visitor_.connection_close_frame_.transport_close_frame_type);
+ }
+
+ ASSERT_EQ(0u, visitor_.ack_frames_.size());
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_CONNECTION_CLOSE_DATA);
+}
+
+// Test the CONNECTION_CLOSE/Application variant.
+TEST_P(QuicFramerTest, ApplicationCloseFrame) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame does not exist in versions other than 99.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_CONNECTION_CLOSE/Application frame)
+ {"",
+ {0x1d}},
+ // error code
+ {"Unable to read connection close error code.",
+ {0x00, 0x11}},
+ {"Unable to read connection close error details.",
+ {
+ // error details length
+ kVarInt62OneByte + 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n'}
+ }
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+
+ EXPECT_EQ(IETF_QUIC_APPLICATION_CONNECTION_CLOSE,
+ visitor_.connection_close_frame_.close_type);
+ EXPECT_EQ(122u, visitor_.connection_close_frame_.extracted_error_code);
+ EXPECT_EQ(0x11, visitor_.connection_close_frame_.quic_error_code);
+ EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details);
+
+ ASSERT_EQ(0u, visitor_.ack_frames_.size());
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_CONNECTION_CLOSE_DATA);
+}
+
+TEST_P(QuicFramerTest, GoAwayFrame) {
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // This frame is not supported in version 99.
+ return;
+ }
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (go away frame)
+ {"",
+ {0x03}},
+ // error code
+ {"Unable to read go away error code.",
+ {0x00, 0x00, 0x00, 0x09}},
+ // stream id
+ {"Unable to read last good stream id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // stream id
+ {"Unable to read goaway reason.",
+ {
+ // error details length
+ 0x0, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n'}
+ }
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (go away frame)
+ {"",
+ {0x03}},
+ // error code
+ {"Unable to read go away error code.",
+ {0x00, 0x00, 0x00, 0x09}},
+ // stream id
+ {"Unable to read last good stream id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // stream id
+ {"Unable to read goaway reason.",
+ {
+ // error details length
+ 0x0, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n'}
+ }
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (go away frame)
+ {"",
+ {0x03}},
+ // error code
+ {"Unable to read go away error code.",
+ {0x00, 0x00, 0x00, 0x09}},
+ // stream id
+ {"Unable to read last good stream id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // stream id
+ {"Unable to read goaway reason.",
+ {
+ // error details length
+ 0x0, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n'}
+ }
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(kStreamId, visitor_.goaway_frame_.last_good_stream_id);
+ EXPECT_EQ(0x9, visitor_.goaway_frame_.error_code);
+ EXPECT_EQ("because I can", visitor_.goaway_frame_.reason_phrase);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_GOAWAY_DATA);
+}
+
+TEST_P(QuicFramerTest, WindowUpdateFrame) {
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // This frame is not in version 99, see MaxDataFrame and MaxStreamDataFrame
+ // for Version 99 equivalents.
+ return;
+ }
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (window update frame)
+ {"",
+ {0x04}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // byte offset
+ {"Unable to read window byte_offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (window update frame)
+ {"",
+ {0x04}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // byte offset
+ {"Unable to read window byte_offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (window update frame)
+ {"",
+ {0x04}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ // byte offset
+ {"Unable to read window byte_offset.",
+ {0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ };
+
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet);
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(kStreamId, visitor_.window_update_frame_.stream_id);
+ EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.byte_offset);
+
+ CheckFramingBoundaries(fragments, QUIC_INVALID_WINDOW_UPDATE_DATA);
+}
+
+TEST_P(QuicFramerTest, MaxDataFrame) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is available only in version 99.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_MAX_DATA frame)
+ {"",
+ {0x10}},
+ // byte offset
+ {"Can not read MAX_DATA byte-offset",
+ {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()),
+ visitor_.window_update_frame_.stream_id);
+ EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.byte_offset);
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_MAX_DATA_FRAME_DATA);
+}
+
+TEST_P(QuicFramerTest, MaxStreamDataFrame) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame available only in version 99.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_MAX_STREAM_DATA frame)
+ {"",
+ {0x11}},
+ // stream id
+ {"Can not read MAX_STREAM_DATA stream id",
+ {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}},
+ // byte offset
+ {"Can not read MAX_STREAM_DATA byte-count",
+ {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(kStreamId, visitor_.window_update_frame_.stream_id);
+ EXPECT_EQ(kStreamOffset, visitor_.window_update_frame_.byte_offset);
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_MAX_STREAM_DATA_FRAME_DATA);
+}
+
+TEST_P(QuicFramerTest, BlockedFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (8 byte connection_id)
+ {"",
+ {0x28}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (blocked frame)
+ {"",
+ {0x05}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ };
+
+ PacketFragments packet44 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (blocked frame)
+ {"",
+ {0x05}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (blocked frame)
+ {"",
+ {0x05}},
+ // stream id
+ {"Unable to read stream_id.",
+ {0x01, 0x02, 0x03, 0x04}},
+ };
+
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_STREAM_BLOCKED frame)
+ {"",
+ {0x15}},
+ // stream id
+ {"Can not read stream blocked stream id.",
+ {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}},
+ // Offset
+ {"Can not read stream blocked offset.",
+ {kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54}},
+ };
+ // clang-format on
+
+ PacketFragments& fragments =
+ framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : (framer_.transport_version() > QUIC_VERSION_43 ? packet44
+ : packet));
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ EXPECT_EQ(kStreamOffset, visitor_.blocked_frame_.offset);
+ } else {
+ EXPECT_EQ(0u, visitor_.blocked_frame_.offset);
+ }
+ EXPECT_EQ(kStreamId, visitor_.blocked_frame_.stream_id);
+
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ CheckFramingBoundaries(fragments, QUIC_INVALID_STREAM_BLOCKED_DATA);
+ } else {
+ CheckFramingBoundaries(fragments, QUIC_INVALID_BLOCKED_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, PingFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type
+ 0x07,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type
+ 0x07,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_PING frame)
+ 0x01,
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(
+ AsChars(framer_.transport_version() == QUIC_VERSION_99
+ ? packet99
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? packet46
+ : framer_.transport_version() > QUIC_VERSION_43
+ ? packet44
+ : packet)),
+ framer_.transport_version() == QUIC_VERSION_99
+ ? QUIC_ARRAYSIZE(packet99)
+ : (framer_.transport_version() > QUIC_VERSION_44
+ ? QUIC_ARRAYSIZE(packet46)
+ : framer_.transport_version() > QUIC_VERSION_43
+ ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet)),
+ false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(1u, visitor_.ping_frames_.size());
+
+ // No need to check the PING frame boundaries because it has no payload.
+}
+
+TEST_P(QuicFramerTest, MessageFrame) {
+ if (framer_.transport_version() <= QUIC_VERSION_44) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet45 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x32}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // message frame type.
+ {"",
+ { 0x21 }},
+ // message length
+ {"Unable to read message length",
+ {0x07}},
+ // message data
+ {"Unable to read message data",
+ {'m', 'e', 's', 's', 'a', 'g', 'e'}},
+ // message frame no length.
+ {"",
+ { 0x20 }},
+ // message data
+ {{},
+ {'m', 'e', 's', 's', 'a', 'g', 'e', '2'}},
+ };
+
+ PacketFragments packet46 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // message frame type.
+ {"",
+ { 0x21 }},
+ // message length
+ {"Unable to read message length",
+ {0x07}},
+ // message data
+ {"Unable to read message data",
+ {'m', 'e', 's', 's', 'a', 'g', 'e'}},
+ // message frame no length.
+ {"",
+ { 0x20 }},
+ // message data
+ {{},
+ {'m', 'e', 's', 's', 'a', 'g', 'e', '2'}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(AssemblePacketFromFragments(
+ framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet45));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ ASSERT_EQ(2u, visitor_.message_frames_.size());
+ EXPECT_EQ(7u, visitor_.message_frames_[0]->message_length);
+ EXPECT_EQ(8u, visitor_.message_frames_[1]->message_length);
+
+ CheckFramingBoundaries(
+ framer_.transport_version() > QUIC_VERSION_44 ? packet46 : packet45,
+ QUIC_INVALID_MESSAGE_DATA);
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketV33) {
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (public reset, 8 byte connection_id)
+ {"",
+ {0x0A}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ {"Unable to read reset message.",
+ {
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected packet number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ }
+ }
+ };
+ // clang-format on
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_reset_packet_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.public_reset_packet_->connection_id);
+ EXPECT_EQ(kNonceProof, visitor_.public_reset_packet_->nonce_proof);
+ EXPECT_EQ(
+ IpAddressFamily::IP_UNSPEC,
+ visitor_.public_reset_packet_->client_address.host().address_family());
+
+ CheckFramingBoundaries(packet, QUIC_INVALID_PUBLIC_RST_PACKET);
+}
+
+TEST_P(QuicFramerTest, PublicResetPacket) {
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (public reset, 8 byte connection_id)
+ {"",
+ {0x0E}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ {"Unable to read reset message.",
+ {
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected packet number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ }
+ }
+ };
+ // clang-format on
+
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_reset_packet_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.public_reset_packet_->connection_id);
+ EXPECT_EQ(kNonceProof, visitor_.public_reset_packet_->nonce_proof);
+ EXPECT_EQ(
+ IpAddressFamily::IP_UNSPEC,
+ visitor_.public_reset_packet_->client_address.host().address_family());
+
+ CheckFramingBoundaries(packet, QUIC_INVALID_PUBLIC_RST_PACKET);
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithTrailingJunk) {
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte connection_id)
+ 0x0A,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected packet number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // trailing junk
+ 'j', 'u', 'n', 'k',
+ };
+ // clang-format on
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ ASSERT_EQ(QUIC_INVALID_PUBLIC_RST_PACKET, framer_.error());
+ EXPECT_EQ("Unable to read reset message.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithClientAddress) {
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (public reset, 8 byte connection_id)
+ {"",
+ {0x0A}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ {"Unable to read reset message.",
+ {
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x03, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // tag kCADR
+ 'C', 'A', 'D', 'R',
+ // end offset 24
+ 0x18, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected packet number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // client address: 4.31.198.44:443
+ 0x02, 0x00,
+ 0x04, 0x1F, 0xC6, 0x2C,
+ 0xBB, 0x01,
+ }
+ }
+ };
+ // clang-format on
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_reset_packet_.get());
+ EXPECT_EQ(FramerTestConnectionId(),
+ visitor_.public_reset_packet_->connection_id);
+ EXPECT_EQ(kNonceProof, visitor_.public_reset_packet_->nonce_proof);
+ EXPECT_EQ("4.31.198.44",
+ visitor_.public_reset_packet_->client_address.host().ToString());
+ EXPECT_EQ(443, visitor_.public_reset_packet_->client_address.port());
+
+ CheckFramingBoundaries(packet, QUIC_INVALID_PUBLIC_RST_PACKET);
+}
+
+TEST_P(QuicFramerTest, IetfStatelessResetPacket) {
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short packet, 1 byte packet number)
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // Random bytes
+ 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44,
+ 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44,
+ 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44,
+ 0x01, 0x11, 0x02, 0x22, 0x03, 0x33, 0x04, 0x44,
+ // stateless reset token
+ 0xB5, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+ if (framer_.transport_version() <= QUIC_VERSION_43) {
+ return;
+ }
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ decrypter_ = new test::TestDecrypter();
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_INITIAL, QuicMakeUnique<NullDecrypter>(
+ Perspective::IS_CLIENT));
+ framer_.InstallDecrypter(ENCRYPTION_ZERO_RTT,
+ std::unique_ptr<QuicDecrypter>(decrypter_));
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT));
+ framer_.SetAlternativeDecrypter(
+ ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false);
+ }
+ // This packet cannot be decrypted because diversification nonce is missing.
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.stateless_reset_packet_.get());
+ EXPECT_EQ(kTestStatelessResetToken,
+ visitor_.stateless_reset_packet_->stateless_reset_token);
+}
+
+TEST_P(QuicFramerTest, IetfStatelessResetPacketInvalidStatelessResetToken) {
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short packet, 1 byte packet number)
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // stateless reset token
+ 0xB6, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+ if (framer_.transport_version() <= QUIC_VERSION_43) {
+ return;
+ }
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ decrypter_ = new test::TestDecrypter();
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_INITIAL, QuicMakeUnique<NullDecrypter>(
+ Perspective::IS_CLIENT));
+ framer_.InstallDecrypter(ENCRYPTION_ZERO_RTT,
+ std::unique_ptr<QuicDecrypter>(decrypter_));
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_CLIENT));
+ framer_.SetAlternativeDecrypter(
+ ENCRYPTION_ZERO_RTT, std::unique_ptr<QuicDecrypter>(decrypter_), false);
+ }
+ // This packet cannot be decrypted because diversification nonce is missing.
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_DECRYPTION_FAILURE, framer_.error());
+ ASSERT_FALSE(visitor_.stateless_reset_packet_);
+}
+
+TEST_P(QuicFramerTest, VersionNegotiationPacket) {
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (version, 8 byte connection_id)
+ {"",
+ {0x29}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // version tag
+ {"Unable to read supported version in negotiation.",
+ {QUIC_VERSION_BYTES,
+ 'Q', '2', '.', '0'}},
+ };
+
+ PacketFragments packet44 = {
+ // type (long header)
+ {"",
+ {0x8F}},
+ // version tag
+ {"",
+ {0x00, 0x00, 0x00, 0x00}},
+ {"",
+ {0x05}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // Supported versions
+ {"Unable to read supported version in negotiation.",
+ {QUIC_VERSION_BYTES,
+ 'Q', '2', '.', '0'}},
+ };
+ // clang-format on
+
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ PacketFragments& fragments =
+ framer_.transport_version() > QUIC_VERSION_43 ? packet44 : packet;
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(fragments));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.version_negotiation_packet_.get());
+ EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size());
+ EXPECT_EQ(GetParam(), visitor_.version_negotiation_packet_->versions[0]);
+
+ // Remove the last version from the packet so that every truncated
+ // version of the packet is invalid, otherwise checking boundaries
+ // is annoyingly complicated.
+ for (size_t i = 0; i < 4; ++i) {
+ fragments.back().fragment.pop_back();
+ }
+ CheckFramingBoundaries(fragments, QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
+}
+
+TEST_P(QuicFramerTest, OldVersionNegotiationPacket) {
+ // clang-format off
+ PacketFragments packet = {
+ // public flags (version, 8 byte connection_id)
+ {"",
+ {0x2D}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // version tag
+ {"Unable to read supported version in negotiation.",
+ {QUIC_VERSION_BYTES,
+ 'Q', '2', '.', '0'}},
+ };
+ // clang-format on
+
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.version_negotiation_packet_.get());
+ EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size());
+ EXPECT_EQ(GetParam(), visitor_.version_negotiation_packet_->versions[0]);
+
+ // Remove the last version from the packet so that every truncated
+ // version of the packet is invalid, otherwise checking boundaries
+ // is annoyingly complicated.
+ for (size_t i = 0; i < 4; ++i) {
+ packet.back().fragment.pop_back();
+ }
+ CheckFramingBoundaries(packet, QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
+}
+
+TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicFrames frames = {QuicFrame(QuicPaddingFrame())};
+
+ // clang-format off
+ unsigned char packet[kMaxOutgoingPacketSize] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet44[kMaxOutgoingPacketSize] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet46[kMaxOutgoingPacketSize] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet99[kMaxOutgoingPacketSize] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+
+ uint64_t header_size = GetPacketHeaderSize(
+ framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
+ memset(p + header_size + 1, 0x00, kMaxOutgoingPacketSize - header_size - 1);
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(p),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStreamFramePacketWithNewPaddingFrame) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+ QuicStreamFrame stream_frame(kStreamId, true, kStreamOffset,
+ QuicStringPiece("hello world!"));
+ QuicPaddingFrame padding_frame(2);
+ QuicFrames frames = {QuicFrame(padding_frame), QuicFrame(stream_frame),
+ QuicFrame(padding_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // paddings
+ 0x00, 0x00,
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // paddings
+ 0x00, 0x00,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // paddings
+ 0x00, 0x00,
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // paddings
+ 0x00, 0x00,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // paddings
+ 0x00, 0x00,
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // paddings
+ 0x00, 0x00,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // paddings
+ 0x00, 0x00,
+ // frame type (IETF_STREAM with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // paddings
+ 0x00, 0x00,
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number_length = PACKET_4BYTE_PACKET_NUMBER;
+ header.packet_number = kPacketNumber;
+
+ QuicFrames frames = {QuicFrame(QuicPaddingFrame())};
+
+ // clang-format off
+ unsigned char packet[kMaxOutgoingPacketSize] = {
+ // public flags (8 byte connection_id and 4 byte packet number)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet44[kMaxOutgoingPacketSize] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet46[kMaxOutgoingPacketSize] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet99[kMaxOutgoingPacketSize] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+
+ uint64_t header_size = GetPacketHeaderSize(
+ framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
+ memset(p + header_size + 1, 0x00, kMaxOutgoingPacketSize - header_size - 1);
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(p),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number_length = PACKET_2BYTE_PACKET_NUMBER;
+ header.packet_number = kPacketNumber;
+
+ QuicFrames frames = {QuicFrame(QuicPaddingFrame())};
+
+ // clang-format off
+ unsigned char packet[kMaxOutgoingPacketSize] = {
+ // public flags (8 byte connection_id and 2 byte packet number)
+ 0x18,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet44[kMaxOutgoingPacketSize] = {
+ // type (short header, 2 byte packet number)
+ 0x31,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet46[kMaxOutgoingPacketSize] = {
+ // type (short header, 2 byte packet number)
+ 0x41,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet99[kMaxOutgoingPacketSize] = {
+ // type (short header, 2 byte packet number)
+ 0x41,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x56, 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+
+ uint64_t header_size = GetPacketHeaderSize(
+ framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ !kIncludeDiversificationNonce, PACKET_2BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
+ memset(p + header_size + 1, 0x00, kMaxOutgoingPacketSize - header_size - 1);
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(p),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number_length = PACKET_1BYTE_PACKET_NUMBER;
+ header.packet_number = kPacketNumber;
+
+ QuicFrames frames = {QuicFrame(QuicPaddingFrame())};
+
+ // clang-format off
+ unsigned char packet[kMaxOutgoingPacketSize] = {
+ // public flags (8 byte connection_id and 1 byte packet number)
+ 0x08,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet44[kMaxOutgoingPacketSize] = {
+ // type (short header, 1 byte packet number)
+ 0x30,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet46[kMaxOutgoingPacketSize] = {
+ // type (short header, 1 byte packet number)
+ 0x40,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet99[kMaxOutgoingPacketSize] = {
+ // type (short header, 1 byte packet number)
+ 0x40,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x78,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+
+ uint64_t header_size = GetPacketHeaderSize(
+ framer_.transport_version(), PACKET_8BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ !kIncludeDiversificationNonce, PACKET_1BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0);
+ memset(p + header_size + 1, 0x00, kMaxOutgoingPacketSize - header_size - 1);
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(p),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStreamFramePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+ if (QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicStreamFrame stream_frame(kStreamId, true, kStreamOffset,
+ QuicStringPiece("hello world!"));
+
+ QuicFrames frames = {QuicFrame(stream_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAM frame with FIN and OFFSET, no length)
+ 0x08 | 0x01 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = true;
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ header.long_packet_type = ZERO_RTT_PROTECTED;
+ }
+ header.packet_number = kPacketNumber;
+ if (QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicStreamFrame stream_frame(kStreamId, true, kStreamOffset,
+ QuicStringPiece("hello world!"));
+ QuicFrames frames = {QuicFrame(stream_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (version, 8 byte connection_id)
+ 0x2D,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54,
+ // data
+ 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet44[] = {
+ // type (long header with packet type ZERO_RTT_PROTECTED)
+ 0xFC,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54,
+ // data
+ 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet46[] = {
+ // type (long header with packet type ZERO_RTT_PROTECTED)
+ 0xD3,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54,
+ // data
+ 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet99[] = {
+ // type (long header with packet type ZERO_RTT_PROTECTED)
+ 0xD3,
+ // version tag
+ QUIC_VERSION_BYTES,
+ // connection_id length
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // length
+ 0x40, 0x1D,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAM frame with fin and offset, no length)
+ 0x08 | 0x01 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54,
+ // data
+ 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
+ };
+ // clang-format on
+
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildCryptoFramePacket) {
+ if (framer_.transport_version() < QUIC_VERSION_99) {
+ // CRYPTO frames aren't supported prior to v46.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ SimpleDataProducer data_producer;
+ framer_.set_data_producer(&data_producer);
+
+ QuicStringPiece crypto_frame_contents("hello world!");
+ QuicCryptoFrame crypto_frame(ENCRYPTION_INITIAL, kStreamOffset,
+ crypto_frame_contents.length());
+ data_producer.SaveCryptoData(ENCRYPTION_INITIAL, kStreamOffset,
+ crypto_frame_contents);
+
+ QuicFrames frames = {QuicFrame(&crypto_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_CRYPTO frame)
+ 0x06,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // length
+ kVarInt62OneByte + 12,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+ // clang-format on
+
+ size_t packet_size = QUIC_ARRAYSIZE(packet);
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ packet_size);
+}
+
+TEST_P(QuicFramerTest, CryptoFrame) {
+ if (framer_.transport_version() < QUIC_VERSION_99) {
+ // CRYPTO frames aren't supported prior to v46.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_CRYPTO frame)
+ {"",
+ {0x06}},
+ // offset
+ {"",
+ {kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54}},
+ // data length
+ {"Invalid data length.",
+ {kVarInt62OneByte + 12}},
+ // data
+ {"Unable to read frame data.",
+ {'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!'}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+ ASSERT_EQ(1u, visitor_.crypto_frames_.size());
+ QuicCryptoFrame* frame = visitor_.crypto_frames_[0].get();
+ EXPECT_EQ(kStreamOffset, frame->offset);
+ EXPECT_EQ("hello world!",
+ std::string(frame->data_buffer, frame->data_length));
+
+ CheckFramingBoundaries(packet, QUIC_INVALID_FRAME_DATA);
+}
+
+TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) {
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (version, 8 byte connection_id)
+ 0x0D,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // version tag
+ QUIC_VERSION_BYTES,
+ };
+ unsigned char packet44[] = {
+ // type (long header)
+ 0x80,
+ // version tag
+ 0x00, 0x00, 0x00, 0x00,
+ // connection_id length
+ 0x05,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // version tag
+ QUIC_VERSION_BYTES,
+ };
+ // clang-format on
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ QuicConnectionId connection_id = FramerTestConnectionId();
+ std::unique_ptr<QuicEncryptedPacket> data(
+ framer_.BuildVersionNegotiationPacket(
+ connection_id, framer_.transport_version() > QUIC_VERSION_43,
+ SupportedVersions(GetParam())));
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlock) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ // Use kSmallLargestObserved to make this test finished in a short time.
+ QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved);
+ ack_frame.ack_delay_time = QuicTime::Delta::Zero();
+
+ QuicFrames frames = {QuicFrame(&ack_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x45,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x12, 0x34,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x45,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x12, 0x34,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x45,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x12, 0x34,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_ACK frame)
+ 0x02,
+ // largest acked
+ kVarInt62TwoBytes + 0x12, 0x34,
+ // Zero delta time.
+ kVarInt62OneByte + 0x00,
+ // Number of additional ack blocks.
+ kVarInt62OneByte + 0x00,
+ // first ack block length.
+ kVarInt62TwoBytes + 0x12, 0x33,
+ };
+ // clang-format on
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlockMaxLength) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicAckFrame ack_frame = InitAckFrame(kPacketNumber);
+ ack_frame.ack_delay_time = QuicTime::Delta::Zero();
+
+ QuicFrames frames = {QuicFrame(&ack_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 4 byte largest observed, 4 byte block length)
+ 0x4A,
+ // largest acked
+ 0x12, 0x34, 0x56, 0x78,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x12, 0x34, 0x56, 0x78,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 4 byte largest observed, 4 byte block length)
+ 0x4A,
+ // largest acked
+ 0x12, 0x34, 0x56, 0x78,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x12, 0x34, 0x56, 0x78,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (no ack blocks, 4 byte largest observed, 4 byte block length)
+ 0x4A,
+ // largest acked
+ 0x12, 0x34, 0x56, 0x78,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x12, 0x34, 0x56, 0x78,
+ // num timestamps.
+ 0x00,
+ };
+
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_ACK frame)
+ 0x02,
+ // largest acked
+ kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x78,
+ // Zero delta time.
+ kVarInt62OneByte + 0x00,
+ // Nr. of additional ack blocks
+ kVarInt62OneByte + 0x00,
+ // first ack block length.
+ kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x77,
+ };
+ // clang-format on
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacketMultipleAckBlocks) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ // Use kSmallLargestObserved to make this test finished in a short time.
+ QuicAckFrame ack_frame =
+ InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(5)},
+ {QuicPacketNumber(10), QuicPacketNumber(500)},
+ {QuicPacketNumber(900), kSmallMissingPacket},
+ {kSmallMissingPacket + 1, kSmallLargestObserved + 1}});
+ ack_frame.ack_delay_time = QuicTime::Delta::Zero();
+
+ QuicFrames frames = {QuicFrame(&ack_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (has ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x65,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // num ack blocks ranges.
+ 0x04,
+ // first ack block length.
+ 0x00, 0x01,
+ // gap to next block.
+ 0x01,
+ // ack block length.
+ 0x0e, 0xaf,
+ // gap to next block.
+ 0xff,
+ // ack block length.
+ 0x00, 0x00,
+ // gap to next block.
+ 0x91,
+ // ack block length.
+ 0x01, 0xea,
+ // gap to next block.
+ 0x05,
+ // ack block length.
+ 0x00, 0x04,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (has ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x65,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // num ack blocks ranges.
+ 0x04,
+ // first ack block length.
+ 0x00, 0x01,
+ // gap to next block.
+ 0x01,
+ // ack block length.
+ 0x0e, 0xaf,
+ // gap to next block.
+ 0xff,
+ // ack block length.
+ 0x00, 0x00,
+ // gap to next block.
+ 0x91,
+ // ack block length.
+ 0x01, 0xea,
+ // gap to next block.
+ 0x05,
+ // ack block length.
+ 0x00, 0x04,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ // (has ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x65,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // num ack blocks ranges.
+ 0x04,
+ // first ack block length.
+ 0x00, 0x01,
+ // gap to next block.
+ 0x01,
+ // ack block length.
+ 0x0e, 0xaf,
+ // gap to next block.
+ 0xff,
+ // ack block length.
+ 0x00, 0x00,
+ // gap to next block.
+ 0x91,
+ // ack block length.
+ 0x01, 0xea,
+ // gap to next block.
+ 0x05,
+ // ack block length.
+ 0x00, 0x04,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_ACK frame)
+ 0x02,
+ // largest acked
+ kVarInt62TwoBytes + 0x12, 0x34,
+ // Zero delta time.
+ kVarInt62OneByte + 0x00,
+ // num additional ack blocks.
+ kVarInt62OneByte + 0x03,
+ // first ack block length.
+ kVarInt62OneByte + 0x00,
+
+ // gap to next block.
+ kVarInt62OneByte + 0x00,
+ // ack block length.
+ kVarInt62TwoBytes + 0x0e, 0xae,
+
+ // gap to next block.
+ kVarInt62TwoBytes + 0x01, 0x8f,
+ // ack block length.
+ kVarInt62TwoBytes + 0x01, 0xe9,
+
+ // gap to next block.
+ kVarInt62OneByte + 0x04,
+ // ack block length.
+ kVarInt62OneByte + 0x03,
+ };
+ // clang-format on
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacketMaxAckBlocks) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ // Use kSmallLargestObservedto make this test finished in a short time.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_acked = kSmallLargestObserved;
+ ack_frame.ack_delay_time = QuicTime::Delta::Zero();
+ // 300 ack blocks.
+ for (size_t i = 2; i < 2 * 300; i += 2) {
+ ack_frame.packets.Add(QuicPacketNumber(i));
+ }
+ ack_frame.packets.AddRange(QuicPacketNumber(600), kSmallLargestObserved + 1);
+
+ QuicFrames frames = {QuicFrame(&ack_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (ack frame)
+ // (has ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x65,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // num ack blocks ranges.
+ 0xff,
+ // first ack block length.
+ 0x0f, 0xdd,
+ // 255 = 4 * 63 + 3
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (ack frame)
+ // (has ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x65,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // num ack blocks ranges.
+ 0xff,
+ // first ack block length.
+ 0x0f, 0xdd,
+ // 255 = 4 * 63 + 3
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (ack frame)
+ // (has ack blocks, 2 byte largest observed, 2 byte block length)
+ 0x65,
+ // largest acked
+ 0x12, 0x34,
+ // Zero delta time.
+ 0x00, 0x00,
+ // num ack blocks ranges.
+ 0xff,
+ // first ack block length.
+ 0x0f, 0xdd,
+ // 255 = 4 * 63 + 3
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01,
+ // num timestamps.
+ 0x00,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (IETF_ACK frame)
+ 0x02,
+ // largest acked
+ kVarInt62TwoBytes + 0x12, 0x34,
+ // Zero delta time.
+ kVarInt62OneByte + 0x00,
+ // num ack blocks ranges.
+ kVarInt62TwoBytes + 0x01, 0x2b,
+ // first ack block length.
+ kVarInt62TwoBytes + 0x0f, 0xdc,
+ // 255 added blocks of gap_size == 1, ack_size == 1
+#define V99AddedBLOCK kVarInt62OneByte + 0x00, kVarInt62OneByte + 0x00
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+ V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK, V99AddedBLOCK,
+
+#undef V99AddedBLOCK
+ };
+ // clang-format on
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildNewStopWaitingPacket) {
+ if (version_.transport_version > QUIC_VERSION_43) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicStopWaitingFrame stop_waiting_frame;
+ stop_waiting_frame.least_unacked = kLeastUnacked;
+
+ QuicFrames frames = {QuicFrame(stop_waiting_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stop waiting frame)
+ 0x06,
+ // least packet number awaiting an ack, delta from packet number.
+ 0x00, 0x00, 0x00, 0x08,
+ };
+
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildRstFramePacketQuic) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicRstStreamFrame rst_frame;
+ rst_frame.stream_id = kStreamId;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ rst_frame.ietf_error_code = 0x01;
+ } else {
+ rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708);
+ }
+ rst_frame.byte_offset = 0x0807060504030201;
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (rst stream frame)
+ 0x01,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // sent byte offset
+ 0x08, 0x07, 0x06, 0x05,
+ 0x04, 0x03, 0x02, 0x01,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ };
+
+ unsigned char packet44[] = {
+ // type (short packet, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (rst stream frame)
+ 0x01,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // sent byte offset
+ 0x08, 0x07, 0x06, 0x05,
+ 0x04, 0x03, 0x02, 0x01,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ };
+
+ unsigned char packet46[] = {
+ // type (short packet, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (rst stream frame)
+ 0x01,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // sent byte offset
+ 0x08, 0x07, 0x06, 0x05,
+ 0x04, 0x03, 0x02, 0x01,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ };
+
+ unsigned char packet99[] = {
+ // type (short packet, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_RST_STREAM frame)
+ 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // error code (not VarInt32 encoded)
+ 0x00, 0x01,
+ // sent byte offset
+ kVarInt62EightBytes + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01
+ };
+ // clang-format on
+
+ QuicFrames frames = {QuicFrame(&rst_frame)};
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildCloseFramePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicConnectionCloseFrame close_frame;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ close_frame.transport_error_code =
+ static_cast<QuicIetfTransportErrorCodes>(0x11);
+ close_frame.transport_close_frame_type = 0x05;
+ close_frame.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ } else {
+ close_frame.quic_error_code = static_cast<QuicErrorCode>(0x05060708);
+ }
+ close_frame.error_details = "because I can";
+
+ QuicFrames frames = {QuicFrame(&close_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (connection close frame)
+ 0x02,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // error details length
+ 0x00, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (connection close frame)
+ 0x02,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // error details length
+ 0x00, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (connection close frame)
+ 0x02,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // error details length
+ 0x00, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_CONNECTION_CLOSE frame)
+ 0x1c,
+ // error code
+ 0x00, 0x11,
+ // Frame type within the CONNECTION_CLOSE frame
+ kVarInt62OneByte + 0x05,
+ // error details length
+ kVarInt62OneByte + 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildTruncatedCloseFramePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicConnectionCloseFrame close_frame;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ close_frame.transport_error_code = PROTOCOL_VIOLATION; // value is 0x0a
+ EXPECT_EQ(0u, close_frame.transport_close_frame_type);
+ close_frame.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ } else {
+ close_frame.quic_error_code = static_cast<QuicErrorCode>(0x05060708);
+ }
+ close_frame.error_details = std::string(2048, 'A');
+ QuicFrames frames = {QuicFrame(&close_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (connection close frame)
+ 0x02,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // error details length
+ 0x01, 0x00,
+ // error details (truncated to 256 bytes)
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (connection close frame)
+ 0x02,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // error details length
+ 0x01, 0x00,
+ // error details (truncated to 256 bytes)
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (connection close frame)
+ 0x02,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // error details length
+ 0x01, 0x00,
+ // error details (truncated to 256 bytes)
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_CONNECTION_CLOSE frame)
+ 0x1c,
+ // error code
+ 0x00, 0x0a,
+ // Frame type within the CONNECTION_CLOSE frame
+ kVarInt62OneByte + 0x00,
+ // error details length
+ kVarInt62TwoBytes + 0x01, 0x00,
+ // error details (truncated to 256 bytes)
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildApplicationCloseFramePacket) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // Versions other than 99 do not have ApplicationClose
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicConnectionCloseFrame app_close_frame;
+ app_close_frame.quic_error_code = static_cast<QuicErrorCode>(0x11);
+ app_close_frame.error_details = "because I can";
+ app_close_frame.close_type = IETF_QUIC_APPLICATION_CONNECTION_CLOSE;
+
+ QuicFrames frames = {QuicFrame(&app_close_frame)};
+
+ // clang-format off
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_APPLICATION_CLOSE frame)
+ 0x1d,
+ // error code
+ 0x00, 0x11,
+ // error details length
+ kVarInt62OneByte + 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildTruncatedApplicationCloseFramePacket) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // Versions other than 99 do not have this frame.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicConnectionCloseFrame app_close_frame;
+ app_close_frame.quic_error_code = static_cast<QuicErrorCode>(0x11);
+ app_close_frame.error_details = std::string(2048, 'A');
+ app_close_frame.close_type = IETF_QUIC_APPLICATION_CONNECTION_CLOSE;
+
+ QuicFrames frames = {QuicFrame(&app_close_frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_APPLICATION_CLOSE frame)
+ 0x1d,
+ // error code
+ 0x00, 0x11,
+ // error details length
+ kVarInt62TwoBytes + 0x01, 0x00,
+ // error details (truncated to 256 bytes)
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildGoAwayPacket) {
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // This frame type is not supported in version 99.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicGoAwayFrame goaway_frame;
+ goaway_frame.error_code = static_cast<QuicErrorCode>(0x05060708);
+ goaway_frame.last_good_stream_id = kStreamId;
+ goaway_frame.reason_phrase = "because I can";
+
+ QuicFrames frames = {QuicFrame(&goaway_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (go away frame)
+ 0x03,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // error details length
+ 0x00, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (go away frame)
+ 0x03,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // error details length
+ 0x00, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (go away frame)
+ 0x03,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // error details length
+ 0x00, 0x0d,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildTruncatedGoAwayPacket) {
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // This frame type is not supported in version 99.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicGoAwayFrame goaway_frame;
+ goaway_frame.error_code = static_cast<QuicErrorCode>(0x05060708);
+ goaway_frame.last_good_stream_id = kStreamId;
+ goaway_frame.reason_phrase = std::string(2048, 'A');
+
+ QuicFrames frames = {QuicFrame(&goaway_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (go away frame)
+ 0x03,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // error details length
+ 0x01, 0x00,
+ // error details (truncated to 256 bytes)
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (go away frame)
+ 0x03,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // error details length
+ 0x01, 0x00,
+ // error details (truncated to 256 bytes)
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (go away frame)
+ 0x03,
+ // error code
+ 0x05, 0x06, 0x07, 0x08,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // error details length
+ 0x01, 0x00,
+ // error details (truncated to 256 bytes)
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A',
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildWindowUpdatePacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicWindowUpdateFrame window_update_frame;
+ window_update_frame.stream_id = kStreamId;
+ window_update_frame.byte_offset = 0x1122334455667788;
+
+ QuicFrames frames = {QuicFrame(&window_update_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // byte offset
+ 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // byte offset
+ 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // byte offset
+ 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_MAX_STREAM_DATA frame)
+ 0x11,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // byte offset
+ kVarInt62EightBytes + 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88,
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildMaxStreamDataPacket) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is available only in this version.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicWindowUpdateFrame window_update_frame;
+ window_update_frame.stream_id = kStreamId;
+ window_update_frame.byte_offset = 0x1122334455667788;
+
+ QuicFrames frames = {QuicFrame(&window_update_frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_MAX_STREAM_DATA frame)
+ 0x11,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // byte offset
+ kVarInt62EightBytes + 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88,
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildMaxDataPacket) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is available only in this version.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicWindowUpdateFrame window_update_frame;
+ window_update_frame.stream_id =
+ QuicUtils::GetInvalidStreamId(framer_.transport_version());
+ window_update_frame.byte_offset = 0x1122334455667788;
+
+ QuicFrames frames = {QuicFrame(&window_update_frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_MAX_DATA frame)
+ 0x10,
+ // byte offset
+ kVarInt62EightBytes + 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88,
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildBlockedPacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicBlockedFrame blocked_frame;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // For V99, the stream ID must be <invalid> for the frame
+ // to be a BLOCKED frame. if it's valid, it will be a
+ // STREAM_BLOCKED frame.
+ blocked_frame.stream_id =
+ QuicUtils::GetInvalidStreamId(framer_.transport_version());
+ } else {
+ blocked_frame.stream_id = kStreamId;
+ }
+ blocked_frame.offset = kStreamOffset;
+
+ QuicFrames frames = {QuicFrame(&blocked_frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ };
+
+ unsigned char packet44[] = {
+ // type (short packet, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ };
+
+ unsigned char packet46[] = {
+ // type (short packet, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ };
+
+ unsigned char packet99[] = {
+ // type (short packet, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_BLOCKED frame)
+ 0x14,
+ // Offset
+ kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p), p_size);
+}
+
+TEST_P(QuicFramerTest, BuildPingPacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicFrames frames = {QuicFrame(QuicPingFrame())};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type
+ 0x07,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type
+ 0x07,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_PING frame)
+ 0x01,
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(p),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildMessagePacket) {
+ if (framer_.transport_version() <= QUIC_VERSION_44) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ 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));
+ QuicFrames frames = {QuicFrame(&frame), QuicFrame(&frame2)};
+
+ // clang-format off
+ unsigned char packet45[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (message frame)
+ 0x21,
+ // Length
+ 0x07,
+ // Message Data
+ 'm', 'e', 's', 's', 'a', 'g', 'e',
+ // frame type (message frame no length)
+ 0x20,
+ // Message Data
+ 'm', 'e', 's', 's', 'a', 'g', 'e', '2'
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (message frame)
+ 0x21,
+ // Length
+ 0x07,
+ // Message Data
+ 'm', 'e', 's', 's', 'a', 'g', 'e',
+ // frame type (message frame no length)
+ 0x20,
+ // Message Data
+ 'm', 'e', 's', 's', 'a', 'g', 'e', '2'
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_MESSAGE frame)
+ 0x21,
+ // Length
+ 0x07,
+ // Message Data
+ 'm', 'e', 's', 's', 'a', 'g', 'e',
+ // frame type (message frame no length)
+ 0x20,
+ // Message Data
+ 'm', 'e', 's', 's', 'a', 'g', 'e', '2'
+ };
+ // clang-format on
+
+ unsigned char* p = packet45;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ }
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(p),
+ QUIC_ARRAYSIZE(packet45));
+}
+
+// Test that the connectivity probing packet is serialized correctly as a
+// padded PING packet.
+TEST_P(QuicFramerTest, BuildConnectivityProbingPacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ping frame)
+ 0x07,
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type
+ 0x07,
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type
+ 0x07,
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_PING frame)
+ 0x01,
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t packet_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ packet_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ packet_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ packet_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+
+ size_t length = framer_.BuildConnectivityProbingPacket(
+ header, buffer.get(), packet_size, ENCRYPTION_INITIAL);
+
+ EXPECT_NE(0u, length);
+ QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+ header);
+
+ test::CompareCharArraysWithHexError("constructed packet", data.data(),
+ data.length(), AsChars(p), packet_size);
+}
+
+// Test that the path challenge connectivity probing packet is serialized
+// correctly as a padded PATH CHALLENGE packet.
+TEST_P(QuicFramerTest, BuildPaddedPathChallengePacket) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+ QuicPathFrameBuffer payload;
+
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // Path Challenge Frame type (IETF_PATH_CHALLENGE)
+ 0x1a,
+ // 8 "random" bytes, MockRandom makes lots of r's
+ 'r', 'r', 'r', 'r', 'r', 'r', 'r', 'r',
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+ MockRandom randomizer;
+
+ size_t length = framer_.BuildPaddedPathChallengePacket(
+ header, buffer.get(), QUIC_ARRAYSIZE(packet), &payload, &randomizer,
+ ENCRYPTION_INITIAL);
+ EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
+
+ // Payload has the random bytes that were generated. Copy them into packet,
+ // above, before checking that the generated packet is correct.
+ EXPECT_EQ(kQuicPathFrameBufferSize, payload.size());
+
+ QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+ header);
+
+ test::CompareCharArraysWithHexError("constructed packet", data.data(),
+ data.length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+// Several tests that the path response connectivity probing packet is
+// serialized correctly as either a padded and unpadded PATH RESPONSE
+// packet. Also generates packets with 1 and 3 PATH_RESPONSES in them to
+// exercised the single- and multiple- payload cases.
+TEST_P(QuicFramerTest, BuildPathResponsePacket1ResponseUnpadded) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+ QuicPathFrameBuffer payload0 = {
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
+
+ // Build 1 PATH RESPONSE, not padded
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // Path Response Frame type (IETF_PATH_RESPONSE)
+ 0x1b,
+ // 8 "random" bytes
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ };
+ // clang-format on
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+ size_t length = framer_.BuildPathResponsePacket(
+ header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
+ /*is_padded=*/false, ENCRYPTION_INITIAL);
+ EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
+ QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+ header);
+
+ test::CompareCharArraysWithHexError("constructed packet", data.data(),
+ data.length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPathResponsePacket1ResponsePadded) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+ QuicPathFrameBuffer payload0 = {
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
+
+ // Build 1 PATH RESPONSE, padded
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // Path Response Frame type (IETF_PATH_RESPONSE)
+ 0x1b,
+ // 8 "random" bytes
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ // Padding type and pad
+ 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+ size_t length = framer_.BuildPathResponsePacket(
+ header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
+ /*is_padded=*/true, ENCRYPTION_INITIAL);
+ EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
+ QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+ header);
+
+ test::CompareCharArraysWithHexError("constructed packet", data.data(),
+ data.length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPathResponsePacket3ResponsesUnpadded) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+ QuicPathFrameBuffer payload0 = {
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
+ QuicPathFrameBuffer payload1 = {
+ {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}};
+ QuicPathFrameBuffer payload2 = {
+ {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}};
+
+ // Build one packet with 3 PATH RESPONSES, no padding
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // 3 path response frames (IETF_PATH_RESPONSE type byte and payload)
+ 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ };
+ // clang-format on
+
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+ payloads.push_back(payload1);
+ payloads.push_back(payload2);
+ size_t length = framer_.BuildPathResponsePacket(
+ header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
+ /*is_padded=*/false, ENCRYPTION_INITIAL);
+ EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
+ QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+ header);
+
+ test::CompareCharArraysWithHexError("constructed packet", data.data(),
+ data.length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPathResponsePacket3ResponsesPadded) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+ QuicPathFrameBuffer payload0 = {
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
+ QuicPathFrameBuffer payload1 = {
+ {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}};
+ QuicPathFrameBuffer payload2 = {
+ {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28}};
+
+ // Build one packet with 3 PATH RESPONSES, with padding
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // 3 path response frames (IETF_PATH_RESPONSE byte and payload)
+ 0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x1b, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x1b, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ // Padding
+ 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+ payloads.push_back(payload1);
+ payloads.push_back(payload2);
+ size_t length = framer_.BuildPathResponsePacket(
+ header, buffer.get(), QUIC_ARRAYSIZE(packet), payloads,
+ /*is_padded=*/true, ENCRYPTION_INITIAL);
+ EXPECT_EQ(length, QUIC_ARRAYSIZE(packet));
+ QuicPacket data(framer_.transport_version(), buffer.release(), length, true,
+ header);
+
+ test::CompareCharArraysWithHexError("constructed packet", data.data(),
+ data.length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+// Test that the MTU discovery packet is serialized correctly as a PING packet.
+TEST_P(QuicFramerTest, BuildMtuDiscoveryPacket) {
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicFrames frames = {QuicFrame(QuicMtuDiscoveryFrame())};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type
+ 0x07,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type
+ 0x07,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_PING frame)
+ 0x01,
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+
+ test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(p),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPublicResetPacket) {
+ QuicPublicResetPacket reset_packet;
+ reset_packet.connection_id = FramerTestConnectionId();
+ reset_packet.nonce_proof = kNonceProof;
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (1) + padding
+ 0x01, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ };
+ // clang-format on
+
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ std::unique_ptr<QuicEncryptedPacket> data(
+ framer_.BuildPublicResetPacket(reset_packet));
+ ASSERT_TRUE(data != nullptr);
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) {
+ QuicPublicResetPacket reset_packet;
+ reset_packet.connection_id = FramerTestConnectionId();
+ reset_packet.nonce_proof = kNonceProof;
+ reset_packet.client_address =
+ QuicSocketAddress(QuicIpAddress::Loopback4(), 0x1234);
+
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98,
+ 0x76, 0x54, 0x32, 0x10,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kCADR
+ 'C', 'A', 'D', 'R',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // client address
+ 0x02, 0x00,
+ 0x7F, 0x00, 0x00, 0x01,
+ 0x34, 0x12,
+ };
+ // clang-format on
+
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ std::unique_ptr<QuicEncryptedPacket> data(
+ framer_.BuildPublicResetPacket(reset_packet));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPublicResetPacketWithEndpointId) {
+ QuicPublicResetPacket reset_packet;
+ reset_packet.connection_id = FramerTestConnectionId();
+ reset_packet.nonce_proof = kNonceProof;
+ reset_packet.endpoint_id = "FakeServerId";
+
+ // The tag value map in CryptoHandshakeMessage is a std::map, so the two tags
+ // in the packet, kRNON and kEPID, have unspecified ordering w.r.t each other.
+ // clang-format off
+ unsigned char packet_variant1[] = {
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98,
+ 0x76, 0x54, 0x32, 0x10,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kEPID
+ 'E', 'P', 'I', 'D',
+ // end offset 20
+ 0x14, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // Endpoint ID
+ 'F', 'a', 'k', 'e', 'S', 'e', 'r', 'v', 'e', 'r', 'I', 'd',
+ };
+ unsigned char packet_variant2[] = {
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98,
+ 0x76, 0x54, 0x32, 0x10,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kEPID
+ 'E', 'P', 'I', 'D',
+ // end offset 12
+ 0x0C, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 20
+ 0x14, 0x00, 0x00, 0x00,
+ // Endpoint ID
+ 'F', 'a', 'k', 'e', 'S', 'e', 'r', 'v', 'e', 'r', 'I', 'd',
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ };
+ // clang-format on
+
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+
+ std::unique_ptr<QuicEncryptedPacket> data(
+ framer_.BuildPublicResetPacket(reset_packet));
+ ASSERT_TRUE(data != nullptr);
+
+ // Variant 1 ends with char 'd'. Variant 1 ends with char 0xAB.
+ if ('d' == data->data()[data->length() - 1]) {
+ test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(),
+ AsChars(packet_variant1), QUIC_ARRAYSIZE(packet_variant1));
+ } else {
+ test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(),
+ AsChars(packet_variant2), QUIC_ARRAYSIZE(packet_variant2));
+ }
+}
+
+TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) {
+ // clang-format off
+ unsigned char packet44[] = {
+ // type (short header, 1 byte packet number)
+ 0x70,
+ // random packet number
+ 0xFE,
+ // stateless reset token
+ 0xB5, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> data(
+ framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(),
+ kTestStatelessResetToken));
+ ASSERT_TRUE(data != nullptr);
+ // Skip packet number byte which is random in stateless reset packet.
+ test::CompareCharArraysWithHexError("constructed packet", data->data(), 1,
+ AsChars(packet44), 1);
+ const size_t random_bytes_length =
+ data->length() - kPacketHeaderTypeSize - sizeof(kTestStatelessResetToken);
+ EXPECT_EQ(kMinRandomBytesLengthInStatelessReset, random_bytes_length);
+ // Verify stateless reset token is correct.
+ test::CompareCharArraysWithHexError(
+ "constructed packet",
+ data->data() + data->length() - sizeof(kTestStatelessResetToken),
+ sizeof(kTestStatelessResetToken),
+ AsChars(packet44) + QUIC_ARRAYSIZE(packet44) -
+ sizeof(kTestStatelessResetToken),
+ sizeof(kTestStatelessResetToken));
+}
+
+TEST_P(QuicFramerTest, EncryptPacket) {
+ QuicPacketNumber packet_number = kPacketNumber;
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+
+ std::unique_ptr<QuicPacket> raw(new QuicPacket(
+ AsChars(p), QUIC_ARRAYSIZE(packet), false, PACKET_8BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, VARIABLE_LENGTH_INTEGER_LENGTH_0));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length = framer_.EncryptPayload(
+ ENCRYPTION_INITIAL, packet_number, *raw, buffer, kMaxOutgoingPacketSize);
+
+ ASSERT_NE(0u, encrypted_length);
+ EXPECT_TRUE(CheckEncryption(packet_number, raw.get()));
+}
+
+TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) {
+ QuicPacketNumber packet_number = kPacketNumber;
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (version, 8 byte connection_id)
+ 0x29,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // version tag
+ 'Q', '.', '1', '0',
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ unsigned char packet44[] = {
+ // type (long header with packet type ZERO_RTT_PROTECTED)
+ 0xFC,
+ // version tag
+ 'Q', '.', '1', '0',
+ // connection_id length
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ unsigned char packet46[] = {
+ // type (long header with packet type ZERO_RTT_PROTECTED)
+ 0xD3,
+ // version tag
+ 'Q', '.', '1', '0',
+ // connection_id length
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ unsigned char packet99[] = {
+ // type (long header with packet type ZERO_RTT_PROTECTED)
+ 0xD3,
+ // version tag
+ 'Q', '.', '1', '0',
+ // connection_id length
+ 0x50,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+
+ std::unique_ptr<QuicPacket> raw(new QuicPacket(
+ AsChars(p),
+ framer_.transport_version() > QUIC_VERSION_43 ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet),
+ false, PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID,
+ kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_4BYTE_PACKET_NUMBER, VARIABLE_LENGTH_INTEGER_LENGTH_0, 0,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0));
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length = framer_.EncryptPayload(
+ ENCRYPTION_INITIAL, packet_number, *raw, buffer, kMaxOutgoingPacketSize);
+
+ ASSERT_NE(0u, encrypted_length);
+ EXPECT_TRUE(CheckEncryption(packet_number, raw.get()));
+}
+
+TEST_P(QuicFramerTest, AckTruncationLargePacket) {
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // This test is not applicable to this version; the range count is
+ // effectively unlimited
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicAckFrame ack_frame;
+ // Create a packet with just the ack.
+ ack_frame = MakeAckFrameWithAckBlocks(300, 0u);
+ QuicFrames frames = {QuicFrame(&ack_frame)};
+
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ std::unique_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames));
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ framer_.EncryptPayload(ENCRYPTION_INITIAL, header.packet_number,
+ *raw_ack_packet, buffer, kMaxOutgoingPacketSize);
+ ASSERT_NE(0u, encrypted_length);
+ // Now make sure we can turn our ack packet back into an ack frame.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ ASSERT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(buffer, encrypted_length, false)));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(QuicPacketNumber(600u), LargestAcked(processed_ack_frame));
+ ASSERT_EQ(256u, processed_ack_frame.packets.NumPacketsSlow());
+ EXPECT_EQ(QuicPacketNumber(90u), processed_ack_frame.packets.Min());
+ EXPECT_EQ(QuicPacketNumber(600u), processed_ack_frame.packets.Max());
+}
+
+TEST_P(QuicFramerTest, AckTruncationSmallPacket) {
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // This test is not applicable to this version; the range count is
+ // effectively unlimited
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ // Create a packet with just the ack.
+ QuicAckFrame ack_frame;
+ ack_frame = MakeAckFrameWithAckBlocks(300, 0u);
+ QuicFrames frames = {QuicFrame(&ack_frame)};
+
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ std::unique_ptr<QuicPacket> raw_ack_packet(
+ BuildDataPacket(header, frames, 500));
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ framer_.EncryptPayload(ENCRYPTION_INITIAL, header.packet_number,
+ *raw_ack_packet, buffer, kMaxOutgoingPacketSize);
+ ASSERT_NE(0u, encrypted_length);
+ // Now make sure we can turn our ack packet back into an ack frame.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ ASSERT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(buffer, encrypted_length, false)));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(QuicPacketNumber(600u), LargestAcked(processed_ack_frame));
+ ASSERT_EQ(240u, processed_ack_frame.packets.NumPacketsSlow());
+ EXPECT_EQ(QuicPacketNumber(122u), processed_ack_frame.packets.Min());
+ EXPECT_EQ(QuicPacketNumber(600u), processed_ack_frame.packets.Max());
+}
+
+TEST_P(QuicFramerTest, CleanTruncation) {
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ // This test is not applicable to this version; the range count is
+ // effectively unlimited
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicAckFrame ack_frame = InitAckFrame(201);
+
+ // Create a packet with just the ack.
+ QuicFrames frames = {QuicFrame(&ack_frame)};
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ std::unique_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames));
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+
+ char buffer[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ framer_.EncryptPayload(ENCRYPTION_INITIAL, header.packet_number,
+ *raw_ack_packet, buffer, kMaxOutgoingPacketSize);
+ ASSERT_NE(0u, encrypted_length);
+
+ // Now make sure we can turn our ack packet back into an ack frame.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ ASSERT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(buffer, encrypted_length, false)));
+
+ // Test for clean truncation of the ack by comparing the length of the
+ // original packets to the re-serialized packets.
+ frames.clear();
+ frames.push_back(QuicFrame(visitor_.ack_frames_[0].get()));
+
+ size_t original_raw_length = raw_ack_packet->length();
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ raw_ack_packet = BuildDataPacket(header, frames);
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+ EXPECT_EQ(original_raw_length, raw_ack_packet->length());
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+}
+
+TEST_P(QuicFramerTest, StopPacketProcessing) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x28,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+
+ // frame type (ack frame)
+ 0x40,
+ // least packet number awaiting an ack
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xA0,
+ // largest observed packet number
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xBF,
+ // num missing packets
+ 0x01,
+ // missing packet
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xBE,
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+
+ // frame type (ack frame)
+ 0x40,
+ // least packet number awaiting an ack
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xA0,
+ // largest observed packet number
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xBF,
+ // num missing packets
+ 0x01,
+ // missing packet
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xBE,
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+
+ // frame type (ack frame)
+ 0x40,
+ // least packet number awaiting an ack
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xA0,
+ // largest observed packet number
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xBF,
+ // num missing packets
+ 0x01,
+ // missing packet
+ 0x12, 0x34, 0x56, 0x78,
+ 0x9A, 0xBE,
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAM frame with fin, length, and offset bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62TwoBytes + 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+
+ // frame type (ack frame)
+ 0x0d,
+ // largest observed packet number
+ kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x78,
+ // Delta time
+ kVarInt62OneByte + 0x00,
+ // Ack Block count
+ kVarInt62OneByte + 0x01,
+ // First block size (one packet)
+ kVarInt62OneByte + 0x00,
+
+ // Next gap size & ack. Missing all preceding packets
+ kVarInt62FourBytes + 0x12, 0x34, 0x56, 0x77,
+ kVarInt62OneByte + 0x00,
+ };
+ // clang-format on
+
+ MockFramerVisitor visitor;
+ framer_.set_visitor(&visitor);
+ EXPECT_CALL(visitor, OnPacket());
+ EXPECT_CALL(visitor, OnPacketHeader(_));
+ EXPECT_CALL(visitor, OnStreamFrame(_)).WillOnce(Return(false));
+ EXPECT_CALL(visitor, OnPacketComplete());
+ EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)).WillOnce(Return(true));
+ EXPECT_CALL(visitor, OnUnauthenticatedHeader(_)).WillOnce(Return(true));
+ EXPECT_CALL(visitor, OnDecryptedPacket(_));
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+}
+
+static char kTestString[] = "At least 20 characters.";
+static QuicStreamId kTestQuicStreamId = 1;
+static bool ExpectedStreamFrame(const QuicStreamFrame& frame) {
+ return (frame.stream_id == kTestQuicStreamId ||
+ frame.stream_id == QuicUtils::GetCryptoStreamId(QUIC_VERSION_99)) &&
+ !frame.fin && frame.offset == 0 &&
+ std::string(frame.data_buffer, frame.data_length) == kTestString;
+ // FIN is hard-coded false in ConstructEncryptedPacket.
+ // Offset 0 is hard-coded in ConstructEncryptedPacket.
+}
+
+// Verify that the packet returned by ConstructEncryptedPacket() can be properly
+// parsed by the framer.
+TEST_P(QuicFramerTest, ConstructEncryptedPacket) {
+ // Since we are using ConstructEncryptedPacket, we have to set the framer's
+ // crypto to be Null.
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullDecrypter>(framer_.perspective()));
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<NullDecrypter>(framer_.perspective()));
+ }
+ ParsedQuicVersionVector versions;
+ versions.push_back(framer_.version());
+ std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
+ TestConnectionId(), EmptyQuicConnectionId(), false, false,
+ kTestQuicStreamId, kTestString, CONNECTION_ID_PRESENT,
+ CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &versions));
+
+ MockFramerVisitor visitor;
+ framer_.set_visitor(&visitor);
+ EXPECT_CALL(visitor, OnPacket()).Times(1);
+ EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_))
+ .Times(1)
+ .WillOnce(Return(true));
+ EXPECT_CALL(visitor, OnUnauthenticatedHeader(_))
+ .Times(1)
+ .WillOnce(Return(true));
+ EXPECT_CALL(visitor, OnPacketHeader(_)).Times(1).WillOnce(Return(true));
+ EXPECT_CALL(visitor, OnDecryptedPacket(_)).Times(1);
+ EXPECT_CALL(visitor, OnError(_)).Times(0);
+ EXPECT_CALL(visitor, OnStreamFrame(_)).Times(0);
+ if (!QuicVersionUsesCryptoFrames(framer_.version().transport_version)) {
+ EXPECT_CALL(visitor, OnStreamFrame(Truly(ExpectedStreamFrame))).Times(1);
+ } else {
+ EXPECT_CALL(visitor, OnCryptoFrame(_)).Times(1);
+ }
+ EXPECT_CALL(visitor, OnPacketComplete()).Times(1);
+
+ EXPECT_TRUE(framer_.ProcessPacket(*packet));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+}
+
+// Verify that the packet returned by ConstructMisFramedEncryptedPacket()
+// does cause the framer to return an error.
+TEST_P(QuicFramerTest, ConstructMisFramedEncryptedPacket) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // Since we are using ConstructEncryptedPacket, we have to set the framer's
+ // crypto to be Null.
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_INITIAL, QuicMakeUnique<NullDecrypter>(
+ framer_.perspective()));
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<NullDecrypter>(framer_.perspective()));
+ }
+ framer_.SetEncrypter(ENCRYPTION_INITIAL,
+ QuicMakeUnique<NullEncrypter>(framer_.perspective()));
+ ParsedQuicVersionVector versions;
+ versions.push_back(framer_.version());
+ std::unique_ptr<QuicEncryptedPacket> packet(ConstructMisFramedEncryptedPacket(
+ TestConnectionId(), EmptyQuicConnectionId(), false, false,
+ kTestQuicStreamId, kTestString, CONNECTION_ID_PRESENT,
+ CONNECTION_ID_ABSENT, PACKET_4BYTE_PACKET_NUMBER, &versions,
+ Perspective::IS_CLIENT));
+
+ MockFramerVisitor visitor;
+ framer_.set_visitor(&visitor);
+ EXPECT_CALL(visitor, OnPacket()).Times(1);
+ EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_))
+ .Times(1)
+ .WillOnce(Return(true));
+ EXPECT_CALL(visitor, OnUnauthenticatedHeader(_))
+ .Times(1)
+ .WillOnce(Return(true));
+ EXPECT_CALL(visitor, OnPacketHeader(_)).Times(1);
+ EXPECT_CALL(visitor, OnDecryptedPacket(_)).Times(1);
+ EXPECT_CALL(visitor, OnError(_)).Times(1);
+ EXPECT_CALL(visitor, OnStreamFrame(_)).Times(0);
+ EXPECT_CALL(visitor, OnPacketComplete()).Times(0);
+
+ EXPECT_FALSE(framer_.ProcessPacket(*packet));
+ EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error());
+}
+
+// Tests for fuzzing with Dr. Fuzz
+// Xref http://www.chromium.org/developers/testing/dr-fuzz for more details.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// target function to be fuzzed by Dr. Fuzz
+void QuicFramerFuzzFunc(unsigned char* data,
+ size_t size,
+ const ParsedQuicVersion& version) {
+ QuicFramer framer(AllSupportedVersions(), QuicTime::Zero(),
+ Perspective::IS_SERVER, kQuicDefaultConnectionIdLength);
+ ASSERT_EQ(GetQuicFlag(FLAGS_quic_supports_tls_handshake), true);
+ const char* const packet_bytes = reinterpret_cast<const char*>(data);
+
+ // Test the CryptoFramer.
+ QuicStringPiece crypto_input(packet_bytes, size);
+ std::unique_ptr<CryptoHandshakeMessage> handshake_message(
+ CryptoFramer::ParseMessage(crypto_input));
+
+ // Test the regular QuicFramer with the same input.
+ NoOpFramerVisitor visitor;
+ framer.set_visitor(&visitor);
+ framer.set_version(version);
+ QuicEncryptedPacket packet(packet_bytes, size);
+ framer.ProcessPacket(packet);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+TEST_P(QuicFramerTest, FramerFuzzTest) {
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x2C,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // private flags
+ 0x00,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin, length, and offset bits set)
+ 0x10 | 0x01 | 0x02 | 0x04,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (stream frame with fin, length, and offset bits set)
+ 0x10 | 0x01 | 0x02 | 0x04,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAM frame with fin, length, and offset bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ 0x01, 0x02, 0x03, 0x04,
+ // offset
+ 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ 0x00, 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ }
+ QuicFramerFuzzFunc(p,
+ framer_.transport_version() > QUIC_VERSION_43
+ ? QUIC_ARRAYSIZE(packet44)
+ : QUIC_ARRAYSIZE(packet),
+ framer_.version());
+}
+
+TEST_P(QuicFramerTest, IetfBlockedFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_BLOCKED)
+ {"",
+ {0x14}},
+ // blocked offset
+ {"Can not read blocked offset.",
+ {kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(kStreamOffset, visitor_.blocked_frame_.offset);
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_BLOCKED_DATA);
+}
+
+TEST_P(QuicFramerTest, BuildIetfBlockedPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicBlockedFrame frame;
+ frame.stream_id = QuicUtils::GetInvalidStreamId(framer_.transport_version());
+ frame.offset = kStreamOffset;
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_BLOCKED)
+ 0x14,
+ // Offset
+ kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, IetfStreamBlockedFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_STREAM_BLOCKED)
+ {"",
+ {0x15}},
+ // blocked offset
+ {"Can not read stream blocked stream id.",
+ {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}},
+ {"Can not read stream blocked offset.",
+ {kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(kStreamId, visitor_.blocked_frame_.stream_id);
+ EXPECT_EQ(kStreamOffset, visitor_.blocked_frame_.offset);
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_STREAM_BLOCKED_DATA);
+}
+
+TEST_P(QuicFramerTest, BuildIetfStreamBlockedPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicBlockedFrame frame;
+ frame.stream_id = kStreamId;
+ frame.offset = kStreamOffset;
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAM_BLOCKED)
+ 0x15,
+ // Stream ID
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // Offset
+ kVarInt62EightBytes + 0x3a, 0x98, 0xFE, 0xDC, 0x32, 0x10, 0x76, 0x54
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, ServerBiDiMaxStreamsFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
+ {"",
+ {0x12}},
+ // max. streams
+ {"Can not read MAX_STREAMS stream count.",
+ {kVarInt62OneByte + 0x03}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a server receiving a MAX_STREAMS frame. The
+ // stream ID that it generates should be a server-initiated
+ // stream ID. The expected Stream ID is
+ // ((0x3-1) * 4) | 0x1 = 0x9
+ // count-to-id server inited, bidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/true, 3),
+ visitor_.max_stream_id_frame_.max_stream_id);
+ CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA);
+}
+
+TEST_P(QuicFramerTest, ClientBiDiMaxStreamsFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // Test runs in client mode, no connection id
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
+ {"",
+ {0x12}},
+ // max. streams
+ {"Can not read MAX_STREAMS stream count.",
+ {kVarInt62OneByte + 0x03}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a client receiving a MAX_STREAMS frame. The
+ // stream ID that it generates should be a client-initiated
+ // stream ID. The expected Stream ID is
+ // ((0x3-1) * 4) = 0xc
+ // It is not 8 because a client-initiated, bidi stream ID's
+ // low bits are 00 - which means that the old crypto stream
+ // falls into this category, and the first stream is streamid=4,
+ // not streamid=0.
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/true, 3),
+ visitor_.max_stream_id_frame_.max_stream_id);
+
+ CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA);
+}
+
+TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL)
+ {"",
+ {0x13}},
+ // max. streams
+ {"Can not read MAX_STREAMS stream count.",
+ {kVarInt62OneByte + 0x03}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a server receiving a MAX_STREAMS frame. The
+ // stream ID that it generates should be a server-initiated
+ // stream ID. The expected Stream ID is
+ // ((0x3-1) * 4) | 0x1 | 0x2 = 0xb
+ // count-to-id server inited, unidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/false, 3),
+ visitor_.max_stream_id_frame_.max_stream_id);
+
+ CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA);
+}
+
+TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // Test runs in client mode, no connection id
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL)
+ {"",
+ {0x13}},
+ // max. streams
+ {"Can not read MAX_STREAMS stream count.",
+ {kVarInt62OneByte + 0x03}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a client receiving a MAX_STREAMS frame. The
+ // stream ID that it generates should be a client-initiated
+ // stream ID. The expected Stream ID is
+ // ((0x3-1) * 4) | 0x02= 0xa
+ // count-to-id client/unidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/false, 3),
+ visitor_.max_stream_id_frame_.max_stream_id);
+
+ CheckFramingBoundaries(packet99, QUIC_MAX_STREAM_ID_DATA);
+}
+
+// The following four tests ensure that the framer can deserialize a stream
+// count that is large enough to cause the resulting stream ID to exceed the
+// current implementation limit(32 bits). The intent is that when this happens,
+// the stream limit is pegged to the maximum supported value. There are four
+// tests, for the four combinations of uni- and bi-directional, server- and
+// client- initiated.
+TEST_P(QuicFramerTest, ServerBiDiMaxStreamsFrameTooBig) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x9A, 0xBC,
+ // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
+ 0x12,
+
+ // max. streams. Max stream ID allowed is 0xffffffff
+ // This encodes a count of 0x40000000, leading to stream
+ // IDs in the range 0x1 00000000 to 0x1 00000003.
+ kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+ false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a server receiving a MAX_STREAMS frame. The
+ // stream ID that it generates should be a server-initiated
+ // stream ID. The expected Stream ID is
+ // 0xfffffffc | 0x01 --> 0xfffffffd
+ // maxid server inited, bidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/true, 0x40000000),
+ visitor_.max_stream_id_frame_.max_stream_id);
+}
+
+TEST_P(QuicFramerTest, ClientBiDiMaxStreamsFrameTooBig) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // Test runs in client mode, no connection id
+ // packet number
+ 0x12, 0x34, 0x9A, 0xBC,
+ // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
+ 0x12,
+
+ // max. streams. Max stream ID allowed is 0xffffffff
+ // This encodes a count of 0x40000000, leading to stream
+ // IDs in the range 0x1 00000000 to 0x1 00000003.
+ kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+ false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a client receiving a MAX_STREAMS frame. The
+ // stream ID that it generates should be a client-initiated
+ // stream ID. The expected Stream ID is
+ // 0xfffffffc --> 0xfffffffc
+ // max id bidi/client-inited
+ // TODO(fkastenholz): Change -2 to -1 when stream id 0 is no longer
+ // special.
+ // Subtract 1 because client/bidi stream ids start counting at
+ // 4, not 0. If we didn;t subtract 1, the resulting math would wrap to stream
+ // id 0, not 0xfffffffc.
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/true, (0x40000000 - 1)),
+ visitor_.max_stream_id_frame_.max_stream_id);
+}
+
+TEST_P(QuicFramerTest, ServerUniDiMaxStreamsFrameTooBig) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x9A, 0xBC,
+ // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL)
+ 0x13,
+
+ // max. streams. Max stream ID allowed is 0xffffffff
+ // This encodes a count of 0x40000000, leading to stream
+ // IDs in the range 0x1 00000000 to 0x1 00000003.
+ kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+ false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a server receiving a MAX_STREAMS frame. The
+ // stream ID that it generates should be a server-initiated
+ // stream ID. The expected Stream ID is
+ // 0xfffffffc | 0x1 | 0x2 = 0xffffffff
+ // maxid server inited, unidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/false, 0x40000000),
+ visitor_.max_stream_id_frame_.max_stream_id);
+}
+
+TEST_P(QuicFramerTest, ClientUniDiMaxStreamsFrameTooBig) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // Test runs in client mode, no connection id
+ // packet number
+ 0x12, 0x34, 0x9A, 0xBC,
+ // frame type (IETF_MAX_STREAMS_UNDIRECTIONAL)
+ 0x13,
+
+ // max. streams. Max stream ID allowed is 0xffffffff
+ // This encodes a count of 0x40000000, leading to stream
+ // IDs in the range 0x1 00000000 to 0x1 00000003.
+ kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+ false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a client receiving a MAX_STREAMS frame. The
+ // stream ID that it generates should be a client-initiated
+ // stream ID. The expected Stream ID is
+ // 0xfffffffc | 0x02= 0xfffffffe
+ // maxid client/unidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/false, 0x40000000),
+ visitor_.max_stream_id_frame_.max_stream_id);
+}
+
+// Check that a stream count of 0 is rejected.
+// Directionality and intiation are not important for
+// this test.
+TEST_P(QuicFramerTest, MaxStreamsFrameZeroCount) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x9A, 0xBC,
+ // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL)
+ 0x12,
+ // max. streams == 0.
+ kVarInt62OneByte + 0x00
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+ false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MAX_STREAM_ID_DATA, framer_.error());
+ EXPECT_EQ(framer_.detailed_error(),
+ "MAX_STREAMS stream count of 0 not supported.");
+}
+
+TEST_P(QuicFramerTest, ServerBiDiStreamsBlockedFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame)
+ {"",
+ {0x16}},
+ // stream id
+ {"Can not read STREAMS_BLOCKED stream id.",
+ {kVarInt62OneByte + 0x03}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a server receiving a STREAMS_BLOCKED frame. The
+ // stream ID that it generates should be a client-initiated
+ // stream ID. The expected Stream ID is
+ // ((0x3-1) * 4) = 0xc
+ // count-to-id client inited, bidi
+ // It is not 8 because a client-initiated, bidi stream ID's
+ // low bits are 00 - which means that the old crypto stream
+ // falls into this category, and the first stream is streamid=4,
+ // not streamid=0.
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/true, 3),
+ visitor_.stream_id_blocked_frame_.stream_id);
+
+ CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA);
+}
+
+TEST_P(QuicFramerTest, ClientBiDiStreamsBlockedFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // Test runs in client mode, no connection id
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame)
+ {"",
+ {0x16}},
+ // stream id
+ {"Can not read STREAMS_BLOCKED stream id.",
+ {kVarInt62OneByte + 0x03}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a client receiving a STREAMS_BLOCKED frame. The
+ // stream ID that it generates should be a server-initiated
+ // stream ID. The expected Stream ID is
+ // ((0x3-1) * 4) | 0x01 = 0x9
+ // count-to-id server inited, bidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/true, 3),
+ visitor_.stream_id_blocked_frame_.stream_id);
+
+ CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA);
+}
+
+TEST_P(QuicFramerTest, ServerUniDiStreamsBlockedFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame)
+ {"",
+ {0x17}},
+ // stream id
+ {"Can not read STREAMS_BLOCKED stream id.",
+ {kVarInt62OneByte + 0x03}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a server receiving a STREAMS_BLOCKED frame. The
+ // stream ID that it generates should be a client-initiated
+ // stream ID. The expected Stream ID is
+ // ((0x3-1) * 4) | 0x2 = 0xa
+ // count-to-id client inited, unidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/false, 3),
+ visitor_.stream_id_blocked_frame_.stream_id);
+
+ CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA);
+}
+
+TEST_P(QuicFramerTest, ClientUniDiStreamsBlockedFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // Test runs in client mode, no connection id
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame)
+ {"",
+ {0x17}},
+ // stream id
+ {"Can not read STREAMS_BLOCKED stream id.",
+ {kVarInt62OneByte + 0x03}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_0BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ // This test is a client receiving a STREAMS_BLOCKED frame. The
+ // stream ID that it generates should be a server-initiated
+ // stream ID. The expected Stream ID is
+ // ((0x3-1) * 4) | 0x01 | 0x2 = 0xb
+ // count-to-id server inited, bidi
+ EXPECT_EQ(GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/false, 3),
+ visitor_.stream_id_blocked_frame_.stream_id);
+
+ CheckFramingBoundaries(packet99, QUIC_STREAM_ID_BLOCKED_DATA);
+}
+
+// Check that when we get a STREAMS_BLOCKED frame that specifies too large
+// a stream count, we reject with an appropriate error. There is no need to
+// check for different combinations of Uni/Bi directional and client/server
+// initiated; the logic does not take these into account.
+TEST_P(QuicFramerTest, StreamsBlockedFrameTooBig) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // Test runs in client mode, no connection id
+ // packet number
+ 0x12, 0x34, 0x9A, 0xBC,
+ // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL)
+ 0x17,
+
+ // max. streams. Max stream ID allowed is 0xffffffff
+ // This encodes a count of 0x40000000, leading to stream
+ // IDs in the range 0x1 00000000 to 0x1 00000003.
+ kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+ false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_STREAM_ID_BLOCKED_DATA, framer_.error());
+ EXPECT_EQ(framer_.detailed_error(),
+ "STREAMS_BLOCKED stream count exceeds implementation limit.");
+}
+
+// Test that count==0 is rejected.
+TEST_P(QuicFramerTest, StreamsBlockedFrameZeroCount) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // Test runs in client mode, no connection id
+ // packet number
+ 0x12, 0x34, 0x9A, 0xBC,
+ // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL)
+ 0x17,
+
+ // max. streams = 0
+ kVarInt62OneByte + 0x00
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet99), QUIC_ARRAYSIZE(packet99),
+ false);
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_STREAM_ID_BLOCKED_DATA, framer_.error());
+ EXPECT_EQ(framer_.detailed_error(),
+ "STREAMS_BLOCKED stream count 0 not supported.");
+}
+
+TEST_P(QuicFramerTest, BuildServerBiDiStreamsBlockedPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicStreamIdBlockedFrame frame;
+ // A server building a STREAMS_BLOCKED frame generates
+ // a server-initiated stream ID. This test is bidirectional.
+ // The low two bits of the stream ID are 01
+ // Expected value is 0x8u | 0x1u;
+ frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/true, 3);
+
+ QuicFrames frames = {QuicFrame(frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame)
+ 0x16,
+ // Stream count
+ kVarInt62OneByte + 0x03
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildClientBiDiStreamsBlockedPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ // This test runs in client mode.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicStreamIdBlockedFrame frame;
+ // A client building a STREAMS_BLOCKED frame generates
+ // a client-initiated stream ID. This test is bidirectional.
+ // The low two bits of the stream ID are 00. Expected value is 0x8
+ frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/true, 3);
+ QuicFrames frames = {QuicFrame(frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAMS_BLOCKED_BIDIRECTIONAL frame)
+ 0x16,
+ // Stream count
+ kVarInt62OneByte + 0x03
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildServerUniStreamsBlockedPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicStreamIdBlockedFrame frame;
+ // A server building a STREAMS_BLOCKED frame generates
+ // a server-initiated stream ID. This test is bidirectional.
+ // The low two bits of the stream ID are 11. Expected value is 0xb
+ frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/false, 3);
+ QuicFrames frames = {QuicFrame(frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame)
+ 0x17,
+ // Stream count
+ kVarInt62OneByte + 0x03
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildClientUniDiStreamsBlockedPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ // This test runs in client mode.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicStreamIdBlockedFrame frame;
+ // A client building a STREAMS_BLOCKED frame generates
+ // a client-initiated stream ID. This test is bidirectional.
+ // The low two bits of the stream ID are 10. Expected value is 0xa
+ frame.stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/false, 3);
+ QuicFrames frames = {QuicFrame(frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STREAMS_BLOCKED_UNIDIRECTIONAL frame)
+ 0x17,
+ // Stream count
+ kVarInt62OneByte + 0x03
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildServerBiDiMaxStreamsPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicMaxStreamIdFrame frame;
+ // A server building a MAX_STREAMS frame generates
+ // a client-initiated stream ID. This test is bidirectional.
+ // The low two bits of the stream ID are 00. Expected value is 0xc
+ // because streamid==0 is special and the first client/bidi
+ // stream is 4, not 0.
+ frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/true, 3);
+ QuicFrames frames = {QuicFrame(frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL frame)
+ 0x12,
+ // Stream count
+ kVarInt62OneByte + 0x03
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildClientBiDiMaxStreamsPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ // This test runs in client mode.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicMaxStreamIdFrame frame;
+ // A client building a MAX_STREAMS frame generates
+ // a server-initiated stream ID. This test is bidirectional.
+ // The low two bits of the stream ID are 01. Expected value is 0x9
+ frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/true, 3);
+ QuicFrames frames = {QuicFrame(frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_MAX_STREAMS_BIDIRECTIONAL frame)
+ 0x12,
+ // Stream count
+ kVarInt62OneByte + 0x03
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildServerUniMaxStreamsPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicMaxStreamIdFrame frame;
+ // A server building a MAX_STREAMS frame generates
+ // a client-initiated stream ID. This test is bidirectional.
+ // The low two bits of the stream ID are 10. Expected value is 0xa
+ frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_CLIENT,
+ /*bidirectional=*/false, 3);
+ QuicFrames frames = {QuicFrame(frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL frame)
+ 0x13,
+ // Stream count
+ kVarInt62OneByte + 0x03
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, BuildClientUniDiMaxStreamsPacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ // This test runs in client mode.
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicMaxStreamIdFrame frame;
+ // A client building a MAX_STREAMS frame generates
+ // a server-initiated stream ID. This test is bidirectional.
+ // The low two bits of the stream ID are 11. Expected value is 0xb
+ frame.max_stream_id = GetNthStreamid(QUIC_VERSION_99, Perspective::IS_SERVER,
+ /*bidirectional=*/false, 3);
+ QuicFrames frames = {QuicFrame(frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_MAX_STREAMS_UNIDIRECTIONAL frame)
+ 0x13,
+ // Stream count
+ kVarInt62OneByte + 0x03
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, NewConnectionIdFrame) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is only for version 99.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_NEW_CONNECTION_ID frame)
+ {"",
+ {0x18}},
+ // error code
+ {"Unable to read new connection ID frame sequence number.",
+ {kVarInt62OneByte + 0x11}},
+ {"Unable to read new connection ID frame connection id length.",
+ {0x08}}, // connection ID length
+ {"Unable to read new connection ID frame connection id.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11}},
+ {"Can not read new connection ID frame reset token.",
+ {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+
+ EXPECT_EQ(FramerTestConnectionIdPlusOne(),
+ visitor_.new_connection_id_.connection_id);
+ EXPECT_EQ(0x11u, visitor_.new_connection_id_.sequence_number);
+ EXPECT_EQ(kTestStatelessResetToken,
+ visitor_.new_connection_id_.stateless_reset_token);
+
+ ASSERT_EQ(0u, visitor_.ack_frames_.size());
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_NEW_CONNECTION_ID_DATA);
+}
+
+TEST_P(QuicFramerTest, NewConnectionIdFrameVariableLength) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is only for version 99.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_NEW_CONNECTION_ID frame)
+ {"",
+ {0x18}},
+ // error code
+ {"Unable to read new connection ID frame sequence number.",
+ {kVarInt62OneByte + 0x11}},
+ {"Unable to read new connection ID frame connection id length.",
+ {0x09}}, // connection ID length
+ {"Unable to read new connection ID frame connection id.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}},
+ {"Can not read new connection ID frame reset token.",
+ {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+
+ EXPECT_EQ(FramerTestConnectionIdNineBytes(),
+ visitor_.new_connection_id_.connection_id);
+ EXPECT_EQ(0x11u, visitor_.new_connection_id_.sequence_number);
+ EXPECT_EQ(kTestStatelessResetToken,
+ visitor_.new_connection_id_.stateless_reset_token);
+
+ ASSERT_EQ(0u, visitor_.ack_frames_.size());
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_NEW_CONNECTION_ID_DATA);
+}
+
+// Verifies that parsing a NEW_CONNECTION_ID frame with a length above the
+// specified maximum fails.
+TEST_P(QuicFramerTest, InvalidLongNewConnectionIdFrame) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // The NEW_CONNECTION_ID frame is only for version 99.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_NEW_CONNECTION_ID frame)
+ {"",
+ {0x18}},
+ // error code
+ {"Unable to read new connection ID frame sequence number.",
+ {kVarInt62OneByte + 0x11}},
+ {"Unable to read new connection ID frame connection id length.",
+ {0x13}}, // connection ID length
+ {"Unable to read new connection ID frame connection id.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ 0xF0, 0xD2, 0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E,
+ 0x42, 0x33, 0x42}},
+ {"Can not read new connection ID frame reset token.",
+ {0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_INVALID_NEW_CONNECTION_ID_DATA, framer_.error());
+ EXPECT_EQ("New connection ID length too high.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, BuildNewConnectionIdFramePacket) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is only for version 99.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicNewConnectionIdFrame frame;
+ frame.sequence_number = 0x11;
+ // Use this value to force a 4-byte encoded variable length connection ID
+ // in the frame.
+ frame.connection_id = FramerTestConnectionIdPlusOne();
+ frame.stateless_reset_token = kTestStatelessResetToken;
+
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_NEW_CONNECTION_ID frame)
+ 0x18,
+ // sequence number
+ kVarInt62OneByte + 0x11,
+ // new connection id length
+ 0x08,
+ // new connection id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+ // stateless reset token
+ 0xb5, 0x69, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, NewTokenFrame) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is only for version 99.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_NEW_TOKEN frame)
+ {"",
+ {0x07}},
+ // Length
+ {"Unable to read new token length.",
+ {kVarInt62OneByte + 0x08}},
+ {"Unable to read new token data.",
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}}
+ };
+ // clang-format on
+ uint8_t expected_token_value[] = {0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07};
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+
+ EXPECT_EQ(sizeof(expected_token_value), visitor_.new_token_.token.length());
+ EXPECT_EQ(0, memcmp(expected_token_value, visitor_.new_token_.token.data(),
+ sizeof(expected_token_value)));
+
+ CheckFramingBoundaries(packet, QUIC_INVALID_NEW_TOKEN);
+}
+
+TEST_P(QuicFramerTest, BuildNewTokenFramePacket) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is only for version 99.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ uint8_t expected_token_value[] = {0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07};
+
+ QuicNewTokenFrame frame(0, std::string((const char*)(expected_token_value),
+ sizeof(expected_token_value)));
+
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_NEW_TOKEN frame)
+ 0x07,
+ // Length and token
+ kVarInt62OneByte + 0x08,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ QUIC_ARRAYSIZE(packet));
+}
+
+TEST_P(QuicFramerTest, IetfStopSendingFrame) {
+ // This test is only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_STOP_SENDING frame)
+ {"",
+ {0x05}},
+ // stream id
+ {"Unable to read stop sending stream id.",
+ {kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04}},
+ {"Unable to read stop sending application error code.",
+ {0x76, 0x54}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(kStreamId, visitor_.stop_sending_frame_.stream_id);
+ EXPECT_EQ(0x7654, visitor_.stop_sending_frame_.application_error_code);
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_STOP_SENDING_FRAME_DATA);
+}
+
+TEST_P(QuicFramerTest, BuildIetfStopSendingPacket) {
+ // This test is only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicStopSendingFrame frame;
+ frame.stream_id = kStreamId;
+ frame.application_error_code = 0xffff;
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_STOP_SENDING frame)
+ 0x05,
+ // Stream ID
+ kVarInt62FourBytes + 0x01, 0x02, 0x03, 0x04,
+ // Application error code
+ 0xff, 0xff
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, IetfPathChallengeFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_PATH_CHALLENGE)
+ {"",
+ {0x1a}},
+ // data
+ {"Can not read path challenge data.",
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}}),
+ visitor_.path_challenge_frame_.data_buffer);
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_PATH_CHALLENGE_DATA);
+}
+
+TEST_P(QuicFramerTest, BuildIetfPathChallengePacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicPathChallengeFrame frame;
+ frame.data_buffer = QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}});
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_PATH_CHALLENGE)
+ 0x1a,
+ // Data
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, IetfPathResponseFrame) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (IETF_PATH_RESPONSE)
+ {"",
+ {0x1b}},
+ // data
+ {"Can not read path response data.",
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}}),
+ visitor_.path_response_frame_.data_buffer);
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_PATH_RESPONSE_DATA);
+}
+
+TEST_P(QuicFramerTest, BuildIetfPathResponsePacket) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicPathResponseFrame frame;
+ frame.data_buffer = QuicPathFrameBuffer({{0, 1, 2, 3, 4, 5, 6, 7}});
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_PATH_RESPONSE)
+ 0x1b,
+ // Data
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, GetRetransmittableControlFrameSize) {
+ QuicRstStreamFrame rst_stream(1, 3, QUIC_STREAM_CANCELLED, 1024);
+ EXPECT_EQ(QuicFramer::GetRstStreamFrameSize(framer_.transport_version(),
+ rst_stream),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&rst_stream)));
+
+ std::string error_detail(2048, 'e');
+ QuicConnectionCloseFrame connection_close(QUIC_NETWORK_IDLE_TIMEOUT,
+ error_detail);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ connection_close.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ }
+
+ EXPECT_EQ(QuicFramer::GetMinConnectionCloseFrameSize(
+ framer_.transport_version(), connection_close) +
+ 256,
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&connection_close)));
+
+ QuicGoAwayFrame goaway(2, QUIC_PEER_GOING_AWAY, 3, error_detail);
+ EXPECT_EQ(QuicFramer::GetMinGoAwayFrameSize() + 256,
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&goaway)));
+
+ QuicWindowUpdateFrame window_update(3, 3, 1024);
+ EXPECT_EQ(QuicFramer::GetWindowUpdateFrameSize(framer_.transport_version(),
+ window_update),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&window_update)));
+
+ QuicBlockedFrame blocked(4, 3, 1024);
+ EXPECT_EQ(
+ QuicFramer::GetBlockedFrameSize(framer_.transport_version(), blocked),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&blocked)));
+
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ QuicNewConnectionIdFrame new_connection_id(5, TestConnectionId(), 1, 101111);
+ EXPECT_EQ(QuicFramer::GetNewConnectionIdFrameSize(new_connection_id),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&new_connection_id)));
+
+ QuicMaxStreamIdFrame max_stream_id(6, 3);
+ EXPECT_EQ(QuicFramer::GetMaxStreamsFrameSize(framer_.transport_version(),
+ max_stream_id),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(max_stream_id)));
+
+ QuicStreamIdBlockedFrame stream_id_blocked(7, 3);
+ EXPECT_EQ(QuicFramer::GetStreamsBlockedFrameSize(framer_.transport_version(),
+ stream_id_blocked),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(stream_id_blocked)));
+
+ QuicPathFrameBuffer buffer = {
+ {0x80, 0x91, 0xa2, 0xb3, 0xc4, 0xd5, 0xe5, 0xf7}};
+ QuicPathResponseFrame path_response_frame(8, buffer);
+ EXPECT_EQ(QuicFramer::GetPathResponseFrameSize(path_response_frame),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&path_response_frame)));
+
+ QuicPathChallengeFrame path_challenge_frame(9, buffer);
+ EXPECT_EQ(QuicFramer::GetPathChallengeFrameSize(path_challenge_frame),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&path_challenge_frame)));
+
+ QuicStopSendingFrame stop_sending_frame(10, 3, 20);
+ EXPECT_EQ(QuicFramer::GetStopSendingFrameSize(stop_sending_frame),
+ QuicFramer::GetRetransmittableControlFrameSize(
+ framer_.transport_version(), QuicFrame(&stop_sending_frame)));
+}
+
+// A set of tests to ensure that bad frame-type encodings
+// are properly detected and handled.
+// First, four tests to see that unknown frame types generate
+// a QUIC_INVALID_FRAME_DATA error with detailed information
+// "Illegal frame type." This regardless of the encoding of the type
+// (1/2/4/8 bytes).
+// This only for version 99.
+TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorUnknown1Byte) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (unknown value, single-byte encoding)
+ {"",
+ {0x38}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error());
+ EXPECT_EQ("Illegal frame type.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorUnknown2Bytes) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (unknown value, two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x01, 0x38}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error());
+ EXPECT_EQ("Illegal frame type.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorUnknown4Bytes) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (unknown value, four-byte encoding)
+ {"",
+ {kVarInt62FourBytes + 0x01, 0x00, 0x00, 0x38}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error());
+ EXPECT_EQ("Illegal frame type.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorUnknown8Bytes) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (unknown value, eight-byte encoding)
+ {"",
+ {kVarInt62EightBytes + 0x01, 0x00, 0x00, 0x01, 0x02, 0x34, 0x56, 0x38}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error());
+ EXPECT_EQ("Illegal frame type.", framer_.detailed_error());
+}
+
+// Three tests to check that known frame types that are not minimally
+// encoded generate IETF_QUIC_PROTOCOL_VIOLATION errors with detailed
+// information "Frame type not minimally encoded."
+// Look at the frame-type encoded in 2, 4, and 8 bytes.
+TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorKnown2Bytes) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (Blocked, two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x08}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(IETF_QUIC_PROTOCOL_VIOLATION, framer_.error());
+ EXPECT_EQ("Frame type not minimally encoded.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorKnown4Bytes) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (Blocked, four-byte encoding)
+ {"",
+ {kVarInt62FourBytes + 0x00, 0x00, 0x00, 0x08}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(IETF_QUIC_PROTOCOL_VIOLATION, framer_.error());
+ EXPECT_EQ("Frame type not minimally encoded.", framer_.detailed_error());
+}
+
+TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorKnown8Bytes) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (Blocked, eight-byte encoding)
+ {"",
+ {kVarInt62EightBytes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(IETF_QUIC_PROTOCOL_VIOLATION, framer_.error());
+ EXPECT_EQ("Frame type not minimally encoded.", framer_.detailed_error());
+}
+
+// Tests to check that all known OETF frame types that are not minimally
+// encoded generate IETF_QUIC_PROTOCOL_VIOLATION errors with detailed
+// information "Frame type not minimally encoded."
+// Just look at 2-byte encoding.
+TEST_P(QuicFramerTest, IetfFrameTypeEncodingErrorKnown2BytesAllTypes) {
+ // This test only for version 99.
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+
+ // clang-format off
+ PacketFragments packets[] = {
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x00}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x01}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x02}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x03}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x04}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x05}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x06}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x07}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x08}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x09}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x0a}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x0b}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x0c}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x0d}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x0e}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x0f}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x10}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x11}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x12}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x13}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x14}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x15}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x16}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x17}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x18}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x20}}
+ },
+ {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x9A, 0xBC}},
+ // frame type (two-byte encoding)
+ {"",
+ {kVarInt62TwoBytes + 0x00, 0x21}}
+ },
+ };
+ // clang-format on
+
+ for (PacketFragments& packet : packets) {
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(IETF_QUIC_PROTOCOL_VIOLATION, framer_.error());
+ EXPECT_EQ("Frame type not minimally encoded.", framer_.detailed_error());
+ }
+}
+
+TEST_P(QuicFramerTest, RetireConnectionIdFrame) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is only for version 99.
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ PacketFragments packet99 = {
+ // type (short header, 4 byte packet number)
+ {"",
+ {0x43}},
+ // connection_id
+ {"",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}},
+ // packet number
+ {"",
+ {0x12, 0x34, 0x56, 0x78}},
+ // frame type (IETF_RETIRE_CONNECTION_ID frame)
+ {"",
+ {0x19}},
+ // Sequence number
+ {"Unable to read retire connection ID frame sequence number.",
+ {kVarInt62TwoBytes + 0x11, 0x22}}
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet99));
+ EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ *encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+
+ EXPECT_EQ(0x1122u, visitor_.retire_connection_id_.sequence_number);
+
+ ASSERT_EQ(0u, visitor_.ack_frames_.size());
+
+ CheckFramingBoundaries(packet99, QUIC_INVALID_RETIRE_CONNECTION_ID_DATA);
+}
+
+TEST_P(QuicFramerTest, BuildRetireConnectionIdFramePacket) {
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ // This frame is only for version 99.
+ return;
+ }
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicRetireConnectionIdFrame frame;
+ frame.sequence_number = 0x1122;
+
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ // clang-format off
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_RETIRE_CONNECTION_ID frame)
+ 0x19,
+ // sequence number
+ kVarInt62TwoBytes + 0x11, 0x22
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet99),
+ QUIC_ARRAYSIZE(packet99));
+}
+
+TEST_P(QuicFramerTest, AckFrameWithInvalidLargestObserved) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x2C,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x45,
+ // largest observed
+ 0x00, 0x00,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x00, 0x00,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x45,
+ // largest observed
+ 0x00, 0x00,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x00, 0x00,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x45,
+ // largest observed
+ 0x00, 0x00,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x00, 0x00,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_ACK frame)
+ 0x02,
+ // Largest acked
+ kVarInt62OneByte + 0x00,
+ // Zero delta time.
+ kVarInt62OneByte + 0x00,
+ // Ack block count 0
+ kVarInt62OneByte + 0x00,
+ // First ack block length
+ kVarInt62OneByte + 0x00,
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(framer_.detailed_error(), "Largest acked is 0.");
+}
+
+TEST_P(QuicFramerTest, FirstAckBlockJustUnderFlow) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x2C,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x45,
+ // largest observed
+ 0x00, 0x02,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x00, 0x03,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x45,
+ // largest observed
+ 0x00, 0x02,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x00, 0x03,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x45,
+ // largest observed
+ 0x00, 0x02,
+ // Zero delta time.
+ 0x00, 0x00,
+ // first ack block length.
+ 0x00, 0x03,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_ACK frame)
+ 0x02,
+ // Largest acked
+ kVarInt62OneByte + 0x02,
+ // Zero delta time.
+ kVarInt62OneByte + 0x00,
+ // Ack block count 0
+ kVarInt62OneByte + 0x00,
+ // First ack block length
+ kVarInt62OneByte + 0x02,
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(framer_.detailed_error(),
+ "Underflow with first ack block length 3 largest acked is 2.");
+}
+
+TEST_P(QuicFramerTest, ThirdAckBlockJustUnderflow) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x2C,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x60,
+ // largest observed
+ 0x0A,
+ // Zero delta time.
+ 0x00, 0x00,
+ // Num of ack blocks
+ 0x02,
+ // first ack block length.
+ 0x02,
+ // gap to next block
+ 0x01,
+ // ack block length
+ 0x01,
+ // gap to next block
+ 0x01,
+ // ack block length
+ 0x06,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet44[] = {
+ // type (short header, 4 byte packet number)
+ 0x32,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x60,
+ // largest observed
+ 0x0A,
+ // Zero delta time.
+ 0x00, 0x00,
+ // Num of ack blocks
+ 0x02,
+ // first ack block length.
+ 0x02,
+ // gap to next block
+ 0x01,
+ // ack block length
+ 0x01,
+ // gap to next block
+ 0x01,
+ // ack block length
+ 0x06,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet46[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (ack frame)
+ 0x60,
+ // largest observed
+ 0x0A,
+ // Zero delta time.
+ 0x00, 0x00,
+ // Num of ack blocks
+ 0x02,
+ // first ack block length.
+ 0x02,
+ // gap to next block
+ 0x01,
+ // ack block length
+ 0x01,
+ // gap to next block
+ 0x01,
+ // ack block length
+ 0x06,
+ // num timestamps.
+ 0x00
+ };
+
+ unsigned char packet99[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // frame type (IETF_ACK frame)
+ 0x02,
+ // Largest acked
+ kVarInt62OneByte + 0x0A,
+ // Zero delta time.
+ kVarInt62OneByte + 0x00,
+ // Ack block count 2
+ kVarInt62OneByte + 0x02,
+ // First ack block length
+ kVarInt62OneByte + 0x01,
+ // gap to next block length
+ kVarInt62OneByte + 0x00,
+ // ack block length
+ kVarInt62OneByte + 0x00,
+ // gap to next block length
+ kVarInt62OneByte + 0x00,
+ // ack block length
+ kVarInt62OneByte + 0x05,
+ };
+ // clang-format on
+
+ unsigned char* p = packet;
+ size_t p_size = QUIC_ARRAYSIZE(packet);
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ p = packet99;
+ p_size = QUIC_ARRAYSIZE(packet99);
+ } else if (framer_.transport_version() > QUIC_VERSION_44) {
+ p = packet46;
+ p_size = QUIC_ARRAYSIZE(packet46);
+ } else if (framer_.transport_version() > QUIC_VERSION_43) {
+ p = packet44;
+ p_size = QUIC_ARRAYSIZE(packet44);
+ }
+
+ QuicEncryptedPacket encrypted(AsChars(p), p_size, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ EXPECT_EQ(framer_.detailed_error(),
+ "Underflow with ack block length 6 latest ack block end is 5.");
+ } else {
+ EXPECT_EQ(framer_.detailed_error(),
+ "Underflow with ack block length 6, end of block is 6.");
+ }
+}
+
+TEST_P(QuicFramerTest, CoalescedPacket) {
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ // clang-format off
+ unsigned char packet[] = {
+ // first coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x50,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x1E,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // second coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x50,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x1E,
+ // packet number
+ 0x12, 0x34, 0x56, 0x79,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'H', 'E', 'L', 'L',
+ 'O', '_', 'W', 'O',
+ 'R', 'L', 'D', '?',
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+ // Stream ID should be the last 3 bytes of kStreamId.
+ EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ ASSERT_EQ(visitor_.coalesced_packets_.size(), 1u);
+ EXPECT_TRUE(framer_.ProcessPacket(*visitor_.coalesced_packets_[0].get()));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+
+ ASSERT_EQ(2u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+ // Stream ID should be the last 3 bytes of kStreamId.
+ EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[1]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[1]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[1]->offset);
+ CheckStreamFrameData("HELLO_WORLD?", visitor_.stream_frames_[1].get());
+}
+
+TEST_P(QuicFramerTest, MismatchedCoalescedPacket) {
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ // clang-format off
+ unsigned char packet[] = {
+ // first coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x50,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x1E,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // second coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x50,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x11,
+ // long header packet length
+ 0x1E,
+ // packet number
+ 0x12, 0x34, 0x56, 0x79,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'H', 'E', 'L', 'L',
+ 'O', '_', 'W', 'O',
+ 'R', 'L', 'D', '?',
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_QUIC_PEER_BUG(EXPECT_TRUE(framer_.ProcessPacket(encrypted)),
+ "Server: Received mismatched coalesced header.*");
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+ // Stream ID should be the last 3 bytes of kStreamId.
+ EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ ASSERT_EQ(visitor_.coalesced_packets_.size(), 0u);
+}
+
+TEST_P(QuicFramerTest, InvalidCoalescedPacket) {
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ // clang-format off
+ unsigned char packet[] = {
+ // first coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x50,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // long header packet length
+ 0x1E,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // frame type (IETF_STREAM frame with FIN, LEN, and OFFSET bits set)
+ 0x08 | 0x01 | 0x02 | 0x04,
+ // stream id
+ kVarInt62FourBytes + 0x00, 0x02, 0x03, 0x04,
+ // offset
+ kVarInt62EightBytes + 0x3A, 0x98, 0xFE, 0xDC,
+ 0x32, 0x10, 0x76, 0x54,
+ // data length
+ kVarInt62OneByte + 0x0c,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ // second coalesced packet
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version would be here but we cut off the invalid coalesced header.
+ };
+ // clang-format on
+
+ QuicEncryptedPacket encrypted(AsChars(packet), QUIC_ARRAYSIZE(packet), false);
+ EXPECT_QUIC_PEER_BUG(EXPECT_TRUE(framer_.ProcessPacket(encrypted)),
+ "Server: Failed to parse received coalesced header.*");
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+
+ // Stream ID should be the last 3 bytes of kStreamId.
+ EXPECT_EQ(0x00FFFFFF & kStreamId, visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(kStreamOffset, visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0].get());
+
+ ASSERT_EQ(visitor_.coalesced_packets_.size(), 0u);
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWithVariableLengthConnectionId) {
+ if (framer_.transport_version() < QUIC_VERSION_46) {
+ 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,
+ sizeof(connection_id_bytes));
+ QuicFramerPeer::SetLargestPacketNumber(&framer_, kPacketNumber - 2);
+ QuicFramerPeer::SetExpectedConnectionIDLength(&framer_,
+ connection_id.length());
+
+ // clang-format off
+ PacketFragments packet = {
+ // type (8 byte connection_id and 1 byte packet number)
+ {"Unable to read type.",
+ {0x40}},
+ // connection_id
+ {"Unable to read Destination ConnectionId.",
+ {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42}},
+ // packet number
+ {"Unable to read packet number.",
+ {0x78}},
+ };
+ // clang-format on
+
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ AssemblePacketFromFragments(packet));
+ EXPECT_FALSE(framer_.ProcessPacket(*encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(connection_id, visitor_.header_->destination_connection_id);
+ EXPECT_FALSE(visitor_.header_->reset_flag);
+ EXPECT_FALSE(visitor_.header_->version_flag);
+ EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER, visitor_.header_->packet_number_length);
+ EXPECT_EQ(kPacketNumber, visitor_.header_->packet_number);
+
+ CheckFramingBoundaries(packet, QUIC_INVALID_PACKET_HEADER);
+}
+
+TEST_P(QuicFramerTest, UpdateExpectedConnectionIdLength) {
+ if (framer_.transport_version() < QUIC_VERSION_46) {
+ return;
+ }
+ SetDecrypterLevel(ENCRYPTION_ZERO_RTT);
+ framer_.SetShouldUpdateExpectedConnectionIdLength(true);
+
+ // clang-format off
+ unsigned char long_header_packet[] = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x60,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ unsigned char long_header_packet99[] = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x60,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // long header packet length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ EXPECT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(long_header_packet),
+ QUIC_ARRAYSIZE(long_header_packet), false)));
+ } else {
+ EXPECT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(long_header_packet99),
+ QUIC_ARRAYSIZE(long_header_packet99), false)));
+ }
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(visitor_.header_.get()->destination_connection_id,
+ FramerTestConnectionIdNineBytes());
+ EXPECT_EQ(visitor_.header_.get()->packet_number,
+ QuicPacketNumber(UINT64_C(0x12345678)));
+
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char short_header_packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // packet number
+ 0x13, 0x37, 0x42, 0x33,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ QuicEncryptedPacket short_header_encrypted(
+ AsChars(short_header_packet), QUIC_ARRAYSIZE(short_header_packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(short_header_encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(visitor_.header_.get()->destination_connection_id,
+ FramerTestConnectionIdNineBytes());
+ EXPECT_EQ(visitor_.header_.get()->packet_number,
+ QuicPacketNumber(UINT64_C(0x13374233)));
+}
+
+TEST_P(QuicFramerTest, MultiplePacketNumberSpaces) {
+ if (framer_.transport_version() < QUIC_VERSION_46) {
+ return;
+ }
+ framer_.SetShouldUpdateExpectedConnectionIdLength(true);
+ framer_.EnableMultiplePacketNumberSpacesSupport();
+
+ // clang-format off
+ unsigned char long_header_packet[] = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x60,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ unsigned char long_header_packet99[] = {
+ // public flags (long header with packet type ZERO_RTT_PROTECTED and
+ // 4-byte packet number)
+ 0xD3,
+ // version
+ QUIC_VERSION_BYTES,
+ // destination connection ID length
+ 0x60,
+ // destination connection ID
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // long header packet length
+ 0x05,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<TestDecrypter>());
+ framer_.RemoveDecrypter(ENCRYPTION_INITIAL);
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_ZERO_RTT, QuicMakeUnique<TestDecrypter>());
+ }
+ if (!QuicVersionHasLongHeaderLengths(framer_.transport_version())) {
+ EXPECT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(long_header_packet),
+ QUIC_ARRAYSIZE(long_header_packet), false)));
+ } else {
+ EXPECT_TRUE(framer_.ProcessPacket(
+ QuicEncryptedPacket(AsChars(long_header_packet99),
+ QUIC_ARRAYSIZE(long_header_packet99), false)));
+ }
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ EXPECT_FALSE(
+ QuicFramerPeer::GetLargestDecryptedPacketNumber(&framer_, INITIAL_DATA)
+ .IsInitialized());
+ EXPECT_FALSE(
+ QuicFramerPeer::GetLargestDecryptedPacketNumber(&framer_, HANDSHAKE_DATA)
+ .IsInitialized());
+ EXPECT_EQ(kPacketNumber, QuicFramerPeer::GetLargestDecryptedPacketNumber(
+ &framer_, APPLICATION_DATA));
+
+ // clang-format off
+ unsigned char short_header_packet[] = {
+ // type (short header, 1 byte packet number)
+ 0x40,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x42,
+ // packet number
+ 0x79,
+ // padding frame
+ 0x00,
+ };
+ // clang-format on
+
+ QuicEncryptedPacket short_header_encrypted(
+ AsChars(short_header_packet), QUIC_ARRAYSIZE(short_header_packet), false);
+ if (framer_.version().KnowsWhichDecrypterToUse()) {
+ framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TestDecrypter>());
+ framer_.RemoveDecrypter(ENCRYPTION_ZERO_RTT);
+ } else {
+ framer_.SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<TestDecrypter>());
+ }
+ EXPECT_TRUE(framer_.ProcessPacket(short_header_encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ EXPECT_FALSE(
+ QuicFramerPeer::GetLargestDecryptedPacketNumber(&framer_, INITIAL_DATA)
+ .IsInitialized());
+ EXPECT_FALSE(
+ QuicFramerPeer::GetLargestDecryptedPacketNumber(&framer_, HANDSHAKE_DATA)
+ .IsInitialized());
+ EXPECT_EQ(kPacketNumber + 1, QuicFramerPeer::GetLargestDecryptedPacketNumber(
+ &framer_, APPLICATION_DATA));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_ietf_framer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_ietf_framer_test.cc
new file mode 100644
index 00000000000..b0c01257356
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_ietf_framer_test.cc
@@ -0,0 +1,1484 @@
+// 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.
+
+// gunit tests for the IETF-format framer --- generally does a simple test
+// for each framer; we generate the template object (eg
+// QuicIetfStreamFrame) with the correct stuff in it, ask that a frame
+// be serialized (call AppendIetf<mumble>) then deserialized (call
+// ProcessIetf<mumble>) and then check that the gazintas and gazoutas
+// are the same.
+//
+// We do minimal checking of the serialized frame
+//
+// We do look at various different values (resulting in different
+// length varints, etc)
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const size_t kNormalPacketBufferSize = 1400;
+// Several different stream ids, should be encoded
+// in 8, 4, 2, and 1 byte, respectively. Last one
+// checks that value==0 works.
+// All stream IDs end in 0x0, so the client/server- initiated
+// and Uni/Bi-directional bits are available to alter, as any
+// given test may wish.
+const QuicIetfStreamId kStreamId8 = UINT64_C(0x3EDCBA9876543210);
+const QuicIetfStreamId kStreamId4 = UINT64_C(0x36543210);
+const QuicIetfStreamId kStreamId2 = UINT64_C(0x3210);
+const QuicIetfStreamId kStreamId1 = UINT64_C(0x10);
+const QuicIetfStreamId kStreamId0 = UINT64_C(0x00);
+
+// Ditto for the offsets.
+const QuicIetfStreamOffset kOffset8 = UINT64_C(0x3210BA9876543210);
+const QuicIetfStreamOffset kOffset4 = UINT64_C(0x32109876);
+const QuicIetfStreamOffset kOffset2 = UINT64_C(0x3456);
+const QuicIetfStreamOffset kOffset1 = UINT64_C(0x3f);
+const QuicIetfStreamOffset kOffset0 = UINT64_C(0x00);
+
+// Structures used to create various ack frames.
+
+// Defines an ack frame to feed through the framer/deframer.
+struct ack_frame {
+ uint64_t delay_time;
+ bool is_ack_ecn;
+ QuicPacketCount ect_0_count;
+ QuicPacketCount ect_1_count;
+ QuicPacketCount ecn_ce_count;
+ const std::vector<QuicAckBlock>& ranges;
+ uint64_t expected_frame_type;
+};
+
+class TestQuicVisitor : public QuicFramerVisitorInterface {
+ public:
+ TestQuicVisitor() {}
+
+ ~TestQuicVisitor() override {}
+
+ void OnError(QuicFramer* f) override {
+ QUIC_DLOG(INFO) << "QuicIetfFramer Error: "
+ << QuicErrorCodeToString(f->error()) << " (" << f->error()
+ << ")";
+ }
+
+ void OnPacket() override {}
+
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {}
+
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {}
+
+ bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
+ PacketHeaderFormat form) override {
+ return true;
+ }
+
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override {
+ return true;
+ }
+
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override {
+ return true;
+ }
+
+ void OnDecryptedPacket(EncryptionLevel level) override {}
+
+ bool OnPacketHeader(const QuicPacketHeader& header) override { return true; }
+
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {}
+
+ bool OnStreamFrame(const QuicStreamFrame& frame) override { return true; }
+
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override { return true; }
+
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override {
+ return true;
+ }
+
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
+ return true;
+ }
+
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override {
+ return true;
+ }
+
+ bool OnAckFrameEnd(QuicPacketNumber start) override { return true; }
+
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
+ return true;
+ }
+
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override { return true; }
+
+ bool OnPingFrame(const QuicPingFrame& frame) override { return true; }
+
+ bool OnMessageFrame(const QuicMessageFrame& frame) override { return true; }
+
+ void OnPacketComplete() override {}
+
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
+ return true;
+ }
+
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override {
+ return true;
+ }
+
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+ return true;
+ }
+
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override {
+ return true;
+ }
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override {
+ return true;
+ }
+
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override { return true; }
+
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {
+ return true;
+ }
+
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override { return true; }
+
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override {
+ return true;
+ }
+
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override {
+ return true;
+ }
+
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override { return true; }
+
+ bool IsValidStatelessResetToken(QuicUint128 token) const override {
+ return true;
+ }
+
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {}
+
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
+ return true;
+ }
+
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
+ return true;
+ }
+};
+
+class QuicIetfFramerTest : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ QuicIetfFramerTest()
+ : start_(QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(0x10)),
+ framer_(AllSupportedVersions(),
+ start_,
+ Perspective::IS_SERVER,
+ kQuicDefaultConnectionIdLength) {
+ framer_.set_visitor(&visitor_);
+ }
+
+ // Utility functions to do actual framing/deframing.
+ void TryStreamFrame(char* packet_buffer,
+ size_t packet_buffer_size,
+ const char* xmit_packet_data,
+ size_t xmit_packet_data_size,
+ QuicIetfStreamId stream_id,
+ QuicIetfStreamOffset offset,
+ bool fin_bit,
+ bool last_frame_bit,
+ QuicIetfFrameType frame_type) {
+ // initialize a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(packet_buffer_size, packet_buffer,
+ NETWORK_BYTE_ORDER); // do not really care
+ // about endianness.
+ // set up to define the source frame we wish to send.
+ QuicStreamFrame source_stream_frame(
+ stream_id, fin_bit, offset, xmit_packet_data, xmit_packet_data_size);
+
+ // Write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendIetfStreamFrame(
+ &framer_, source_stream_frame, last_frame_bit, &writer));
+ // Better have something in the packet buffer.
+ EXPECT_NE(0u, writer.length());
+ // Now set up a reader to read in the frame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ // A StreamFrame to hold the results... we know the frame type,
+ // put it into the QuicIetfStreamFrame
+ QuicStreamFrame sink_stream_frame;
+ if (xmit_packet_data_size) {
+ EXPECT_EQ(sink_stream_frame.data_buffer, nullptr);
+ EXPECT_EQ(sink_stream_frame.data_length, 0u);
+ }
+
+ EXPECT_TRUE(QuicFramerPeer::ProcessIetfStreamFrame(
+ &framer_, &reader, frame_type, &sink_stream_frame));
+
+ // Now check that the streamid, fin-bit, offset, and
+ // data len all match the input.
+ EXPECT_EQ(sink_stream_frame.stream_id, source_stream_frame.stream_id);
+ EXPECT_EQ(sink_stream_frame.fin, source_stream_frame.fin);
+ EXPECT_EQ(sink_stream_frame.data_length, source_stream_frame.data_length);
+ if (frame_type & IETF_STREAM_FRAME_OFF_BIT) {
+ // There was an offset in the frame, see if xmit and rcv vales equal.
+ EXPECT_EQ(sink_stream_frame.offset, source_stream_frame.offset);
+ } else {
+ // Offset not in frame, so it better come out 0.
+ EXPECT_EQ(sink_stream_frame.offset, 0u);
+ }
+ if (xmit_packet_data_size) {
+ ASSERT_NE(sink_stream_frame.data_buffer, nullptr);
+ ASSERT_NE(source_stream_frame.data_buffer, nullptr);
+ EXPECT_EQ(strcmp(sink_stream_frame.data_buffer,
+ source_stream_frame.data_buffer),
+ 0);
+ } else {
+ // No data in the frame.
+ EXPECT_EQ(source_stream_frame.data_length, 0u);
+ EXPECT_EQ(sink_stream_frame.data_length, 0u);
+ }
+ }
+
+ // Overall ack frame encode/decode/compare function
+ // Encodes an ack frame as specified at |*frame|
+ // Then decodes the frame,
+ // Then compares the two
+ // Does some basic checking:
+ // - did the writer write something?
+ // - did the reader read the entire packet?
+ // - did the things the reader read match what the writer wrote?
+ // Returns true if it all worked false if not.
+ bool TryAckFrame(char* packet_buffer,
+ size_t packet_buffer_size,
+ struct ack_frame* frame) {
+ QuicAckFrame transmit_frame = InitAckFrame(frame->ranges);
+ if (frame->is_ack_ecn) {
+ transmit_frame.ecn_counters_populated = true;
+ transmit_frame.ect_0_count = frame->ect_0_count;
+ transmit_frame.ect_1_count = frame->ect_1_count;
+ transmit_frame.ecn_ce_count = frame->ecn_ce_count;
+ }
+ transmit_frame.ack_delay_time =
+ QuicTime::Delta::FromMicroseconds(frame->delay_time);
+ size_t expected_size =
+ QuicFramerPeer::GetIetfAckFrameSize(&framer_, transmit_frame);
+
+ // Make a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(expected_size, packet_buffer, NETWORK_BYTE_ORDER);
+
+ // Write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendIetfAckFrameAndTypeByte(
+ &framer_, transmit_frame, &writer));
+
+ size_t expected_frame_length = QuicFramerPeer::ComputeFrameLength(
+ &framer_, QuicFrame(&transmit_frame), false,
+ static_cast<QuicPacketNumberLength>(123456u));
+
+ // Encoded length should match what ComputeFrameLength returns
+ EXPECT_EQ(expected_frame_length, writer.length());
+ // and what is in the buffer should be the expected size.
+ EXPECT_EQ(expected_size, writer.length()) << "Frame is " << transmit_frame;
+ // Now set up a reader to read in the frame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ // read in the frame type
+ uint8_t received_frame_type;
+ EXPECT_TRUE(reader.ReadUInt8(&received_frame_type));
+ EXPECT_EQ(frame->expected_frame_type, received_frame_type);
+
+ // an AckFrame to hold the results
+ QuicAckFrame receive_frame;
+
+ EXPECT_TRUE(QuicFramerPeer::ProcessIetfAckFrame(
+ &framer_, &reader, received_frame_type, &receive_frame));
+
+ if (frame->is_ack_ecn &&
+ (frame->ect_0_count || frame->ect_1_count || frame->ecn_ce_count)) {
+ EXPECT_TRUE(receive_frame.ecn_counters_populated);
+ EXPECT_EQ(receive_frame.ect_0_count, frame->ect_0_count);
+ EXPECT_EQ(receive_frame.ect_1_count, frame->ect_1_count);
+ EXPECT_EQ(receive_frame.ecn_ce_count, frame->ecn_ce_count);
+ } else {
+ EXPECT_FALSE(receive_frame.ecn_counters_populated);
+ EXPECT_EQ(receive_frame.ect_0_count, 0u);
+ EXPECT_EQ(receive_frame.ect_1_count, 0u);
+ EXPECT_EQ(receive_frame.ecn_ce_count, 0u);
+ }
+
+ // Now check that the received frame matches the sent frame.
+ EXPECT_EQ(transmit_frame.largest_acked, receive_frame.largest_acked);
+ // The ~0x7 needs some explaining. The ack frame format down shifts the
+ // delay time by 3 (divide by 8) to allow for greater ranges in delay time.
+ // Therefore, if we give the framer a delay time that is not an
+ // even multiple of 8, the value that the deframer produces will
+ // not be the same as what the framer got. The downshift on
+ // framing and upshift on deframing results in clearing the 3
+ // low-order bits ... The masking basically does the same thing,
+ // so the compare works properly.
+ return true;
+ }
+
+ // encode, decode, and check a Path Challenge frame.
+ bool TryPathChallengeFrame(char* packet_buffer,
+ size_t packet_buffer_size,
+ const QuicPathFrameBuffer& data) {
+ // Make a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(packet_buffer_size, packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ QuicPathChallengeFrame transmit_frame(0, data);
+
+ // write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendPathChallengeFrame(
+ &framer_, transmit_frame, &writer));
+
+ // Check for correct length in the packet buffer.
+ EXPECT_EQ(kQuicPathChallengeFrameSize, writer.length());
+
+ // now set up a reader to read in the frame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ QuicPathChallengeFrame receive_frame;
+
+ EXPECT_TRUE(QuicFramerPeer::ProcessPathChallengeFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Now check that the received frame matches the sent frame.
+ EXPECT_EQ(
+ 0, memcmp(transmit_frame.data_buffer.data(),
+ receive_frame.data_buffer.data(), kQuicPathFrameBufferSize));
+ return true;
+ }
+
+ // encode, decode, and check a Path Response frame.
+ bool TryPathResponseFrame(char* packet_buffer,
+ size_t packet_buffer_size,
+ const QuicPathFrameBuffer& data) {
+ // Make a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(packet_buffer_size, packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ QuicPathResponseFrame transmit_frame(0, data);
+
+ // Write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendPathResponseFrame(
+ &framer_, transmit_frame, &writer));
+
+ // Check for correct length in the packet buffer.
+ EXPECT_EQ(kQuicPathResponseFrameSize, writer.length());
+
+ // Set up a reader to read in the frame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ QuicPathResponseFrame receive_frame;
+
+ EXPECT_TRUE(QuicFramerPeer::ProcessPathResponseFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Now check that the received frame matches the sent frame.
+ EXPECT_EQ(
+ 0, memcmp(transmit_frame.data_buffer.data(),
+ receive_frame.data_buffer.data(), kQuicPathFrameBufferSize));
+ return true;
+ }
+
+ // Test the Serialization/deserialization of a Reset Stream Frame.
+ void TryResetFrame(char* packet_buffer,
+ size_t packet_buffer_size,
+ QuicStreamId stream_id,
+ uint16_t error_code,
+ QuicStreamOffset final_offset) {
+ // Initialize a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(packet_buffer_size, packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ QuicRstStreamFrame transmit_frame(static_cast<QuicControlFrameId>(1),
+ stream_id, error_code, final_offset);
+
+ // Write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendIetfResetStreamFrame(
+ &framer_, transmit_frame, &writer));
+ // Check that the size of the serialzed frame is in the allowed range.
+ EXPECT_LT(3u, writer.length());
+ EXPECT_GT(19u, writer.length());
+ // Now set up a reader to read in the thing in.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ // A QuicRstStreamFrame to hold the results
+ QuicRstStreamFrame receive_frame;
+ EXPECT_TRUE(QuicFramerPeer::ProcessIetfResetStreamFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Now check that the received values match the input.
+ EXPECT_EQ(receive_frame.stream_id, transmit_frame.stream_id);
+ EXPECT_EQ(receive_frame.ietf_error_code, transmit_frame.ietf_error_code);
+ EXPECT_EQ(receive_frame.byte_offset, transmit_frame.byte_offset);
+ }
+
+ void TryMaxStreamsFrame(QuicStreamId stream_id,
+ bool unidirectional,
+ bool stream_id_server_initiated) {
+ if (!unidirectional && !stream_id_server_initiated && stream_id == 0) {
+ // For bidirectional, client initiated, streams, 0 is not allowed,
+ // it's used for the crypto stream and is not included in the counting.
+ return;
+ }
+
+ char packet_buffer[kNormalPacketBufferSize];
+ memset(packet_buffer, 0, sizeof(packet_buffer));
+
+ Perspective old_perspective = framer_.perspective();
+ // Set up the writer and transmit QuicMaxStreamIdFrame
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+ if (stream_id_server_initiated) {
+ stream_id |= 0x01;
+ }
+ if (unidirectional) {
+ stream_id |= 0x02;
+ }
+
+ // Set the perspective of the sender. If the stream id is supposed to
+ // be server-initiated, then the sender of the MAX_STREAMS should be
+ // a client, and vice versa. Do this prior to constructing the frame or
+ // generating the packet, so that any internal dependencies are satisfied.
+ QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated)
+ ? Perspective::IS_CLIENT
+ : Perspective::IS_SERVER);
+ QuicMaxStreamIdFrame transmit_frame(0, stream_id);
+
+ // Add the frame.
+ EXPECT_TRUE(QuicFramerPeer::AppendMaxStreamsFrame(&framer_, transmit_frame,
+ &writer));
+
+ // Check that buffer length is in the expected range
+ EXPECT_LE(1u, writer.length());
+ EXPECT_GE(8u, writer.length());
+
+ // Set the perspective for the receiver.
+ QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated)
+ ? Perspective::IS_SERVER
+ : Perspective::IS_CLIENT);
+
+ // Set up reader and empty receive QuicPaddingFrame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicMaxStreamIdFrame receive_frame;
+
+ // Deframe it
+ EXPECT_TRUE(QuicFramerPeer::ProcessMaxStreamsFrame(
+ &framer_, &reader, &receive_frame,
+ (unidirectional) ? IETF_MAX_STREAMS_UNIDIRECTIONAL
+ : IETF_MAX_STREAMS_BIDIRECTIONAL))
+ << " Error: " << framer_.detailed_error();
+
+ // Now check that received and sent data are equivalent
+ EXPECT_EQ(stream_id, receive_frame.max_stream_id);
+ EXPECT_EQ(transmit_frame.max_stream_id, receive_frame.max_stream_id);
+ QuicFramerPeer::SetPerspective(&framer_, old_perspective);
+ }
+
+ void TryStreamsBlockedFrame(QuicStreamId stream_id,
+ bool unidirectional,
+ bool stream_id_server_initiated) {
+ if (!unidirectional && !stream_id_server_initiated && stream_id == 0) {
+ // For bidirectional, client initiated, streams, 0 is not allowed,
+ // it's used for the crypto stream and is not included in the counting.
+ return;
+ }
+
+ char packet_buffer[kNormalPacketBufferSize];
+ memset(packet_buffer, 0, sizeof(packet_buffer));
+
+ Perspective old_perspective = framer_.perspective();
+ // Set up the writer and transmit QuicMaxStreamIdFrame
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+ if (stream_id_server_initiated) {
+ stream_id |= 0x01;
+ }
+ if (unidirectional) {
+ stream_id |= 0x02;
+ }
+
+ // Set the perspective of the sender. If the stream id is supposed to
+ // be server-initiated, then the sender of the MAX_STREAMS should be
+ // a client, and vice versa. Do this prior to constructing the frame or
+ // generating the packet, so that any internal dependencies are satisfied.
+ QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated)
+ ? Perspective::IS_SERVER
+ : Perspective::IS_CLIENT);
+ QuicStreamIdBlockedFrame transmit_frame(0, stream_id);
+
+ // Add the frame.
+ EXPECT_TRUE(QuicFramerPeer::AppendStreamsBlockedFrame(
+ &framer_, transmit_frame, &writer));
+
+ // Check that buffer length is in the expected range
+ EXPECT_LE(1u, writer.length());
+ EXPECT_GE(8u, writer.length());
+
+ // Set the perspective for the receiver.
+ QuicFramerPeer::SetPerspective(&framer_, (stream_id_server_initiated)
+ ? Perspective::IS_CLIENT
+ : Perspective::IS_SERVER);
+
+ // Set up reader and empty receive QuicPaddingFrame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicStreamIdBlockedFrame receive_frame;
+
+ // Deframe it
+ EXPECT_TRUE(QuicFramerPeer::ProcessStreamsBlockedFrame(
+ &framer_, &reader, &receive_frame,
+ (unidirectional) ? IETF_STREAMS_BLOCKED_UNIDIRECTIONAL
+ : IETF_STREAMS_BLOCKED_BIDIRECTIONAL));
+
+ // Now check that received and sent data are equivalent
+ EXPECT_EQ(stream_id, receive_frame.stream_id);
+ EXPECT_EQ(transmit_frame.stream_id, receive_frame.stream_id);
+ QuicFramerPeer::SetPerspective(&framer_, old_perspective);
+ }
+
+ QuicTime start_;
+ QuicFramer framer_;
+ test::TestQuicVisitor visitor_;
+};
+
+struct stream_frame_variant {
+ QuicIetfStreamId stream_id;
+ QuicIetfStreamOffset offset;
+ bool fin_bit;
+ bool last_frame_bit;
+ uint8_t frame_type;
+} stream_frame_to_test[] = {
+#define IETF_STREAM0 (((uint8_t)IETF_STREAM))
+
+#define IETF_STREAM1 (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_FIN_BIT)
+
+#define IETF_STREAM2 (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_LEN_BIT)
+
+#define IETF_STREAM3 \
+ (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_LEN_BIT | \
+ IETF_STREAM_FRAME_FIN_BIT)
+
+#define IETF_STREAM4 (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_OFF_BIT)
+
+#define IETF_STREAM5 \
+ (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_OFF_BIT | \
+ IETF_STREAM_FRAME_FIN_BIT)
+
+#define IETF_STREAM6 \
+ (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_OFF_BIT | \
+ IETF_STREAM_FRAME_LEN_BIT)
+
+#define IETF_STREAM7 \
+ (((uint8_t)IETF_STREAM) | IETF_STREAM_FRAME_OFF_BIT | \
+ IETF_STREAM_FRAME_LEN_BIT | IETF_STREAM_FRAME_FIN_BIT)
+
+ {kStreamId8, kOffset8, true, false, IETF_STREAM7},
+ {kStreamId8, kOffset8, false, false, IETF_STREAM6},
+ {kStreamId8, kOffset4, true, false, IETF_STREAM7},
+ {kStreamId8, kOffset4, false, false, IETF_STREAM6},
+ {kStreamId8, kOffset2, true, false, IETF_STREAM7},
+ {kStreamId8, kOffset2, false, false, IETF_STREAM6},
+ {kStreamId8, kOffset1, true, false, IETF_STREAM7},
+ {kStreamId8, kOffset1, false, false, IETF_STREAM6},
+ {kStreamId8, kOffset0, true, false, IETF_STREAM3},
+ {kStreamId8, kOffset0, false, false, IETF_STREAM2},
+ {kStreamId4, kOffset8, true, false, IETF_STREAM7},
+ {kStreamId4, kOffset8, false, false, IETF_STREAM6},
+ {kStreamId4, kOffset4, true, false, IETF_STREAM7},
+ {kStreamId4, kOffset4, false, false, IETF_STREAM6},
+ {kStreamId4, kOffset2, true, false, IETF_STREAM7},
+ {kStreamId4, kOffset2, false, false, IETF_STREAM6},
+ {kStreamId4, kOffset1, true, false, IETF_STREAM7},
+ {kStreamId4, kOffset1, false, false, IETF_STREAM6},
+ {kStreamId4, kOffset0, true, false, IETF_STREAM3},
+ {kStreamId4, kOffset0, false, false, IETF_STREAM2},
+ {kStreamId2, kOffset8, true, false, IETF_STREAM7},
+ {kStreamId2, kOffset8, false, false, IETF_STREAM6},
+ {kStreamId2, kOffset4, true, false, IETF_STREAM7},
+ {kStreamId2, kOffset4, false, false, IETF_STREAM6},
+ {kStreamId2, kOffset2, true, false, IETF_STREAM7},
+ {kStreamId2, kOffset2, false, false, IETF_STREAM6},
+ {kStreamId2, kOffset1, true, false, IETF_STREAM7},
+ {kStreamId2, kOffset1, false, false, IETF_STREAM6},
+ {kStreamId2, kOffset0, true, false, IETF_STREAM3},
+ {kStreamId2, kOffset0, false, false, IETF_STREAM2},
+ {kStreamId1, kOffset8, true, false, IETF_STREAM7},
+ {kStreamId1, kOffset8, false, false, IETF_STREAM6},
+ {kStreamId1, kOffset4, true, false, IETF_STREAM7},
+ {kStreamId1, kOffset4, false, false, IETF_STREAM6},
+ {kStreamId1, kOffset2, true, false, IETF_STREAM7},
+ {kStreamId1, kOffset2, false, false, IETF_STREAM6},
+ {kStreamId1, kOffset1, true, false, IETF_STREAM7},
+ {kStreamId1, kOffset1, false, false, IETF_STREAM6},
+ {kStreamId1, kOffset0, true, false, IETF_STREAM3},
+ {kStreamId1, kOffset0, false, false, IETF_STREAM2},
+ {kStreamId0, kOffset8, true, false, IETF_STREAM7},
+ {kStreamId0, kOffset8, false, false, IETF_STREAM6},
+ {kStreamId0, kOffset4, true, false, IETF_STREAM7},
+ {kStreamId0, kOffset4, false, false, IETF_STREAM6},
+ {kStreamId0, kOffset2, true, false, IETF_STREAM7},
+ {kStreamId0, kOffset2, false, false, IETF_STREAM6},
+ {kStreamId0, kOffset1, true, false, IETF_STREAM7},
+ {kStreamId0, kOffset1, false, false, IETF_STREAM6},
+ {kStreamId0, kOffset0, true, false, IETF_STREAM3},
+ {kStreamId0, kOffset0, false, false, IETF_STREAM2},
+
+ {kStreamId8, kOffset8, true, true, IETF_STREAM5},
+ {kStreamId8, kOffset8, false, true, IETF_STREAM4},
+ {kStreamId8, kOffset4, true, true, IETF_STREAM5},
+ {kStreamId8, kOffset4, false, true, IETF_STREAM4},
+ {kStreamId8, kOffset2, true, true, IETF_STREAM5},
+ {kStreamId8, kOffset2, false, true, IETF_STREAM4},
+ {kStreamId8, kOffset1, true, true, IETF_STREAM5},
+ {kStreamId8, kOffset1, false, true, IETF_STREAM4},
+ {kStreamId8, kOffset0, true, true, IETF_STREAM1},
+ {kStreamId8, kOffset0, false, true, IETF_STREAM0},
+ {kStreamId4, kOffset8, true, true, IETF_STREAM5},
+ {kStreamId4, kOffset8, false, true, IETF_STREAM4},
+ {kStreamId4, kOffset4, true, true, IETF_STREAM5},
+ {kStreamId4, kOffset4, false, true, IETF_STREAM4},
+ {kStreamId4, kOffset2, true, true, IETF_STREAM5},
+ {kStreamId4, kOffset2, false, true, IETF_STREAM4},
+ {kStreamId4, kOffset1, true, true, IETF_STREAM5},
+ {kStreamId4, kOffset1, false, true, IETF_STREAM4},
+ {kStreamId4, kOffset0, true, true, IETF_STREAM1},
+ {kStreamId4, kOffset0, false, true, IETF_STREAM0},
+ {kStreamId2, kOffset8, true, true, IETF_STREAM5},
+ {kStreamId2, kOffset8, false, true, IETF_STREAM4},
+ {kStreamId2, kOffset4, true, true, IETF_STREAM5},
+ {kStreamId2, kOffset4, false, true, IETF_STREAM4},
+ {kStreamId2, kOffset2, true, true, IETF_STREAM5},
+ {kStreamId2, kOffset2, false, true, IETF_STREAM4},
+ {kStreamId2, kOffset1, true, true, IETF_STREAM5},
+ {kStreamId2, kOffset1, false, true, IETF_STREAM4},
+ {kStreamId2, kOffset0, true, true, IETF_STREAM1},
+ {kStreamId2, kOffset0, false, true, IETF_STREAM0},
+ {kStreamId1, kOffset8, true, true, IETF_STREAM5},
+ {kStreamId1, kOffset8, false, true, IETF_STREAM4},
+ {kStreamId1, kOffset4, true, true, IETF_STREAM5},
+ {kStreamId1, kOffset4, false, true, IETF_STREAM4},
+ {kStreamId1, kOffset2, true, true, IETF_STREAM5},
+ {kStreamId1, kOffset2, false, true, IETF_STREAM4},
+ {kStreamId1, kOffset1, true, true, IETF_STREAM5},
+ {kStreamId1, kOffset1, false, true, IETF_STREAM4},
+ {kStreamId1, kOffset0, true, true, IETF_STREAM1},
+ {kStreamId1, kOffset0, false, true, IETF_STREAM0},
+ {kStreamId0, kOffset8, true, true, IETF_STREAM5},
+ {kStreamId0, kOffset8, false, true, IETF_STREAM4},
+ {kStreamId0, kOffset4, true, true, IETF_STREAM5},
+ {kStreamId0, kOffset4, false, true, IETF_STREAM4},
+ {kStreamId0, kOffset2, true, true, IETF_STREAM5},
+ {kStreamId0, kOffset2, false, true, IETF_STREAM4},
+ {kStreamId0, kOffset1, true, true, IETF_STREAM5},
+ {kStreamId0, kOffset1, false, true, IETF_STREAM4},
+ {kStreamId0, kOffset0, true, true, IETF_STREAM1},
+ {kStreamId0, kOffset0, false, true, IETF_STREAM0},
+};
+
+TEST_F(QuicIetfFramerTest, StreamFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+ const char* transmit_packet_data =
+ "this is a test of some packet data, "
+ "can do a simple strcmp to see if the "
+ "input and output are the same!";
+
+ size_t transmit_packet_data_len = strlen(transmit_packet_data) + 1;
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(stream_frame_to_test); ++i) {
+ SCOPED_TRACE(i);
+ struct stream_frame_variant* variant = &stream_frame_to_test[i];
+ TryStreamFrame(packet_buffer, sizeof(packet_buffer), transmit_packet_data,
+ transmit_packet_data_len, variant->stream_id,
+ variant->offset, variant->fin_bit, variant->last_frame_bit,
+ static_cast<QuicIetfFrameType>(variant->frame_type));
+ }
+}
+// As the previous test, but with no data.
+TEST_F(QuicIetfFramerTest, ZeroLengthStreamFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(stream_frame_to_test); ++i) {
+ SCOPED_TRACE(i);
+ struct stream_frame_variant* variant = &stream_frame_to_test[i];
+ TryStreamFrame(packet_buffer, sizeof(packet_buffer),
+ /* xmit_packet_data = */ NULL,
+ /* xmit_packet_data_size = */ 0, variant->stream_id,
+ variant->offset, variant->fin_bit, variant->last_frame_bit,
+ static_cast<QuicIetfFrameType>(variant->frame_type));
+ }
+}
+
+TEST_F(QuicIetfFramerTest, CryptoFrame) {
+ SimpleDataProducer data_producer;
+ framer_.set_data_producer(&data_producer);
+ char packet_buffer[kNormalPacketBufferSize];
+
+ QuicStringPiece frame_data("This is a CRYPTO frame.");
+
+ QuicStreamOffset offsets[] = {kOffset8, kOffset4, kOffset2, kOffset1,
+ kOffset0};
+ for (QuicStreamOffset offset : offsets) {
+ QuicCryptoFrame frame(ENCRYPTION_INITIAL, offset, frame_data.length());
+ data_producer.SaveCryptoData(ENCRYPTION_INITIAL, offset, frame_data);
+
+ QuicDataWriter writer(QUIC_ARRAYSIZE(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ // Write the frame.
+ EXPECT_TRUE(QuicFramerPeer::AppendCryptoFrame(&framer_, frame, &writer));
+ EXPECT_NE(0u, writer.length());
+ // Read it back.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicCryptoFrame read_frame;
+ EXPECT_TRUE(
+ QuicFramerPeer::ProcessCryptoFrame(&framer_, &reader, &read_frame));
+
+ // Check that the frames match:
+ QuicStringPiece read_data(read_frame.data_buffer, read_frame.data_length);
+ EXPECT_EQ(read_frame.data_length, frame.data_length);
+ EXPECT_EQ(read_frame.offset, frame.offset);
+ EXPECT_EQ(read_data, frame_data);
+ }
+}
+
+TEST_F(QuicIetfFramerTest, ConnectionCloseEmptyString) {
+ char packet_buffer[kNormalPacketBufferSize];
+
+ // initialize a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ // empty string,
+ std::string test_string = "Ich Bin Ein Jelly Donut?";
+ QuicConnectionCloseFrame sent_frame;
+ sent_frame.quic_error_code = static_cast<QuicErrorCode>(0);
+ sent_frame.error_details = test_string;
+ sent_frame.transport_close_frame_type = 123;
+ sent_frame.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ // write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendIetfConnectionCloseFrame(
+ &framer_, sent_frame, &writer));
+
+ // better have something in the packet buffer.
+ EXPECT_NE(0u, writer.length());
+
+ // now set up a reader to read in the frame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ // a QuicConnectionCloseFrame to hold the results.
+ QuicConnectionCloseFrame sink_frame;
+
+ EXPECT_TRUE(QuicFramerPeer::ProcessIetfConnectionCloseFrame(
+ &framer_, &reader, IETF_QUIC_TRANSPORT_CONNECTION_CLOSE, &sink_frame));
+
+ // Now check that received == sent
+ EXPECT_EQ(sent_frame.quic_error_code, sink_frame.quic_error_code);
+ EXPECT_EQ(sink_frame.quic_error_code, static_cast<QuicErrorCode>(0));
+ EXPECT_EQ(sink_frame.error_details, test_string);
+ EXPECT_EQ(sink_frame.close_type, sent_frame.close_type);
+ EXPECT_EQ(sent_frame.close_type, IETF_QUIC_TRANSPORT_CONNECTION_CLOSE);
+}
+
+TEST_F(QuicIetfFramerTest, ApplicationCloseEmptyString) {
+ char packet_buffer[kNormalPacketBufferSize];
+
+ // initialize a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ // empty string,
+ std::string test_string = "Ich Bin Ein Jelly Donut?";
+ QuicConnectionCloseFrame sent_frame;
+ sent_frame.quic_error_code = static_cast<QuicErrorCode>(0);
+ sent_frame.error_details = test_string;
+ sent_frame.close_type = IETF_QUIC_APPLICATION_CONNECTION_CLOSE;
+ // write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendIetfConnectionCloseFrame(
+ &framer_, sent_frame, &writer));
+
+ // better have something in the packet buffer.
+ EXPECT_NE(0u, writer.length());
+
+ // now set up a reader to read in the frame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ // a QuicConnectionCloseFrame to hold the results.
+ QuicConnectionCloseFrame sink_frame;
+
+ EXPECT_TRUE(QuicFramerPeer::ProcessIetfConnectionCloseFrame(
+ &framer_, &reader, IETF_QUIC_APPLICATION_CONNECTION_CLOSE, &sink_frame));
+
+ // Now check that received == sent
+ EXPECT_EQ(sink_frame.quic_error_code, static_cast<QuicErrorCode>(0));
+ EXPECT_EQ(sent_frame.quic_error_code, sink_frame.quic_error_code);
+ EXPECT_EQ(sink_frame.error_details, test_string);
+ EXPECT_EQ(sent_frame.close_type, IETF_QUIC_APPLICATION_CONNECTION_CLOSE);
+ EXPECT_EQ(sent_frame.close_type, sink_frame.close_type);
+}
+
+// Testing for the IETF ACK framer.
+// clang-format off
+struct ack_frame ack_frame_variants[] = {
+ {90000,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1000), QuicPacketNumber(2001)}},
+ IETF_ACK},
+ {0,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1000), QuicPacketNumber(2001)}},
+ IETF_ACK},
+ {1,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(2)},
+ {QuicPacketNumber(5), QuicPacketNumber(6)}},
+ IETF_ACK},
+ {63,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(2)},
+ {QuicPacketNumber(5), QuicPacketNumber(6)}},
+ IETF_ACK},
+ {64,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(2)},
+ {QuicPacketNumber(3), QuicPacketNumber(4)},
+ {QuicPacketNumber(5), QuicPacketNumber(6)},
+ {QuicPacketNumber(7), QuicPacketNumber(8)},
+ {QuicPacketNumber(9), QuicPacketNumber(10)},
+ {QuicPacketNumber(11), QuicPacketNumber(12)}},
+ IETF_ACK},
+ {10000,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(2)},
+ {QuicPacketNumber(3), QuicPacketNumber(4)},
+ {QuicPacketNumber(5), QuicPacketNumber(6)},
+ {QuicPacketNumber(7), QuicPacketNumber(8)},
+ {QuicPacketNumber(9), QuicPacketNumber(10)},
+ {QuicPacketNumber(11), QuicPacketNumber(12)}},
+ IETF_ACK},
+ {100000000,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(2)},
+ {QuicPacketNumber(3), QuicPacketNumber(4)},
+ {QuicPacketNumber(5), QuicPacketNumber(6)},
+ {QuicPacketNumber(7), QuicPacketNumber(8)},
+ {QuicPacketNumber(9), QuicPacketNumber(10)},
+ {QuicPacketNumber(11), QuicPacketNumber(12)}},
+ IETF_ACK},
+ {0,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)}},
+ IETF_ACK},
+ {9223372036854775807,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(11)},
+ {QuicPacketNumber(74), QuicPacketNumber(138)}},
+ IETF_ACK},
+ // This ack is for packets 60 & 125. There are 64 packets in the gap.
+ // The encoded value is gap_size - 1, or 63. Crosses a VarInt62 encoding
+ // boundary...
+ {1,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(60), QuicPacketNumber(61)},
+ {QuicPacketNumber(125), QuicPacketNumber(126)}},
+ IETF_ACK},
+ {2,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(130)}},
+ IETF_ACK},
+ {3,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(195)}},
+ IETF_ACK},
+ {4,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(194)}},
+ IETF_ACK},
+ {5,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(193)}},
+ IETF_ACK},
+ {6,
+ false,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(192)}},
+ IETF_ACK},
+ // declare some ack_ecn frames to try.
+ {6,
+ false,
+ 100,
+ 200,
+ 300,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(192)}},
+ IETF_ACK},
+ {6,
+ true,
+ 100,
+ 200,
+ 300,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(192)}},
+ IETF_ACK_ECN},
+ {6,
+ true,
+ 100,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(192)}},
+ IETF_ACK_ECN},
+ {6,
+ true,
+ 0,
+ 200,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(192)}},
+ IETF_ACK_ECN},
+ {6,
+ true,
+ 0,
+ 0,
+ 300,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(192)}},
+ IETF_ACK_ECN},
+ {6,
+ true,
+ 0,
+ 0,
+ 0,
+ {{QuicPacketNumber(1), QuicPacketNumber(65)},
+ {QuicPacketNumber(129), QuicPacketNumber(192)}},
+ IETF_ACK},
+};
+// clang-format on
+
+TEST_F(QuicIetfFramerTest, AckFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+ for (auto ack_frame_variant : ack_frame_variants) {
+ EXPECT_TRUE(
+ TryAckFrame(packet_buffer, sizeof(packet_buffer), &ack_frame_variant));
+ }
+}
+
+// Test the case of having a QuicAckFrame with no ranges in it. By
+// examination of the Google Quic Ack code and tests, this case should
+// be handled as an ack with no "ranges after the first"; the
+// AckBlockCount should be 0 and the FirstAckBlock should be
+// |LargestAcked| - 1 (number of packets preceding the LargestAcked.
+TEST_F(QuicIetfFramerTest, AckFrameNoRanges) {
+ char packet_buffer[kNormalPacketBufferSize];
+
+ // Make a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ QuicAckFrame transmit_frame;
+ transmit_frame.largest_acked = QuicPacketNumber(1);
+ transmit_frame.ack_delay_time = QuicTime::Delta::FromMicroseconds(0);
+
+ size_t expected_size =
+ QuicFramerPeer::GetIetfAckFrameSize(&framer_, transmit_frame);
+ // Write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendIetfAckFrameAndTypeByte(
+ &framer_, transmit_frame, &writer));
+
+ uint8_t packet[] = {
+ 0x02, // type, IETF_ACK
+ 0x01, // largest_acked,
+ 0x00, // delay
+ 0x00, // count of additional ack blocks
+ 0x00, // size of first ack block (packets preceding largest_acked)
+ };
+ EXPECT_EQ(expected_size, sizeof(packet));
+ EXPECT_EQ(sizeof(packet), writer.length());
+ EXPECT_EQ(0, memcmp(packet, packet_buffer, writer.length()));
+
+ // Now set up a reader to read in the frame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ // an AckFrame to hold the results
+ QuicAckFrame receive_frame;
+
+ // read in the frame type
+ uint8_t received_frame_type;
+ EXPECT_TRUE(reader.ReadUInt8(&received_frame_type));
+ EXPECT_EQ(received_frame_type, IETF_ACK);
+
+ EXPECT_TRUE(QuicFramerPeer::ProcessIetfAckFrame(&framer_, &reader, IETF_ACK,
+ &receive_frame));
+
+ // Now check that the received frame matches the sent frame.
+ EXPECT_EQ(transmit_frame.largest_acked, receive_frame.largest_acked);
+}
+
+TEST_F(QuicIetfFramerTest, PathChallengeFrame) {
+ // Double-braces needed on some platforms due to
+ // https://bugs.llvm.org/show_bug.cgi?id=21629
+ QuicPathFrameBuffer buffer0 = {{0, 0, 0, 0, 0, 0, 0, 0}};
+ QuicPathFrameBuffer buffer1 = {
+ {0x80, 0x91, 0xa2, 0xb3, 0xc4, 0xd5, 0xe5, 0xf7}};
+ char packet_buffer[kNormalPacketBufferSize];
+ EXPECT_TRUE(
+ TryPathChallengeFrame(packet_buffer, sizeof(packet_buffer), buffer0));
+ EXPECT_TRUE(
+ TryPathChallengeFrame(packet_buffer, sizeof(packet_buffer), buffer1));
+}
+
+TEST_F(QuicIetfFramerTest, PathResponseFrame) {
+ // Double-braces needed on some platforms due to
+ // https://bugs.llvm.org/show_bug.cgi?id=21629
+ QuicPathFrameBuffer buffer0 = {{0, 0, 0, 0, 0, 0, 0, 0}};
+ QuicPathFrameBuffer buffer1 = {
+ {0x80, 0x91, 0xa2, 0xb3, 0xc4, 0xd5, 0xe5, 0xf7}};
+ char packet_buffer[kNormalPacketBufferSize];
+ EXPECT_TRUE(
+ TryPathResponseFrame(packet_buffer, sizeof(packet_buffer), buffer0));
+ EXPECT_TRUE(
+ TryPathResponseFrame(packet_buffer, sizeof(packet_buffer), buffer1));
+}
+
+TEST_F(QuicIetfFramerTest, ResetStreamFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+ struct resets {
+ QuicStreamId stream_id;
+ uint16_t error_code;
+ QuicStreamOffset final_offset;
+ } reset_frames[] = {
+ {0, 55, 0},
+ {0x10, 73, 0x300},
+ };
+ for (auto reset : reset_frames) {
+ TryResetFrame(packet_buffer, sizeof(packet_buffer), reset.stream_id,
+ reset.error_code, reset.final_offset);
+ }
+}
+
+TEST_F(QuicIetfFramerTest, StopSendingFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+
+ // Make a writer so that the serialized packet is placed in
+ // packet_buffer.
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ QuicStopSendingFrame transmit_frame;
+ transmit_frame.stream_id = 12345;
+ transmit_frame.application_error_code = 543;
+
+ // Write the frame to the packet buffer.
+ EXPECT_TRUE(QuicFramerPeer::AppendStopSendingFrame(&framer_, transmit_frame,
+ &writer));
+ // Check that the number of bytes in the buffer is in the
+ // allowed range.
+ EXPECT_LE(3u, writer.length());
+ EXPECT_GE(10u, writer.length());
+
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+
+ // A frame to hold the results
+ QuicStopSendingFrame receive_frame;
+
+ EXPECT_TRUE(QuicFramerPeer::ProcessStopSendingFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Verify that the transmitted and received values are the same.
+ EXPECT_EQ(receive_frame.stream_id, 12345u);
+ EXPECT_EQ(receive_frame.application_error_code, 543u);
+ EXPECT_EQ(receive_frame.stream_id, transmit_frame.stream_id);
+ EXPECT_EQ(receive_frame.application_error_code,
+ transmit_frame.application_error_code);
+}
+
+TEST_F(QuicIetfFramerTest, MaxDataFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+ QuicStreamOffset window_sizes[] = {0, 1, 2, 5, 10,
+ 20, 50, 100, 200, 500,
+ 1000000, kOffset8, kOffset4, kOffset2};
+ for (QuicStreamOffset window_size : window_sizes) {
+ memset(packet_buffer, 0, sizeof(packet_buffer));
+
+ // Set up the writer and transmit QuicWindowUpdateFrame
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+ QuicWindowUpdateFrame transmit_frame(0, 99, window_size);
+
+ // Add the frame.
+ EXPECT_TRUE(
+ QuicFramerPeer::AppendMaxDataFrame(&framer_, transmit_frame, &writer));
+
+ // Check that the number of bytes in the buffer is in the expected range.
+ EXPECT_LE(1u, writer.length());
+ EXPECT_GE(8u, writer.length());
+
+ // Set up reader and an empty QuicWindowUpdateFrame
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicWindowUpdateFrame receive_frame;
+
+ // Deframe it
+ EXPECT_TRUE(
+ QuicFramerPeer::ProcessMaxDataFrame(&framer_, &reader, &receive_frame));
+
+ // Now check that the received data equals the sent data.
+ EXPECT_EQ(transmit_frame.byte_offset, window_size);
+ EXPECT_EQ(transmit_frame.byte_offset, receive_frame.byte_offset);
+ EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()),
+ receive_frame.stream_id);
+ }
+}
+
+TEST_F(QuicIetfFramerTest, MaxStreamDataFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+ QuicStreamOffset window_sizes[] = {0, 1, 2, 5, 10,
+ 20, 50, 100, 200, 500,
+ 1000000, kOffset8, kOffset4, kOffset2};
+ QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1,
+ kStreamId0};
+
+ for (QuicIetfStreamId stream_id : stream_ids) {
+ for (QuicStreamOffset window_size : window_sizes) {
+ memset(packet_buffer, 0, sizeof(packet_buffer));
+
+ // Set up the writer and transmit QuicWindowUpdateFrame
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+ QuicWindowUpdateFrame transmit_frame(0, stream_id, window_size);
+
+ // Add the frame.
+ EXPECT_TRUE(QuicFramerPeer::AppendMaxStreamDataFrame(
+ &framer_, transmit_frame, &writer));
+
+ // Check that number of bytes in the buffer is in the expected range.
+ EXPECT_LE(2u, writer.length());
+ EXPECT_GE(16u, writer.length());
+
+ // Set up reader and empty receive QuicPaddingFrame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicWindowUpdateFrame receive_frame;
+
+ // Deframe it
+ EXPECT_TRUE(QuicFramerPeer::ProcessMaxStreamDataFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Now check that received data and sent data are equal.
+ EXPECT_EQ(transmit_frame.byte_offset, window_size);
+ EXPECT_EQ(transmit_frame.byte_offset, receive_frame.byte_offset);
+ EXPECT_EQ(stream_id, receive_frame.stream_id);
+ EXPECT_EQ(transmit_frame.stream_id, receive_frame.stream_id);
+ }
+ }
+}
+
+TEST_F(QuicIetfFramerTest, MaxStreamsFrame) {
+ QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1,
+ kStreamId0};
+
+ for (QuicIetfStreamId stream_id : stream_ids) {
+ // Cover all four combinations of uni/bi-directional and
+ // server-/client- initiation.
+ TryMaxStreamsFrame(stream_id, /*unidirectional=*/true,
+ /*stream_id_server_initiated=*/true);
+ TryMaxStreamsFrame(stream_id, /*unidirectional=*/true,
+ /*stream_id_server_initiated=*/false);
+ TryMaxStreamsFrame(stream_id, /*unidirectional=*/false,
+ /*stream_id_server_initiated=*/true);
+ TryMaxStreamsFrame(stream_id, /*unidirectional=*/false,
+ /*stream_id_server_initiated=*/false);
+ }
+}
+
+TEST_F(QuicIetfFramerTest, BlockedFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+ QuicStreamOffset offsets[] = {kOffset8, kOffset4, kOffset2, kOffset1,
+ kOffset0};
+
+ for (QuicStreamOffset offset : offsets) {
+ memset(packet_buffer, 0, sizeof(packet_buffer));
+
+ // Set up the writer and transmit QuicBlockedFrame
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+ QuicBlockedFrame transmit_frame(
+ 0, QuicUtils::GetInvalidStreamId(framer_.transport_version()), offset);
+
+ // Add the frame.
+ EXPECT_TRUE(QuicFramerPeer::AppendIetfBlockedFrame(&framer_, transmit_frame,
+ &writer));
+
+ // Check that buffer length is in the expected range
+ EXPECT_LE(1u, writer.length());
+ EXPECT_GE(8u, writer.length());
+
+ // Set up reader and empty receive QuicFrame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicBlockedFrame receive_frame;
+
+ // Deframe it
+ EXPECT_TRUE(QuicFramerPeer::ProcessIetfBlockedFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Check that received and sent data are equivalent
+ EXPECT_EQ(QuicUtils::GetInvalidStreamId(framer_.transport_version()),
+ receive_frame.stream_id);
+ EXPECT_EQ(offset, receive_frame.offset);
+ EXPECT_EQ(transmit_frame.offset, receive_frame.offset);
+ }
+}
+
+TEST_F(QuicIetfFramerTest, StreamBlockedFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+ QuicStreamOffset offsets[] = {0, 1, 2, 5, 10,
+ 20, 50, 100, 200, 500,
+ 1000000, kOffset8, kOffset4, kOffset2};
+ QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1,
+ kStreamId0};
+
+ for (QuicIetfStreamId stream_id : stream_ids) {
+ for (QuicStreamOffset offset : offsets) {
+ memset(packet_buffer, 0, sizeof(packet_buffer));
+
+ // Set up the writer and transmit QuicWindowUpdateFrame
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+ QuicBlockedFrame transmit_frame(0, stream_id, offset);
+
+ // Add the frame.
+ EXPECT_TRUE(QuicFramerPeer::AppendStreamBlockedFrame(
+ &framer_, transmit_frame, &writer));
+
+ // Check that number of bytes in the buffer is in the expected range.
+ EXPECT_LE(2u, writer.length());
+ EXPECT_GE(16u, writer.length());
+
+ // Set up reader and empty receive QuicPaddingFrame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicBlockedFrame receive_frame;
+
+ // Deframe it
+ EXPECT_TRUE(QuicFramerPeer::ProcessStreamBlockedFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Now check that received == sent
+ EXPECT_EQ(transmit_frame.offset, offset);
+ EXPECT_EQ(transmit_frame.offset, receive_frame.offset);
+ EXPECT_EQ(stream_id, receive_frame.stream_id);
+ EXPECT_EQ(transmit_frame.stream_id, receive_frame.stream_id);
+ }
+ }
+}
+
+TEST_F(QuicIetfFramerTest, StreamsBlockedFrame) {
+ QuicIetfStreamId stream_ids[] = {kStreamId4, kStreamId2, kStreamId1,
+ kStreamId0};
+
+ for (QuicIetfStreamId stream_id : stream_ids) {
+ TryStreamsBlockedFrame(stream_id,
+ /*unidirectional=*/false,
+ /*stream_id_server_initiated=*/false);
+ TryStreamsBlockedFrame(stream_id,
+ /*unidirectional=*/false,
+ /*stream_id_server_initiated=*/true);
+ TryStreamsBlockedFrame(stream_id,
+ /*unidirectional=*/true,
+ /*stream_id_server_initiated=*/false);
+ TryStreamsBlockedFrame(stream_id,
+ /*unidirectional=*/true,
+ /*stream_id_server_initiated=*/true);
+ }
+}
+
+TEST_F(QuicIetfFramerTest, NewConnectionIdFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+
+ QuicNewConnectionIdFrame transmit_frame;
+ transmit_frame.connection_id = TestConnectionId(UINT64_C(0x0edcba9876543201));
+ transmit_frame.sequence_number = 0x01020304;
+ // The token is defined as a uint128 -- a 16-byte integer.
+ // The value is set in this manner because we want each
+ // byte to have a specific value so that the binary
+ // packet check (below) is good. If we used integer
+ // operations (eg. "token = 0x12345...") then the bytes
+ // would be set in host order.
+ unsigned char token_bytes[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f};
+ memcpy(&transmit_frame.stateless_reset_token, token_bytes,
+ sizeof(transmit_frame.stateless_reset_token));
+
+ memset(packet_buffer, 0, sizeof(packet_buffer));
+
+ // Set up the writer and transmit QuicStreamIdBlockedFrame
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ // Add the frame.
+ EXPECT_TRUE(QuicFramerPeer::AppendNewConnectionIdFrame(
+ &framer_, transmit_frame, &writer));
+ // Check that buffer length is correct
+ EXPECT_EQ(29u, writer.length());
+ // clang-format off
+ uint8_t packet[] = {
+ // sequence number, 0x80 for varint62 encoding
+ 0x80 + 0x01, 0x02, 0x03, 0x04,
+ // new connection id length, is not varint62 encoded.
+ 0x08,
+ // new connection id, is not varint62 encoded.
+ 0x0E, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x01,
+ // the reset token:
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ // clang-format on
+ EXPECT_EQ(0, memcmp(packet_buffer, packet, sizeof(packet)));
+
+ // Set up reader and empty receive QuicPaddingFrame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicNewConnectionIdFrame receive_frame;
+
+ // Deframe it
+ EXPECT_TRUE(QuicFramerPeer::ProcessNewConnectionIdFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Now check that received == sent
+ EXPECT_EQ(transmit_frame.connection_id, receive_frame.connection_id);
+ EXPECT_EQ(transmit_frame.sequence_number, receive_frame.sequence_number);
+ EXPECT_EQ(transmit_frame.stateless_reset_token,
+ receive_frame.stateless_reset_token);
+}
+
+TEST_F(QuicIetfFramerTest, RetireConnectionIdFrame) {
+ char packet_buffer[kNormalPacketBufferSize];
+
+ QuicRetireConnectionIdFrame transmit_frame;
+ transmit_frame.sequence_number = 0x01020304;
+
+ memset(packet_buffer, 0, sizeof(packet_buffer));
+
+ // Set up the writer and transmit QuicStreamIdBlockedFrame
+ QuicDataWriter writer(sizeof(packet_buffer), packet_buffer,
+ NETWORK_BYTE_ORDER);
+
+ // Add the frame.
+ EXPECT_TRUE(QuicFramerPeer::AppendRetireConnectionIdFrame(
+ &framer_, transmit_frame, &writer));
+ // Check that buffer length is correct
+ EXPECT_EQ(4u, writer.length());
+ // clang-format off
+ uint8_t packet[] = {
+ // sequence number, 0x80 for varint62 encoding
+ 0x80 + 0x01, 0x02, 0x03, 0x04,
+ };
+
+ // clang-format on
+ EXPECT_EQ(0, memcmp(packet_buffer, packet, sizeof(packet)));
+
+ // Set up reader and empty receive QuicPaddingFrame.
+ QuicDataReader reader(packet_buffer, writer.length(), NETWORK_BYTE_ORDER);
+ QuicRetireConnectionIdFrame receive_frame;
+
+ // Deframe it
+ EXPECT_TRUE(QuicFramerPeer::ProcessRetireConnectionIdFrame(&framer_, &reader,
+ &receive_frame));
+
+ // Now check that received == sent
+ EXPECT_EQ(transmit_frame.sequence_number, receive_frame.sequence_number);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_interval.h b/chromium/net/third_party/quiche/src/quic/core/quic_interval.h
new file mode 100644
index 00000000000..c860e88e15e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_interval.h
@@ -0,0 +1,378 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_INTERVAL_H_
+#define QUICHE_QUIC_CORE_QUIC_INTERVAL_H_
+
+// An QuicInterval<T> is a data structure used to represent a contiguous,
+// mutable range over an ordered type T. Supported operations include testing a
+// value to see whether it is included in the QuicInterval, comparing two
+// QuicIntervals, and performing their union, intersection, and difference. For
+// the purposes of this library, an "ordered type" is any type that induces a
+// total order on its values via its less-than operator (operator<()). Examples
+// of such types are basic arithmetic types like int and double as well as class
+// types like string.
+//
+// An QuicInterval<T> is represented using the usual C++ STL convention, namely
+// as the half-open QuicInterval [min, max). A point p is considered to be
+// contained in the QuicInterval iff p >= min && p < max. One consequence of
+// this definition is that for any non-empty QuicInterval, min is contained in
+// the QuicInterval but max is not. There is no canonical representation for the
+// empty QuicInterval; rather, any QuicInterval where max <= min is regarded as
+// empty. As a consequence, two empty QuicIntervals will still compare as equal
+// despite possibly having different underlying min() or max() values. Also
+// beware of the terminology used here: the library uses the terms "min" and
+// "max" rather than "begin" and "end" as is conventional for the STL.
+//
+// T is required to be default- and copy-constructable, to have an assignment
+// operator, and the full complement of comparison operators (<, <=, ==, !=, >=,
+// >). A difference operator (operator-()) is required if
+// QuicInterval<T>::Length is used.
+//
+// QuicInterval supports operator==. Two QuicIntervals are considered equal if
+// either they are both empty or if their corresponding min and max fields
+// compare equal. QuicInterval also provides an operator<. Unfortunately,
+// operator< is currently buggy because its behavior is inconsistent with
+// operator==: two empty ranges with different representations may be regarded
+// as equal by operator== but regarded as different by operator<. Bug 9240050
+// has been created to address this.
+//
+//
+// Examples:
+// QuicInterval<int> r1(0, 100); // The QuicInterval [0, 100).
+// EXPECT_TRUE(r1.Contains(0));
+// EXPECT_TRUE(r1.Contains(50));
+// EXPECT_FALSE(r1.Contains(100)); // 100 is just outside the QuicInterval.
+//
+// QuicInterval<int> r2(50, 150); // The QuicInterval [50, 150).
+// EXPECT_TRUE(r1.Intersects(r2));
+// EXPECT_FALSE(r1.Contains(r2));
+// EXPECT_TRUE(r1.IntersectWith(r2)); // Mutates r1.
+// EXPECT_EQ(QuicInterval<int>(50, 100), r1); // r1 is now [50, 100).
+//
+// QuicInterval<int> r3(1000, 2000); // The QuicInterval [1000, 2000).
+// EXPECT_TRUE(r1.IntersectWith(r3)); // Mutates r1.
+// EXPECT_TRUE(r1.Empty()); // Now r1 is empty.
+// EXPECT_FALSE(r1.Contains(r1.min())); // e.g. doesn't contain its own min.
+
+#include <stddef.h>
+#include <algorithm>
+#include <ostream>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+namespace quic {
+
+template <typename T>
+class QuicInterval {
+ private:
+ // Type trait for deriving the return type for QuicInterval::Length. If
+ // operator-() is not defined for T, then the return type is void. This makes
+ // the signature for Length compile so that the class can be used for such T,
+ // but code that calls Length would still generate a compilation error.
+ template <typename U>
+ class DiffTypeOrVoid {
+ private:
+ template <typename V>
+ static auto f(const V* v) -> decltype(*v - *v);
+ template <typename V>
+ static void f(...);
+
+ public:
+ using type = typename std::decay<decltype(f<U>(nullptr))>::type;
+ };
+
+ public:
+ // Construct an QuicInterval representing an empty QuicInterval.
+ QuicInterval() : min_(), max_() {}
+
+ // Construct an QuicInterval representing the QuicInterval [min, max). If min
+ // < max, the constructed object will represent the non-empty QuicInterval
+ // containing all values from min up to (but not including) max. On the other
+ // hand, if min >= max, the constructed object will represent the empty
+ // QuicInterval.
+ QuicInterval(const T& min, const T& max) : min_(min), max_(max) {}
+
+ template <typename U1,
+ typename U2,
+ typename = typename std::enable_if<
+ std::is_convertible<U1, T>::value &&
+ std::is_convertible<U2, T>::value>::type>
+ QuicInterval(U1&& min, U2&& max)
+ : min_(std::forward<U1>(min)), max_(std::forward<U2>(max)) {}
+
+ const T& min() const { return min_; }
+ const T& max() const { return max_; }
+ void SetMin(const T& t) { min_ = t; }
+ void SetMax(const T& t) { max_ = t; }
+
+ void Set(const T& min, const T& max) {
+ SetMin(min);
+ SetMax(max);
+ }
+
+ void Clear() { *this = {}; }
+
+ bool Empty() const { return min() >= max(); }
+
+ // Returns the length of this QuicInterval. The value returned is zero if
+ // Empty() is true; otherwise the value returned is max() - min().
+ typename DiffTypeOrVoid<T>::type Length() const {
+ return (Empty() ? min() : max()) - min();
+ }
+
+ // Returns true iff t >= min() && t < max().
+ bool Contains(const T& t) const { return min() <= t && max() > t; }
+
+ // Returns true iff *this and i are non-empty, and *this includes i. "*this
+ // includes i" means that for all t, if i.Contains(t) then this->Contains(t).
+ // Note the unintuitive consequence of this definition: this method always
+ // returns false when i is the empty QuicInterval.
+ bool Contains(const QuicInterval& i) const {
+ return !Empty() && !i.Empty() && min() <= i.min() && max() >= i.max();
+ }
+
+ // Returns true iff there exists some point t for which this->Contains(t) &&
+ // i.Contains(t) evaluates to true, i.e. if the intersection is non-empty.
+ bool Intersects(const QuicInterval& i) const {
+ return !Empty() && !i.Empty() && min() < i.max() && max() > i.min();
+ }
+
+ // Returns true iff there exists some point t for which this->Contains(t) &&
+ // i.Contains(t) evaluates to true, i.e. if the intersection is non-empty.
+ // Furthermore, if the intersection is non-empty and the out pointer is not
+ // null, this method stores the calculated intersection in *out.
+ bool Intersects(const QuicInterval& i, QuicInterval* out) const;
+
+ // Sets *this to be the intersection of itself with i. Returns true iff
+ // *this was modified.
+ bool IntersectWith(const QuicInterval& i);
+
+ // Calculates the smallest QuicInterval containing both *this i, and updates
+ // *this to represent that QuicInterval, and returns true iff *this was
+ // modified.
+ bool SpanningUnion(const QuicInterval& i);
+
+ // Determines the difference between two QuicIntervals by finding all points
+ // that are contained in *this but not in i, coalesces those points into the
+ // largest possible contiguous QuicIntervals, and appends those QuicIntervals
+ // to the *difference vector. Intuitively this can be thought of as "erasing"
+ // i from *this. This will either completely erase *this (leaving nothing
+ // behind), partially erase some of *this from the left or right side (leaving
+ // some residual behind), or erase a hole in the middle of *this (leaving
+ // behind an QuicInterval on either side). Therefore, 0, 1, or 2 QuicIntervals
+ // will be appended to *difference. The method returns true iff the
+ // intersection of *this and i is non-empty. The caller owns the vector and
+ // the QuicInterval* pointers inside it. The difference vector is required to
+ // be non-null.
+ bool Difference(const QuicInterval& i,
+ std::vector<QuicInterval*>* difference) const;
+
+ // Determines the difference between two QuicIntervals as in
+ // Difference(QuicInterval&, vector*), but stores the results directly in out
+ // parameters rather than dynamically allocating an QuicInterval* and
+ // appending it to a vector. If two results are generated, the one with the
+ // smaller value of min() will be stored in *lo and the other in *hi.
+ // Otherwise (if fewer than two results are generated), unused arguments will
+ // be set to the empty QuicInterval (it is possible that *lo will be empty and
+ // *hi non-empty). The method returns true iff the intersection of *this and i
+ // is non-empty.
+ bool Difference(const QuicInterval& i,
+ QuicInterval* lo,
+ QuicInterval* hi) const;
+
+ friend bool operator==(const QuicInterval& a, const QuicInterval& b) {
+ bool ae = a.Empty();
+ bool be = b.Empty();
+ if (ae && be)
+ return true; // All empties are equal.
+ if (ae != be)
+ return false; // Empty cannot equal nonempty.
+ return a.min() == b.min() && a.max() == b.max();
+ }
+
+ friend bool operator!=(const QuicInterval& a, const QuicInterval& b) {
+ return !(a == b);
+ }
+
+ // Defines a comparator which can be used to induce an order on QuicIntervals,
+ // so that, for example, they can be stored in an ordered container such as
+ // std::set. The ordering is arbitrary, but does provide the guarantee that,
+ // for non-empty QuicIntervals X and Y, if X contains Y, then X <= Y.
+ // TODO(kosak): The current implementation of this comparator has a problem
+ // because the ordering it induces is inconsistent with that of Equals(). In
+ // particular, this comparator does not properly consider all empty
+ // QuicIntervals equivalent. Bug 9240050 has been created to track this.
+ friend bool operator<(const QuicInterval& a, const QuicInterval& b) {
+ return a.min() < b.min() || (!(b.min() < a.min()) && b.max() < a.max());
+ }
+
+ private:
+ T min_; // Inclusive lower bound.
+ T max_; // Exclusive upper bound.
+};
+
+// Constructs an QuicInterval by deducing the types from the function arguments.
+template <typename T>
+QuicInterval<T> MakeQuicInterval(T&& lhs, T&& rhs) {
+ return QuicInterval<T>(std::forward<T>(lhs), std::forward<T>(rhs));
+}
+
+// Note: ideally we'd use
+// decltype(out << "[" << i.min() << ", " << i.max() << ")")
+// as return type of the function, but as of July 2017 this triggers g++
+// "sorry, unimplemented: string literal in function template signature" error.
+template <typename T>
+auto operator<<(std::ostream& out, const QuicInterval<T>& i)
+ -> decltype(out << i.min()) {
+ return out << "[" << i.min() << ", " << i.max() << ")";
+}
+
+//==============================================================================
+// Implementation details: Clients can stop reading here.
+
+template <typename T>
+bool QuicInterval<T>::Intersects(const QuicInterval& i,
+ QuicInterval* out) const {
+ if (!Intersects(i))
+ return false;
+ if (out != nullptr) {
+ *out = QuicInterval(std::max(min(), i.min()), std::min(max(), i.max()));
+ }
+ return true;
+}
+
+template <typename T>
+bool QuicInterval<T>::IntersectWith(const QuicInterval& i) {
+ if (Empty())
+ return false;
+ bool modified = false;
+ if (i.min() > min()) {
+ SetMin(i.min());
+ modified = true;
+ }
+ if (i.max() < max()) {
+ SetMax(i.max());
+ modified = true;
+ }
+ return modified;
+}
+
+template <typename T>
+bool QuicInterval<T>::SpanningUnion(const QuicInterval& i) {
+ if (i.Empty())
+ return false;
+ if (Empty()) {
+ *this = i;
+ return true;
+ }
+ bool modified = false;
+ if (i.min() < min()) {
+ SetMin(i.min());
+ modified = true;
+ }
+ if (i.max() > max()) {
+ SetMax(i.max());
+ modified = true;
+ }
+ return modified;
+}
+
+template <typename T>
+bool QuicInterval<T>::Difference(const QuicInterval& i,
+ std::vector<QuicInterval*>* difference) const {
+ if (Empty()) {
+ // <empty> - <i> = <empty>
+ return false;
+ }
+ if (i.Empty()) {
+ // <this> - <empty> = <this>
+ difference->push_back(new QuicInterval(*this));
+ return false;
+ }
+ if (min() < i.max() && min() >= i.min() && max() > i.max()) {
+ // [------ this ------)
+ // [------ i ------)
+ // [-- result ---)
+ difference->push_back(new QuicInterval(i.max(), max()));
+ return true;
+ }
+ if (max() > i.min() && max() <= i.max() && min() < i.min()) {
+ // [------ this ------)
+ // [------ i ------)
+ // [- result -)
+ difference->push_back(new QuicInterval(min(), i.min()));
+ return true;
+ }
+ if (min() < i.min() && max() > i.max()) {
+ // [------- this --------)
+ // [---- i ----)
+ // [ R1 ) [ R2 )
+ // There are two results: R1 and R2.
+ difference->push_back(new QuicInterval(min(), i.min()));
+ difference->push_back(new QuicInterval(i.max(), max()));
+ return true;
+ }
+ if (min() >= i.min() && max() <= i.max()) {
+ // [--- this ---)
+ // [------ i --------)
+ // Intersection is <this>, so difference yields the empty QuicInterval.
+ // Nothing is appended to *difference.
+ return true;
+ }
+ // No intersection. Append <this>.
+ difference->push_back(new QuicInterval(*this));
+ return false;
+}
+
+template <typename T>
+bool QuicInterval<T>::Difference(const QuicInterval& i,
+ QuicInterval* lo,
+ QuicInterval* hi) const {
+ // Initialize *lo and *hi to empty
+ *lo = {};
+ *hi = {};
+ if (Empty())
+ return false;
+ if (i.Empty()) {
+ *lo = *this;
+ return false;
+ }
+ if (min() < i.max() && min() >= i.min() && max() > i.max()) {
+ // [------ this ------)
+ // [------ i ------)
+ // [-- result ---)
+ *hi = QuicInterval(i.max(), max());
+ return true;
+ }
+ if (max() > i.min() && max() <= i.max() && min() < i.min()) {
+ // [------ this ------)
+ // [------ i ------)
+ // [- result -)
+ *lo = QuicInterval(min(), i.min());
+ return true;
+ }
+ if (min() < i.min() && max() > i.max()) {
+ // [------- this --------)
+ // [---- i ----)
+ // [ R1 ) [ R2 )
+ // There are two results: R1 and R2.
+ *lo = QuicInterval(min(), i.min());
+ *hi = QuicInterval(i.max(), max());
+ return true;
+ }
+ if (min() >= i.min() && max() <= i.max()) {
+ // [--- this ---)
+ // [------ i --------)
+ // Intersection is <this>, so difference yields the empty QuicInterval.
+ return true;
+ }
+ *lo = *this; // No intersection.
+ return false;
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_INTERVAL_H_
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
new file mode 100644
index 00000000000..47225287120
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_interval_set.h
@@ -0,0 +1,914 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_INTERVAL_SET_H_
+#define QUICHE_QUIC_CORE_QUIC_INTERVAL_SET_H_
+
+// QuicIntervalSet<T> is a data structure used to represent a sorted set of
+// non-empty, non-adjacent, and mutually disjoint intervals. Mutations to an
+// interval set preserve these properties, altering the set as needed. For
+// example, adding [2, 3) to a set containing only [1, 2) would result in the
+// set containing the single interval [1, 3).
+//
+// Supported operations include testing whether an Interval is contained in the
+// QuicIntervalSet, comparing two QuicIntervalSets, and performing
+// QuicIntervalSet union, intersection, and difference.
+//
+// QuicIntervalSet maintains the minimum number of entries needed to represent
+// the set of underlying intervals. When the QuicIntervalSet is modified (e.g.
+// due to an Add operation), other interval entries may be coalesced, removed,
+// or otherwise modified in order to maintain this invariant. The intervals are
+// maintained in sorted order, by ascending min() value.
+//
+// The reader is cautioned to beware of the terminology used here: this library
+// uses the terms "min" and "max" rather than "begin" and "end" as is
+// conventional for the STL. The terminology [min, max) refers to the half-open
+// interval which (if the interval is not empty) contains min but does not
+// contain max. An interval is considered empty if min >= max.
+//
+// T is required to be default- and copy-constructible, to have an assignment
+// operator, a difference operator (operator-()), and the full complement of
+// comparison operators (<, <=, ==, !=, >=, >). These requirements are inherited
+// from value_type.
+//
+// QuicIntervalSet has constant-time move operations.
+//
+//
+// Examples:
+// QuicIntervalSet<int> intervals;
+// intervals.Add(Interval<int>(10, 20));
+// intervals.Add(Interval<int>(30, 40));
+// // intervals contains [10,20) and [30,40).
+// intervals.Add(Interval<int>(15, 35));
+// // intervals has been coalesced. It now contains the single range [10,40).
+// EXPECT_EQ(1, intervals.Size());
+// EXPECT_TRUE(intervals.Contains(Interval<int>(10, 40)));
+//
+// intervals.Difference(Interval<int>(10, 20));
+// // intervals should now contain the single range [20, 40).
+// EXPECT_EQ(1, intervals.Size());
+// EXPECT_TRUE(intervals.Contains(Interval<int>(20, 40)));
+
+#include <stddef.h>
+#include <algorithm>
+#include <initializer_list>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+template <typename T>
+class QuicIntervalSet {
+ public:
+ typedef QuicInterval<T> value_type;
+
+ private:
+ struct IntervalLess {
+ bool operator()(const value_type& a, const value_type& b) const;
+ };
+ typedef std::set<value_type, IntervalLess> Set;
+
+ public:
+ typedef typename Set::const_iterator const_iterator;
+ typedef typename Set::const_reverse_iterator const_reverse_iterator;
+
+ // Instantiates an empty QuicIntervalSet.
+ QuicIntervalSet() {}
+
+ // Instantiates an QuicIntervalSet containing exactly one initial half-open
+ // interval [min, max), unless the given interval is empty, in which case the
+ // QuicIntervalSet will be empty.
+ explicit QuicIntervalSet(const value_type& interval) { Add(interval); }
+
+ // Instantiates an QuicIntervalSet containing the half-open interval [min,
+ // max).
+ QuicIntervalSet(const T& min, const T& max) { Add(min, max); }
+
+ QuicIntervalSet(std::initializer_list<value_type> il) { assign(il); }
+
+ // Clears this QuicIntervalSet.
+ void Clear() { intervals_.clear(); }
+
+ // Returns the number of disjoint intervals contained in this QuicIntervalSet.
+ size_t Size() const { return intervals_.size(); }
+
+ // Returns the smallest interval that contains all intervals in this
+ // QuicIntervalSet, or the empty interval if the set is empty.
+ value_type SpanningInterval() const;
+
+ // Adds "interval" to this QuicIntervalSet. Adding the empty interval has no
+ // effect.
+ void Add(const value_type& interval);
+
+ // Adds the interval [min, max) to this QuicIntervalSet. Adding the empty
+ // interval has no effect.
+ void Add(const T& min, const T& max) { Add(value_type(min, max)); }
+
+ // Same semantics as Add(const value_type&), but optimized for the case where
+ // rbegin()->min() <= |interval|.min() <= rbegin()->max().
+ void AddOptimizedForAppend(const value_type& interval) {
+ if (Empty()) {
+ Add(interval);
+ return;
+ }
+
+ const_reverse_iterator last_interval = intervals_.rbegin();
+
+ // If interval.min() is outside of [last_interval->min, last_interval->max],
+ // we can not simply extend last_interval->max.
+ if (interval.min() < last_interval->min() ||
+ interval.min() > last_interval->max()) {
+ Add(interval);
+ return;
+ }
+
+ if (interval.max() <= last_interval->max()) {
+ // interval is fully contained by last_interval.
+ return;
+ }
+
+ // Extend last_interval.max to interval.max, in place.
+ //
+ // Set does not allow in-place updates due to the potential of violating its
+ // ordering requirements. But we know setting the max of the last interval
+ // is safe w.r.t set ordering and other invariants of QuicIntervalSet, so we
+ // force an in-place update for performance.
+ const_cast<value_type*>(&(*last_interval))->SetMax(interval.max());
+ }
+
+ // Same semantics as Add(const T&, const T&), but optimized for the case where
+ // rbegin()->max() == |min|.
+ void AddOptimizedForAppend(const T& min, const T& max) {
+ AddOptimizedForAppend(value_type(min, max));
+ }
+
+ // TODO(wub): Similar to AddOptimizedForAppend, we can also have a
+ // AddOptimizedForPrepend if there is a use case.
+
+ // Returns true if this QuicIntervalSet is empty.
+ bool Empty() const { return intervals_.empty(); }
+
+ // Returns true if any interval in this QuicIntervalSet contains the indicated
+ // value.
+ bool Contains(const T& value) const;
+
+ // Returns true if there is some interval in this QuicIntervalSet that wholly
+ // contains the given interval. An interval O "wholly contains" a non-empty
+ // interval I if O.Contains(p) is true for every p in I. This is the same
+ // definition used by value_type::Contains(). This method returns false on
+ // the empty interval, due to a (perhaps unintuitive) convention inherited
+ // from value_type.
+ // Example:
+ // Assume an QuicIntervalSet containing the entries { [10,20), [30,40) }.
+ // Contains(Interval(15, 16)) returns true, because [10,20) contains
+ // [15,16). However, Contains(Interval(15, 35)) returns false.
+ bool Contains(const value_type& interval) const;
+
+ // Returns true if for each interval in "other", there is some (possibly
+ // different) interval in this QuicIntervalSet which wholly contains it. See
+ // Contains(const value_type& interval) for the meaning of "wholly contains".
+ // Perhaps unintuitively, this method returns false if "other" is the empty
+ // set. The algorithmic complexity of this method is O(other.Size() *
+ // log(this->Size())). The method could be rewritten to run in O(other.Size()
+ // + this->Size()), and this alternative could be implemented as a free
+ // function using the public API.
+ bool Contains(const QuicIntervalSet<T>& other) const;
+
+ // Returns true if there is some interval in this QuicIntervalSet that wholly
+ // contains the interval [min, max). See Contains(const value_type&).
+ bool Contains(const T& min, const T& max) const {
+ return Contains(value_type(min, max));
+ }
+
+ // Returns true if for some interval in "other", there is some interval in
+ // this QuicIntervalSet that intersects with it. See value_type::Intersects()
+ // for the definition of interval intersection.
+ bool Intersects(const QuicIntervalSet& other) const;
+
+ // Returns an iterator to the value_type in the QuicIntervalSet that contains
+ // the given value. In other words, returns an iterator to the unique interval
+ // [min, max) in the QuicIntervalSet that has the property min <= value < max.
+ // If there is no such interval, this method returns end().
+ const_iterator Find(const T& value) const;
+
+ // Returns an iterator to the value_type in the QuicIntervalSet that wholly
+ // contains the given interval. In other words, returns an iterator to the
+ // unique interval outer in the QuicIntervalSet that has the property that
+ // outer.Contains(interval). If there is no such interval, or if interval is
+ // empty, returns end().
+ const_iterator Find(const value_type& interval) const;
+
+ // Returns an iterator to the value_type in the QuicIntervalSet that wholly
+ // contains [min, max). In other words, returns an iterator to the unique
+ // interval outer in the QuicIntervalSet that has the property that
+ // outer.Contains(Interval<T>(min, max)). If there is no such interval, or if
+ // interval is empty, returns end().
+ const_iterator Find(const T& min, const T& max) const {
+ return Find(value_type(min, max));
+ }
+
+ // Returns an iterator pointing to the first value_type which contains or
+ // goes after the given value.
+ //
+ // Example:
+ // [10, 20) [30, 40)
+ // ^ LowerBound(10)
+ // ^ LowerBound(15)
+ // ^ LowerBound(20)
+ // ^ LowerBound(25)
+ const_iterator LowerBound(const T& value) const;
+
+ // Returns an iterator pointing to the first value_type which goes after
+ // the given value.
+ //
+ // Example:
+ // [10, 20) [30, 40)
+ // ^ UpperBound(10)
+ // ^ UpperBound(15)
+ // ^ UpperBound(20)
+ // ^ UpperBound(25)
+ const_iterator UpperBound(const T& value) const;
+
+ // Returns true if every value within the passed interval is not Contained
+ // within the QuicIntervalSet.
+ // Note that empty intervals are always considered disjoint from the
+ // QuicIntervalSet (even though the QuicIntervalSet doesn't `Contain` them).
+ bool IsDisjoint(const value_type& interval) const;
+
+ // Merges all the values contained in "other" into this QuicIntervalSet.
+ void Union(const QuicIntervalSet& other);
+
+ // Modifies this QuicIntervalSet so that it contains only those values that
+ // are currently present both in *this and in the QuicIntervalSet "other".
+ void Intersection(const QuicIntervalSet& other);
+
+ // Mutates this QuicIntervalSet so that it contains only those values that are
+ // currently in *this but not in "interval".
+ void Difference(const value_type& interval);
+
+ // Mutates this QuicIntervalSet so that it contains only those values that are
+ // currently in *this but not in the interval [min, max).
+ void Difference(const T& min, const T& max);
+
+ // Mutates this QuicIntervalSet so that it contains only those values that are
+ // currently in *this but not in the QuicIntervalSet "other".
+ void Difference(const QuicIntervalSet& other);
+
+ // Mutates this QuicIntervalSet so that it contains only those values that are
+ // in [min, max) but not currently in *this.
+ void Complement(const T& min, const T& max);
+
+ // QuicIntervalSet's begin() iterator. The invariants of QuicIntervalSet
+ // guarantee that for each entry e in the set, e.min() < e.max() (because the
+ // entries are non-empty) and for each entry f that appears later in the set,
+ // e.max() < f.min() (because the entries are ordered, pairwise-disjoint, and
+ // non-adjacent). Modifications to this QuicIntervalSet invalidate these
+ // iterators.
+ const_iterator begin() const { return intervals_.begin(); }
+
+ // QuicIntervalSet's end() iterator.
+ const_iterator end() const { return intervals_.end(); }
+
+ // QuicIntervalSet's rbegin() and rend() iterators. Iterator invalidation
+ // semantics are the same as those for begin() / end().
+ const_reverse_iterator rbegin() const { return intervals_.rbegin(); }
+
+ const_reverse_iterator rend() const { return intervals_.rend(); }
+
+ template <typename Iter>
+ void assign(Iter first, Iter last) {
+ Clear();
+ for (; first != last; ++first)
+ Add(*first);
+ }
+
+ void assign(std::initializer_list<value_type> il) {
+ assign(il.begin(), il.end());
+ }
+
+ // Returns a human-readable representation of this set. This will typically be
+ // (though is not guaranteed to be) of the form
+ // "[a1, b1) [a2, b2) ... [an, bn)"
+ // where the intervals are in the same order as given by traversal from
+ // begin() to end(). This representation is intended for human consumption;
+ // computer programs should not rely on the output being in exactly this form.
+ std::string ToString() const;
+
+ QuicIntervalSet& operator=(std::initializer_list<value_type> il) {
+ assign(il.begin(), il.end());
+ return *this;
+ }
+
+ // Swap this QuicIntervalSet with *other. This is a constant-time operation.
+ void Swap(QuicIntervalSet<T>* other) { intervals_.swap(other->intervals_); }
+
+ friend bool operator==(const QuicIntervalSet& a, const QuicIntervalSet& b) {
+ return a.Size() == b.Size() &&
+ std::equal(a.begin(), a.end(), b.begin(), NonemptyIntervalEq());
+ }
+
+ friend bool operator!=(const QuicIntervalSet& a, const QuicIntervalSet& b) {
+ return !(a == b);
+ }
+
+ private:
+ // Simple member-wise equality, since all intervals are non-empty.
+ struct NonemptyIntervalEq {
+ bool operator()(const value_type& a, const value_type& b) const {
+ return a.min() == b.min() && a.max() == b.max();
+ }
+ };
+
+ // Removes overlapping ranges and coalesces adjacent intervals as needed.
+ void Compact(const typename Set::iterator& begin,
+ const typename Set::iterator& end);
+
+ // Returns true if this set is valid (i.e. all intervals in it are non-empty,
+ // non-adjacent, and mutually disjoint). Currently this is used as an
+ // integrity check by the Intersection() and Difference() methods, but is only
+ // invoked for debug builds (via DCHECK).
+ bool Valid() const;
+
+ // Finds the first interval that potentially intersects 'other'.
+ const_iterator FindIntersectionCandidate(const QuicIntervalSet& other) const;
+
+ // Finds the first interval that potentially intersects 'interval'.
+ const_iterator FindIntersectionCandidate(const value_type& interval) const;
+
+ // Helper for Intersection() and Difference(): Finds the next pair of
+ // intervals from 'x' and 'y' that intersect. 'mine' is an iterator
+ // over x->intervals_. 'theirs' is an iterator over y.intervals_. 'mine'
+ // and 'theirs' are advanced until an intersecting pair is found.
+ // Non-intersecting intervals (aka "holes") from x->intervals_ can be
+ // optionally erased by "on_hole".
+ template <typename X, typename Func>
+ static bool FindNextIntersectingPairImpl(X* x,
+ const QuicIntervalSet& y,
+ const_iterator* mine,
+ const_iterator* theirs,
+ Func on_hole);
+
+ // The variant of the above method that doesn't mutate this QuicIntervalSet.
+ bool FindNextIntersectingPair(const QuicIntervalSet& other,
+ const_iterator* mine,
+ const_iterator* theirs) const {
+ return FindNextIntersectingPairImpl(
+ this, other, mine, theirs,
+ [](const QuicIntervalSet*, const_iterator, const_iterator) {});
+ }
+
+ // The variant of the above method that mutates this QuicIntervalSet by
+ // erasing holes.
+ bool FindNextIntersectingPairAndEraseHoles(const QuicIntervalSet& other,
+ const_iterator* mine,
+ const_iterator* theirs) {
+ return FindNextIntersectingPairImpl(
+ this, other, mine, theirs,
+ [](QuicIntervalSet* x, const_iterator from, const_iterator to) {
+ x->intervals_.erase(from, to);
+ });
+ }
+
+ // The representation for the intervals. The intervals in this set are
+ // non-empty, pairwise-disjoint, non-adjacent and ordered in ascending order
+ // by min().
+ Set intervals_;
+};
+
+template <typename T>
+auto operator<<(std::ostream& out, const QuicIntervalSet<T>& seq)
+ -> decltype(out << *seq.begin()) {
+ out << "{";
+ for (const auto& interval : seq) {
+ out << " " << interval;
+ }
+ out << " }";
+
+ return out;
+}
+
+template <typename T>
+void swap(QuicIntervalSet<T>& x, QuicIntervalSet<T>& y);
+
+//==============================================================================
+// Implementation details: Clients can stop reading here.
+
+template <typename T>
+typename QuicIntervalSet<T>::value_type QuicIntervalSet<T>::SpanningInterval()
+ const {
+ value_type result;
+ if (!intervals_.empty()) {
+ result.SetMin(intervals_.begin()->min());
+ result.SetMax(intervals_.rbegin()->max());
+ }
+ return result;
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Add(const value_type& interval) {
+ if (interval.Empty())
+ return;
+ std::pair<typename Set::iterator, bool> ins = intervals_.insert(interval);
+ if (!ins.second) {
+ // This interval already exists.
+ return;
+ }
+ // Determine the minimal range that will have to be compacted. We know that
+ // the QuicIntervalSet was valid before the addition of the interval, so only
+ // need to start with the interval itself (although Compact takes an open
+ // range so begin needs to be the interval to the left). We don't know how
+ // many ranges this interval may cover, so we need to find the appropriate
+ // interval to end with on the right.
+ typename Set::iterator begin = ins.first;
+ if (begin != intervals_.begin())
+ --begin;
+ const value_type target_end(interval.max(), interval.max());
+ const typename Set::iterator end = intervals_.upper_bound(target_end);
+ Compact(begin, end);
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Contains(const T& value) const {
+ value_type tmp(value, value);
+ // Find the first interval with min() > value, then move back one step
+ const_iterator it = intervals_.upper_bound(tmp);
+ if (it == intervals_.begin())
+ return false;
+ --it;
+ return it->Contains(value);
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Contains(const value_type& interval) const {
+ // Find the first interval with min() > value, then move back one step.
+ const_iterator it = intervals_.upper_bound(interval);
+ if (it == intervals_.begin())
+ return false;
+ --it;
+ return it->Contains(interval);
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Contains(const QuicIntervalSet<T>& other) const {
+ if (!SpanningInterval().Contains(other.SpanningInterval())) {
+ return false;
+ }
+
+ for (const_iterator i = other.begin(); i != other.end(); ++i) {
+ // If we don't contain the interval, can return false now.
+ if (!Contains(*i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// This method finds the interval that Contains() "value", if such an interval
+// exists in the QuicIntervalSet. The way this is done is to locate the
+// "candidate interval", the only interval that could *possibly* contain value,
+// and test it using Contains(). The candidate interval is the interval with the
+// largest min() having min() <= value.
+//
+// Determining the candidate interval takes a couple of steps. First, since the
+// underlying std::set stores intervals, not values, we need to create a "probe
+// interval" suitable for use as a search key. The probe interval used is
+// [value, value). Now we can restate the problem as finding the largest
+// interval in the QuicIntervalSet that is <= the probe interval.
+//
+// This restatement only works if the set's comparator behaves in a certain way.
+// In particular it needs to order first by ascending min(), and then by
+// descending max(). The comparator used by this library is defined in exactly
+// this way. To see why descending max() is required, consider the following
+// example. Assume an QuicIntervalSet containing these intervals:
+//
+// [0, 5) [10, 20) [50, 60)
+//
+// Consider searching for the value 15. The probe interval [15, 15) is created,
+// and [10, 20) is identified as the largest interval in the set <= the probe
+// interval. This is the correct interval needed for the Contains() test, which
+// will then return true.
+//
+// Now consider searching for the value 30. The probe interval [30, 30) is
+// created, and again [10, 20] is identified as the largest interval <= the
+// probe interval. This is again the correct interval needed for the Contains()
+// test, which in this case returns false.
+//
+// Finally, consider searching for the value 10. The probe interval [10, 10) is
+// created. Here the ordering relationship between [10, 10) and [10, 20) becomes
+// vitally important. If [10, 10) were to come before [10, 20), then [0, 5)
+// would be the largest interval <= the probe, leading to the wrong choice of
+// interval for the Contains() test. Therefore [10, 10) needs to come after
+// [10, 20). The simplest way to make this work in the general case is to order
+// by ascending min() but descending max(). In this ordering, the empty interval
+// is larger than any non-empty interval with the same min(). The comparator
+// used by this library is careful to induce this ordering.
+//
+// Another detail involves the choice of which std::set method to use to try to
+// find the candidate interval. The most appropriate entry point is
+// set::upper_bound(), which finds the smallest interval which is > the probe
+// interval. The semantics of upper_bound() are slightly different from what we
+// want (namely, to find the largest interval which is <= the probe interval)
+// but they are close enough; the interval found by upper_bound() will always be
+// one step past the interval we are looking for (if it exists) or at begin()
+// (if it does not). Getting to the proper interval is a simple matter of
+// decrementing the iterator.
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::Find(
+ const T& value) const {
+ value_type tmp(value, value);
+ const_iterator it = intervals_.upper_bound(tmp);
+ if (it == intervals_.begin())
+ return intervals_.end();
+ --it;
+ if (it->Contains(value))
+ return it;
+ else
+ return intervals_.end();
+}
+
+// This method finds the interval that Contains() the interval "probe", if such
+// an interval exists in the QuicIntervalSet. The way this is done is to locate
+// the "candidate interval", the only interval that could *possibly* contain
+// "probe", and test it using Contains(). The candidate interval is the largest
+// interval that is <= the probe interval.
+//
+// The search for the candidate interval only works if the comparator used
+// behaves in a certain way. In particular it needs to order first by ascending
+// min(), and then by descending max(). The comparator used by this library is
+// defined in exactly this way. To see why descending max() is required,
+// consider the following example. Assume an QuicIntervalSet containing these
+// intervals:
+//
+// [0, 5) [10, 20) [50, 60)
+//
+// Consider searching for the probe [15, 17). [10, 20) is the largest interval
+// in the set which is <= the probe interval. This is the correct interval
+// needed for the Contains() test, which will then return true, because [10, 20)
+// contains [15, 17).
+//
+// Now consider searching for the probe [30, 32). Again [10, 20] is the largest
+// interval <= the probe interval. This is again the correct interval needed for
+// the Contains() test, which in this case returns false, because [10, 20) does
+// not contain [30, 32).
+//
+// Finally, consider searching for the probe [10, 12). Here the ordering
+// relationship between [10, 12) and [10, 20) becomes vitally important. If
+// [10, 12) were to come before [10, 20), then [0, 5) would be the largest
+// interval <= the probe, leading to the wrong choice of interval for the
+// Contains() test. Therefore [10, 12) needs to come after [10, 20). The
+// simplest way to make this work in the general case is to order by ascending
+// min() but descending max(). In this ordering, given two intervals with the
+// same min(), the wider one goes before the narrower one. The comparator used
+// by this library is careful to induce this ordering.
+//
+// Another detail involves the choice of which std::set method to use to try to
+// find the candidate interval. The most appropriate entry point is
+// set::upper_bound(), which finds the smallest interval which is > the probe
+// interval. The semantics of upper_bound() are slightly different from what we
+// want (namely, to find the largest interval which is <= the probe interval)
+// but they are close enough; the interval found by upper_bound() will always be
+// one step past the interval we are looking for (if it exists) or at begin()
+// (if it does not). Getting to the proper interval is a simple matter of
+// decrementing the iterator.
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::Find(
+ const value_type& probe) const {
+ const_iterator it = intervals_.upper_bound(probe);
+ if (it == intervals_.begin())
+ return intervals_.end();
+ --it;
+ if (it->Contains(probe))
+ return it;
+ else
+ return intervals_.end();
+}
+
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::LowerBound(
+ const T& value) const {
+ const_iterator it = intervals_.lower_bound(value_type(value, value));
+ if (it == intervals_.begin()) {
+ return it;
+ }
+
+ // The previous intervals_.lower_bound() checking is essentially based on
+ // interval.min(), so we need to check whether the `value` is contained in
+ // the previous interval.
+ --it;
+ if (it->Contains(value)) {
+ return it;
+ } else {
+ return ++it;
+ }
+}
+
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator QuicIntervalSet<T>::UpperBound(
+ const T& value) const {
+ return intervals_.upper_bound(value_type(value, value));
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::IsDisjoint(const value_type& interval) const {
+ if (interval.Empty())
+ return true;
+ value_type tmp(interval.min(), interval.min());
+ // Find the first interval with min() > interval.min()
+ const_iterator it = intervals_.upper_bound(tmp);
+ if (it != intervals_.end() && interval.max() > it->min())
+ return false;
+ if (it == intervals_.begin())
+ return true;
+ --it;
+ return it->max() <= interval.min();
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Union(const QuicIntervalSet& other) {
+ intervals_.insert(other.begin(), other.end());
+ Compact(intervals_.begin(), intervals_.end());
+}
+
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator
+QuicIntervalSet<T>::FindIntersectionCandidate(
+ const QuicIntervalSet& other) const {
+ return FindIntersectionCandidate(*other.intervals_.begin());
+}
+
+template <typename T>
+typename QuicIntervalSet<T>::const_iterator
+QuicIntervalSet<T>::FindIntersectionCandidate(
+ const value_type& interval) const {
+ // Use upper_bound to efficiently find the first interval in intervals_
+ // where min() is greater than interval.min(). If the result
+ // isn't the beginning of intervals_ then move backwards one interval since
+ // the interval before it is the first candidate where max() may be
+ // greater than interval.min().
+ // In other words, no interval before that can possibly intersect with any
+ // of other.intervals_.
+ const_iterator mine = intervals_.upper_bound(interval);
+ if (mine != intervals_.begin()) {
+ --mine;
+ }
+ return mine;
+}
+
+template <typename T>
+template <typename X, typename Func>
+bool QuicIntervalSet<T>::FindNextIntersectingPairImpl(X* x,
+ const QuicIntervalSet& y,
+ const_iterator* mine,
+ const_iterator* theirs,
+ Func on_hole) {
+ CHECK(x != nullptr);
+ if ((*mine == x->intervals_.end()) || (*theirs == y.intervals_.end())) {
+ return false;
+ }
+ while (!(**mine).Intersects(**theirs)) {
+ const_iterator erase_first = *mine;
+ // Skip over intervals in 'mine' that don't reach 'theirs'.
+ while (*mine != x->intervals_.end() && (**mine).max() <= (**theirs).min()) {
+ ++(*mine);
+ }
+ on_hole(x, erase_first, *mine);
+ // We're done if the end of intervals_ is reached.
+ if (*mine == x->intervals_.end()) {
+ return false;
+ }
+ // Skip over intervals 'theirs' that don't reach 'mine'.
+ while (*theirs != y.intervals_.end() &&
+ (**theirs).max() <= (**mine).min()) {
+ ++(*theirs);
+ }
+ // If the end of other.intervals_ is reached, we're done.
+ if (*theirs == y.intervals_.end()) {
+ on_hole(x, *mine, x->intervals_.end());
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Intersection(const QuicIntervalSet& other) {
+ if (!SpanningInterval().Intersects(other.SpanningInterval())) {
+ intervals_.clear();
+ return;
+ }
+
+ const_iterator mine = FindIntersectionCandidate(other);
+ // Remove any intervals that cannot possibly intersect with other.intervals_.
+ intervals_.erase(intervals_.begin(), mine);
+ const_iterator theirs = other.FindIntersectionCandidate(*this);
+
+ while (FindNextIntersectingPairAndEraseHoles(other, &mine, &theirs)) {
+ // OK, *mine and *theirs intersect. Now, we find the largest
+ // span of intervals in other (starting at theirs) - say [a..b]
+ // - that intersect *mine, and we replace *mine with (*mine
+ // intersect x) for all x in [a..b] Note that subsequent
+ // intervals in this can't intersect any intervals in [a..b) --
+ // they may only intersect b or subsequent intervals in other.
+ value_type i(*mine);
+ intervals_.erase(mine);
+ mine = intervals_.end();
+ value_type intersection;
+ while (theirs != other.intervals_.end() &&
+ i.Intersects(*theirs, &intersection)) {
+ std::pair<typename Set::iterator, bool> ins =
+ intervals_.insert(intersection);
+ DCHECK(ins.second);
+ mine = ins.first;
+ ++theirs;
+ }
+ DCHECK(mine != intervals_.end());
+ --theirs;
+ ++mine;
+ }
+ DCHECK(Valid());
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Intersects(const QuicIntervalSet& other) const {
+ if (!SpanningInterval().Intersects(other.SpanningInterval())) {
+ return false;
+ }
+
+ const_iterator mine = FindIntersectionCandidate(other);
+ if (mine == intervals_.end()) {
+ return false;
+ }
+ const_iterator theirs = other.FindIntersectionCandidate(*mine);
+
+ return FindNextIntersectingPair(other, &mine, &theirs);
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Difference(const value_type& interval) {
+ if (!SpanningInterval().Intersects(interval)) {
+ return;
+ }
+ Difference(QuicIntervalSet<T>(interval));
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Difference(const T& min, const T& max) {
+ Difference(value_type(min, max));
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Difference(const QuicIntervalSet& other) {
+ if (!SpanningInterval().Intersects(other.SpanningInterval())) {
+ return;
+ }
+
+ const_iterator mine = FindIntersectionCandidate(other);
+ // If no interval in mine reaches the first interval of theirs then we're
+ // done.
+ if (mine == intervals_.end()) {
+ return;
+ }
+ const_iterator theirs = other.FindIntersectionCandidate(*this);
+
+ while (FindNextIntersectingPair(other, &mine, &theirs)) {
+ // At this point *mine and *theirs overlap. Remove mine from
+ // intervals_ and replace it with the possibly two intervals that are
+ // the difference between mine and theirs.
+ value_type i(*mine);
+ intervals_.erase(mine++);
+ value_type lo;
+ value_type hi;
+ i.Difference(*theirs, &lo, &hi);
+
+ if (!lo.Empty()) {
+ // We have a low end. This can't intersect anything else.
+ std::pair<typename Set::iterator, bool> ins = intervals_.insert(lo);
+ DCHECK(ins.second);
+ }
+
+ if (!hi.Empty()) {
+ std::pair<typename Set::iterator, bool> ins = intervals_.insert(hi);
+ DCHECK(ins.second);
+ mine = ins.first;
+ }
+ }
+ DCHECK(Valid());
+}
+
+template <typename T>
+void QuicIntervalSet<T>::Complement(const T& min, const T& max) {
+ QuicIntervalSet<T> span(min, max);
+ span.Difference(*this);
+ intervals_.swap(span.intervals_);
+}
+
+template <typename T>
+std::string QuicIntervalSet<T>::ToString() const {
+ std::ostringstream os;
+ os << *this;
+ return os.str();
+}
+
+// This method compacts the QuicIntervalSet, merging pairs of overlapping
+// intervals into a single interval. In the steady state, the QuicIntervalSet
+// does not contain any such pairs. However, the way the Union() and Add()
+// methods work is to temporarily put the QuicIntervalSet into such a state and
+// then to call Compact() to "fix it up" so that it is no longer in that state.
+//
+// Compact() needs the interval set to allow two intervals [a,b) and [a,c)
+// (having the same min() but different max()) to briefly coexist in the set at
+// the same time, and be adjacent to each other, so that they can be efficiently
+// located and merged into a single interval. This state would be impossible
+// with a comparator which only looked at min(), as such a comparator would
+// consider such pairs equal. Fortunately, the comparator used by
+// QuicIntervalSet does exactly what is needed, ordering first by ascending
+// min(), then by descending max().
+template <typename T>
+void QuicIntervalSet<T>::Compact(const typename Set::iterator& begin,
+ const typename Set::iterator& end) {
+ if (begin == end)
+ return;
+ typename Set::iterator next = begin;
+ typename Set::iterator prev = begin;
+ typename Set::iterator it = begin;
+ ++it;
+ ++next;
+ while (it != end) {
+ ++next;
+ if (prev->max() >= it->min()) {
+ // Overlapping / coalesced range; merge the two intervals.
+ T min = prev->min();
+ T max = std::max(prev->max(), it->max());
+ value_type i(min, max);
+ intervals_.erase(prev);
+ intervals_.erase(it);
+ std::pair<typename Set::iterator, bool> ins = intervals_.insert(i);
+ DCHECK(ins.second);
+ prev = ins.first;
+ } else {
+ prev = it;
+ }
+ it = next;
+ }
+}
+
+template <typename T>
+bool QuicIntervalSet<T>::Valid() const {
+ const_iterator prev = end();
+ for (const_iterator it = begin(); it != end(); ++it) {
+ // invalid or empty interval.
+ if (it->min() >= it->max())
+ return false;
+ // Not sorted, not disjoint, or adjacent.
+ if (prev != end() && prev->max() >= it->min())
+ return false;
+ prev = it;
+ }
+ return true;
+}
+
+template <typename T>
+void swap(QuicIntervalSet<T>& x, QuicIntervalSet<T>& y) {
+ x.Swap(&y);
+}
+
+// This comparator orders intervals first by ascending min() and then by
+// descending max(). Readers who are satisified with that explanation can stop
+// reading here. The remainder of this comment is for the benefit of future
+// maintainers of this library.
+//
+// The reason for this ordering is that this comparator has to serve two
+// masters. First, it has to maintain the intervals in its internal set in the
+// order that clients expect to see them. Clients see these intervals via the
+// iterators provided by begin()/end() or as a result of invoking Get(). For
+// this reason, the comparator orders intervals by ascending min().
+//
+// If client iteration were the only consideration, then ordering by ascending
+// min() would be good enough. This is because the intervals in the
+// QuicIntervalSet are non-empty, non-adjacent, and mutually disjoint; such
+// intervals happen to always have disjoint min() values, so such a comparator
+// would never even have to look at max() in order to work correctly for this
+// class.
+//
+// However, in addition to ordering by ascending min(), this comparator also has
+// a second responsibility: satisfying the special needs of this library's
+// peculiar internal implementation. These needs require the comparator to order
+// first by ascending min() and then by descending max(). The best way to
+// understand why this is so is to check out the comments associated with the
+// Find() and Compact() methods.
+template <typename T>
+bool QuicIntervalSet<T>::IntervalLess::operator()(const value_type& a,
+ const value_type& b) const {
+ return a.min() < b.min() || (a.min() == b.min() && a.max() > b.max());
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_INTERVAL_SET_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_interval_set_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_interval_set_test.cc
new file mode 100644
index 00000000000..3fb483aa82a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_interval_set_test.cc
@@ -0,0 +1,1010 @@
+// 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 "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+
+#include <stdarg.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using ::testing::ElementsAreArray;
+
+class QuicIntervalSetTest : public QuicTest {
+ protected:
+ virtual void SetUp() {
+ // Initialize two QuicIntervalSets for union, intersection, and difference
+ // tests
+ is.Add(100, 200);
+ is.Add(300, 400);
+ is.Add(500, 600);
+ is.Add(700, 800);
+ is.Add(900, 1000);
+ is.Add(1100, 1200);
+ is.Add(1300, 1400);
+ is.Add(1500, 1600);
+ is.Add(1700, 1800);
+ is.Add(1900, 2000);
+ is.Add(2100, 2200);
+
+ // Lots of different cases:
+ other.Add(50, 70); // disjoint, at the beginning
+ other.Add(2250, 2270); // disjoint, at the end
+ other.Add(650, 670); // disjoint, in the middle
+ other.Add(350, 360); // included
+ other.Add(370, 380); // also included (two at once)
+ other.Add(470, 530); // overlaps low end
+ other.Add(770, 830); // overlaps high end
+ other.Add(870, 900); // meets at low end
+ other.Add(1200, 1230); // meets at high end
+ other.Add(1270, 1830); // overlaps multiple ranges
+ }
+
+ virtual void TearDown() {
+ is.Clear();
+ EXPECT_TRUE(is.Empty());
+ other.Clear();
+ EXPECT_TRUE(other.Empty());
+ }
+ QuicIntervalSet<int> is;
+ QuicIntervalSet<int> other;
+};
+
+TEST_F(QuicIntervalSetTest, IsDisjoint) {
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(0, 99)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(0, 100)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 200)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 299)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(400, 407)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(405, 499)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(2300, 2300)));
+ EXPECT_TRUE(
+ is.IsDisjoint(QuicInterval<int>(2300, std::numeric_limits<int>::max())));
+ EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(100, 105)));
+ EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(199, 300)));
+ EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(250, 450)));
+ EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(299, 400)));
+ EXPECT_FALSE(is.IsDisjoint(QuicInterval<int>(250, 2000)));
+ EXPECT_FALSE(
+ is.IsDisjoint(QuicInterval<int>(2199, std::numeric_limits<int>::max())));
+ // Empty intervals.
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(90, 90)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(100, 100)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(100, 90)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(150, 150)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(200, 200)));
+ EXPECT_TRUE(is.IsDisjoint(QuicInterval<int>(400, 300)));
+}
+
+// Base helper method for verifying the contents of an interval set.
+// Returns true iff <is> contains <count> intervals whose successive
+// endpoints match the sequence of args in <ap>:
+static bool VA_Check(const QuicIntervalSet<int>& is, int count, va_list ap) {
+ std::vector<QuicInterval<int>> intervals(is.begin(), is.end());
+ if (count != static_cast<int>(intervals.size())) {
+ QUIC_LOG(ERROR) << "Expected " << count << " intervals, got "
+ << intervals.size() << ": " << is;
+ return false;
+ }
+ if (count != static_cast<int>(is.Size())) {
+ QUIC_LOG(ERROR) << "Expected " << count << " intervals, got Size "
+ << is.Size() << ": " << is;
+ return false;
+ }
+ bool result = true;
+ for (int i = 0; i < count; i++) {
+ int min = va_arg(ap, int);
+ int max = va_arg(ap, int);
+ if (min != intervals[i].min() || max != intervals[i].max()) {
+ QUIC_LOG(ERROR) << "Expected: [" << min << ", " << max << ") got "
+ << intervals[i] << " in " << is;
+ result = false;
+ }
+ }
+ return result;
+}
+
+static bool Check(const QuicIntervalSet<int>& is, int count, ...) {
+ va_list ap;
+ va_start(ap, count);
+ const bool result = VA_Check(is, count, ap);
+ va_end(ap);
+ return result;
+}
+
+// Some helper functions for testing Contains and Find, which are logically the
+// same.
+static void TestContainsAndFind(const QuicIntervalSet<int>& is, int value) {
+ EXPECT_TRUE(is.Contains(value)) << "Set does not contain " << value;
+ auto it = is.Find(value);
+ EXPECT_NE(it, is.end()) << "No iterator to interval containing " << value;
+ EXPECT_TRUE(it->Contains(value)) << "Iterator does not contain " << value;
+}
+
+static void TestContainsAndFind(const QuicIntervalSet<int>& is,
+ int min,
+ int max) {
+ EXPECT_TRUE(is.Contains(min, max))
+ << "Set does not contain interval with min " << min << "and max " << max;
+ auto it = is.Find(min, max);
+ EXPECT_NE(it, is.end()) << "No iterator to interval with min " << min
+ << "and max " << max;
+ EXPECT_TRUE(it->Contains(QuicInterval<int>(min, max)))
+ << "Iterator does not contain interval with min " << min << "and max "
+ << max;
+}
+
+static void TestNotContainsAndFind(const QuicIntervalSet<int>& is, int value) {
+ EXPECT_FALSE(is.Contains(value)) << "Set contains " << value;
+ auto it = is.Find(value);
+ EXPECT_EQ(it, is.end()) << "There is iterator to interval containing "
+ << value;
+}
+
+static void TestNotContainsAndFind(const QuicIntervalSet<int>& is,
+ int min,
+ int max) {
+ EXPECT_FALSE(is.Contains(min, max))
+ << "Set contains interval with min " << min << "and max " << max;
+ auto it = is.Find(min, max);
+ EXPECT_EQ(it, is.end()) << "There is iterator to interval with min " << min
+ << "and max " << max;
+}
+
+TEST_F(QuicIntervalSetTest, AddOptimizedForAppend) {
+ QuicIntervalSet<int> empty_one, empty_two;
+ empty_one.AddOptimizedForAppend(QuicInterval<int>(0, 99));
+ EXPECT_TRUE(Check(empty_one, 1, 0, 99));
+
+ empty_two.AddOptimizedForAppend(1, 50);
+ EXPECT_TRUE(Check(empty_two, 1, 1, 50));
+
+ QuicIntervalSet<int> iset;
+ iset.AddOptimizedForAppend(100, 150);
+ iset.AddOptimizedForAppend(200, 250);
+ EXPECT_TRUE(Check(iset, 2, 100, 150, 200, 250));
+
+ iset.AddOptimizedForAppend(199, 200);
+ EXPECT_TRUE(Check(iset, 2, 100, 150, 199, 250));
+
+ iset.AddOptimizedForAppend(251, 260);
+ EXPECT_TRUE(Check(iset, 3, 100, 150, 199, 250, 251, 260));
+
+ iset.AddOptimizedForAppend(252, 260);
+ EXPECT_TRUE(Check(iset, 3, 100, 150, 199, 250, 251, 260));
+
+ iset.AddOptimizedForAppend(252, 300);
+ EXPECT_TRUE(Check(iset, 3, 100, 150, 199, 250, 251, 300));
+
+ iset.AddOptimizedForAppend(300, 350);
+ EXPECT_TRUE(Check(iset, 3, 100, 150, 199, 250, 251, 350));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetBasic) {
+ // Test Add, Get, Contains and Find
+ QuicIntervalSet<int> iset;
+ EXPECT_TRUE(iset.Empty());
+ EXPECT_EQ(0u, iset.Size());
+ iset.Add(100, 200);
+ EXPECT_FALSE(iset.Empty());
+ EXPECT_EQ(1u, iset.Size());
+ iset.Add(100, 150);
+ iset.Add(150, 200);
+ iset.Add(130, 170);
+ iset.Add(90, 150);
+ iset.Add(170, 220);
+ iset.Add(300, 400);
+ iset.Add(250, 450);
+ EXPECT_FALSE(iset.Empty());
+ EXPECT_EQ(2u, iset.Size());
+ EXPECT_TRUE(Check(iset, 2, 90, 220, 250, 450));
+
+ // Test two intervals with a.max == b.min, that will just join up.
+ iset.Clear();
+ iset.Add(100, 200);
+ iset.Add(200, 300);
+ EXPECT_FALSE(iset.Empty());
+ EXPECT_EQ(1u, iset.Size());
+ EXPECT_TRUE(Check(iset, 1, 100, 300));
+
+ // Test adding two sets together.
+ iset.Clear();
+ QuicIntervalSet<int> iset_add;
+ iset.Add(100, 200);
+ iset.Add(100, 150);
+ iset.Add(150, 200);
+ iset.Add(130, 170);
+ iset_add.Add(90, 150);
+ iset_add.Add(170, 220);
+ iset_add.Add(300, 400);
+ iset_add.Add(250, 450);
+
+ iset.Union(iset_add);
+ EXPECT_FALSE(iset.Empty());
+ EXPECT_EQ(2u, iset.Size());
+ EXPECT_TRUE(Check(iset, 2, 90, 220, 250, 450));
+
+ // Test begin()/end(), and rbegin()/rend()
+ // to iterate over intervals.
+ {
+ std::vector<QuicInterval<int>> expected(iset.begin(), iset.end());
+
+ std::vector<QuicInterval<int>> actual1;
+ std::copy(iset.begin(), iset.end(), back_inserter(actual1));
+ ASSERT_EQ(expected.size(), actual1.size());
+
+ std::vector<QuicInterval<int>> actual2;
+ std::copy(iset.begin(), iset.end(), back_inserter(actual2));
+ ASSERT_EQ(expected.size(), actual2.size());
+
+ for (size_t i = 0; i < expected.size(); i++) {
+ EXPECT_EQ(expected[i].min(), actual1[i].min());
+ EXPECT_EQ(expected[i].max(), actual1[i].max());
+
+ EXPECT_EQ(expected[i].min(), actual2[i].min());
+ EXPECT_EQ(expected[i].max(), actual2[i].max());
+ }
+
+ // Ensure that the rbegin()/rend() iterators correctly yield the intervals
+ // in reverse order.
+ EXPECT_THAT(std::vector<QuicInterval<int>>(iset.rbegin(), iset.rend()),
+ ElementsAreArray(expected.rbegin(), expected.rend()));
+ }
+
+ TestNotContainsAndFind(iset, 89);
+ TestContainsAndFind(iset, 90);
+ TestContainsAndFind(iset, 120);
+ TestContainsAndFind(iset, 219);
+ TestNotContainsAndFind(iset, 220);
+ TestNotContainsAndFind(iset, 235);
+ TestNotContainsAndFind(iset, 249);
+ TestContainsAndFind(iset, 250);
+ TestContainsAndFind(iset, 300);
+ TestContainsAndFind(iset, 449);
+ TestNotContainsAndFind(iset, 450);
+ TestNotContainsAndFind(iset, 451);
+
+ TestNotContainsAndFind(iset, 50, 60);
+ TestNotContainsAndFind(iset, 50, 90);
+ TestNotContainsAndFind(iset, 50, 200);
+ TestNotContainsAndFind(iset, 90, 90);
+ TestContainsAndFind(iset, 90, 200);
+ TestContainsAndFind(iset, 100, 200);
+ TestContainsAndFind(iset, 100, 220);
+ TestNotContainsAndFind(iset, 100, 221);
+ TestNotContainsAndFind(iset, 220, 220);
+ TestNotContainsAndFind(iset, 240, 300);
+ TestContainsAndFind(iset, 250, 300);
+ TestContainsAndFind(iset, 260, 300);
+ TestContainsAndFind(iset, 300, 450);
+ TestNotContainsAndFind(iset, 300, 451);
+
+ QuicIntervalSet<int> iset_contains;
+ iset_contains.Add(50, 90);
+ EXPECT_FALSE(iset.Contains(iset_contains));
+ iset_contains.Clear();
+
+ iset_contains.Add(90, 200);
+ EXPECT_TRUE(iset.Contains(iset_contains));
+ iset_contains.Add(100, 200);
+ EXPECT_TRUE(iset.Contains(iset_contains));
+ iset_contains.Add(100, 220);
+ EXPECT_TRUE(iset.Contains(iset_contains));
+ iset_contains.Add(250, 300);
+ EXPECT_TRUE(iset.Contains(iset_contains));
+ iset_contains.Add(300, 450);
+ EXPECT_TRUE(iset.Contains(iset_contains));
+ iset_contains.Add(300, 451);
+ EXPECT_FALSE(iset.Contains(iset_contains));
+ EXPECT_FALSE(iset.Contains(QuicInterval<int>()));
+ EXPECT_FALSE(iset.Contains(QuicIntervalSet<int>()));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetContainsEmpty) {
+ const QuicIntervalSet<int> empty;
+ const QuicIntervalSet<int> other_empty;
+ const QuicIntervalSet<int> non_empty({{10, 20}, {40, 50}});
+ EXPECT_FALSE(empty.Contains(empty));
+ EXPECT_FALSE(empty.Contains(other_empty));
+ EXPECT_FALSE(empty.Contains(non_empty));
+ EXPECT_FALSE(non_empty.Contains(empty));
+}
+
+TEST_F(QuicIntervalSetTest, Equality) {
+ QuicIntervalSet<int> is_copy = is;
+ EXPECT_EQ(is, is);
+ EXPECT_EQ(is, is_copy);
+ EXPECT_NE(is, other);
+ EXPECT_NE(is, QuicIntervalSet<int>());
+ EXPECT_EQ(QuicIntervalSet<int>(), QuicIntervalSet<int>());
+}
+
+TEST_F(QuicIntervalSetTest, LowerAndUpperBound) {
+ QuicIntervalSet<int> intervals;
+ intervals.Add(10, 20);
+ intervals.Add(30, 40);
+
+ // [10, 20) [30, 40) end
+ // ^ LowerBound(5)
+ // ^ LowerBound(10)
+ // ^ LowerBound(15)
+ // ^ LowerBound(20)
+ // ^ LowerBound(25)
+ // ^ LowerBound(30)
+ // ^ LowerBound(35)
+ // ^ LowerBound(40)
+ // ^ LowerBound(50)
+ EXPECT_EQ(intervals.LowerBound(5)->min(), 10);
+ EXPECT_EQ(intervals.LowerBound(10)->min(), 10);
+ EXPECT_EQ(intervals.LowerBound(15)->min(), 10);
+ EXPECT_EQ(intervals.LowerBound(20)->min(), 30);
+ EXPECT_EQ(intervals.LowerBound(25)->min(), 30);
+ EXPECT_EQ(intervals.LowerBound(30)->min(), 30);
+ EXPECT_EQ(intervals.LowerBound(35)->min(), 30);
+ EXPECT_EQ(intervals.LowerBound(40), intervals.end());
+ EXPECT_EQ(intervals.LowerBound(50), intervals.end());
+
+ // [10, 20) [30, 40) end
+ // ^ UpperBound(5)
+ // ^ UpperBound(10)
+ // ^ UpperBound(15)
+ // ^ UpperBound(20)
+ // ^ UpperBound(25)
+ // ^ UpperBound(30)
+ // ^ UpperBound(35)
+ // ^ UpperBound(40)
+ // ^ UpperBound(50)
+ EXPECT_EQ(intervals.UpperBound(5)->min(), 10);
+ EXPECT_EQ(intervals.UpperBound(10)->min(), 30);
+ EXPECT_EQ(intervals.UpperBound(15)->min(), 30);
+ EXPECT_EQ(intervals.UpperBound(20)->min(), 30);
+ EXPECT_EQ(intervals.UpperBound(25)->min(), 30);
+ EXPECT_EQ(intervals.UpperBound(30), intervals.end());
+ EXPECT_EQ(intervals.UpperBound(35), intervals.end());
+ EXPECT_EQ(intervals.UpperBound(40), intervals.end());
+ EXPECT_EQ(intervals.UpperBound(50), intervals.end());
+}
+
+TEST_F(QuicIntervalSetTest, SpanningInterval) {
+ // Spanning interval of an empty set is empty:
+ {
+ QuicIntervalSet<int> iset;
+ const QuicInterval<int>& ival = iset.SpanningInterval();
+ EXPECT_TRUE(ival.Empty());
+ }
+
+ // Spanning interval of a set with one interval is that interval:
+ {
+ QuicIntervalSet<int> iset;
+ iset.Add(100, 200);
+ const QuicInterval<int>& ival = iset.SpanningInterval();
+ EXPECT_EQ(100, ival.min());
+ EXPECT_EQ(200, ival.max());
+ }
+
+ // Spanning interval of a set with multiple elements is determined
+ // by the endpoints of the first and last element:
+ {
+ const QuicInterval<int>& ival = is.SpanningInterval();
+ EXPECT_EQ(100, ival.min());
+ EXPECT_EQ(2200, ival.max());
+ }
+ {
+ const QuicInterval<int>& ival = other.SpanningInterval();
+ EXPECT_EQ(50, ival.min());
+ EXPECT_EQ(2270, ival.max());
+ }
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetUnion) {
+ is.Union(other);
+ EXPECT_TRUE(Check(is, 12, 50, 70, 100, 200, 300, 400, 470, 600, 650, 670, 700,
+ 830, 870, 1000, 1100, 1230, 1270, 1830, 1900, 2000, 2100,
+ 2200, 2250, 2270));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersection) {
+ EXPECT_TRUE(is.Intersects(other));
+ EXPECT_TRUE(other.Intersects(is));
+ is.Intersection(other);
+ EXPECT_TRUE(Check(is, 7, 350, 360, 370, 380, 500, 530, 770, 800, 1300, 1400,
+ 1500, 1600, 1700, 1800));
+ EXPECT_TRUE(is.Intersects(other));
+ EXPECT_TRUE(other.Intersects(is));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionBothEmpty) {
+ QuicIntervalSet<std::string> mine, theirs;
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionEmptyMine) {
+ QuicIntervalSet<std::string> mine;
+ QuicIntervalSet<std::string> theirs("a", "b");
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionEmptyTheirs) {
+ QuicIntervalSet<std::string> mine("a", "b");
+ QuicIntervalSet<std::string> theirs;
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionTheirsBeforeMine) {
+ QuicIntervalSet<std::string> mine("y", "z");
+ QuicIntervalSet<std::string> theirs;
+ theirs.Add("a", "b");
+ theirs.Add("c", "d");
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionMineBeforeTheirs) {
+ QuicIntervalSet<std::string> mine;
+ mine.Add("a", "b");
+ mine.Add("c", "d");
+ QuicIntervalSet<std::string> theirs("y", "z");
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest,
+ QuicIntervalSetIntersectionTheirsBeforeMineInt64Singletons) {
+ QuicIntervalSet<int64_t> mine({{10, 15}});
+ QuicIntervalSet<int64_t> theirs({{-20, -5}});
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest,
+ QuicIntervalSetIntersectionMineBeforeTheirsIntSingletons) {
+ QuicIntervalSet<int> mine({{10, 15}});
+ QuicIntervalSet<int> theirs({{90, 95}});
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionTheirsBetweenMine) {
+ QuicIntervalSet<int64_t> mine({{0, 5}, {40, 50}});
+ QuicIntervalSet<int64_t> theirs({{10, 15}});
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionMineBetweenTheirs) {
+ QuicIntervalSet<int> mine({{20, 25}});
+ QuicIntervalSet<int> theirs({{10, 15}, {30, 32}});
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionAlternatingIntervals) {
+ QuicIntervalSet<int> mine, theirs;
+ mine.Add(10, 20);
+ mine.Add(40, 50);
+ mine.Add(60, 70);
+ theirs.Add(25, 39);
+ theirs.Add(55, 59);
+ theirs.Add(75, 79);
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(mine.Empty());
+ EXPECT_FALSE(mine.Intersects(theirs));
+ EXPECT_FALSE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest,
+ QuicIntervalSetIntersectionAdjacentAlternatingNonIntersectingIntervals) {
+ // Make sure that intersection with adjacent interval set is empty.
+ const QuicIntervalSet<int> x1({{0, 10}});
+ const QuicIntervalSet<int> y1({{-50, 0}, {10, 95}});
+
+ QuicIntervalSet<int> result1 = x1;
+ result1.Intersection(y1);
+ EXPECT_TRUE(result1.Empty()) << result1;
+
+ const QuicIntervalSet<int16_t> x2({{0, 10}, {20, 30}, {40, 90}});
+ const QuicIntervalSet<int16_t> y2(
+ {{-50, -40}, {-2, 0}, {10, 20}, {32, 40}, {90, 95}});
+
+ QuicIntervalSet<int16_t> result2 = x2;
+ result2.Intersection(y2);
+ EXPECT_TRUE(result2.Empty()) << result2;
+
+ const QuicIntervalSet<int64_t> x3({{-1, 5}, {5, 10}});
+ const QuicIntervalSet<int64_t> y3({{-10, -1}, {10, 95}});
+
+ QuicIntervalSet<int64_t> result3 = x3;
+ result3.Intersection(y3);
+ EXPECT_TRUE(result3.Empty()) << result3;
+}
+
+TEST_F(QuicIntervalSetTest,
+ QuicIntervalSetIntersectionAlternatingIntersectingIntervals) {
+ const QuicIntervalSet<int> x1({{0, 10}});
+ const QuicIntervalSet<int> y1({{-50, 1}, {9, 95}});
+ const QuicIntervalSet<int> expected_result1({{0, 1}, {9, 10}});
+
+ QuicIntervalSet<int> result1 = x1;
+ result1.Intersection(y1);
+ EXPECT_EQ(result1, expected_result1);
+
+ const QuicIntervalSet<int16_t> x2({{0, 10}, {20, 30}, {40, 90}});
+ const QuicIntervalSet<int16_t> y2(
+ {{-50, -40}, {-2, 2}, {9, 21}, {32, 41}, {85, 95}});
+ const QuicIntervalSet<int16_t> expected_result2(
+ {{0, 2}, {9, 10}, {20, 21}, {40, 41}, {85, 90}});
+
+ QuicIntervalSet<int16_t> result2 = x2;
+ result2.Intersection(y2);
+ EXPECT_EQ(result2, expected_result2);
+
+ const QuicIntervalSet<int64_t> x3({{-1, 5}, {5, 10}});
+ const QuicIntervalSet<int64_t> y3({{-10, 3}, {4, 95}});
+ const QuicIntervalSet<int64_t> expected_result3({{-1, 3}, {4, 10}});
+
+ QuicIntervalSet<int64_t> result3 = x3;
+ result3.Intersection(y3);
+ EXPECT_EQ(result3, expected_result3);
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionIdentical) {
+ QuicIntervalSet<int> copy(is);
+ EXPECT_TRUE(copy.Intersects(is));
+ EXPECT_TRUE(is.Intersects(copy));
+ is.Intersection(copy);
+ EXPECT_EQ(copy, is);
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionSuperset) {
+ QuicIntervalSet<int> mine(-1, 10000);
+ EXPECT_TRUE(mine.Intersects(is));
+ EXPECT_TRUE(is.Intersects(mine));
+ mine.Intersection(is);
+ EXPECT_EQ(is, mine);
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionSubset) {
+ QuicIntervalSet<int> copy(is);
+ QuicIntervalSet<int> theirs(-1, 10000);
+ EXPECT_TRUE(copy.Intersects(theirs));
+ EXPECT_TRUE(theirs.Intersects(copy));
+ is.Intersection(theirs);
+ EXPECT_EQ(copy, is);
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetIntersectionLargeSet) {
+ QuicIntervalSet<int> mine, theirs;
+ // mine: [0, 9), [10, 19), ..., [990, 999)
+ for (int i = 0; i < 1000; i += 10) {
+ mine.Add(i, i + 9);
+ }
+
+ theirs.Add(500, 520);
+ theirs.Add(535, 545);
+ theirs.Add(801, 809);
+ EXPECT_TRUE(mine.Intersects(theirs));
+ EXPECT_TRUE(theirs.Intersects(mine));
+ mine.Intersection(theirs);
+ EXPECT_TRUE(Check(mine, 5, 500, 509, 510, 519, 535, 539, 540, 545, 801, 809));
+ EXPECT_TRUE(mine.Intersects(theirs));
+ EXPECT_TRUE(theirs.Intersects(mine));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifference) {
+ is.Difference(other);
+ EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600,
+ 700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200));
+ QuicIntervalSet<int> copy = is;
+ is.Difference(copy);
+ EXPECT_TRUE(is.Empty());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceSingleBounds) {
+ std::vector<QuicInterval<int>> ivals(other.begin(), other.end());
+ for (const QuicInterval<int>& ival : ivals) {
+ is.Difference(ival.min(), ival.max());
+ }
+ EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600,
+ 700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceSingleInterval) {
+ std::vector<QuicInterval<int>> ivals(other.begin(), other.end());
+ for (const QuicInterval<int>& ival : ivals) {
+ is.Difference(ival);
+ }
+ EXPECT_TRUE(Check(is, 10, 100, 200, 300, 350, 360, 370, 380, 400, 530, 600,
+ 700, 770, 900, 1000, 1100, 1200, 1900, 2000, 2100, 2200));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceAlternatingIntervals) {
+ QuicIntervalSet<int> mine, theirs;
+ mine.Add(10, 20);
+ mine.Add(40, 50);
+ mine.Add(60, 70);
+ theirs.Add(25, 39);
+ theirs.Add(55, 59);
+ theirs.Add(75, 79);
+
+ mine.Difference(theirs);
+ EXPECT_TRUE(Check(mine, 3, 10, 20, 40, 50, 60, 70));
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceEmptyMine) {
+ QuicIntervalSet<std::string> mine, theirs;
+ theirs.Add("a", "b");
+
+ mine.Difference(theirs);
+ EXPECT_TRUE(mine.Empty());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceEmptyTheirs) {
+ QuicIntervalSet<std::string> mine, theirs;
+ mine.Add("a", "b");
+
+ mine.Difference(theirs);
+ EXPECT_EQ(1u, mine.Size());
+ EXPECT_EQ("a", mine.begin()->min());
+ EXPECT_EQ("b", mine.begin()->max());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceTheirsBeforeMine) {
+ QuicIntervalSet<std::string> mine, theirs;
+ mine.Add("y", "z");
+ theirs.Add("a", "b");
+
+ mine.Difference(theirs);
+ EXPECT_EQ(1u, mine.Size());
+ EXPECT_EQ("y", mine.begin()->min());
+ EXPECT_EQ("z", mine.begin()->max());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceMineBeforeTheirs) {
+ QuicIntervalSet<std::string> mine, theirs;
+ mine.Add("a", "b");
+ theirs.Add("y", "z");
+
+ mine.Difference(theirs);
+ EXPECT_EQ(1u, mine.Size());
+ EXPECT_EQ("a", mine.begin()->min());
+ EXPECT_EQ("b", mine.begin()->max());
+}
+
+TEST_F(QuicIntervalSetTest, QuicIntervalSetDifferenceIdentical) {
+ QuicIntervalSet<std::string> mine;
+ mine.Add("a", "b");
+ mine.Add("c", "d");
+ QuicIntervalSet<std::string> theirs(mine);
+
+ mine.Difference(theirs);
+ EXPECT_TRUE(mine.Empty());
+}
+
+TEST_F(QuicIntervalSetTest, EmptyComplement) {
+ // The complement of an empty set is the input interval:
+ QuicIntervalSet<int> iset;
+ iset.Complement(100, 200);
+ EXPECT_TRUE(Check(iset, 1, 100, 200));
+}
+
+TEST(QuicIntervalSetMultipleCompactionTest, OuterCovering) {
+ QuicIntervalSet<int> iset;
+ // First add a bunch of disjoint ranges
+ iset.Add(100, 150);
+ iset.Add(200, 250);
+ iset.Add(300, 350);
+ iset.Add(400, 450);
+ EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450));
+ // Now add a big range that covers all of these ranges
+ iset.Add(0, 500);
+ EXPECT_TRUE(Check(iset, 1, 0, 500));
+}
+
+TEST(QuicIntervalSetMultipleCompactionTest, InnerCovering) {
+ QuicIntervalSet<int> iset;
+ // First add a bunch of disjoint ranges
+ iset.Add(100, 150);
+ iset.Add(200, 250);
+ iset.Add(300, 350);
+ iset.Add(400, 450);
+ EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450));
+ // Now add a big range that partially covers the left and right most ranges.
+ iset.Add(125, 425);
+ EXPECT_TRUE(Check(iset, 1, 100, 450));
+}
+
+TEST(QuicIntervalSetMultipleCompactionTest, LeftCovering) {
+ QuicIntervalSet<int> iset;
+ // First add a bunch of disjoint ranges
+ iset.Add(100, 150);
+ iset.Add(200, 250);
+ iset.Add(300, 350);
+ iset.Add(400, 450);
+ EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450));
+ // Now add a big range that partially covers the left most range.
+ iset.Add(125, 500);
+ EXPECT_TRUE(Check(iset, 1, 100, 500));
+}
+
+TEST(QuicIntervalSetMultipleCompactionTest, RightCovering) {
+ QuicIntervalSet<int> iset;
+ // First add a bunch of disjoint ranges
+ iset.Add(100, 150);
+ iset.Add(200, 250);
+ iset.Add(300, 350);
+ iset.Add(400, 450);
+ EXPECT_TRUE(Check(iset, 4, 100, 150, 200, 250, 300, 350, 400, 450));
+ // Now add a big range that partially covers the right most range.
+ iset.Add(0, 425);
+ EXPECT_TRUE(Check(iset, 1, 0, 450));
+}
+
+// Helper method for testing and verifying the results of a one-interval
+// completement case.
+static bool CheckOneComplement(int add_min,
+ int add_max,
+ int comp_min,
+ int comp_max,
+ int count,
+ ...) {
+ QuicIntervalSet<int> iset;
+ iset.Add(add_min, add_max);
+ iset.Complement(comp_min, comp_max);
+ bool result = true;
+ va_list ap;
+ va_start(ap, count);
+ if (!VA_Check(iset, count, ap)) {
+ result = false;
+ }
+ va_end(ap);
+ return result;
+}
+
+TEST_F(QuicIntervalSetTest, SingleIntervalComplement) {
+ // Verify the complement of a set with one interval (i):
+ // |----- i -----|
+ // |----- args -----|
+ EXPECT_TRUE(CheckOneComplement(0, 10, 50, 150, 1, 50, 150));
+
+ // |----- i -----|
+ // |----- args -----|
+ EXPECT_TRUE(CheckOneComplement(50, 150, 0, 100, 1, 0, 50));
+
+ // |----- i -----|
+ // |----- args -----|
+ EXPECT_TRUE(CheckOneComplement(50, 150, 50, 150, 0));
+
+ // |---------- i ----------|
+ // |----- args -----|
+ EXPECT_TRUE(CheckOneComplement(50, 500, 100, 300, 0));
+
+ // |----- i -----|
+ // |---------- args ----------|
+ EXPECT_TRUE(CheckOneComplement(50, 500, 0, 800, 2, 0, 50, 500, 800));
+
+ // |----- i -----|
+ // |----- args -----|
+ EXPECT_TRUE(CheckOneComplement(50, 150, 100, 300, 1, 150, 300));
+
+ // |----- i -----|
+ // |----- args -----|
+ EXPECT_TRUE(CheckOneComplement(50, 150, 200, 300, 1, 200, 300));
+}
+
+// Helper method that copies <iset> and takes its complement,
+// returning false if Check succeeds.
+static bool CheckComplement(const QuicIntervalSet<int>& iset,
+ int comp_min,
+ int comp_max,
+ int count,
+ ...) {
+ QuicIntervalSet<int> iset_copy = iset;
+ iset_copy.Complement(comp_min, comp_max);
+ bool result = true;
+ va_list ap;
+ va_start(ap, count);
+ if (!VA_Check(iset_copy, count, ap)) {
+ result = false;
+ }
+ va_end(ap);
+ return result;
+}
+
+TEST_F(QuicIntervalSetTest, MultiIntervalComplement) {
+ // Initialize a small test set:
+ QuicIntervalSet<int> iset;
+ iset.Add(100, 200);
+ iset.Add(300, 400);
+ iset.Add(500, 600);
+
+ // |----- i -----|
+ // |----- comp -----|
+ EXPECT_TRUE(CheckComplement(iset, 0, 50, 1, 0, 50));
+
+ // |----- i -----|
+ // |----- comp -----|
+ EXPECT_TRUE(CheckComplement(iset, 0, 200, 1, 0, 100));
+ EXPECT_TRUE(CheckComplement(iset, 0, 220, 2, 0, 100, 200, 220));
+
+ // |----- i -----|
+ // |----- comp -----|
+ EXPECT_TRUE(CheckComplement(iset, 100, 600, 2, 200, 300, 400, 500));
+
+ // |---------- i ----------|
+ // |----- comp -----|
+ EXPECT_TRUE(CheckComplement(iset, 300, 400, 0));
+ EXPECT_TRUE(CheckComplement(iset, 250, 400, 1, 250, 300));
+ EXPECT_TRUE(CheckComplement(iset, 300, 450, 1, 400, 450));
+ EXPECT_TRUE(CheckComplement(iset, 250, 450, 2, 250, 300, 400, 450));
+
+ // |----- i -----|
+ // |---------- comp ----------|
+ EXPECT_TRUE(
+ CheckComplement(iset, 0, 700, 4, 0, 100, 200, 300, 400, 500, 600, 700));
+
+ // |----- i -----|
+ // |----- comp -----|
+ EXPECT_TRUE(CheckComplement(iset, 400, 700, 2, 400, 500, 600, 700));
+ EXPECT_TRUE(CheckComplement(iset, 350, 700, 2, 400, 500, 600, 700));
+
+ // |----- i -----|
+ // |----- comp -----|
+ EXPECT_TRUE(CheckComplement(iset, 700, 800, 1, 700, 800));
+}
+
+// Verifies ToString, operator<< don't assert.
+TEST_F(QuicIntervalSetTest, ToString) {
+ QuicIntervalSet<int> iset;
+ iset.Add(300, 400);
+ iset.Add(100, 200);
+ iset.Add(500, 600);
+ EXPECT_TRUE(!iset.ToString().empty());
+ QUIC_VLOG(2) << iset;
+ // Order and format of ToString() output is guaranteed.
+ EXPECT_EQ("{ [100, 200) [300, 400) [500, 600) }", iset.ToString());
+ EXPECT_EQ("{ [1, 2) }", QuicIntervalSet<int>(1, 2).ToString());
+ EXPECT_EQ("{ }", QuicIntervalSet<int>().ToString());
+}
+
+TEST_F(QuicIntervalSetTest, ConstructionDiscardsEmptyInterval) {
+ EXPECT_TRUE(QuicIntervalSet<int>(QuicInterval<int>(2, 2)).Empty());
+ EXPECT_TRUE(QuicIntervalSet<int>(2, 2).Empty());
+ EXPECT_FALSE(QuicIntervalSet<int>(QuicInterval<int>(2, 3)).Empty());
+ EXPECT_FALSE(QuicIntervalSet<int>(2, 3).Empty());
+}
+
+TEST_F(QuicIntervalSetTest, Swap) {
+ QuicIntervalSet<int> a, b;
+ a.Add(300, 400);
+ b.Add(100, 200);
+ b.Add(500, 600);
+ a.Swap(&b);
+ EXPECT_TRUE(Check(a, 2, 100, 200, 500, 600));
+ EXPECT_TRUE(Check(b, 1, 300, 400));
+ swap(a, b);
+ EXPECT_TRUE(Check(a, 1, 300, 400));
+ EXPECT_TRUE(Check(b, 2, 100, 200, 500, 600));
+}
+
+TEST_F(QuicIntervalSetTest, OutputReturnsOstreamRef) {
+ std::stringstream ss;
+ const QuicIntervalSet<int> v(QuicInterval<int>(1, 2));
+ auto return_type_is_a_ref = [](std::ostream&) {};
+ return_type_is_a_ref(ss << v);
+}
+
+struct NotOstreamable {
+ bool operator<(const NotOstreamable&) const { return false; }
+ bool operator>(const NotOstreamable&) const { return false; }
+ bool operator!=(const NotOstreamable&) const { return false; }
+ bool operator>=(const NotOstreamable&) const { return true; }
+ bool operator<=(const NotOstreamable&) const { return true; }
+ bool operator==(const NotOstreamable&) const { return true; }
+};
+
+TEST_F(QuicIntervalSetTest, IntervalOfTypeWithNoOstreamSupport) {
+ const NotOstreamable v;
+ const QuicIntervalSet<NotOstreamable> d(QuicInterval<NotOstreamable>(v, v));
+ // EXPECT_EQ builds a string representation of d. If d::operator<<()
+ // would be defined then this test would not compile because NotOstreamable
+ // objects lack the operator<<() support.
+ EXPECT_EQ(d, d);
+}
+
+class QuicIntervalSetInitTest : public QuicTest {
+ protected:
+ const std::vector<QuicInterval<int>> intervals_{{0, 1}, {2, 4}};
+};
+
+TEST_F(QuicIntervalSetInitTest, DirectInit) {
+ std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}};
+ QuicIntervalSet<int> s(il);
+ EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, CopyInit) {
+ std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}};
+ QuicIntervalSet<int> s = il;
+ EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, AssignIterPair) {
+ QuicIntervalSet<int> s(0, 1000); // Make sure assign clears.
+ s.assign(intervals_.begin(), intervals_.end());
+ EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, AssignInitList) {
+ QuicIntervalSet<int> s(0, 1000); // Make sure assign clears.
+ s.assign({{0, 1}, {2, 3}, {3, 4}});
+ EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, AssignmentInitList) {
+ std::initializer_list<QuicInterval<int>> il = {{0, 1}, {2, 3}, {3, 4}};
+ QuicIntervalSet<int> s;
+ s = il;
+ EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+TEST_F(QuicIntervalSetInitTest, BracedInitThenBracedAssign) {
+ QuicIntervalSet<int> s{{0, 1}, {2, 3}, {3, 4}};
+ s = {{0, 1}, {2, 4}};
+ EXPECT_THAT(s, ElementsAreArray(intervals_));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_interval_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_interval_test.cc
new file mode 100644
index 00000000000..209299d8321
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_interval_test.cc
@@ -0,0 +1,457 @@
+// 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 "net/third_party/quiche/src/quic/core/quic_interval.h"
+
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+template <typename ForwardIterator>
+void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) {
+ while (begin != end) {
+ auto temp = begin;
+ ++begin;
+ delete *temp;
+ }
+}
+
+template <typename T>
+void STLDeleteElements(T* container) {
+ if (!container)
+ return;
+ STLDeleteContainerPointers(container->begin(), container->end());
+ container->clear();
+}
+
+class ConstructorListener {
+ public:
+ ConstructorListener(int* copy_construct_counter, int* move_construct_counter)
+ : copy_construct_counter_(copy_construct_counter),
+ move_construct_counter_(move_construct_counter) {
+ *copy_construct_counter_ = 0;
+ *move_construct_counter_ = 0;
+ }
+ ConstructorListener(const ConstructorListener& other) {
+ copy_construct_counter_ = other.copy_construct_counter_;
+ move_construct_counter_ = other.move_construct_counter_;
+ ++*copy_construct_counter_;
+ }
+ ConstructorListener(ConstructorListener&& other) {
+ copy_construct_counter_ = other.copy_construct_counter_;
+ move_construct_counter_ = other.move_construct_counter_;
+ ++*move_construct_counter_;
+ }
+ bool operator<(const ConstructorListener&) { return false; }
+ bool operator>(const ConstructorListener&) { return false; }
+ bool operator<=(const ConstructorListener&) { return true; }
+ bool operator>=(const ConstructorListener&) { return true; }
+ bool operator==(const ConstructorListener&) { return true; }
+
+ private:
+ int* copy_construct_counter_;
+ int* move_construct_counter_;
+};
+
+TEST(QuicIntervalConstructorTest, Move) {
+ int object1_copy_count, object1_move_count;
+ ConstructorListener object1(&object1_copy_count, &object1_move_count);
+ int object2_copy_count, object2_move_count;
+ ConstructorListener object2(&object2_copy_count, &object2_move_count);
+
+ QuicInterval<ConstructorListener> interval(object1, std::move(object2));
+ EXPECT_EQ(1, object1_copy_count);
+ EXPECT_EQ(0, object1_move_count);
+ EXPECT_EQ(0, object2_copy_count);
+ EXPECT_EQ(1, object2_move_count);
+}
+
+TEST(QuicIntervalConstructorTest, ImplicitConversion) {
+ struct WrappedInt {
+ WrappedInt(int value) : value(value) {}
+ bool operator<(const WrappedInt& other) { return value < other.value; }
+ bool operator>(const WrappedInt& other) { return value > other.value; }
+ bool operator<=(const WrappedInt& other) { return value <= other.value; }
+ bool operator>=(const WrappedInt& other) { return value >= other.value; }
+ bool operator==(const WrappedInt& other) { return value == other.value; }
+ int value;
+ };
+
+ static_assert(std::is_convertible<int, WrappedInt>::value, "");
+ static_assert(
+ std::is_constructible<QuicInterval<WrappedInt>, int, int>::value, "");
+
+ QuicInterval<WrappedInt> i(10, 20);
+ EXPECT_EQ(10, i.min().value);
+ EXPECT_EQ(20, i.max().value);
+}
+
+class QuicIntervalTest : public QuicTest {
+ protected:
+ // Test intersection between the two intervals i1 and i2. Tries
+ // i1.IntersectWith(i2) and vice versa. The intersection should change i1 iff
+ // changes_i1 is true, and the same for changes_i2. The resulting
+ // intersection should be result.
+ void TestIntersect(const QuicInterval<int64_t>& i1,
+ const QuicInterval<int64_t>& i2,
+ bool changes_i1,
+ bool changes_i2,
+ const QuicInterval<int64_t>& result) {
+ QuicInterval<int64_t> i;
+ i = i1;
+ EXPECT_TRUE(i.IntersectWith(i2) == changes_i1 && i == result);
+ i = i2;
+ EXPECT_TRUE(i.IntersectWith(i1) == changes_i2 && i == result);
+ }
+};
+
+TEST_F(QuicIntervalTest, ConstructorsCopyAndClear) {
+ QuicInterval<int32_t> empty;
+ EXPECT_TRUE(empty.Empty());
+
+ QuicInterval<int32_t> d2(0, 100);
+ EXPECT_EQ(0, d2.min());
+ EXPECT_EQ(100, d2.max());
+ EXPECT_EQ(QuicInterval<int32_t>(0, 100), d2);
+ EXPECT_NE(QuicInterval<int32_t>(0, 99), d2);
+
+ empty = d2;
+ EXPECT_EQ(0, d2.min());
+ EXPECT_EQ(100, d2.max());
+ EXPECT_TRUE(empty == d2);
+ EXPECT_EQ(empty, d2);
+ EXPECT_TRUE(d2 == empty);
+ EXPECT_EQ(d2, empty);
+
+ QuicInterval<int32_t> max_less_than_min(40, 20);
+ EXPECT_TRUE(max_less_than_min.Empty());
+ EXPECT_EQ(40, max_less_than_min.min());
+ EXPECT_EQ(20, max_less_than_min.max());
+
+ QuicInterval<int> d3(10, 20);
+ d3.Clear();
+ EXPECT_TRUE(d3.Empty());
+}
+
+TEST_F(QuicIntervalTest, MakeQuicInterval) {
+ static_assert(
+ std::is_same<QuicInterval<int>, decltype(MakeQuicInterval(0, 3))>::value,
+ "Type is deduced incorrectly.");
+ static_assert(std::is_same<QuicInterval<double>,
+ decltype(MakeQuicInterval(0., 3.))>::value,
+ "Type is deduced incorrectly.");
+
+ EXPECT_EQ(MakeQuicInterval(0., 3.), QuicInterval<double>(0, 3));
+}
+
+TEST_F(QuicIntervalTest, GettersSetters) {
+ QuicInterval<int32_t> d1(100, 200);
+
+ // SetMin:
+ d1.SetMin(30);
+ EXPECT_EQ(30, d1.min());
+ EXPECT_EQ(200, d1.max());
+
+ // SetMax:
+ d1.SetMax(220);
+ EXPECT_EQ(30, d1.min());
+ EXPECT_EQ(220, d1.max());
+
+ // Set:
+ d1.Clear();
+ d1.Set(30, 220);
+ EXPECT_EQ(30, d1.min());
+ EXPECT_EQ(220, d1.max());
+
+ // SpanningUnion:
+ QuicInterval<int32_t> d2;
+ EXPECT_TRUE(!d1.SpanningUnion(d2));
+ EXPECT_EQ(30, d1.min());
+ EXPECT_EQ(220, d1.max());
+
+ EXPECT_TRUE(d2.SpanningUnion(d1));
+ EXPECT_EQ(30, d2.min());
+ EXPECT_EQ(220, d2.max());
+
+ d2.SetMin(40);
+ d2.SetMax(100);
+ EXPECT_TRUE(!d1.SpanningUnion(d2));
+ EXPECT_EQ(30, d1.min());
+ EXPECT_EQ(220, d1.max());
+
+ d2.SetMin(20);
+ d2.SetMax(100);
+ EXPECT_TRUE(d1.SpanningUnion(d2));
+ EXPECT_EQ(20, d1.min());
+ EXPECT_EQ(220, d1.max());
+
+ d2.SetMin(50);
+ d2.SetMax(300);
+ EXPECT_TRUE(d1.SpanningUnion(d2));
+ EXPECT_EQ(20, d1.min());
+ EXPECT_EQ(300, d1.max());
+
+ d2.SetMin(0);
+ d2.SetMax(500);
+ EXPECT_TRUE(d1.SpanningUnion(d2));
+ EXPECT_EQ(0, d1.min());
+ EXPECT_EQ(500, d1.max());
+
+ d2.SetMin(100);
+ d2.SetMax(0);
+ EXPECT_TRUE(!d1.SpanningUnion(d2));
+ EXPECT_EQ(0, d1.min());
+ EXPECT_EQ(500, d1.max());
+ EXPECT_TRUE(d2.SpanningUnion(d1));
+ EXPECT_EQ(0, d2.min());
+ EXPECT_EQ(500, d2.max());
+}
+
+TEST_F(QuicIntervalTest, CoveringOps) {
+ const QuicInterval<int64_t> empty;
+ const QuicInterval<int64_t> d(100, 200);
+ const QuicInterval<int64_t> d1(0, 50);
+ const QuicInterval<int64_t> d2(50, 110);
+ const QuicInterval<int64_t> d3(110, 180);
+ const QuicInterval<int64_t> d4(180, 220);
+ const QuicInterval<int64_t> d5(220, 300);
+ const QuicInterval<int64_t> d6(100, 150);
+ const QuicInterval<int64_t> d7(150, 200);
+ const QuicInterval<int64_t> d8(0, 300);
+
+ // Intersection:
+ EXPECT_TRUE(d.Intersects(d));
+ EXPECT_TRUE(!empty.Intersects(d) && !d.Intersects(empty));
+ EXPECT_TRUE(!d.Intersects(d1) && !d1.Intersects(d));
+ EXPECT_TRUE(d.Intersects(d2) && d2.Intersects(d));
+ EXPECT_TRUE(d.Intersects(d3) && d3.Intersects(d));
+ EXPECT_TRUE(d.Intersects(d4) && d4.Intersects(d));
+ EXPECT_TRUE(!d.Intersects(d5) && !d5.Intersects(d));
+ EXPECT_TRUE(d.Intersects(d6) && d6.Intersects(d));
+ EXPECT_TRUE(d.Intersects(d7) && d7.Intersects(d));
+ EXPECT_TRUE(d.Intersects(d8) && d8.Intersects(d));
+
+ QuicInterval<int64_t> i;
+ EXPECT_TRUE(d.Intersects(d, &i) && d == i);
+ EXPECT_TRUE(!empty.Intersects(d, nullptr) && !d.Intersects(empty, nullptr));
+ EXPECT_TRUE(!d.Intersects(d1, nullptr) && !d1.Intersects(d, nullptr));
+ EXPECT_TRUE(d.Intersects(d2, &i) && i == QuicInterval<int64_t>(100, 110));
+ EXPECT_TRUE(d2.Intersects(d, &i) && i == QuicInterval<int64_t>(100, 110));
+ EXPECT_TRUE(d.Intersects(d3, &i) && i == d3);
+ EXPECT_TRUE(d3.Intersects(d, &i) && i == d3);
+ EXPECT_TRUE(d.Intersects(d4, &i) && i == QuicInterval<int64_t>(180, 200));
+ EXPECT_TRUE(d4.Intersects(d, &i) && i == QuicInterval<int64_t>(180, 200));
+ EXPECT_TRUE(!d.Intersects(d5, nullptr) && !d5.Intersects(d, nullptr));
+ EXPECT_TRUE(d.Intersects(d6, &i) && i == d6);
+ EXPECT_TRUE(d6.Intersects(d, &i) && i == d6);
+ EXPECT_TRUE(d.Intersects(d7, &i) && i == d7);
+ EXPECT_TRUE(d7.Intersects(d, &i) && i == d7);
+ EXPECT_TRUE(d.Intersects(d8, &i) && i == d);
+ EXPECT_TRUE(d8.Intersects(d, &i) && i == d);
+
+ // Test IntersectsWith().
+ // Arguments are TestIntersect(i1, i2, changes_i1, changes_i2, result).
+ TestIntersect(empty, d, false, true, empty);
+ TestIntersect(d, d1, true, true, empty);
+ TestIntersect(d1, d2, true, true, empty);
+ TestIntersect(d, d2, true, true, QuicInterval<int64_t>(100, 110));
+ TestIntersect(d8, d, true, false, d);
+ TestIntersect(d8, d1, true, false, d1);
+ TestIntersect(d8, d5, true, false, d5);
+
+ // Contains:
+ EXPECT_TRUE(!empty.Contains(d) && !d.Contains(empty));
+ EXPECT_TRUE(d.Contains(d));
+ EXPECT_TRUE(!d.Contains(d1) && !d1.Contains(d));
+ EXPECT_TRUE(!d.Contains(d2) && !d2.Contains(d));
+ EXPECT_TRUE(d.Contains(d3) && !d3.Contains(d));
+ EXPECT_TRUE(!d.Contains(d4) && !d4.Contains(d));
+ EXPECT_TRUE(!d.Contains(d5) && !d5.Contains(d));
+ EXPECT_TRUE(d.Contains(d6) && !d6.Contains(d));
+ EXPECT_TRUE(d.Contains(d7) && !d7.Contains(d));
+ EXPECT_TRUE(!d.Contains(d8) && d8.Contains(d));
+
+ EXPECT_TRUE(d.Contains(100));
+ EXPECT_TRUE(!d.Contains(200));
+ EXPECT_TRUE(d.Contains(150));
+ EXPECT_TRUE(!d.Contains(99));
+ EXPECT_TRUE(!d.Contains(201));
+
+ // Difference:
+ std::vector<QuicInterval<int64_t>*> diff;
+
+ EXPECT_TRUE(!d.Difference(empty, &diff));
+ EXPECT_EQ(1u, diff.size());
+ EXPECT_EQ(100, diff[0]->min());
+ EXPECT_EQ(200, diff[0]->max());
+ STLDeleteElements(&diff);
+ EXPECT_TRUE(!empty.Difference(d, &diff) && diff.empty());
+
+ EXPECT_TRUE(d.Difference(d, &diff) && diff.empty());
+ EXPECT_TRUE(!d.Difference(d1, &diff));
+ EXPECT_EQ(1u, diff.size());
+ EXPECT_EQ(100, diff[0]->min());
+ EXPECT_EQ(200, diff[0]->max());
+ STLDeleteElements(&diff);
+
+ QuicInterval<int64_t> lo;
+ QuicInterval<int64_t> hi;
+
+ EXPECT_TRUE(d.Difference(d2, &lo, &hi));
+ EXPECT_TRUE(lo.Empty());
+ EXPECT_EQ(110, hi.min());
+ EXPECT_EQ(200, hi.max());
+ EXPECT_TRUE(d.Difference(d2, &diff));
+ EXPECT_EQ(1u, diff.size());
+ EXPECT_EQ(110, diff[0]->min());
+ EXPECT_EQ(200, diff[0]->max());
+ STLDeleteElements(&diff);
+
+ EXPECT_TRUE(d.Difference(d3, &lo, &hi));
+ EXPECT_EQ(100, lo.min());
+ EXPECT_EQ(110, lo.max());
+ EXPECT_EQ(180, hi.min());
+ EXPECT_EQ(200, hi.max());
+ EXPECT_TRUE(d.Difference(d3, &diff));
+ EXPECT_EQ(2u, diff.size());
+ EXPECT_EQ(100, diff[0]->min());
+ EXPECT_EQ(110, diff[0]->max());
+ EXPECT_EQ(180, diff[1]->min());
+ EXPECT_EQ(200, diff[1]->max());
+ STLDeleteElements(&diff);
+
+ EXPECT_TRUE(d.Difference(d4, &lo, &hi));
+ EXPECT_EQ(100, lo.min());
+ EXPECT_EQ(180, lo.max());
+ EXPECT_TRUE(hi.Empty());
+ EXPECT_TRUE(d.Difference(d4, &diff));
+ EXPECT_EQ(1u, diff.size());
+ EXPECT_EQ(100, diff[0]->min());
+ EXPECT_EQ(180, diff[0]->max());
+ STLDeleteElements(&diff);
+
+ EXPECT_FALSE(d.Difference(d5, &lo, &hi));
+ EXPECT_EQ(100, lo.min());
+ EXPECT_EQ(200, lo.max());
+ EXPECT_TRUE(hi.Empty());
+ EXPECT_FALSE(d.Difference(d5, &diff));
+ EXPECT_EQ(1u, diff.size());
+ EXPECT_EQ(100, diff[0]->min());
+ EXPECT_EQ(200, diff[0]->max());
+ STLDeleteElements(&diff);
+
+ EXPECT_TRUE(d.Difference(d6, &lo, &hi));
+ EXPECT_TRUE(lo.Empty());
+ EXPECT_EQ(150, hi.min());
+ EXPECT_EQ(200, hi.max());
+ EXPECT_TRUE(d.Difference(d6, &diff));
+ EXPECT_EQ(1u, diff.size());
+ EXPECT_EQ(150, diff[0]->min());
+ EXPECT_EQ(200, diff[0]->max());
+ STLDeleteElements(&diff);
+
+ EXPECT_TRUE(d.Difference(d7, &lo, &hi));
+ EXPECT_EQ(100, lo.min());
+ EXPECT_EQ(150, lo.max());
+ EXPECT_TRUE(hi.Empty());
+ EXPECT_TRUE(d.Difference(d7, &diff));
+ EXPECT_EQ(1u, diff.size());
+ EXPECT_EQ(100, diff[0]->min());
+ EXPECT_EQ(150, diff[0]->max());
+ STLDeleteElements(&diff);
+
+ EXPECT_TRUE(d.Difference(d8, &lo, &hi));
+ EXPECT_TRUE(lo.Empty());
+ EXPECT_TRUE(hi.Empty());
+ EXPECT_TRUE(d.Difference(d8, &diff) && diff.empty());
+}
+
+TEST_F(QuicIntervalTest, Length) {
+ const QuicInterval<int> empty1;
+ const QuicInterval<int> empty2(1, 1);
+ const QuicInterval<int> empty3(1, 0);
+ const QuicInterval<QuicTime> empty4(
+ QuicTime::Zero() + QuicTime::Delta::FromSeconds(1), QuicTime::Zero());
+ const QuicInterval<int> d1(1, 2);
+ const QuicInterval<int> d2(0, 50);
+ const QuicInterval<QuicTime> d3(
+ QuicTime::Zero(), QuicTime::Zero() + QuicTime::Delta::FromSeconds(1));
+ const QuicInterval<QuicTime> d4(
+ QuicTime::Zero() + QuicTime::Delta::FromSeconds(3600),
+ QuicTime::Zero() + QuicTime::Delta::FromSeconds(5400));
+
+ EXPECT_EQ(0, empty1.Length());
+ EXPECT_EQ(0, empty2.Length());
+ EXPECT_EQ(0, empty3.Length());
+ EXPECT_EQ(QuicTime::Delta::Zero(), empty4.Length());
+ EXPECT_EQ(1, d1.Length());
+ EXPECT_EQ(50, d2.Length());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(1), d3.Length());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(1800), d4.Length());
+}
+
+TEST_F(QuicIntervalTest, IntervalOfTypeWithNoOperatorMinus) {
+ // QuicInterval<T> should work even if T does not support operator-(). We
+ // just can't call QuicInterval<T>::Length() for such types.
+ const QuicInterval<std::string> d1("a", "b");
+ const QuicInterval<std::pair<int, int>> d2({1, 2}, {4, 3});
+ EXPECT_EQ("a", d1.min());
+ EXPECT_EQ("b", d1.max());
+ EXPECT_EQ(std::make_pair(1, 2), d2.min());
+ EXPECT_EQ(std::make_pair(4, 3), d2.max());
+}
+
+struct NoEquals {
+ NoEquals(int v) : value(v) {} // NOLINT
+ int value;
+ bool operator<(const NoEquals& other) const { return value < other.value; }
+};
+
+TEST_F(QuicIntervalTest, OrderedComparisonForTypeWithoutEquals) {
+ const QuicInterval<NoEquals> d1(0, 4);
+ const QuicInterval<NoEquals> d2(0, 3);
+ const QuicInterval<NoEquals> d3(1, 4);
+ const QuicInterval<NoEquals> d4(1, 5);
+ const QuicInterval<NoEquals> d6(0, 4);
+ EXPECT_TRUE(d1 < d2);
+ EXPECT_TRUE(d1 < d3);
+ EXPECT_TRUE(d1 < d4);
+ EXPECT_FALSE(d1 < d6);
+}
+
+TEST_F(QuicIntervalTest, OutputReturnsOstreamRef) {
+ std::stringstream ss;
+ const QuicInterval<int> v(1, 2);
+ // If (ss << v) were to return a value, it wouldn't match the signature of
+ // return_type_is_a_ref() function.
+ auto return_type_is_a_ref = [](std::ostream&) {};
+ return_type_is_a_ref(ss << v);
+}
+
+struct NotOstreamable {
+ bool operator<(const NotOstreamable&) const { return false; }
+ bool operator>=(const NotOstreamable&) const { return true; }
+ bool operator==(const NotOstreamable&) const { return true; }
+};
+
+TEST_F(QuicIntervalTest, IntervalOfTypeWithNoOstreamSupport) {
+ const NotOstreamable v;
+ const QuicInterval<NotOstreamable> d(v, v);
+ // EXPECT_EQ builds a string representation of d. If d::operator<<() would be
+ // defined then this test would not compile because NotOstreamable objects
+ // lack the operator<<() support.
+ EXPECT_EQ(d, d);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..b8c78c6fd58
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_lru_cache.h
@@ -0,0 +1,74 @@
+// 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_CORE_QUIC_LRU_CACHE_H_
+#define QUICHE_QUIC_CORE_QUIC_LRU_CACHE_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+// A LRU cache that maps from type Key to Value* in QUIC.
+// This cache CANNOT be shared by multiple threads (even with locks) because
+// Value* returned by Lookup() can be invalid if the entry is evicted by other
+// threads.
+template <class K, class V>
+class QuicLRUCache {
+ public:
+ explicit QuicLRUCache(size_t capacity) : capacity_(capacity) {}
+ QuicLRUCache(const QuicLRUCache&) = delete;
+ QuicLRUCache& operator=(const QuicLRUCache&) = delete;
+
+ // Inserts one unit of |key|, |value| pair to the cache. Cache takes ownership
+ // of inserted |value|.
+ void Insert(const K& key, std::unique_ptr<V> value) {
+ auto it = cache_.find(key);
+ if (it != cache_.end()) {
+ cache_.erase(it);
+ }
+ cache_.emplace(key, std::move(value));
+
+ if (cache_.size() > capacity_) {
+ cache_.pop_front();
+ }
+ DCHECK_LE(cache_.size(), capacity_);
+ }
+
+ // If cache contains an entry for |key|, return a pointer to it. This returned
+ // value is guaranteed to be valid until Insert or Clear.
+ // Else return nullptr.
+ V* Lookup(const K& key) {
+ auto it = cache_.find(key);
+ if (it == cache_.end()) {
+ return nullptr;
+ }
+
+ std::unique_ptr<V> value = std::move(it->second);
+ cache_.erase(it);
+ auto result = cache_.emplace(key, std::move(value));
+ DCHECK(result.second);
+ return result.first->second.get();
+ }
+
+ // Removes all entries from the cache.
+ void Clear() { cache_.clear(); }
+
+ // Returns maximum size of the cache.
+ size_t MaxSize() const { return capacity_; }
+
+ // Returns current size of the cache.
+ size_t Size() const { return cache_.size(); }
+
+ private:
+ QuicLinkedHashMap<K, std::unique_ptr<V>> cache_;
+ const size_t capacity_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_LRU_CACHE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_lru_cache_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_lru_cache_test.cc
new file mode 100644
index 00000000000..92f788b7af7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_lru_cache_test.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_lru_cache.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+struct CachedItem {
+ explicit CachedItem(uint32_t new_value) : value(new_value) {}
+
+ uint32_t value;
+};
+
+TEST(QuicLRUCacheTest, InsertAndLookup) {
+ QuicLRUCache<int, CachedItem> cache(5);
+ EXPECT_EQ(nullptr, cache.Lookup(1));
+ EXPECT_EQ(0u, cache.Size());
+ EXPECT_EQ(5u, cache.MaxSize());
+
+ // Check that item 1 was properly inserted.
+ std::unique_ptr<CachedItem> item1(new CachedItem(11));
+ cache.Insert(1, std::move(item1));
+ EXPECT_EQ(1u, cache.Size());
+ EXPECT_EQ(11u, cache.Lookup(1)->value);
+
+ // Check that item 2 overrides item 1.
+ std::unique_ptr<CachedItem> item2(new CachedItem(12));
+ cache.Insert(1, std::move(item2));
+ EXPECT_EQ(1u, cache.Size());
+ EXPECT_EQ(12u, cache.Lookup(1)->value);
+
+ std::unique_ptr<CachedItem> item3(new CachedItem(13));
+ cache.Insert(3, std::move(item3));
+ EXPECT_EQ(2u, cache.Size());
+ EXPECT_EQ(13u, cache.Lookup(3)->value);
+
+ // No memory leakage.
+ cache.Clear();
+ EXPECT_EQ(0u, cache.Size());
+}
+
+TEST(QuicLRUCacheTest, Eviction) {
+ QuicLRUCache<int, CachedItem> cache(3);
+
+ for (size_t i = 1; i <= 4; ++i) {
+ std::unique_ptr<CachedItem> item(new CachedItem(10 + i));
+ cache.Insert(i, std::move(item));
+ }
+
+ EXPECT_EQ(3u, cache.Size());
+ EXPECT_EQ(3u, cache.MaxSize());
+
+ // Make sure item 1 is evicted.
+ EXPECT_EQ(nullptr, cache.Lookup(1));
+ EXPECT_EQ(14u, cache.Lookup(4)->value);
+
+ EXPECT_EQ(12u, cache.Lookup(2)->value);
+ std::unique_ptr<CachedItem> item5(new CachedItem(15));
+ cache.Insert(5, std::move(item5));
+ // Make sure item 3 is evicted.
+ EXPECT_EQ(nullptr, cache.Lookup(3));
+ EXPECT_EQ(15u, cache.Lookup(5)->value);
+
+ // No memory leakage.
+ cache.Clear();
+ EXPECT_EQ(0u, cache.Size());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena.h b/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena.h
new file mode 100644
index 00000000000..c76a70f2f8a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// An arena that consists of a single inlined block of |ArenaSize|. Useful to
+// avoid repeated calls to malloc/new and to improve memory locality. DCHECK's
+// if an allocation out of the arena ever fails in debug builds; falls back to
+// heap allocation in release builds.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_ONE_BLOCK_ARENA_H_
+#define QUICHE_QUIC_CORE_QUIC_ONE_BLOCK_ARENA_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_arena_scoped_ptr.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+template <uint32_t ArenaSize>
+class QuicOneBlockArena {
+ static const uint32_t kMaxAlign = 8;
+
+ public:
+ QuicOneBlockArena();
+ QuicOneBlockArena(const QuicOneBlockArena&) = delete;
+ QuicOneBlockArena& operator=(const QuicOneBlockArena&) = delete;
+
+ // Instantiates an object of type |T| with |args|. |args| are perfectly
+ // forwarded to |T|'s constructor. The returned pointer's lifetime is
+ // controlled by QuicArenaScopedPtr.
+ template <typename T, typename... Args>
+ QuicArenaScopedPtr<T> New(Args&&... args);
+
+ private:
+ // Returns the size of |T| aligned up to |kMaxAlign|.
+ template <typename T>
+ static inline uint32_t AlignedSize() {
+ return ((sizeof(T) + (kMaxAlign - 1)) / kMaxAlign) * kMaxAlign;
+ }
+
+ // Actual storage.
+ // Subtle/annoying: the value '8' must be coded explicitly into the alignment
+ // declaration for MSVC.
+ QUIC_ALIGNED(8) char storage_[ArenaSize];
+ // Current offset into the storage.
+ uint32_t offset_;
+};
+
+template <uint32_t ArenaSize>
+QuicOneBlockArena<ArenaSize>::QuicOneBlockArena() : offset_(0) {}
+
+template <uint32_t ArenaSize>
+template <typename T, typename... Args>
+QuicArenaScopedPtr<T> QuicOneBlockArena<ArenaSize>::New(Args&&... args) {
+ DCHECK_LT(AlignedSize<T>(), ArenaSize)
+ << "Object is too large for the arena.";
+ static_assert(QUIC_ALIGN_OF(T) > 1,
+ "Objects added to the arena must be at least 2B aligned.");
+ if (QUIC_PREDICT_FALSE(offset_ > ArenaSize - AlignedSize<T>())) {
+ QUIC_BUG << "Ran out of space in QuicOneBlockArena at " << this
+ << ", max size was " << ArenaSize << ", failing request was "
+ << AlignedSize<T>() << ", end of arena was " << offset_;
+ return QuicArenaScopedPtr<T>(new T(std::forward<Args>(args)...));
+ }
+
+ void* buf = &storage_[offset_];
+ new (buf) T(std::forward<Args>(args)...);
+ offset_ += AlignedSize<T>();
+ return QuicArenaScopedPtr<T>(buf,
+ QuicArenaScopedPtr<T>::ConstructFrom::kArena);
+}
+
+// QuicConnections currently use around 1KB of polymorphic types which would
+// ordinarily be on the heap. Instead, store them inline in an arena.
+using QuicConnectionArena = QuicOneBlockArena<1024>;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_ONE_BLOCK_ARENA_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena_test.cc
new file mode 100644
index 00000000000..3175ac54abf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_one_block_arena_test.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h"
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace {
+
+static const uint32_t kMaxAlign = 8;
+
+struct TestObject {
+ uint32_t value;
+};
+
+class QuicOneBlockArenaTest : public QuicTest {};
+
+TEST_F(QuicOneBlockArenaTest, AllocateSuccess) {
+ QuicOneBlockArena<1024> arena;
+ QuicArenaScopedPtr<TestObject> ptr = arena.New<TestObject>();
+ EXPECT_TRUE(ptr.is_from_arena());
+}
+
+TEST_F(QuicOneBlockArenaTest, Exhaust) {
+ QuicOneBlockArena<1024> arena;
+ for (size_t i = 0; i < 1024 / kMaxAlign; ++i) {
+ QuicArenaScopedPtr<TestObject> ptr = arena.New<TestObject>();
+ EXPECT_TRUE(ptr.is_from_arena());
+ }
+ QuicArenaScopedPtr<TestObject> ptr;
+ EXPECT_QUIC_BUG(ptr = arena.New<TestObject>(),
+ "Ran out of space in QuicOneBlockArena");
+ EXPECT_FALSE(ptr.is_from_arena());
+}
+
+TEST_F(QuicOneBlockArenaTest, NoOverlaps) {
+ QuicOneBlockArena<1024> arena;
+ std::vector<QuicArenaScopedPtr<TestObject>> objects;
+ QuicIntervalSet<uintptr_t> used;
+ for (size_t i = 0; i < 1024 / kMaxAlign; ++i) {
+ QuicArenaScopedPtr<TestObject> ptr = arena.New<TestObject>();
+ EXPECT_TRUE(ptr.is_from_arena());
+
+ uintptr_t begin = reinterpret_cast<uintptr_t>(ptr.get());
+ uintptr_t end = begin + sizeof(TestObject);
+ EXPECT_FALSE(used.Contains(begin));
+ EXPECT_FALSE(used.Contains(end - 1));
+ used.Add(begin, end);
+ }
+}
+
+} // namespace
+} // namespace quic
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
new file mode 100644
index 00000000000..96b5bc6e089
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.cc
@@ -0,0 +1,1041 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+namespace {
+
+QuicLongHeaderType EncryptionlevelToLongHeaderType(EncryptionLevel level) {
+ switch (level) {
+ case ENCRYPTION_INITIAL:
+ return INITIAL;
+ case ENCRYPTION_HANDSHAKE:
+ return HANDSHAKE;
+ case ENCRYPTION_ZERO_RTT:
+ return ZERO_RTT_PROTECTED;
+ case ENCRYPTION_FORWARD_SECURE:
+ QUIC_BUG
+ << "Try to derive long header type for packet with encryption level: "
+ << QuicUtils::EncryptionLevelToString(level);
+ return INVALID_PACKET_TYPE;
+ default:
+ QUIC_BUG << QuicUtils::EncryptionLevelToString(level);
+ return INVALID_PACKET_TYPE;
+ }
+}
+
+} // namespace
+
+#define ENDPOINT \
+ (framer_->perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
+
+QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ DelegateInterface* delegate)
+ : QuicPacketCreator(connection_id,
+ framer,
+ QuicRandom::GetInstance(),
+ delegate) {}
+
+QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random,
+ DelegateInterface* delegate)
+ : delegate_(delegate),
+ debug_delegate_(nullptr),
+ framer_(framer),
+ random_(random),
+ send_version_in_packet_(framer->perspective() == Perspective::IS_CLIENT),
+ have_diversification_nonce_(false),
+ max_packet_length_(0),
+ connection_id_included_(CONNECTION_ID_PRESENT),
+ packet_size_(0),
+ connection_id_(connection_id),
+ packet_(QuicPacketNumber(),
+ PACKET_1BYTE_PACKET_NUMBER,
+ nullptr,
+ 0,
+ false,
+ false),
+ pending_padding_bytes_(0),
+ needs_full_padding_(false),
+ can_set_transmission_type_(false),
+ set_transmission_type_for_next_frame_(
+ GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)) {
+ SetMaxPacketLength(kDefaultMaxPacketSize);
+}
+
+QuicPacketCreator::~QuicPacketCreator() {
+ DeleteFrames(&packet_.retransmittable_frames);
+}
+
+void QuicPacketCreator::SetEncrypter(EncryptionLevel level,
+ std::unique_ptr<QuicEncrypter> encrypter) {
+ framer_->SetEncrypter(level, std::move(encrypter));
+ max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_);
+}
+
+bool QuicPacketCreator::CanSetMaxPacketLength() const {
+ // |max_packet_length_| should not be changed mid-packet.
+ return queued_frames_.empty();
+}
+
+void QuicPacketCreator::SetMaxPacketLength(QuicByteCount length) {
+ DCHECK(CanSetMaxPacketLength());
+
+ // Avoid recomputing |max_plaintext_size_| if the length does not actually
+ // change.
+ if (length == max_packet_length_) {
+ return;
+ }
+
+ max_packet_length_ = length;
+ max_plaintext_size_ = framer_->GetMaxPlaintextSize(max_packet_length_);
+}
+
+// Stops serializing version of the protocol in packets sent after this call.
+// A packet that is already open might send kQuicVersionSize bytes less than the
+// maximum packet size if we stop sending version before it is serialized.
+void QuicPacketCreator::StopSendingVersion() {
+ DCHECK(send_version_in_packet_);
+ DCHECK_LE(framer_->transport_version(), QUIC_VERSION_43);
+ send_version_in_packet_ = false;
+ if (packet_size_ > 0) {
+ DCHECK_LT(kQuicVersionSize, packet_size_);
+ packet_size_ -= kQuicVersionSize;
+ }
+}
+
+void QuicPacketCreator::SetDiversificationNonce(
+ const DiversificationNonce& nonce) {
+ DCHECK(!have_diversification_nonce_);
+ have_diversification_nonce_ = true;
+ diversification_nonce_ = nonce;
+}
+
+void QuicPacketCreator::UpdatePacketNumberLength(
+ QuicPacketNumber least_packet_awaited_by_peer,
+ QuicPacketCount max_packets_in_flight) {
+ if (!queued_frames_.empty()) {
+ // Don't change creator state if there are frames queued.
+ QUIC_BUG << "Called UpdatePacketNumberLength with " << queued_frames_.size()
+ << " queued_frames. First frame type:"
+ << queued_frames_.front().type
+ << " last frame type:" << queued_frames_.back().type;
+ return;
+ }
+
+ DCHECK_LE(least_packet_awaited_by_peer, packet_.packet_number + 1);
+ const uint64_t current_delta =
+ packet_.packet_number + 1 - least_packet_awaited_by_peer;
+ const uint64_t delta = std::max(current_delta, max_packets_in_flight);
+ packet_.packet_number_length = QuicFramer::GetMinPacketNumberLength(
+ framer_->transport_version(), QuicPacketNumber(delta * 4));
+}
+
+bool QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset,
+ TransmissionType transmission_type,
+ QuicFrame* frame) {
+ if (!CreateCryptoFrame(level, write_length, offset, frame)) {
+ return false;
+ }
+ // When crypto data was sent in stream frames, ConsumeData is called with
+ // |needs_full_padding = true|. Keep the same behavior here when sending
+ // crypto frames.
+ //
+ // TODO(nharper): Check what the IETF drafts say about padding out initial
+ // messages and change this as appropriate.
+ needs_full_padding_ = true;
+ return AddFrame(*frame, /*save_retransmittable_frames*/ true,
+ transmission_type);
+}
+
+bool QuicPacketCreator::ConsumeData(QuicStreamId id,
+ size_t data_size,
+ QuicStreamOffset offset,
+ bool fin,
+ bool needs_full_padding,
+ TransmissionType transmission_type,
+ QuicFrame* frame) {
+ if (!HasRoomForStreamFrame(id, offset, data_size)) {
+ return false;
+ }
+ CreateStreamFrame(id, data_size, offset, fin, frame);
+ // Explicitly disallow multi-packet CHLOs.
+ if (FLAGS_quic_enforce_single_packet_chlo &&
+ StreamFrameIsClientHello(frame->stream_frame) &&
+ frame->stream_frame.data_length < data_size) {
+ const std::string error_details =
+ "Client hello won't fit in a single packet.";
+ QUIC_BUG << error_details << " Constructed stream frame length: "
+ << frame->stream_frame.data_length
+ << " CHLO length: " << data_size;
+ delegate_->OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, error_details,
+ ConnectionCloseSource::FROM_SELF);
+ return false;
+ }
+ if (!AddFrame(*frame, /*save_retransmittable_frames=*/true,
+ transmission_type)) {
+ // Fails if we try to write unencrypted stream data.
+ return false;
+ }
+ if (needs_full_padding) {
+ needs_full_padding_ = true;
+ }
+
+ return true;
+}
+
+bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id,
+ QuicStreamOffset offset,
+ size_t data_size) {
+ return BytesFree() >
+ QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id,
+ offset, true, data_size);
+}
+
+bool QuicPacketCreator::HasRoomForMessageFrame(QuicByteCount length) {
+ return BytesFree() >= QuicFramer::GetMessageFrameSize(
+ framer_->transport_version(), true, length);
+}
+
+// TODO(fkastenholz): this method should not use constant values for
+// the last-frame-in-packet and data-length parameters to
+// GetMinStreamFrameSize. Proper values should be plumbed in from
+// higher up. This was left this way for now for a few reasons. First,
+// higher up calls to StreamFramePacketOverhead() do not always know
+// this information, leading to a cascade of changes and B) the
+// higher-up software does not always loop, calling
+// StreamFramePacketOverhead() once for every packet -- eg there is
+// a test in quic_connection_test that calls it once and assumes that
+// the value is the same for all packets.
+
+// static
+size_t QuicPacketCreator::StreamFramePacketOverhead(
+ QuicTransportVersion version,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool include_version,
+ bool include_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicVariableLengthIntegerLength length_length,
+ QuicStreamOffset offset) {
+ return GetPacketHeaderSize(version, destination_connection_id_length,
+ source_connection_id_length, include_version,
+ include_diversification_nonce,
+ packet_number_length, retry_token_length_length, 0,
+ length_length) +
+
+ // Assumes this is a packet with a single stream frame in it. Since
+ // last_frame_in_packet is set true, the size of the length field is
+ // not included in the calculation. This is OK because in other places
+ // in the code, the logic adds back 2 (the size of the Google QUIC
+ // length) when a frame is not the last frame of the packet. This is
+ // also acceptable for IETF Quic; even though the length field could be
+ // 8 bytes long, in practice it will not be longer than 2 bytes (enough
+ // to encode 16K). A length that would be encoded in 2 bytes (0xfff)
+ // is passed just for cleanliness.
+ //
+ // TODO(fkastenholz): This is very hacky and feels brittle. Ideally we
+ // would calculate the correct lengths at the correct time, based on
+ // the state at that time/place.
+ QuicFramer::GetMinStreamFrameSize(version, 1u, offset, true,
+ kMaxOutgoingPacketSize);
+}
+
+void QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
+ size_t data_size,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicFrame* frame) {
+ DCHECK_GT(
+ max_packet_length_,
+ StreamFramePacketOverhead(
+ framer_->transport_version(), GetDestinationConnectionIdLength(),
+ GetSourceConnectionIdLength(), kIncludeVersion,
+ IncludeNonceInPublicHeader(), PACKET_6BYTE_PACKET_NUMBER,
+ GetRetryTokenLengthLength(), GetLengthLength(), offset));
+
+ QUIC_BUG_IF(!HasRoomForStreamFrame(id, offset, data_size))
+ << "No room for Stream frame, BytesFree: " << BytesFree()
+ << " MinStreamFrameSize: "
+ << QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id,
+ offset, true, data_size);
+
+ QUIC_BUG_IF(data_size == 0 && !fin)
+ << "Creating a stream frame for stream ID:" << id
+ << " with no data or fin.";
+ size_t min_frame_size = QuicFramer::GetMinStreamFrameSize(
+ framer_->transport_version(), id, offset,
+ /* last_frame_in_packet= */ true, data_size);
+ size_t bytes_consumed =
+ std::min<size_t>(BytesFree() - min_frame_size, data_size);
+
+ bool set_fin = fin && bytes_consumed == data_size; // Last frame.
+ *frame = QuicFrame(QuicStreamFrame(id, set_fin, offset, bytes_consumed));
+}
+
+bool QuicPacketCreator::CreateCryptoFrame(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset,
+ QuicFrame* frame) {
+ size_t min_frame_size =
+ QuicFramer::GetMinCryptoFrameSize(write_length, offset);
+ if (BytesFree() <= min_frame_size) {
+ return false;
+ }
+ size_t max_write_length = BytesFree() - min_frame_size;
+ size_t bytes_consumed = std::min<size_t>(max_write_length, write_length);
+ *frame = QuicFrame(new QuicCryptoFrame(level, offset, bytes_consumed));
+ return true;
+}
+
+void QuicPacketCreator::ReserializeAllFrames(
+ const QuicPendingRetransmission& retransmission,
+ char* buffer,
+ size_t buffer_len) {
+ DCHECK(queued_frames_.empty());
+ DCHECK_EQ(0, packet_.num_padding_bytes);
+ QUIC_BUG_IF(retransmission.retransmittable_frames.empty())
+ << "Attempt to serialize empty packet";
+ const EncryptionLevel default_encryption_level = packet_.encryption_level;
+
+ // Temporarily set the packet number length and change the encryption level.
+ packet_.packet_number_length = retransmission.packet_number_length;
+ if (retransmission.num_padding_bytes == -1) {
+ // Only retransmit padding when original packet needs full padding. Padding
+ // from pending_padding_bytes_ are not retransmitted.
+ needs_full_padding_ = true;
+ }
+ // Only preserve the original encryption level if it's a handshake packet or
+ // if we haven't gone forward secure.
+ if (retransmission.has_crypto_handshake ||
+ packet_.encryption_level != ENCRYPTION_FORWARD_SECURE) {
+ packet_.encryption_level = retransmission.encryption_level;
+ }
+
+ // Serialize the packet and restore packet number length state.
+ for (const QuicFrame& frame : retransmission.retransmittable_frames) {
+ bool success = AddFrame(frame, false, retransmission.transmission_type);
+ QUIC_BUG_IF(!success) << " Failed to add frame of type:" << frame.type
+ << " num_frames:"
+ << retransmission.retransmittable_frames.size()
+ << " retransmission.packet_number_length:"
+ << retransmission.packet_number_length
+ << " packet_.packet_number_length:"
+ << packet_.packet_number_length;
+ }
+ packet_.transmission_type = retransmission.transmission_type;
+ SerializePacket(buffer, buffer_len);
+ packet_.original_packet_number = retransmission.packet_number;
+ OnSerializedPacket();
+ // Restore old values.
+ packet_.encryption_level = default_encryption_level;
+}
+
+void QuicPacketCreator::Flush() {
+ if (!HasPendingFrames() && pending_padding_bytes_ == 0) {
+ return;
+ }
+
+ QUIC_CACHELINE_ALIGNED char stack_buffer[kMaxOutgoingPacketSize];
+ char* serialized_packet_buffer = delegate_->GetPacketBuffer();
+ if (serialized_packet_buffer == nullptr) {
+ serialized_packet_buffer = stack_buffer;
+ }
+
+ SerializePacket(serialized_packet_buffer, kMaxOutgoingPacketSize);
+ OnSerializedPacket();
+}
+
+void QuicPacketCreator::OnSerializedPacket() {
+ if (packet_.encrypted_buffer == nullptr) {
+ const std::string error_details = "Failed to SerializePacket.";
+ QUIC_BUG << error_details;
+ delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET,
+ error_details,
+ ConnectionCloseSource::FROM_SELF);
+ return;
+ }
+
+ SerializedPacket packet(std::move(packet_));
+ ClearPacket();
+ delegate_->OnSerializedPacket(&packet);
+}
+
+void QuicPacketCreator::ClearPacket() {
+ packet_.has_ack = false;
+ packet_.has_stop_waiting = false;
+ packet_.has_crypto_handshake = NOT_HANDSHAKE;
+ packet_.num_padding_bytes = 0;
+ packet_.original_packet_number.Clear();
+ if (!can_set_transmission_type_ || ShouldSetTransmissionTypeForNextFrame()) {
+ packet_.transmission_type = NOT_RETRANSMISSION;
+ }
+ packet_.encrypted_buffer = nullptr;
+ packet_.encrypted_length = 0;
+ DCHECK(packet_.retransmittable_frames.empty());
+ packet_.largest_acked.Clear();
+ needs_full_padding_ = false;
+}
+
+void QuicPacketCreator::CreateAndSerializeStreamFrame(
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset iov_offset,
+ QuicStreamOffset stream_offset,
+ bool fin,
+ TransmissionType transmission_type,
+ size_t* num_bytes_consumed) {
+ DCHECK(queued_frames_.empty());
+ // Write out the packet header
+ QuicPacketHeader header;
+ FillPacketHeader(&header);
+
+ QUIC_CACHELINE_ALIGNED char stack_buffer[kMaxOutgoingPacketSize];
+ char* encrypted_buffer = delegate_->GetPacketBuffer();
+ if (encrypted_buffer == nullptr) {
+ encrypted_buffer = stack_buffer;
+ }
+
+ QuicDataWriter writer(kMaxOutgoingPacketSize, encrypted_buffer);
+ size_t length_field_offset = 0;
+ if (!framer_->AppendPacketHeader(header, &writer, &length_field_offset)) {
+ QUIC_BUG << "AppendPacketHeader failed";
+ return;
+ }
+
+ // Create a Stream frame with the remaining space.
+ QUIC_BUG_IF(iov_offset == write_length && !fin)
+ << "Creating a stream frame with no data or fin.";
+ const size_t remaining_data_size = write_length - iov_offset;
+ const size_t min_frame_size = QuicFramer::GetMinStreamFrameSize(
+ framer_->transport_version(), id, stream_offset,
+ /* last_frame_in_packet= */ true, remaining_data_size);
+ const size_t available_size =
+ max_plaintext_size_ - writer.length() - min_frame_size;
+ const size_t bytes_consumed =
+ std::min<size_t>(available_size, remaining_data_size);
+
+ const bool set_fin = fin && (bytes_consumed == remaining_data_size);
+ QuicStreamFrame frame(id, set_fin, stream_offset, bytes_consumed);
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnFrameAddedToPacket(QuicFrame(frame));
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Adding frame: " << frame;
+
+ // TODO(ianswett): AppendTypeByte and AppendStreamFrame could be optimized
+ // into one method that takes a QuicStreamFrame, if warranted.
+ if (!framer_->AppendTypeByte(QuicFrame(frame),
+ /* no stream frame length */ true, &writer)) {
+ QUIC_BUG << "AppendTypeByte failed";
+ return;
+ }
+ if (!framer_->AppendStreamFrame(frame, /* no stream frame length */ true,
+ &writer)) {
+ QUIC_BUG << "AppendStreamFrame failed";
+ return;
+ }
+
+ if (!framer_->WriteIetfLongHeaderLength(header, &writer, length_field_offset,
+ packet_.encryption_level)) {
+ return;
+ }
+
+ if (ShouldSetTransmissionTypeForNextFrame()) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_set_transmission_type_for_next_frame, 1,
+ 2);
+ packet_.transmission_type = transmission_type;
+ }
+
+ size_t encrypted_length = framer_->EncryptInPlace(
+ packet_.encryption_level, packet_.packet_number,
+ GetStartOfEncryptedData(framer_->transport_version(), header),
+ writer.length(), kMaxOutgoingPacketSize, encrypted_buffer);
+ if (encrypted_length == 0) {
+ QUIC_BUG << "Failed to encrypt packet number " << header.packet_number;
+ return;
+ }
+ // TODO(ianswett): Optimize the storage so RetransmitableFrames can be
+ // unioned with a QuicStreamFrame and a UniqueStreamBuffer.
+ *num_bytes_consumed = bytes_consumed;
+ packet_size_ = 0;
+ packet_.encrypted_buffer = encrypted_buffer;
+ packet_.encrypted_length = encrypted_length;
+ packet_.retransmittable_frames.push_back(QuicFrame(frame));
+ OnSerializedPacket();
+}
+
+bool QuicPacketCreator::HasPendingFrames() const {
+ return !queued_frames_.empty();
+}
+
+bool QuicPacketCreator::HasPendingRetransmittableFrames() const {
+ return !packet_.retransmittable_frames.empty();
+}
+
+bool QuicPacketCreator::HasPendingStreamFramesOfStream(QuicStreamId id) const {
+ for (const auto& frame : packet_.retransmittable_frames) {
+ if (frame.type == STREAM_FRAME && frame.stream_frame.stream_id == id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t QuicPacketCreator::ExpansionOnNewFrame() const {
+ // If the last frame in the packet is a message frame, then it will expand to
+ // include the varint message length when a new frame is added.
+ const bool has_trailing_message_frame =
+ !queued_frames_.empty() && queued_frames_.back().type == MESSAGE_FRAME;
+ if (has_trailing_message_frame) {
+ return QuicDataWriter::GetVarInt62Len(
+ queued_frames_.back().message_frame->message_length);
+ }
+ // If the last frame in the packet is a stream frame, then it will expand to
+ // include the stream_length field when a new frame is added.
+ const bool has_trailing_stream_frame =
+ !queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME;
+ if (!has_trailing_stream_frame) {
+ return 0;
+ }
+ if (framer_->transport_version() == QUIC_VERSION_99) {
+ return QuicDataWriter::GetVarInt62Len(
+ queued_frames_.back().stream_frame.data_length);
+ }
+ return kQuicStreamPayloadLengthSize;
+}
+
+size_t QuicPacketCreator::BytesFree() {
+ DCHECK_GE(max_plaintext_size_, PacketSize());
+ return max_plaintext_size_ -
+ std::min(max_plaintext_size_, PacketSize() + ExpansionOnNewFrame());
+}
+
+size_t QuicPacketCreator::PacketSize() {
+ if (!queued_frames_.empty()) {
+ return packet_size_;
+ }
+ packet_size_ = GetPacketHeaderSize(
+ framer_->transport_version(), GetDestinationConnectionIdLength(),
+ GetSourceConnectionIdLength(), IncludeVersionInHeader(),
+ IncludeNonceInPublicHeader(), GetPacketNumberLength(),
+ GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength());
+ return packet_size_;
+}
+
+bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame,
+ TransmissionType transmission_type) {
+ return AddFrame(frame, /*save_retransmittable_frames=*/true,
+ transmission_type);
+}
+
+bool QuicPacketCreator::AddPaddedSavedFrame(
+ const QuicFrame& frame,
+ TransmissionType transmission_type) {
+ if (AddFrame(frame, /*save_retransmittable_frames=*/true,
+ transmission_type)) {
+ needs_full_padding_ = true;
+ return true;
+ }
+ return false;
+}
+
+void QuicPacketCreator::SerializePacket(char* encrypted_buffer,
+ size_t encrypted_buffer_len) {
+ DCHECK_LT(0u, encrypted_buffer_len);
+ QUIC_BUG_IF(queued_frames_.empty() && pending_padding_bytes_ == 0)
+ << "Attempt to serialize empty packet";
+ QuicPacketHeader header;
+ // FillPacketHeader increments packet_number_.
+ FillPacketHeader(&header);
+
+ MaybeAddPadding();
+
+ DCHECK_GE(max_plaintext_size_, packet_size_);
+ // Use the packet_size_ instead of the buffer size to ensure smaller
+ // packet sizes are properly used.
+ size_t length =
+ framer_->BuildDataPacket(header, queued_frames_, encrypted_buffer,
+ packet_size_, packet_.encryption_level);
+ if (length == 0) {
+ QUIC_BUG << "Failed to serialize " << queued_frames_.size() << " frames.";
+ return;
+ }
+
+ // ACK Frames will be truncated due to length only if they're the only frame
+ // in the packet, and if packet_size_ was set to max_plaintext_size_. If
+ // truncation due to length occurred, then GetSerializedFrameLength will have
+ // returned all bytes free.
+ bool possibly_truncated_by_length = packet_size_ == max_plaintext_size_ &&
+ queued_frames_.size() == 1 &&
+ queued_frames_.back().type == ACK_FRAME;
+ // Because of possible truncation, we can't be confident that our
+ // packet size calculation worked correctly.
+ if (!possibly_truncated_by_length) {
+ DCHECK_EQ(packet_size_, length);
+ }
+ const size_t encrypted_length = framer_->EncryptInPlace(
+ packet_.encryption_level, packet_.packet_number,
+ GetStartOfEncryptedData(framer_->transport_version(), header), length,
+ encrypted_buffer_len, encrypted_buffer);
+ if (encrypted_length == 0) {
+ QUIC_BUG << "Failed to encrypt packet number " << packet_.packet_number;
+ return;
+ }
+
+ packet_size_ = 0;
+ queued_frames_.clear();
+ packet_.encrypted_buffer = encrypted_buffer;
+ packet_.encrypted_length = encrypted_length;
+}
+
+std::unique_ptr<QuicEncryptedPacket>
+QuicPacketCreator::SerializeVersionNegotiationPacket(
+ bool ietf_quic,
+ const ParsedQuicVersionVector& supported_versions) {
+ DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective());
+ std::unique_ptr<QuicEncryptedPacket> encrypted =
+ QuicFramer::BuildVersionNegotiationPacket(connection_id_, ietf_quic,
+ supported_versions);
+ DCHECK(encrypted);
+ DCHECK_GE(max_packet_length_, encrypted->length());
+ return encrypted;
+}
+
+OwningSerializedPacketPointer
+QuicPacketCreator::SerializeConnectivityProbingPacket() {
+ QUIC_BUG_IF(framer_->transport_version() == QUIC_VERSION_99)
+ << "Must not be version 99 to serialize padded ping connectivity probe";
+ QuicPacketHeader header;
+ // FillPacketHeader increments packet_number_.
+ FillPacketHeader(&header);
+
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+ size_t length = framer_->BuildConnectivityProbingPacket(
+ header, buffer.get(), max_plaintext_size_, packet_.encryption_level);
+ DCHECK(length);
+
+ const size_t encrypted_length = framer_->EncryptInPlace(
+ packet_.encryption_level, packet_.packet_number,
+ GetStartOfEncryptedData(framer_->transport_version(), header), length,
+ kMaxOutgoingPacketSize, buffer.get());
+ DCHECK(encrypted_length);
+
+ OwningSerializedPacketPointer serialize_packet(new SerializedPacket(
+ header.packet_number, header.packet_number_length, buffer.release(),
+ encrypted_length, /*has_ack=*/false, /*has_stop_waiting=*/false));
+
+ serialize_packet->encryption_level = packet_.encryption_level;
+ serialize_packet->transmission_type = NOT_RETRANSMISSION;
+
+ return serialize_packet;
+}
+
+OwningSerializedPacketPointer
+QuicPacketCreator::SerializePathChallengeConnectivityProbingPacket(
+ QuicPathFrameBuffer* payload) {
+ QUIC_BUG_IF(framer_->transport_version() != QUIC_VERSION_99)
+ << "Must be version 99 to serialize path challenge connectivity probe, "
+ "is version "
+ << framer_->transport_version();
+ QuicPacketHeader header;
+ // FillPacketHeader increments packet_number_.
+ FillPacketHeader(&header);
+
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+ size_t length = framer_->BuildPaddedPathChallengePacket(
+ header, buffer.get(), max_plaintext_size_, payload, random_,
+ packet_.encryption_level);
+ DCHECK(length);
+
+ const size_t encrypted_length = framer_->EncryptInPlace(
+ packet_.encryption_level, packet_.packet_number,
+ GetStartOfEncryptedData(framer_->transport_version(), header), length,
+ kMaxOutgoingPacketSize, buffer.get());
+ DCHECK(encrypted_length);
+
+ OwningSerializedPacketPointer serialize_packet(new SerializedPacket(
+ header.packet_number, header.packet_number_length, buffer.release(),
+ encrypted_length, /*has_ack=*/false, /*has_stop_waiting=*/false));
+
+ serialize_packet->encryption_level = packet_.encryption_level;
+ serialize_packet->transmission_type = NOT_RETRANSMISSION;
+
+ return serialize_packet;
+}
+
+OwningSerializedPacketPointer
+QuicPacketCreator::SerializePathResponseConnectivityProbingPacket(
+ const QuicDeque<QuicPathFrameBuffer>& payloads,
+ const bool is_padded) {
+ QUIC_BUG_IF(framer_->transport_version() != QUIC_VERSION_99)
+ << "Must be version 99 to serialize path response connectivity probe, is "
+ "version "
+ << framer_->transport_version();
+ QuicPacketHeader header;
+ // FillPacketHeader increments packet_number_.
+ FillPacketHeader(&header);
+
+ std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+ size_t length = framer_->BuildPathResponsePacket(
+ header, buffer.get(), max_plaintext_size_, payloads, is_padded,
+ packet_.encryption_level);
+ DCHECK(length);
+
+ const size_t encrypted_length = framer_->EncryptInPlace(
+ packet_.encryption_level, packet_.packet_number,
+ GetStartOfEncryptedData(framer_->transport_version(), header), length,
+ kMaxOutgoingPacketSize, buffer.get());
+ DCHECK(encrypted_length);
+
+ OwningSerializedPacketPointer serialize_packet(new SerializedPacket(
+ header.packet_number, header.packet_number_length, buffer.release(),
+ encrypted_length, /*has_ack=*/false, /*has_stop_waiting=*/false));
+
+ serialize_packet->encryption_level = packet_.encryption_level;
+ serialize_packet->transmission_type = NOT_RETRANSMISSION;
+
+ return serialize_packet;
+}
+
+// TODO(b/74062209): Make this a public method of framer?
+SerializedPacket QuicPacketCreator::NoPacket() {
+ return SerializedPacket(QuicPacketNumber(), PACKET_1BYTE_PACKET_NUMBER,
+ nullptr, 0, false, false);
+}
+
+QuicConnectionIdIncluded QuicPacketCreator::GetDestinationConnectionIdIncluded()
+ const {
+ if (framer_->transport_version() > QUIC_VERSION_43) {
+ // Packets sent by client always include destination connection ID, and
+ // those sent by the server do not include destination connection ID.
+ return framer_->perspective() == Perspective::IS_CLIENT
+ ? CONNECTION_ID_PRESENT
+ : CONNECTION_ID_ABSENT;
+ }
+ return connection_id_included_;
+}
+
+QuicConnectionIdIncluded QuicPacketCreator::GetSourceConnectionIdIncluded()
+ const {
+ // Long header packets sent by server include source connection ID.
+ if (HasIetfLongHeader() && framer_->perspective() == Perspective::IS_SERVER) {
+ return CONNECTION_ID_PRESENT;
+ }
+ return CONNECTION_ID_ABSENT;
+}
+
+QuicConnectionIdLength QuicPacketCreator::GetDestinationConnectionIdLength()
+ const {
+ DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id_,
+ transport_version()));
+ return GetDestinationConnectionIdIncluded() == CONNECTION_ID_PRESENT
+ ? static_cast<QuicConnectionIdLength>(connection_id_.length())
+ : PACKET_0BYTE_CONNECTION_ID;
+}
+
+QuicConnectionIdLength QuicPacketCreator::GetSourceConnectionIdLength() const {
+ DCHECK(QuicUtils::IsConnectionIdValidForVersion(connection_id_,
+ transport_version()));
+ return GetSourceConnectionIdIncluded() == CONNECTION_ID_PRESENT
+ ? static_cast<QuicConnectionIdLength>(connection_id_.length())
+ : PACKET_0BYTE_CONNECTION_ID;
+}
+
+QuicPacketNumberLength QuicPacketCreator::GetPacketNumberLength() const {
+ if (HasIetfLongHeader() && framer_->transport_version() != QUIC_VERSION_99) {
+ return PACKET_4BYTE_PACKET_NUMBER;
+ }
+ return packet_.packet_number_length;
+}
+
+QuicVariableLengthIntegerLength QuicPacketCreator::GetRetryTokenLengthLength()
+ const {
+ if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) &&
+ HasIetfLongHeader() &&
+ EncryptionlevelToLongHeaderType(packet_.encryption_level) == INITIAL) {
+ return QuicDataWriter::GetVarInt62Len(GetRetryToken().length());
+ }
+ return VARIABLE_LENGTH_INTEGER_LENGTH_0;
+}
+
+QuicStringPiece QuicPacketCreator::GetRetryToken() const {
+ return retry_token_;
+}
+
+void QuicPacketCreator::SetRetryToken(QuicStringPiece retry_token) {
+ retry_token_ = std::string(retry_token);
+}
+
+QuicVariableLengthIntegerLength QuicPacketCreator::GetLengthLength() const {
+ if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) &&
+ HasIetfLongHeader()) {
+ QuicLongHeaderType long_header_type =
+ EncryptionlevelToLongHeaderType(packet_.encryption_level);
+ if (long_header_type == INITIAL || long_header_type == ZERO_RTT_PROTECTED ||
+ long_header_type == HANDSHAKE) {
+ return VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+ }
+ return VARIABLE_LENGTH_INTEGER_LENGTH_0;
+}
+
+void QuicPacketCreator::FillPacketHeader(QuicPacketHeader* header) {
+ header->destination_connection_id = connection_id_;
+ header->destination_connection_id_included =
+ GetDestinationConnectionIdIncluded();
+ header->source_connection_id = connection_id_;
+ header->source_connection_id_included = GetSourceConnectionIdIncluded();
+ header->reset_flag = false;
+ header->version_flag = IncludeVersionInHeader();
+ if (IncludeNonceInPublicHeader()) {
+ DCHECK_EQ(Perspective::IS_SERVER, framer_->perspective());
+ header->nonce = &diversification_nonce_;
+ } else {
+ header->nonce = nullptr;
+ }
+ if (!packet_.packet_number.IsInitialized()) {
+ packet_.packet_number = framer_->first_sending_packet_number();
+ } else {
+ ++packet_.packet_number;
+ }
+ header->packet_number = packet_.packet_number;
+ header->packet_number_length = GetPacketNumberLength();
+ header->retry_token_length_length = GetRetryTokenLengthLength();
+ header->retry_token = GetRetryToken();
+ header->length_length = GetLengthLength();
+ header->remaining_packet_length = 0;
+ if (!HasIetfLongHeader()) {
+ return;
+ }
+ header->long_packet_type =
+ EncryptionlevelToLongHeaderType(packet_.encryption_level);
+}
+
+bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
+ bool save_retransmittable_frames,
+ TransmissionType transmission_type) {
+ QUIC_DVLOG(1) << ENDPOINT << "Adding frame with transmission type "
+ << transmission_type << ": " << frame;
+ if (frame.type == STREAM_FRAME &&
+ frame.stream_frame.stream_id !=
+ QuicUtils::GetCryptoStreamId(framer_->transport_version()) &&
+ packet_.encryption_level == ENCRYPTION_INITIAL) {
+ const std::string error_details =
+ "Cannot send stream data without encryption.";
+ QUIC_BUG << error_details;
+ delegate_->OnUnrecoverableError(
+ QUIC_ATTEMPT_TO_SEND_UNENCRYPTED_STREAM_DATA, error_details,
+ ConnectionCloseSource::FROM_SELF);
+ return false;
+ }
+ size_t frame_len = framer_->GetSerializedFrameLength(
+ frame, BytesFree(), queued_frames_.empty(),
+ /* last_frame_in_packet= */ true, GetPacketNumberLength());
+ if (frame_len == 0) {
+ // Current open packet is full.
+ Flush();
+ return false;
+ }
+ DCHECK_LT(0u, packet_size_);
+
+ packet_size_ += ExpansionOnNewFrame() + frame_len;
+
+ if (save_retransmittable_frames &&
+ QuicUtils::IsRetransmittableFrame(frame.type)) {
+ packet_.retransmittable_frames.push_back(frame);
+ queued_frames_.push_back(frame);
+ if (QuicUtils::IsHandshakeFrame(frame, framer_->transport_version())) {
+ packet_.has_crypto_handshake = IS_HANDSHAKE;
+ }
+ } else {
+ queued_frames_.push_back(frame);
+ }
+
+ if (frame.type == ACK_FRAME) {
+ packet_.has_ack = true;
+ packet_.largest_acked = LargestAcked(*frame.ack_frame);
+ }
+ if (frame.type == STOP_WAITING_FRAME) {
+ packet_.has_stop_waiting = true;
+ }
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnFrameAddedToPacket(frame);
+ }
+
+ // Packet transmission type is determined by the last added retransmittable
+ // frame.
+ if (ShouldSetTransmissionTypeForNextFrame() &&
+ QuicUtils::IsRetransmittableFrame(frame.type)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_set_transmission_type_for_next_frame, 2,
+ 2);
+ packet_.transmission_type = transmission_type;
+ }
+ return true;
+}
+
+void QuicPacketCreator::MaybeAddPadding() {
+ // The current packet should have no padding bytes because padding is only
+ // added when this method is called just before the packet is serialized.
+ DCHECK_EQ(0, packet_.num_padding_bytes);
+ if (BytesFree() == 0) {
+ // Don't pad full packets.
+ return;
+ }
+
+ if (packet_.transmission_type == PROBING_RETRANSMISSION) {
+ needs_full_padding_ = true;
+ }
+
+ if (!needs_full_padding_ && pending_padding_bytes_ == 0) {
+ // Do not need padding.
+ return;
+ }
+
+ if (needs_full_padding_) {
+ // Full padding does not consume pending padding bytes.
+ packet_.num_padding_bytes = -1;
+ } else {
+ packet_.num_padding_bytes =
+ std::min<int16_t>(pending_padding_bytes_, BytesFree());
+ pending_padding_bytes_ -= packet_.num_padding_bytes;
+ }
+
+ bool success =
+ AddFrame(QuicFrame(QuicPaddingFrame(packet_.num_padding_bytes)), false,
+ packet_.transmission_type);
+ DCHECK(success);
+}
+
+bool QuicPacketCreator::IncludeNonceInPublicHeader() const {
+ return have_diversification_nonce_ &&
+ packet_.encryption_level == ENCRYPTION_ZERO_RTT;
+}
+
+bool QuicPacketCreator::IncludeVersionInHeader() const {
+ if (framer_->transport_version() > QUIC_VERSION_43) {
+ return packet_.encryption_level < ENCRYPTION_FORWARD_SECURE;
+ }
+ return send_version_in_packet_;
+}
+
+void QuicPacketCreator::AddPendingPadding(QuicByteCount size) {
+ pending_padding_bytes_ += size;
+}
+
+bool QuicPacketCreator::StreamFrameIsClientHello(
+ const QuicStreamFrame& frame) const {
+ if (framer_->perspective() == Perspective::IS_SERVER ||
+ frame.stream_id !=
+ QuicUtils::GetCryptoStreamId(framer_->transport_version())) {
+ return false;
+ }
+ // The ClientHello is always sent with INITIAL encryption.
+ return packet_.encryption_level == ENCRYPTION_INITIAL;
+}
+
+void QuicPacketCreator::SetConnectionIdIncluded(
+ QuicConnectionIdIncluded connection_id_included) {
+ DCHECK(connection_id_included == CONNECTION_ID_PRESENT ||
+ connection_id_included == CONNECTION_ID_ABSENT);
+ DCHECK(framer_->perspective() == Perspective::IS_SERVER ||
+ connection_id_included != CONNECTION_ID_ABSENT);
+ connection_id_included_ = connection_id_included;
+}
+
+void QuicPacketCreator::SetConnectionId(QuicConnectionId connection_id) {
+ connection_id_ = connection_id;
+}
+
+void QuicPacketCreator::SetTransmissionType(TransmissionType type) {
+ DCHECK(can_set_transmission_type_);
+
+ if (!ShouldSetTransmissionTypeForNextFrame()) {
+ QUIC_DVLOG_IF(1, type != packet_.transmission_type)
+ << ENDPOINT << "Setting Transmission type to "
+ << QuicUtils::TransmissionTypeToString(type);
+
+ packet_.transmission_type = type;
+ }
+}
+
+QuicPacketLength QuicPacketCreator::GetCurrentLargestMessagePayload() const {
+ if (framer_->transport_version() <= QUIC_VERSION_44) {
+ return 0;
+ }
+ const size_t packet_header_size = GetPacketHeaderSize(
+ framer_->transport_version(), GetDestinationConnectionIdLength(),
+ GetSourceConnectionIdLength(), IncludeVersionInHeader(),
+ IncludeNonceInPublicHeader(), GetPacketNumberLength(),
+ // No Retry token on packets containing application data.
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, GetLengthLength());
+ // This is the largest possible message payload when the length field is
+ // omitted.
+ return max_plaintext_size_ -
+ std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize);
+}
+
+QuicPacketLength QuicPacketCreator::GetGuaranteedLargestMessagePayload() const {
+ if (framer_->transport_version() <= QUIC_VERSION_44) {
+ return 0;
+ }
+ // QUIC Crypto server packets may include a diversification nonce.
+ const bool may_include_nonce =
+ framer_->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO &&
+ framer_->perspective() == Perspective::IS_SERVER;
+ // IETF QUIC long headers include a length on client 0RTT packets.
+ QuicVariableLengthIntegerLength length_length =
+ framer_->perspective() == Perspective::IS_CLIENT
+ ? VARIABLE_LENGTH_INTEGER_LENGTH_2
+ : VARIABLE_LENGTH_INTEGER_LENGTH_0;
+ const size_t packet_header_size = GetPacketHeaderSize(
+ framer_->transport_version(), GetDestinationConnectionIdLength(),
+ // Assume CID lengths don't change, but version may be present.
+ GetSourceConnectionIdLength(), kIncludeVersion, may_include_nonce,
+ PACKET_4BYTE_PACKET_NUMBER,
+ // No Retry token on packets containing application data.
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, length_length);
+ // This is the largest possible message payload when the length field is
+ // omitted.
+ const QuicPacketLength largest_payload =
+ max_plaintext_size_ -
+ std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize);
+ // This must always be less than or equal to GetCurrentLargestMessagePayload.
+ DCHECK_LE(largest_payload, GetCurrentLargestMessagePayload());
+ return largest_payload;
+}
+
+bool QuicPacketCreator::HasIetfLongHeader() const {
+ return framer_->transport_version() > QUIC_VERSION_43 &&
+ packet_.encryption_level < ENCRYPTION_FORWARD_SECURE;
+}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic
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
new file mode 100644
index 00000000000..85e00917a15
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator.h
@@ -0,0 +1,421 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Accumulates frames for the next packet until more frames no longer fit or
+// it's time to create a packet from them.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_CREATOR_H_
+#define QUICHE_QUIC_CORE_QUIC_PACKET_CREATOR_H_
+
+#include <cstddef>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection_close_delegate_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+namespace test {
+class QuicPacketCreatorPeer;
+}
+
+class QUIC_EXPORT_PRIVATE QuicPacketCreator {
+ public:
+ // A delegate interface for further processing serialized packet.
+ class QUIC_EXPORT_PRIVATE DelegateInterface
+ : public QuicConnectionCloseDelegateInterface {
+ public:
+ ~DelegateInterface() override {}
+ // Get a buffer of kMaxOutgoingPacketSize bytes to serialize the next
+ // packet. If return nullptr, QuicPacketCreator will serialize on a stack
+ // buffer.
+ virtual char* GetPacketBuffer() = 0;
+ // Called when a packet is serialized. Delegate does not take the ownership
+ // of |serialized_packet|, but takes ownership of any frames it removes
+ // from |packet.retransmittable_frames|.
+ virtual void OnSerializedPacket(SerializedPacket* serialized_packet) = 0;
+ };
+
+ // Interface which gets callbacks from the QuicPacketCreator at interesting
+ // points. Implementations must not mutate the state of the creator
+ // as a result of these callbacks.
+ class QUIC_EXPORT_PRIVATE DebugDelegate {
+ public:
+ virtual ~DebugDelegate() {}
+
+ // Called when a frame has been added to the current packet.
+ virtual void OnFrameAddedToPacket(const QuicFrame& frame) {}
+ };
+
+ QuicPacketCreator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ DelegateInterface* delegate);
+ QuicPacketCreator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random,
+ DelegateInterface* delegate);
+ QuicPacketCreator(const QuicPacketCreator&) = delete;
+ QuicPacketCreator& operator=(const QuicPacketCreator&) = delete;
+
+ ~QuicPacketCreator();
+
+ // Makes the framer not serialize the protocol version in sent packets.
+ void StopSendingVersion();
+
+ // SetDiversificationNonce sets the nonce that will be sent in each public
+ // header of packets encrypted at the initial encryption level. Should only
+ // be called by servers.
+ void SetDiversificationNonce(const DiversificationNonce& nonce);
+
+ // Update the packet number length to use in future packets as soon as it
+ // can be safely changed.
+ // TODO(fayang): Directly set packet number length instead of compute it in
+ // creator.
+ void UpdatePacketNumberLength(QuicPacketNumber least_packet_awaited_by_peer,
+ QuicPacketCount max_packets_in_flight);
+
+ // The overhead the framing will add for a packet with one frame.
+ static size_t StreamFramePacketOverhead(
+ QuicTransportVersion version,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool include_version,
+ bool include_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicVariableLengthIntegerLength length_length,
+ QuicStreamOffset offset);
+
+ // Returns false and flushes all pending frames if current open packet is
+ // full.
+ // If current packet is not full, creates a stream frame that fits into the
+ // open packet and adds it to the packet.
+ bool ConsumeData(QuicStreamId id,
+ size_t data_length,
+ QuicStreamOffset offset,
+ bool fin,
+ bool needs_full_padding,
+ TransmissionType transmission_type,
+ QuicFrame* frame);
+
+ // Creates a CRYPTO frame that fits into the current packet (which must be
+ // empty) and adds it to the packet.
+ bool ConsumeCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset,
+ TransmissionType transmission_type,
+ QuicFrame* frame);
+
+ // Returns true if current open packet can accommodate more stream frames of
+ // stream |id| at |offset| and data length |data_size|, false otherwise.
+ bool HasRoomForStreamFrame(QuicStreamId id,
+ QuicStreamOffset offset,
+ size_t data_size);
+
+ // Returns true if current open packet can accommodate a message frame of
+ // |length|.
+ bool HasRoomForMessageFrame(QuicByteCount length);
+
+ // Re-serializes frames with the original packet's packet number length.
+ // Used for retransmitting packets to ensure they aren't too long.
+ void ReserializeAllFrames(const QuicPendingRetransmission& retransmission,
+ char* buffer,
+ size_t buffer_len);
+
+ // Serializes all added frames into a single packet and invokes the delegate_
+ // to further process the SerializedPacket.
+ void Flush();
+
+ // Optimized method to create a QuicStreamFrame and serialize it. Adds the
+ // QuicStreamFrame to the returned SerializedPacket. Sets
+ // |num_bytes_consumed| to the number of bytes consumed to create the
+ // QuicStreamFrame.
+ void CreateAndSerializeStreamFrame(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset iov_offset,
+ QuicStreamOffset stream_offset,
+ bool fin,
+ TransmissionType transmission_type,
+ size_t* num_bytes_consumed);
+
+ // Returns true if there are frames pending to be serialized.
+ bool HasPendingFrames() const;
+
+ // Returns true if there are retransmittable frames pending to be serialized.
+ bool HasPendingRetransmittableFrames() const;
+
+ // Returns true if there are stream frames for |id| pending to be serialized.
+ bool HasPendingStreamFramesOfStream(QuicStreamId id) const;
+
+ // Returns the number of bytes which are available to be used by additional
+ // frames in the packet. Since stream frames are slightly smaller when they
+ // are the last frame in a packet, this method will return a different
+ // value than max_packet_size - PacketSize(), in this case.
+ size_t BytesFree();
+
+ // Returns the number of bytes that the packet will expand by if a new frame
+ // is added to the packet. If the last frame was a stream frame, it will
+ // expand slightly when a new frame is added, and this method returns the
+ // amount of expected expansion.
+ size_t ExpansionOnNewFrame() const;
+
+ // Returns the number of bytes in the current packet, including the header,
+ // if serialized with the current frames. Adding a frame to the packet
+ // may change the serialized length of existing frames, as per the comment
+ // in BytesFree.
+ size_t PacketSize();
+
+ // Tries to add |frame| to the packet creator's list of frames to be
+ // serialized. If the frame does not fit into the current packet, flushes the
+ // packet and returns false.
+ bool AddSavedFrame(const QuicFrame& frame,
+ TransmissionType transmission_type);
+
+ // Identical to AddSavedFrame, but allows the frame to be padded.
+ bool AddPaddedSavedFrame(const QuicFrame& frame,
+ TransmissionType transmission_type);
+
+ // Creates a version negotiation packet which supports |supported_versions|.
+ std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket(
+ bool ietf_quic,
+ const ParsedQuicVersionVector& supported_versions);
+
+ // Creates a connectivity probing packet for versions prior to version 99.
+ OwningSerializedPacketPointer SerializeConnectivityProbingPacket();
+
+ // Create connectivity probing request and response packets using PATH
+ // CHALLENGE and PATH RESPONSE frames, respectively, for version 99/IETF QUIC.
+ // SerializePathChallengeConnectivityProbingPacket will pad the packet to be
+ // MTU bytes long.
+ OwningSerializedPacketPointer SerializePathChallengeConnectivityProbingPacket(
+ QuicPathFrameBuffer* payload);
+
+ // If |is_padded| is true then SerializePathResponseConnectivityProbingPacket
+ // will pad the packet to be MTU bytes long, else it will not pad the packet.
+ // |payloads| is cleared.
+ OwningSerializedPacketPointer SerializePathResponseConnectivityProbingPacket(
+ const QuicDeque<QuicPathFrameBuffer>& payloads,
+ const bool is_padded);
+
+ // Returns a dummy packet that is valid but contains no useful information.
+ static SerializedPacket NoPacket();
+
+ // Returns length of destination connection ID to send over the wire.
+ QuicConnectionIdLength GetDestinationConnectionIdLength() const;
+
+ // Returns length of source connection ID to send over the wire.
+ QuicConnectionIdLength GetSourceConnectionIdLength() const;
+
+ // Sets whether the connection ID should be sent over the wire.
+ void SetConnectionIdIncluded(QuicConnectionIdIncluded connection_id_included);
+
+ // Update the connection ID used in outgoing packets.
+ void SetConnectionId(QuicConnectionId connection_id);
+
+ // Sets the encryption level that will be applied to new packets.
+ void set_encryption_level(EncryptionLevel level) {
+ packet_.encryption_level = level;
+ }
+
+ // packet number of the last created packet, or 0 if no packets have been
+ // created.
+ QuicPacketNumber packet_number() const { return packet_.packet_number; }
+
+ QuicByteCount max_packet_length() const { return max_packet_length_; }
+
+ bool has_ack() const { return packet_.has_ack; }
+
+ bool has_stop_waiting() const { return packet_.has_stop_waiting; }
+
+ // Sets the encrypter to use for the encryption level and updates the max
+ // plaintext size.
+ void SetEncrypter(EncryptionLevel level,
+ std::unique_ptr<QuicEncrypter> encrypter);
+
+ // Indicates whether the packet creator is in a state where it can change
+ // current maximum packet length.
+ bool CanSetMaxPacketLength() const;
+
+ // Sets the maximum packet length.
+ void SetMaxPacketLength(QuicByteCount length);
+
+ // Increases pending_padding_bytes by |size|. Pending padding will be sent by
+ // MaybeAddPadding().
+ void AddPendingPadding(QuicByteCount size);
+
+ // Sets transmission type of next constructed packets.
+ void SetTransmissionType(TransmissionType type);
+
+ // Sets the retry token to be sent over the wire in v99 IETF Initial packets.
+ void SetRetryToken(QuicStringPiece retry_token);
+
+ // Returns the largest payload that will fit into a single MESSAGE frame.
+ QuicPacketLength GetCurrentLargestMessagePayload() const;
+ // Returns the largest payload that will fit into a single MESSAGE frame at
+ // any point during the connection. This assumes the version and
+ // connection ID lengths do not change.
+ QuicPacketLength GetGuaranteedLargestMessagePayload() const;
+
+ void set_debug_delegate(DebugDelegate* debug_delegate) {
+ debug_delegate_ = debug_delegate;
+ }
+
+ void set_can_set_transmission_type(bool can_set_transmission_type) {
+ can_set_transmission_type_ = can_set_transmission_type;
+ }
+
+ bool ShouldSetTransmissionTypeForNextFrame() const {
+ return can_set_transmission_type_ && set_transmission_type_for_next_frame_;
+ }
+
+ QuicByteCount pending_padding_bytes() const { return pending_padding_bytes_; }
+
+ QuicTransportVersion transport_version() const {
+ return framer_->transport_version();
+ }
+
+ private:
+ friend class test::QuicPacketCreatorPeer;
+
+ // Creates a stream frame which fits into the current open packet. If
+ // |data_size| is 0 and fin is true, the expected behavior is to consume
+ // the fin.
+ void CreateStreamFrame(QuicStreamId id,
+ size_t data_size,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicFrame* frame);
+
+ // Creates a CRYPTO frame which fits into the current open packet. Returns
+ // false if there isn't enough room in the current open packet for a CRYPTO
+ // frame, and true if there is.
+ bool CreateCryptoFrame(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset,
+ QuicFrame* frame);
+
+ void FillPacketHeader(QuicPacketHeader* header);
+
+ // Adds a |frame| if there is space and returns false and flushes all pending
+ // frames if there isn't room. If |save_retransmittable_frames| is true,
+ // saves the |frame| in the next SerializedPacket.
+ bool AddFrame(const QuicFrame& frame,
+ bool save_retransmittable_frames,
+ TransmissionType transmission_type);
+
+ // Adds a padding frame to the current packet (if there is space) when (1)
+ // current packet needs full padding or (2) there are pending paddings.
+ void MaybeAddPadding();
+
+ // Serializes all frames which have been added and adds any which should be
+ // retransmitted to packet_.retransmittable_frames. All frames must fit into
+ // a single packet.
+ // Fails if |buffer_len| isn't long enough for the encrypted packet.
+ void SerializePacket(char* encrypted_buffer, size_t buffer_len);
+
+ // Called after a new SerialiedPacket is created to call the delegate's
+ // OnSerializedPacket and reset state.
+ void OnSerializedPacket();
+
+ // Clears all fields of packet_ that should be cleared between serializations.
+ void ClearPacket();
+
+ // Returns true if a diversification nonce should be included in the current
+ // packet's header.
+ bool IncludeNonceInPublicHeader() const;
+
+ // Returns true if version should be included in current packet's header.
+ bool IncludeVersionInHeader() const;
+
+ // Returns length of packet number to send over the wire.
+ // packet_.packet_number_length should never be read directly, use this
+ // function instead.
+ QuicPacketNumberLength GetPacketNumberLength() const;
+
+ // Returns whether the destination connection ID is sent over the wire.
+ QuicConnectionIdIncluded GetDestinationConnectionIdIncluded() const;
+
+ // Returns whether the source connection ID is sent over the wire.
+ QuicConnectionIdIncluded GetSourceConnectionIdIncluded() const;
+
+ // Returns length of the retry token variable length integer to send over the
+ // wire. Is non-zero for v99 IETF Initial packets.
+ QuicVariableLengthIntegerLength GetRetryTokenLengthLength() const;
+
+ // Returns the retry token to send over the wire, only sent in
+ // v99 IETF Initial packets.
+ QuicStringPiece GetRetryToken() const;
+
+ // Returns length of the length variable length integer to send over the
+ // wire. Is non-zero for v99 IETF Initial, 0-RTT or Handshake packets.
+ QuicVariableLengthIntegerLength GetLengthLength() const;
+
+ // Returns true if |frame| is a ClientHello.
+ bool StreamFrameIsClientHello(const QuicStreamFrame& frame) const;
+
+ // Returns true if packet under construction has IETF long header.
+ bool HasIetfLongHeader() const;
+
+ // Does not own these delegates or the framer.
+ DelegateInterface* delegate_;
+ DebugDelegate* debug_delegate_;
+ QuicFramer* framer_;
+ QuicRandom* random_;
+
+ // Controls whether version should be included while serializing the packet.
+ // send_version_in_packet_ should never be read directly, use
+ // IncludeVersionInHeader() instead.
+ bool send_version_in_packet_;
+ // If true, then |diversification_nonce_| will be included in the header of
+ // all packets created at the initial encryption level.
+ bool have_diversification_nonce_;
+ DiversificationNonce diversification_nonce_;
+ // Maximum length including headers and encryption (UDP payload length.)
+ QuicByteCount max_packet_length_;
+ size_t max_plaintext_size_;
+ // Whether the connection_id is sent over the wire.
+ QuicConnectionIdIncluded connection_id_included_;
+
+ // Frames to be added to the next SerializedPacket
+ QuicFrames queued_frames_;
+
+ // packet_size should never be read directly, use PacketSize() instead.
+ // TODO(ianswett): Move packet_size_ into SerializedPacket once
+ // QuicEncryptedPacket has been flattened into SerializedPacket.
+ size_t packet_size_;
+ QuicConnectionId connection_id_;
+
+ // Packet used to invoke OnSerializedPacket.
+ SerializedPacket packet_;
+
+ // Retry token to send over the wire in v99 IETF Initial packets.
+ std::string retry_token_;
+
+ // Pending padding bytes to send. Pending padding bytes will be sent in next
+ // packet(s) (after all other frames) if current constructed packet does not
+ // have room to send all of them.
+ QuicByteCount pending_padding_bytes_;
+
+ // Indicates whether current constructed packet needs full padding to max
+ // packet size. Please note, full padding does not consume pending padding
+ // bytes.
+ bool needs_full_padding_;
+
+ // If true, packet_'s transmission type is only set by
+ // SetPacketTransmissionType and does not get cleared in ClearPacket.
+ bool can_set_transmission_type_;
+
+ // Latched value of --quic_set_transmission_type_for_next_frame. Don't use
+ // this variable directly, use ShouldSetTransmissionTypeForNextFrame instead.
+ bool set_transmission_type_for_next_frame_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PACKET_CREATOR_H_
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
new file mode 100644
index 00000000000..2b71b06e684
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_creator_test.cc
@@ -0,0 +1,1804 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+
+#include <cstdint>
+#include <memory>
+#include <ostream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+// Run tests with combinations of {ParsedQuicVersion,
+// ToggleVersionSerialization}.
+struct TestParams {
+ TestParams(ParsedQuicVersion version, bool version_serialization)
+ : version(version), version_serialization(version_serialization) {}
+
+ friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+ os << "{ version: " << ParsedQuicVersionToString(p.version)
+ << " include version: " << p.version_serialization << " }";
+ return os;
+ }
+
+ ParsedQuicVersion version;
+ bool version_serialization;
+};
+
+// Constructs various test permutations.
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ ParsedQuicVersionVector all_supported_versions = AllSupportedVersions();
+ for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+ params.push_back(TestParams(all_supported_versions[i], true));
+ params.push_back(TestParams(all_supported_versions[i], false));
+ }
+ return params;
+}
+
+class MockDebugDelegate : public QuicPacketCreator::DebugDelegate {
+ public:
+ ~MockDebugDelegate() override = default;
+
+ MOCK_METHOD1(OnFrameAddedToPacket, void(const QuicFrame& frame));
+};
+
+class TestPacketCreator : public QuicPacketCreator {
+ public:
+ TestPacketCreator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ DelegateInterface* delegate,
+ SimpleDataProducer* producer)
+ : QuicPacketCreator(connection_id, framer, delegate),
+ producer_(producer),
+ version_(framer->transport_version()) {}
+
+ bool ConsumeData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ size_t total_length,
+ size_t iov_offset,
+ QuicStreamOffset offset,
+ bool fin,
+ bool needs_full_padding,
+ TransmissionType transmission_type,
+ QuicFrame* frame) {
+ // Save data before data is consumed.
+ QuicByteCount data_length = total_length - iov_offset;
+ if (data_length > 0) {
+ producer_->SaveStreamData(id, iov, iov_count, iov_offset, data_length);
+ }
+ return QuicPacketCreator::ConsumeData(id, data_length - iov_offset, offset,
+ fin, needs_full_padding,
+ transmission_type, frame);
+ }
+
+ void StopSendingVersion() {
+ if (version_ > QUIC_VERSION_43) {
+ set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ return;
+ }
+ QuicPacketCreator::StopSendingVersion();
+ }
+
+ SimpleDataProducer* producer_;
+ QuicTransportVersion version_;
+};
+
+class QuicPacketCreatorTest : public QuicTestWithParam<TestParams> {
+ public:
+ void ClearSerializedPacketForTests(SerializedPacket* serialized_packet) {
+ if (serialized_packet == nullptr) {
+ return;
+ }
+ ClearSerializedPacket(serialized_packet);
+ }
+
+ void SaveSerializedPacket(SerializedPacket* serialized_packet) {
+ if (serialized_packet == nullptr) {
+ return;
+ }
+ delete[] serialized_packet_.encrypted_buffer;
+ serialized_packet_ = *serialized_packet;
+ serialized_packet_.encrypted_buffer = CopyBuffer(*serialized_packet);
+ serialized_packet->retransmittable_frames.clear();
+ }
+
+ void DeleteSerializedPacket() {
+ delete[] serialized_packet_.encrypted_buffer;
+ serialized_packet_.encrypted_buffer = nullptr;
+ ClearSerializedPacket(&serialized_packet_);
+ }
+
+ protected:
+ QuicPacketCreatorTest()
+ : connection_id_(TestConnectionId(2)),
+ server_framer_(SupportedVersions(GetParam().version),
+ QuicTime::Zero(),
+ Perspective::IS_SERVER,
+ connection_id_.length()),
+ client_framer_(SupportedVersions(GetParam().version),
+ QuicTime::Zero(),
+ Perspective::IS_CLIENT,
+ connection_id_.length()),
+ data_("foo"),
+ creator_(connection_id_, &client_framer_, &delegate_, &producer_),
+ serialized_packet_(creator_.NoPacket()) {
+ EXPECT_CALL(delegate_, GetPacketBuffer()).WillRepeatedly(Return(nullptr));
+ creator_.SetEncrypter(ENCRYPTION_HANDSHAKE, QuicMakeUnique<NullEncrypter>(
+ Perspective::IS_CLIENT));
+ creator_.SetEncrypter(ENCRYPTION_ZERO_RTT, QuicMakeUnique<NullEncrypter>(
+ Perspective::IS_CLIENT));
+ creator_.SetEncrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT));
+ client_framer_.set_visitor(&framer_visitor_);
+ server_framer_.set_visitor(&framer_visitor_);
+ client_framer_.set_data_producer(&producer_);
+ if (server_framer_.version().KnowsWhichDecrypterToUse()) {
+ server_framer_.InstallDecrypter(
+ ENCRYPTION_ZERO_RTT,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_SERVER));
+ server_framer_.InstallDecrypter(
+ ENCRYPTION_HANDSHAKE,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_SERVER));
+ server_framer_.InstallDecrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_SERVER));
+ }
+ }
+
+ ~QuicPacketCreatorTest() override {
+ delete[] serialized_packet_.encrypted_buffer;
+ ClearSerializedPacket(&serialized_packet_);
+ }
+
+ SerializedPacket SerializeAllFrames(const QuicFrames& frames) {
+ SerializedPacket packet = QuicPacketCreatorPeer::SerializeAllFrames(
+ &creator_, frames, buffer_, kMaxOutgoingPacketSize);
+ EXPECT_EQ(QuicPacketCreatorPeer::GetEncryptionLevel(&creator_),
+ packet.encryption_level);
+ return packet;
+ }
+
+ void ProcessPacket(const SerializedPacket& packet) {
+ QuicEncryptedPacket encrypted_packet(packet.encrypted_buffer,
+ packet.encrypted_length);
+ server_framer_.ProcessPacket(encrypted_packet);
+ }
+
+ void CheckStreamFrame(const QuicFrame& frame,
+ QuicStreamId stream_id,
+ const std::string& data,
+ QuicStreamOffset offset,
+ bool fin) {
+ EXPECT_EQ(STREAM_FRAME, frame.type);
+ EXPECT_EQ(stream_id, frame.stream_frame.stream_id);
+ char buf[kMaxOutgoingPacketSize];
+ QuicDataWriter writer(kMaxOutgoingPacketSize, buf, HOST_BYTE_ORDER);
+ if (frame.stream_frame.data_length > 0) {
+ producer_.WriteStreamData(stream_id, frame.stream_frame.offset,
+ frame.stream_frame.data_length, &writer);
+ }
+ EXPECT_EQ(data, QuicStringPiece(buf, frame.stream_frame.data_length));
+ EXPECT_EQ(offset, frame.stream_frame.offset);
+ EXPECT_EQ(fin, frame.stream_frame.fin);
+ }
+
+ // Returns the number of bytes consumed by the header of packet, including
+ // the version.
+ size_t GetPacketHeaderOverhead(QuicTransportVersion version) {
+ return GetPacketHeaderSize(
+ version, creator_.GetDestinationConnectionIdLength(),
+ creator_.GetSourceConnectionIdLength(),
+ QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
+ !kIncludeDiversificationNonce,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_),
+ QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_), 0,
+ QuicPacketCreatorPeer::GetLengthLength(&creator_));
+ }
+
+ // Returns the number of bytes of overhead that will be added to a packet
+ // of maximum length.
+ size_t GetEncryptionOverhead() {
+ return creator_.max_packet_length() -
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+ }
+
+ // Returns the number of bytes consumed by the non-data fields of a stream
+ // frame, assuming it is the last frame in the packet
+ size_t GetStreamFrameOverhead(QuicTransportVersion version) {
+ return QuicFramer::GetMinStreamFrameSize(
+ version, GetNthClientInitiatedStreamId(1), kOffset, true,
+ /* data_length= */ 0);
+ }
+
+ QuicPendingRetransmission CreateRetransmission(
+ const QuicFrames& retransmittable_frames,
+ bool has_crypto_handshake,
+ int num_padding_bytes,
+ EncryptionLevel encryption_level,
+ QuicPacketNumberLength packet_number_length) {
+ return QuicPendingRetransmission(QuicPacketNumber(1u), NOT_RETRANSMISSION,
+ retransmittable_frames,
+ has_crypto_handshake, num_padding_bytes,
+ encryption_level, packet_number_length);
+ }
+
+ bool IsDefaultTestConfiguration() {
+ TestParams p = GetParam();
+ return p.version == AllSupportedVersions()[0] && p.version_serialization;
+ }
+
+ QuicStreamId GetNthClientInitiatedStreamId(int n) const {
+ return QuicUtils::GetHeadersStreamId(creator_.transport_version()) + n * 2;
+ }
+
+ static const QuicStreamOffset kOffset = 0u;
+
+ char buffer_[kMaxOutgoingPacketSize];
+ QuicConnectionId connection_id_;
+ QuicFrames frames_;
+ QuicFramer server_framer_;
+ QuicFramer client_framer_;
+ StrictMock<MockFramerVisitor> framer_visitor_;
+ StrictMock<MockPacketCreatorDelegate> delegate_;
+ std::string data_;
+ struct iovec iov_;
+ TestPacketCreator creator_;
+ SerializedPacket serialized_packet_;
+ SimpleDataProducer producer_;
+ SimpleBufferAllocator allocator_;
+};
+
+// Run all packet creator tests with all supported versions of QUIC, and with
+// and without version in the packet header, as well as doing a run for each
+// length of truncated connection id.
+INSTANTIATE_TEST_SUITE_P(QuicPacketCreatorTests,
+ QuicPacketCreatorTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicPacketCreatorTest, SerializeFrames) {
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+ creator_.set_encryption_level(level);
+ frames_.push_back(QuicFrame(new QuicAckFrame(InitAckFrame(1))));
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ if (level != ENCRYPTION_INITIAL) {
+ frames_.push_back(
+ QuicFrame(QuicStreamFrame(stream_id, false, 0u, QuicStringPiece())));
+ frames_.push_back(
+ QuicFrame(QuicStreamFrame(stream_id, true, 0u, QuicStringPiece())));
+ }
+ SerializedPacket serialized = SerializeAllFrames(frames_);
+ EXPECT_EQ(level, serialized.encryption_level);
+ delete frames_[0].ack_frame;
+ frames_.clear();
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnAckFrameStart(_, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(framer_visitor_,
+ OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2)))
+ .WillOnce(Return(true));
+ EXPECT_CALL(framer_visitor_, OnAckFrameEnd(QuicPacketNumber(1)))
+ .WillOnce(Return(true));
+ if (level != ENCRYPTION_INITIAL) {
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ }
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized);
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
+ if (client_framer_.transport_version() > QUIC_VERSION_43) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ }
+ // If the original packet number length, the current packet number
+ // length, and the configured send packet number length are different, the
+ // retransmit must sent with the original length and the others do not change.
+ QuicPacketCreatorPeer::SetPacketNumberLength(&creator_,
+ PACKET_2BYTE_PACKET_NUMBER);
+ QuicFrames frames;
+ std::string data("a");
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
+ /*fin=*/false, 0u, QuicStringPiece());
+ frames.push_back(QuicFrame(stream_frame));
+ } else {
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data);
+ frames.push_back(
+ QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length())));
+ }
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, true /* has_crypto_handshake */, -1 /* needs full padding */,
+ ENCRYPTION_INITIAL, PACKET_4BYTE_PACKET_NUMBER));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.ReserializeAllFrames(retransmission, buffer, kMaxOutgoingPacketSize);
+ // The packet number length is updated after every packet is sent,
+ // so there is no need to restore the old length after sending.
+ EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+ EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
+ serialized_packet_.packet_number_length);
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ } else {
+ EXPECT_CALL(framer_visitor_, OnCryptoFrame(_));
+ }
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized_packet_);
+ DeleteFrames(&frames);
+}
+
+TEST_P(QuicPacketCreatorTest, ReserializeCryptoFrameWithForwardSecurity) {
+ QuicFrames frames;
+ std::string data("a");
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
+ /*fin=*/false, 0u, QuicStringPiece());
+ frames.push_back(QuicFrame(stream_frame));
+ } else {
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data);
+ frames.push_back(
+ QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length())));
+ }
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, true /* has_crypto_handshake */, -1 /* needs full padding */,
+ ENCRYPTION_INITIAL,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.ReserializeAllFrames(retransmission, buffer, kMaxOutgoingPacketSize);
+ EXPECT_EQ(ENCRYPTION_INITIAL, serialized_packet_.encryption_level);
+ DeleteFrames(&frames);
+}
+
+TEST_P(QuicPacketCreatorTest, ReserializeFrameWithForwardSecurity) {
+ QuicStreamFrame stream_frame(0u, /*fin=*/false, 0u, QuicStringPiece());
+ QuicFrames frames;
+ frames.push_back(QuicFrame(stream_frame));
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, false /* has_crypto_handshake */, 0 /* no padding */,
+ ENCRYPTION_INITIAL,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.ReserializeAllFrames(retransmission, buffer, kMaxOutgoingPacketSize);
+ EXPECT_EQ(ENCRYPTION_FORWARD_SECURE, serialized_packet_.encryption_level);
+}
+
+TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPadding) {
+ QuicFrame frame;
+ std::string data = "fake handshake message data";
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ MakeIOVector(data, &iov_);
+ producer_.SaveStreamData(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_,
+ 1u, 0u, iov_.iov_len);
+ QuicPacketCreatorPeer::CreateStreamFrame(
+ &creator_,
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
+ iov_.iov_len, 0u, false, &frame);
+ } else {
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data);
+ EXPECT_TRUE(QuicPacketCreatorPeer::CreateCryptoFrame(
+ &creator_, ENCRYPTION_INITIAL, data.length(), 0, &frame));
+ }
+ QuicFrames frames;
+ frames.push_back(frame);
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, true /* has_crypto_handshake */, -1 /* needs full padding */,
+ ENCRYPTION_INITIAL,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.ReserializeAllFrames(retransmission, buffer, kMaxOutgoingPacketSize);
+ EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length);
+ DeleteFrames(&frames);
+}
+
+TEST_P(QuicPacketCreatorTest, DoNotRetransmitPendingPadding) {
+ QuicFrame frame;
+ std::string data = "fake message data";
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ MakeIOVector(data, &iov_);
+ producer_.SaveStreamData(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_,
+ 1u, 0u, iov_.iov_len);
+ QuicPacketCreatorPeer::CreateStreamFrame(
+ &creator_,
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
+ iov_.iov_len, 0u, false, &frame);
+ } else {
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data);
+ EXPECT_TRUE(QuicPacketCreatorPeer::CreateCryptoFrame(
+ &creator_, ENCRYPTION_INITIAL, data.length(), 0, &frame));
+ }
+
+ const int kNumPaddingBytes1 = 4;
+ int packet_size = 0;
+ {
+ QuicFrames frames;
+ frames.push_back(frame);
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, false /* has_crypto_handshake */,
+ kNumPaddingBytes1 /* padding bytes */, ENCRYPTION_INITIAL,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.ReserializeAllFrames(retransmission, buffer,
+ kMaxOutgoingPacketSize);
+ packet_size = serialized_packet_.encrypted_length;
+ }
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ EXPECT_CALL(framer_visitor_, OnCryptoFrame(_));
+ } else {
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ }
+ // Pending paddings are not retransmitted.
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(0);
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized_packet_);
+
+ const int kNumPaddingBytes2 = 44;
+ QuicFrames frames;
+ frames.push_back(frame);
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, false /* has_crypto_handshake */,
+ kNumPaddingBytes2 /* padding bytes */, ENCRYPTION_INITIAL,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.ReserializeAllFrames(retransmission, buffer, kMaxOutgoingPacketSize);
+
+ EXPECT_EQ(packet_size, serialized_packet_.encrypted_length);
+ DeleteFrames(&frames);
+}
+
+TEST_P(QuicPacketCreatorTest, ReserializeFramesWithFullPacketAndPadding) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ const size_t overhead =
+ GetPacketHeaderOverhead(client_framer_.transport_version()) +
+ GetEncryptionOverhead() +
+ GetStreamFrameOverhead(client_framer_.transport_version());
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ for (int delta = -5; delta <= 0; ++delta) {
+ std::string data(capacity + delta, 'A');
+ size_t bytes_free = 0 - delta;
+
+ QuicFrame frame;
+ SimpleDataProducer producer;
+ QuicPacketCreatorPeer::framer(&creator_)->set_data_producer(&producer);
+ MakeIOVector(data, &iov_);
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ producer.SaveStreamData(stream_id, &iov_, 1u, 0u, iov_.iov_len);
+ QuicPacketCreatorPeer::CreateStreamFrame(&creator_, stream_id, iov_.iov_len,
+ kOffset, false, &frame);
+ QuicFrames frames;
+ frames.push_back(frame);
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, false /* has_crypto_handshake */, -1 /* needs full padding */,
+ ENCRYPTION_FORWARD_SECURE,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.ReserializeAllFrames(retransmission, buffer,
+ kMaxOutgoingPacketSize);
+
+ // If there is not enough space in the packet to fit a padding frame
+ // (1 byte) and to expand the stream frame (another 2 bytes) the packet
+ // will not be padded.
+ if (bytes_free < 3) {
+ EXPECT_EQ(kDefaultMaxPacketSize - bytes_free,
+ serialized_packet_.encrypted_length);
+ } else {
+ EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length);
+ }
+
+ frames_.clear();
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) {
+ QuicConnectionCloseFrame frame(QUIC_NO_ERROR, "error");
+ if (GetParam().version.transport_version == QUIC_VERSION_99) {
+ frame.close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&frame));
+ SerializedPacket serialized = SerializeAllFrames(frames);
+ EXPECT_EQ(ENCRYPTION_INITIAL, serialized.encryption_level);
+ ASSERT_EQ(QuicPacketNumber(1u), serialized.packet_number);
+ ASSERT_EQ(QuicPacketNumber(1u), creator_.packet_number());
+
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+
+ ProcessPacket(serialized);
+}
+
+TEST_P(QuicPacketCreatorTest, ConsumeCryptoData) {
+ std::string data = "crypto data";
+ QuicFrame frame;
+ ASSERT_TRUE(creator_.ConsumeCryptoData(ENCRYPTION_INITIAL, data.length(), 0,
+ NOT_RETRANSMISSION, &frame));
+ EXPECT_EQ(frame.crypto_frame->data_length, data.length());
+ EXPECT_TRUE(creator_.HasPendingFrames());
+}
+
+TEST_P(QuicPacketCreatorTest, ConsumeData) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ QuicFrame frame;
+ MakeIOVector("test", &iov_);
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ ASSERT_TRUE(creator_.ConsumeData(stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u,
+ false, false, NOT_RETRANSMISSION, &frame));
+ size_t consumed = frame.stream_frame.data_length;
+ EXPECT_EQ(4u, consumed);
+ CheckStreamFrame(frame, stream_id, "test", 0u, false);
+ EXPECT_TRUE(creator_.HasPendingFrames());
+}
+
+TEST_P(QuicPacketCreatorTest, ConsumeDataFin) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ QuicFrame frame;
+ MakeIOVector("test", &iov_);
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ ASSERT_TRUE(creator_.ConsumeData(stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u,
+ true, false, NOT_RETRANSMISSION, &frame));
+ size_t consumed = frame.stream_frame.data_length;
+ EXPECT_EQ(4u, consumed);
+ CheckStreamFrame(frame, stream_id, "test", 0u, true);
+ EXPECT_TRUE(creator_.HasPendingFrames());
+}
+
+TEST_P(QuicPacketCreatorTest, ConsumeDataFinOnly) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ QuicFrame frame;
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ ASSERT_TRUE(creator_.ConsumeData(stream_id, nullptr, 0u, 0u, 0u, 0u, true,
+ false, NOT_RETRANSMISSION, &frame));
+ size_t consumed = frame.stream_frame.data_length;
+ EXPECT_EQ(0u, consumed);
+ CheckStreamFrame(frame, stream_id, std::string(), 0u, true);
+ EXPECT_TRUE(creator_.HasPendingFrames());
+}
+
+TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ const size_t overhead =
+ GetPacketHeaderOverhead(client_framer_.transport_version()) +
+ GetEncryptionOverhead();
+ for (size_t i = overhead; i < overhead + 100; ++i) {
+ creator_.SetMaxPacketLength(i);
+ const bool should_have_room =
+ i >
+ overhead + GetStreamFrameOverhead(client_framer_.transport_version());
+ ASSERT_EQ(should_have_room,
+ creator_.HasRoomForStreamFrame(GetNthClientInitiatedStreamId(1),
+ kOffset, /* data_size=*/0xffff));
+ if (should_have_room) {
+ QuicFrame frame;
+ MakeIOVector("testdata", &iov_);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(Invoke(
+ this, &QuicPacketCreatorTest::ClearSerializedPacketForTests));
+ ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_,
+ 1u, iov_.iov_len, 0u, kOffset, false,
+ false, NOT_RETRANSMISSION, &frame));
+ size_t bytes_consumed = frame.stream_frame.data_length;
+ EXPECT_LT(0u, bytes_consumed);
+ creator_.Flush();
+ }
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead =
+ GetPacketHeaderOverhead(client_framer_.transport_version()) +
+ GetEncryptionOverhead() +
+ GetStreamFrameOverhead(client_framer_.transport_version());
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ std::string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+ QuicFrame frame;
+ MakeIOVector(data, &iov_);
+ ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_,
+ 1u, iov_.iov_len, 0u, kOffset, false,
+ false, NOT_RETRANSMISSION, &frame));
+
+ // BytesFree() returns bytes available for the next frame, which will
+ // be two bytes smaller since the stream frame would need to be grown.
+ EXPECT_EQ(2u, creator_.ExpansionOnNewFrame());
+ size_t expected_bytes_free = bytes_free < 3 ? 0 : bytes_free - 2;
+ EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.Flush();
+ ASSERT_TRUE(serialized_packet_.encrypted_buffer);
+ DeleteSerializedPacket();
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
+ // This test serializes crypto payloads slightly larger than a packet, which
+ // Causes the multi-packet ClientHello check to fail.
+ FLAGS_quic_enforce_single_packet_chlo = false;
+ // Compute the total overhead for a single frame in packet.
+ size_t overhead =
+ GetPacketHeaderOverhead(client_framer_.transport_version()) +
+ GetEncryptionOverhead();
+ if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ overhead +=
+ QuicFramer::GetMinCryptoFrameSize(kOffset, kMaxOutgoingPacketSize);
+ } else {
+ overhead += GetStreamFrameOverhead(client_framer_.transport_version());
+ }
+ ASSERT_GT(kMaxOutgoingPacketSize, overhead);
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ SCOPED_TRACE(delta);
+ std::string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+
+ QuicFrame frame;
+ MakeIOVector(data, &iov_);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(
+ Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ ASSERT_TRUE(creator_.ConsumeData(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
+ &iov_, 1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION,
+ &frame));
+ size_t bytes_consumed = frame.stream_frame.data_length;
+ EXPECT_LT(0u, bytes_consumed);
+ } else {
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, kOffset, data);
+ ASSERT_TRUE(creator_.ConsumeCryptoData(ENCRYPTION_INITIAL, data.length(),
+ kOffset, NOT_RETRANSMISSION,
+ &frame));
+ size_t bytes_consumed = frame.crypto_frame->data_length;
+ EXPECT_LT(0u, bytes_consumed);
+ }
+ creator_.Flush();
+ ASSERT_TRUE(serialized_packet_.encrypted_buffer);
+ // If there is not enough space in the packet to fit a padding frame
+ // (1 byte) and to expand the stream frame (another 2 bytes) the packet
+ // will not be padded.
+ if (bytes_free < 3 &&
+ !QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ EXPECT_EQ(kDefaultMaxPacketSize - bytes_free,
+ serialized_packet_.encrypted_length);
+ } else {
+ EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length);
+ }
+ DeleteSerializedPacket();
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead =
+ GetPacketHeaderOverhead(client_framer_.transport_version()) +
+ GetEncryptionOverhead() +
+ GetStreamFrameOverhead(client_framer_.transport_version());
+ ASSERT_GT(kDefaultMaxPacketSize, overhead);
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ std::string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+
+ QuicFrame frame;
+ MakeIOVector(data, &iov_);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ ASSERT_TRUE(creator_.ConsumeData(GetNthClientInitiatedStreamId(1), &iov_,
+ 1u, iov_.iov_len, 0u, kOffset, false,
+ false, NOT_RETRANSMISSION, &frame));
+ size_t bytes_consumed = frame.stream_frame.data_length;
+ EXPECT_LT(0u, bytes_consumed);
+ creator_.Flush();
+ ASSERT_TRUE(serialized_packet_.encrypted_buffer);
+ if (bytes_free > 0) {
+ EXPECT_EQ(kDefaultMaxPacketSize - bytes_free,
+ serialized_packet_.encrypted_length);
+ } else {
+ EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length);
+ }
+ DeleteSerializedPacket();
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
+ QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER);
+ ParsedQuicVersionVector versions;
+ versions.push_back(test::QuicVersionMax());
+ const bool ietf_quic = GetParam().version.transport_version > QUIC_VERSION_43;
+ std::unique_ptr<QuicEncryptedPacket> encrypted(
+ creator_.SerializeVersionNegotiationPacket(ietf_quic, versions));
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnVersionNegotiationPacket(_));
+ }
+ QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_CLIENT);
+ client_framer_.ProcessPacket(*encrypted);
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeConnectivityProbingPacket) {
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+
+ creator_.set_encryption_level(level);
+
+ OwningSerializedPacketPointer encrypted;
+ if (GetParam().version.transport_version == QUIC_VERSION_99) {
+ QuicPathFrameBuffer payload = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}};
+ encrypted =
+ creator_.SerializePathChallengeConnectivityProbingPacket(&payload);
+ } else {
+ encrypted = creator_.SerializeConnectivityProbingPacket();
+ }
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ if (GetParam().version.transport_version == QUIC_VERSION_99) {
+ EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ } else {
+ EXPECT_CALL(framer_visitor_, OnPingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ }
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER);
+ server_framer_.ProcessPacket(QuicEncryptedPacket(
+ encrypted->encrypted_buffer, encrypted->encrypted_length));
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, SerializePathChallengeProbePacket) {
+ if (GetParam().version.transport_version != QUIC_VERSION_99) {
+ return;
+ }
+ QuicPathFrameBuffer payload = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}};
+
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+
+ creator_.set_encryption_level(level);
+
+ OwningSerializedPacketPointer encrypted(
+ creator_.SerializePathChallengeConnectivityProbingPacket(&payload));
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPathChallengeFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ // QuicFramerPeer::SetPerspective(&client_framer_, Perspective::IS_SERVER);
+ server_framer_.ProcessPacket(QuicEncryptedPacket(
+ encrypted->encrypted_buffer, encrypted->encrypted_length));
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket1PayloadPadded) {
+ if (GetParam().version.transport_version != QUIC_VERSION_99) {
+ return;
+ }
+ QuicPathFrameBuffer payload0 = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}};
+
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+ creator_.set_encryption_level(level);
+
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+
+ OwningSerializedPacketPointer encrypted(
+ creator_.SerializePathResponseConnectivityProbingPacket(payloads,
+ true));
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ server_framer_.ProcessPacket(QuicEncryptedPacket(
+ encrypted->encrypted_buffer, encrypted->encrypted_length));
+ }
+}
+
+TEST_P(QuicPacketCreatorTest,
+ SerializePathResponseProbePacket1PayloadUnPadded) {
+ if (GetParam().version.transport_version != QUIC_VERSION_99) {
+ return;
+ }
+ QuicPathFrameBuffer payload0 = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}};
+
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+ creator_.set_encryption_level(level);
+
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+
+ OwningSerializedPacketPointer encrypted(
+ creator_.SerializePathResponseConnectivityProbingPacket(payloads,
+ false));
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ server_framer_.ProcessPacket(QuicEncryptedPacket(
+ encrypted->encrypted_buffer, encrypted->encrypted_length));
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket2PayloadsPadded) {
+ if (GetParam().version.transport_version != QUIC_VERSION_99) {
+ return;
+ }
+ QuicPathFrameBuffer payload0 = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}};
+ QuicPathFrameBuffer payload1 = {
+ {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}};
+
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+ creator_.set_encryption_level(level);
+
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+ payloads.push_back(payload1);
+
+ OwningSerializedPacketPointer encrypted(
+ creator_.SerializePathResponseConnectivityProbingPacket(payloads,
+ true));
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2);
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ server_framer_.ProcessPacket(QuicEncryptedPacket(
+ encrypted->encrypted_buffer, encrypted->encrypted_length));
+ }
+}
+
+TEST_P(QuicPacketCreatorTest,
+ SerializePathResponseProbePacket2PayloadsUnPadded) {
+ if (GetParam().version.transport_version != QUIC_VERSION_99) {
+ return;
+ }
+ QuicPathFrameBuffer payload0 = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}};
+ QuicPathFrameBuffer payload1 = {
+ {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}};
+
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+ creator_.set_encryption_level(level);
+
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+ payloads.push_back(payload1);
+
+ OwningSerializedPacketPointer encrypted(
+ creator_.SerializePathResponseConnectivityProbingPacket(payloads,
+ false));
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(2);
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ server_framer_.ProcessPacket(QuicEncryptedPacket(
+ encrypted->encrypted_buffer, encrypted->encrypted_length));
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, SerializePathResponseProbePacket3PayloadsPadded) {
+ if (GetParam().version.transport_version != QUIC_VERSION_99) {
+ return;
+ }
+ QuicPathFrameBuffer payload0 = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}};
+ QuicPathFrameBuffer payload1 = {
+ {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}};
+ QuicPathFrameBuffer payload2 = {
+ {0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde, 0xad}};
+
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+ creator_.set_encryption_level(level);
+
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+ payloads.push_back(payload1);
+ payloads.push_back(payload2);
+
+ OwningSerializedPacketPointer encrypted(
+ creator_.SerializePathResponseConnectivityProbingPacket(payloads,
+ true));
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3);
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ server_framer_.ProcessPacket(QuicEncryptedPacket(
+ encrypted->encrypted_buffer, encrypted->encrypted_length));
+ }
+}
+
+TEST_P(QuicPacketCreatorTest,
+ SerializePathResponseProbePacket3PayloadsUnpadded) {
+ if (GetParam().version.transport_version != QUIC_VERSION_99) {
+ return;
+ }
+ QuicPathFrameBuffer payload0 = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee}};
+ QuicPathFrameBuffer payload1 = {
+ {0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde}};
+ QuicPathFrameBuffer payload2 = {
+ {0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xee, 0xde, 0xad}};
+
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ EncryptionLevel level = static_cast<EncryptionLevel>(i);
+ creator_.set_encryption_level(level);
+
+ QuicDeque<QuicPathFrameBuffer> payloads;
+ payloads.push_back(payload0);
+ payloads.push_back(payload1);
+ payloads.push_back(payload2);
+
+ OwningSerializedPacketPointer encrypted(
+ creator_.SerializePathResponseConnectivityProbingPacket(payloads,
+ false));
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_)).Times(3);
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+
+ server_framer_.ProcessPacket(QuicEncryptedPacket(
+ encrypted->encrypted_buffer, encrypted->encrypted_length));
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) {
+ if (GetParam().version.transport_version > QUIC_VERSION_43 &&
+ GetParam().version.transport_version != QUIC_VERSION_99) {
+ EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ } else {
+ EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+ }
+
+ QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64);
+ creator_.UpdatePacketNumberLength(QuicPacketNumber(2),
+ 10000 / kDefaultMaxPacketSize);
+ EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+
+ QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64 * 256);
+ creator_.UpdatePacketNumberLength(QuicPacketNumber(2),
+ 10000 / kDefaultMaxPacketSize);
+ EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+
+ QuicPacketCreatorPeer::SetPacketNumber(&creator_, 64 * 256 * 256);
+ creator_.UpdatePacketNumberLength(QuicPacketNumber(2),
+ 10000 / kDefaultMaxPacketSize);
+ EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+
+ QuicPacketCreatorPeer::SetPacketNumber(&creator_,
+ UINT64_C(64) * 256 * 256 * 256 * 256);
+ creator_.UpdatePacketNumberLength(QuicPacketNumber(2),
+ 10000 / kDefaultMaxPacketSize);
+ EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+}
+
+TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthCwnd) {
+ QuicPacketCreatorPeer::SetPacketNumber(&creator_, 1);
+ if (GetParam().version.transport_version > QUIC_VERSION_43 &&
+ GetParam().version.transport_version != QUIC_VERSION_99) {
+ EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ } else {
+ EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+ }
+
+ creator_.UpdatePacketNumberLength(QuicPacketNumber(1),
+ 10000 / kDefaultMaxPacketSize);
+ EXPECT_EQ(PACKET_1BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+
+ creator_.UpdatePacketNumberLength(QuicPacketNumber(1),
+ 10000 * 256 / kDefaultMaxPacketSize);
+ EXPECT_EQ(PACKET_2BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+
+ creator_.UpdatePacketNumberLength(QuicPacketNumber(1),
+ 10000 * 256 * 256 / kDefaultMaxPacketSize);
+ EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+
+ creator_.UpdatePacketNumberLength(
+ QuicPacketNumber(1),
+ UINT64_C(1000) * 256 * 256 * 256 * 256 / kDefaultMaxPacketSize);
+ EXPECT_EQ(PACKET_6BYTE_PACKET_NUMBER,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_));
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeFrame) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ std::string data("a");
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
+ /*fin=*/false, 0u, QuicStringPiece());
+ frames_.push_back(QuicFrame(stream_frame));
+ } else {
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data);
+ frames_.push_back(
+ QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length())));
+ }
+ SerializedPacket serialized = SerializeAllFrames(frames_);
+
+ QuicPacketHeader header;
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_))
+ .WillOnce(DoAll(SaveArg<0>(&header), Return(true)));
+ if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ EXPECT_CALL(framer_visitor_, OnCryptoFrame(_));
+ } else {
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ }
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized);
+ EXPECT_EQ(GetParam().version_serialization, header.version_flag);
+ DeleteFrames(&frames_);
+}
+
+TEST_P(QuicPacketCreatorTest, ConsumeDataLargerThanOneStreamFrame) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ // A string larger than fits into a frame.
+ QuicFrame frame;
+ size_t payload_length = creator_.max_packet_length();
+ const std::string too_long_payload(payload_length, 'a');
+ MakeIOVector(too_long_payload, &iov_);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ ASSERT_TRUE(creator_.ConsumeData(stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u,
+ true, false, NOT_RETRANSMISSION, &frame));
+ size_t consumed = frame.stream_frame.data_length;
+ // The entire payload could not be consumed.
+ EXPECT_GT(payload_length, consumed);
+ creator_.Flush();
+ DeleteSerializedPacket();
+}
+
+TEST_P(QuicPacketCreatorTest, AddFrameAndFlush) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ const size_t max_plaintext_size =
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version())));
+ EXPECT_EQ(max_plaintext_size -
+ GetPacketHeaderSize(
+ client_framer_.transport_version(),
+ creator_.GetDestinationConnectionIdLength(),
+ creator_.GetSourceConnectionIdLength(),
+ QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
+ !kIncludeDiversificationNonce,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_),
+ QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_),
+ 0, QuicPacketCreatorPeer::GetLengthLength(&creator_)),
+ creator_.BytesFree());
+ StrictMock<MockDebugDelegate> debug;
+ creator_.set_debug_delegate(&debug);
+
+ // Add a variety of frame types and then a padding frame.
+ QuicAckFrame ack_frame(InitAckFrame(10u));
+ EXPECT_CALL(debug, OnFrameAddedToPacket(_));
+ EXPECT_TRUE(
+ creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version())));
+
+ QuicFrame frame;
+ MakeIOVector("test", &iov_);
+ EXPECT_CALL(debug, OnFrameAddedToPacket(_));
+ ASSERT_TRUE(creator_.ConsumeData(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_,
+ 1u, iov_.iov_len, 0u, 0u, false, false, NOT_RETRANSMISSION, &frame));
+ size_t consumed = frame.stream_frame.data_length;
+ EXPECT_EQ(4u, consumed);
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_TRUE(creator_.HasPendingStreamFramesOfStream(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version())));
+
+ QuicPaddingFrame padding_frame;
+ EXPECT_CALL(debug, OnFrameAddedToPacket(_));
+ EXPECT_TRUE(
+ creator_.AddSavedFrame(QuicFrame(padding_frame), NOT_RETRANSMISSION));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_EQ(0u, creator_.BytesFree());
+
+ // Packet is full. Creator will flush.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ EXPECT_FALSE(
+ creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION));
+
+ // Ensure the packet is successfully created.
+ ASSERT_TRUE(serialized_packet_.encrypted_buffer);
+ ASSERT_FALSE(serialized_packet_.retransmittable_frames.empty());
+ const QuicFrames& retransmittable = serialized_packet_.retransmittable_frames;
+ ASSERT_EQ(1u, retransmittable.size());
+ EXPECT_EQ(STREAM_FRAME, retransmittable[0].type);
+ EXPECT_TRUE(serialized_packet_.has_ack);
+ EXPECT_EQ(QuicPacketNumber(10u), serialized_packet_.largest_acked);
+ DeleteSerializedPacket();
+
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ EXPECT_FALSE(creator_.HasPendingStreamFramesOfStream(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version())));
+ EXPECT_EQ(max_plaintext_size -
+ GetPacketHeaderSize(
+ client_framer_.transport_version(),
+ creator_.GetDestinationConnectionIdLength(),
+ creator_.GetSourceConnectionIdLength(),
+ QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
+ !kIncludeDiversificationNonce,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_),
+ QuicPacketCreatorPeer::GetRetryTokenLengthLength(&creator_),
+ 0, QuicPacketCreatorPeer::GetLengthLength(&creator_)),
+ creator_.BytesFree());
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeAndSendStreamFrame) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ EXPECT_FALSE(creator_.HasPendingFrames());
+
+ MakeIOVector("test", &iov_);
+ producer_.SaveStreamData(
+ QuicUtils::GetHeadersStreamId(client_framer_.transport_version()), &iov_,
+ 1u, 0u, iov_.iov_len);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ size_t num_bytes_consumed;
+ StrictMock<MockDebugDelegate> debug;
+ creator_.set_debug_delegate(&debug);
+ EXPECT_CALL(debug, OnFrameAddedToPacket(_));
+ creator_.CreateAndSerializeStreamFrame(
+ QuicUtils::GetHeadersStreamId(client_framer_.transport_version()),
+ iov_.iov_len, 0, 0, true, NOT_RETRANSMISSION, &num_bytes_consumed);
+ EXPECT_EQ(4u, num_bytes_consumed);
+
+ // Ensure the packet is successfully created.
+ ASSERT_TRUE(serialized_packet_.encrypted_buffer);
+ ASSERT_FALSE(serialized_packet_.retransmittable_frames.empty());
+ const QuicFrames& retransmittable = serialized_packet_.retransmittable_frames;
+ ASSERT_EQ(1u, retransmittable.size());
+ EXPECT_EQ(STREAM_FRAME, retransmittable[0].type);
+ DeleteSerializedPacket();
+
+ EXPECT_FALSE(creator_.HasPendingFrames());
+}
+
+TEST_P(QuicPacketCreatorTest, AddUnencryptedStreamDataClosesConnection) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (!IsDefaultTestConfiguration()) {
+ return;
+ }
+
+ creator_.set_encryption_level(ENCRYPTION_INITIAL);
+ EXPECT_CALL(delegate_, OnUnrecoverableError(_, _, _));
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetHeadersStreamId(client_framer_.transport_version()),
+ /*fin=*/false, 0u, QuicStringPiece());
+ EXPECT_QUIC_BUG(
+ creator_.AddSavedFrame(QuicFrame(stream_frame), NOT_RETRANSMISSION),
+ "Cannot send stream data without encryption.");
+}
+
+TEST_P(QuicPacketCreatorTest, ChloTooLarge) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (!IsDefaultTestConfiguration()) {
+ return;
+ }
+
+ // This test only matters when the crypto handshake is sent in stream frames.
+ // TODO(b/128596274): Re-enable when this check is supported for CRYPTO
+ // frames.
+ if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ return;
+ }
+
+ CryptoHandshakeMessage message;
+ message.set_tag(kCHLO);
+ message.set_minimum_size(kMaxOutgoingPacketSize);
+ CryptoFramer framer;
+ std::unique_ptr<QuicData> message_data;
+ message_data = framer.ConstructHandshakeMessage(message);
+
+ struct iovec iov;
+ MakeIOVector(QuicStringPiece(message_data->data(), message_data->length()),
+ &iov);
+ QuicFrame frame;
+ EXPECT_CALL(delegate_,
+ OnUnrecoverableError(QUIC_CRYPTO_CHLO_TOO_LARGE, _, _));
+ EXPECT_QUIC_BUG(creator_.ConsumeData(QuicUtils::GetCryptoStreamId(
+ client_framer_.transport_version()),
+ &iov, 1u, iov.iov_len, 0u, 0u, false,
+ false, NOT_RETRANSMISSION, &frame),
+ "Client hello won't fit in a single packet.");
+}
+
+TEST_P(QuicPacketCreatorTest, PendingPadding) {
+ EXPECT_EQ(0u, creator_.pending_padding_bytes());
+ creator_.AddPendingPadding(kMaxNumRandomPaddingBytes * 10);
+ EXPECT_EQ(kMaxNumRandomPaddingBytes * 10, creator_.pending_padding_bytes());
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(
+ Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ // Flush all paddings.
+ while (creator_.pending_padding_bytes() > 0) {
+ creator_.Flush();
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ // Packet only contains padding.
+ ProcessPacket(serialized_packet_);
+ }
+ EXPECT_EQ(0u, creator_.pending_padding_bytes());
+}
+
+TEST_P(QuicPacketCreatorTest, FullPaddingDoesNotConsumePendingPadding) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ creator_.AddPendingPadding(kMaxNumRandomPaddingBytes);
+ QuicFrame frame;
+ MakeIOVector("test", &iov_);
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ ASSERT_TRUE(creator_.ConsumeData(
+ stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false,
+ /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.Flush();
+ EXPECT_EQ(kMaxNumRandomPaddingBytes, creator_.pending_padding_bytes());
+}
+
+TEST_P(QuicPacketCreatorTest, SendPendingPaddingInRetransmission) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ QuicStreamFrame stream_frame(stream_id,
+ /*fin=*/false, 0u, QuicStringPiece());
+ QuicFrames frames;
+ frames.push_back(QuicFrame(stream_frame));
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, true, /*num_padding_bytes=*/0, ENCRYPTION_FORWARD_SECURE,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.AddPendingPadding(kMaxNumRandomPaddingBytes);
+ creator_.ReserializeAllFrames(retransmission, buffer, kMaxOutgoingPacketSize);
+ EXPECT_EQ(0u, creator_.pending_padding_bytes());
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized_packet_);
+}
+
+TEST_P(QuicPacketCreatorTest, SendPacketAfterFullPaddingRetransmission) {
+ // Making sure needs_full_padding gets reset after a full padding
+ // retransmission.
+ EXPECT_EQ(0u, creator_.pending_padding_bytes());
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ QuicFrame frame;
+ std::string data = "fake handshake message data";
+ MakeIOVector(data, &iov_);
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ stream_id =
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version());
+ }
+ producer_.SaveStreamData(stream_id, &iov_, 1u, 0u, iov_.iov_len);
+ QuicPacketCreatorPeer::CreateStreamFrame(&creator_, stream_id, iov_.iov_len,
+ 0u, false, &frame);
+ QuicFrames frames;
+ frames.push_back(frame);
+ char buffer[kMaxOutgoingPacketSize];
+ QuicPendingRetransmission retransmission(CreateRetransmission(
+ frames, true, /*num_padding_bytes=*/-1, ENCRYPTION_FORWARD_SECURE,
+ QuicPacketCreatorPeer::GetPacketNumberLength(&creator_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(
+ Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.ReserializeAllFrames(retransmission, buffer, kMaxOutgoingPacketSize);
+ EXPECT_EQ(kDefaultMaxPacketSize, serialized_packet_.encrypted_length);
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ // Full padding.
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized_packet_);
+
+ creator_.ConsumeData(stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false,
+ NOT_RETRANSMISSION, &frame);
+ creator_.Flush();
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ // needs_full_padding gets reset.
+ EXPECT_CALL(framer_visitor_, OnPaddingFrame(_)).Times(0);
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized_packet_);
+ DeleteFrames(&frames);
+}
+
+TEST_P(QuicPacketCreatorTest, ConsumeDataAndRandomPadding) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ const QuicByteCount kStreamFramePayloadSize = 100u;
+ // Set the packet size be enough for one stream frame with 0 stream offset +
+ // 1.
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ size_t length =
+ GetPacketHeaderOverhead(client_framer_.transport_version()) +
+ GetEncryptionOverhead() +
+ QuicFramer::GetMinStreamFrameSize(
+ client_framer_.transport_version(), stream_id, 0,
+ /*last_frame_in_packet=*/false, kStreamFramePayloadSize + 1) +
+ kStreamFramePayloadSize + 1;
+ creator_.SetMaxPacketLength(length);
+ creator_.AddPendingPadding(kMaxNumRandomPaddingBytes);
+ QuicByteCount pending_padding_bytes = creator_.pending_padding_bytes();
+ QuicFrame frame;
+ char buf[kStreamFramePayloadSize + 1] = {};
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(
+ Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ // Send stream frame of size kStreamFramePayloadSize.
+ MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_);
+ creator_.ConsumeData(stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false, false,
+ NOT_RETRANSMISSION, &frame);
+ creator_.Flush();
+ // 1 byte padding is sent.
+ EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes());
+ // Send stream frame of size kStreamFramePayloadSize + 1.
+ MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize + 1), &iov_);
+ creator_.ConsumeData(stream_id, &iov_, 1u, iov_.iov_len, 0u,
+ kStreamFramePayloadSize, false, false,
+ NOT_RETRANSMISSION, &frame);
+ // No padding is sent.
+ creator_.Flush();
+ EXPECT_EQ(pending_padding_bytes - 1, creator_.pending_padding_bytes());
+ // Flush all paddings.
+ while (creator_.pending_padding_bytes() > 0) {
+ creator_.Flush();
+ }
+ EXPECT_EQ(0u, creator_.pending_padding_bytes());
+}
+
+TEST_P(QuicPacketCreatorTest, FlushWithExternalBuffer) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ char external_buffer[kMaxOutgoingPacketSize];
+ char* expected_buffer = external_buffer;
+ EXPECT_CALL(delegate_, GetPacketBuffer()).WillOnce(Return(expected_buffer));
+
+ QuicFrame frame;
+ MakeIOVector("test", &iov_);
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ ASSERT_TRUE(creator_.ConsumeData(
+ stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u, false,
+ /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame));
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke([expected_buffer](SerializedPacket* serialized_packet) {
+ EXPECT_EQ(expected_buffer, serialized_packet->encrypted_buffer);
+ ClearSerializedPacket(serialized_packet);
+ }));
+ creator_.Flush();
+}
+
+// Test for error found in
+// https://bugs.chromium.org/p/chromium/issues/detail?id=859949 where a gap
+// length that crosses an IETF VarInt length boundary would cause a
+// failure. While this test is not applicable to versions other than version 99,
+// it should still work. Hence, it is not made version-specific.
+TEST_P(QuicPacketCreatorTest, IetfAckGapErrorRegression) {
+ QuicAckFrame ack_frame =
+ InitAckFrame({{QuicPacketNumber(60), QuicPacketNumber(61)},
+ {QuicPacketNumber(125), QuicPacketNumber(126)}});
+ frames_.push_back(QuicFrame(&ack_frame));
+ SerializeAllFrames(frames_);
+}
+
+TEST_P(QuicPacketCreatorTest, AddMessageFrame) {
+ if (client_framer_.transport_version() <= QUIC_VERSION_44) {
+ return;
+ }
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .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');
+ QuicMessageFrame* message_frame =
+ new QuicMessageFrame(1, MakeSpan(&allocator_, message, &storage));
+ EXPECT_TRUE(
+ creator_.AddSavedFrame(QuicFrame(message_frame), NOT_RETRANSMISSION));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ creator_.Flush();
+
+ QuicMessageFrame* frame2 =
+ new QuicMessageFrame(2, MakeSpan(&allocator_, "message", &storage));
+ EXPECT_TRUE(creator_.AddSavedFrame(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));
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame3), NOT_RETRANSMISSION));
+ EXPECT_EQ(1u, creator_.ExpansionOnNewFrame());
+ creator_.Flush();
+
+ QuicFrame frame;
+ MakeIOVector("test", &iov_);
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ EXPECT_TRUE(creator_.ConsumeData(stream_id, &iov_, 1u, iov_.iov_len, 0u, 0u,
+ false, false, NOT_RETRANSMISSION, &frame));
+ QuicMessageFrame* frame4 =
+ new QuicMessageFrame(4, MakeSpan(&allocator_, "message", &storage));
+ EXPECT_TRUE(creator_.AddSavedFrame(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));
+ EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&frame5), NOT_RETRANSMISSION));
+ EXPECT_FALSE(creator_.HasPendingFrames());
+}
+
+TEST_P(QuicPacketCreatorTest, MessageFrameConsumption) {
+ if (client_framer_.transport_version() <= QUIC_VERSION_44) {
+ return;
+ }
+ std::string message_data(kDefaultMaxPacketSize, 'a');
+ QuicStringPiece 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}) {
+ creator_.set_encryption_level(level);
+ // Test all possible sizes of message frames.
+ for (size_t message_size = 0;
+ message_size <= creator_.GetCurrentLargestMessagePayload();
+ ++message_size) {
+ QuicMessageFrame* frame = new QuicMessageFrame(
+ 0, MakeSpan(&allocator_,
+ QuicStringPiece(message_buffer.data(), message_size),
+ &storage));
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ size_t expansion_bytes = message_size >= 64 ? 2 : 1;
+ EXPECT_EQ(expansion_bytes, creator_.ExpansionOnNewFrame());
+ // Verify BytesFree returns bytes available for the next frame, which
+ // should subtract the message length.
+ size_t expected_bytes_free =
+ creator_.GetCurrentLargestMessagePayload() - message_size <
+ expansion_bytes
+ ? 0
+ : creator_.GetCurrentLargestMessagePayload() - expansion_bytes -
+ message_size;
+ EXPECT_EQ(expected_bytes_free, creator_.BytesFree());
+ EXPECT_LE(creator_.GetGuaranteedLargestMessagePayload(),
+ creator_.GetCurrentLargestMessagePayload());
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+ creator_.Flush();
+ ASSERT_TRUE(serialized_packet_.encrypted_buffer);
+ DeleteSerializedPacket();
+ }
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, PacketTransmissionType) {
+ creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ creator_.set_can_set_transmission_type(true);
+ creator_.SetTransmissionType(NOT_RETRANSMISSION);
+
+ QuicAckFrame temp_ack_frame = InitAckFrame(1);
+ QuicFrame ack_frame(&temp_ack_frame);
+ ASSERT_FALSE(QuicUtils::IsRetransmittableFrame(ack_frame.type));
+
+ QuicStreamId stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ client_framer_.transport_version(), Perspective::IS_CLIENT);
+ QuicFrame stream_frame(QuicStreamFrame(stream_id,
+ /*fin=*/false, 0u, QuicStringPiece()));
+ ASSERT_TRUE(QuicUtils::IsRetransmittableFrame(stream_frame.type));
+
+ QuicFrame padding_frame{QuicPaddingFrame()};
+ ASSERT_FALSE(QuicUtils::IsRetransmittableFrame(padding_frame.type));
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+
+ EXPECT_TRUE(creator_.AddSavedFrame(ack_frame, LOSS_RETRANSMISSION));
+ ASSERT_FALSE(serialized_packet_.encrypted_buffer);
+
+ EXPECT_TRUE(creator_.AddSavedFrame(stream_frame, RTO_RETRANSMISSION));
+ ASSERT_FALSE(serialized_packet_.encrypted_buffer);
+
+ EXPECT_TRUE(creator_.AddSavedFrame(padding_frame, TLP_RETRANSMISSION));
+ creator_.Flush();
+ ASSERT_TRUE(serialized_packet_.encrypted_buffer);
+
+ if (creator_.ShouldSetTransmissionTypeForNextFrame()) {
+ // The last retransmittable frame on packet is a stream frame, the packet's
+ // transmission type should be the same as the stream frame's.
+ EXPECT_EQ(serialized_packet_.transmission_type, RTO_RETRANSMISSION);
+ } else {
+ EXPECT_EQ(serialized_packet_.transmission_type, NOT_RETRANSMISSION);
+ }
+ DeleteSerializedPacket();
+}
+
+TEST_P(QuicPacketCreatorTest, RetryToken) {
+ if (!GetParam().version_serialization ||
+ !QuicVersionHasLongHeaderLengths(client_framer_.transport_version())) {
+ return;
+ }
+
+ char retry_token_bytes[] = {1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16};
+
+ creator_.SetRetryToken(
+ std::string(retry_token_bytes, sizeof(retry_token_bytes)));
+
+ std::string data("a");
+ if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(client_framer_.transport_version()),
+ /*fin=*/false, 0u, QuicStringPiece());
+ frames_.push_back(QuicFrame(stream_frame));
+ } else {
+ producer_.SaveCryptoData(ENCRYPTION_INITIAL, 0, data);
+ frames_.push_back(
+ QuicFrame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data.length())));
+ }
+ SerializedPacket serialized = SerializeAllFrames(frames_);
+
+ QuicPacketHeader header;
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_))
+ .WillOnce(DoAll(SaveArg<0>(&header), Return(true)));
+ if (QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+ EXPECT_CALL(framer_visitor_, OnCryptoFrame(_));
+ } else {
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ }
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized);
+ ASSERT_TRUE(header.version_flag);
+ ASSERT_EQ(header.long_packet_type, INITIAL);
+ ASSERT_EQ(header.retry_token.length(), sizeof(retry_token_bytes));
+ test::CompareCharArraysWithHexError(
+ "retry token", header.retry_token.data(), header.retry_token.length(),
+ retry_token_bytes, sizeof(retry_token_bytes));
+ DeleteFrames(&frames_);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.cc
new file mode 100644
index 00000000000..6efadcf12c6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.cc
@@ -0,0 +1,546 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_generator.h"
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate)
+ : delegate_(delegate),
+ packet_creator_(connection_id, framer, random_generator, delegate),
+ next_transmission_type_(NOT_RETRANSMISSION),
+ flusher_attached_(false),
+ should_send_ack_(false),
+ should_send_stop_waiting_(false),
+ random_generator_(random_generator),
+ fully_pad_crypto_handshake_packets_(true),
+ deprecate_ack_bundling_mode_(
+ GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {}
+
+QuicPacketGenerator::~QuicPacketGenerator() {
+ DeleteFrames(&queued_control_frames_);
+}
+
+void QuicPacketGenerator::SetShouldSendAck(bool also_send_stop_waiting) {
+ DCHECK(!deprecate_ack_bundling_mode_);
+ if (packet_creator_.has_ack()) {
+ // Ack already queued, nothing to do.
+ return;
+ }
+
+ if (also_send_stop_waiting && packet_creator_.has_stop_waiting()) {
+ QUIC_BUG << "Should only ever be one pending stop waiting frame.";
+ return;
+ }
+
+ should_send_ack_ = true;
+ should_send_stop_waiting_ = also_send_stop_waiting;
+ SendQueuedFrames(/*flush=*/false);
+}
+
+void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) {
+ QUIC_BUG_IF(IsControlFrame(frame.type) && !GetControlFrameId(frame))
+ << "Adding a control frame with no control frame id: " << frame;
+ if (deprecate_ack_bundling_mode_) {
+ MaybeBundleAckOpportunistically();
+ }
+ queued_control_frames_.push_back(frame);
+ SendQueuedFrames(/*flush=*/false);
+}
+
+size_t QuicPacketGenerator::ConsumeCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset) {
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to write crypto data.";
+ if (deprecate_ack_bundling_mode_) {
+ MaybeBundleAckOpportunistically();
+ }
+ // To make reasoning about crypto frames easier, we don't combine them with
+ // other retransmittable frames in a single packet.
+ // TODO(nharper): Once we have separate packet number spaces, everything
+ // should be driven by encryption level, and we should stop flushing in this
+ // spot.
+ const bool flush = packet_creator_.HasPendingRetransmittableFrames();
+ SendQueuedFrames(flush);
+
+ size_t total_bytes_consumed = 0;
+
+ while (total_bytes_consumed < write_length) {
+ QuicFrame frame;
+ if (!packet_creator_.ConsumeCryptoData(
+ level, write_length - total_bytes_consumed,
+ offset + total_bytes_consumed, next_transmission_type_, &frame)) {
+ // The only pending data in the packet is non-retransmittable frames. I'm
+ // assuming here that they won't occupy so much of the packet that a
+ // CRYPTO frame won't fit.
+ QUIC_BUG << "Failed to ConsumeCryptoData at level " << level;
+ return 0;
+ }
+ total_bytes_consumed += frame.crypto_frame->data_length;
+
+ // TODO(ianswett): Move to having the creator flush itself when it's full.
+ packet_creator_.Flush();
+ }
+
+ // Don't allow the handshake to be bundled with other retransmittable frames.
+ SendQueuedFrames(/*flush=*/true);
+
+ return total_bytes_consumed;
+}
+
+QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to write stream data.";
+ bool has_handshake =
+ (id == QuicUtils::GetCryptoStreamId(packet_creator_.transport_version()));
+ if (deprecate_ack_bundling_mode_) {
+ MaybeBundleAckOpportunistically();
+ }
+ bool fin = state != NO_FIN;
+ QUIC_BUG_IF(has_handshake && fin)
+ << "Handshake packets should never send a fin";
+ // To make reasoning about crypto frames easier, we don't combine them with
+ // other retransmittable frames in a single packet.
+ const bool flush =
+ has_handshake && packet_creator_.HasPendingRetransmittableFrames();
+ SendQueuedFrames(flush);
+
+ size_t total_bytes_consumed = 0;
+ bool fin_consumed = false;
+
+ if (!packet_creator_.HasRoomForStreamFrame(id, offset, write_length)) {
+ packet_creator_.Flush();
+ }
+
+ if (!fin && (write_length == 0)) {
+ QUIC_BUG << "Attempt to consume empty data without FIN.";
+ return QuicConsumedData(0, false);
+ }
+ // We determine if we can enter the fast path before executing
+ // the slow path loop.
+ bool run_fast_path =
+ !has_handshake && state != FIN_AND_PADDING && !HasQueuedFrames() &&
+ write_length - total_bytes_consumed > kMaxOutgoingPacketSize;
+
+ while (!run_fast_path && delegate_->ShouldGeneratePacket(
+ HAS_RETRANSMITTABLE_DATA,
+ has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) {
+ QuicFrame frame;
+ bool needs_full_padding =
+ has_handshake && fully_pad_crypto_handshake_packets_;
+
+ if (!packet_creator_.ConsumeData(id, write_length - total_bytes_consumed,
+ offset + total_bytes_consumed, fin,
+ needs_full_padding,
+ next_transmission_type_, &frame)) {
+ // The creator is always flushed if there's not enough room for a new
+ // stream frame before ConsumeData, so ConsumeData should always succeed.
+ QUIC_BUG << "Failed to ConsumeData, stream:" << id;
+ return QuicConsumedData(0, false);
+ }
+
+ // A stream frame is created and added.
+ size_t bytes_consumed = frame.stream_frame.data_length;
+ total_bytes_consumed += bytes_consumed;
+ fin_consumed = fin && total_bytes_consumed == write_length;
+ if (fin_consumed && state == FIN_AND_PADDING) {
+ AddRandomPadding();
+ }
+ DCHECK(total_bytes_consumed == write_length ||
+ (bytes_consumed > 0 && packet_creator_.HasPendingFrames()));
+
+ if (total_bytes_consumed == write_length) {
+ // We're done writing the data. Exit the loop.
+ // We don't make this a precondition because we could have 0 bytes of data
+ // if we're simply writing a fin.
+ break;
+ }
+ // TODO(ianswett): Move to having the creator flush itself when it's full.
+ packet_creator_.Flush();
+
+ run_fast_path =
+ !has_handshake && state != FIN_AND_PADDING && !HasQueuedFrames() &&
+ write_length - total_bytes_consumed > kMaxOutgoingPacketSize;
+ }
+
+ if (run_fast_path) {
+ return ConsumeDataFastPath(id, write_length, offset, state != NO_FIN,
+ total_bytes_consumed);
+ }
+
+ // Don't allow the handshake to be bundled with other retransmittable frames.
+ if (has_handshake) {
+ SendQueuedFrames(/*flush=*/true);
+ }
+
+ return QuicConsumedData(total_bytes_consumed, fin_consumed);
+}
+
+QuicConsumedData QuicPacketGenerator::ConsumeDataFastPath(
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ bool fin,
+ size_t total_bytes_consumed) {
+ DCHECK_NE(id,
+ QuicUtils::GetCryptoStreamId(packet_creator_.transport_version()));
+
+ while (total_bytes_consumed < write_length &&
+ delegate_->ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ // Serialize and encrypt the packet.
+ size_t bytes_consumed = 0;
+ packet_creator_.CreateAndSerializeStreamFrame(
+ id, write_length, total_bytes_consumed, offset + total_bytes_consumed,
+ fin, next_transmission_type_, &bytes_consumed);
+ total_bytes_consumed += bytes_consumed;
+ }
+
+ return QuicConsumedData(total_bytes_consumed,
+ fin && (total_bytes_consumed == write_length));
+}
+
+void QuicPacketGenerator::GenerateMtuDiscoveryPacket(QuicByteCount target_mtu) {
+ // MTU discovery frames must be sent by themselves.
+ if (!packet_creator_.CanSetMaxPacketLength()) {
+ QUIC_BUG << "MTU discovery packets should only be sent when no other "
+ << "frames needs to be sent.";
+ return;
+ }
+ const QuicByteCount current_mtu = GetCurrentMaxPacketLength();
+
+ // The MTU discovery frame is allocated on the stack, since it is going to be
+ // serialized within this function.
+ QuicMtuDiscoveryFrame mtu_discovery_frame;
+ QuicFrame frame(mtu_discovery_frame);
+
+ // Send the probe packet with the new length.
+ SetMaxPacketLength(target_mtu);
+ const bool success =
+ packet_creator_.AddPaddedSavedFrame(frame, next_transmission_type_);
+ packet_creator_.Flush();
+ // The only reason AddFrame can fail is that the packet is too full to fit in
+ // a ping. This is not possible for any sane MTU.
+ DCHECK(success);
+
+ // Reset the packet length back.
+ SetMaxPacketLength(current_mtu);
+}
+
+bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const {
+ DCHECK(HasPendingFrames() || packet_creator_.pending_padding_bytes() > 0);
+ HasRetransmittableData retransmittable =
+ (should_send_ack_ || should_send_stop_waiting_ ||
+ packet_creator_.pending_padding_bytes() > 0)
+ ? NO_RETRANSMITTABLE_DATA
+ : HAS_RETRANSMITTABLE_DATA;
+ if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
+ DCHECK(!queued_control_frames_.empty()); // These are retransmittable.
+ }
+ return delegate_->ShouldGeneratePacket(retransmittable, NOT_HANDSHAKE);
+}
+
+void QuicPacketGenerator::SendQueuedFrames(bool flush) {
+ // Only add pending frames if we are SURE we can then send the whole packet.
+ while (HasPendingFrames() &&
+ (flush || CanSendWithNextPendingFrameAddition())) {
+ bool first_frame = packet_creator_.CanSetMaxPacketLength();
+ if (!AddNextPendingFrame() && first_frame) {
+ // A single frame cannot fit into the packet, tear down the connection.
+ QUIC_BUG << "A single frame cannot fit into packet."
+ << " should_send_ack: " << should_send_ack_
+ << " should_send_stop_waiting: " << should_send_stop_waiting_
+ << " number of queued_control_frames: "
+ << queued_control_frames_.size();
+ if (!queued_control_frames_.empty()) {
+ QUIC_LOG(INFO) << queued_control_frames_[0];
+ }
+ delegate_->OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET,
+ "Single frame cannot fit into a packet",
+ ConnectionCloseSource::FROM_SELF);
+ return;
+ }
+ }
+ if (flush) {
+ packet_creator_.Flush();
+ }
+}
+
+bool QuicPacketGenerator::PacketFlusherAttached() const {
+ return flusher_attached_;
+}
+
+void QuicPacketGenerator::AttachPacketFlusher() {
+ flusher_attached_ = true;
+}
+
+void QuicPacketGenerator::Flush() {
+ SendQueuedFrames(/*flush=*/false);
+ packet_creator_.Flush();
+ SendRemainingPendingPadding();
+ flusher_attached_ = false;
+}
+
+void QuicPacketGenerator::FlushAllQueuedFrames() {
+ SendQueuedFrames(/*flush=*/true);
+}
+
+bool QuicPacketGenerator::HasQueuedFrames() const {
+ return packet_creator_.HasPendingFrames() || HasPendingFrames();
+}
+
+bool QuicPacketGenerator::IsPendingPacketEmpty() const {
+ return !packet_creator_.HasPendingFrames();
+}
+
+bool QuicPacketGenerator::HasPendingFrames() const {
+ return should_send_ack_ || should_send_stop_waiting_ ||
+ !queued_control_frames_.empty();
+}
+
+bool QuicPacketGenerator::AddNextPendingFrame() {
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to write control frames.";
+ if (should_send_ack_) {
+ should_send_ack_ = !packet_creator_.AddSavedFrame(
+ delegate_->GetUpdatedAckFrame(), next_transmission_type_);
+ return !should_send_ack_;
+ }
+
+ if (should_send_stop_waiting_) {
+ delegate_->PopulateStopWaitingFrame(&pending_stop_waiting_frame_);
+ // If we can't this add the frame now, then we still need to do so later.
+ should_send_stop_waiting_ = !packet_creator_.AddSavedFrame(
+ QuicFrame(pending_stop_waiting_frame_), next_transmission_type_);
+ // Return success if we have cleared out this flag (i.e., added the frame).
+ // If we still need to send, then the frame is full, and we have failed.
+ return !should_send_stop_waiting_;
+ }
+
+ QUIC_BUG_IF(queued_control_frames_.empty())
+ << "AddNextPendingFrame called with no queued control frames.";
+
+ if (!packet_creator_.AddSavedFrame(queued_control_frames_.back(),
+ next_transmission_type_)) {
+ // Packet was full.
+ return false;
+ }
+ queued_control_frames_.pop_back();
+ return true;
+}
+
+void QuicPacketGenerator::StopSendingVersion() {
+ packet_creator_.StopSendingVersion();
+}
+
+void QuicPacketGenerator::SetDiversificationNonce(
+ const DiversificationNonce& nonce) {
+ packet_creator_.SetDiversificationNonce(nonce);
+}
+
+QuicPacketNumber QuicPacketGenerator::packet_number() const {
+ return packet_creator_.packet_number();
+}
+
+QuicByteCount QuicPacketGenerator::GetCurrentMaxPacketLength() const {
+ return packet_creator_.max_packet_length();
+}
+
+void QuicPacketGenerator::SetMaxPacketLength(QuicByteCount length) {
+ DCHECK(packet_creator_.CanSetMaxPacketLength());
+ packet_creator_.SetMaxPacketLength(length);
+}
+
+std::unique_ptr<QuicEncryptedPacket>
+QuicPacketGenerator::SerializeVersionNegotiationPacket(
+ bool ietf_quic,
+ const ParsedQuicVersionVector& supported_versions) {
+ return packet_creator_.SerializeVersionNegotiationPacket(ietf_quic,
+ supported_versions);
+}
+
+OwningSerializedPacketPointer
+QuicPacketGenerator::SerializeConnectivityProbingPacket() {
+ return packet_creator_.SerializeConnectivityProbingPacket();
+}
+
+OwningSerializedPacketPointer
+QuicPacketGenerator::SerializePathChallengeConnectivityProbingPacket(
+ QuicPathFrameBuffer* payload) {
+ return packet_creator_.SerializePathChallengeConnectivityProbingPacket(
+ payload);
+}
+
+OwningSerializedPacketPointer
+QuicPacketGenerator::SerializePathResponseConnectivityProbingPacket(
+ const QuicDeque<QuicPathFrameBuffer>& payloads,
+ const bool is_padded) {
+ return packet_creator_.SerializePathResponseConnectivityProbingPacket(
+ payloads, is_padded);
+}
+
+void QuicPacketGenerator::ReserializeAllFrames(
+ const QuicPendingRetransmission& retransmission,
+ char* buffer,
+ size_t buffer_len) {
+ packet_creator_.ReserializeAllFrames(retransmission, buffer, buffer_len);
+}
+
+void QuicPacketGenerator::UpdatePacketNumberLength(
+ QuicPacketNumber least_packet_awaited_by_peer,
+ QuicPacketCount max_packets_in_flight) {
+ return packet_creator_.UpdatePacketNumberLength(least_packet_awaited_by_peer,
+ max_packets_in_flight);
+}
+
+void QuicPacketGenerator::SetConnectionIdLength(uint32_t length) {
+ if (length == 0) {
+ packet_creator_.SetConnectionIdIncluded(CONNECTION_ID_ABSENT);
+ } else {
+ packet_creator_.SetConnectionIdIncluded(CONNECTION_ID_PRESENT);
+ }
+}
+
+void QuicPacketGenerator::set_encryption_level(EncryptionLevel level) {
+ packet_creator_.set_encryption_level(level);
+}
+
+void QuicPacketGenerator::SetEncrypter(
+ EncryptionLevel level,
+ std::unique_ptr<QuicEncrypter> encrypter) {
+ packet_creator_.SetEncrypter(level, std::move(encrypter));
+}
+
+void QuicPacketGenerator::AddRandomPadding() {
+ packet_creator_.AddPendingPadding(
+ random_generator_->RandUint64() % kMaxNumRandomPaddingBytes + 1);
+}
+
+void QuicPacketGenerator::SendRemainingPendingPadding() {
+ while (packet_creator_.pending_padding_bytes() > 0 && !HasQueuedFrames() &&
+ CanSendWithNextPendingFrameAddition()) {
+ packet_creator_.Flush();
+ }
+}
+
+bool QuicPacketGenerator::HasRetransmittableFrames() const {
+ return !queued_control_frames_.empty() ||
+ packet_creator_.HasPendingRetransmittableFrames();
+}
+
+bool QuicPacketGenerator::HasPendingStreamFramesOfStream(
+ QuicStreamId id) const {
+ return packet_creator_.HasPendingStreamFramesOfStream(id);
+}
+
+void QuicPacketGenerator::SetTransmissionType(TransmissionType type) {
+ packet_creator_.SetTransmissionType(type);
+ if (packet_creator_.ShouldSetTransmissionTypeForNextFrame()) {
+ next_transmission_type_ = type;
+ }
+}
+
+void QuicPacketGenerator::SetCanSetTransmissionType(
+ bool can_set_transmission_type) {
+ packet_creator_.set_can_set_transmission_type(can_set_transmission_type);
+}
+
+MessageStatus QuicPacketGenerator::AddMessageFrame(QuicMessageId message_id,
+ QuicMemSliceSpan message) {
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to add message frame.";
+ if (deprecate_ack_bundling_mode_) {
+ MaybeBundleAckOpportunistically();
+ }
+ const QuicByteCount message_length = message.total_length();
+ if (message_length > GetCurrentLargestMessagePayload()) {
+ return MESSAGE_STATUS_TOO_LARGE;
+ }
+ SendQueuedFrames(/*flush=*/false);
+ if (!packet_creator_.HasRoomForMessageFrame(message_length)) {
+ packet_creator_.Flush();
+ }
+ QuicMessageFrame* frame = new QuicMessageFrame(message_id, message);
+ const bool success =
+ packet_creator_.AddSavedFrame(QuicFrame(frame), next_transmission_type_);
+ if (!success) {
+ QUIC_BUG << "Failed to send message " << message_id;
+ delete frame;
+ return MESSAGE_STATUS_INTERNAL_ERROR;
+ }
+ return MESSAGE_STATUS_SUCCESS;
+}
+
+void QuicPacketGenerator::MaybeBundleAckOpportunistically() {
+ DCHECK(deprecate_ack_bundling_mode_);
+ if (packet_creator_.has_ack()) {
+ // Ack already queued, nothing to do.
+ return;
+ }
+ if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ return;
+ }
+ const bool flushed =
+ FlushAckFrame(delegate_->MaybeBundleAckOpportunistically());
+ DCHECK(flushed);
+}
+
+bool QuicPacketGenerator::FlushAckFrame(const QuicFrames& frames) {
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to send ACK frame.";
+ for (const auto& frame : frames) {
+ DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME);
+ if (packet_creator_.HasPendingFrames()) {
+ if (packet_creator_.AddSavedFrame(frame, next_transmission_type_)) {
+ // There is pending frames and current frame fits.
+ continue;
+ }
+ }
+ DCHECK(!packet_creator_.HasPendingFrames());
+ // There is no pending frames, consult the delegate whether a packet can be
+ // generated.
+ if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ return false;
+ }
+ const bool success =
+ packet_creator_.AddSavedFrame(frame, next_transmission_type_);
+ QUIC_BUG_IF(!success) << "Failed to flush " << frame;
+ }
+ return true;
+}
+
+QuicPacketLength QuicPacketGenerator::GetCurrentLargestMessagePayload() const {
+ return packet_creator_.GetCurrentLargestMessagePayload();
+}
+
+QuicPacketLength QuicPacketGenerator::GetGuaranteedLargestMessagePayload()
+ const {
+ return packet_creator_.GetGuaranteedLargestMessagePayload();
+}
+
+void QuicPacketGenerator::SetConnectionId(QuicConnectionId connection_id) {
+ packet_creator_.SetConnectionId(connection_id);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.h
new file mode 100644
index 00000000000..dc191a0eba2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator.h
@@ -0,0 +1,316 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Responsible for generating packets on behalf of a QuicConnection.
+// Packets are serialized just-in-time. Control frames are queued.
+// Ack and Feedback frames will be requested from the Connection
+// just-in-time. When a packet needs to be sent, the Generator
+// will serialize a packet and pass it to QuicConnection::SendOrQueuePacket()
+//
+// The Generator's mode of operation is controlled by two conditions:
+//
+// 1) Is the Delegate writable?
+//
+// If the Delegate is not writable, then no operations will cause
+// a packet to be serialized. In particular:
+// * SetShouldSendAck will simply record that an ack is to be sent.
+// * AddControlFrame will enqueue the control frame.
+// * ConsumeData will do nothing.
+//
+// If the Delegate is writable, then the behavior depends on the second
+// condition:
+//
+// 2) Is the Generator in batch mode?
+//
+// If the Generator is NOT in batch mode, then each call to a write
+// operation will serialize one or more packets. The contents will
+// include any previous queued frames. If an ack should be sent
+// but has not been sent, then the Delegate will be asked to create
+// an Ack frame which will then be included in the packet. When
+// the write call completes, the current packet will be serialized
+// and sent to the Delegate, even if it is not full.
+//
+// If the Generator is in batch mode, then each write operation will
+// add data to the "current" packet. When the current packet becomes
+// full, it will be serialized and sent to the packet. When batch
+// mode is ended via |FinishBatchOperations|, the current packet
+// will be serialzied, even if it is not full.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_GENERATOR_H_
+#define QUICHE_QUIC_CORE_QUIC_PACKET_GENERATOR_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <list>
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+
+namespace quic {
+
+namespace test {
+class QuicPacketGeneratorPeer;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE QuicPacketGenerator {
+ public:
+ class QUIC_EXPORT_PRIVATE DelegateInterface
+ : public QuicPacketCreator::DelegateInterface {
+ public:
+ ~DelegateInterface() override {}
+ // Consults delegate whether a packet should be generated.
+ virtual bool ShouldGeneratePacket(HasRetransmittableData retransmittable,
+ IsHandshake handshake) = 0;
+ // Called when there is data to be sent. Retrieves updated ACK frame from
+ // the delegate.
+ virtual const QuicFrames MaybeBundleAckOpportunistically() = 0;
+ // TODO(fayang): Remove these two interfaces when deprecating
+ // quic_deprecate_ack_bundling_mode.
+ virtual const QuicFrame GetUpdatedAckFrame() = 0;
+ virtual void PopulateStopWaitingFrame(
+ QuicStopWaitingFrame* stop_waiting) = 0;
+ };
+
+ QuicPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate);
+ QuicPacketGenerator(const QuicPacketGenerator&) = delete;
+ QuicPacketGenerator& operator=(const QuicPacketGenerator&) = delete;
+
+ ~QuicPacketGenerator();
+
+ // Indicates that an ACK frame should be sent.
+ // If |also_send_stop_waiting| is true, then it also indicates that a
+ // STOP_WAITING frame should be sent as well.
+ // The contents of the frame(s) will be generated via a call to the delegate
+ // CreateAckFrame() when the packet is serialized.
+ void SetShouldSendAck(bool also_send_stop_waiting);
+
+ void AddControlFrame(const QuicFrame& frame);
+
+ // Given some data, may consume part or all of it and pass it to the
+ // packet creator to be serialized into packets. If not in batch
+ // mode, these packets will also be sent during this call.
+ // When |state| is FIN_AND_PADDING, random padding of size [1, 256] will be
+ // added after stream frames. If current constructed packet cannot
+ // accommodate, the padding will overflow to the next packet(s).
+ QuicConsumedData ConsumeData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state);
+
+ // Consumes data for CRYPTO frames sent at |level| starting at |offset| for a
+ // total of |write_length| bytes, and returns the number of bytes consumed.
+ // The data is passed into the packet creator and serialized into one or more
+ // packets.
+ size_t ConsumeCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset);
+
+ // Sends as many data only packets as allowed by the send algorithm and the
+ // available iov.
+ // This path does not support padding, or bundling pending frames.
+ // In case we access this method from ConsumeData, total_bytes_consumed
+ // keeps track of how many bytes have already been consumed.
+ QuicConsumedData ConsumeDataFastPath(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ bool fin,
+ size_t total_bytes_consumed);
+
+ // Generates an MTU discovery packet of specified size.
+ void GenerateMtuDiscoveryPacket(QuicByteCount target_mtu);
+
+ // Indicates whether packet flusher is currently attached.
+ bool PacketFlusherAttached() const;
+ // Attaches packet flusher.
+ void AttachPacketFlusher();
+ // Flushes everything, including all queued frames and pending padding.
+ void Flush();
+
+ // Flushes all queued frames, even frames which are not sendable.
+ void FlushAllQueuedFrames();
+
+ bool HasQueuedFrames() const;
+
+ // Whether the pending packet has no frames in it at the moment.
+ bool IsPendingPacketEmpty() const;
+
+ // Makes the framer not serialize the protocol version in sent packets.
+ void StopSendingVersion();
+
+ // SetDiversificationNonce sets the nonce that will be sent in each public
+ // header of packets encrypted at the initial encryption level. Should only
+ // be called by servers.
+ void SetDiversificationNonce(const DiversificationNonce& nonce);
+
+ // Creates a version negotiation packet which supports |supported_versions|.
+ std::unique_ptr<QuicEncryptedPacket> SerializeVersionNegotiationPacket(
+ bool ietf_quic,
+ const ParsedQuicVersionVector& supported_versions);
+
+ // Creates a connectivity probing packet.
+ OwningSerializedPacketPointer SerializeConnectivityProbingPacket();
+
+ // Create connectivity probing request and response packets using PATH
+ // CHALLENGE and PATH RESPONSE frames, respectively.
+ // SerializePathChallengeConnectivityProbingPacket will pad the packet to be
+ // MTU bytes long.
+ OwningSerializedPacketPointer SerializePathChallengeConnectivityProbingPacket(
+ QuicPathFrameBuffer* payload);
+
+ // If |is_padded| is true then SerializePathResponseConnectivityProbingPacket
+ // will pad the packet to be MTU bytes long, else it will not pad the packet.
+ // |payloads| is cleared.
+ OwningSerializedPacketPointer SerializePathResponseConnectivityProbingPacket(
+ const QuicDeque<QuicPathFrameBuffer>& payloads,
+ const bool is_padded);
+
+ // Re-serializes frames with the original packet's packet number length.
+ // Used for retransmitting packets to ensure they aren't too long.
+ void ReserializeAllFrames(const QuicPendingRetransmission& retransmission,
+ char* buffer,
+ size_t buffer_len);
+
+ // Update the packet number length to use in future packets as soon as it
+ // can be safely changed.
+ void UpdatePacketNumberLength(QuicPacketNumber least_packet_awaited_by_peer,
+ QuicPacketCount max_packets_in_flight);
+
+ // Set the minimum number of bytes for the connection id length;
+ void SetConnectionIdLength(uint32_t length);
+
+ // Sets the encrypter to use for the encryption level.
+ void SetEncrypter(EncryptionLevel level,
+ std::unique_ptr<QuicEncrypter> encrypter);
+
+ // Returns true if there are control frames or current constructed packet has
+ // pending retransmittable frames.
+ bool HasRetransmittableFrames() const;
+
+ // Returns true if current constructed packet has pending stream frames for
+ // stream |id|.
+ bool HasPendingStreamFramesOfStream(QuicStreamId id) const;
+
+ // Sets the encryption level that will be applied to new packets.
+ void set_encryption_level(EncryptionLevel level);
+
+ // packet number of the last created packet, or 0 if no packets have been
+ // created.
+ QuicPacketNumber packet_number() const;
+
+ // Returns the maximum length a current packet can actually have.
+ QuicByteCount GetCurrentMaxPacketLength() const;
+
+ // Set maximum packet length in the creator immediately. May not be called
+ // when there are frames queued in the creator.
+ void SetMaxPacketLength(QuicByteCount length);
+
+ // Set transmission type of next constructed packets.
+ void SetTransmissionType(TransmissionType type);
+
+ // Allow/Disallow setting transmission type of next constructed packets.
+ void SetCanSetTransmissionType(bool can_set_transmission_type);
+
+ // Tries to add a message frame containing |message| and returns the status.
+ MessageStatus AddMessageFrame(QuicMessageId message_id,
+ QuicMemSliceSpan message);
+
+ // Called to flush ACK and STOP_WAITING frames, returns false if the flush
+ // fails.
+ bool FlushAckFrame(const QuicFrames& frames);
+
+ // Returns the largest payload that will fit into a single MESSAGE frame.
+ QuicPacketLength GetCurrentLargestMessagePayload() const;
+ QuicPacketLength GetGuaranteedLargestMessagePayload() const;
+
+ // Update the connection ID used in outgoing packets.
+ void SetConnectionId(QuicConnectionId connection_id);
+
+ void set_debug_delegate(QuicPacketCreator::DebugDelegate* debug_delegate) {
+ packet_creator_.set_debug_delegate(debug_delegate);
+ }
+
+ bool should_send_ack() const { return should_send_ack_; }
+
+ void set_fully_pad_crypto_hadshake_packets(bool new_value) {
+ fully_pad_crypto_handshake_packets_ = new_value;
+ }
+
+ bool fully_pad_crypto_handshake_packets() const {
+ return fully_pad_crypto_handshake_packets_;
+ }
+
+ bool deprecate_ack_bundling_mode() const {
+ return deprecate_ack_bundling_mode_;
+ }
+
+ private:
+ friend class test::QuicPacketGeneratorPeer;
+
+ void SendQueuedFrames(bool flush);
+
+ // Test to see if we have pending ack, or control frames.
+ bool HasPendingFrames() const;
+ // Returns true if addition of a pending frame (which might be
+ // retransmittable) would still allow the resulting packet to be sent now.
+ bool CanSendWithNextPendingFrameAddition() const;
+ // Add exactly one pending frame, preferring ack frames over control frames.
+ // Returns true if a pending frame is successfully added.
+ // Returns false and flushes current open packet if the pending frame cannot
+ // fit into current open packet.
+ bool AddNextPendingFrame();
+
+ // Adds a random amount of padding (between 1 to 256 bytes).
+ void AddRandomPadding();
+
+ // Sends remaining pending padding.
+ // Pending paddings should only be sent when there is nothing else to send.
+ void SendRemainingPendingPadding();
+
+ // Called when there is data to be sent, Retrieves updated ACK frame from
+ // delegate_ and flushes it.
+ void MaybeBundleAckOpportunistically();
+
+ DelegateInterface* delegate_;
+
+ QuicPacketCreator packet_creator_;
+ QuicFrames queued_control_frames_;
+
+ // Transmission type of the next serialized packet.
+ TransmissionType next_transmission_type_;
+
+ // True if packet flusher is currently attached.
+ bool flusher_attached_;
+
+ // Flags to indicate the need for just-in-time construction of a frame.
+ // TODO(fayang): Remove these two booleans when deprecating
+ // quic_deprecate_ack_bundling_mode.
+ bool should_send_ack_;
+ bool should_send_stop_waiting_;
+ // If we put a non-retransmittable frame in this packet, then we have to hold
+ // a reference to it until we flush (and serialize it). Retransmittable frames
+ // are referenced elsewhere so that they can later be (optionally)
+ // retransmitted.
+ // TODO(fayang): Remove this when deprecating
+ // quic_deprecate_ack_bundling_mode.
+ QuicStopWaitingFrame pending_stop_waiting_frame_;
+
+ QuicRandom* random_generator_;
+
+ // Whether crypto handshake packets should be fully padded.
+ bool fully_pad_crypto_handshake_packets_;
+
+ // Latched value of quic_deprecate_ack_bundling_mode.
+ const bool deprecate_ack_bundling_mode_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PACKET_GENERATOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator_test.cc
new file mode 100644
index 00000000000..1e12424743e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_generator_test.cc
@@ -0,0 +1,1560 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_generator.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+
+using testing::_;
+using testing::InSequence;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockDelegate : public QuicPacketGenerator::DelegateInterface {
+ public:
+ MockDelegate() {}
+ MockDelegate(const MockDelegate&) = delete;
+ MockDelegate& operator=(const MockDelegate&) = delete;
+ ~MockDelegate() override {}
+
+ MOCK_METHOD2(ShouldGeneratePacket,
+ bool(HasRetransmittableData retransmittable,
+ IsHandshake handshake));
+ MOCK_METHOD0(MaybeBundleAckOpportunistically, const QuicFrames());
+ MOCK_METHOD0(GetUpdatedAckFrame, const QuicFrame());
+ MOCK_METHOD1(PopulateStopWaitingFrame, void(QuicStopWaitingFrame*));
+ MOCK_METHOD0(GetPacketBuffer, char*());
+ MOCK_METHOD1(OnSerializedPacket, void(SerializedPacket* packet));
+ MOCK_METHOD3(OnUnrecoverableError,
+ void(QuicErrorCode, const std::string&, ConnectionCloseSource));
+
+ void SetCanWriteAnything() {
+ EXPECT_CALL(*this, ShouldGeneratePacket(_, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _))
+ .WillRepeatedly(Return(true));
+ }
+
+ void SetCanNotWrite() {
+ EXPECT_CALL(*this, ShouldGeneratePacket(_, _))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _))
+ .WillRepeatedly(Return(false));
+ }
+
+ // Use this when only ack frames should be allowed to be written.
+ void SetCanWriteOnlyNonRetransmittable() {
+ EXPECT_CALL(*this, ShouldGeneratePacket(_, _))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*this, ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, _))
+ .WillRepeatedly(Return(true));
+ }
+};
+
+// Simple struct for describing the contents of a packet.
+// Useful in conjunction with a SimpleQuicFrame for validating that a packet
+// contains the expected frames.
+struct PacketContents {
+ PacketContents()
+ : num_ack_frames(0),
+ num_connection_close_frames(0),
+ num_goaway_frames(0),
+ num_rst_stream_frames(0),
+ num_stop_waiting_frames(0),
+ num_stream_frames(0),
+ num_crypto_frames(0),
+ num_ping_frames(0),
+ num_mtu_discovery_frames(0),
+ num_padding_frames(0) {}
+
+ size_t num_ack_frames;
+ size_t num_connection_close_frames;
+ size_t num_goaway_frames;
+ size_t num_rst_stream_frames;
+ size_t num_stop_waiting_frames;
+ size_t num_stream_frames;
+ size_t num_crypto_frames;
+ size_t num_ping_frames;
+ size_t num_mtu_discovery_frames;
+ size_t num_padding_frames;
+};
+
+} // namespace
+
+class TestPacketGenerator : public QuicPacketGenerator {
+ public:
+ TestPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate,
+ SimpleDataProducer* producer)
+ : QuicPacketGenerator(connection_id, framer, random_generator, delegate),
+ ack_frame_(InitAckFrame(1)),
+ delegate_(static_cast<MockDelegate*>(delegate)),
+ producer_(producer) {}
+
+ void AddControlFrame(const QuicFrame& frame, bool bundle_ack) {
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+ !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack()) {
+ QuicFrames frames;
+ if (bundle_ack) {
+ frames.push_back(QuicFrame(&ack_frame_));
+ }
+ if (delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically())
+ .WillOnce(Return(frames));
+ }
+ }
+ QuicPacketGenerator::AddControlFrame(frame);
+ }
+
+ QuicConsumedData ConsumeDataFastPath(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ size_t total_length,
+ QuicStreamOffset offset,
+ bool fin) {
+ // Save data before data is consumed.
+ if (total_length > 0) {
+ producer_->SaveStreamData(id, iov, iov_count, 0, total_length);
+ }
+ return QuicPacketGenerator::ConsumeDataFastPath(id, total_length, offset,
+ fin, 0);
+ }
+
+ QuicConsumedData ConsumeData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ size_t total_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ // Save data before data is consumed.
+ if (total_length > 0) {
+ producer_->SaveStreamData(id, iov, iov_count, 0, total_length);
+ }
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+ !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() &&
+ delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1);
+ }
+ return QuicPacketGenerator::ConsumeData(id, total_length, offset, state);
+ }
+
+ MessageStatus AddMessageFrame(QuicMessageId message_id,
+ QuicMemSliceSpan message) {
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+ !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() &&
+ delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1);
+ }
+ return QuicPacketGenerator::AddMessageFrame(message_id, message);
+ }
+
+ size_t ConsumeCryptoData(EncryptionLevel level,
+ QuicStringPiece data,
+ QuicStreamOffset offset) {
+ producer_->SaveCryptoData(level, offset, data);
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+ !QuicPacketGeneratorPeer::GetPacketCreator(this)->has_ack() &&
+ delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ EXPECT_CALL(*delegate_, MaybeBundleAckOpportunistically()).Times(1);
+ }
+ return QuicPacketGenerator::ConsumeCryptoData(level, data.length(), offset);
+ }
+
+ QuicAckFrame ack_frame_;
+ MockDelegate* delegate_;
+ SimpleDataProducer* producer_;
+};
+
+class QuicPacketGeneratorTest : public QuicTest {
+ public:
+ QuicPacketGeneratorTest()
+ : framer_(AllSupportedVersions(),
+ QuicTime::Zero(),
+ Perspective::IS_CLIENT,
+ kQuicDefaultConnectionIdLength),
+ generator_(TestConnectionId(),
+ &framer_,
+ &random_generator_,
+ &delegate_,
+ &producer_),
+ creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)),
+ ack_frame_(InitAckFrame(1)) {
+ EXPECT_CALL(delegate_, GetPacketBuffer()).WillRepeatedly(Return(nullptr));
+ creator_->SetEncrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullEncrypter>(Perspective::IS_CLIENT));
+ creator_->set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+ framer_.set_data_producer(&producer_);
+ if (simple_framer_.framer()->version().KnowsWhichDecrypterToUse()) {
+ simple_framer_.framer()->InstallDecrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullDecrypter>(Perspective::IS_SERVER));
+ }
+ generator_.AttachPacketFlusher();
+ }
+
+ ~QuicPacketGeneratorTest() override {
+ for (SerializedPacket& packet : packets_) {
+ delete[] packet.encrypted_buffer;
+ ClearSerializedPacket(&packet);
+ }
+ }
+
+ void SavePacket(SerializedPacket* packet) {
+ packet->encrypted_buffer = CopyBuffer(*packet);
+ packets_.push_back(*packet);
+ packet->encrypted_buffer = nullptr;
+ packet->retransmittable_frames.clear();
+ }
+
+ protected:
+ QuicRstStreamFrame* CreateRstStreamFrame() {
+ return new QuicRstStreamFrame(1, 1, QUIC_STREAM_NO_ERROR, 0);
+ }
+
+ QuicGoAwayFrame* CreateGoAwayFrame() {
+ return new QuicGoAwayFrame(2, QUIC_NO_ERROR, 1, std::string());
+ }
+
+ void CheckPacketContains(const PacketContents& contents,
+ size_t packet_index) {
+ ASSERT_GT(packets_.size(), packet_index);
+ const SerializedPacket& packet = packets_[packet_index];
+ size_t num_retransmittable_frames =
+ contents.num_connection_close_frames + contents.num_goaway_frames +
+ contents.num_rst_stream_frames + contents.num_stream_frames +
+ contents.num_crypto_frames + contents.num_ping_frames;
+ size_t num_frames =
+ contents.num_ack_frames + contents.num_stop_waiting_frames +
+ contents.num_mtu_discovery_frames + contents.num_padding_frames +
+ num_retransmittable_frames;
+
+ if (num_retransmittable_frames == 0) {
+ ASSERT_TRUE(packet.retransmittable_frames.empty());
+ } else {
+ ASSERT_FALSE(packet.retransmittable_frames.empty());
+ EXPECT_EQ(num_retransmittable_frames,
+ packet.retransmittable_frames.size());
+ }
+
+ ASSERT_TRUE(packet.encrypted_buffer != nullptr);
+ ASSERT_TRUE(simple_framer_.ProcessPacket(
+ QuicEncryptedPacket(packet.encrypted_buffer, packet.encrypted_length)));
+ EXPECT_EQ(num_frames, simple_framer_.num_frames());
+ EXPECT_EQ(contents.num_ack_frames, simple_framer_.ack_frames().size());
+ EXPECT_EQ(contents.num_connection_close_frames,
+ simple_framer_.connection_close_frames().size());
+ EXPECT_EQ(contents.num_goaway_frames,
+ simple_framer_.goaway_frames().size());
+ EXPECT_EQ(contents.num_rst_stream_frames,
+ simple_framer_.rst_stream_frames().size());
+ EXPECT_EQ(contents.num_stream_frames,
+ simple_framer_.stream_frames().size());
+ EXPECT_EQ(contents.num_crypto_frames,
+ simple_framer_.crypto_frames().size());
+ EXPECT_EQ(contents.num_stop_waiting_frames,
+ simple_framer_.stop_waiting_frames().size());
+ EXPECT_EQ(contents.num_padding_frames,
+ simple_framer_.padding_frames().size());
+
+ // From the receiver's perspective, MTU discovery frames are ping frames.
+ EXPECT_EQ(contents.num_ping_frames + contents.num_mtu_discovery_frames,
+ simple_framer_.ping_frames().size());
+ }
+
+ void CheckPacketHasSingleStreamFrame(size_t packet_index) {
+ ASSERT_GT(packets_.size(), packet_index);
+ const SerializedPacket& packet = packets_[packet_index];
+ ASSERT_FALSE(packet.retransmittable_frames.empty());
+ EXPECT_EQ(1u, packet.retransmittable_frames.size());
+ ASSERT_TRUE(packet.encrypted_buffer != nullptr);
+ ASSERT_TRUE(simple_framer_.ProcessPacket(
+ QuicEncryptedPacket(packet.encrypted_buffer, packet.encrypted_length)));
+ EXPECT_EQ(1u, simple_framer_.num_frames());
+ EXPECT_EQ(1u, simple_framer_.stream_frames().size());
+ }
+
+ void CheckAllPacketsHaveSingleStreamFrame() {
+ for (size_t i = 0; i < packets_.size(); i++) {
+ CheckPacketHasSingleStreamFrame(i);
+ }
+ }
+
+ void CreateData(size_t len) {
+ data_array_.reset(new char[len]);
+ memset(data_array_.get(), '?', len);
+ iov_.iov_base = data_array_.get();
+ iov_.iov_len = len;
+ }
+
+ QuicFramer framer_;
+ MockRandom random_generator_;
+ StrictMock<MockDelegate> delegate_;
+ TestPacketGenerator generator_;
+ QuicPacketCreator* creator_;
+ SimpleQuicFramer simple_framer_;
+ std::vector<SerializedPacket> packets_;
+ QuicAckFrame ack_frame_;
+ struct iovec iov_;
+ SimpleBufferAllocator allocator_;
+
+ private:
+ std::unique_ptr<char[]> data_array_;
+ SimpleDataProducer producer_;
+};
+
+class MockDebugDelegate : public QuicPacketCreator::DebugDelegate {
+ public:
+ MOCK_METHOD1(OnFrameAddedToPacket, void(const QuicFrame&));
+};
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) {
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ return;
+ }
+ delegate_.SetCanNotWrite();
+
+ generator_.SetShouldSendAck(false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) {
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ return;
+ }
+ StrictMock<MockDebugDelegate> debug_delegate;
+
+ generator_.set_debug_delegate(&debug_delegate);
+ delegate_.SetCanWriteOnlyNonRetransmittable();
+
+ EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+ .WillOnce(Return(QuicFrame(&ack_frame_)));
+ EXPECT_CALL(debug_delegate, OnFrameAddedToPacket(_)).Times(1);
+
+ generator_.SetShouldSendAck(false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) {
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ return;
+ }
+ delegate_.SetCanWriteOnlyNonRetransmittable();
+
+ EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+ .WillOnce(Return(QuicFrame(&ack_frame_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ generator_.SetShouldSendAck(false);
+ generator_.Flush();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ CheckPacketContains(contents, 0);
+}
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_MultipleCalls) {
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ return;
+ }
+ // Make sure that calling SetShouldSendAck multiple times does not result in a
+ // crash. Previously this would result in multiple QuicFrames queued in the
+ // packet generator, with all but the last with internal pointers to freed
+ // memory.
+ delegate_.SetCanWriteAnything();
+
+ // Only one AckFrame should be created.
+ EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+ .WillOnce(Return(QuicFrame(&ack_frame_)));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .Times(1)
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ generator_.SetShouldSendAck(false);
+ generator_.SetShouldSendAck(false);
+ generator_.Flush();
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritable) {
+ delegate_.SetCanNotWrite();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+ /*bundle_ack=*/false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_OnlyAckWritable) {
+ delegate_.SetCanWriteOnlyNonRetransmittable();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+ /*bundle_ack=*/false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) {
+ delegate_.SetCanWriteAnything();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+ /*bundle_ack=*/false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritableBatchThenFlush) {
+ delegate_.SetCanNotWrite();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+ /*bundle_ack=*/false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+ generator_.Flush();
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ generator_.AttachPacketFlusher();
+ generator_.FlushAllQueuedFrames();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_rst_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) {
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+ /*bundle_ack=*/false);
+ generator_.Flush();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_rst_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeCryptoData) {
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ std::string data = "crypto data";
+ size_t consumed_bytes =
+ generator_.ConsumeCryptoData(ENCRYPTION_INITIAL, data, 0);
+ generator_.Flush();
+ EXPECT_EQ(data.length(), consumed_bytes);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_crypto_frames = 1;
+ contents.num_padding_frames = 1;
+ CheckPacketContains(contents, 0);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) {
+ delegate_.SetCanNotWrite();
+
+ MakeIOVector("foo", &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ EXPECT_EQ(0u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) {
+ delegate_.SetCanWriteAnything();
+
+ MakeIOVector("foo", &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) {
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ MakeIOVector("foo", &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ generator_.Flush();
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+}
+
+// Test the behavior of ConsumeData when the data consumed is for the crypto
+// handshake stream. Ensure that the packet is always sent and padded even if
+// the generator operates in batch mode.
+TEST_F(QuicPacketGeneratorTest, ConsumeData_Handshake) {
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ MakeIOVector("foo", &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetCryptoStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, NO_FIN);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ contents.num_padding_frames = 1;
+ CheckPacketContains(contents, 0);
+
+ ASSERT_EQ(1u, packets_.size());
+ ASSERT_EQ(kDefaultMaxPacketSize, generator_.GetCurrentMaxPacketLength());
+ EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].encrypted_length);
+}
+
+// Test the behavior of ConsumeData when the data is for the crypto handshake
+// stream, but padding is disabled.
+TEST_F(QuicPacketGeneratorTest, ConsumeData_Handshake_PaddingDisabled) {
+ generator_.set_fully_pad_crypto_hadshake_packets(false);
+
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ MakeIOVector("foo", &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetCryptoStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, NO_FIN);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ contents.num_padding_frames = 0;
+ CheckPacketContains(contents, 0);
+
+ ASSERT_EQ(1u, packets_.size());
+
+ // Packet is not fully padded, but we want to future packets to be larger.
+ ASSERT_EQ(kDefaultMaxPacketSize, generator_.GetCurrentMaxPacketLength());
+ EXPECT_EQ(27, packets_[0].encrypted_length);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_EmptyData) {
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_QUIC_BUG(generator_.ConsumeData(QuicUtils::GetHeadersStreamId(
+ framer_.transport_version()),
+ nullptr, 0, 0, 0, NO_FIN),
+ "Attempt to consume empty data without FIN.");
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ ConsumeDataMultipleTimes_WritableAndShouldNotFlush) {
+ delegate_.SetCanWriteAnything();
+
+ MakeIOVector("foo", &iov_);
+ generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ MakeIOVector("quux", &iov_);
+ QuicConsumedData consumed =
+ generator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 3, NO_FIN);
+ EXPECT_EQ(4u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) {
+ delegate_.SetCanWriteAnything();
+
+ MakeIOVector("foo", &iov_);
+ generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ MakeIOVector("quux", &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 3, NO_FIN);
+ EXPECT_EQ(4u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ // Now both frames will be flushed out.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ generator_.Flush();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 2;
+ CheckPacketContains(contents, 0);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
+ // Set the packet size be enough for two stream frames with 0 stream offset,
+ // but not enough for a stream frame of 0 offset and one with non-zero offset.
+ size_t length =
+ NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
+ GetPacketHeaderSize(
+ framer_.transport_version(),
+ creator_->GetDestinationConnectionIdLength(),
+ creator_->GetSourceConnectionIdLength(),
+ QuicPacketCreatorPeer::SendVersionInPacket(creator_),
+ !kIncludeDiversificationNonce,
+ QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+ QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0,
+ QuicPacketCreatorPeer::GetLengthLength(creator_)) +
+ // Add an extra 3 bytes for the payload and 1 byte so
+ // BytesFree is larger than the GetMinStreamFrameSize.
+ QuicFramer::GetMinStreamFrameSize(framer_.transport_version(), 1, 0,
+ false, 3) +
+ 3 +
+ QuicFramer::GetMinStreamFrameSize(framer_.transport_version(), 1, 0, true,
+ 1) +
+ 1;
+ generator_.SetMaxPacketLength(length);
+ delegate_.SetCanWriteAnything();
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ }
+ // Queue enough data to prevent a stream frame with a non-zero offset from
+ // fitting.
+ MakeIOVector("foo", &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, NO_FIN);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ // This frame will not fit with the existing frame, causing the queued frame
+ // to be serialized, and it will be added to a new open packet.
+ MakeIOVector("bar", &iov_);
+ consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 3, FIN);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ creator_->Flush();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+ CheckPacketContains(contents, 1);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataFastPath) {
+ delegate_.SetCanWriteAnything();
+ generator_.SetCanSetTransmissionType(true);
+ generator_.SetTransmissionType(LOSS_RETRANSMISSION);
+
+ // Create a 10000 byte IOVector.
+ CreateData(10000);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ QuicConsumedData consumed = generator_.ConsumeDataFastPath(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, true);
+ EXPECT_EQ(10000u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+ EXPECT_FALSE(packets_.empty());
+ SerializedPacket packet = packets_.back();
+ EXPECT_TRUE(!packet.retransmittable_frames.empty());
+ EXPECT_EQ(LOSS_RETRANSMISSION, packet.transmission_type);
+ EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type);
+ const QuicStreamFrame& stream_frame =
+ packet.retransmittable_frames.front().stream_frame;
+ EXPECT_EQ(10000u, stream_frame.data_length + stream_frame.offset);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataLarge) {
+ delegate_.SetCanWriteAnything();
+
+ // Create a 10000 byte IOVector.
+ CreateData(10000);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ EXPECT_EQ(10000u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+ EXPECT_FALSE(packets_.empty());
+ SerializedPacket packet = packets_.back();
+ EXPECT_TRUE(!packet.retransmittable_frames.empty());
+ EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type);
+ const QuicStreamFrame& stream_frame =
+ packet.retransmittable_frames.front().stream_frame;
+ EXPECT_EQ(10000u, stream_frame.data_length + stream_frame.offset);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckFalse) {
+ delegate_.SetCanNotWrite();
+
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ generator_.SetShouldSendAck(false);
+ }
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+ /*bundle_ack=*/true);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ delegate_.SetCanWriteAnything();
+
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+ .WillOnce(Return(QuicFrame(&ack_frame_)));
+ }
+
+ // Create a 10000 byte IOVector.
+ CreateData(10000);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ generator_.Flush();
+
+ EXPECT_EQ(10000u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ EXPECT_FALSE(packets_.empty());
+ SerializedPacket packet = packets_.back();
+ EXPECT_TRUE(!packet.retransmittable_frames.empty());
+ EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type);
+ const QuicStreamFrame& stream_frame =
+ packet.retransmittable_frames.front().stream_frame;
+ EXPECT_EQ(10000u, stream_frame.data_length + stream_frame.offset);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckTrue) {
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+ delegate_.SetCanNotWrite();
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ generator_.SetShouldSendAck(true /* stop_waiting */);
+ }
+ delegate_.SetCanWriteAnything();
+
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ // Set up frames to write into the creator when control frames are written.
+ EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+ .WillOnce(Return(QuicFrame(&ack_frame_)));
+ EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_));
+ // Generator should have queued control frames, and creator should be empty.
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+ EXPECT_FALSE(creator_->HasPendingFrames());
+ }
+
+ // Create a 10000 byte IOVector.
+ CreateData(10000);
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ generator_.Flush();
+
+ EXPECT_EQ(10000u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ EXPECT_FALSE(packets_.empty());
+ SerializedPacket packet = packets_.back();
+ EXPECT_TRUE(!packet.retransmittable_frames.empty());
+ EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type);
+ const QuicStreamFrame& stream_frame =
+ packet.retransmittable_frames.front().stream_frame;
+ EXPECT_EQ(10000u, stream_frame.data_length + stream_frame.offset);
+}
+
+TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
+ delegate_.SetCanNotWrite();
+
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ generator_.SetShouldSendAck(false);
+ }
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+ /*bundle_ack=*/true);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+ EXPECT_FALSE(generator_.HasPendingStreamFramesOfStream(3));
+
+ delegate_.SetCanWriteAnything();
+
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ // When the first write operation is invoked, the ack frame will be
+ // returned.
+ EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+ .WillOnce(Return(QuicFrame(&ack_frame_)));
+ }
+
+ // Send some data and a control frame
+ MakeIOVector("quux", &iov_);
+ generator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 0, NO_FIN);
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()),
+ /*bundle_ack=*/false);
+ }
+ EXPECT_TRUE(generator_.HasPendingStreamFramesOfStream(3));
+
+ // All five frames will be flushed out in a single packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ generator_.Flush();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+ EXPECT_FALSE(generator_.HasPendingStreamFramesOfStream(3));
+
+ PacketContents contents;
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ // ACK will be flushed by connection.
+ contents.num_ack_frames = 0;
+ } else {
+ contents.num_ack_frames = 1;
+ }
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ contents.num_goaway_frames = 1;
+ } else {
+ contents.num_goaway_frames = 0;
+ }
+ contents.num_rst_stream_frames = 1;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+}
+
+TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
+ delegate_.SetCanNotWrite();
+
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ generator_.SetShouldSendAck(false);
+ }
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()),
+ /*bundle_ack=*/true);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ delegate_.SetCanWriteAnything();
+
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ // When the first write operation is invoked, the ack frame will be
+ // returned.
+ EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+ .WillOnce(Return(QuicFrame(&ack_frame_)));
+ }
+
+ {
+ InSequence dummy;
+ // All five frames will be flushed out in a single packet
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ }
+
+ // Send enough data to exceed one packet
+ size_t data_len = kDefaultMaxPacketSize + 100;
+ CreateData(data_len);
+ QuicConsumedData consumed =
+ generator_.ConsumeData(3, &iov_, 1u, iov_.iov_len, 0, FIN);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()),
+ /*bundle_ack=*/false);
+ }
+
+ generator_.Flush();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ // The first packet should have the queued data and part of the stream data.
+ PacketContents contents;
+ if (GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ // ACK will be sent by connection.
+ contents.num_ack_frames = 0;
+ } else {
+ contents.num_ack_frames = 1;
+ }
+ contents.num_rst_stream_frames = 1;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+
+ // The second should have the remainder of the stream data.
+ PacketContents contents2;
+ if (framer_.transport_version() != QUIC_VERSION_99) {
+ contents2.num_goaway_frames = 1;
+ } else {
+ contents2.num_goaway_frames = 0;
+ }
+ contents2.num_stream_frames = 1;
+ CheckPacketContains(contents2, 1);
+}
+
+// Regression test of b/120493795.
+TEST_F(QuicPacketGeneratorTest, PacketTransmissionType) {
+ delegate_.SetCanWriteAnything();
+ generator_.SetCanSetTransmissionType(true);
+
+ // The first ConsumeData will fill the packet without flush.
+ generator_.SetTransmissionType(LOSS_RETRANSMISSION);
+
+ size_t data_len = 1324;
+ CreateData(data_len);
+ QuicStreamId stream1_id =
+ QuicUtils::GetHeadersStreamId(framer_.transport_version());
+ QuicConsumedData consumed =
+ generator_.ConsumeData(stream1_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ ASSERT_EQ(0u, creator_->BytesFree())
+ << "Test setup failed: Please increase data_len to "
+ << data_len + creator_->BytesFree() << " bytes.";
+
+ // The second ConsumeData can not be added to the packet and will flush.
+ generator_.SetTransmissionType(NOT_RETRANSMISSION);
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ QuicStreamId stream2_id = stream1_id + 4;
+
+ consumed =
+ generator_.ConsumeData(stream2_id, &iov_, 1u, iov_.iov_len, 0, NO_FIN);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+
+ // Ensure the packet is successfully created.
+ ASSERT_EQ(1u, packets_.size());
+ ASSERT_TRUE(packets_[0].encrypted_buffer);
+ ASSERT_EQ(1u, packets_[0].retransmittable_frames.size());
+ EXPECT_EQ(stream1_id,
+ packets_[0].retransmittable_frames[0].stream_frame.stream_id);
+ if (GetQuicReloadableFlag(quic_set_transmission_type_for_next_frame)) {
+ // Since the second frame was not added, the packet's transmission type
+ // should be the first frame's type.
+ EXPECT_EQ(packets_[0].transmission_type, LOSS_RETRANSMISSION);
+ } else {
+ EXPECT_EQ(packets_[0].transmission_type, NOT_RETRANSMISSION);
+ }
+}
+
+TEST_F(QuicPacketGeneratorTest, TestConnectionIdLength) {
+ QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+ generator_.SetConnectionIdLength(0);
+ EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID,
+ creator_->GetDestinationConnectionIdLength());
+
+ for (size_t i = 1; i < 10; i++) {
+ generator_.SetConnectionIdLength(i);
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ EXPECT_EQ(PACKET_0BYTE_CONNECTION_ID,
+ creator_->GetDestinationConnectionIdLength());
+ } else {
+ EXPECT_EQ(PACKET_8BYTE_CONNECTION_ID,
+ creator_->GetDestinationConnectionIdLength());
+ }
+ }
+}
+
+// Test whether SetMaxPacketLength() works in the situation when the queue is
+// empty, and we send three packets worth of data.
+TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Initial) {
+ delegate_.SetCanWriteAnything();
+
+ // Send enough data for three packets.
+ size_t data_len = 3 * kDefaultMaxPacketSize + 1;
+ size_t packet_len = kDefaultMaxPacketSize + 100;
+ ASSERT_LE(packet_len, kMaxOutgoingPacketSize);
+ generator_.SetMaxPacketLength(packet_len);
+ EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength());
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .Times(3)
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ CreateData(data_len);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len,
+ /*offset=*/0, FIN);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ // We expect three packets, and first two of them have to be of packet_len
+ // size. We check multiple packets (instead of just one) because we want to
+ // ensure that |max_packet_length_| does not get changed incorrectly by the
+ // generator after first packet is serialized.
+ ASSERT_EQ(3u, packets_.size());
+ EXPECT_EQ(packet_len, packets_[0].encrypted_length);
+ EXPECT_EQ(packet_len, packets_[1].encrypted_length);
+ CheckAllPacketsHaveSingleStreamFrame();
+}
+
+// Test whether SetMaxPacketLength() works in the situation when we first write
+// data, then change packet size, then write data again.
+TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_Middle) {
+ delegate_.SetCanWriteAnything();
+
+ // We send enough data to overflow default packet length, but not the altered
+ // one.
+ size_t data_len = kDefaultMaxPacketSize;
+ size_t packet_len = kDefaultMaxPacketSize + 100;
+ ASSERT_LE(packet_len, kMaxOutgoingPacketSize);
+
+ // We expect to see three packets in total.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .Times(3)
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ // Send two packets before packet size change.
+ CreateData(data_len);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len,
+ /*offset=*/0, NO_FIN);
+ generator_.Flush();
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ // Make sure we already have two packets.
+ ASSERT_EQ(2u, packets_.size());
+
+ // Increase packet size.
+ generator_.SetMaxPacketLength(packet_len);
+ EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength());
+
+ // Send a packet after packet size change.
+ CreateData(data_len);
+ generator_.AttachPacketFlusher();
+ consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, data_len, FIN);
+ generator_.Flush();
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ // We expect first data chunk to get fragmented, but the second one to fit
+ // into a single packet.
+ ASSERT_EQ(3u, packets_.size());
+ EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].encrypted_length);
+ EXPECT_LE(kDefaultMaxPacketSize, packets_[2].encrypted_length);
+ CheckAllPacketsHaveSingleStreamFrame();
+}
+
+// Test whether SetMaxPacketLength() works correctly when we force the change of
+// the packet size in the middle of the batched packet.
+TEST_F(QuicPacketGeneratorTest, SetMaxPacketLength_MidpacketFlush) {
+ delegate_.SetCanWriteAnything();
+
+ size_t first_write_len = kDefaultMaxPacketSize / 2;
+ size_t packet_len = kDefaultMaxPacketSize + 100;
+ size_t second_write_len = packet_len + 1;
+ ASSERT_LE(packet_len, kMaxOutgoingPacketSize);
+
+ // First send half of the packet worth of data. We are in the batch mode, so
+ // should not cause packet serialization.
+ CreateData(first_write_len);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len,
+ /*offset=*/0, NO_FIN);
+ EXPECT_EQ(first_write_len, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ // Make sure we have no packets so far.
+ ASSERT_EQ(0u, packets_.size());
+
+ // Expect a packet to be flushed.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ // Increase packet size after flushing all frames.
+ // Ensure it's immediately enacted.
+ generator_.FlushAllQueuedFrames();
+ generator_.SetMaxPacketLength(packet_len);
+ EXPECT_EQ(packet_len, generator_.GetCurrentMaxPacketLength());
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ // We expect to see exactly one packet serialized after that, because we send
+ // a value somewhat exceeding new max packet size, and the tail data does not
+ // get serialized because we are still in the batch mode.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ // Send a more than a packet worth of data to the same stream. This should
+ // trigger serialization of one packet, and queue another one.
+ CreateData(second_write_len);
+ consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len,
+ /*offset=*/first_write_len, FIN);
+ EXPECT_EQ(second_write_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ // We expect the first packet to be underfilled, and the second packet be up
+ // to the new max packet size.
+ ASSERT_EQ(2u, packets_.size());
+ EXPECT_GT(kDefaultMaxPacketSize, packets_[0].encrypted_length);
+ EXPECT_EQ(packet_len, packets_[1].encrypted_length);
+
+ CheckAllPacketsHaveSingleStreamFrame();
+}
+
+// Test sending a connectivity probing packet.
+TEST_F(QuicPacketGeneratorTest, GenerateConnectivityProbingPacket) {
+ delegate_.SetCanWriteAnything();
+
+ OwningSerializedPacketPointer probing_packet;
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ QuicPathFrameBuffer payload = {
+ {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}};
+ probing_packet =
+ generator_.SerializePathChallengeConnectivityProbingPacket(&payload);
+ } else {
+ probing_packet = generator_.SerializeConnectivityProbingPacket();
+ }
+
+ ASSERT_TRUE(simple_framer_.ProcessPacket(QuicEncryptedPacket(
+ probing_packet->encrypted_buffer, probing_packet->encrypted_length)));
+
+ EXPECT_EQ(2u, simple_framer_.num_frames());
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ EXPECT_EQ(1u, simple_framer_.path_challenge_frames().size());
+ } else {
+ EXPECT_EQ(1u, simple_framer_.ping_frames().size());
+ }
+ EXPECT_EQ(1u, simple_framer_.padding_frames().size());
+}
+
+// Test sending an MTU probe, without any surrounding data.
+TEST_F(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_Simple) {
+ delegate_.SetCanWriteAnything();
+
+ const size_t target_mtu = kDefaultMaxPacketSize + 100;
+ static_assert(target_mtu < kMaxOutgoingPacketSize,
+ "The MTU probe used by the test exceeds maximum packet size");
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ generator_.GenerateMtuDiscoveryPacket(target_mtu);
+
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+ ASSERT_EQ(1u, packets_.size());
+ EXPECT_EQ(target_mtu, packets_[0].encrypted_length);
+
+ PacketContents contents;
+ contents.num_mtu_discovery_frames = 1;
+ contents.num_padding_frames = 1;
+ CheckPacketContains(contents, 0);
+}
+
+// Test sending an MTU probe. Surround it with data, to ensure that it resets
+// the MTU to the value before the probe was sent.
+TEST_F(QuicPacketGeneratorTest, GenerateMtuDiscoveryPacket_SurroundedByData) {
+ delegate_.SetCanWriteAnything();
+
+ const size_t target_mtu = kDefaultMaxPacketSize + 100;
+ static_assert(target_mtu < kMaxOutgoingPacketSize,
+ "The MTU probe used by the test exceeds maximum packet size");
+
+ // Send enough data so it would always cause two packets to be sent.
+ const size_t data_len = target_mtu + 1;
+
+ // Send a total of five packets: two packets before the probe, the probe
+ // itself, and two packets after the probe.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .Times(5)
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ // Send data before the MTU probe.
+ CreateData(data_len);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len,
+ /*offset=*/0, NO_FIN);
+ generator_.Flush();
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ // Send the MTU probe.
+ generator_.GenerateMtuDiscoveryPacket(target_mtu);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ // Send data after the MTU probe.
+ CreateData(data_len);
+ generator_.AttachPacketFlusher();
+ consumed = generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len,
+ /*offset=*/data_len, FIN);
+ generator_.Flush();
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ ASSERT_EQ(5u, packets_.size());
+ EXPECT_EQ(kDefaultMaxPacketSize, packets_[0].encrypted_length);
+ EXPECT_EQ(target_mtu, packets_[2].encrypted_length);
+ EXPECT_EQ(kDefaultMaxPacketSize, packets_[3].encrypted_length);
+
+ PacketContents probe_contents;
+ probe_contents.num_mtu_discovery_frames = 1;
+ probe_contents.num_padding_frames = 1;
+
+ CheckPacketHasSingleStreamFrame(0);
+ CheckPacketHasSingleStreamFrame(1);
+ CheckPacketContains(probe_contents, 2);
+ CheckPacketHasSingleStreamFrame(3);
+ CheckPacketHasSingleStreamFrame(4);
+}
+
+TEST_F(QuicPacketGeneratorTest, DontCrashOnInvalidStopWaiting) {
+ if (framer_.transport_version() > QUIC_VERSION_43) {
+ return;
+ }
+ // Test added to ensure the generator does not crash when an invalid frame is
+ // added. Because this is an indication of internal programming errors,
+ // DFATALs are expected.
+ // A 1 byte packet number length can't encode a gap of 1000.
+ QuicPacketCreatorPeer::SetPacketNumber(creator_, 1000);
+
+ delegate_.SetCanNotWrite();
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ generator_.SetShouldSendAck(true);
+ }
+ delegate_.SetCanWriteAnything();
+
+ if (!GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode)) {
+ // Set up frames to write into the creator when control frames are written.
+ EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+ .WillOnce(Return(QuicFrame(&ack_frame_)));
+ EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_));
+ // Generator should have queued control frames, and creator should be empty.
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+ EXPECT_FALSE(creator_->HasPendingFrames());
+ }
+
+ // This will not serialize any packets, because of the invalid frame.
+ EXPECT_CALL(delegate_,
+ OnUnrecoverableError(QUIC_FAILED_TO_SERIALIZE_PACKET, _,
+ ConnectionCloseSource::FROM_SELF));
+ EXPECT_QUIC_BUG(generator_.Flush(),
+ "packet_number_length 1 is too small "
+ "for least_unacked_delta: 1001");
+}
+
+// Regression test for b/31486443.
+TEST_F(QuicPacketGeneratorTest, ConnectionCloseFrameLargerThanPacketSize) {
+ delegate_.SetCanWriteAnything();
+ char buf[2000] = {};
+ QuicStringPiece error_details(buf, 2000);
+ QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(
+ QUIC_PACKET_WRITE_ERROR, std::string(error_details));
+ if (framer_.transport_version() == QUIC_VERSION_99) {
+ frame->close_type = IETF_QUIC_TRANSPORT_CONNECTION_CLOSE;
+ }
+ generator_.AddControlFrame(QuicFrame(frame), /*bundle_ack=*/false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, RandomPaddingAfterFinSingleStreamSinglePacket) {
+ const QuicByteCount kStreamFramePayloadSize = 100u;
+ char buf[kStreamFramePayloadSize] = {};
+ const QuicStreamId kDataStreamId = 5;
+ // Set the packet size be enough for one stream frame with 0 stream offset and
+ // max size of random padding.
+ size_t length =
+ NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
+ GetPacketHeaderSize(
+ framer_.transport_version(),
+ creator_->GetDestinationConnectionIdLength(),
+ creator_->GetSourceConnectionIdLength(),
+ QuicPacketCreatorPeer::SendVersionInPacket(creator_),
+ !kIncludeDiversificationNonce,
+ QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+ QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0,
+ QuicPacketCreatorPeer::GetLengthLength(creator_)) +
+ QuicFramer::GetMinStreamFrameSize(
+ framer_.transport_version(), kDataStreamId, 0,
+ /*last_frame_in_packet=*/false,
+ kStreamFramePayloadSize + kMaxNumRandomPaddingBytes) +
+ kStreamFramePayloadSize + kMaxNumRandomPaddingBytes;
+ generator_.SetMaxPacketLength(length);
+ delegate_.SetCanWriteAnything();
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kDataStreamId, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING);
+ generator_.Flush();
+ EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ EXPECT_EQ(1u, packets_.size());
+ PacketContents contents;
+ // The packet has both stream and padding frames.
+ contents.num_padding_frames = 1;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, 0);
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ RandomPaddingAfterFinSingleStreamMultiplePackets) {
+ const QuicByteCount kStreamFramePayloadSize = 100u;
+ char buf[kStreamFramePayloadSize] = {};
+ const QuicStreamId kDataStreamId = 5;
+ // Set the packet size be enough for one stream frame with 0 stream offset +
+ // 1. One or more packets will accommodate.
+ size_t length =
+ NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
+ GetPacketHeaderSize(
+ framer_.transport_version(),
+ creator_->GetDestinationConnectionIdLength(),
+ creator_->GetSourceConnectionIdLength(),
+ QuicPacketCreatorPeer::SendVersionInPacket(creator_),
+ !kIncludeDiversificationNonce,
+ QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+ QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0,
+ QuicPacketCreatorPeer::GetLengthLength(creator_)) +
+ QuicFramer::GetMinStreamFrameSize(
+ framer_.transport_version(), kDataStreamId, 0,
+ /*last_frame_in_packet=*/false, kStreamFramePayloadSize + 1) +
+ kStreamFramePayloadSize + 1;
+ generator_.SetMaxPacketLength(length);
+ delegate_.SetCanWriteAnything();
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kDataStreamId, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING);
+ generator_.Flush();
+ EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ EXPECT_LE(1u, packets_.size());
+ PacketContents contents;
+ // The first packet has both stream and padding frames.
+ contents.num_stream_frames = 1;
+ contents.num_padding_frames = 1;
+ CheckPacketContains(contents, 0);
+
+ for (size_t i = 1; i < packets_.size(); ++i) {
+ // Following packets only have paddings.
+ contents.num_stream_frames = 0;
+ contents.num_padding_frames = 1;
+ CheckPacketContains(contents, i);
+ }
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ RandomPaddingAfterFinMultipleStreamsMultiplePackets) {
+ const QuicByteCount kStreamFramePayloadSize = 100u;
+ char buf[kStreamFramePayloadSize] = {};
+ const QuicStreamId kDataStreamId1 = 5;
+ const QuicStreamId kDataStreamId2 = 6;
+ // Set the packet size be enough for first frame with 0 stream offset + second
+ // frame + 1 byte payload. two or more packets will accommodate.
+ size_t length =
+ NullEncrypter(Perspective::IS_CLIENT).GetCiphertextSize(0) +
+ GetPacketHeaderSize(
+ framer_.transport_version(),
+ creator_->GetDestinationConnectionIdLength(),
+ creator_->GetSourceConnectionIdLength(),
+ QuicPacketCreatorPeer::SendVersionInPacket(creator_),
+ !kIncludeDiversificationNonce,
+ QuicPacketCreatorPeer::GetPacketNumberLength(creator_),
+ QuicPacketCreatorPeer::GetRetryTokenLengthLength(creator_), 0,
+ QuicPacketCreatorPeer::GetLengthLength(creator_)) +
+ QuicFramer::GetMinStreamFrameSize(
+ framer_.transport_version(), kDataStreamId1, 0,
+ /*last_frame_in_packet=*/false, kStreamFramePayloadSize) +
+ kStreamFramePayloadSize +
+ QuicFramer::GetMinStreamFrameSize(framer_.transport_version(),
+ kDataStreamId1, 0,
+ /*last_frame_in_packet=*/false, 1) +
+ 1;
+ generator_.SetMaxPacketLength(length);
+ delegate_.SetCanWriteAnything();
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+ MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kDataStreamId1, &iov_, 1u, iov_.iov_len, 0, FIN_AND_PADDING);
+ EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed);
+ MakeIOVector(QuicStringPiece(buf, kStreamFramePayloadSize), &iov_);
+ consumed = generator_.ConsumeData(kDataStreamId2, &iov_, 1u, iov_.iov_len, 0,
+ FIN_AND_PADDING);
+ EXPECT_EQ(kStreamFramePayloadSize, consumed.bytes_consumed);
+ generator_.Flush();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+ EXPECT_LE(2u, packets_.size());
+ PacketContents contents;
+ // The first packet has two stream frames.
+ contents.num_stream_frames = 2;
+ CheckPacketContains(contents, 0);
+
+ // The second packet has one stream frame and padding frames.
+ contents.num_stream_frames = 1;
+ contents.num_padding_frames = 1;
+ CheckPacketContains(contents, 1);
+
+ for (size_t i = 2; i < packets_.size(); ++i) {
+ // Following packets only have paddings.
+ contents.num_stream_frames = 0;
+ contents.num_padding_frames = 1;
+ CheckPacketContains(contents, i);
+ }
+}
+
+TEST_F(QuicPacketGeneratorTest, AddMessageFrame) {
+ if (framer_.transport_version() <= QUIC_VERSION_44) {
+ return;
+ }
+ quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
+ delegate_.SetCanWriteAnything();
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+
+ MakeIOVector("foo", &iov_);
+ generator_.ConsumeData(
+ QuicUtils::GetHeadersStreamId(framer_.transport_version()), &iov_, 1u,
+ iov_.iov_len, 0, FIN);
+ EXPECT_EQ(MESSAGE_STATUS_SUCCESS,
+ generator_.AddMessageFrame(
+ 1, MakeSpan(&allocator_, "message", &storage)));
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ // Add a message which causes the flush of current packet.
+ EXPECT_EQ(
+ MESSAGE_STATUS_SUCCESS,
+ generator_.AddMessageFrame(
+ 2, MakeSpan(
+ &allocator_,
+ std::string(generator_.GetCurrentLargestMessagePayload(), 'a'),
+ &storage)));
+ EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+ // Failed to send messages which cannot fit into one packet.
+ EXPECT_EQ(
+ MESSAGE_STATUS_TOO_LARGE,
+ generator_.AddMessageFrame(
+ 3,
+ MakeSpan(&allocator_,
+ std::string(
+ generator_.GetCurrentLargestMessagePayload() + 10, 'a'),
+ &storage)));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.cc
new file mode 100644
index 00000000000..b3009ce5c7a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.cc
@@ -0,0 +1,119 @@
+// 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 <algorithm>
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_number.h"
+
+namespace quic {
+
+QuicPacketNumber::QuicPacketNumber()
+ : packet_number_(UninitializedPacketNumber()) {}
+
+QuicPacketNumber::QuicPacketNumber(uint64_t packet_number)
+ : packet_number_(packet_number) {
+ DCHECK_NE(UninitializedPacketNumber(), packet_number)
+ << "Use default constructor for uninitialized packet number";
+}
+
+void QuicPacketNumber::Clear() {
+ packet_number_ = UninitializedPacketNumber();
+}
+
+void QuicPacketNumber::UpdateMax(QuicPacketNumber new_value) {
+ if (!new_value.IsInitialized()) {
+ return;
+ }
+ if (!IsInitialized()) {
+ packet_number_ = new_value.ToUint64();
+ } else {
+ packet_number_ = std::max(packet_number_, new_value.ToUint64());
+ }
+}
+
+uint64_t QuicPacketNumber::Hash() const {
+ DCHECK(IsInitialized());
+ return packet_number_;
+}
+
+uint64_t QuicPacketNumber::ToUint64() const {
+ DCHECK(IsInitialized());
+ return packet_number_;
+}
+
+bool QuicPacketNumber::IsInitialized() const {
+ return packet_number_ != UninitializedPacketNumber();
+}
+
+QuicPacketNumber& QuicPacketNumber::operator++() {
+#ifndef NDEBUG
+ DCHECK(IsInitialized());
+ DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max() - 1);
+#endif
+ packet_number_++;
+ return *this;
+}
+
+QuicPacketNumber QuicPacketNumber::operator++(int) {
+#ifndef NDEBUG
+ DCHECK(IsInitialized());
+ DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max() - 1);
+#endif
+ QuicPacketNumber previous(*this);
+ packet_number_++;
+ return previous;
+}
+
+QuicPacketNumber& QuicPacketNumber::operator--() {
+#ifndef NDEBUG
+ DCHECK(IsInitialized());
+ DCHECK_GE(ToUint64(), 1UL);
+#endif
+ packet_number_--;
+ return *this;
+}
+
+QuicPacketNumber QuicPacketNumber::operator--(int) {
+#ifndef NDEBUG
+ DCHECK(IsInitialized());
+ DCHECK_GE(ToUint64(), 1UL);
+#endif
+ QuicPacketNumber previous(*this);
+ packet_number_--;
+ return previous;
+}
+
+QuicPacketNumber& QuicPacketNumber::operator+=(uint64_t delta) {
+#ifndef NDEBUG
+ DCHECK(IsInitialized());
+ DCHECK_GT(std::numeric_limits<uint64_t>::max() - ToUint64(), delta);
+#endif
+ packet_number_ += delta;
+ return *this;
+}
+
+QuicPacketNumber& QuicPacketNumber::operator-=(uint64_t delta) {
+#ifndef NDEBUG
+ DCHECK(IsInitialized());
+ DCHECK_GE(ToUint64(), delta);
+#endif
+ packet_number_ -= delta;
+ return *this;
+}
+
+std::ostream& operator<<(std::ostream& os, const QuicPacketNumber& p) {
+ if (p.IsInitialized()) {
+ os << p.packet_number_;
+ } else {
+ os << "uninitialized";
+ }
+ return os;
+}
+
+// static
+uint64_t QuicPacketNumber::UninitializedPacketNumber() {
+ return std::numeric_limits<uint64_t>::max();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.h
new file mode 100644
index 00000000000..5348abd7c69
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number.h
@@ -0,0 +1,150 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_NUMBER_H_
+#define QUICHE_QUIC_CORE_QUIC_PACKET_NUMBER_H_
+
+#include <limits>
+#include <ostream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+// QuicPacketNumber can either initialized or uninitialized. An initialized
+// packet number is simply an ordinal number. A sentinel value is used to
+// represent an uninitialized packet number.
+class QUIC_EXPORT_PRIVATE QuicPacketNumber {
+ public:
+ // Construct an uninitialized packet number.
+ QuicPacketNumber();
+ // Construct a packet number from uint64_t. |packet_number| cannot equal the
+ // sentinel value.
+ explicit QuicPacketNumber(uint64_t packet_number);
+
+ // Packet number becomes uninitialized after calling this function.
+ void Clear();
+
+ // Updates this packet number to be |new_value| if it is greater than current
+ // value.
+ void UpdateMax(QuicPacketNumber new_value);
+
+ // REQUIRES: IsInitialized() == true.
+ uint64_t Hash() const;
+
+ // Converts packet number to uint64_t.
+ // REQUIRES: IsInitialized() == true.
+ uint64_t ToUint64() const;
+
+ // Returns true if packet number is considered initialized.
+ bool IsInitialized() const;
+
+ // REQUIRES: IsInitialized() == true && ToUint64() <
+ // numeric_limits<uint64_t>::max() - 1.
+ QuicPacketNumber& operator++();
+ QuicPacketNumber operator++(int);
+ // REQUIRES: IsInitialized() == true && ToUint64() >= 1.
+ QuicPacketNumber& operator--();
+ QuicPacketNumber operator--(int);
+
+ // REQUIRES: IsInitialized() == true && numeric_limits<uint64_t>::max() -
+ // ToUint64() > |delta|.
+ QuicPacketNumber& operator+=(uint64_t delta);
+ // REQUIRES: IsInitialized() == true && ToUint64() >= |delta|.
+ QuicPacketNumber& operator-=(uint64_t delta);
+
+ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os,
+ const QuicPacketNumber& p);
+
+ private:
+ // All following operators REQUIRE operands.Initialized() == true.
+ friend inline bool operator==(QuicPacketNumber lhs, QuicPacketNumber rhs);
+ friend inline bool operator!=(QuicPacketNumber lhs, QuicPacketNumber rhs);
+ friend inline bool operator<(QuicPacketNumber lhs, QuicPacketNumber rhs);
+ friend inline bool operator<=(QuicPacketNumber lhs, QuicPacketNumber rhs);
+ friend inline bool operator>(QuicPacketNumber lhs, QuicPacketNumber rhs);
+ friend inline bool operator>=(QuicPacketNumber lhs, QuicPacketNumber rhs);
+
+ // REQUIRES: numeric_limits<uint64_t>::max() - lhs.ToUint64() > |delta|.
+ friend inline QuicPacketNumber operator+(QuicPacketNumber lhs,
+ uint64_t delta);
+ // REQUIRES: lhs.ToUint64() >= |delta|.
+ friend inline QuicPacketNumber operator-(QuicPacketNumber lhs,
+ uint64_t delta);
+ // REQUIRES: lhs >= rhs.
+ friend inline uint64_t operator-(QuicPacketNumber lhs, QuicPacketNumber rhs);
+
+ // The sentinel value representing an uninitialized packet number.
+ static uint64_t UninitializedPacketNumber();
+
+ uint64_t packet_number_;
+};
+
+class QuicPacketNumberHash {
+ public:
+ uint64_t operator()(QuicPacketNumber packet_number) const noexcept {
+ return packet_number.Hash();
+ }
+};
+
+inline bool operator==(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+ DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+ return lhs.packet_number_ == rhs.packet_number_;
+}
+
+inline bool operator!=(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+ DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+ return lhs.packet_number_ != rhs.packet_number_;
+}
+
+inline bool operator<(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+ DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+ return lhs.packet_number_ < rhs.packet_number_;
+}
+
+inline bool operator<=(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+ DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+ return lhs.packet_number_ <= rhs.packet_number_;
+}
+
+inline bool operator>(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+ DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+ return lhs.packet_number_ > rhs.packet_number_;
+}
+
+inline bool operator>=(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+ DCHECK(lhs.IsInitialized() && rhs.IsInitialized()) << lhs << " vs. " << rhs;
+ return lhs.packet_number_ >= rhs.packet_number_;
+}
+
+inline QuicPacketNumber operator+(QuicPacketNumber lhs, uint64_t delta) {
+#ifndef NDEBUG
+ DCHECK(lhs.IsInitialized());
+ DCHECK_GT(std::numeric_limits<uint64_t>::max() - lhs.ToUint64(), delta);
+#endif
+ return QuicPacketNumber(lhs.packet_number_ + delta);
+}
+
+inline QuicPacketNumber operator-(QuicPacketNumber lhs, uint64_t delta) {
+#ifndef NDEBUG
+ DCHECK(lhs.IsInitialized());
+ DCHECK_GE(lhs.ToUint64(), delta);
+#endif
+ return QuicPacketNumber(lhs.packet_number_ - delta);
+}
+
+inline uint64_t operator-(QuicPacketNumber lhs, QuicPacketNumber rhs) {
+ DCHECK(lhs.IsInitialized() && rhs.IsInitialized() && lhs >= rhs)
+ << lhs << " vs. " << rhs;
+ return lhs.packet_number_ - rhs.packet_number_;
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PACKET_NUMBER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_number_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number_test.cc
new file mode 100644
index 00000000000..e0184a81d44
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_number_test.cc
@@ -0,0 +1,67 @@
+// 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 "net/third_party/quiche/src/quic/core/quic_packet_number.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+TEST(QuicPacketNumberTest, BasicTest) {
+ QuicPacketNumber num;
+ EXPECT_FALSE(num.IsInitialized());
+
+ QuicPacketNumber num2(10);
+ EXPECT_TRUE(num2.IsInitialized());
+ EXPECT_EQ(10u, num2.ToUint64());
+ EXPECT_EQ(10u, num2.Hash());
+ num2.UpdateMax(num);
+ EXPECT_EQ(10u, num2.ToUint64());
+ num2.UpdateMax(QuicPacketNumber(9));
+ EXPECT_EQ(10u, num2.ToUint64());
+ num2.UpdateMax(QuicPacketNumber(11));
+ EXPECT_EQ(11u, num2.ToUint64());
+ num2.Clear();
+ EXPECT_FALSE(num2.IsInitialized());
+ num2.UpdateMax(QuicPacketNumber(9));
+ EXPECT_EQ(9u, num2.ToUint64());
+
+ QuicPacketNumber num4(0);
+ EXPECT_TRUE(num4.IsInitialized());
+ EXPECT_EQ(0u, num4.ToUint64());
+ EXPECT_EQ(0u, num4.Hash());
+ num4.Clear();
+ EXPECT_FALSE(num4.IsInitialized());
+}
+
+TEST(QuicPacketNumberTest, Operators) {
+ QuicPacketNumber num(100);
+ EXPECT_EQ(QuicPacketNumber(100), num++);
+ EXPECT_EQ(QuicPacketNumber(101), num);
+ EXPECT_EQ(QuicPacketNumber(101), num--);
+ EXPECT_EQ(QuicPacketNumber(100), num);
+
+ EXPECT_EQ(QuicPacketNumber(101), ++num);
+ EXPECT_EQ(QuicPacketNumber(100), --num);
+
+ QuicPacketNumber num3(0);
+ EXPECT_EQ(QuicPacketNumber(0), num3++);
+ EXPECT_EQ(QuicPacketNumber(1), num3);
+ EXPECT_EQ(QuicPacketNumber(2), ++num3);
+
+ EXPECT_EQ(QuicPacketNumber(2), num3--);
+ EXPECT_EQ(QuicPacketNumber(1), num3);
+ EXPECT_EQ(QuicPacketNumber(0), --num3);
+}
+
+} // namespace
+
+} // namespace test
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.cc
new file mode 100644
index 00000000000..f03aa6d80fe
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.cc
@@ -0,0 +1,226 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+
+#include <errno.h>
+#ifndef __APPLE__
+// This is a GNU header that is not present on Apple platforms
+#include <features.h>
+#endif
+#include <string.h>
+#include <sys/socket.h>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_server_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace quic {
+
+QuicPacketReader::QuicPacketReader() {
+ Initialize();
+}
+
+void QuicPacketReader::Initialize() {
+#if MMSG_MORE
+ // Zero initialize uninitialized memory.
+ memset(mmsg_hdr_, 0, sizeof(mmsg_hdr_));
+
+ for (int i = 0; i < kNumPacketsPerReadMmsgCall; ++i) {
+ packets_[i].iov.iov_base = packets_[i].buf;
+ packets_[i].iov.iov_len = sizeof(packets_[i].buf);
+ memset(&packets_[i].raw_address, 0, sizeof(packets_[i].raw_address));
+ memset(packets_[i].cbuf, 0, sizeof(packets_[i].cbuf));
+ memset(packets_[i].buf, 0, sizeof(packets_[i].buf));
+
+ msghdr* hdr = &mmsg_hdr_[i].msg_hdr;
+ hdr->msg_name = &packets_[i].raw_address;
+ hdr->msg_namelen = sizeof(sockaddr_storage);
+ hdr->msg_iov = &packets_[i].iov;
+ hdr->msg_iovlen = 1;
+
+ hdr->msg_control = packets_[i].cbuf;
+ hdr->msg_controllen = kCmsgSpaceForReadPacket;
+ }
+#endif
+}
+
+QuicPacketReader::~QuicPacketReader() = default;
+
+bool QuicPacketReader::ReadAndDispatchPackets(
+ int fd,
+ int port,
+ const QuicClock& clock,
+ ProcessPacketInterface* processor,
+ QuicPacketCount* packets_dropped) {
+#if MMSG_MORE_NO_ANDROID
+ return ReadAndDispatchManyPackets(fd, port, clock, processor,
+ packets_dropped);
+#else
+ return ReadAndDispatchSinglePacket(fd, port, clock, processor,
+ packets_dropped);
+#endif
+}
+
+bool QuicPacketReader::ReadAndDispatchManyPackets(
+ int fd,
+ int port,
+ const QuicClock& clock,
+ ProcessPacketInterface* processor,
+ QuicPacketCount* packets_dropped) {
+#if MMSG_MORE_NO_ANDROID
+ // Re-set the length fields in case recvmmsg has changed them.
+ for (int i = 0; i < kNumPacketsPerReadMmsgCall; ++i) {
+ DCHECK_LE(kMaxOutgoingPacketSize, packets_[i].iov.iov_len);
+ msghdr* hdr = &mmsg_hdr_[i].msg_hdr;
+ hdr->msg_namelen = sizeof(sockaddr_storage);
+ DCHECK_EQ(1, hdr->msg_iovlen);
+ hdr->msg_controllen = kCmsgSpaceForReadPacket;
+ hdr->msg_flags = 0;
+ }
+
+ int packets_read =
+ recvmmsg(fd, mmsg_hdr_, kNumPacketsPerReadMmsgCall, MSG_TRUNC, nullptr);
+
+ if (packets_read <= 0) {
+ return false; // recvmmsg failed.
+ }
+
+ bool use_quic_time =
+ GetQuicReloadableFlag(quic_use_quic_time_for_received_timestamp);
+ QuicTime fallback_timestamp(QuicTime::Zero());
+ QuicWallTime fallback_walltimestamp = QuicWallTime::Zero();
+ for (int i = 0; i < packets_read; ++i) {
+ if (mmsg_hdr_[i].msg_len == 0) {
+ continue;
+ }
+
+ if (QUIC_PREDICT_FALSE(mmsg_hdr_[i].msg_hdr.msg_flags & MSG_CTRUNC)) {
+ QUIC_BUG << "Incorrectly set control length: "
+ << mmsg_hdr_[i].msg_hdr.msg_controllen << ", expected "
+ << kCmsgSpaceForReadPacket;
+ continue;
+ }
+
+ if (QUIC_PREDICT_FALSE(mmsg_hdr_[i].msg_hdr.msg_flags & MSG_TRUNC)) {
+ QUIC_LOG_FIRST_N(WARNING, 100)
+ << "Dropping truncated QUIC packet: buffer size:"
+ << packets_[i].iov.iov_len << " packet size:" << mmsg_hdr_[i].msg_len;
+ QUIC_SERVER_HISTOGRAM_COUNTS(
+ "QuicPacketReader.DroppedPacketSize", mmsg_hdr_[i].msg_len, 1, 10000,
+ 20, "In QuicPacketReader, the size of big packets that are dropped.");
+ continue;
+ }
+
+ QuicSocketAddress peer_address(packets_[i].raw_address);
+ QuicIpAddress self_ip;
+ QuicWallTime packet_walltimestamp = QuicWallTime::Zero();
+ QuicSocketUtils::GetAddressAndTimestampFromMsghdr(
+ &mmsg_hdr_[i].msg_hdr, &self_ip, &packet_walltimestamp);
+ if (!self_ip.IsInitialized()) {
+ QUIC_BUG << "Unable to get self IP address.";
+ continue;
+ }
+
+ // This isn't particularly desirable, but not all platforms support socket
+ // timestamping.
+ QuicTime timestamp(QuicTime::Zero());
+ if (!use_quic_time) {
+ if (packet_walltimestamp.IsZero()) {
+ if (fallback_walltimestamp.IsZero()) {
+ fallback_walltimestamp = clock.WallNow();
+ }
+ packet_walltimestamp = fallback_walltimestamp;
+ }
+ timestamp = clock.ConvertWallTimeToQuicTime(packet_walltimestamp);
+
+ } else {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_use_quic_time_for_received_timestamp);
+ if (packet_walltimestamp.IsZero()) {
+ if (!fallback_timestamp.IsInitialized()) {
+ fallback_timestamp = clock.Now();
+ }
+ timestamp = fallback_timestamp;
+ } else {
+ timestamp = clock.ConvertWallTimeToQuicTime(packet_walltimestamp);
+ }
+ }
+ int ttl = 0;
+ bool has_ttl =
+ QuicSocketUtils::GetTtlFromMsghdr(&mmsg_hdr_[i].msg_hdr, &ttl);
+ char* headers = nullptr;
+ size_t headers_length = 0;
+ QuicSocketUtils::GetPacketHeadersFromMsghdr(&mmsg_hdr_[i].msg_hdr, &headers,
+ &headers_length);
+ QuicReceivedPacket packet(reinterpret_cast<char*>(packets_[i].iov.iov_base),
+ mmsg_hdr_[i].msg_len, timestamp, false, ttl,
+ has_ttl, headers, headers_length, false);
+ QuicSocketAddress self_address(self_ip, port);
+ processor->ProcessPacket(self_address, peer_address, packet);
+ }
+
+ if (packets_dropped != nullptr) {
+ QuicSocketUtils::GetOverflowFromMsghdr(&mmsg_hdr_[0].msg_hdr,
+ packets_dropped);
+ }
+
+ // We may not have read all of the packets available on the socket.
+ return packets_read == kNumPacketsPerReadMmsgCall;
+#else
+ QUIC_LOG(FATAL) << "Unsupported";
+ return false;
+#endif
+}
+
+/* static */
+bool QuicPacketReader::ReadAndDispatchSinglePacket(
+ int fd,
+ int port,
+ const QuicClock& clock,
+ ProcessPacketInterface* processor,
+ QuicPacketCount* packets_dropped) {
+ char buf[kMaxV4PacketSize];
+
+ QuicSocketAddress peer_address;
+ QuicIpAddress self_ip;
+ QuicWallTime walltimestamp = QuicWallTime::Zero();
+ int bytes_read =
+ QuicSocketUtils::ReadPacket(fd, buf, QUIC_ARRAYSIZE(buf), packets_dropped,
+ &self_ip, &walltimestamp, &peer_address);
+ if (bytes_read < 0) {
+ return false; // ReadPacket failed.
+ }
+
+ if (!self_ip.IsInitialized()) {
+ QUIC_BUG << "Unable to get self IP address.";
+ return false;
+ }
+ // This isn't particularly desirable, but not all platforms support socket
+ // timestamping.
+ if (walltimestamp.IsZero()) {
+ walltimestamp = clock.WallNow();
+ }
+ QuicTime timestamp = clock.ConvertWallTimeToQuicTime(walltimestamp);
+
+ QuicReceivedPacket packet(buf, bytes_read, timestamp, false);
+ QuicSocketAddress self_address(self_ip, port);
+ processor->ProcessPacket(self_address, peer_address, packet);
+
+ // The socket read was successful, so return true even if packet dispatch
+ // failed.
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.h
new file mode 100644
index 00000000000..1826ef3573b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_reader.h
@@ -0,0 +1,90 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A class to read incoming QUIC packets from the UDP socket.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_READER_H_
+#define QUICHE_QUIC_CORE_QUIC_PACKET_READER_H_
+
+#include <netinet/in.h>
+// Include here to guarantee this header gets included (for MSG_WAITFORONE)
+// regardless of how the below transitive header include set may change.
+#include <sys/socket.h>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+namespace quic {
+
+#if MMSG_MORE
+// Read in larger batches to minimize recvmmsg overhead.
+const int kNumPacketsPerReadMmsgCall = 16;
+#endif
+
+class QuicPacketReader {
+ public:
+ QuicPacketReader();
+ QuicPacketReader(const QuicPacketReader&) = delete;
+ QuicPacketReader& operator=(const QuicPacketReader&) = delete;
+
+ virtual ~QuicPacketReader();
+
+ // Reads a number of packets from the given fd, and then passes them off to
+ // the PacketProcessInterface. Returns true if there may be additional
+ // packets available on the socket.
+ // Populates |packets_dropped| if it is non-null and the socket is configured
+ // to track dropped packets and some packets are read.
+ // If the socket has timestamping enabled, the per packet timestamps will be
+ // passed to the processor. Otherwise, |clock| will be used.
+ virtual bool ReadAndDispatchPackets(int fd,
+ int port,
+ const QuicClock& clock,
+ ProcessPacketInterface* processor,
+ QuicPacketCount* packets_dropped);
+
+ private:
+ // Initialize the internal state of the reader.
+ void Initialize();
+
+ // Reads and dispatches many packets using recvmmsg.
+ bool ReadAndDispatchManyPackets(int fd,
+ int port,
+ const QuicClock& clock,
+ ProcessPacketInterface* processor,
+ QuicPacketCount* packets_dropped);
+
+ // Reads and dispatches a single packet using recvmsg.
+ static bool ReadAndDispatchSinglePacket(int fd,
+ int port,
+ const QuicClock& clock,
+ ProcessPacketInterface* processor,
+ QuicPacketCount* packets_dropped);
+
+#if MMSG_MORE
+ // Storage only used when recvmmsg is available.
+ // TODO(danzh): change it to be a pointer to avoid the allocation on the stack
+ // from exceeding maximum allowed frame size.
+ // packets_ and mmsg_hdr_ are used to supply cbuf and buf to the recvmmsg
+ // call.
+ struct PacketData {
+ iovec iov;
+ // raw_address is used for address information provided by the recvmmsg
+ // call on the packets.
+ struct sockaddr_storage raw_address;
+ // cbuf is used for ancillary data from the kernel on recvmmsg.
+ char cbuf[kCmsgSpaceForReadPacket];
+ // buf is used for the data read from the kernel on recvmmsg.
+ char buf[kMaxV4PacketSize];
+ };
+ PacketData packets_[kNumPacketsPerReadMmsgCall];
+ mmsghdr mmsg_hdr_[kNumPacketsPerReadMmsgCall];
+#endif
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PACKET_READER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer.h
new file mode 100644
index 00000000000..667254faf33
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer.h
@@ -0,0 +1,132 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_
+#define QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+struct WriteResult;
+
+struct QUIC_EXPORT_PRIVATE PerPacketOptions {
+ virtual ~PerPacketOptions() {}
+
+ // Returns a heap-allocated copy of |this|.
+ //
+ // The subclass implementation of this method should look like this:
+ // return QuicMakeUnique<MyAwesomePerPacketOptions>(*this);
+ //
+ // This method is declared pure virtual in order to ensure the subclasses
+ // would not forget to override it.
+ virtual std::unique_ptr<PerPacketOptions> Clone() const = 0;
+
+ // Specifies release time delay for this packet.
+ QuicTime::Delta release_time_delay = QuicTime::Delta::Zero();
+};
+
+// An interface between writers and the entity managing the
+// socket (in our case the QuicDispatcher). This allows the Dispatcher to
+// control writes, and manage any writers who end up write blocked.
+// A concrete writer works in one of the two modes:
+// - PassThrough mode. This is the default mode. Caller calls WritePacket with
+// caller-allocated packet buffer. Unless the writer is blocked, each call to
+// WritePacket triggers a write using the underlying socket API.
+//
+// - Batch mode. In this mode, a call to WritePacket may not cause a packet to
+// be sent using the underlying socket API. Instead, multiple packets are
+// saved in the writer's internal buffer until they are flushed. The flush can
+// be explicit, by calling Flush, or implicit, e.g. by calling
+// WritePacket when the internal buffer is near full.
+//
+// Buffer management:
+// In Batch mode, a writer manages an internal buffer, which is large enough to
+// hold multiple packets' data. If the caller calls WritePacket with a
+// caller-allocated packet buffer, the writer will memcpy the buffer into the
+// internal buffer. Caller can also avoid this memcpy by:
+// 1. Call GetNextWriteLocation to get a pointer P into the internal buffer.
+// 2. Serialize the packet directly to P.
+// 3. Call WritePacket with P as the |buffer|.
+class QUIC_EXPORT_PRIVATE QuicPacketWriter {
+ public:
+ virtual ~QuicPacketWriter() {}
+
+ // PassThrough mode:
+ // Sends the packet out to the peer, with some optional per-packet options.
+ // If the write succeeded, the result's status is WRITE_STATUS_OK and
+ // bytes_written is populated. If the write failed, the result's status is
+ // WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR and error_code is populated.
+ //
+ // Batch mode:
+ // If the writer is blocked, return WRITE_STATUS_BLOCKED immediately.
+ // If the packet can be batched with other buffered packets, save the packet
+ // to the internal buffer.
+ // If the packet can not be batched, or the internal buffer is near full after
+ // it is buffered, the internal buffer is flushed to free up space.
+ // Return WriteResult(WRITE_STATUS_OK, <bytes_flushed>) on success. When
+ // <bytes_flushed> is zero, it means the packet is buffered and not flushed.
+ // Return WRITE_STATUS_BLOCKED if the packet is not buffered and the socket is
+ // blocked while flushing.
+ // Otherwise return an error status.
+ //
+ // Options must be either null, or created for the particular QuicPacketWriter
+ // implementation. Options may be ignored, depending on the implementation.
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) = 0;
+
+ // Returns true if the network socket is not writable.
+ virtual bool IsWriteBlocked() const = 0;
+
+ // Records that the socket has become writable, for example when an EPOLLOUT
+ // is received or an asynchronous write completes.
+ virtual void SetWritable() = 0;
+
+ // Returns the maximum size of the packet which can be written using this
+ // writer for the supplied peer address. This size may actually exceed the
+ // size of a valid QUIC packet.
+ virtual QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const = 0;
+
+ // Returns true if the socket supports release timestamp.
+ virtual bool SupportsReleaseTime() const = 0;
+
+ // True=Batch mode. False=PassThrough mode.
+ virtual bool IsBatchMode() const = 0;
+
+ // PassThrough mode: Return nullptr.
+ //
+ // Batch mode:
+ // Return the starting address for the next packet's data. A minimum of
+ // kMaxOutgoingPacketSize is guaranteed to be available from the returned
+ // address. If the internal buffer does not have enough space, nullptr is
+ // returned. All arguments should be identical to the follow-up call to
+ // |WritePacket|, they are here to allow advanced packet memory management in
+ // packet writers, e.g. one packet buffer pool per |peer_address|.
+ virtual char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) = 0;
+
+ // PassThrough mode: Return WriteResult(WRITE_STATUS_OK, 0).
+ //
+ // Batch mode:
+ // Try send all buffered packets.
+ // - Return WriteResult(WRITE_STATUS_OK, <bytes_flushed>) if all buffered
+ // packets were sent successfully.
+ // - Return WRITE_STATUS_BLOCKED, or an error status, if the underlying socket
+ // is blocked or returned an error while sending. Some packets may have been
+ // sent, packets not sent will stay in the internal buffer.
+ virtual WriteResult Flush() = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.cc
new file mode 100644
index 00000000000..f1f25d871bf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.cc
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+QuicPacketWriterWrapper::QuicPacketWriterWrapper() = default;
+
+QuicPacketWriterWrapper::~QuicPacketWriterWrapper() {
+ unset_writer();
+}
+
+WriteResult QuicPacketWriterWrapper::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ return writer_->WritePacket(buffer, buf_len, self_address, peer_address,
+ options);
+}
+
+bool QuicPacketWriterWrapper::IsWriteBlocked() const {
+ return writer_->IsWriteBlocked();
+}
+
+void QuicPacketWriterWrapper::SetWritable() {
+ writer_->SetWritable();
+}
+
+QuicByteCount QuicPacketWriterWrapper::GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const {
+ return writer_->GetMaxPacketSize(peer_address);
+}
+
+bool QuicPacketWriterWrapper::SupportsReleaseTime() const {
+ return writer_->SupportsReleaseTime();
+}
+
+bool QuicPacketWriterWrapper::IsBatchMode() const {
+ return writer_->IsBatchMode();
+}
+
+char* QuicPacketWriterWrapper::GetNextWriteLocation(
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ return writer_->GetNextWriteLocation(self_address, peer_address);
+}
+
+WriteResult QuicPacketWriterWrapper::Flush() {
+ return writer_->Flush();
+}
+
+void QuicPacketWriterWrapper::set_writer(QuicPacketWriter* writer) {
+ unset_writer();
+ writer_ = writer;
+ owns_writer_ = true;
+}
+
+void QuicPacketWriterWrapper::set_non_owning_writer(QuicPacketWriter* writer) {
+ unset_writer();
+ writer_ = writer;
+ owns_writer_ = false;
+}
+
+void QuicPacketWriterWrapper::unset_writer() {
+ if (owns_writer_) {
+ delete writer_;
+ }
+
+ owns_writer_ = false;
+ writer_ = nullptr;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h
new file mode 100644
index 00000000000..e93bfcfafa7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_WRAPPER_H_
+#define QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_WRAPPER_H_
+
+#include <cstddef>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+
+namespace quic {
+
+// Wraps a writer object to allow dynamically extending functionality. Use
+// cases: replace writer while dispatcher and connections hold on to the
+// wrapper; mix in monitoring; mix in mocks in unit tests.
+class QuicPacketWriterWrapper : public QuicPacketWriter {
+ public:
+ QuicPacketWriterWrapper();
+ QuicPacketWriterWrapper(const QuicPacketWriterWrapper&) = delete;
+ QuicPacketWriterWrapper& operator=(const QuicPacketWriterWrapper&) = delete;
+ ~QuicPacketWriterWrapper() override;
+
+ // Default implementation of the QuicPacketWriter interface. Passes everything
+ // to |writer_|.
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+ bool IsWriteBlocked() const override;
+ void SetWritable() override;
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const override;
+ bool SupportsReleaseTime() const override;
+ bool IsBatchMode() const override;
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override;
+ WriteResult Flush() override;
+
+ // Takes ownership of |writer|.
+ void set_writer(QuicPacketWriter* writer);
+
+ // Does not take ownership of |writer|.
+ void set_non_owning_writer(QuicPacketWriter* writer);
+
+ virtual void set_peer_address(const QuicSocketAddress& peer_address) {}
+
+ QuicPacketWriter* writer() { return writer_; }
+
+ private:
+ void unset_writer();
+
+ QuicPacketWriter* writer_ = nullptr;
+ bool owns_writer_ = false;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PACKET_WRITER_WRAPPER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc b/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc
new file mode 100644
index 00000000000..9b2cb097a34
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets.cc
@@ -0,0 +1,422 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+QuicConnectionIdLength GetIncludedConnectionIdLength(
+ QuicConnectionId connection_id,
+ QuicConnectionIdIncluded connection_id_included) {
+ DCHECK(connection_id_included == CONNECTION_ID_PRESENT ||
+ connection_id_included == CONNECTION_ID_ABSENT);
+ return connection_id_included == CONNECTION_ID_PRESENT
+ ? static_cast<QuicConnectionIdLength>(connection_id.length())
+ : PACKET_0BYTE_CONNECTION_ID;
+}
+
+QuicConnectionIdLength GetIncludedDestinationConnectionIdLength(
+ const QuicPacketHeader& header) {
+ return GetIncludedConnectionIdLength(
+ header.destination_connection_id,
+ header.destination_connection_id_included);
+}
+
+QuicConnectionIdLength GetIncludedSourceConnectionIdLength(
+ const QuicPacketHeader& header) {
+ return GetIncludedConnectionIdLength(header.source_connection_id,
+ header.source_connection_id_included);
+}
+
+size_t GetPacketHeaderSize(QuicTransportVersion version,
+ const QuicPacketHeader& header) {
+ return GetPacketHeaderSize(
+ version, GetIncludedDestinationConnectionIdLength(header),
+ GetIncludedSourceConnectionIdLength(header), header.version_flag,
+ header.nonce != nullptr, header.packet_number_length,
+ header.retry_token_length_length, header.retry_token.length(),
+ header.length_length);
+}
+
+size_t GetPacketHeaderSize(
+ QuicTransportVersion version,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool include_version,
+ bool include_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicByteCount retry_token_length,
+ QuicVariableLengthIntegerLength length_length) {
+ if (version > QUIC_VERSION_43) {
+ if (include_version) {
+ // Long header.
+ return kPacketHeaderTypeSize + kConnectionIdLengthSize +
+ destination_connection_id_length + source_connection_id_length +
+ (version > QUIC_VERSION_44 ? packet_number_length
+ : PACKET_4BYTE_PACKET_NUMBER) +
+ kQuicVersionSize +
+ (include_diversification_nonce ? kDiversificationNonceSize : 0) +
+ retry_token_length_length + retry_token_length + length_length;
+ }
+ // Short header.
+ return kPacketHeaderTypeSize + destination_connection_id_length +
+ packet_number_length;
+ }
+ return kPublicFlagsSize + destination_connection_id_length +
+ (include_version ? kQuicVersionSize : 0) + packet_number_length +
+ (include_diversification_nonce ? kDiversificationNonceSize : 0);
+}
+
+size_t GetStartOfEncryptedData(QuicTransportVersion version,
+ const QuicPacketHeader& header) {
+ return GetPacketHeaderSize(version, header);
+}
+
+size_t GetStartOfEncryptedData(
+ QuicTransportVersion version,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool include_version,
+ bool include_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicByteCount retry_token_length,
+ QuicVariableLengthIntegerLength length_length) {
+ // Encryption starts before private flags.
+ return GetPacketHeaderSize(
+ version, destination_connection_id_length, source_connection_id_length,
+ include_version, include_diversification_nonce, packet_number_length,
+ retry_token_length_length, retry_token_length, length_length);
+}
+
+QuicPacketHeader::QuicPacketHeader()
+ : destination_connection_id(EmptyQuicConnectionId()),
+ destination_connection_id_included(CONNECTION_ID_PRESENT),
+ source_connection_id(EmptyQuicConnectionId()),
+ source_connection_id_included(CONNECTION_ID_ABSENT),
+ reset_flag(false),
+ version_flag(false),
+ has_possible_stateless_reset_token(false),
+ packet_number_length(PACKET_4BYTE_PACKET_NUMBER),
+ version(UnsupportedQuicVersion()),
+ nonce(nullptr),
+ form(GOOGLE_QUIC_PACKET),
+ long_packet_type(INITIAL),
+ possible_stateless_reset_token(0),
+ retry_token_length_length(VARIABLE_LENGTH_INTEGER_LENGTH_0),
+ retry_token(QuicStringPiece()),
+ length_length(VARIABLE_LENGTH_INTEGER_LENGTH_0),
+ remaining_packet_length(0) {}
+
+QuicPacketHeader::QuicPacketHeader(const QuicPacketHeader& other) = default;
+
+QuicPacketHeader::~QuicPacketHeader() {}
+
+QuicPublicResetPacket::QuicPublicResetPacket()
+ : connection_id(EmptyQuicConnectionId()), nonce_proof(0) {}
+
+QuicPublicResetPacket::QuicPublicResetPacket(QuicConnectionId connection_id)
+ : connection_id(connection_id), nonce_proof(0) {}
+
+QuicVersionNegotiationPacket::QuicVersionNegotiationPacket()
+ : connection_id(EmptyQuicConnectionId()) {}
+
+QuicVersionNegotiationPacket::QuicVersionNegotiationPacket(
+ QuicConnectionId connection_id)
+ : connection_id(connection_id) {}
+
+QuicVersionNegotiationPacket::QuicVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& other) = default;
+
+QuicVersionNegotiationPacket::~QuicVersionNegotiationPacket() {}
+
+QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket()
+ : stateless_reset_token(0) {}
+
+QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket(
+ const QuicPacketHeader& header,
+ QuicUint128 token)
+ : header(header), stateless_reset_token(token) {}
+
+QuicIetfStatelessResetPacket::QuicIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& other) = default;
+
+QuicIetfStatelessResetPacket::~QuicIetfStatelessResetPacket() {}
+
+std::ostream& operator<<(std::ostream& os, const QuicPacketHeader& header) {
+ os << "{ destination_connection_id: " << header.destination_connection_id
+ << " ("
+ << (header.destination_connection_id_included == CONNECTION_ID_PRESENT
+ ? "present"
+ : "absent")
+ << "), source_connection_id: " << header.source_connection_id << " ("
+ << (header.source_connection_id_included == CONNECTION_ID_PRESENT
+ ? "present"
+ : "absent")
+ << "), packet_number_length: " << header.packet_number_length
+ << ", reset_flag: " << header.reset_flag
+ << ", version_flag: " << header.version_flag;
+ if (header.version_flag) {
+ os << ", version: " << ParsedQuicVersionToString(header.version);
+ if (header.long_packet_type != INVALID_PACKET_TYPE) {
+ os << ", long_packet_type: "
+ << QuicUtils::QuicLongHeaderTypetoString(header.long_packet_type);
+ }
+ if (header.retry_token_length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0) {
+ os << ", retry_token_length_length: "
+ << static_cast<int>(header.retry_token_length_length);
+ }
+ if (header.retry_token.length() != 0) {
+ os << ", retry_token_length: " << header.retry_token.length();
+ }
+ if (header.length_length != VARIABLE_LENGTH_INTEGER_LENGTH_0) {
+ os << ", length_length: " << static_cast<int>(header.length_length);
+ }
+ if (header.remaining_packet_length != 0) {
+ os << ", remaining_packet_length: " << header.remaining_packet_length;
+ }
+ }
+ if (header.nonce != nullptr) {
+ os << ", diversification_nonce: "
+ << QuicTextUtils::HexEncode(
+ QuicStringPiece(header.nonce->data(), header.nonce->size()));
+ }
+ os << ", packet_number: " << header.packet_number << " }\n";
+ return os;
+}
+
+QuicData::QuicData(const char* buffer, size_t length)
+ : buffer_(buffer), length_(length), owns_buffer_(false) {}
+
+QuicData::QuicData(const char* buffer, size_t length, bool owns_buffer)
+ : buffer_(buffer), length_(length), owns_buffer_(owns_buffer) {}
+
+QuicData::~QuicData() {
+ if (owns_buffer_) {
+ delete[] const_cast<char*>(buffer_);
+ }
+}
+
+QuicPacket::QuicPacket(
+ char* buffer,
+ size_t length,
+ bool owns_buffer,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool includes_version,
+ bool includes_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicByteCount retry_token_length,
+ QuicVariableLengthIntegerLength length_length)
+ : QuicData(buffer, length, owns_buffer),
+ buffer_(buffer),
+ destination_connection_id_length_(destination_connection_id_length),
+ source_connection_id_length_(source_connection_id_length),
+ includes_version_(includes_version),
+ includes_diversification_nonce_(includes_diversification_nonce),
+ packet_number_length_(packet_number_length),
+ retry_token_length_length_(retry_token_length_length),
+ retry_token_length_(retry_token_length),
+ length_length_(length_length) {}
+
+QuicPacket::QuicPacket(QuicTransportVersion version,
+ char* buffer,
+ size_t length,
+ bool owns_buffer,
+ const QuicPacketHeader& header)
+ : QuicPacket(buffer,
+ length,
+ owns_buffer,
+ GetIncludedDestinationConnectionIdLength(header),
+ GetIncludedSourceConnectionIdLength(header),
+ header.version_flag,
+ header.nonce != nullptr,
+ header.packet_number_length,
+ header.retry_token_length_length,
+ header.retry_token.length(),
+ header.length_length) {}
+
+QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer, size_t length)
+ : QuicData(buffer, length) {}
+
+QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer,
+ size_t length,
+ bool owns_buffer)
+ : QuicData(buffer, length, owns_buffer) {}
+
+std::unique_ptr<QuicEncryptedPacket> QuicEncryptedPacket::Clone() const {
+ char* buffer = new char[this->length()];
+ memcpy(buffer, this->data(), this->length());
+ return QuicMakeUnique<QuicEncryptedPacket>(buffer, this->length(), true);
+}
+
+std::ostream& operator<<(std::ostream& os, const QuicEncryptedPacket& s) {
+ os << s.length() << "-byte data";
+ return os;
+}
+
+QuicReceivedPacket::QuicReceivedPacket(const char* buffer,
+ size_t length,
+ QuicTime receipt_time)
+ : QuicReceivedPacket(buffer,
+ length,
+ receipt_time,
+ false /* owns_buffer */) {}
+
+QuicReceivedPacket::QuicReceivedPacket(const char* buffer,
+ size_t length,
+ QuicTime receipt_time,
+ bool owns_buffer)
+ : QuicReceivedPacket(buffer,
+ length,
+ receipt_time,
+ owns_buffer,
+ 0 /* ttl */,
+ true /* ttl_valid */) {}
+
+QuicReceivedPacket::QuicReceivedPacket(const char* buffer,
+ size_t length,
+ QuicTime receipt_time,
+ bool owns_buffer,
+ int ttl,
+ bool ttl_valid)
+ : quic::QuicReceivedPacket(buffer,
+ length,
+ receipt_time,
+ owns_buffer,
+ ttl,
+ ttl_valid,
+ nullptr /* packet_headers */,
+ 0 /* headers_length */,
+ false /* owns_header_buffer */) {}
+
+QuicReceivedPacket::QuicReceivedPacket(const char* buffer,
+ size_t length,
+ QuicTime receipt_time,
+ bool owns_buffer,
+ int ttl,
+ bool ttl_valid,
+ char* packet_headers,
+ size_t headers_length,
+ bool owns_header_buffer)
+ : QuicEncryptedPacket(buffer, length, owns_buffer),
+ receipt_time_(receipt_time),
+ ttl_(ttl_valid ? ttl : -1),
+ packet_headers_(packet_headers),
+ headers_length_(headers_length),
+ owns_header_buffer_(owns_header_buffer) {}
+
+QuicReceivedPacket::~QuicReceivedPacket() {
+ if (owns_header_buffer_) {
+ delete[] static_cast<char*>(packet_headers_);
+ }
+}
+
+std::unique_ptr<QuicReceivedPacket> QuicReceivedPacket::Clone() const {
+ char* buffer = new char[this->length()];
+ memcpy(buffer, this->data(), this->length());
+ if (this->packet_headers()) {
+ char* headers_buffer = new char[this->headers_length()];
+ memcpy(headers_buffer, this->packet_headers(), this->headers_length());
+ return QuicMakeUnique<QuicReceivedPacket>(
+ buffer, this->length(), receipt_time(), true, ttl(), ttl() >= 0,
+ headers_buffer, this->headers_length(), true);
+ }
+
+ return QuicMakeUnique<QuicReceivedPacket>(
+ buffer, this->length(), receipt_time(), true, ttl(), ttl() >= 0);
+}
+
+std::ostream& operator<<(std::ostream& os, const QuicReceivedPacket& s) {
+ os << s.length() << "-byte data";
+ return os;
+}
+
+QuicStringPiece QuicPacket::AssociatedData(QuicTransportVersion version) const {
+ return QuicStringPiece(
+ data(),
+ GetStartOfEncryptedData(version, destination_connection_id_length_,
+ source_connection_id_length_, includes_version_,
+ includes_diversification_nonce_,
+ packet_number_length_, retry_token_length_length_,
+ retry_token_length_, length_length_));
+}
+
+QuicStringPiece QuicPacket::Plaintext(QuicTransportVersion version) const {
+ const size_t start_of_encrypted_data = GetStartOfEncryptedData(
+ version, destination_connection_id_length_, source_connection_id_length_,
+ includes_version_, includes_diversification_nonce_, packet_number_length_,
+ retry_token_length_length_, retry_token_length_, length_length_);
+ return QuicStringPiece(data() + start_of_encrypted_data,
+ length() - start_of_encrypted_data);
+}
+
+SerializedPacket::SerializedPacket(QuicPacketNumber packet_number,
+ QuicPacketNumberLength packet_number_length,
+ const char* encrypted_buffer,
+ QuicPacketLength encrypted_length,
+ bool has_ack,
+ bool has_stop_waiting)
+ : encrypted_buffer(encrypted_buffer),
+ encrypted_length(encrypted_length),
+ has_crypto_handshake(NOT_HANDSHAKE),
+ num_padding_bytes(0),
+ packet_number(packet_number),
+ packet_number_length(packet_number_length),
+ encryption_level(ENCRYPTION_INITIAL),
+ has_ack(has_ack),
+ has_stop_waiting(has_stop_waiting),
+ transmission_type(NOT_RETRANSMISSION) {}
+
+SerializedPacket::SerializedPacket(const SerializedPacket& other) = default;
+
+SerializedPacket& SerializedPacket::operator=(const SerializedPacket& other) =
+ default;
+
+SerializedPacket::SerializedPacket(SerializedPacket&& other)
+ : encrypted_buffer(other.encrypted_buffer),
+ encrypted_length(other.encrypted_length),
+ has_crypto_handshake(other.has_crypto_handshake),
+ num_padding_bytes(other.num_padding_bytes),
+ packet_number(other.packet_number),
+ packet_number_length(other.packet_number_length),
+ encryption_level(other.encryption_level),
+ has_ack(other.has_ack),
+ has_stop_waiting(other.has_stop_waiting),
+ transmission_type(other.transmission_type),
+ original_packet_number(other.original_packet_number),
+ largest_acked(other.largest_acked) {
+ retransmittable_frames.swap(other.retransmittable_frames);
+}
+
+SerializedPacket::~SerializedPacket() {}
+
+void ClearSerializedPacket(SerializedPacket* serialized_packet) {
+ if (!serialized_packet->retransmittable_frames.empty()) {
+ DeleteFrames(&serialized_packet->retransmittable_frames);
+ }
+ serialized_packet->encrypted_buffer = nullptr;
+ serialized_packet->encrypted_length = 0;
+ serialized_packet->largest_acked.Clear();
+}
+
+char* CopyBuffer(const SerializedPacket& packet) {
+ char* dst_buffer = new char[packet.encrypted_length];
+ memcpy(dst_buffer, packet.encrypted_buffer, packet.encrypted_length);
+ return dst_buffer;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_packets.h b/chromium/net/third_party/quiche/src/quic/core/quic_packets.h
new file mode 100644
index 00000000000..e745e072ced
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_packets.h
@@ -0,0 +1,368 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PACKETS_H_
+#define QUICHE_QUIC_CORE_QUIC_PACKETS_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <list>
+#include <memory>
+#include <ostream>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+class QuicPacket;
+struct QuicPacketHeader;
+
+// Number of connection ID bytes that are actually included over the wire.
+QUIC_EXPORT_PRIVATE QuicConnectionIdLength
+GetIncludedConnectionIdLength(QuicConnectionId connection_id,
+ QuicConnectionIdIncluded connection_id_included);
+
+// Number of destination connection ID bytes that are actually included over the
+// wire for this particular header.
+QUIC_EXPORT_PRIVATE QuicConnectionIdLength
+GetIncludedDestinationConnectionIdLength(const QuicPacketHeader& header);
+
+// Number of source connection ID bytes that are actually included over the
+// wire for this particular header.
+QUIC_EXPORT_PRIVATE QuicConnectionIdLength
+GetIncludedSourceConnectionIdLength(const QuicPacketHeader& header);
+
+// Size in bytes of the data packet header.
+QUIC_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicTransportVersion version,
+ const QuicPacketHeader& header);
+
+QUIC_EXPORT_PRIVATE size_t
+GetPacketHeaderSize(QuicTransportVersion version,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool include_version,
+ bool include_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicByteCount retry_token_length,
+ QuicVariableLengthIntegerLength length_length);
+
+// Index of the first byte in a QUIC packet of encrypted data.
+QUIC_EXPORT_PRIVATE size_t
+GetStartOfEncryptedData(QuicTransportVersion version,
+ const QuicPacketHeader& header);
+
+QUIC_EXPORT_PRIVATE size_t GetStartOfEncryptedData(
+ QuicTransportVersion version,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool include_version,
+ bool include_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicByteCount retry_token_length,
+ QuicVariableLengthIntegerLength length_length);
+
+struct QUIC_EXPORT_PRIVATE QuicPacketHeader {
+ QuicPacketHeader();
+ QuicPacketHeader(const QuicPacketHeader& other);
+ ~QuicPacketHeader();
+
+ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os,
+ const QuicPacketHeader& header);
+
+ // Universal header. All QuicPacket headers will have a connection_id and
+ // public flags.
+ QuicConnectionId destination_connection_id;
+ QuicConnectionIdIncluded destination_connection_id_included;
+ QuicConnectionId source_connection_id;
+ QuicConnectionIdIncluded source_connection_id_included;
+ // This is only used for Google QUIC.
+ bool reset_flag;
+ // For Google QUIC, version flag in packets from the server means version
+ // negotiation packet. For IETF QUIC, version flag means long header.
+ bool version_flag;
+ // Indicates whether |possible_stateless_reset_token| contains a valid value
+ // parsed from the packet buffer. IETF QUIC only, always false for GQUIC.
+ bool has_possible_stateless_reset_token;
+ QuicPacketNumberLength packet_number_length;
+ ParsedQuicVersion version;
+ // nonce contains an optional, 32-byte nonce value. If not included in the
+ // packet, |nonce| will be empty.
+ DiversificationNonce* nonce;
+ QuicPacketNumber packet_number;
+ // Format of this header.
+ PacketHeaderFormat form;
+ // Short packet type is reflected in packet_number_length.
+ QuicLongHeaderType long_packet_type;
+ // Only valid if |has_possible_stateless_reset_token| is true.
+ // Stores last 16 bytes of a this packet, used to check whether this packet is
+ // a stateless reset packet on decryption failure.
+ QuicUint128 possible_stateless_reset_token;
+ // Length of the retry token length variable length integer field,
+ // carried only by v99 IETF Initial packets.
+ QuicVariableLengthIntegerLength retry_token_length_length;
+ // Retry token, carried only by v99 IETF Initial packets.
+ QuicStringPiece retry_token;
+ // Length of the length variable length integer field,
+ // carried only by v99 IETF Initial, 0-RTT and Handshake packets.
+ QuicVariableLengthIntegerLength length_length;
+ // Length of the packet number and payload, carried only by v99 IETF Initial,
+ // 0-RTT and Handshake packets. Also includes the length of the
+ // diversification nonce in server to client 0-RTT packets.
+ QuicByteCount remaining_packet_length;
+};
+
+struct QUIC_EXPORT_PRIVATE QuicPublicResetPacket {
+ QuicPublicResetPacket();
+ explicit QuicPublicResetPacket(QuicConnectionId connection_id);
+
+ QuicConnectionId connection_id;
+ QuicPublicResetNonceProof nonce_proof;
+ QuicSocketAddress client_address;
+ // An arbitrary string to identify an endpoint. Used by clients to
+ // differentiate traffic from Google servers vs Non-google servers.
+ // Will not be used if empty().
+ std::string endpoint_id;
+};
+
+struct QUIC_EXPORT_PRIVATE QuicVersionNegotiationPacket {
+ QuicVersionNegotiationPacket();
+ explicit QuicVersionNegotiationPacket(QuicConnectionId connection_id);
+ QuicVersionNegotiationPacket(const QuicVersionNegotiationPacket& other);
+ ~QuicVersionNegotiationPacket();
+
+ QuicConnectionId connection_id;
+ ParsedQuicVersionVector versions;
+};
+
+struct QUIC_EXPORT_PRIVATE QuicIetfStatelessResetPacket {
+ QuicIetfStatelessResetPacket();
+ QuicIetfStatelessResetPacket(const QuicPacketHeader& header,
+ QuicUint128 token);
+ QuicIetfStatelessResetPacket(const QuicIetfStatelessResetPacket& other);
+ ~QuicIetfStatelessResetPacket();
+
+ QuicPacketHeader header;
+ QuicUint128 stateless_reset_token;
+};
+
+class QUIC_EXPORT_PRIVATE QuicData {
+ public:
+ QuicData(const char* buffer, size_t length);
+ QuicData(const char* buffer, size_t length, bool owns_buffer);
+ QuicData(const QuicData&) = delete;
+ QuicData& operator=(const QuicData&) = delete;
+ virtual ~QuicData();
+
+ QuicStringPiece AsStringPiece() const {
+ return QuicStringPiece(data(), length());
+ }
+
+ const char* data() const { return buffer_; }
+ size_t length() const { return length_; }
+
+ private:
+ const char* buffer_;
+ size_t length_;
+ bool owns_buffer_;
+};
+
+class QUIC_EXPORT_PRIVATE QuicPacket : public QuicData {
+ public:
+ QuicPacket(char* buffer,
+ size_t length,
+ bool owns_buffer,
+ QuicConnectionIdLength destination_connection_id_length,
+ QuicConnectionIdLength source_connection_id_length,
+ bool includes_version,
+ bool includes_diversification_nonce,
+ QuicPacketNumberLength packet_number_length,
+ QuicVariableLengthIntegerLength retry_token_length_length,
+ QuicByteCount retry_token_length,
+ QuicVariableLengthIntegerLength length_length);
+ QuicPacket(QuicTransportVersion version,
+ char* buffer,
+ size_t length,
+ bool owns_buffer,
+ const QuicPacketHeader& header);
+ QuicPacket(const QuicPacket&) = delete;
+ QuicPacket& operator=(const QuicPacket&) = delete;
+
+ QuicStringPiece AssociatedData(QuicTransportVersion version) const;
+ QuicStringPiece Plaintext(QuicTransportVersion version) const;
+
+ char* mutable_data() { return buffer_; }
+
+ private:
+ char* buffer_;
+ const QuicConnectionIdLength destination_connection_id_length_;
+ const QuicConnectionIdLength source_connection_id_length_;
+ const bool includes_version_;
+ const bool includes_diversification_nonce_;
+ const QuicPacketNumberLength packet_number_length_;
+ const QuicVariableLengthIntegerLength retry_token_length_length_;
+ const QuicByteCount retry_token_length_;
+ const QuicVariableLengthIntegerLength length_length_;
+};
+
+class QUIC_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData {
+ public:
+ QuicEncryptedPacket(const char* buffer, size_t length);
+ QuicEncryptedPacket(const char* buffer, size_t length, bool owns_buffer);
+ QuicEncryptedPacket(const QuicEncryptedPacket&) = delete;
+ QuicEncryptedPacket& operator=(const QuicEncryptedPacket&) = delete;
+
+ // Clones the packet into a new packet which owns the buffer.
+ std::unique_ptr<QuicEncryptedPacket> Clone() const;
+
+ // By default, gtest prints the raw bytes of an object. The bool data
+ // member (in the base class QuicData) causes this object to have padding
+ // bytes, which causes the default gtest object printer to read
+ // uninitialize memory. So we need to teach gtest how to print this object.
+ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os,
+ const QuicEncryptedPacket& s);
+};
+
+// A received encrypted QUIC packet, with a recorded time of receipt.
+class QUIC_EXPORT_PRIVATE QuicReceivedPacket : public QuicEncryptedPacket {
+ public:
+ QuicReceivedPacket(const char* buffer, size_t length, QuicTime receipt_time);
+ QuicReceivedPacket(const char* buffer,
+ size_t length,
+ QuicTime receipt_time,
+ bool owns_buffer);
+ QuicReceivedPacket(const char* buffer,
+ size_t length,
+ QuicTime receipt_time,
+ bool owns_buffer,
+ int ttl,
+ bool ttl_valid);
+ QuicReceivedPacket(const char* buffer,
+ size_t length,
+ QuicTime receipt_time,
+ bool owns_buffer,
+ int ttl,
+ bool ttl_valid,
+ char* packet_headers,
+ size_t headers_length,
+ bool owns_header_buffer);
+ ~QuicReceivedPacket();
+ QuicReceivedPacket(const QuicReceivedPacket&) = delete;
+ QuicReceivedPacket& operator=(const QuicReceivedPacket&) = delete;
+
+ // Clones the packet into a new packet which owns the buffer.
+ std::unique_ptr<QuicReceivedPacket> Clone() const;
+
+ // Returns the time at which the packet was received.
+ QuicTime receipt_time() const { return receipt_time_; }
+
+ // This is the TTL of the packet, assuming ttl_vaild_ is true.
+ int ttl() const { return ttl_; }
+
+ // Start of packet headers.
+ char* packet_headers() const { return packet_headers_; }
+
+ // Length of packet headers.
+ int headers_length() const { return headers_length_; }
+
+ // By default, gtest prints the raw bytes of an object. The bool data
+ // member (in the base class QuicData) causes this object to have padding
+ // bytes, which causes the default gtest object printer to read
+ // uninitialize memory. So we need to teach gtest how to print this object.
+ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os,
+ const QuicReceivedPacket& s);
+
+ private:
+ const QuicTime receipt_time_;
+ int ttl_;
+ // Points to the start of packet headers.
+ char* packet_headers_;
+ // Length of packet headers.
+ int headers_length_;
+ // Whether owns the buffer for packet headers.
+ bool owns_header_buffer_;
+};
+
+struct QUIC_EXPORT_PRIVATE SerializedPacket {
+ SerializedPacket(QuicPacketNumber packet_number,
+ QuicPacketNumberLength packet_number_length,
+ const char* encrypted_buffer,
+ QuicPacketLength encrypted_length,
+ bool has_ack,
+ bool has_stop_waiting);
+ SerializedPacket(const SerializedPacket& other);
+ SerializedPacket& operator=(const SerializedPacket& other);
+ SerializedPacket(SerializedPacket&& other);
+ ~SerializedPacket();
+
+ // Not owned.
+ const char* encrypted_buffer;
+ QuicPacketLength encrypted_length;
+ QuicFrames retransmittable_frames;
+ IsHandshake has_crypto_handshake;
+ // -1: full padding to the end of a max-sized packet
+ // 0: no padding
+ // otherwise: only pad up to num_padding_bytes bytes
+ int16_t num_padding_bytes;
+ QuicPacketNumber packet_number;
+ QuicPacketNumberLength packet_number_length;
+ EncryptionLevel encryption_level;
+ bool has_ack;
+ bool has_stop_waiting;
+ TransmissionType transmission_type;
+ QuicPacketNumber original_packet_number;
+ // The largest acked of the AckFrame in this packet if has_ack is true,
+ // 0 otherwise.
+ QuicPacketNumber largest_acked;
+};
+
+// Deletes and clears all the frames and the packet from serialized packet.
+QUIC_EXPORT_PRIVATE void ClearSerializedPacket(
+ SerializedPacket* serialized_packet);
+
+// Allocates a new char[] of size |packet.encrypted_length| and copies in
+// |packet.encrypted_buffer|.
+QUIC_EXPORT_PRIVATE char* CopyBuffer(const SerializedPacket& packet);
+
+struct QUIC_EXPORT_PRIVATE SerializedPacketDeleter {
+ void operator()(SerializedPacket* packet) {
+ if (packet->encrypted_buffer != nullptr) {
+ delete[] packet->encrypted_buffer;
+ }
+ delete packet;
+ }
+};
+
+// On destruction, OwningSerializedPacketPointer deletes a packet's (on-heap)
+// encrypted_buffer before deleting the (also on-heap) packet itself.
+// TODO(wub): Maybe delete retransmittable_frames too?
+typedef std::unique_ptr<SerializedPacket, SerializedPacketDeleter>
+ OwningSerializedPacketPointer;
+
+// Context for an incoming packet.
+struct QUIC_EXPORT_PRIVATE QuicPerPacketContext {
+ virtual ~QuicPerPacketContext() {}
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PACKETS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_pending_retransmission.h b/chromium/net/third_party/quiche/src/quic/core/quic_pending_retransmission.h
new file mode 100644
index 00000000000..d1e9657a093
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_pending_retransmission.h
@@ -0,0 +1,54 @@
+// 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 QUICHE_QUIC_CORE_QUIC_PENDING_RETRANSMISSION_H_
+#define QUICHE_QUIC_CORE_QUIC_PENDING_RETRANSMISSION_H_
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Struct to store the pending retransmission information.
+struct QUIC_EXPORT_PRIVATE QuicPendingRetransmission {
+ QuicPendingRetransmission(QuicPacketNumber packet_number,
+ TransmissionType transmission_type,
+ const QuicFrames& retransmittable_frames,
+ bool has_crypto_handshake,
+ int num_padding_bytes,
+ EncryptionLevel encryption_level,
+ QuicPacketNumberLength packet_number_length)
+ : packet_number(packet_number),
+ retransmittable_frames(retransmittable_frames),
+ transmission_type(transmission_type),
+ has_crypto_handshake(has_crypto_handshake),
+ num_padding_bytes(num_padding_bytes),
+ encryption_level(encryption_level),
+ packet_number_length(packet_number_length) {}
+
+ QuicPendingRetransmission(QuicPacketNumber packet_number,
+ TransmissionType transmission_type,
+ const QuicTransmissionInfo& tranmission_info)
+ : packet_number(packet_number),
+ retransmittable_frames(tranmission_info.retransmittable_frames),
+ transmission_type(transmission_type),
+ has_crypto_handshake(tranmission_info.has_crypto_handshake),
+ num_padding_bytes(tranmission_info.num_padding_bytes),
+ encryption_level(tranmission_info.encryption_level),
+ packet_number_length(tranmission_info.packet_number_length) {}
+
+ QuicPacketNumber packet_number;
+ const QuicFrames& retransmittable_frames;
+ TransmissionType transmission_type;
+ bool has_crypto_handshake;
+ int num_padding_bytes;
+ EncryptionLevel encryption_level;
+ QuicPacketNumberLength packet_number_length;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PENDING_RETRANSMISSION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_process_packet_interface.h b/chromium/net/third_party/quiche/src/quic/core/quic_process_packet_interface.h
new file mode 100644
index 00000000000..fc4257e9927
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_process_packet_interface.h
@@ -0,0 +1,24 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_PROCESS_PACKET_INTERFACE_H_
+#define QUICHE_QUIC_CORE_QUIC_PROCESS_PACKET_INTERFACE_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+// A class to process each incoming packet.
+class ProcessPacketInterface {
+ public:
+ virtual ~ProcessPacketInterface() {}
+ virtual void ProcessPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_PROCESS_PACKET_INTERFACE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc
new file mode 100644
index 00000000000..528b8665bb6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.cc
@@ -0,0 +1,359 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_received_packet_manager.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// The maximum number of packets to ack immediately after a missing packet for
+// fast retransmission to kick in at the sender. This limit is created to
+// reduce the number of acks sent that have no benefit for fast retransmission.
+// Set to the number of nacks needed for fast retransmit plus one for protection
+// against an ack loss
+const size_t kMaxPacketsAfterNewMissing = 4;
+
+// Maximum number of retransmittable packets received before sending an ack.
+const QuicPacketCount kDefaultRetransmittablePacketsBeforeAck = 2;
+// Minimum number of packets received before ack decimation is enabled.
+// This intends to avoid the beginning of slow start, when CWNDs may be
+// rapidly increasing.
+const QuicPacketCount kMinReceivedBeforeAckDecimation = 100;
+// Wait for up to 10 retransmittable packets before sending an ack.
+const QuicPacketCount kMaxRetransmittablePacketsBeforeAck = 10;
+// One quarter RTT delay when doing ack decimation.
+const float kAckDecimationDelay = 0.25;
+// One eighth RTT delay when doing ack decimation.
+const float kShortAckDecimationDelay = 0.125;
+} // namespace
+
+QuicReceivedPacketManager::QuicReceivedPacketManager()
+ : QuicReceivedPacketManager(nullptr) {}
+
+QuicReceivedPacketManager::QuicReceivedPacketManager(QuicConnectionStats* stats)
+ : ack_frame_updated_(false),
+ max_ack_ranges_(0),
+ time_largest_observed_(QuicTime::Zero()),
+ save_timestamps_(false),
+ stats_(stats),
+ ack_mode_(GetQuicReloadableFlag(quic_enable_ack_decimation)
+ ? ACK_DECIMATION
+ : TCP_ACKING),
+ num_retransmittable_packets_received_since_last_ack_sent_(0),
+ min_received_before_ack_decimation_(kMinReceivedBeforeAckDecimation),
+ ack_frequency_before_ack_decimation_(
+ kDefaultRetransmittablePacketsBeforeAck),
+ ack_decimation_delay_(kAckDecimationDelay),
+ unlimited_ack_decimation_(false),
+ fast_ack_after_quiescence_(false),
+ ack_timeout_(QuicTime::Zero()),
+ time_of_previous_received_packet_(QuicTime::Zero()),
+ was_last_packet_missing_(false),
+ decide_when_to_send_acks_(
+ GetQuicReloadableFlag(quic_deprecate_ack_bundling_mode) &&
+ GetQuicReloadableFlag(quic_rpm_decides_when_to_send_acks)) {}
+
+QuicReceivedPacketManager::~QuicReceivedPacketManager() {}
+
+void QuicReceivedPacketManager::SetFromConfig(const QuicConfig& config,
+ Perspective perspective) {
+ DCHECK(decide_when_to_send_acks_);
+ if (GetQuicReloadableFlag(quic_enable_ack_decimation) &&
+ config.HasClientSentConnectionOption(kACD0, perspective)) {
+ ack_mode_ = TCP_ACKING;
+ }
+ if (config.HasClientSentConnectionOption(kACKD, perspective)) {
+ ack_mode_ = ACK_DECIMATION;
+ }
+ if (config.HasClientSentConnectionOption(kAKD2, perspective)) {
+ ack_mode_ = ACK_DECIMATION_WITH_REORDERING;
+ }
+ if (config.HasClientSentConnectionOption(kAKD3, perspective)) {
+ ack_mode_ = ACK_DECIMATION;
+ ack_decimation_delay_ = kShortAckDecimationDelay;
+ }
+ if (config.HasClientSentConnectionOption(kAKD4, perspective)) {
+ ack_mode_ = ACK_DECIMATION_WITH_REORDERING;
+ ack_decimation_delay_ = kShortAckDecimationDelay;
+ }
+ if (config.HasClientSentConnectionOption(kAKDU, perspective)) {
+ unlimited_ack_decimation_ = true;
+ }
+ if (config.HasClientSentConnectionOption(kACKQ, perspective)) {
+ fast_ack_after_quiescence_ = true;
+ }
+}
+
+void QuicReceivedPacketManager::RecordPacketReceived(
+ const QuicPacketHeader& header,
+ QuicTime receipt_time) {
+ const QuicPacketNumber packet_number = header.packet_number;
+ DCHECK(IsAwaitingPacket(packet_number)) << " packet_number:" << packet_number;
+ if (decide_when_to_send_acks_) {
+ was_last_packet_missing_ = IsMissing(packet_number);
+ }
+ if (!ack_frame_updated_) {
+ ack_frame_.received_packet_times.clear();
+ }
+ ack_frame_updated_ = true;
+
+ if (LargestAcked(ack_frame_).IsInitialized() &&
+ LargestAcked(ack_frame_) > packet_number) {
+ // Record how out of order stats.
+ ++stats_->packets_reordered;
+ stats_->max_sequence_reordering =
+ std::max(stats_->max_sequence_reordering,
+ LargestAcked(ack_frame_) - packet_number);
+ int64_t reordering_time_us =
+ (receipt_time - time_largest_observed_).ToMicroseconds();
+ stats_->max_time_reordering_us =
+ std::max(stats_->max_time_reordering_us, reordering_time_us);
+ }
+ if (!LargestAcked(ack_frame_).IsInitialized() ||
+ packet_number > LargestAcked(ack_frame_)) {
+ ack_frame_.largest_acked = packet_number;
+ time_largest_observed_ = receipt_time;
+ }
+ ack_frame_.packets.Add(packet_number);
+
+ if (save_timestamps_) {
+ // The timestamp format only handles packets in time order.
+ if (!ack_frame_.received_packet_times.empty() &&
+ ack_frame_.received_packet_times.back().second > receipt_time) {
+ LOG(WARNING)
+ << "Receive time went backwards from: "
+ << ack_frame_.received_packet_times.back().second.ToDebuggingValue()
+ << " to " << receipt_time.ToDebuggingValue();
+ } else {
+ ack_frame_.received_packet_times.push_back(
+ std::make_pair(packet_number, receipt_time));
+ }
+ }
+
+ if (least_received_packet_number_.IsInitialized()) {
+ least_received_packet_number_ =
+ std::min(least_received_packet_number_, packet_number);
+ } else {
+ least_received_packet_number_ = packet_number;
+ }
+}
+
+bool QuicReceivedPacketManager::IsMissing(QuicPacketNumber packet_number) {
+ return LargestAcked(ack_frame_).IsInitialized() &&
+ packet_number < LargestAcked(ack_frame_) &&
+ !ack_frame_.packets.Contains(packet_number);
+}
+
+bool QuicReceivedPacketManager::IsAwaitingPacket(
+ QuicPacketNumber packet_number) const {
+ return quic::IsAwaitingPacket(ack_frame_, packet_number,
+ peer_least_packet_awaiting_ack_);
+}
+
+const QuicFrame QuicReceivedPacketManager::GetUpdatedAckFrame(
+ QuicTime approximate_now) {
+ if (!decide_when_to_send_acks_) {
+ ack_frame_updated_ = false;
+ }
+ if (time_largest_observed_ == QuicTime::Zero()) {
+ // We have received no packets.
+ ack_frame_.ack_delay_time = QuicTime::Delta::Infinite();
+ } else {
+ // Ensure the delta is zero if approximate now is "in the past".
+ ack_frame_.ack_delay_time = approximate_now < time_largest_observed_
+ ? QuicTime::Delta::Zero()
+ : approximate_now - time_largest_observed_;
+ }
+ while (max_ack_ranges_ > 0 &&
+ ack_frame_.packets.NumIntervals() > max_ack_ranges_) {
+ ack_frame_.packets.RemoveSmallestInterval();
+ }
+ // Clear all packet times if any are too far from largest observed.
+ // It's expected this is extremely rare.
+ for (auto it = ack_frame_.received_packet_times.begin();
+ it != ack_frame_.received_packet_times.end();) {
+ if (LargestAcked(ack_frame_) - it->first >=
+ std::numeric_limits<uint8_t>::max()) {
+ it = ack_frame_.received_packet_times.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ return QuicFrame(&ack_frame_);
+}
+
+void QuicReceivedPacketManager::DontWaitForPacketsBefore(
+ QuicPacketNumber least_unacked) {
+ if (!least_unacked.IsInitialized()) {
+ return;
+ }
+ // ValidateAck() should fail if peer_least_packet_awaiting_ack shrinks.
+ DCHECK(!peer_least_packet_awaiting_ack_.IsInitialized() ||
+ peer_least_packet_awaiting_ack_ <= least_unacked);
+ if (!peer_least_packet_awaiting_ack_.IsInitialized() ||
+ least_unacked > peer_least_packet_awaiting_ack_) {
+ peer_least_packet_awaiting_ack_ = least_unacked;
+ bool packets_updated = ack_frame_.packets.RemoveUpTo(least_unacked);
+ if (packets_updated) {
+ // Ack frame gets updated because packets set is updated because of stop
+ // waiting frame.
+ ack_frame_updated_ = true;
+ }
+ }
+ DCHECK(ack_frame_.packets.Empty() ||
+ !peer_least_packet_awaiting_ack_.IsInitialized() ||
+ ack_frame_.packets.Min() >= peer_least_packet_awaiting_ack_);
+}
+
+void QuicReceivedPacketManager::MaybeUpdateAckTimeout(
+ bool should_last_packet_instigate_acks,
+ QuicPacketNumber last_received_packet_number,
+ QuicTime time_of_last_received_packet,
+ QuicTime now,
+ const RttStats* rtt_stats,
+ QuicTime::Delta delayed_ack_time) {
+ DCHECK(decide_when_to_send_acks_);
+ if (!ack_frame_updated_) {
+ // ACK frame has not been updated, nothing to do.
+ return;
+ }
+
+ if (was_last_packet_missing_ && last_sent_largest_acked_.IsInitialized() &&
+ last_received_packet_number < last_sent_largest_acked_) {
+ // Only ack immediately if an ACK frame was sent with a larger largest acked
+ // than the newly received packet number.
+ ack_timeout_ = now;
+ return;
+ }
+
+ if (!should_last_packet_instigate_acks) {
+ return;
+ }
+
+ ++num_retransmittable_packets_received_since_last_ack_sent_;
+ if (ack_mode_ != TCP_ACKING &&
+ last_received_packet_number >= PeerFirstSendingPacketNumber() +
+ min_received_before_ack_decimation_) {
+ // Ack up to 10 packets at once unless ack decimation is unlimited.
+ if (!unlimited_ack_decimation_ &&
+ num_retransmittable_packets_received_since_last_ack_sent_ >=
+ kMaxRetransmittablePacketsBeforeAck) {
+ ack_timeout_ = now;
+ return;
+ }
+ // Wait for the minimum of the ack decimation delay or the delayed ack time
+ // before sending an ack.
+ QuicTime::Delta ack_delay = std::min(
+ delayed_ack_time, rtt_stats->min_rtt() * ack_decimation_delay_);
+ if (fast_ack_after_quiescence_ && now - time_of_previous_received_packet_ >
+ rtt_stats->SmoothedOrInitialRtt()) {
+ // Ack the first packet out of queiscence faster, because QUIC does
+ // not pace the first few packets and commonly these may be handshake
+ // or TLP packets, which we'd like to acknowledge quickly.
+ ack_delay = QuicTime::Delta::FromMilliseconds(1);
+ }
+ MaybeUpdateAckTimeoutTo(now + ack_delay);
+ } else {
+ // Ack with a timer or every 2 packets by default.
+ if (num_retransmittable_packets_received_since_last_ack_sent_ >=
+ ack_frequency_before_ack_decimation_) {
+ ack_timeout_ = now;
+ } else if (fast_ack_after_quiescence_ &&
+ (now - time_of_previous_received_packet_) >
+ rtt_stats->SmoothedOrInitialRtt()) {
+ // Ack the first packet out of queiscence faster, because QUIC does
+ // not pace the first few packets and commonly these may be handshake
+ // or TLP packets, which we'd like to acknowledge quickly.
+ MaybeUpdateAckTimeoutTo(now + QuicTime::Delta::FromMilliseconds(1));
+ } else {
+ MaybeUpdateAckTimeoutTo(now + delayed_ack_time);
+ }
+ }
+
+ // If there are new missing packets to report, send an ack immediately.
+ if (HasNewMissingPackets()) {
+ if (ack_mode_ == ACK_DECIMATION_WITH_REORDERING) {
+ // Wait the minimum of an eighth min_rtt and the existing ack time.
+ QuicTime ack_time = now + kShortAckDecimationDelay * rtt_stats->min_rtt();
+ MaybeUpdateAckTimeoutTo(ack_time);
+ } else {
+ ack_timeout_ = now;
+ }
+ }
+
+ if (fast_ack_after_quiescence_) {
+ time_of_previous_received_packet_ = time_of_last_received_packet;
+ }
+}
+
+void QuicReceivedPacketManager::ResetAckStates() {
+ DCHECK(decide_when_to_send_acks_);
+ ack_frame_updated_ = false;
+ ack_timeout_ = QuicTime::Zero();
+ num_retransmittable_packets_received_since_last_ack_sent_ = 0;
+ last_sent_largest_acked_ = LargestAcked(ack_frame_);
+}
+
+void QuicReceivedPacketManager::MaybeUpdateAckTimeoutTo(QuicTime time) {
+ DCHECK(decide_when_to_send_acks_);
+ if (!ack_timeout_.IsInitialized() || ack_timeout_ > time) {
+ ack_timeout_ = time;
+ }
+}
+
+bool QuicReceivedPacketManager::HasMissingPackets() const {
+ if (ack_frame_.packets.Empty()) {
+ return false;
+ }
+ if (ack_frame_.packets.NumIntervals() > 1) {
+ return true;
+ }
+ if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ return ack_frame_.packets.Min() >
+ (peer_least_packet_awaiting_ack_.IsInitialized()
+ ? peer_least_packet_awaiting_ack_
+ : QuicPacketNumber(1));
+ }
+ return peer_least_packet_awaiting_ack_.IsInitialized() &&
+ ack_frame_.packets.Min() > peer_least_packet_awaiting_ack_;
+}
+
+bool QuicReceivedPacketManager::HasNewMissingPackets() const {
+ return HasMissingPackets() &&
+ ack_frame_.packets.LastIntervalLength() <= kMaxPacketsAfterNewMissing;
+}
+
+bool QuicReceivedPacketManager::ack_frame_updated() const {
+ return ack_frame_updated_;
+}
+
+QuicPacketNumber QuicReceivedPacketManager::GetLargestObserved() const {
+ return LargestAcked(ack_frame_);
+}
+
+QuicPacketNumber QuicReceivedPacketManager::PeerFirstSendingPacketNumber()
+ const {
+ if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ return QuicPacketNumber(1);
+ }
+ if (!least_received_packet_number_.IsInitialized()) {
+ QUIC_BUG << "No packets have been received yet";
+ return QuicPacketNumber(1);
+ }
+ return least_received_packet_number_;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.h
new file mode 100644
index 00000000000..7c9bd7fccf8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager.h
@@ -0,0 +1,197 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_RECEIVED_PACKET_MANAGER_H_
+#define QUICHE_QUIC_CORE_QUIC_RECEIVED_PACKET_MANAGER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class RttStats;
+
+namespace test {
+class QuicConnectionPeer;
+class QuicReceivedPacketManagerPeer;
+class UberReceivedPacketManagerPeer;
+} // namespace test
+
+struct QuicConnectionStats;
+
+// Records all received packets by a connection.
+class QUIC_EXPORT_PRIVATE QuicReceivedPacketManager {
+ public:
+ QuicReceivedPacketManager();
+ explicit QuicReceivedPacketManager(QuicConnectionStats* stats);
+ QuicReceivedPacketManager(const QuicReceivedPacketManager&) = delete;
+ QuicReceivedPacketManager& operator=(const QuicReceivedPacketManager&) =
+ delete;
+ virtual ~QuicReceivedPacketManager();
+
+ void SetFromConfig(const QuicConfig& config, Perspective perspective);
+
+ // Updates the internal state concerning which packets have been received.
+ // header: the packet header.
+ // timestamp: the arrival time of the packet.
+ virtual void RecordPacketReceived(const QuicPacketHeader& header,
+ QuicTime receipt_time);
+
+ // Checks whether |packet_number| is missing and less than largest observed.
+ virtual bool IsMissing(QuicPacketNumber packet_number);
+
+ // Checks if we're still waiting for the packet with |packet_number|.
+ virtual bool IsAwaitingPacket(QuicPacketNumber packet_number) const;
+
+ // Retrieves a frame containing a QuicAckFrame. The ack frame may not be
+ // changed outside QuicReceivedPacketManager and must be serialized before
+ // another packet is received, or it will change.
+ const QuicFrame GetUpdatedAckFrame(QuicTime approximate_now);
+
+ // Deletes all missing packets before least unacked. The connection won't
+ // process any packets with packet number before |least_unacked| that it
+ // received after this call.
+ void DontWaitForPacketsBefore(QuicPacketNumber least_unacked);
+
+ // Called to update ack_timeout_ to the time when an ACK needs to be sent. A
+ // caller can decide whether and when to send an ACK by retrieving
+ // ack_timeout_. If ack_timeout_ is not initialized, no ACK needs to be sent.
+ // Otherwise, ACK needs to be sent by the specified time.
+ void MaybeUpdateAckTimeout(bool should_last_packet_instigate_acks,
+ QuicPacketNumber last_received_packet_number,
+ QuicTime time_of_last_received_packet,
+ QuicTime now,
+ const RttStats* rtt_stats,
+ QuicTime::Delta delayed_ack_time);
+
+ // Resets ACK related states, called after an ACK is successfully sent.
+ void ResetAckStates();
+
+ // Returns true if there are any missing packets.
+ bool HasMissingPackets() const;
+
+ // Returns true when there are new missing packets to be reported within 3
+ // packets of the largest observed.
+ virtual bool HasNewMissingPackets() const;
+
+ QuicPacketNumber peer_least_packet_awaiting_ack() const {
+ return peer_least_packet_awaiting_ack_;
+ }
+
+ virtual bool ack_frame_updated() const;
+
+ QuicPacketNumber GetLargestObserved() const;
+
+ // Returns peer first sending packet number to our best knowledge. If
+ // GetQuicRestartFlag(quic_enable_accept_random_ipn) is false, returns 1.
+ // Otherwise considers least_received_packet_number_ as peer first sending
+ // packet number. Please note, this function should only be called when at
+ // least one packet has been received.
+ QuicPacketNumber PeerFirstSendingPacketNumber() const;
+
+ void set_connection_stats(QuicConnectionStats* stats) { stats_ = stats; }
+
+ // For logging purposes.
+ const QuicAckFrame& ack_frame() const { return ack_frame_; }
+
+ void set_max_ack_ranges(size_t max_ack_ranges) {
+ max_ack_ranges_ = max_ack_ranges;
+ }
+
+ void set_save_timestamps(bool save_timestamps) {
+ save_timestamps_ = save_timestamps;
+ }
+
+ size_t min_received_before_ack_decimation() const {
+ return min_received_before_ack_decimation_;
+ }
+ void set_min_received_before_ack_decimation(size_t new_value) {
+ min_received_before_ack_decimation_ = new_value;
+ }
+
+ size_t ack_frequency_before_ack_decimation() const {
+ return ack_frequency_before_ack_decimation_;
+ }
+ void set_ack_frequency_before_ack_decimation(size_t new_value) {
+ DCHECK_GT(new_value, 0u);
+ ack_frequency_before_ack_decimation_ = new_value;
+ }
+
+ QuicTime ack_timeout() const { return ack_timeout_; }
+
+ bool decide_when_to_send_acks() const { return decide_when_to_send_acks_; }
+
+ private:
+ friend class test::QuicConnectionPeer;
+ friend class test::QuicReceivedPacketManagerPeer;
+ friend class test::UberReceivedPacketManagerPeer;
+
+ // Sets ack_timeout_ to |time| if ack_timeout_ is not initialized or > time.
+ void MaybeUpdateAckTimeoutTo(QuicTime time);
+
+ // Least packet number of the the packet sent by the peer for which it
+ // hasn't received an ack.
+ QuicPacketNumber peer_least_packet_awaiting_ack_;
+
+ // Received packet information used to produce acks.
+ QuicAckFrame ack_frame_;
+
+ // True if |ack_frame_| has been updated since UpdateReceivedPacketInfo was
+ // last called.
+ bool ack_frame_updated_;
+
+ // Maximum number of ack ranges allowed to be stored in the ack frame.
+ size_t max_ack_ranges_;
+
+ // The time we received the largest_observed packet number, or zero if
+ // no packet numbers have been received since UpdateReceivedPacketInfo.
+ // Needed for calculating ack_delay_time.
+ QuicTime time_largest_observed_;
+
+ // If true, save timestamps in the ack_frame_.
+ bool save_timestamps_;
+
+ // Least packet number received from peer.
+ QuicPacketNumber least_received_packet_number_;
+
+ QuicConnectionStats* stats_;
+
+ AckMode ack_mode_;
+ // How many retransmittable packets have arrived without sending an ack.
+ QuicPacketCount num_retransmittable_packets_received_since_last_ack_sent_;
+ // Ack decimation will start happening after this many packets are received.
+ size_t min_received_before_ack_decimation_;
+ // Before ack decimation starts (if enabled), we ack every n-th packet.
+ size_t ack_frequency_before_ack_decimation_;
+ // The max delay in fraction of min_rtt to use when sending decimated acks.
+ float ack_decimation_delay_;
+ // When true, removes ack decimation's max number of packets(10) before
+ // sending an ack.
+ bool unlimited_ack_decimation_;
+ // When true, use a 1ms delayed ack timer if it's been an SRTT since a packet
+ // was received.
+ bool fast_ack_after_quiescence_;
+
+ // Time that an ACK needs to be sent. 0 means no ACK is pending. Used when
+ // decide_when_to_send_acks_ is true.
+ QuicTime ack_timeout_;
+
+ // The time the previous ack-instigating packet was received and processed.
+ QuicTime time_of_previous_received_packet_;
+ // Whether the most recent packet was missing before it was received.
+ bool was_last_packet_missing_;
+
+ // Last sent largest acked, which gets updated when ACK was successfully sent.
+ QuicPacketNumber last_sent_largest_acked_;
+
+ // Latched value of quic_deprecate_ack_bundling_mode and
+ // quic_rpm_decides_when_to_send_acks.
+ const bool decide_when_to_send_acks_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_RECEIVED_PACKET_MANAGER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc
new file mode 100644
index 00000000000..0512cb719d0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_received_packet_manager_test.cc
@@ -0,0 +1,807 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_received_packet_manager.h"
+
+#include <algorithm>
+#include <ostream>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+namespace test {
+
+class QuicReceivedPacketManagerPeer {
+ public:
+ static void SetAckMode(QuicReceivedPacketManager* manager, AckMode ack_mode) {
+ manager->ack_mode_ = ack_mode;
+ }
+
+ static void SetFastAckAfterQuiescence(QuicReceivedPacketManager* manager,
+ bool fast_ack_after_quiescence) {
+ manager->fast_ack_after_quiescence_ = fast_ack_after_quiescence;
+ }
+
+ static void SetAckDecimationDelay(QuicReceivedPacketManager* manager,
+ float ack_decimation_delay) {
+ manager->ack_decimation_delay_ = ack_decimation_delay;
+ }
+};
+
+namespace {
+
+const bool kInstigateAck = true;
+const QuicTime::Delta kMinRttMs = QuicTime::Delta::FromMilliseconds(40);
+const QuicTime::Delta kDelayedAckTime =
+ QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+
+struct TestParams {
+ explicit TestParams(QuicTransportVersion version) : version(version) {}
+
+ friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+ os << "{ version: " << QuicVersionToString(p.version) << " }";
+ return os;
+ }
+
+ QuicTransportVersion version;
+};
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ QuicTransportVersionVector all_supported_versions =
+ AllSupportedTransportVersions();
+ for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+ params.push_back(TestParams(all_supported_versions[i]));
+ }
+ return params;
+}
+
+class QuicReceivedPacketManagerTest : public QuicTestWithParam<TestParams> {
+ protected:
+ QuicReceivedPacketManagerTest() : received_manager_(&stats_) {
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ rtt_stats_.UpdateRtt(kMinRttMs, QuicTime::Delta::Zero(), QuicTime::Zero());
+ received_manager_.set_save_timestamps(true);
+ }
+
+ void RecordPacketReceipt(uint64_t packet_number) {
+ RecordPacketReceipt(packet_number, QuicTime::Zero());
+ }
+
+ void RecordPacketReceipt(uint64_t packet_number, QuicTime receipt_time) {
+ QuicPacketHeader header;
+ header.packet_number = QuicPacketNumber(packet_number);
+ received_manager_.RecordPacketReceived(header, receipt_time);
+ }
+
+ bool HasPendingAck() {
+ DCHECK(received_manager_.decide_when_to_send_acks());
+ return received_manager_.ack_timeout().IsInitialized();
+ }
+
+ void MaybeUpdateAckTimeout(bool should_last_packet_instigate_acks,
+ uint64_t last_received_packet_number) {
+ DCHECK(received_manager_.decide_when_to_send_acks());
+ received_manager_.MaybeUpdateAckTimeout(
+ should_last_packet_instigate_acks,
+ QuicPacketNumber(last_received_packet_number), clock_.ApproximateNow(),
+ clock_.ApproximateNow(), &rtt_stats_, kDelayedAckTime);
+ }
+
+ void CheckAckTimeout(QuicTime time) {
+ DCHECK(HasPendingAck() && received_manager_.ack_timeout() == time);
+ if (time <= clock_.ApproximateNow()) {
+ // ACK timeout expires, send an ACK.
+ received_manager_.ResetAckStates();
+ DCHECK(!HasPendingAck());
+ }
+ }
+
+ MockClock clock_;
+ RttStats rtt_stats_;
+ QuicConnectionStats stats_;
+ QuicReceivedPacketManager received_manager_;
+};
+
+INSTANTIATE_TEST_SUITE_P(QuicReceivedPacketManagerTest,
+ QuicReceivedPacketManagerTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) {
+ QuicPacketHeader header;
+ header.packet_number = QuicPacketNumber(2u);
+ received_manager_.RecordPacketReceived(header, QuicTime::Zero());
+ header.packet_number = QuicPacketNumber(7u);
+ received_manager_.RecordPacketReceived(header, QuicTime::Zero());
+ EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(3u)));
+ EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(6u)));
+ received_manager_.DontWaitForPacketsBefore(QuicPacketNumber(4));
+ EXPECT_FALSE(received_manager_.IsAwaitingPacket(QuicPacketNumber(3u)));
+ EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(6u)));
+}
+
+TEST_P(QuicReceivedPacketManagerTest, GetUpdatedAckFrame) {
+ QuicPacketHeader header;
+ header.packet_number = QuicPacketNumber(2u);
+ QuicTime two_ms = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2);
+ EXPECT_FALSE(received_manager_.ack_frame_updated());
+ received_manager_.RecordPacketReceived(header, two_ms);
+ EXPECT_TRUE(received_manager_.ack_frame_updated());
+
+ QuicFrame ack = received_manager_.GetUpdatedAckFrame(QuicTime::Zero());
+ if (received_manager_.decide_when_to_send_acks()) {
+ received_manager_.ResetAckStates();
+ }
+ EXPECT_FALSE(received_manager_.ack_frame_updated());
+ // When UpdateReceivedPacketInfo with a time earlier than the time of the
+ // largest observed packet, make sure that the delta is 0, not negative.
+ EXPECT_EQ(QuicTime::Delta::Zero(), ack.ack_frame->ack_delay_time);
+ EXPECT_EQ(1u, ack.ack_frame->received_packet_times.size());
+
+ QuicTime four_ms = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(4);
+ ack = received_manager_.GetUpdatedAckFrame(four_ms);
+ if (received_manager_.decide_when_to_send_acks()) {
+ received_manager_.ResetAckStates();
+ }
+ EXPECT_FALSE(received_manager_.ack_frame_updated());
+ // When UpdateReceivedPacketInfo after not having received a new packet,
+ // the delta should still be accurate.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2),
+ ack.ack_frame->ack_delay_time);
+ // And received packet times won't have change.
+ EXPECT_EQ(1u, ack.ack_frame->received_packet_times.size());
+
+ header.packet_number = QuicPacketNumber(999u);
+ received_manager_.RecordPacketReceived(header, two_ms);
+ header.packet_number = QuicPacketNumber(4u);
+ received_manager_.RecordPacketReceived(header, two_ms);
+ header.packet_number = QuicPacketNumber(1000u);
+ received_manager_.RecordPacketReceived(header, two_ms);
+ EXPECT_TRUE(received_manager_.ack_frame_updated());
+ ack = received_manager_.GetUpdatedAckFrame(two_ms);
+ if (received_manager_.decide_when_to_send_acks()) {
+ received_manager_.ResetAckStates();
+ }
+ EXPECT_FALSE(received_manager_.ack_frame_updated());
+ // UpdateReceivedPacketInfo should discard any times which can't be
+ // expressed on the wire.
+ EXPECT_EQ(2u, ack.ack_frame->received_packet_times.size());
+}
+
+TEST_P(QuicReceivedPacketManagerTest, UpdateReceivedConnectionStats) {
+ EXPECT_FALSE(received_manager_.ack_frame_updated());
+ RecordPacketReceipt(1);
+ EXPECT_TRUE(received_manager_.ack_frame_updated());
+ RecordPacketReceipt(6);
+ RecordPacketReceipt(2,
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1));
+
+ EXPECT_EQ(4u, stats_.max_sequence_reordering);
+ EXPECT_EQ(1000, stats_.max_time_reordering_us);
+ EXPECT_EQ(1u, stats_.packets_reordered);
+}
+
+TEST_P(QuicReceivedPacketManagerTest, LimitAckRanges) {
+ received_manager_.set_max_ack_ranges(10);
+ EXPECT_FALSE(received_manager_.ack_frame_updated());
+ for (int i = 0; i < 100; ++i) {
+ RecordPacketReceipt(1 + 2 * i);
+ EXPECT_TRUE(received_manager_.ack_frame_updated());
+ received_manager_.GetUpdatedAckFrame(QuicTime::Zero());
+ EXPECT_GE(10u, received_manager_.ack_frame().packets.NumIntervals());
+ EXPECT_EQ(QuicPacketNumber(1u + 2 * i),
+ received_manager_.ack_frame().packets.Max());
+ for (int j = 0; j < std::min(10, i + 1); ++j) {
+ ASSERT_GE(i, j);
+ EXPECT_TRUE(received_manager_.ack_frame().packets.Contains(
+ QuicPacketNumber(1 + (i - j) * 2)));
+ if (i > j) {
+ EXPECT_FALSE(received_manager_.ack_frame().packets.Contains(
+ QuicPacketNumber((i - j) * 2)));
+ }
+ }
+ }
+}
+
+TEST_P(QuicReceivedPacketManagerTest, IgnoreOutOfOrderTimestamps) {
+ EXPECT_FALSE(received_manager_.ack_frame_updated());
+ RecordPacketReceipt(1, QuicTime::Zero());
+ EXPECT_TRUE(received_manager_.ack_frame_updated());
+ EXPECT_EQ(1u, received_manager_.ack_frame().received_packet_times.size());
+ RecordPacketReceipt(2,
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1));
+ EXPECT_EQ(2u, received_manager_.ack_frame().received_packet_times.size());
+ RecordPacketReceipt(3, QuicTime::Zero());
+ EXPECT_EQ(2u, received_manager_.ack_frame().received_packet_times.size());
+}
+
+TEST_P(QuicReceivedPacketManagerTest, HasMissingPackets) {
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_QUIC_BUG(received_manager_.PeerFirstSendingPacketNumber(),
+ "No packets have been received yet");
+ } else {
+ EXPECT_EQ(QuicPacketNumber(1),
+ received_manager_.PeerFirstSendingPacketNumber());
+ }
+ RecordPacketReceipt(4, QuicTime::Zero());
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_EQ(QuicPacketNumber(4),
+ received_manager_.PeerFirstSendingPacketNumber());
+ EXPECT_FALSE(received_manager_.HasMissingPackets());
+ } else {
+ EXPECT_TRUE(received_manager_.HasMissingPackets());
+ EXPECT_EQ(QuicPacketNumber(1),
+ received_manager_.PeerFirstSendingPacketNumber());
+ }
+ RecordPacketReceipt(3, QuicTime::Zero());
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ EXPECT_FALSE(received_manager_.HasMissingPackets());
+ EXPECT_EQ(QuicPacketNumber(3),
+ received_manager_.PeerFirstSendingPacketNumber());
+ } else {
+ EXPECT_TRUE(received_manager_.HasMissingPackets());
+ EXPECT_EQ(QuicPacketNumber(1),
+ received_manager_.PeerFirstSendingPacketNumber());
+ }
+ RecordPacketReceipt(1, QuicTime::Zero());
+ EXPECT_EQ(QuicPacketNumber(1),
+ received_manager_.PeerFirstSendingPacketNumber());
+ EXPECT_TRUE(received_manager_.HasMissingPackets());
+ RecordPacketReceipt(2, QuicTime::Zero());
+ EXPECT_EQ(QuicPacketNumber(1),
+ received_manager_.PeerFirstSendingPacketNumber());
+ EXPECT_FALSE(received_manager_.HasMissingPackets());
+}
+
+TEST_P(QuicReceivedPacketManagerTest, OutOfOrderReceiptCausesAckSent) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(3, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 3);
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ // Delayed ack is scheduled.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ } else {
+ // Should ack immediately since we have missing packets.
+ CheckAckTimeout(clock_.ApproximateNow());
+ }
+
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 2);
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 1);
+ // Should ack immediately, since this fills the last hole.
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ RecordPacketReceipt(4, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 4);
+ // Delayed ack is scheduled.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+}
+
+TEST_P(QuicReceivedPacketManagerTest, OutOfOrderAckReceiptCausesNoAck) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 2);
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 1);
+ EXPECT_FALSE(HasPendingAck());
+}
+
+TEST_P(QuicReceivedPacketManagerTest, AckReceiptCausesAckSend) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 1);
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 2);
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(3, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 3);
+ // Delayed ack is scheduled.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ clock_.AdvanceTime(kDelayedAckTime);
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ RecordPacketReceipt(4, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 4);
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(5, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 5);
+ EXPECT_FALSE(HasPendingAck());
+}
+
+TEST_P(QuicReceivedPacketManagerTest, AckSentEveryNthPacket) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ received_manager_.set_ack_frequency_before_ack_decimation(3);
+
+ // Receives packets 1 - 39.
+ for (size_t i = 1; i <= 39; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 3 == 0) {
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+}
+
+TEST_P(QuicReceivedPacketManagerTest, AckDecimationReducesAcks) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_,
+ ACK_DECIMATION_WITH_REORDERING);
+
+ // Start ack decimation from 10th packet.
+ received_manager_.set_min_received_before_ack_decimation(10);
+
+ // Receives packets 1 - 29.
+ for (size_t i = 1; i <= 29; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i <= 10) {
+ // For packets 1-10, ack every 2 packets.
+ if (i % 2 == 0) {
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ continue;
+ }
+ // ack at 20.
+ if (i == 20) {
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kMinRttMs * 0.25);
+ }
+ }
+
+ // We now receive the 30th packet, and so we send an ack.
+ RecordPacketReceipt(30, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 30);
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_P(QuicReceivedPacketManagerTest, SendDelayedAfterQuiescence) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetFastAckAfterQuiescence(&received_manager_,
+ true);
+ // The beginning of the connection counts as quiescence.
+ QuicTime ack_time =
+ clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 1);
+ CheckAckTimeout(ack_time);
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Process another packet immediately after sending the ack and expect the
+ // ack timeout to be set delayed ack time in the future.
+ ack_time = clock_.ApproximateNow() + kDelayedAckTime;
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 2);
+ CheckAckTimeout(ack_time);
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(kDelayedAckTime);
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Wait 1 second and enesure the ack timeout is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ RecordPacketReceipt(3, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 3);
+ CheckAckTimeout(ack_time);
+}
+
+TEST_P(QuicReceivedPacketManagerTest, SendDelayedAckDecimation) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, ACK_DECIMATION);
+ // The ack time should be based on min_rtt * 1/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (uint64_t i = 1; i < 10; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_P(QuicReceivedPacketManagerTest,
+ SendDelayedAckAckDecimationAfterQuiescence) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, ACK_DECIMATION);
+ QuicReceivedPacketManagerPeer::SetFastAckAfterQuiescence(&received_manager_,
+ true);
+ // The beginning of the connection counts as quiescence.
+ QuicTime ack_time =
+ clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 1);
+ CheckAckTimeout(ack_time);
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Process another packet immedately after sending the ack and expect the
+ // ack timeout to be set delayed ack time in the future.
+ ack_time = clock_.ApproximateNow() + kDelayedAckTime;
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 2);
+ CheckAckTimeout(ack_time);
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(kDelayedAckTime);
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Wait 1 second and enesure the ack timeout is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ RecordPacketReceipt(3, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 3);
+ CheckAckTimeout(ack_time);
+ // Process enough packets to get into ack decimation behavior.
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 4; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+ EXPECT_FALSE(HasPendingAck());
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (uint64_t i = 1; i < 10; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Wait 1 second and enesure the ack timeout is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10);
+ CheckAckTimeout(ack_time);
+}
+
+TEST_P(QuicReceivedPacketManagerTest,
+ SendDelayedAckDecimationUnlimitedAggregation) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicConfig config;
+ QuicTagVector connection_options;
+ connection_options.push_back(kACKD);
+ // No limit on the number of packets received before sending an ack.
+ connection_options.push_back(kAKDU);
+ config.SetConnectionOptionsToSend(connection_options);
+ received_manager_.SetFromConfig(config, Perspective::IS_CLIENT);
+
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // 18 packets will not cause an ack to be sent. 19 will because when
+ // stop waiting frames are in use, we ack every 20 packets no matter what.
+ for (int i = 1; i <= 18; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(ack_time);
+}
+
+TEST_P(QuicReceivedPacketManagerTest, SendDelayedAckDecimationEighthRtt) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_, ACK_DECIMATION);
+ QuicReceivedPacketManagerPeer::SetAckDecimationDelay(&received_manager_,
+ 0.125);
+
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125;
+
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (uint64_t i = 1; i < 10; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_P(QuicReceivedPacketManagerTest, SendDelayedAckDecimationWithReordering) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_,
+ ACK_DECIMATION_WITH_REORDERING);
+
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ // Receive one packet out of order and then the rest in order.
+ // The loop leaves a one packet gap between acks sent to simulate some loss.
+ for (int j = 0; j < 3; ++j) {
+ // Process packet 10 first and ensure the timeout is one eighth min_rtt.
+ RecordPacketReceipt(kFirstDecimatedPacket + 9 + (j * 11),
+ clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 9 + (j * 11));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 0; i < 9; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i + (j * 11),
+ clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck,
+ kFirstDecimatedPacket + i + (j * 11));
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+ }
+}
+
+TEST_P(QuicReceivedPacketManagerTest,
+ SendDelayedAckDecimationWithLargeReordering) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_,
+ ACK_DECIMATION_WITH_REORDERING);
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ RecordPacketReceipt(kFirstDecimatedPacket + 19, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 19);
+ ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125;
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 1; i < 9; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // The next packet received in order will cause an immediate ack, because it
+ // fills a hole.
+ RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10);
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_P(QuicReceivedPacketManagerTest,
+ SendDelayedAckDecimationWithReorderingEighthRtt) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_,
+ ACK_DECIMATION_WITH_REORDERING);
+ QuicReceivedPacketManagerPeer::SetAckDecimationDelay(&received_manager_,
+ 0.125);
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125;
+
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // Process packet 10 first and ensure the timeout is one eighth min_rtt.
+ RecordPacketReceipt(kFirstDecimatedPacket + 9, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 9);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 1; i < 9; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck + i, kFirstDecimatedPacket);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_P(QuicReceivedPacketManagerTest,
+ SendDelayedAckDecimationWithLargeReorderingEighthRtt) {
+ if (!received_manager_.decide_when_to_send_acks()) {
+ return;
+ }
+ EXPECT_FALSE(HasPendingAck());
+ QuicReceivedPacketManagerPeer::SetAckMode(&received_manager_,
+ ACK_DECIMATION_WITH_REORDERING);
+ QuicReceivedPacketManagerPeer::SetAckDecimationDelay(&received_manager_,
+ 0.125);
+
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125;
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ RecordPacketReceipt(kFirstDecimatedPacket + 19, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 19);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 1; i < 9; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // The next packet received in order will cause an immediate ack, because it
+ // fills a hole.
+ RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10);
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..456e81c18dd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.cc
@@ -0,0 +1,1266 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+
+#include <algorithm>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/general_loss_algorithm.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+
+namespace quic {
+
+namespace {
+static const int64_t kDefaultRetransmissionTimeMs = 500;
+static const int64_t kMaxRetransmissionTimeMs = 60000;
+// Maximum number of exponential backoffs used for RTO timeouts.
+static const size_t kMaxRetransmissions = 10;
+// Maximum number of packets retransmitted upon an RTO.
+static const size_t kMaxRetransmissionsOnTimeout = 2;
+// The path degrading delay is the sum of this number of consecutive RTO delays.
+const size_t kNumRetransmissionDelaysForPathDegradingDelay = 2;
+
+// Ensure the handshake timer isnt't faster than 10ms.
+// This limits the tenth retransmitted packet to 10s after the initial CHLO.
+static const int64_t kMinHandshakeTimeoutMs = 10;
+
+// Sends up to two tail loss probes before firing an RTO,
+// per draft RFC draft-dukkipati-tcpm-tcp-loss-probe.
+static const size_t kDefaultMaxTailLossProbes = 2;
+
+inline bool HasCryptoHandshake(const QuicTransmissionInfo& transmission_info) {
+ DCHECK(!transmission_info.has_crypto_handshake ||
+ !transmission_info.retransmittable_frames.empty());
+ return transmission_info.has_crypto_handshake;
+}
+
+// Returns true if retransmissions the specified type leave the data in flight.
+inline bool RetransmissionLeavesBytesInFlight(
+ TransmissionType transmission_type) {
+ // Both TLP and the new RTO leave the packets in flight and let the loss
+ // detection decide if packets are lost.
+ return transmission_type == TLP_RETRANSMISSION ||
+ transmission_type == PROBING_RETRANSMISSION ||
+ transmission_type == RTO_RETRANSMISSION;
+}
+
+// Returns true of retransmissions of the specified type should retransmit
+// the frames directly (as opposed to resulting in a loss notification).
+inline bool ShouldForceRetransmission(TransmissionType transmission_type) {
+ return transmission_type == HANDSHAKE_RETRANSMISSION ||
+ transmission_type == TLP_RETRANSMISSION ||
+ transmission_type == PROBING_RETRANSMISSION ||
+ transmission_type == RTO_RETRANSMISSION;
+}
+
+} // namespace
+
+#define ENDPOINT \
+ (unacked_packets_.perspective() == Perspective::IS_SERVER ? "Server: " \
+ : "Client: ")
+
+QuicSentPacketManager::QuicSentPacketManager(
+ Perspective perspective,
+ const QuicClock* clock,
+ QuicConnectionStats* stats,
+ CongestionControlType congestion_control_type,
+ LossDetectionType loss_type)
+ : unacked_packets_(perspective),
+ clock_(clock),
+ stats_(stats),
+ debug_delegate_(nullptr),
+ network_change_visitor_(nullptr),
+ initial_congestion_window_(kInitialCongestionWindow),
+ loss_algorithm_(GetInitialLossAlgorithm()),
+ general_loss_algorithm_(loss_type),
+ uber_loss_algorithm_(loss_type),
+ consecutive_rto_count_(0),
+ consecutive_tlp_count_(0),
+ consecutive_crypto_retransmission_count_(0),
+ pending_timer_transmission_count_(0),
+ max_tail_loss_probes_(kDefaultMaxTailLossProbes),
+ max_rto_packets_(kMaxRetransmissionsOnTimeout),
+ enable_half_rtt_tail_loss_probe_(false),
+ using_pacing_(false),
+ use_new_rto_(false),
+ conservative_handshake_retransmits_(false),
+ min_tlp_timeout_(
+ QuicTime::Delta::FromMilliseconds(kMinTailLossProbeTimeoutMs)),
+ min_rto_timeout_(
+ QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs)),
+ ietf_style_tlp_(false),
+ ietf_style_2x_tlp_(false),
+ largest_mtu_acked_(0),
+ handshake_confirmed_(false),
+ delayed_ack_time_(
+ QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs)),
+ rtt_updated_(false),
+ acked_packets_iter_(last_ack_frame_.packets.rbegin()),
+ tolerate_reneging_(GetQuicReloadableFlag(quic_tolerate_reneging)) {
+ if (tolerate_reneging_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_tolerate_reneging);
+ }
+ SetSendAlgorithm(congestion_control_type);
+}
+
+LossDetectionInterface* QuicSentPacketManager::GetInitialLossAlgorithm() {
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ return &uber_loss_algorithm_;
+ }
+ return &general_loss_algorithm_;
+}
+
+QuicSentPacketManager::~QuicSentPacketManager() {}
+
+void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) {
+ const Perspective perspective = unacked_packets_.perspective();
+ if (config.HasReceivedInitialRoundTripTimeUs() &&
+ config.ReceivedInitialRoundTripTimeUs() > 0) {
+ if (!config.HasClientSentConnectionOption(kNRTT, perspective)) {
+ SetInitialRtt(QuicTime::Delta::FromMicroseconds(
+ config.ReceivedInitialRoundTripTimeUs()));
+ }
+ } else if (config.HasInitialRoundTripTimeUsToSend() &&
+ config.GetInitialRoundTripTimeUsToSend() > 0) {
+ SetInitialRtt(QuicTime::Delta::FromMicroseconds(
+ config.GetInitialRoundTripTimeUsToSend()));
+ }
+ if (config.HasClientSentConnectionOption(kMAD0, perspective)) {
+ rtt_stats_.set_ignore_max_ack_delay(true);
+ }
+ if (config.HasClientSentConnectionOption(kMAD1, perspective)) {
+ rtt_stats_.set_initial_max_ack_delay(delayed_ack_time_);
+ }
+ if (config.HasClientSentConnectionOption(kMAD2, perspective)) {
+ min_tlp_timeout_ = QuicTime::Delta::Zero();
+ }
+ if (config.HasClientSentConnectionOption(kMAD3, perspective)) {
+ min_rto_timeout_ = QuicTime::Delta::Zero();
+ }
+ if (config.HasClientSentConnectionOption(kMAD4, perspective)) {
+ ietf_style_tlp_ = true;
+ }
+ if (config.HasClientSentConnectionOption(kMAD5, perspective)) {
+ ietf_style_2x_tlp_ = true;
+ }
+
+ // Configure congestion control.
+ if (config.HasClientRequestedIndependentOption(kTBBR, perspective)) {
+ SetSendAlgorithm(kBBR);
+ }
+ if (config.HasClientRequestedIndependentOption(kRENO, perspective)) {
+ SetSendAlgorithm(kRenoBytes);
+ } else if (config.HasClientRequestedIndependentOption(kBYTE, perspective) ||
+ (GetQuicReloadableFlag(quic_default_to_bbr) &&
+ config.HasClientRequestedIndependentOption(kQBIC, perspective))) {
+ SetSendAlgorithm(kCubicBytes);
+ } else if (GetQuicReloadableFlag(quic_enable_pcc3) &&
+ config.HasClientRequestedIndependentOption(kTPCC, perspective)) {
+ SetSendAlgorithm(kPCC);
+ }
+ // Initial window.
+ if (GetQuicReloadableFlag(quic_unified_iw_options)) {
+ if (config.HasClientRequestedIndependentOption(kIW03, perspective)) {
+ initial_congestion_window_ = 3;
+ send_algorithm_->SetInitialCongestionWindowInPackets(3);
+ }
+ if (config.HasClientRequestedIndependentOption(kIW10, perspective)) {
+ initial_congestion_window_ = 10;
+ send_algorithm_->SetInitialCongestionWindowInPackets(10);
+ }
+ if (config.HasClientRequestedIndependentOption(kIW20, perspective)) {
+ initial_congestion_window_ = 20;
+ send_algorithm_->SetInitialCongestionWindowInPackets(20);
+ }
+ if (config.HasClientRequestedIndependentOption(kIW50, perspective)) {
+ initial_congestion_window_ = 50;
+ send_algorithm_->SetInitialCongestionWindowInPackets(50);
+ }
+ }
+
+ using_pacing_ = !FLAGS_quic_disable_pacing_for_perf_tests;
+
+ if (config.HasClientSentConnectionOption(k1CON, perspective)) {
+ send_algorithm_->SetNumEmulatedConnections(1);
+ }
+ if (config.HasClientSentConnectionOption(kNTLP, perspective)) {
+ max_tail_loss_probes_ = 0;
+ }
+ if (config.HasClientSentConnectionOption(k1TLP, perspective)) {
+ max_tail_loss_probes_ = 1;
+ }
+ if (config.HasClientSentConnectionOption(k1RTO, perspective)) {
+ max_rto_packets_ = 1;
+ }
+ if (config.HasClientSentConnectionOption(kTLPR, perspective)) {
+ enable_half_rtt_tail_loss_probe_ = true;
+ }
+ if (config.HasClientSentConnectionOption(kNRTO, perspective)) {
+ use_new_rto_ = true;
+ }
+ // Configure loss detection.
+ if (config.HasClientRequestedIndependentOption(kTIME, perspective)) {
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ uber_loss_algorithm_.SetLossDetectionType(kTime);
+ } else {
+ general_loss_algorithm_.SetLossDetectionType(kTime);
+ }
+ }
+ if (config.HasClientRequestedIndependentOption(kATIM, perspective)) {
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ uber_loss_algorithm_.SetLossDetectionType(kAdaptiveTime);
+ } else {
+ general_loss_algorithm_.SetLossDetectionType(kAdaptiveTime);
+ }
+ }
+ if (config.HasClientRequestedIndependentOption(kLFAK, perspective)) {
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ uber_loss_algorithm_.SetLossDetectionType(kLazyFack);
+ } else {
+ general_loss_algorithm_.SetLossDetectionType(kLazyFack);
+ }
+ }
+ if (config.HasClientSentConnectionOption(kCONH, perspective)) {
+ conservative_handshake_retransmits_ = true;
+ }
+ send_algorithm_->SetFromConfig(config, perspective);
+
+ if (network_change_visitor_ != nullptr) {
+ network_change_visitor_->OnCongestionChange();
+ }
+}
+
+void QuicSentPacketManager::ResumeConnectionState(
+ const CachedNetworkParameters& cached_network_params,
+ bool max_bandwidth_resumption) {
+ QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(
+ max_bandwidth_resumption
+ ? cached_network_params.max_bandwidth_estimate_bytes_per_second()
+ : cached_network_params.bandwidth_estimate_bytes_per_second());
+ QuicTime::Delta rtt =
+ QuicTime::Delta::FromMilliseconds(cached_network_params.min_rtt_ms());
+ AdjustNetworkParameters(bandwidth, rtt);
+}
+
+void QuicSentPacketManager::AdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) {
+ if (!rtt.IsZero()) {
+ SetInitialRtt(rtt);
+ }
+ send_algorithm_->AdjustNetworkParameters(bandwidth, rtt);
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnAdjustNetworkParameters(bandwidth, rtt);
+ }
+}
+
+void QuicSentPacketManager::SetHandshakeConfirmed() {
+ handshake_confirmed_ = true;
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ NeuterHandshakePackets();
+ }
+}
+
+void QuicSentPacketManager::PostProcessNewlyAckedPackets(
+ const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time,
+ bool rtt_updated,
+ QuicByteCount prior_bytes_in_flight) {
+ if (session_decides_what_to_write()) {
+ unacked_packets_.NotifyAggregatedStreamFrameAcked(
+ last_ack_frame_.ack_delay_time);
+ }
+ InvokeLossDetection(ack_receive_time);
+ // Ignore losses in RTO mode.
+ if (consecutive_rto_count_ > 0 && !use_new_rto_) {
+ packets_lost_.clear();
+ }
+ MaybeInvokeCongestionEvent(rtt_updated, prior_bytes_in_flight,
+ ack_receive_time);
+ unacked_packets_.RemoveObsoletePackets();
+
+ sustained_bandwidth_recorder_.RecordEstimate(
+ send_algorithm_->InRecovery(), send_algorithm_->InSlowStart(),
+ send_algorithm_->BandwidthEstimate(), ack_receive_time, clock_->WallNow(),
+ rtt_stats_.smoothed_rtt());
+
+ // Anytime we are making forward progress and have a new RTT estimate, reset
+ // the backoff counters.
+ if (rtt_updated) {
+ if (consecutive_rto_count_ > 0) {
+ // If the ack acknowledges data sent prior to the RTO,
+ // the RTO was spurious.
+ if (LargestAcked(ack_frame) < first_rto_transmission_) {
+ // Replace SRTT with latest_rtt and increase the variance to prevent
+ // a spurious RTO from happening again.
+ rtt_stats_.ExpireSmoothedMetrics();
+ } else {
+ if (!use_new_rto_) {
+ send_algorithm_->OnRetransmissionTimeout(true);
+ }
+ }
+ }
+ // Reset all retransmit counters any time a new packet is acked.
+ consecutive_rto_count_ = 0;
+ consecutive_tlp_count_ = 0;
+ consecutive_crypto_retransmission_count_ = 0;
+ }
+
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnIncomingAck(ack_frame, ack_receive_time,
+ LargestAcked(ack_frame), rtt_updated,
+ GetLeastUnacked());
+ }
+ // Remove packets below least unacked from all_packets_acked_ and
+ // last_ack_frame_.
+ last_ack_frame_.packets.RemoveUpTo(unacked_packets_.GetLeastUnacked());
+ last_ack_frame_.received_packet_times.clear();
+}
+
+void QuicSentPacketManager::MaybeInvokeCongestionEvent(
+ bool rtt_updated,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time) {
+ if (!rtt_updated && packets_acked_.empty() && packets_lost_.empty()) {
+ return;
+ }
+ if (using_pacing_) {
+ pacing_sender_.OnCongestionEvent(rtt_updated, prior_in_flight, event_time,
+ packets_acked_, packets_lost_);
+ } else {
+ send_algorithm_->OnCongestionEvent(rtt_updated, prior_in_flight, event_time,
+ packets_acked_, packets_lost_);
+ }
+ packets_acked_.clear();
+ packets_lost_.clear();
+ if (network_change_visitor_ != nullptr) {
+ network_change_visitor_->OnCongestionChange();
+ }
+}
+
+void QuicSentPacketManager::RetransmitUnackedPackets(
+ TransmissionType retransmission_type) {
+ DCHECK(retransmission_type == ALL_UNACKED_RETRANSMISSION ||
+ retransmission_type == ALL_INITIAL_RETRANSMISSION);
+ QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ if ((retransmission_type == ALL_UNACKED_RETRANSMISSION ||
+ it->encryption_level == ENCRYPTION_ZERO_RTT) &&
+ unacked_packets_.HasRetransmittableFrames(*it)) {
+ MarkForRetransmission(packet_number, retransmission_type);
+ }
+ }
+}
+
+void QuicSentPacketManager::NeuterUnencryptedPackets() {
+ QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+ if (session_decides_what_to_write()) {
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ if (!it->retransmittable_frames.empty() &&
+ it->encryption_level == ENCRYPTION_INITIAL) {
+ // Once the connection swithes to forward secure, no unencrypted packets
+ // will be sent. The data has been abandoned in the cryto stream. Remove
+ // it from in flight.
+ unacked_packets_.RemoveFromInFlight(packet_number);
+ }
+ }
+ return;
+ }
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ if (it->encryption_level == ENCRYPTION_INITIAL &&
+ unacked_packets_.HasRetransmittableFrames(*it)) {
+ // Once you're forward secure, no unencrypted packets will be sent, crypto
+ // or otherwise. Unencrypted packets are neutered and abandoned, to ensure
+ // they are not retransmitted or considered lost from a congestion control
+ // perspective.
+ pending_retransmissions_.erase(packet_number);
+ unacked_packets_.RemoveFromInFlight(packet_number);
+ unacked_packets_.RemoveRetransmittability(packet_number);
+ }
+ }
+}
+
+void QuicSentPacketManager::NeuterHandshakePackets() {
+ DCHECK(unacked_packets_.use_uber_loss_algorithm());
+ QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ if (session_decides_what_to_write()) {
+ if (!it->retransmittable_frames.empty() &&
+ unacked_packets_.GetPacketNumberSpace(it->encryption_level) ==
+ HANDSHAKE_DATA) {
+ unacked_packets_.RemoveFromInFlight(packet_number);
+ }
+ continue;
+ }
+ if (unacked_packets_.GetPacketNumberSpace(it->encryption_level) ==
+ HANDSHAKE_DATA &&
+ unacked_packets_.HasRetransmittableFrames(*it)) {
+ pending_retransmissions_.erase(packet_number);
+ unacked_packets_.RemoveFromInFlight(packet_number);
+ unacked_packets_.RemoveRetransmittability(packet_number);
+ }
+ }
+}
+
+void QuicSentPacketManager::MarkForRetransmission(
+ QuicPacketNumber packet_number,
+ TransmissionType transmission_type) {
+ QuicTransmissionInfo* transmission_info =
+ unacked_packets_.GetMutableTransmissionInfo(packet_number);
+ // When session decides what to write, a previous RTO retransmission may cause
+ // connection close; packets without retransmittable frames can be marked for
+ // loss retransmissions.
+ QUIC_BUG_IF((transmission_type != LOSS_RETRANSMISSION &&
+ (!session_decides_what_to_write() ||
+ transmission_type != RTO_RETRANSMISSION)) &&
+ !unacked_packets_.HasRetransmittableFrames(*transmission_info))
+ << "transmission_type: "
+ << QuicUtils::TransmissionTypeToString(transmission_type);
+ // Handshake packets should never be sent as probing retransmissions.
+ DCHECK(!transmission_info->has_crypto_handshake ||
+ transmission_type != PROBING_RETRANSMISSION);
+ if (!RetransmissionLeavesBytesInFlight(transmission_type)) {
+ unacked_packets_.RemoveFromInFlight(transmission_info);
+ }
+
+ if (!session_decides_what_to_write()) {
+ if (!unacked_packets_.HasRetransmittableFrames(*transmission_info)) {
+ return;
+ }
+ if (!QuicContainsKey(pending_retransmissions_, packet_number)) {
+ pending_retransmissions_[packet_number] = transmission_type;
+ }
+ return;
+ }
+
+ HandleRetransmission(transmission_type, transmission_info);
+
+ // Update packet state according to transmission type.
+ transmission_info->state =
+ QuicUtils::RetransmissionTypeToPacketState(transmission_type);
+}
+
+void QuicSentPacketManager::HandleRetransmission(
+ TransmissionType transmission_type,
+ QuicTransmissionInfo* transmission_info) {
+ DCHECK(session_decides_what_to_write());
+ if (ShouldForceRetransmission(transmission_type)) {
+ // TODO(fayang): Consider to make RTO and PROBING retransmission
+ // strategies be configurable by applications. Today, TLP, RTO and PROBING
+ // retransmissions are handled similarly, i.e., always retranmist the
+ // oldest outstanding data. This is not ideal in general because different
+ // applications may want different strategies. For example, some
+ // applications may want to use higher priority stream data for bandwidth
+ // probing, and some applications want to consider RTO is an indication of
+ // loss, etc.
+ unacked_packets_.RetransmitFrames(*transmission_info, transmission_type);
+ return;
+ }
+
+ unacked_packets_.NotifyFramesLost(*transmission_info, transmission_type);
+ if (transmission_info->retransmittable_frames.empty()) {
+ return;
+ }
+
+ if (transmission_type == LOSS_RETRANSMISSION) {
+ // Record the first packet sent after loss, which allows to wait 1
+ // more RTT before giving up on this lost packet.
+ transmission_info->retransmission =
+ unacked_packets_.largest_sent_packet() + 1;
+ } else {
+ // Clear the recorded first packet sent after loss when version or
+ // encryption changes.
+ transmission_info->retransmission.Clear();
+ }
+}
+
+void QuicSentPacketManager::RecordOneSpuriousRetransmission(
+ const QuicTransmissionInfo& info) {
+ stats_->bytes_spuriously_retransmitted += info.bytes_sent;
+ ++stats_->packets_spuriously_retransmitted;
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnSpuriousPacketRetransmission(info.transmission_type,
+ info.bytes_sent);
+ }
+}
+
+void QuicSentPacketManager::RecordSpuriousRetransmissions(
+ const QuicTransmissionInfo& info,
+ QuicPacketNumber acked_packet_number) {
+ if (session_decides_what_to_write()) {
+ RecordOneSpuriousRetransmission(info);
+ if (info.transmission_type == LOSS_RETRANSMISSION) {
+ // Only inform the loss detection of spurious retransmits it caused.
+ loss_algorithm_->SpuriousRetransmitDetected(
+ unacked_packets_, clock_->Now(), rtt_stats_, acked_packet_number);
+ }
+ return;
+ }
+ QuicPacketNumber retransmission = info.retransmission;
+ while (retransmission.IsInitialized()) {
+ const QuicTransmissionInfo& retransmit_info =
+ unacked_packets_.GetTransmissionInfo(retransmission);
+ retransmission = retransmit_info.retransmission;
+ RecordOneSpuriousRetransmission(retransmit_info);
+ }
+ // Only inform the loss detection of spurious retransmits it caused.
+ if (unacked_packets_.GetTransmissionInfo(info.retransmission)
+ .transmission_type == LOSS_RETRANSMISSION) {
+ loss_algorithm_->SpuriousRetransmitDetected(
+ unacked_packets_, clock_->Now(), rtt_stats_, info.retransmission);
+ }
+}
+
+QuicPendingRetransmission QuicSentPacketManager::NextPendingRetransmission() {
+ QUIC_BUG_IF(pending_retransmissions_.empty())
+ << "Unexpected call to NextPendingRetransmission() with empty pending "
+ << "retransmission list. Corrupted memory usage imminent.";
+ QUIC_BUG_IF(session_decides_what_to_write())
+ << "Unexpected call to NextPendingRetransmission() when session handles "
+ "retransmissions";
+ QuicPacketNumber packet_number = pending_retransmissions_.begin()->first;
+ TransmissionType transmission_type = pending_retransmissions_.begin()->second;
+ if (unacked_packets_.HasPendingCryptoPackets()) {
+ // Ensure crypto packets are retransmitted before other packets.
+ for (const auto& pair : pending_retransmissions_) {
+ if (HasCryptoHandshake(
+ unacked_packets_.GetTransmissionInfo(pair.first))) {
+ packet_number = pair.first;
+ transmission_type = pair.second;
+ break;
+ }
+ }
+ }
+ DCHECK(unacked_packets_.IsUnacked(packet_number)) << packet_number;
+ const QuicTransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(packet_number);
+ DCHECK(unacked_packets_.HasRetransmittableFrames(transmission_info));
+
+ return QuicPendingRetransmission(packet_number, transmission_type,
+ transmission_info);
+}
+
+QuicPacketNumber QuicSentPacketManager::GetNewestRetransmission(
+ QuicPacketNumber packet_number,
+ const QuicTransmissionInfo& transmission_info) const {
+ if (session_decides_what_to_write()) {
+ return packet_number;
+ }
+ QuicPacketNumber retransmission = transmission_info.retransmission;
+ while (retransmission.IsInitialized()) {
+ packet_number = retransmission;
+ retransmission =
+ unacked_packets_.GetTransmissionInfo(retransmission).retransmission;
+ }
+ return packet_number;
+}
+
+void QuicSentPacketManager::MarkPacketHandled(QuicPacketNumber packet_number,
+ QuicTransmissionInfo* info,
+ QuicTime::Delta ack_delay_time) {
+ QuicPacketNumber newest_transmission =
+ GetNewestRetransmission(packet_number, *info);
+ // Remove the most recent packet, if it is pending retransmission.
+ pending_retransmissions_.erase(newest_transmission);
+
+ if (newest_transmission == packet_number) {
+ // Try to aggregate acked stream frames if acked packet is not a
+ // retransmission.
+ const bool fast_path = session_decides_what_to_write() &&
+ info->transmission_type == NOT_RETRANSMISSION;
+ if (fast_path) {
+ unacked_packets_.MaybeAggregateAckedStreamFrame(*info, ack_delay_time);
+ } else {
+ if (session_decides_what_to_write()) {
+ unacked_packets_.NotifyAggregatedStreamFrameAcked(ack_delay_time);
+ }
+ const bool new_data_acked =
+ unacked_packets_.NotifyFramesAcked(*info, ack_delay_time);
+ if (session_decides_what_to_write() && !new_data_acked &&
+ info->transmission_type != NOT_RETRANSMISSION) {
+ // Record as a spurious retransmission if this packet is a
+ // retransmission and no new data gets acked.
+ QUIC_DVLOG(1) << "Detect spurious retransmitted packet "
+ << packet_number << " transmission type: "
+ << QuicUtils::TransmissionTypeToString(
+ info->transmission_type);
+ RecordSpuriousRetransmissions(*info, packet_number);
+ }
+ }
+ } else {
+ DCHECK(!session_decides_what_to_write());
+ RecordSpuriousRetransmissions(*info, packet_number);
+ // Remove the most recent packet from flight if it's a crypto handshake
+ // packet, since they won't be acked now that one has been processed.
+ // Other crypto handshake packets won't be in flight, only the newest
+ // transmission of a crypto packet is in flight at once.
+ // TODO(ianswett): Instead of handling all crypto packets special,
+ // only handle nullptr encrypted packets in a special way.
+ const QuicTransmissionInfo& newest_transmission_info =
+ unacked_packets_.GetTransmissionInfo(newest_transmission);
+ unacked_packets_.NotifyFramesAcked(newest_transmission_info,
+ ack_delay_time);
+ if (HasCryptoHandshake(newest_transmission_info)) {
+ unacked_packets_.RemoveFromInFlight(newest_transmission);
+ }
+ }
+
+ if (network_change_visitor_ != nullptr &&
+ info->bytes_sent > largest_mtu_acked_) {
+ largest_mtu_acked_ = info->bytes_sent;
+ network_change_visitor_->OnPathMtuIncreased(largest_mtu_acked_);
+ }
+ unacked_packets_.RemoveFromInFlight(info);
+ unacked_packets_.RemoveRetransmittability(info);
+ info->state = ACKED;
+}
+
+bool QuicSentPacketManager::OnPacketSent(
+ SerializedPacket* serialized_packet,
+ QuicPacketNumber original_packet_number,
+ QuicTime sent_time,
+ TransmissionType transmission_type,
+ HasRetransmittableData has_retransmittable_data) {
+ QuicPacketNumber packet_number = serialized_packet->packet_number;
+ DCHECK_LE(FirstSendingPacketNumber(), packet_number);
+ DCHECK(!unacked_packets_.IsUnacked(packet_number));
+ QUIC_BUG_IF(serialized_packet->encrypted_length == 0)
+ << "Cannot send empty packets.";
+
+ if (original_packet_number.IsInitialized()) {
+ pending_retransmissions_.erase(original_packet_number);
+ }
+
+ if (pending_timer_transmission_count_ > 0) {
+ --pending_timer_transmission_count_;
+ }
+
+ bool in_flight = has_retransmittable_data == HAS_RETRANSMITTABLE_DATA;
+ if (using_pacing_) {
+ pacing_sender_.OnPacketSent(
+ sent_time, unacked_packets_.bytes_in_flight(), packet_number,
+ serialized_packet->encrypted_length, has_retransmittable_data);
+ } else {
+ send_algorithm_->OnPacketSent(
+ sent_time, unacked_packets_.bytes_in_flight(), packet_number,
+ serialized_packet->encrypted_length, has_retransmittable_data);
+ }
+
+ unacked_packets_.AddSentPacket(serialized_packet, original_packet_number,
+ transmission_type, sent_time, in_flight);
+ // Reset the retransmission timer anytime a pending packet is sent.
+ return in_flight;
+}
+
+void QuicSentPacketManager::OnRetransmissionTimeout() {
+ DCHECK(unacked_packets_.HasInFlightPackets());
+ DCHECK_EQ(0u, pending_timer_transmission_count_);
+ // Handshake retransmission, timer based loss detection, TLP, and RTO are
+ // implemented with a single alarm. The handshake alarm is set when the
+ // handshake has not completed, the loss alarm is set when the loss detection
+ // algorithm says to, and the TLP and RTO alarms are set after that.
+ // The TLP alarm is always set to run for under an RTO.
+ switch (GetRetransmissionMode()) {
+ case HANDSHAKE_MODE:
+ ++stats_->crypto_retransmit_count;
+ RetransmitCryptoPackets();
+ return;
+ case LOSS_MODE: {
+ ++stats_->loss_timeout_count;
+ QuicByteCount prior_in_flight = unacked_packets_.bytes_in_flight();
+ const QuicTime now = clock_->Now();
+ InvokeLossDetection(now);
+ MaybeInvokeCongestionEvent(false, prior_in_flight, now);
+ return;
+ }
+ case TLP_MODE:
+ ++stats_->tlp_count;
+ ++consecutive_tlp_count_;
+ pending_timer_transmission_count_ = 1;
+ // TLPs prefer sending new data instead of retransmitting data, so
+ // give the connection a chance to write before completing the TLP.
+ return;
+ case RTO_MODE:
+ ++stats_->rto_count;
+ RetransmitRtoPackets();
+ return;
+ }
+}
+
+void QuicSentPacketManager::RetransmitCryptoPackets() {
+ DCHECK_EQ(HANDSHAKE_MODE, GetRetransmissionMode());
+ ++consecutive_crypto_retransmission_count_;
+ bool packet_retransmitted = false;
+ QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+ std::vector<QuicPacketNumber> crypto_retransmissions;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ // Only retransmit frames which are in flight, and therefore have been sent.
+ if (!it->in_flight ||
+ (session_decides_what_to_write() && it->state != OUTSTANDING) ||
+ !it->has_crypto_handshake ||
+ !unacked_packets_.HasRetransmittableFrames(*it)) {
+ continue;
+ }
+ packet_retransmitted = true;
+ if (session_decides_what_to_write()) {
+ crypto_retransmissions.push_back(packet_number);
+ } else {
+ MarkForRetransmission(packet_number, HANDSHAKE_RETRANSMISSION);
+ }
+ ++pending_timer_transmission_count_;
+ }
+ DCHECK(packet_retransmitted) << "No crypto packets found to retransmit.";
+ if (session_decides_what_to_write()) {
+ for (QuicPacketNumber retransmission : crypto_retransmissions) {
+ MarkForRetransmission(retransmission, HANDSHAKE_RETRANSMISSION);
+ }
+ }
+}
+
+bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() {
+ if (pending_timer_transmission_count_ == 0) {
+ return false;
+ }
+ if (!MaybeRetransmitOldestPacket(TLP_RETRANSMISSION)) {
+ // If no tail loss probe can be sent, because there are no retransmittable
+ // packets, execute a conventional RTO to abandon old packets.
+ if (GetQuicReloadableFlag(quic_optimize_inflight_check)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_optimize_inflight_check);
+ pending_timer_transmission_count_ = 0;
+ RetransmitRtoPackets();
+ }
+ return false;
+ }
+ return true;
+}
+
+bool QuicSentPacketManager::MaybeRetransmitOldestPacket(TransmissionType type) {
+ QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ // Only retransmit frames which are in flight, and therefore have been sent.
+ if (!it->in_flight ||
+ (session_decides_what_to_write() && it->state != OUTSTANDING) ||
+ !unacked_packets_.HasRetransmittableFrames(*it)) {
+ continue;
+ }
+ MarkForRetransmission(packet_number, type);
+ return true;
+ }
+ QUIC_DVLOG(1)
+ << "No retransmittable packets, so RetransmitOldestPacket failed.";
+ return false;
+}
+
+void QuicSentPacketManager::RetransmitRtoPackets() {
+ QUIC_BUG_IF(pending_timer_transmission_count_ > 0)
+ << "Retransmissions already queued:" << pending_timer_transmission_count_;
+ // Mark two packets for retransmission.
+ QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+ std::vector<QuicPacketNumber> retransmissions;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ if ((!session_decides_what_to_write() || it->state == OUTSTANDING) &&
+ unacked_packets_.HasRetransmittableFrames(*it) &&
+ pending_timer_transmission_count_ < max_rto_packets_) {
+ if (session_decides_what_to_write()) {
+ retransmissions.push_back(packet_number);
+ } else {
+ MarkForRetransmission(packet_number, RTO_RETRANSMISSION);
+ }
+ ++pending_timer_transmission_count_;
+ }
+ // Abandon non-retransmittable data that's in flight to ensure it doesn't
+ // fill up the congestion window.
+ bool has_retransmissions = it->retransmission.IsInitialized();
+ if (session_decides_what_to_write()) {
+ has_retransmissions = it->state != OUTSTANDING;
+ }
+ if (it->in_flight && !has_retransmissions &&
+ !unacked_packets_.HasRetransmittableFrames(*it)) {
+ // Log only for non-retransmittable data.
+ // Retransmittable data is marked as lost during loss detection, and will
+ // be logged later.
+ unacked_packets_.RemoveFromInFlight(packet_number);
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnPacketLoss(packet_number, RTO_RETRANSMISSION,
+ clock_->Now());
+ }
+ }
+ }
+ if (pending_timer_transmission_count_ > 0) {
+ if (consecutive_rto_count_ == 0) {
+ first_rto_transmission_ = unacked_packets_.largest_sent_packet() + 1;
+ }
+ ++consecutive_rto_count_;
+ }
+ if (session_decides_what_to_write()) {
+ for (QuicPacketNumber retransmission : retransmissions) {
+ MarkForRetransmission(retransmission, RTO_RETRANSMISSION);
+ }
+ }
+}
+
+QuicSentPacketManager::RetransmissionTimeoutMode
+QuicSentPacketManager::GetRetransmissionMode() const {
+ DCHECK(unacked_packets_.HasInFlightPackets());
+ if (!handshake_confirmed_ && unacked_packets_.HasPendingCryptoPackets()) {
+ return HANDSHAKE_MODE;
+ }
+ if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) {
+ return LOSS_MODE;
+ }
+ if (consecutive_tlp_count_ < max_tail_loss_probes_) {
+ if (GetQuicReloadableFlag(quic_optimize_inflight_check) ||
+ unacked_packets_.HasUnackedRetransmittableFrames()) {
+ return TLP_MODE;
+ }
+ }
+ return RTO_MODE;
+}
+
+void QuicSentPacketManager::InvokeLossDetection(QuicTime time) {
+ if (!packets_acked_.empty()) {
+ DCHECK_LE(packets_acked_.front().packet_number,
+ packets_acked_.back().packet_number);
+ largest_newly_acked_ = packets_acked_.back().packet_number;
+ }
+ loss_algorithm_->DetectLosses(unacked_packets_, time, rtt_stats_,
+ largest_newly_acked_, packets_acked_,
+ &packets_lost_);
+ for (const LostPacket& packet : packets_lost_) {
+ ++stats_->packets_lost;
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnPacketLoss(packet.packet_number, LOSS_RETRANSMISSION,
+ time);
+ }
+
+ MarkForRetransmission(packet.packet_number, LOSS_RETRANSMISSION);
+ }
+}
+
+bool QuicSentPacketManager::MaybeUpdateRTT(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicTime ack_receive_time) {
+ // We rely on ack_delay_time to compute an RTT estimate, so we
+ // only update rtt when the largest observed gets acked.
+ if (!unacked_packets_.IsUnacked(largest_acked)) {
+ return false;
+ }
+ // We calculate the RTT based on the highest ACKed packet number, the lower
+ // packet numbers will include the ACK aggregation delay.
+ const QuicTransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(largest_acked);
+ // Ensure the packet has a valid sent time.
+ if (transmission_info.sent_time == QuicTime::Zero()) {
+ QUIC_BUG << "Acked packet has zero sent time, largest_acked:"
+ << largest_acked;
+ return false;
+ }
+ if (transmission_info.sent_time > ack_receive_time) {
+ QUIC_CODE_COUNT(quic_receive_acked_before_sending);
+ }
+
+ QuicTime::Delta send_delta = ack_receive_time - transmission_info.sent_time;
+ rtt_stats_.UpdateRtt(send_delta, ack_delay_time, ack_receive_time);
+
+ return true;
+}
+
+QuicTime::Delta QuicSentPacketManager::TimeUntilSend(QuicTime now) const {
+ // The TLP logic is entirely contained within QuicSentPacketManager, so the
+ // send algorithm does not need to be consulted.
+ if (pending_timer_transmission_count_ > 0) {
+ return QuicTime::Delta::Zero();
+ }
+
+ if (using_pacing_) {
+ return pacing_sender_.TimeUntilSend(now,
+ unacked_packets_.bytes_in_flight());
+ }
+
+ return send_algorithm_->CanSend(unacked_packets_.bytes_in_flight())
+ ? QuicTime::Delta::Zero()
+ : QuicTime::Delta::Infinite();
+}
+
+const QuicTime QuicSentPacketManager::GetRetransmissionTime() const {
+ // Don't set the timer if there is nothing to retransmit or we've already
+ // queued a tlp transmission and it hasn't been sent yet.
+ if (!unacked_packets_.HasInFlightPackets() ||
+ pending_timer_transmission_count_ > 0) {
+ return QuicTime::Zero();
+ }
+ if (!GetQuicReloadableFlag(quic_optimize_inflight_check) &&
+ !unacked_packets_.HasUnackedRetransmittableFrames()) {
+ return QuicTime::Zero();
+ }
+ switch (GetRetransmissionMode()) {
+ case HANDSHAKE_MODE:
+ return unacked_packets_.GetLastCryptoPacketSentTime() +
+ GetCryptoRetransmissionDelay();
+ case LOSS_MODE:
+ return loss_algorithm_->GetLossTimeout();
+ case TLP_MODE: {
+ // TODO(ianswett): When CWND is available, it would be preferable to
+ // set the timer based on the earliest retransmittable packet.
+ // Base the updated timer on the send time of the last packet.
+ const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime();
+ const QuicTime tlp_time = sent_time + GetTailLossProbeDelay();
+ // Ensure the TLP timer never gets set to a time in the past.
+ return std::max(clock_->ApproximateNow(), tlp_time);
+ }
+ case RTO_MODE: {
+ // The RTO is based on the first outstanding packet.
+ const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime();
+ QuicTime rto_time = sent_time + GetRetransmissionDelay();
+ // Wait for TLP packets to be acked before an RTO fires.
+ QuicTime tlp_time =
+ unacked_packets_.GetLastPacketSentTime() + GetTailLossProbeDelay();
+ return std::max(tlp_time, rto_time);
+ }
+ }
+ DCHECK(false);
+ return QuicTime::Zero();
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetPathDegradingDelay() const {
+ QuicTime::Delta delay = QuicTime::Delta::Zero();
+ for (size_t i = 0; i < max_tail_loss_probes_; ++i) {
+ delay = delay + GetTailLossProbeDelay(i);
+ }
+ for (size_t i = 0; i < kNumRetransmissionDelaysForPathDegradingDelay; ++i) {
+ delay = delay + GetRetransmissionDelay(i);
+ }
+ return delay;
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay()
+ const {
+ // This is equivalent to the TailLossProbeDelay, but slightly more aggressive
+ // because crypto handshake messages don't incur a delayed ack time.
+ QuicTime::Delta srtt = rtt_stats_.SmoothedOrInitialRtt();
+ int64_t delay_ms;
+ if (conservative_handshake_retransmits_) {
+ // Using the delayed ack time directly could cause conservative handshake
+ // retransmissions to actually be more aggressive than the default.
+ delay_ms = std::max(delayed_ack_time_.ToMilliseconds(),
+ static_cast<int64_t>(2 * srtt.ToMilliseconds()));
+ } else {
+ delay_ms = std::max(kMinHandshakeTimeoutMs,
+ static_cast<int64_t>(1.5 * srtt.ToMilliseconds()));
+ }
+ return QuicTime::Delta::FromMilliseconds(
+ delay_ms << consecutive_crypto_retransmission_count_);
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetTailLossProbeDelay(
+ size_t consecutive_tlp_count) const {
+ QuicTime::Delta srtt = rtt_stats_.SmoothedOrInitialRtt();
+ if (enable_half_rtt_tail_loss_probe_ && consecutive_tlp_count == 0u) {
+ return std::max(min_tlp_timeout_, srtt * 0.5);
+ }
+ if (ietf_style_tlp_) {
+ return std::max(min_tlp_timeout_, 1.5 * srtt + rtt_stats_.max_ack_delay());
+ }
+ if (ietf_style_2x_tlp_) {
+ return std::max(min_tlp_timeout_, 2 * srtt + rtt_stats_.max_ack_delay());
+ }
+ if (!unacked_packets_.HasMultipleInFlightPackets()) {
+ // This expression really should be using the delayed ack time, but in TCP
+ // MinRTO was traditionally set to 2x the delayed ack timer and this
+ // expression assumed QUIC did the same.
+ return std::max(2 * srtt, 1.5 * srtt + (min_rto_timeout_ * 0.5));
+ }
+ return std::max(min_tlp_timeout_, 2 * srtt);
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay(
+ size_t consecutive_rto_count) const {
+ QuicTime::Delta retransmission_delay = QuicTime::Delta::Zero();
+ if (rtt_stats_.smoothed_rtt().IsZero()) {
+ // We are in the initial state, use default timeout values.
+ retransmission_delay =
+ QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
+ } else {
+ retransmission_delay =
+ rtt_stats_.smoothed_rtt() + 4 * rtt_stats_.mean_deviation();
+ if (retransmission_delay < min_rto_timeout_) {
+ retransmission_delay = min_rto_timeout_;
+ }
+ }
+
+ // Calculate exponential back off.
+ retransmission_delay =
+ retransmission_delay *
+ (1 << std::min<size_t>(consecutive_rto_count, kMaxRetransmissions));
+
+ if (retransmission_delay.ToMilliseconds() > kMaxRetransmissionTimeMs) {
+ return QuicTime::Delta::FromMilliseconds(kMaxRetransmissionTimeMs);
+ }
+ return retransmission_delay;
+}
+
+QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const {
+ if (send_algorithm_->GetCongestionControlType() != kBBR) {
+ return QuicTime::Delta::Infinite();
+ }
+
+ if (!send_algorithm_->InSlowStart()) {
+ return stats_->slowstart_duration;
+ }
+
+ return clock_->ApproximateNow() - stats_->slowstart_start_time +
+ stats_->slowstart_duration;
+}
+
+std::string QuicSentPacketManager::GetDebugState() const {
+ return send_algorithm_->GetDebugState();
+}
+
+void QuicSentPacketManager::CancelRetransmissionsForStream(
+ QuicStreamId stream_id) {
+ if (session_decides_what_to_write()) {
+ return;
+ }
+ unacked_packets_.CancelRetransmissionsForStream(stream_id);
+ auto it = pending_retransmissions_.begin();
+ while (it != pending_retransmissions_.end()) {
+ if (unacked_packets_.HasRetransmittableFrames(it->first)) {
+ ++it;
+ continue;
+ }
+ it = pending_retransmissions_.erase(it);
+ }
+}
+
+void QuicSentPacketManager::SetSendAlgorithm(
+ CongestionControlType congestion_control_type) {
+ SetSendAlgorithm(SendAlgorithmInterface::Create(
+ clock_, &rtt_stats_, &unacked_packets_, congestion_control_type,
+ QuicRandom::GetInstance(), stats_, initial_congestion_window_));
+}
+
+void QuicSentPacketManager::SetSendAlgorithm(
+ SendAlgorithmInterface* send_algorithm) {
+ send_algorithm_.reset(send_algorithm);
+ pacing_sender_.set_sender(send_algorithm);
+}
+
+void QuicSentPacketManager::OnConnectionMigration(AddressChangeType type) {
+ if (type == PORT_CHANGE || type == IPV4_SUBNET_CHANGE) {
+ // Rtt and cwnd do not need to be reset when the peer address change is
+ // considered to be caused by NATs.
+ return;
+ }
+ consecutive_rto_count_ = 0;
+ consecutive_tlp_count_ = 0;
+ rtt_stats_.OnConnectionMigration();
+ send_algorithm_->OnConnectionMigration();
+}
+
+void QuicSentPacketManager::OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicTime ack_receive_time) {
+ DCHECK(packets_acked_.empty());
+ DCHECK_LE(largest_acked, unacked_packets_.largest_sent_packet());
+ rtt_updated_ =
+ MaybeUpdateRTT(largest_acked, ack_delay_time, ack_receive_time);
+ DCHECK(!unacked_packets_.largest_acked().IsInitialized() ||
+ largest_acked >= unacked_packets_.largest_acked() ||
+ tolerate_reneging_);
+ last_ack_frame_.ack_delay_time = ack_delay_time;
+ acked_packets_iter_ = last_ack_frame_.packets.rbegin();
+}
+
+void QuicSentPacketManager::OnAckRange(QuicPacketNumber start,
+ QuicPacketNumber end) {
+ if (!last_ack_frame_.largest_acked.IsInitialized() ||
+ end > last_ack_frame_.largest_acked + 1) {
+ // Largest acked increases.
+ unacked_packets_.IncreaseLargestAcked(end - 1);
+ last_ack_frame_.largest_acked = end - 1;
+ }
+ // Drop ack ranges which ack packets below least_unacked.
+ QuicPacketNumber least_unacked = unacked_packets_.GetLeastUnacked();
+ if (least_unacked.IsInitialized() && end <= least_unacked) {
+ return;
+ }
+ start = std::max(start, least_unacked);
+ do {
+ QuicPacketNumber newly_acked_start = start;
+ if (acked_packets_iter_ != last_ack_frame_.packets.rend()) {
+ newly_acked_start = std::max(start, acked_packets_iter_->max());
+ }
+ for (QuicPacketNumber acked = end - 1; acked >= newly_acked_start;
+ --acked) {
+ // Check if end is above the current range. If so add newly acked packets
+ // in descending order.
+ packets_acked_.push_back(AckedPacket(acked, 0, QuicTime::Zero()));
+ if (acked == FirstSendingPacketNumber()) {
+ break;
+ }
+ }
+ if (acked_packets_iter_ == last_ack_frame_.packets.rend() ||
+ start > acked_packets_iter_->min()) {
+ // Finish adding all newly acked packets.
+ return;
+ }
+ end = std::min(end, acked_packets_iter_->min());
+ ++acked_packets_iter_;
+ } while (start < end);
+}
+
+void QuicSentPacketManager::OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) {
+ last_ack_frame_.received_packet_times.push_back({packet_number, timestamp});
+ for (AckedPacket& packet : packets_acked_) {
+ if (packet.packet_number == packet_number) {
+ packet.receive_timestamp = timestamp;
+ return;
+ }
+ }
+}
+
+AckResult QuicSentPacketManager::OnAckFrameEnd(
+ QuicTime ack_receive_time,
+ EncryptionLevel ack_decrypted_level) {
+ QuicByteCount prior_bytes_in_flight = unacked_packets_.bytes_in_flight();
+ // Reverse packets_acked_ so that it is in ascending order.
+ reverse(packets_acked_.begin(), packets_acked_.end());
+ for (AckedPacket& acked_packet : packets_acked_) {
+ QuicTransmissionInfo* info =
+ unacked_packets_.GetMutableTransmissionInfo(acked_packet.packet_number);
+ if (!QuicUtils::IsAckable(info->state)) {
+ if (info->state == ACKED) {
+ QUIC_BUG << "Trying to ack an already acked packet: "
+ << acked_packet.packet_number
+ << ", last_ack_frame_: " << last_ack_frame_
+ << ", least_unacked: " << unacked_packets_.GetLeastUnacked()
+ << ", packets_acked_: " << packets_acked_;
+ } else {
+ QUIC_PEER_BUG << "Received "
+ << QuicUtils::EncryptionLevelToString(ack_decrypted_level)
+ << " ack for unackable packet: "
+ << acked_packet.packet_number << " with state: "
+ << QuicUtils::SentPacketStateToString(info->state);
+ if (supports_multiple_packet_number_spaces()) {
+ if (info->state == NEVER_SENT) {
+ return UNSENT_PACKETS_ACKED;
+ }
+ return UNACKABLE_PACKETS_ACKED;
+ }
+ }
+ continue;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Got an "
+ << QuicUtils::EncryptionLevelToString(ack_decrypted_level)
+ << " ack for packet " << acked_packet.packet_number;
+ const PacketNumberSpace packet_number_space =
+ unacked_packets_.use_uber_loss_algorithm()
+ ? unacked_packets_.GetPacketNumberSpace(info->encryption_level)
+ : NUM_PACKET_NUMBER_SPACES;
+ if (supports_multiple_packet_number_spaces() &&
+ QuicUtils::GetPacketNumberSpace(ack_decrypted_level) !=
+ packet_number_space) {
+ return PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE;
+ }
+ last_ack_frame_.packets.Add(acked_packet.packet_number);
+ largest_packet_peer_knows_is_acked_.UpdateMax(info->largest_acked);
+ if (supports_multiple_packet_number_spaces()) {
+ largest_packets_peer_knows_is_acked_[packet_number_space].UpdateMax(
+ info->largest_acked);
+ }
+ // If data is associated with the most recent transmission of this
+ // packet, then inform the caller.
+ if (info->in_flight) {
+ acked_packet.bytes_acked = info->bytes_sent;
+ } else {
+ // Unackable packets are skipped earlier.
+ largest_newly_acked_ = acked_packet.packet_number;
+ }
+ if (unacked_packets_.use_uber_loss_algorithm()) {
+ unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
+ packet_number_space, acked_packet.packet_number);
+ }
+ MarkPacketHandled(acked_packet.packet_number, info,
+ last_ack_frame_.ack_delay_time);
+ }
+ const bool acked_new_packet = !packets_acked_.empty();
+ PostProcessNewlyAckedPackets(last_ack_frame_, ack_receive_time, rtt_updated_,
+ prior_bytes_in_flight);
+
+ return acked_new_packet ? PACKETS_NEWLY_ACKED : NO_PACKETS_NEWLY_ACKED;
+}
+
+void QuicSentPacketManager::SetDebugDelegate(DebugDelegate* debug_delegate) {
+ debug_delegate_ = debug_delegate;
+}
+
+void QuicSentPacketManager::OnApplicationLimited() {
+ if (using_pacing_) {
+ pacing_sender_.OnApplicationLimited();
+ }
+ send_algorithm_->OnApplicationLimited(unacked_packets_.bytes_in_flight());
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnApplicationLimited();
+ }
+}
+
+QuicTime QuicSentPacketManager::GetNextReleaseTime() const {
+ return using_pacing_ ? pacing_sender_.ideal_next_packet_send_time()
+ : QuicTime::Zero();
+}
+
+void QuicSentPacketManager::SetInitialRtt(QuicTime::Delta rtt) {
+ const QuicTime::Delta min_rtt =
+ QuicTime::Delta::FromMicroseconds(kMinInitialRoundTripTimeUs);
+ const QuicTime::Delta max_rtt =
+ QuicTime::Delta::FromMicroseconds(kMaxInitialRoundTripTimeUs);
+ rtt_stats_.set_initial_rtt(std::max(min_rtt, std::min(max_rtt, rtt)));
+}
+
+void QuicSentPacketManager::EnableMultiplePacketNumberSpacesSupport() {
+ unacked_packets_.EnableMultiplePacketNumberSpacesSupport();
+}
+
+QuicPacketNumber QuicSentPacketManager::GetLargestAckedPacket(
+ EncryptionLevel decrypted_packet_level) const {
+ DCHECK(supports_multiple_packet_number_spaces());
+ return unacked_packets_.GetLargestAckedOfPacketNumberSpace(
+ QuicUtils::GetPacketNumberSpace(decrypted_packet_level));
+}
+
+QuicPacketNumber QuicSentPacketManager::GetLargestSentPacket(
+ EncryptionLevel decrypted_packet_level) const {
+ DCHECK(supports_multiple_packet_number_spaces());
+ return unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ decrypted_packet_level);
+}
+
+QuicPacketNumber QuicSentPacketManager::GetLargestPacketPeerKnowsIsAcked(
+ EncryptionLevel decrypted_packet_level) const {
+ DCHECK(supports_multiple_packet_number_spaces());
+ return largest_packets_peer_knows_is_acked_[QuicUtils::GetPacketNumberSpace(
+ decrypted_packet_level)];
+}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic
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
new file mode 100644
index 00000000000..7e10b77048c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h
@@ -0,0 +1,634 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_SENT_PACKET_MANAGER_H_
+#define QUICHE_QUIC_CORE_QUIC_SENT_PACKET_MANAGER_H_
+
+#include <cstddef>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/pacing_sender.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
+#include "net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h"
+#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class QuicConnectionPeer;
+class QuicSentPacketManagerPeer;
+} // namespace test
+
+class QuicClock;
+class QuicConfig;
+struct QuicConnectionStats;
+
+// Class which tracks the set of packets sent on a QUIC connection and contains
+// a send algorithm to decide when to send new packets. It keeps track of any
+// retransmittable data associated with each packet. If a packet is
+// retransmitted, it will keep track of each version of a packet so that if a
+// previous transmission is acked, the data will not be retransmitted.
+class QUIC_EXPORT_PRIVATE QuicSentPacketManager {
+ public:
+ // Interface which gets callbacks from the QuicSentPacketManager at
+ // interesting points. Implementations must not mutate the state of
+ // the packet manager or connection as a result of these callbacks.
+ class QUIC_EXPORT_PRIVATE DebugDelegate {
+ public:
+ virtual ~DebugDelegate() {}
+
+ // Called when a spurious retransmission is detected.
+ virtual void OnSpuriousPacketRetransmission(
+ TransmissionType transmission_type,
+ QuicByteCount byte_size) {}
+
+ virtual void OnIncomingAck(const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time,
+ QuicPacketNumber largest_observed,
+ bool rtt_updated,
+ QuicPacketNumber least_unacked_sent_packet) {}
+
+ virtual void OnPacketLoss(QuicPacketNumber lost_packet_number,
+ TransmissionType transmission_type,
+ QuicTime detection_time) {}
+
+ virtual void OnApplicationLimited() {}
+
+ virtual void OnAdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) {}
+ };
+
+ // Interface which gets callbacks from the QuicSentPacketManager when
+ // network-related state changes. Implementations must not mutate the
+ // state of the packet manager as a result of these callbacks.
+ class QUIC_EXPORT_PRIVATE NetworkChangeVisitor {
+ public:
+ virtual ~NetworkChangeVisitor() {}
+
+ // Called when congestion window or RTT may have changed.
+ virtual void OnCongestionChange() = 0;
+
+ // Called when the Path MTU may have increased.
+ virtual void OnPathMtuIncreased(QuicPacketLength packet_size) = 0;
+ };
+
+ QuicSentPacketManager(Perspective perspective,
+ const QuicClock* clock,
+ QuicConnectionStats* stats,
+ CongestionControlType congestion_control_type,
+ LossDetectionType loss_type);
+ QuicSentPacketManager(const QuicSentPacketManager&) = delete;
+ QuicSentPacketManager& operator=(const QuicSentPacketManager&) = delete;
+ virtual ~QuicSentPacketManager();
+
+ virtual void SetFromConfig(const QuicConfig& config);
+
+ // Pass the CachedNetworkParameters to the send algorithm.
+ void ResumeConnectionState(
+ const CachedNetworkParameters& cached_network_params,
+ bool max_bandwidth_resumption);
+
+ void SetMaxPacingRate(QuicBandwidth max_pacing_rate) {
+ pacing_sender_.set_max_pacing_rate(max_pacing_rate);
+ }
+
+ QuicBandwidth MaxPacingRate() const {
+ return pacing_sender_.max_pacing_rate();
+ }
+
+ // Set handshake_confirmed_ to true and neuter packets in HANDSHAKE packet
+ // number space.
+ void SetHandshakeConfirmed();
+
+ // Requests retransmission of all unacked packets of |retransmission_type|.
+ // The behavior of this method depends on the value of |retransmission_type|:
+ // ALL_UNACKED_RETRANSMISSION - All unacked packets will be retransmitted.
+ // This can happen, for example, after a version negotiation packet has been
+ // received and all packets needs to be retransmitted with the new version.
+ // ALL_INITIAL_RETRANSMISSION - Only initially encrypted packets will be
+ // retransmitted. This can happen, for example, when a CHLO has been rejected
+ // and the previously encrypted data needs to be encrypted with a new key.
+ void RetransmitUnackedPackets(TransmissionType retransmission_type);
+
+ // Notify the sent packet manager of an external network measurement or
+ // prediction for either |bandwidth| or |rtt|; either can be empty.
+ void AdjustNetworkParameters(QuicBandwidth bandwidth, QuicTime::Delta rtt);
+
+ // Retransmits the oldest pending packet there is still a tail loss probe
+ // pending. Invoked after OnRetransmissionTimeout.
+ bool MaybeRetransmitTailLossProbe();
+
+ // Retransmits the oldest pending packet.
+ bool MaybeRetransmitOldestPacket(TransmissionType type);
+
+ // Removes the retransmittable frames from all unencrypted packets to ensure
+ // they don't get retransmitted.
+ // TODO(fayang): Consider remove this function when deprecating
+ // quic_use_uber_loss_algorithm.
+ void NeuterUnencryptedPackets();
+
+ // Returns true if there are pending retransmissions.
+ // Not const because retransmissions may be cancelled before returning.
+ bool HasPendingRetransmissions() const {
+ return !pending_retransmissions_.empty();
+ }
+
+ // Retrieves the next pending retransmission. You must ensure that
+ // there are pending retransmissions prior to calling this function.
+ QuicPendingRetransmission NextPendingRetransmission();
+
+ // Returns true if there's outstanding crypto data.
+ bool HasUnackedCryptoPackets() const {
+ return unacked_packets_.HasPendingCryptoPackets();
+ }
+
+ // Returns true if there are packets in flight expecting to be acknowledged.
+ bool HasInFlightPackets() const {
+ return unacked_packets_.HasInFlightPackets();
+ }
+
+ // Returns the smallest packet number of a serialized packet which has not
+ // been acked by the peer.
+ QuicPacketNumber GetLeastUnacked() const {
+ return unacked_packets_.GetLeastUnacked();
+ }
+
+ // Called when we have sent bytes to the peer. This informs the manager both
+ // the number of bytes sent and if they were retransmitted. Returns true if
+ // the sender should reset the retransmission timer.
+ bool OnPacketSent(SerializedPacket* serialized_packet,
+ QuicPacketNumber original_packet_number,
+ QuicTime sent_time,
+ TransmissionType transmission_type,
+ HasRetransmittableData has_retransmittable_data);
+
+ // Called when the retransmission timer expires.
+ void OnRetransmissionTimeout();
+
+ // Calculate the time until we can send the next packet to the wire.
+ // Note 1: When kUnknownWaitTime is returned, there is no need to poll
+ // TimeUntilSend again until we receive an OnIncomingAckFrame event.
+ // Note 2: Send algorithms may or may not use |retransmit| in their
+ // calculations.
+ QuicTime::Delta TimeUntilSend(QuicTime now) const;
+
+ // Returns the current delay for the retransmission timer, which may send
+ // either a tail loss probe or do a full RTO. Returns QuicTime::Zero() if
+ // there are no retransmittable packets.
+ const QuicTime GetRetransmissionTime() const;
+
+ // Returns the current delay for the path degrading timer, which is used to
+ // notify the session that this connection is degrading.
+ const QuicTime::Delta GetPathDegradingDelay() const;
+
+ const RttStats* GetRttStats() const { return &rtt_stats_; }
+
+ // Returns the estimated bandwidth calculated by the congestion algorithm.
+ QuicBandwidth BandwidthEstimate() const {
+ return send_algorithm_->BandwidthEstimate();
+ }
+
+ const QuicSustainedBandwidthRecorder* SustainedBandwidthRecorder() const {
+ return &sustained_bandwidth_recorder_;
+ }
+
+ // Returns the size of the current congestion window in number of
+ // kDefaultTCPMSS-sized segments. Note, this is not the *available* window.
+ // Some send algorithms may not use a congestion window and will return 0.
+ QuicPacketCount GetCongestionWindowInTcpMss() const {
+ return send_algorithm_->GetCongestionWindow() / kDefaultTCPMSS;
+ }
+
+ // Returns the number of packets of length |max_packet_length| which fit in
+ // the current congestion window. More packets may end up in flight if the
+ // congestion window has been recently reduced, of if non-full packets are
+ // sent.
+ QuicPacketCount EstimateMaxPacketsInFlight(
+ QuicByteCount max_packet_length) const {
+ return send_algorithm_->GetCongestionWindow() / max_packet_length;
+ }
+
+ // Returns the size of the current congestion window size in bytes.
+ QuicByteCount GetCongestionWindowInBytes() const {
+ return send_algorithm_->GetCongestionWindow();
+ }
+
+ // Returns the size of the slow start congestion window in nume of 1460 byte
+ // TCP segments, aka ssthresh. Some send algorithms do not define a slow
+ // start threshold and will return 0.
+ QuicPacketCount GetSlowStartThresholdInTcpMss() const {
+ return send_algorithm_->GetSlowStartThreshold() / kDefaultTCPMSS;
+ }
+
+ // Return the total time spent in slow start so far. If the sender is
+ // currently in slow start, the return value will include the duration between
+ // the most recent entry to slow start and now.
+ //
+ // Only implemented for BBR. Return QuicTime::Delta::Infinite() for other
+ // congestion controllers.
+ QuicTime::Delta GetSlowStartDuration() const;
+
+ // Returns debugging information about the state of the congestion controller.
+ std::string GetDebugState() const;
+
+ // Returns the number of bytes that are considered in-flight, i.e. not lost or
+ // acknowledged.
+ QuicByteCount GetBytesInFlight() const {
+ return unacked_packets_.bytes_in_flight();
+ }
+
+ // No longer retransmit data for |stream_id|.
+ void CancelRetransmissionsForStream(QuicStreamId stream_id);
+
+ // Called when peer address changes and the connection migrates.
+ void OnConnectionMigration(AddressChangeType type);
+
+ // Called when an ack frame is initially parsed.
+ void OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicTime ack_receive_time);
+
+ // Called when ack range [start, end) is received. Populates packets_acked_
+ // with newly acked packets.
+ void OnAckRange(QuicPacketNumber start, QuicPacketNumber end);
+
+ // Called when a timestamp is processed. If it's present in packets_acked_,
+ // the timestamp field is set. Otherwise, the timestamp is ignored.
+ void OnAckTimestamp(QuicPacketNumber packet_number, QuicTime timestamp);
+
+ // Called when an ack frame is parsed completely.
+ AckResult OnAckFrameEnd(QuicTime ack_receive_time,
+ EncryptionLevel ack_decrypted_level);
+
+ // Called to enable/disable letting session decide what to write.
+ void SetSessionDecideWhatToWrite(bool session_decides_what_to_write) {
+ unacked_packets_.SetSessionDecideWhatToWrite(session_decides_what_to_write);
+ }
+
+ void EnableMultiplePacketNumberSpacesSupport();
+
+ void SetDebugDelegate(DebugDelegate* debug_delegate);
+
+ void SetPacingAlarmGranularity(QuicTime::Delta alarm_granularity) {
+ pacing_sender_.set_alarm_granularity(alarm_granularity);
+ }
+
+ QuicPacketNumber GetLargestObserved() const {
+ return unacked_packets_.largest_acked();
+ }
+
+ QuicPacketNumber GetLargestAckedPacket(
+ EncryptionLevel decrypted_packet_level) const;
+
+ QuicPacketNumber GetLargestSentPacket() const {
+ return unacked_packets_.largest_sent_packet();
+ }
+
+ QuicPacketNumber GetLargestSentPacket(
+ EncryptionLevel decrypted_packet_level) const;
+
+ QuicPacketNumber GetLargestPacketPeerKnowsIsAcked(
+ EncryptionLevel decrypted_packet_level) const;
+
+ void SetNetworkChangeVisitor(NetworkChangeVisitor* visitor) {
+ DCHECK(!network_change_visitor_);
+ DCHECK(visitor);
+ network_change_visitor_ = visitor;
+ }
+
+ bool InSlowStart() const { return send_algorithm_->InSlowStart(); }
+
+ size_t GetConsecutiveRtoCount() const { return consecutive_rto_count_; }
+
+ size_t GetConsecutiveTlpCount() const { return consecutive_tlp_count_; }
+
+ void OnApplicationLimited();
+
+ const SendAlgorithmInterface* GetSendAlgorithm() const {
+ return send_algorithm_.get();
+ }
+
+ void SetSessionNotifier(SessionNotifierInterface* session_notifier) {
+ unacked_packets_.SetSessionNotifier(session_notifier);
+ }
+
+ QuicTime GetNextReleaseTime() const;
+
+ QuicPacketCount initial_congestion_window() const {
+ return initial_congestion_window_;
+ }
+
+ QuicPacketNumber largest_packet_peer_knows_is_acked() const {
+ return largest_packet_peer_knows_is_acked_;
+ }
+
+ bool handshake_confirmed() const { return handshake_confirmed_; }
+
+ bool session_decides_what_to_write() const {
+ return unacked_packets_.session_decides_what_to_write();
+ }
+
+ size_t pending_timer_transmission_count() const {
+ return pending_timer_transmission_count_;
+ }
+
+ QuicTime::Delta delayed_ack_time() const { return delayed_ack_time_; }
+
+ void set_delayed_ack_time(QuicTime::Delta delayed_ack_time) {
+ // The delayed ack time should never be more than one half the min RTO time.
+ DCHECK_LE(delayed_ack_time, (min_rto_timeout_ * 0.5));
+ delayed_ack_time_ = delayed_ack_time;
+ }
+
+ bool enable_half_rtt_tail_loss_probe() const {
+ return enable_half_rtt_tail_loss_probe_;
+ }
+
+ void set_enable_half_rtt_tail_loss_probe(
+ bool enable_half_rtt_tail_loss_probe) {
+ enable_half_rtt_tail_loss_probe_ = enable_half_rtt_tail_loss_probe;
+ }
+
+ const QuicUnackedPacketMap& unacked_packets() const {
+ return unacked_packets_;
+ }
+
+ // Sets the send algorithm to the given congestion control type and points the
+ // pacing sender at |send_algorithm_|. Can be called any number of times.
+ void SetSendAlgorithm(CongestionControlType congestion_control_type);
+
+ // Sets the send algorithm to |send_algorithm| and points the pacing sender at
+ // |send_algorithm_|. Takes ownership of |send_algorithm|. Can be called any
+ // number of times.
+ // Setting the send algorithm once the connection is underway is dangerous.
+ void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm);
+
+ bool tolerate_reneging() const { return tolerate_reneging_; }
+
+ bool supports_multiple_packet_number_spaces() const {
+ return unacked_packets_.supports_multiple_packet_number_spaces();
+ }
+
+ bool use_uber_loss_algorithm() const {
+ return unacked_packets_.use_uber_loss_algorithm();
+ }
+
+ private:
+ friend class test::QuicConnectionPeer;
+ friend class test::QuicSentPacketManagerPeer;
+
+ // The retransmission timer is a single timer which switches modes depending
+ // upon connection state.
+ enum RetransmissionTimeoutMode {
+ // A conventional TCP style RTO.
+ RTO_MODE,
+ // A tail loss probe. By default, QUIC sends up to two before RTOing.
+ TLP_MODE,
+ // Retransmission of handshake packets prior to handshake completion.
+ HANDSHAKE_MODE,
+ // Re-invoke the loss detection when a packet is not acked before the
+ // loss detection algorithm expects.
+ LOSS_MODE,
+ };
+
+ typedef QuicLinkedHashMap<QuicPacketNumber,
+ TransmissionType,
+ QuicPacketNumberHash>
+ PendingRetransmissionMap;
+
+ // Returns the current retransmission mode.
+ RetransmissionTimeoutMode GetRetransmissionMode() const;
+
+ // Retransmits all crypto stream packets.
+ void RetransmitCryptoPackets();
+
+ // Retransmits two packets for an RTO and removes any non-retransmittable
+ // packets from flight.
+ void RetransmitRtoPackets();
+
+ // Returns the timeout for retransmitting crypto handshake packets.
+ const QuicTime::Delta GetCryptoRetransmissionDelay() const;
+
+ // Returns the timeout for a new tail loss probe. |consecutive_tlp_count| is
+ // the number of consecutive tail loss probes that have already been sent.
+ const QuicTime::Delta GetTailLossProbeDelay(
+ size_t consecutive_tlp_count) const;
+
+ // Calls GetTailLossProbeDelay() with values from the current state of this
+ // packet manager as its params.
+ const QuicTime::Delta GetTailLossProbeDelay() const {
+ return GetTailLossProbeDelay(consecutive_tlp_count_);
+ }
+
+ // Returns the retransmission timeout, after which a full RTO occurs.
+ // |consecutive_rto_count| is the number of consecutive RTOs that have already
+ // occurred.
+ const QuicTime::Delta GetRetransmissionDelay(
+ size_t consecutive_rto_count) const;
+
+ // Calls GetRetransmissionDelay() with values from the current state of this
+ // packet manager as its params.
+ const QuicTime::Delta GetRetransmissionDelay() const {
+ return GetRetransmissionDelay(consecutive_rto_count_);
+ }
+
+ // Returns the newest transmission associated with a packet.
+ QuicPacketNumber GetNewestRetransmission(
+ QuicPacketNumber packet_number,
+ const QuicTransmissionInfo& transmission_info) const;
+
+ // Update the RTT if the ack is for the largest acked packet number.
+ // Returns true if the rtt was updated.
+ bool MaybeUpdateRTT(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicTime ack_receive_time);
+
+ // Invokes the loss detection algorithm and loses and retransmits packets if
+ // necessary.
+ void InvokeLossDetection(QuicTime time);
+
+ // Invokes OnCongestionEvent if |rtt_updated| is true, there are pending acks,
+ // or pending losses. Clears pending acks and pending losses afterwards.
+ // |prior_in_flight| is the number of bytes in flight before the losses or
+ // acks, |event_time| is normally the timestamp of the ack packet which caused
+ // the event, although it can be the time at which loss detection was
+ // triggered.
+ void MaybeInvokeCongestionEvent(bool rtt_updated,
+ QuicByteCount prior_in_flight,
+ QuicTime event_time);
+
+ // Removes the retransmittability and in flight properties from the packet at
+ // |info| due to receipt by the peer.
+ void MarkPacketHandled(QuicPacketNumber packet_number,
+ QuicTransmissionInfo* info,
+ QuicTime::Delta ack_delay_time);
+
+ // Request that |packet_number| be retransmitted after the other pending
+ // retransmissions. Does not add it to the retransmissions if it's already
+ // a pending retransmission.
+ void MarkForRetransmission(QuicPacketNumber packet_number,
+ TransmissionType transmission_type);
+
+ // Performs whatever work is need to retransmit the data correctly, either
+ // by retransmitting the frames directly or by notifying that the frames
+ // are lost.
+ void HandleRetransmission(TransmissionType transmission_type,
+ QuicTransmissionInfo* transmission_info);
+
+ // Called after packets have been marked handled with last received ack frame.
+ void PostProcessNewlyAckedPackets(const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time,
+ bool rtt_updated,
+ QuicByteCount prior_bytes_in_flight);
+
+ // Notify observers that packet with QuicTransmissionInfo |info| is a spurious
+ // retransmission. It is caller's responsibility to guarantee the packet with
+ // QuicTransmissionInfo |info| is a spurious retransmission before calling
+ // this function.
+ void RecordOneSpuriousRetransmission(const QuicTransmissionInfo& info);
+
+ // Notify observers about spurious retransmits of packet with
+ // QuicTransmissionInfo |info|.
+ void RecordSpuriousRetransmissions(const QuicTransmissionInfo& info,
+ QuicPacketNumber acked_packet_number);
+
+ // Sets the initial RTT of the connection.
+ void SetInitialRtt(QuicTime::Delta rtt);
+
+ // Should only be called from constructor.
+ LossDetectionInterface* GetInitialLossAlgorithm();
+
+ // Called when handshake is confirmed to remove the retransmittable frames
+ // from all packets of HANDSHAKE_DATA packet number space to ensure they don't
+ // get retransmitted and will eventually be removed from unacked packets map.
+ // Only used when quic_use_uber_loss_algorithm is true. Please note, this only
+ // applies to QUIC Crypto and needs to be changed when switches to IETF QUIC
+ // with QUIC TLS.
+ void NeuterHandshakePackets();
+
+ // Newly serialized retransmittable packets are added to this map, which
+ // contains owning pointers to any contained frames. If a packet is
+ // retransmitted, this map will contain entries for both the old and the new
+ // packet. The old packet's retransmittable frames entry will be nullptr,
+ // while the new packet's entry will contain the frames to retransmit.
+ // If the old packet is acked before the new packet, then the old entry will
+ // be removed from the map and the new entry's retransmittable frames will be
+ // set to nullptr.
+ QuicUnackedPacketMap unacked_packets_;
+
+ // Pending retransmissions which have not been packetized and sent yet.
+ PendingRetransmissionMap pending_retransmissions_;
+
+ const QuicClock* clock_;
+ QuicConnectionStats* stats_;
+
+ DebugDelegate* debug_delegate_;
+ NetworkChangeVisitor* network_change_visitor_;
+ QuicPacketCount initial_congestion_window_;
+ RttStats rtt_stats_;
+ std::unique_ptr<SendAlgorithmInterface> send_algorithm_;
+ // Not owned. Always points to |general_loss_algorithm_| outside of tests.
+ LossDetectionInterface* loss_algorithm_;
+ // TODO(fayang): Remove general_loss_algorithm_ when deprecating
+ // quic_use_uber_loss_algorithm.
+ GeneralLossAlgorithm general_loss_algorithm_;
+ UberLossAlgorithm uber_loss_algorithm_;
+
+ // Tracks the first RTO packet. If any packet before that packet gets acked,
+ // it indicates the RTO was spurious and should be reversed(F-RTO).
+ QuicPacketNumber first_rto_transmission_;
+ // Number of times the RTO timer has fired in a row without receiving an ack.
+ size_t consecutive_rto_count_;
+ // Number of times the tail loss probe has been sent.
+ size_t consecutive_tlp_count_;
+ // Number of times the crypto handshake has been retransmitted.
+ size_t consecutive_crypto_retransmission_count_;
+ // Number of pending transmissions of TLP, RTO, or crypto packets.
+ size_t pending_timer_transmission_count_;
+ // Maximum number of tail loss probes to send before firing an RTO.
+ size_t max_tail_loss_probes_;
+ // Maximum number of packets to send upon RTO.
+ QuicPacketCount max_rto_packets_;
+ // If true, send the TLP at 0.5 RTT.
+ bool enable_half_rtt_tail_loss_probe_;
+ bool using_pacing_;
+ // If true, use the new RTO with loss based CWND reduction instead of the send
+ // algorithms's OnRetransmissionTimeout to reduce the congestion window.
+ bool use_new_rto_;
+ // If true, use a more conservative handshake retransmission policy.
+ bool conservative_handshake_retransmits_;
+ // The minimum TLP timeout.
+ QuicTime::Delta min_tlp_timeout_;
+ // The minimum RTO.
+ QuicTime::Delta min_rto_timeout_;
+ // Whether to use IETF style TLP that includes the max ack delay.
+ bool ietf_style_tlp_;
+ // IETF style TLP, but with a 2x multiplier instead of 1.5x.
+ bool ietf_style_2x_tlp_;
+
+ // Vectors packets acked and lost as a result of the last congestion event.
+ AckedPacketVector packets_acked_;
+ LostPacketVector packets_lost_;
+ // Largest newly acknowledged packet.
+ QuicPacketNumber largest_newly_acked_;
+ // Largest packet in bytes ever acknowledged.
+ QuicPacketLength largest_mtu_acked_;
+
+ // Replaces certain calls to |send_algorithm_| when |using_pacing_| is true.
+ // Calls into |send_algorithm_| for the underlying congestion control.
+ PacingSender pacing_sender_;
+
+ // Set to true after the crypto handshake has successfully completed. After
+ // this is true we no longer use HANDSHAKE_MODE, and further frames sent on
+ // the crypto stream (i.e. SCUP messages) are treated like normal
+ // retransmittable frames.
+ bool handshake_confirmed_;
+
+ // Records bandwidth from server to client in normal operation, over periods
+ // of time with no loss events.
+ QuicSustainedBandwidthRecorder sustained_bandwidth_recorder_;
+
+ // The largest acked value that was sent in an ack, which has then been acked.
+ QuicPacketNumber largest_packet_peer_knows_is_acked_;
+ // The largest acked value that was sent in an ack, which has then been acked
+ // for per packet number space. Only used when connection supports multiple
+ // packet number spaces.
+ QuicPacketNumber
+ largest_packets_peer_knows_is_acked_[NUM_PACKET_NUMBER_SPACES];
+
+ // The maximum amount of time to wait before sending an acknowledgement.
+ // The recovery code assumes the delayed ack time is the same on both sides.
+ QuicTime::Delta delayed_ack_time_;
+
+ // Latest received ack frame.
+ QuicAckFrame last_ack_frame_;
+
+ // Record whether RTT gets updated by last largest acked..
+ bool rtt_updated_;
+
+ // A reverse iterator of last_ack_frame_.packets. This is reset in
+ // OnAckRangeStart, and gradually moves in OnAckRange..
+ PacketNumberQueue::const_reverse_iterator acked_packets_iter_;
+
+ // Latched value of quic_tolerate_reneging.
+ const bool tolerate_reneging_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_SENT_PACKET_MANAGER_H_
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
new file mode 100644
index 00000000000..08e307c1a06
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sent_packet_manager_test.cc
@@ -0,0 +1,2695 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::IsEmpty;
+using testing::Not;
+using testing::Pointwise;
+using testing::Return;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace quic {
+namespace test {
+namespace {
+// Default packet length.
+const uint32_t kDefaultLength = 1000;
+
+// Stream ID for data sent in CreatePacket().
+const QuicStreamId kStreamId = 7;
+
+// Matcher to check that the packet number matches the second argument.
+MATCHER(PacketNumberEq, "") {
+ return ::testing::get<0>(arg).packet_number ==
+ QuicPacketNumber(::testing::get<1>(arg));
+}
+
+class MockDebugDelegate : public QuicSentPacketManager::DebugDelegate {
+ public:
+ MOCK_METHOD2(OnSpuriousPacketRetransmission,
+ void(TransmissionType transmission_type,
+ QuicByteCount byte_size));
+ MOCK_METHOD3(OnPacketLoss,
+ void(QuicPacketNumber lost_packet_number,
+ TransmissionType transmission_type,
+ QuicTime detection_time));
+};
+
+class QuicSentPacketManagerTest : public QuicTestWithParam<bool> {
+ public:
+ void RetransmitCryptoPacket(uint64_t packet_number) {
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA));
+ SerializedPacket packet(CreatePacket(packet_number, false));
+ packet.retransmittable_frames.push_back(
+ QuicFrame(QuicStreamFrame(1, false, 0, QuicStringPiece())));
+ packet.has_crypto_handshake = IS_HANDSHAKE;
+ manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+ HANDSHAKE_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ }
+
+ void RetransmitDataPacket(uint64_t packet_number, TransmissionType type) {
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA));
+ SerializedPacket packet(CreatePacket(packet_number, true));
+ manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), type,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ protected:
+ QuicSentPacketManagerTest()
+ : manager_(Perspective::IS_SERVER, &clock_, &stats_, kCubicBytes, kNack),
+ send_algorithm_(new StrictMock<MockSendAlgorithm>),
+ network_change_visitor_(new StrictMock<MockNetworkChangeVisitor>) {
+ QuicSentPacketManagerPeer::SetSendAlgorithm(&manager_, send_algorithm_);
+ // Disable tail loss probes for most tests.
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 0);
+ // Advance the time 1s so the send times are never QuicTime::Zero.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+ manager_.SetNetworkChangeVisitor(network_change_visitor_.get());
+ manager_.SetSessionNotifier(&notifier_);
+ manager_.SetSessionDecideWhatToWrite(GetParam());
+
+ EXPECT_CALL(*send_algorithm_, HasReliableBandwidthEstimate())
+ .Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, BandwidthEstimate())
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber());
+ EXPECT_CALL(*network_change_visitor_, OnPathMtuIncreased(1000))
+ .Times(AnyNumber());
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(notifier_, HasUnackedCryptoData())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(notifier_, OnStreamFrameRetransmitted(_)).Times(AnyNumber());
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).WillRepeatedly(Return(true));
+ }
+
+ ~QuicSentPacketManagerTest() override {}
+
+ QuicByteCount BytesInFlight() {
+ return QuicSentPacketManagerPeer::GetBytesInFlight(&manager_);
+ }
+ void VerifyUnackedPackets(uint64_t* packets, size_t num_packets) {
+ if (num_packets == 0) {
+ EXPECT_TRUE(manager_.unacked_packets().empty());
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ &manager_));
+ return;
+ }
+
+ EXPECT_FALSE(manager_.unacked_packets().empty());
+ EXPECT_EQ(QuicPacketNumber(packets[0]), manager_.GetLeastUnacked());
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(QuicSentPacketManagerPeer::IsUnacked(&manager_, packets[i]))
+ << packets[i];
+ }
+ }
+
+ void VerifyRetransmittablePackets(uint64_t* packets, size_t num_packets) {
+ EXPECT_EQ(
+ num_packets,
+ QuicSentPacketManagerPeer::GetNumRetransmittablePackets(&manager_));
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasRetransmittableFrames(
+ &manager_, packets[i]))
+ << " packets[" << i << "]:" << packets[i];
+ }
+ }
+
+ void ExpectAck(uint64_t largest_observed) {
+ EXPECT_CALL(
+ *send_algorithm_,
+ // Ensure the AckedPacketVector argument contains largest_observed.
+ OnCongestionEvent(true, _, _,
+ Pointwise(PacketNumberEq(), {largest_observed}),
+ IsEmpty()));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ }
+
+ void ExpectUpdatedRtt(uint64_t largest_observed) {
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(true, _, _, IsEmpty(), IsEmpty()));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ }
+
+ void ExpectAckAndLoss(bool rtt_updated,
+ uint64_t largest_observed,
+ uint64_t lost_packet) {
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnCongestionEvent(rtt_updated, _, _,
+ Pointwise(PacketNumberEq(), {largest_observed}),
+ Pointwise(PacketNumberEq(), {lost_packet})));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ }
+
+ // |packets_acked| and |packets_lost| should be in packet number order.
+ void ExpectAcksAndLosses(bool rtt_updated,
+ uint64_t* packets_acked,
+ size_t num_packets_acked,
+ uint64_t* packets_lost,
+ size_t num_packets_lost) {
+ std::vector<QuicPacketNumber> ack_vector;
+ for (size_t i = 0; i < num_packets_acked; ++i) {
+ ack_vector.push_back(QuicPacketNumber(packets_acked[i]));
+ }
+ std::vector<QuicPacketNumber> lost_vector;
+ for (size_t i = 0; i < num_packets_lost; ++i) {
+ lost_vector.push_back(QuicPacketNumber(packets_lost[i]));
+ }
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(rtt_updated, _, _,
+ Pointwise(PacketNumberEq(), ack_vector),
+ Pointwise(PacketNumberEq(), lost_vector)));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange())
+ .Times(AnyNumber());
+ }
+
+ void RetransmitAndSendPacket(uint64_t old_packet_number,
+ uint64_t new_packet_number) {
+ RetransmitAndSendPacket(old_packet_number, new_packet_number,
+ TLP_RETRANSMISSION);
+ }
+
+ void RetransmitAndSendPacket(uint64_t old_packet_number,
+ uint64_t new_packet_number,
+ TransmissionType transmission_type) {
+ bool is_lost = false;
+ if (manager_.session_decides_what_to_write()) {
+ if (transmission_type == HANDSHAKE_RETRANSMISSION ||
+ transmission_type == TLP_RETRANSMISSION ||
+ transmission_type == RTO_RETRANSMISSION ||
+ transmission_type == PROBING_RETRANSMISSION) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(
+ Invoke([this, new_packet_number](TransmissionType type) {
+ RetransmitDataPacket(new_packet_number, type);
+ })));
+ } else {
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
+ is_lost = true;
+ }
+ }
+ QuicSentPacketManagerPeer::MarkForRetransmission(
+ &manager_, old_packet_number, transmission_type);
+ if (manager_.session_decides_what_to_write()) {
+ if (!is_lost) {
+ return;
+ }
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), QuicPacketNumber(new_packet_number),
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA));
+ SerializedPacket packet(CreatePacket(new_packet_number, true));
+ manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+ transmission_type, HAS_RETRANSMITTABLE_DATA);
+ return;
+ }
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ QuicPendingRetransmission next_retransmission =
+ manager_.NextPendingRetransmission();
+ EXPECT_EQ(QuicPacketNumber(old_packet_number),
+ next_retransmission.packet_number);
+ EXPECT_EQ(transmission_type, next_retransmission.transmission_type);
+
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), QuicPacketNumber(new_packet_number),
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA));
+ SerializedPacket packet(CreatePacket(new_packet_number, false));
+ manager_.OnPacketSent(&packet, QuicPacketNumber(old_packet_number),
+ clock_.Now(), transmission_type,
+ HAS_RETRANSMITTABLE_DATA);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_,
+ new_packet_number));
+ }
+
+ SerializedPacket CreateDataPacket(uint64_t packet_number) {
+ return CreatePacket(packet_number, true);
+ }
+
+ SerializedPacket CreatePacket(uint64_t packet_number, bool retransmittable) {
+ SerializedPacket packet(QuicPacketNumber(packet_number),
+ PACKET_4BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+ false, false);
+ if (retransmittable) {
+ packet.retransmittable_frames.push_back(
+ QuicFrame(QuicStreamFrame(kStreamId, false, 0, QuicStringPiece())));
+ }
+ return packet;
+ }
+
+ void SendDataPacket(uint64_t packet_number) {
+ SendDataPacket(packet_number, ENCRYPTION_INITIAL);
+ }
+
+ void SendDataPacket(uint64_t packet_number,
+ EncryptionLevel encryption_level) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(),
+ QuicPacketNumber(packet_number), _, _));
+ SerializedPacket packet(CreateDataPacket(packet_number));
+ packet.encryption_level = encryption_level;
+ manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ }
+
+ void SendCryptoPacket(uint64_t packet_number) {
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA));
+ SerializedPacket packet(CreatePacket(packet_number, false));
+ packet.retransmittable_frames.push_back(
+ QuicFrame(QuicStreamFrame(1, false, 0, QuicStringPiece())));
+ packet.has_crypto_handshake = IS_HANDSHAKE;
+ manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, HasUnackedCryptoData())
+ .WillRepeatedly(Return(true));
+ }
+ }
+
+ void SendAckPacket(uint64_t packet_number, uint64_t largest_acked) {
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
+ kDefaultLength, NO_RETRANSMITTABLE_DATA));
+ SerializedPacket packet(CreatePacket(packet_number, false));
+ packet.largest_acked = QuicPacketNumber(largest_acked);
+ manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+ NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA);
+ }
+
+ // Based on QuicConnection's WritePendingRetransmissions.
+ void RetransmitNextPacket(uint64_t retransmission_packet_number) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_CALL(
+ *send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(retransmission_packet_number),
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA));
+ const QuicPendingRetransmission pending =
+ manager_.NextPendingRetransmission();
+ SerializedPacket packet(CreatePacket(retransmission_packet_number, false));
+ manager_.OnPacketSent(&packet, pending.packet_number, clock_.Now(),
+ pending.transmission_type, HAS_RETRANSMITTABLE_DATA);
+ }
+
+ QuicSentPacketManager manager_;
+ MockClock clock_;
+ QuicConnectionStats stats_;
+ MockSendAlgorithm* send_algorithm_;
+ std::unique_ptr<MockNetworkChangeVisitor> network_change_visitor_;
+ StrictMock<MockSessionNotifier> notifier_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests, QuicSentPacketManagerTest, testing::Bool());
+
+TEST_P(QuicSentPacketManagerTest, IsUnacked) {
+ VerifyUnackedPackets(nullptr, 0);
+ SendDataPacket(1);
+
+ uint64_t unacked[] = {1};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ uint64_t retransmittable[] = {1};
+ VerifyRetransmittablePackets(retransmittable,
+ QUIC_ARRAYSIZE(retransmittable));
+}
+
+TEST_P(QuicSentPacketManagerTest, IsUnAckedRetransmit) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_, 2));
+ uint64_t unacked[] = {1, 2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ std::vector<uint64_t> retransmittable;
+ if (manager_.session_decides_what_to_write()) {
+ retransmittable = {1, 2};
+ } else {
+ retransmittable = {2};
+ }
+ VerifyRetransmittablePackets(&retransmittable[0], retransmittable.size());
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmitThenAck) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ // Ack 2 but not 1.
+ ExpectAck(2);
+ manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ }
+ // Packet 1 is unacked, pending, but not retransmittable.
+ uint64_t unacked[] = {1};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) {
+ SendDataPacket(1);
+ if (manager_.session_decides_what_to_write()) {
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(2, type);
+ })));
+ }
+ }
+ QuicSentPacketManagerPeer::MarkForRetransmission(&manager_, 1,
+ TLP_RETRANSMISSION);
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ }
+ // Ack 1.
+ 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(), ENCRYPTION_INITIAL));
+
+ // There should no longer be a pending retransmission.
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ uint64_t unacked[] = {2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ // We do not know packet 2 is a spurious retransmission until it gets acked.
+ } else {
+ // No unacked packets remain.
+ VerifyUnackedPackets(nullptr, 0);
+ }
+ VerifyRetransmittablePackets(nullptr, 0);
+ EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmitThenStopRetransmittingBeforeSend) {
+ SendDataPacket(1);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _));
+ }
+ QuicSentPacketManagerPeer::MarkForRetransmission(&manager_, 1,
+ TLP_RETRANSMISSION);
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ }
+
+ manager_.CancelRetransmissionsForStream(kStreamId);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ }
+
+ // There should no longer be a pending retransmission.
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ uint64_t unacked[] = {1};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+ EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmitThenAckPrevious) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // Ack 1 but not 2.
+ 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(), ENCRYPTION_INITIAL));
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ }
+ // 2 remains unacked, but no packets have retransmittable data.
+ uint64_t unacked[] = {2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+ if (manager_.session_decides_what_to_write()) {
+ // Ack 2 causes 2 be considered as spurious retransmission.
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).WillOnce(Return(false));
+ ExpectAck(2);
+ manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ }
+
+ EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmitThenAckPreviousThenNackRetransmit) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // First, ACK packet 1 which makes packet 2 non-retransmittable.
+ 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(), ENCRYPTION_INITIAL));
+
+ SendDataPacket(3);
+ SendDataPacket(4);
+ SendDataPacket(5);
+ clock_.AdvanceTime(rtt);
+
+ // Next, NACK packet 2 three times.
+ ExpectAck(3);
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ ExpectAck(4);
+ manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ ExpectAckAndLoss(true, 5, 2);
+ if (manager_.session_decides_what_to_write()) {
+ // Frames in all packets are acked.
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ // Notify session that stream frame in packet 2 gets lost although it is
+ // not outstanding.
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ if (manager_.session_decides_what_to_write()) {
+ uint64_t unacked[] = {2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ } else {
+ // No packets remain unacked.
+ VerifyUnackedPackets(nullptr, 0);
+ }
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_P(QuicSentPacketManagerTest,
+ DISABLED_RetransmitTwiceThenAckPreviousBeforeSend) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ // Fire the RTO, which will mark 2 for retransmission (but will not send it).
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.OnRetransmissionTimeout();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+
+ // Ack 1 but not 2, before 2 is able to be sent.
+ // Since 1 has been retransmitted, it has already been lost, and so the
+ // send algorithm is not informed that it has been ACK'd.
+ ExpectUpdatedRtt(1);
+ EXPECT_CALL(*send_algorithm_, RevertRetransmissionTimeout());
+ manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ // Since 2 was marked for retransmit, when 1 is acked, 2 is kept for RTT.
+ uint64_t unacked[] = {2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmitTwiceThenAckFirst) {
+ StrictMock<MockDebugDelegate> debug_delegate;
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(debug_delegate, OnSpuriousPacketRetransmission(
+ TLP_RETRANSMISSION, kDefaultLength))
+ .Times(1);
+ } else {
+ EXPECT_CALL(debug_delegate, OnSpuriousPacketRetransmission(
+ TLP_RETRANSMISSION, kDefaultLength))
+ .Times(2);
+ }
+ manager_.SetDebugDelegate(&debug_delegate);
+
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // Ack 1 but not 2 or 3.
+ ExpectAck(1);
+ manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ if (manager_.session_decides_what_to_write()) {
+ // Frames in packets 2 and 3 are acked.
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_))
+ .Times(2)
+ .WillRepeatedly(Return(false));
+ }
+
+ // 2 and 3 remain unacked, but no packets have retransmittable data.
+ uint64_t unacked[] = {2, 3};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ // Ensure packet 2 is lost when 4 is sent and 3 and 4 are acked.
+ SendDataPacket(4);
+ if (manager_.session_decides_what_to_write()) {
+ // No new data gets acked in packet 3.
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+ }
+ uint64_t acked[] = {3, 4};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ uint64_t unacked2[] = {2};
+ VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ SendDataPacket(5);
+ ExpectAckAndLoss(true, 5, 2);
+ EXPECT_CALL(debug_delegate,
+ OnPacketLoss(QuicPacketNumber(2), LOSS_RETRANSMISSION, _));
+ if (manager_.session_decides_what_to_write()) {
+ // Frames in all packets are acked.
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ // Notify session that stream frame in packet 2 gets lost although it is
+ // not outstanding.
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ if (manager_.session_decides_what_to_write()) {
+ uint64_t unacked[] = {2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ } else {
+ VerifyUnackedPackets(nullptr, 0);
+ }
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ if (manager_.session_decides_what_to_write()) {
+ // Spurious retransmission is detected when packet 3 gets acked. We cannot
+ // know packet 2 is a spurious until it gets acked.
+ EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted);
+ } else {
+ EXPECT_EQ(2u, stats_.packets_spuriously_retransmitted);
+ }
+}
+
+TEST_P(QuicSentPacketManagerTest, AckOriginalTransmission) {
+ auto loss_algorithm = QuicMakeUnique<MockLossAlgorithm>();
+ QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm.get());
+
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ // Ack original transmission, but that wasn't lost via fast retransmit,
+ // so no call on OnSpuriousRetransmission is expected.
+ {
+ ExpectAck(1);
+ EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
+ manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ }
+
+ SendDataPacket(3);
+ SendDataPacket(4);
+ // Ack 4, which causes 3 to be retransmitted.
+ {
+ ExpectAck(4);
+ EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
+ manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(5));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION);
+ }
+
+ // Ack 3, which causes SpuriousRetransmitDetected to be called.
+ {
+ uint64_t acked[] = {3};
+ ExpectAcksAndLosses(false, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(*loss_algorithm,
+ SpuriousRetransmitDetected(_, _, _, QuicPacketNumber(5)));
+ manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ if (manager_.session_decides_what_to_write()) {
+ // Ack 3 will not cause 5 be considered as a spurious retransmission. Ack
+ // 5 will cause 5 be considered as a spurious retransmission as no new
+ // data gets acked.
+ ExpectAck(5);
+ EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).WillOnce(Return(false));
+ manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ }
+ }
+}
+
+TEST_P(QuicSentPacketManagerTest, GetLeastUnacked) {
+ EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked());
+}
+
+TEST_P(QuicSentPacketManagerTest, GetLeastUnackedUnacked) {
+ SendDataPacket(1);
+ EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked());
+}
+
+TEST_P(QuicSentPacketManagerTest, AckAckAndUpdateRtt) {
+ EXPECT_FALSE(manager_.largest_packet_peer_knows_is_acked().IsInitialized());
+ SendDataPacket(1);
+ SendAckPacket(2, 1);
+
+ // Now ack the ack and expect an RTT update.
+ uint64_t acked[] = {1, 2};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(2),
+ QuicTime::Delta::FromMilliseconds(5), clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(1), manager_.largest_packet_peer_knows_is_acked());
+
+ SendAckPacket(3, 3);
+
+ // Now ack the ack and expect only an RTT update.
+ uint64_t acked2[] = {3};
+ ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(3u),
+ manager_.largest_packet_peer_knows_is_acked());
+}
+
+TEST_P(QuicSentPacketManagerTest, Rtt) {
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(20);
+ SendDataPacket(1);
+ clock_.AdvanceTime(expected_rtt);
+
+ 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(), ENCRYPTION_INITIAL));
+ EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt());
+}
+
+TEST_P(QuicSentPacketManagerTest, RttWithInvalidDelta) {
+ // Expect that the RTT is equal to the local time elapsed, since the
+ // ack_delay_time is larger than the local time elapsed
+ // and is hence invalid.
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(1);
+ clock_.AdvanceTime(expected_rtt);
+
+ ExpectAck(1);
+ manager_.OnAckFrameStart(QuicPacketNumber(1),
+ QuicTime::Delta::FromMilliseconds(11), clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt());
+}
+
+TEST_P(QuicSentPacketManagerTest, RttWithInfiniteDelta) {
+ // Expect that the RTT is equal to the local time elapsed, since the
+ // ack_delay_time is infinite, and is hence invalid.
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(1);
+ clock_.AdvanceTime(expected_rtt);
+
+ 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(), ENCRYPTION_INITIAL));
+ EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt());
+}
+
+TEST_P(QuicSentPacketManagerTest, RttZeroDelta) {
+ // Expect that the RTT is the time between send and receive since the
+ // ack_delay_time is zero.
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(1);
+ clock_.AdvanceTime(expected_rtt);
+
+ ExpectAck(1);
+ manager_.OnAckFrameStart(QuicPacketNumber(1), QuicTime::Delta::Zero(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ EXPECT_EQ(expected_rtt, manager_.GetRttStats()->latest_rtt());
+}
+
+TEST_P(QuicSentPacketManagerTest, TailLossProbeTimeout) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+
+ // Send 1 packet.
+ SendDataPacket(1);
+
+ // The first tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(2, type); })));
+ }
+ manager_.MaybeRetransmitTailLossProbe();
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(2);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+
+ // The second tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(3, type); })));
+ }
+ manager_.MaybeRetransmitTailLossProbe();
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ }
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+ EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now()));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // Ack the third and ensure the first two are still pending.
+ ExpectAck(3);
+
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ // Acking two more packets will lose both of them due to nacks.
+ SendDataPacket(4);
+ SendDataPacket(5);
+ uint64_t acked[] = {4, 5};
+ uint64_t lost[] = {1, 2};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), lost,
+ QUIC_ARRAYSIZE(lost));
+ if (manager_.session_decides_what_to_write()) {
+ // Frames in all packets are acked.
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ // Notify session that stream frame in packets 1 and 2 get lost although
+ // they are not outstanding.
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(2);
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ EXPECT_EQ(2u, stats_.tlp_count);
+ EXPECT_EQ(0u, stats_.rto_count);
+}
+
+TEST_P(QuicSentPacketManagerTest, TailLossProbeThenRTO) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+
+ // Send 100 packets.
+ const size_t kNumSentPackets = 100;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ QuicTime rto_packet_time = clock_.Now();
+ // Advance the time.
+ clock_.AdvanceTime(manager_.GetRetransmissionTime() - clock_.Now());
+
+ // The first tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(101, type);
+ })));
+ }
+ manager_.MaybeRetransmitTailLossProbe();
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(101);
+ }
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+ EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now()));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ clock_.AdvanceTime(manager_.GetRetransmissionTime() - clock_.Now());
+
+ // The second tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(102, type);
+ })));
+ }
+ EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(102);
+ }
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+ EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now()));
+
+ // Ensure the RTO is set based on the correct packet.
+ rto_packet_time = clock_.Now();
+ EXPECT_EQ(rto_packet_time + QuicTime::Delta::FromMilliseconds(500),
+ manager_.GetRetransmissionTime());
+
+ // Advance the time enough to ensure all packets are RTO'd.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(2)
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(103, type);
+ })))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(104, type);
+ })));
+ }
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(2u, stats_.tlp_count);
+ EXPECT_EQ(1u, stats_.rto_count);
+ if (manager_.session_decides_what_to_write()) {
+ // There are 2 RTO retransmissions.
+ EXPECT_EQ(104 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ }
+ if (!manager_.session_decides_what_to_write()) {
+ // Send and Ack the RTO and ensure OnRetransmissionTimeout is called.
+ EXPECT_EQ(102 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(103);
+ }
+ QuicPacketNumber largest_acked = QuicPacketNumber(103);
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(
+ true, _, _, Pointwise(PacketNumberEq(), {largest_acked}), _));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ if (manager_.session_decides_what_to_write()) {
+ // Although frames in packet 3 gets acked, it would be kept for another
+ // RTT.
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
+ // Packets [1, 102] are lost, although stream frame in packet 3 is not
+ // outstanding.
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(102);
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(103), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(103), QuicPacketNumber(104));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ // All packets before 103 should be lost.
+ if (manager_.session_decides_what_to_write()) {
+ // Packet 104 is still in flight.
+ EXPECT_EQ(1000u, QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ } else {
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ }
+}
+
+TEST_P(QuicSentPacketManagerTest, CryptoHandshakeTimeout) {
+ // Send 2 crypto packets and 3 data packets.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ const size_t kNumSentDataPackets = 3;
+ for (size_t i = 1; i <= kNumSentDataPackets; ++i) {
+ SendDataPacket(kNumSentCryptoPackets + i);
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The first retransmits 2 packets.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(2)
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(6); }))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(7); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The second retransmits 2 packets.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(2)
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(8); }))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(9); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+ RetransmitNextPacket(8);
+ RetransmitNextPacket(9);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now ack the two crypto packets and the speculatively encrypted request,
+ // and ensure the first four crypto packets get abandoned, but not lost.
+ uint64_t acked[] = {3, 4, 5, 8, 9};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, HasUnackedCryptoData())
+ .WillRepeatedly(Return(false));
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(9), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(8), QuicPacketNumber(10));
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(6));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, CryptoHandshakeTimeoutVersionNegotiation) {
+ // Send 2 crypto packets and 3 data packets.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ const size_t kNumSentDataPackets = 3;
+ for (size_t i = 1; i <= kNumSentDataPackets; ++i) {
+ SendDataPacket(kNumSentCryptoPackets + i);
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(2)
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(6); }))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(7); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now act like a version negotiation packet arrived, which would cause all
+ // unacked packets to be retransmitted.
+ if (manager_.session_decides_what_to_write()) {
+ // Mark packets [1, 7] lost. And the frames in 6 and 7 are same as packets 1
+ // and 2, respectively.
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(7);
+ }
+ manager_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION);
+
+ // Ensure the first two pending packets are the crypto retransmits.
+ if (manager_.session_decides_what_to_write()) {
+ RetransmitCryptoPacket(8);
+ RetransmitCryptoPacket(9);
+ RetransmitDataPacket(10, ALL_UNACKED_RETRANSMISSION);
+ RetransmitDataPacket(11, ALL_UNACKED_RETRANSMISSION);
+ RetransmitDataPacket(12, ALL_UNACKED_RETRANSMISSION);
+ } else {
+ ASSERT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(QuicPacketNumber(6u),
+ manager_.NextPendingRetransmission().packet_number);
+ RetransmitNextPacket(8);
+ EXPECT_EQ(QuicPacketNumber(7u),
+ manager_.NextPendingRetransmission().packet_number);
+ RetransmitNextPacket(9);
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ // Send 3 more data packets and ensure the least unacked is raised.
+ RetransmitNextPacket(10);
+ RetransmitNextPacket(11);
+ RetransmitNextPacket(12);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+
+ EXPECT_EQ(QuicPacketNumber(1u), manager_.GetLeastUnacked());
+ // Least unacked isn't raised until an ack is received, so ack the
+ // crypto packets.
+ uint64_t acked[] = {8, 9};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(9), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(8), QuicPacketNumber(10));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, HasUnackedCryptoData())
+ .WillRepeatedly(Return(false));
+ }
+ EXPECT_EQ(QuicPacketNumber(10u), manager_.GetLeastUnacked());
+}
+
+TEST_P(QuicSentPacketManagerTest, CryptoHandshakeSpuriousRetransmission) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit the crypto packet as 2.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(2);
+ }
+
+ // Retransmit the crypto packet as 3.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(3); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(3);
+ }
+
+ // Now ack the second crypto packet, and ensure the first gets removed, but
+ // the third does not.
+ uint64_t acked[] = {2};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, HasUnackedCryptoData())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ uint64_t unacked[] = {3};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+}
+
+TEST_P(QuicSentPacketManagerTest, CryptoHandshakeTimeoutUnsentDataPacket) {
+ // Send 2 crypto packets and 1 data packet.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ SendDataPacket(3);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit 2 crypto packets, but not the serialized packet.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(2)
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(4); }))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(5); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(4);
+ RetransmitNextPacket(5);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest,
+ CryptoHandshakeRetransmissionThenRetransmitAll) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit the crypto packet as 2.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(2);
+ }
+ // Now retransmit all the unacked packets, which occurs when there is a
+ // version negotiation.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(2);
+ }
+ manager_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION);
+ if (manager_.session_decides_what_to_write()) {
+ // Both packets 1 and 2 are unackable.
+ EXPECT_FALSE(QuicSentPacketManagerPeer::IsUnacked(&manager_, 1));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::IsUnacked(&manager_, 2));
+ } else {
+ // Packet 2 is useful because it does not get retransmitted and still has
+ // retransmittable frames.
+ uint64_t unacked[] = {1, 2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest,
+ CryptoHandshakeRetransmissionThenNeuterAndAck) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit the crypto packet as 2.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(2);
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit the crypto packet as 3.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(3); }));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(3);
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now neuter all unacked unencrypted packets, which occurs when the
+ // connection goes forward secure.
+ manager_.NeuterUnencryptedPackets();
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, HasUnackedCryptoData())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ }
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ uint64_t unacked[] = {1, 2, 3};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ // Ensure both packets get discarded when packet 2 is acked.
+ uint64_t acked[] = {3};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmissionTimeout) {
+ StrictMock<MockDebugDelegate> debug_delegate;
+ manager_.SetDebugDelegate(&debug_delegate);
+
+ // Send 100 packets.
+ const size_t kNumSentPackets = 100;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(2)
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(101, type);
+ })))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(102, type);
+ })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(102 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ } else {
+ ASSERT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(100 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(101);
+ ASSERT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(102);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+
+ // Ack a retransmission.
+ // Ensure no packets are lost.
+ QuicPacketNumber largest_acked = QuicPacketNumber(102);
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(true, _, _,
+ Pointwise(PacketNumberEq(), {largest_acked}),
+ /*lost_packets=*/IsEmpty()));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ // RTO's use loss detection instead of immediately declaring retransmitted
+ // packets lost.
+ for (int i = 1; i <= 99; ++i) {
+ EXPECT_CALL(debug_delegate,
+ OnPacketLoss(QuicPacketNumber(i), LOSS_RETRANSMISSION, _));
+ }
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
+ // Packets [1, 99] are considered as lost, although stream frame in packet
+ // 2 is not outstanding.
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(99);
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(102), QuicTime::Delta::Zero(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(102), QuicPacketNumber(103));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmissionTimeoutOnePacket) {
+ // Set the 1RTO connection option.
+ QuicConfig client_config;
+ QuicTagVector options;
+ options.push_back(k1RTO);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*send_algorithm_, PacingRate(_))
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillRepeatedly(Return(10 * kDefaultTCPMSS));
+ manager_.SetFromConfig(client_config);
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
+
+ StrictMock<MockDebugDelegate> debug_delegate;
+ manager_.SetDebugDelegate(&debug_delegate);
+
+ // Send 100 packets.
+ const size_t kNumSentPackets = 100;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(1)
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(101, type);
+ })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(101 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ } else {
+ ASSERT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(100 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(101);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+}
+
+TEST_P(QuicSentPacketManagerTest, NewRetransmissionTimeout) {
+ QuicConfig client_config;
+ QuicTagVector options;
+ options.push_back(kNRTO);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*send_algorithm_, PacingRate(_))
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillRepeatedly(Return(10 * kDefaultTCPMSS));
+ manager_.SetFromConfig(client_config);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_));
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
+
+ // Send 100 packets.
+ const size_t kNumSentPackets = 100;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(2)
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(101, type);
+ })))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(102, type);
+ })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(102 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ } else {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(100 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(101);
+ RetransmitNextPacket(102);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+
+ // Ack a retransmission and expect no call to OnRetransmissionTimeout.
+ // This will include packets in the lost packet map.
+ QuicPacketNumber largest_acked = QuicPacketNumber(102);
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(true, _, _,
+ Pointwise(PacketNumberEq(), {largest_acked}),
+ /*lost_packets=*/Not(IsEmpty())));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
+ // Packets [1, 99] are considered as lost, although stream frame in packet
+ // 2 is not outstanding.
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(99);
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(102), QuicTime::Delta::Zero(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(102), QuicPacketNumber(103));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+}
+
+TEST_P(QuicSentPacketManagerTest, TwoRetransmissionTimeoutsAckSecond) {
+ // Send 1 packet.
+ SendDataPacket(1);
+
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(2, type); })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(2 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ } else {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(2);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+
+ // Rto a second time.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(3, type); })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(3 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ } else {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(2 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(3);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+
+ // Ack a retransmission and ensure OnRetransmissionTimeout is called.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ ExpectAck(2);
+ manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Zero(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ // The original packet and newest should be outstanding.
+ EXPECT_EQ(2 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, TwoRetransmissionTimeoutsAckFirst) {
+ // Send 1 packet.
+ SendDataPacket(1);
+
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(2, type); })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(2 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ } else {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(2);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+
+ // Rto a second time.
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(3, type); })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_EQ(3 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ } else {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(2 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(3);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ }
+
+ // Ack a retransmission and ensure OnRetransmissionTimeout is called.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ ExpectAck(3);
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Zero(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ // The first two packets should still be outstanding.
+ EXPECT_EQ(2 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, GetTransmissionTime) {
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_P(QuicSentPacketManagerTest, GetTransmissionTimeCryptoHandshake) {
+ QuicTime crypto_packet_send_time = clock_.Now();
+ SendCryptoPacket(1);
+
+ // Check the min.
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(1));
+ EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromMilliseconds(10),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(100));
+
+ QuicTime::Delta srtt = rtt_stats->initial_rtt();
+ QuicTime expected_time = clock_.Now() + 1.5 * srtt;
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(1.5 * srtt);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); }));
+ // When session decides what to write, crypto_packet_send_time gets updated.
+ crypto_packet_send_time = clock_.Now();
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(2);
+ }
+
+ // The retransmission time should now be twice as far in the future.
+ expected_time = crypto_packet_send_time + srtt * 2 * 1.5;
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet for the 2nd time.
+ clock_.AdvanceTime(2 * 1.5 * srtt);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(3); }));
+ // When session decides what to write, crypto_packet_send_time gets updated.
+ crypto_packet_send_time = clock_.Now();
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(3);
+ }
+
+ // Verify exponential backoff of the retransmission timeout.
+ expected_time = crypto_packet_send_time + srtt * 4 * 1.5;
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_P(QuicSentPacketManagerTest,
+ GetConservativeTransmissionTimeCryptoHandshake) {
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kCONH);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ // Calling SetFromConfig requires mocking out some send algorithm methods.
+ EXPECT_CALL(*send_algorithm_, PacingRate(_))
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillRepeatedly(Return(10 * kDefaultTCPMSS));
+
+ QuicTime crypto_packet_send_time = clock_.Now();
+ SendCryptoPacket(1);
+
+ // Check the min.
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(1));
+ EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromMilliseconds(25),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(100));
+
+ QuicTime::Delta srtt = rtt_stats->initial_rtt();
+ QuicTime expected_time = clock_.Now() + 2 * srtt;
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(2 * srtt);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(2); }));
+ crypto_packet_send_time = clock_.Now();
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(2);
+ }
+
+ // The retransmission time should now be twice as far in the future.
+ expected_time = crypto_packet_send_time + srtt * 2 * 2;
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_P(QuicSentPacketManagerTest, GetTransmissionTimeTailLossProbe) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ // Check the min.
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(1));
+ EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromMilliseconds(10),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ rtt_stats->set_initial_rtt(QuicTime::Delta::FromMilliseconds(100));
+ QuicTime::Delta srtt = rtt_stats->initial_rtt();
+ QuicTime::Delta expected_tlp_delay = 2 * srtt;
+ QuicTime expected_time = clock_.Now() + expected_tlp_delay;
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(expected_tlp_delay);
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now()));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(3, type); })));
+ }
+ EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+ if (!manager_.session_decides_what_to_write()) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ }
+ EXPECT_CALL(*send_algorithm_, CanSend(_)).WillOnce(Return(false));
+ EXPECT_EQ(QuicTime::Delta::Infinite(), manager_.TimeUntilSend(clock_.Now()));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ expected_time = clock_.Now() + expected_tlp_delay;
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_P(QuicSentPacketManagerTest, GetTransmissionTimeSpuriousRTO) {
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendDataPacket(3);
+ SendDataPacket(4);
+
+ QuicTime::Delta expected_rto_delay =
+ rtt_stats->smoothed_rtt() + 4 * rtt_stats->mean_deviation();
+ QuicTime expected_time = clock_.Now() + expected_rto_delay;
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(expected_rto_delay);
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .Times(2)
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(5, type); })))
+ .WillOnce(WithArgs<1>(Invoke(
+ [this](TransmissionType type) { RetransmitDataPacket(6, type); })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ // All packets are still considered inflight.
+ EXPECT_EQ(4 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(5);
+ RetransmitNextPacket(6);
+ }
+ // All previous packets are inflight, plus two rto retransmissions.
+ EXPECT_EQ(6 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The delay should double the second time.
+ expected_time = clock_.Now() + expected_rto_delay + expected_rto_delay;
+ // Once we always base the timer on the right edge, leaving the older packets
+ // in flight doesn't change the timeout.
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Ack a packet before the first RTO and ensure the RTO timeout returns to the
+ // original value and OnRetransmissionTimeout is not called or reverted.
+ ExpectAck(2);
+ manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(5 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+
+ // Wait 2RTTs from now for the RTO, since it's the max of the RTO time
+ // and the TLP time. In production, there would always be two TLP's first.
+ // Since retransmission was spurious, smoothed_rtt_ is expired, and replaced
+ // by the latest RTT sample of 500ms.
+ expected_time = clock_.Now() + QuicTime::Delta::FromMilliseconds(1000);
+ // Once we always base the timer on the right edge, leaving the older packets
+ // in flight doesn't change the timeout.
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_P(QuicSentPacketManagerTest, GetTransmissionDelayMin) {
+ SendDataPacket(1);
+ // Provide a 1ms RTT sample.
+ const_cast<RttStats*>(manager_.GetRttStats())
+ ->UpdateRtt(QuicTime::Delta::FromMilliseconds(1), QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(200);
+
+ // If the delay is smaller than the min, ensure it exponentially backs off
+ // from the min.
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, i));
+ delay = delay + delay;
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this, i](TransmissionType type) {
+ RetransmitDataPacket(i + 2, type);
+ })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(i + 2);
+ }
+ }
+}
+
+TEST_P(QuicSentPacketManagerTest, GetTransmissionDelayMax) {
+ SendDataPacket(1);
+ // Provide a 60s RTT sample.
+ const_cast<RttStats*>(manager_.GetRttStats())
+ ->UpdateRtt(QuicTime::Delta::FromSeconds(60), QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(60),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(60),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0));
+}
+
+TEST_P(QuicSentPacketManagerTest, GetTransmissionDelayExponentialBackoff) {
+ SendDataPacket(1);
+ QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(500);
+
+ // Delay should back off exponentially.
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, i));
+ delay = delay + delay;
+ if (manager_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this, i](TransmissionType type) {
+ RetransmitDataPacket(i + 2, type);
+ })));
+ }
+ manager_.OnRetransmissionTimeout();
+ if (!manager_.session_decides_what_to_write()) {
+ RetransmitNextPacket(i + 2);
+ }
+ }
+}
+
+TEST_P(QuicSentPacketManagerTest, RetransmissionDelay) {
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ const int64_t kRttMs = 250;
+ const int64_t kDeviationMs = 5;
+
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(kRttMs),
+ QuicTime::Delta::Zero(), clock_.Now());
+
+ // Initial value is to set the median deviation to half of the initial rtt,
+ // the median in then multiplied by a factor of 4 and finally the smoothed rtt
+ // is added which is the initial rtt.
+ QuicTime::Delta expected_delay =
+ QuicTime::Delta::FromMilliseconds(kRttMs + kRttMs / 2 * 4);
+ EXPECT_EQ(expected_delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ EXPECT_EQ(expected_delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0));
+
+ for (int i = 0; i < 100; ++i) {
+ // Run to make sure that we converge.
+ rtt_stats->UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs),
+ QuicTime::Delta::Zero(), clock_.Now());
+ rtt_stats->UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs),
+ QuicTime::Delta::Zero(), clock_.Now());
+ }
+ expected_delay = QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs * 4);
+
+ EXPECT_NEAR(kRttMs, rtt_stats->smoothed_rtt().ToMilliseconds(), 1);
+ EXPECT_NEAR(expected_delay.ToMilliseconds(),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_)
+ .ToMilliseconds(),
+ 1);
+ EXPECT_EQ(QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, GetLossDelay) {
+ auto loss_algorithm = QuicMakeUnique<MockLossAlgorithm>();
+ QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm.get());
+
+ EXPECT_CALL(*loss_algorithm, GetLossTimeout())
+ .WillRepeatedly(Return(QuicTime::Zero()));
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ // Handle an ack which causes the loss algorithm to be evaluated and
+ // set the loss timeout.
+ ExpectAck(2);
+ EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
+ manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ QuicTime timeout(clock_.Now() + QuicTime::Delta::FromMilliseconds(10));
+ EXPECT_CALL(*loss_algorithm, GetLossTimeout())
+ .WillRepeatedly(Return(timeout));
+ EXPECT_EQ(timeout, manager_.GetRetransmissionTime());
+
+ // Fire the retransmission timeout and ensure the loss detection algorithm
+ // is invoked.
+ EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
+ manager_.OnRetransmissionTimeout();
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateTimeLossDetectionFromOptions) {
+ EXPECT_EQ(kNack, QuicSentPacketManagerPeer::GetLossAlgorithm(&manager_)
+ ->GetLossDetectionType());
+
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kTIME);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+
+ EXPECT_EQ(kTime, QuicSentPacketManagerPeer::GetLossAlgorithm(&manager_)
+ ->GetLossDetectionType());
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateCongestionControlFromOptions) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(kRENO);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+ ->GetCongestionControlType());
+
+ options.clear();
+ options.push_back(kTBBR);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kBBR, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+ ->GetCongestionControlType());
+
+ options.clear();
+ options.push_back(kBYTE);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kCubicBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+ ->GetCongestionControlType());
+ options.clear();
+ options.push_back(kRENO);
+ options.push_back(kBYTE);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+ ->GetCongestionControlType());
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateClientCongestionControlFromOptions) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ // No change if the server receives client options.
+ const SendAlgorithmInterface* mock_sender =
+ QuicSentPacketManagerPeer::GetSendAlgorithm(manager_);
+ options.push_back(kRENO);
+ config.SetClientConnectionOptions(options);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(mock_sender, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_));
+
+ // Change the congestion control on the client with client options.
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+ ->GetCongestionControlType());
+
+ options.clear();
+ options.push_back(kTBBR);
+ config.SetClientConnectionOptions(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kBBR, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+ ->GetCongestionControlType());
+
+ options.clear();
+ options.push_back(kBYTE);
+ config.SetClientConnectionOptions(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kCubicBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+ ->GetCongestionControlType());
+
+ options.clear();
+ options.push_back(kRENO);
+ options.push_back(kBYTE);
+ config.SetClientConnectionOptions(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+ ->GetCongestionControlType());
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNumConnectionsFromOptions) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(k1CON);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetNumEmulatedConnections(1));
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ QuicConfig client_config;
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetNumEmulatedConnections(1));
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(client_config);
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNoMinTLPFromOptionsAtServer) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(kMAD2);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*send_algorithm_, PacingRate(_))
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(10 * kDefaultTCPMSS));
+ manager_.SetFromConfig(config);
+ // Set the initial RTT to 1us.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt(
+ QuicTime::Delta::FromMicroseconds(1));
+ // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(200ms).
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+
+ // Send two packets, and the TLP should be 2 us.
+ SendDataPacket(1);
+ SendDataPacket(2);
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNoMinTLPFromOptionsAtClient) {
+ QuicConfig client_config;
+ QuicTagVector options;
+
+ options.push_back(kMAD2);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*send_algorithm_, PacingRate(_))
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(10 * kDefaultTCPMSS));
+ manager_.SetFromConfig(client_config);
+ // Set the initial RTT to 1us.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt(
+ QuicTime::Delta::FromMicroseconds(1));
+ // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(200ms).
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(100002),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+ // Send two packets, and the TLP should be 2 us.
+ SendDataPacket(1);
+ SendDataPacket(2);
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtServer) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(kMAD4);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+ // Provide an RTT measurement of 100ms.
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // Expect 1.5x * SRTT + 0ms MAD
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(150),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(150),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+ // Expect 1.5x * SRTT + 50ms MAD
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(150),
+ QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats->smoothed_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateIETFTLPFromOptionsAtClient) {
+ QuicConfig client_config;
+ QuicTagVector options;
+
+ options.push_back(kMAD4);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(client_config);
+ // Provide an RTT measurement of 100ms.
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ // Expect 1.5x * SRTT + 0ms MAD
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(150),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(150),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+ // Expect 1.5x * SRTT + 50ms MAD
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(150),
+ QuicTime::Delta::FromMilliseconds(50), QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats->smoothed_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(200),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNoMinRTOFromOptionsAtServer) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(kMAD3);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+ // Provide one RTT measurement, because otherwise we use the default of 500ms.
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMicroseconds(1),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0));
+ // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(0ms).
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNoMinRTOFromOptionsAtClient) {
+ QuicConfig client_config;
+ QuicTagVector options;
+
+ options.push_back(kMAD3);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(client_config);
+ // Provide one RTT measurement, because otherwise we use the default of 500ms.
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMicroseconds(1),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_, 0));
+ // The TLP with fewer than 2 packets outstanding includes 1/2 min RTO(0ms).
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_));
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicSentPacketManagerPeer::GetTailLossProbeDelay(&manager_, 0));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNoTLPFromOptionsAtServer) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(kNTLP);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetMaxTailLossProbes(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNoTLPFromOptionsAtClient) {
+ QuicConfig client_config;
+ QuicTagVector options;
+
+ options.push_back(kNTLP);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(client_config);
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetMaxTailLossProbes(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, Negotiate1TLPFromOptionsAtServer) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(k1TLP);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(1u, QuicSentPacketManagerPeer::GetMaxTailLossProbes(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, Negotiate1TLPFromOptionsAtClient) {
+ QuicConfig client_config;
+ QuicTagVector options;
+
+ options.push_back(k1TLP);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(client_config);
+ EXPECT_EQ(1u, QuicSentPacketManagerPeer::GetMaxTailLossProbes(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateTLPRttFromOptionsAtServer) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(kTLPR);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+ EXPECT_TRUE(
+ QuicSentPacketManagerPeer::GetEnableHalfRttTailLossProbe(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateTLPRttFromOptionsAtClient) {
+ QuicConfig client_config;
+ QuicTagVector options;
+
+ options.push_back(kTLPR);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(client_config);
+ EXPECT_TRUE(
+ QuicSentPacketManagerPeer::GetEnableHalfRttTailLossProbe(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNewRTOFromOptionsAtServer) {
+ EXPECT_FALSE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_));
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(kNRTO);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, NegotiateNewRTOFromOptionsAtClient) {
+ EXPECT_FALSE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_));
+ QuicConfig client_config;
+ QuicTagVector options;
+
+ options.push_back(kNRTO);
+ QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(client_config);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::GetUseNewRto(&manager_));
+}
+
+TEST_P(QuicSentPacketManagerTest, UseInitialRoundTripTimeToSend) {
+ QuicTime::Delta initial_rtt = QuicTime::Delta::FromMilliseconds(325);
+ EXPECT_NE(initial_rtt, manager_.GetRttStats()->smoothed_rtt());
+
+ QuicConfig config;
+ config.SetInitialRoundTripTimeUsToSend(initial_rtt.ToMicroseconds());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+ manager_.SetFromConfig(config);
+
+ EXPECT_EQ(QuicTime::Delta::Zero(), manager_.GetRttStats()->smoothed_rtt());
+ EXPECT_EQ(initial_rtt, manager_.GetRttStats()->initial_rtt());
+}
+
+TEST_P(QuicSentPacketManagerTest, ResumeConnectionState) {
+ // The sent packet manager should use the RTT from CachedNetworkParameters if
+ // it is provided.
+ const QuicTime::Delta kRtt = QuicTime::Delta::FromMilliseconds(1234);
+ CachedNetworkParameters cached_network_params;
+ cached_network_params.set_min_rtt_ms(kRtt.ToMilliseconds());
+
+ EXPECT_CALL(*send_algorithm_,
+ AdjustNetworkParameters(QuicBandwidth::Zero(), kRtt));
+ manager_.ResumeConnectionState(cached_network_params, false);
+ EXPECT_EQ(kRtt, manager_.GetRttStats()->initial_rtt());
+}
+
+TEST_P(QuicSentPacketManagerTest, ConnectionMigrationUnspecifiedChange) {
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ QuicTime::Delta default_init_rtt = rtt_stats->initial_rtt();
+ rtt_stats->set_initial_rtt(default_init_rtt * 2);
+ EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt());
+
+ QuicSentPacketManagerPeer::SetConsecutiveRtoCount(&manager_, 1);
+ EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount());
+ QuicSentPacketManagerPeer::SetConsecutiveTlpCount(&manager_, 2);
+ EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount());
+
+ EXPECT_CALL(*send_algorithm_, OnConnectionMigration());
+ manager_.OnConnectionMigration(IPV4_TO_IPV4_CHANGE);
+
+ EXPECT_EQ(default_init_rtt, rtt_stats->initial_rtt());
+ EXPECT_EQ(0u, manager_.GetConsecutiveRtoCount());
+ EXPECT_EQ(0u, manager_.GetConsecutiveTlpCount());
+}
+
+TEST_P(QuicSentPacketManagerTest, ConnectionMigrationIPSubnetChange) {
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ QuicTime::Delta default_init_rtt = rtt_stats->initial_rtt();
+ rtt_stats->set_initial_rtt(default_init_rtt * 2);
+ EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt());
+
+ QuicSentPacketManagerPeer::SetConsecutiveRtoCount(&manager_, 1);
+ EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount());
+ QuicSentPacketManagerPeer::SetConsecutiveTlpCount(&manager_, 2);
+ EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount());
+
+ manager_.OnConnectionMigration(IPV4_SUBNET_CHANGE);
+
+ EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt());
+ EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount());
+ EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount());
+}
+
+TEST_P(QuicSentPacketManagerTest, ConnectionMigrationPortChange) {
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ QuicTime::Delta default_init_rtt = rtt_stats->initial_rtt();
+ rtt_stats->set_initial_rtt(default_init_rtt * 2);
+ EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt());
+
+ QuicSentPacketManagerPeer::SetConsecutiveRtoCount(&manager_, 1);
+ EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount());
+ QuicSentPacketManagerPeer::SetConsecutiveTlpCount(&manager_, 2);
+ EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount());
+
+ manager_.OnConnectionMigration(PORT_CHANGE);
+
+ EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt());
+ EXPECT_EQ(1u, manager_.GetConsecutiveRtoCount());
+ EXPECT_EQ(2u, manager_.GetConsecutiveTlpCount());
+}
+
+TEST_P(QuicSentPacketManagerTest, PathMtuIncreased) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), QuicPacketNumber(1), _, _));
+ SerializedPacket packet(QuicPacketNumber(1), PACKET_4BYTE_PACKET_NUMBER,
+ nullptr, kDefaultLength + 100, false, false);
+ manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+
+ // Ack the large packet and expect the path MTU to increase.
+ ExpectAck(1);
+ EXPECT_CALL(*network_change_visitor_,
+ OnPathMtuIncreased(kDefaultLength + 100));
+ QuicAckFrame ack_frame = InitAckFrame(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(), ENCRYPTION_INITIAL));
+}
+
+TEST_P(QuicSentPacketManagerTest, OnAckRangeSlowPath) {
+ // Send packets 1 - 20.
+ for (size_t i = 1; i <= 20; ++i) {
+ SendDataPacket(i);
+ }
+ // Ack [5, 7), [10, 12), [15, 17).
+ uint64_t acked1[] = {5, 6, 10, 11, 15, 16};
+ uint64_t lost1[] = {1, 2, 3, 4, 7, 8, 9, 12, 13};
+ ExpectAcksAndLosses(true, acked1, QUIC_ARRAYSIZE(acked1), lost1,
+ QUIC_ARRAYSIZE(lost1));
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(AnyNumber());
+ manager_.OnAckFrameStart(QuicPacketNumber(16), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(15), QuicPacketNumber(17));
+ manager_.OnAckRange(QuicPacketNumber(10), QuicPacketNumber(12));
+ manager_.OnAckRange(QuicPacketNumber(5), QuicPacketNumber(7));
+ // Make sure empty range does not harm.
+ manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ // Ack [4, 8), [9, 13), [14, 21).
+ uint64_t acked2[] = {4, 7, 9, 12, 14, 17, 18, 19, 20};
+ ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(20), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(14), QuicPacketNumber(21));
+ manager_.OnAckRange(QuicPacketNumber(9), QuicPacketNumber(13));
+ manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(8));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+}
+
+TEST_P(QuicSentPacketManagerTest, TolerateReneging) {
+ if (!manager_.tolerate_reneging()) {
+ return;
+ }
+ // Send packets 1 - 20.
+ for (size_t i = 1; i <= 20; ++i) {
+ SendDataPacket(i);
+ }
+ // Ack [5, 7), [10, 12), [15, 17).
+ uint64_t acked1[] = {5, 6, 10, 11, 15, 16};
+ uint64_t lost1[] = {1, 2, 3, 4, 7, 8, 9, 12, 13};
+ ExpectAcksAndLosses(true, acked1, QUIC_ARRAYSIZE(acked1), lost1,
+ QUIC_ARRAYSIZE(lost1));
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(AnyNumber());
+ manager_.OnAckFrameStart(QuicPacketNumber(16), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(15), QuicPacketNumber(17));
+ manager_.OnAckRange(QuicPacketNumber(10), QuicPacketNumber(12));
+ manager_.OnAckRange(QuicPacketNumber(5), QuicPacketNumber(7));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ // Making sure reneged ACK does not harm. Ack [4, 8), [9, 13).
+ uint64_t acked2[] = {4, 7, 9, 12};
+ ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(12), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(9), QuicPacketNumber(13));
+ manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(8));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(16), manager_.GetLargestObserved());
+}
+
+TEST_P(QuicSentPacketManagerTest, MultiplePacketNumberSpaces) {
+ if (!GetQuicReloadableFlag(quic_use_uber_loss_algorithm)) {
+ return;
+ }
+ manager_.EnableMultiplePacketNumberSpacesSupport();
+ EXPECT_FALSE(
+ manager_.GetLargestSentPacket(ENCRYPTION_INITIAL).IsInitialized());
+ EXPECT_FALSE(
+ manager_.GetLargestAckedPacket(ENCRYPTION_INITIAL).IsInitialized());
+ // Send packet 1.
+ SendDataPacket(1, ENCRYPTION_INITIAL);
+ EXPECT_EQ(QuicPacketNumber(1),
+ manager_.GetLargestSentPacket(ENCRYPTION_INITIAL));
+ EXPECT_FALSE(
+ manager_.GetLargestSentPacket(ENCRYPTION_HANDSHAKE).IsInitialized());
+ // Ack packet 1.
+ 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(), ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(1),
+ manager_.GetLargestAckedPacket(ENCRYPTION_INITIAL));
+ EXPECT_FALSE(
+ manager_.GetLargestAckedPacket(ENCRYPTION_HANDSHAKE).IsInitialized());
+ // Send packets 2 and 3.
+ SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+ SendDataPacket(3, ENCRYPTION_HANDSHAKE);
+ EXPECT_EQ(QuicPacketNumber(1),
+ manager_.GetLargestSentPacket(ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(3),
+ manager_.GetLargestSentPacket(ENCRYPTION_HANDSHAKE));
+ EXPECT_FALSE(
+ manager_.GetLargestSentPacket(ENCRYPTION_ZERO_RTT).IsInitialized());
+ // Ack packet 2.
+ ExpectAck(2);
+ manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(2),
+ manager_.GetLargestAckedPacket(ENCRYPTION_HANDSHAKE));
+ EXPECT_FALSE(
+ manager_.GetLargestAckedPacket(ENCRYPTION_ZERO_RTT).IsInitialized());
+ // Ack packet 3.
+ ExpectAck(3);
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(3),
+ manager_.GetLargestAckedPacket(ENCRYPTION_HANDSHAKE));
+ EXPECT_FALSE(
+ manager_.GetLargestAckedPacket(ENCRYPTION_ZERO_RTT).IsInitialized());
+ // Send packets 4 and 5.
+ SendDataPacket(4, ENCRYPTION_ZERO_RTT);
+ SendDataPacket(5, ENCRYPTION_ZERO_RTT);
+ EXPECT_EQ(QuicPacketNumber(1),
+ manager_.GetLargestSentPacket(ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(3),
+ manager_.GetLargestSentPacket(ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(5),
+ manager_.GetLargestSentPacket(ENCRYPTION_ZERO_RTT));
+ EXPECT_EQ(QuicPacketNumber(5),
+ manager_.GetLargestSentPacket(ENCRYPTION_FORWARD_SECURE));
+ // Ack packet 5.
+ ExpectAck(5);
+ manager_.OnAckFrameStart(QuicPacketNumber(5), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(5), QuicPacketNumber(6));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_FORWARD_SECURE));
+ EXPECT_EQ(QuicPacketNumber(3),
+ manager_.GetLargestAckedPacket(ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(5),
+ manager_.GetLargestAckedPacket(ENCRYPTION_ZERO_RTT));
+ EXPECT_EQ(QuicPacketNumber(5),
+ manager_.GetLargestAckedPacket(ENCRYPTION_FORWARD_SECURE));
+
+ // Send packets 6 - 8.
+ SendDataPacket(6, ENCRYPTION_FORWARD_SECURE);
+ SendDataPacket(7, ENCRYPTION_FORWARD_SECURE);
+ SendDataPacket(8, ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(QuicPacketNumber(1),
+ manager_.GetLargestSentPacket(ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(3),
+ manager_.GetLargestSentPacket(ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(8),
+ manager_.GetLargestSentPacket(ENCRYPTION_ZERO_RTT));
+ EXPECT_EQ(QuicPacketNumber(8),
+ manager_.GetLargestSentPacket(ENCRYPTION_FORWARD_SECURE));
+ // Ack all packets.
+ uint64_t acked[] = {4, 6, 7, 8};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(8), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(4), QuicPacketNumber(9));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_FORWARD_SECURE));
+ EXPECT_EQ(QuicPacketNumber(3),
+ manager_.GetLargestAckedPacket(ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(8),
+ manager_.GetLargestAckedPacket(ENCRYPTION_ZERO_RTT));
+ EXPECT_EQ(QuicPacketNumber(8),
+ manager_.GetLargestAckedPacket(ENCRYPTION_FORWARD_SECURE));
+}
+
+TEST_P(QuicSentPacketManagerTest, PacketsGetAckedInWrongPacketNumberSpace) {
+ if (!GetQuicReloadableFlag(quic_use_uber_loss_algorithm)) {
+ return;
+ }
+ manager_.EnableMultiplePacketNumberSpacesSupport();
+ // Send packet 1.
+ SendDataPacket(1, ENCRYPTION_INITIAL);
+ // Send packets 2 and 3.
+ SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+ SendDataPacket(3, ENCRYPTION_HANDSHAKE);
+
+ // ACK packets 2 and 3 in the wrong packet number space.
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+}
+
+TEST_P(QuicSentPacketManagerTest, PacketsGetAckedInWrongPacketNumberSpace2) {
+ if (!GetQuicReloadableFlag(quic_use_uber_loss_algorithm)) {
+ return;
+ }
+ manager_.EnableMultiplePacketNumberSpacesSupport();
+ // Send packet 1.
+ SendDataPacket(1, ENCRYPTION_INITIAL);
+ // Send packets 2 and 3.
+ SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+ SendDataPacket(3, ENCRYPTION_HANDSHAKE);
+
+ // ACK packet 1 in the wrong packet number space.
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_HANDSHAKE));
+}
+
+TEST_P(QuicSentPacketManagerTest,
+ ToleratePacketsGetAckedInWrongPacketNumberSpace) {
+ if (!GetQuicReloadableFlag(quic_use_uber_loss_algorithm)) {
+ return;
+ }
+ manager_.EnableMultiplePacketNumberSpacesSupport();
+ // Send packet 1.
+ SendDataPacket(1, ENCRYPTION_INITIAL);
+ // Ack packet 1.
+ 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(), ENCRYPTION_INITIAL));
+
+ // Send packets 2 and 3.
+ SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+ SendDataPacket(3, ENCRYPTION_HANDSHAKE);
+
+ // Packet 1 gets acked in the wrong packet number space. Since packet 1 has
+ // been acked in the correct packet number space, tolerate it.
+ uint64_t acked[] = {2, 3};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(3), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(4));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_HANDSHAKE));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..6a4e56f88b9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_server_id.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+
+#include <string>
+#include <tuple>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+QuicServerId::QuicServerId() : QuicServerId("", 0, false) {}
+
+QuicServerId::QuicServerId(const std::string& host, uint16_t port)
+ : QuicServerId(host, port, false) {}
+
+QuicServerId::QuicServerId(const std::string& host,
+ uint16_t port,
+ bool privacy_mode_enabled)
+ : host_(host), port_(port), privacy_mode_enabled_(privacy_mode_enabled) {}
+
+QuicServerId::~QuicServerId() {}
+
+bool QuicServerId::operator<(const QuicServerId& other) const {
+ return std::tie(port_, host_, privacy_mode_enabled_) <
+ std::tie(other.port_, other.host_, other.privacy_mode_enabled_);
+}
+
+bool QuicServerId::operator==(const QuicServerId& other) const {
+ return privacy_mode_enabled_ == other.privacy_mode_enabled_ &&
+ host_ == other.host_ && port_ == other.port_;
+}
+
+size_t QuicServerId::EstimateMemoryUsage() const {
+ return QuicEstimateMemoryUsage(host_);
+}
+
+} // 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
new file mode 100644
index 00000000000..7adeb86b817
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_server_id.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_SERVER_ID_H_
+#define QUICHE_QUIC_CORE_QUIC_SERVER_ID_H_
+
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// The id used to identify sessions. Includes the hostname, port, scheme and
+// privacy_mode.
+class QUIC_EXPORT_PRIVATE QuicServerId {
+ public:
+ QuicServerId();
+ QuicServerId(const std::string& host, uint16_t port);
+ QuicServerId(const std::string& host,
+ uint16_t port,
+ bool privacy_mode_enabled);
+ ~QuicServerId();
+
+ // Needed to be an element of std::set.
+ 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_;
+ bool privacy_mode_enabled_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_SERVER_ID_H_
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
new file mode 100644
index 00000000000..cb519b85d17
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_server_id_test.cc
@@ -0,0 +1,129 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+
+namespace {
+
+class QuicServerIdTest : public QuicTest {};
+
+TEST_F(QuicServerIdTest, Constructor) {
+ QuicServerId google_server_id("google.com", 10, false);
+ EXPECT_EQ("google.com", google_server_id.host());
+ EXPECT_EQ(10, google_server_id.port());
+ EXPECT_FALSE(google_server_id.privacy_mode_enabled());
+
+ QuicServerId private_server_id("mail.google.com", 12, true);
+ EXPECT_EQ("mail.google.com", private_server_id.host());
+ EXPECT_EQ(12, private_server_id.port());
+ EXPECT_TRUE(private_server_id.privacy_mode_enabled());
+}
+
+TEST_F(QuicServerIdTest, LessThan) {
+ QuicServerId a_10_https("a.com", 10, false);
+ QuicServerId a_11_https("a.com", 11, false);
+ QuicServerId b_10_https("b.com", 10, false);
+ QuicServerId b_11_https("b.com", 11, false);
+
+ QuicServerId a_10_https_private("a.com", 10, true);
+ QuicServerId a_11_https_private("a.com", 11, true);
+ QuicServerId b_10_https_private("b.com", 10, true);
+ QuicServerId b_11_https_private("b.com", 11, true);
+
+ // Test combinations of host, port, and privacy being same on left and
+ // right side of less than.
+ EXPECT_FALSE(a_10_https < a_10_https);
+ EXPECT_TRUE(a_10_https < a_10_https_private);
+ EXPECT_FALSE(a_10_https_private < a_10_https);
+ EXPECT_FALSE(a_10_https_private < a_10_https_private);
+
+ // Test with either host, port or https being different on left and right side
+ // of less than.
+ bool left_privacy;
+ bool right_privacy;
+ for (int i = 0; i < 4; i++) {
+ left_privacy = (i / 2 == 0);
+ right_privacy = (i % 2 == 0);
+ QuicServerId a_10_https_left_private("a.com", 10, left_privacy);
+ QuicServerId a_10_https_right_private("a.com", 10, right_privacy);
+ QuicServerId a_11_https_left_private("a.com", 11, left_privacy);
+ QuicServerId a_11_https_right_private("a.com", 11, right_privacy);
+
+ QuicServerId b_10_https_left_private("b.com", 10, left_privacy);
+ QuicServerId b_10_https_right_private("b.com", 10, right_privacy);
+ QuicServerId b_11_https_left_private("b.com", 11, left_privacy);
+ QuicServerId b_11_https_right_private("b.com", 11, right_privacy);
+
+ EXPECT_TRUE(a_10_https_left_private < a_11_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_10_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_11_https_right_private);
+ EXPECT_FALSE(a_11_https_left_private < a_10_https_right_private);
+ EXPECT_FALSE(a_11_https_left_private < b_10_https_right_private);
+ EXPECT_TRUE(a_11_https_left_private < b_11_https_right_private);
+ EXPECT_FALSE(b_10_https_left_private < a_10_https_right_private);
+ EXPECT_TRUE(b_10_https_left_private < a_11_https_right_private);
+ EXPECT_TRUE(b_10_https_left_private < b_11_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_10_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_11_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < b_10_https_right_private);
+ }
+}
+
+TEST_F(QuicServerIdTest, Equals) {
+ bool left_privacy;
+ bool right_privacy;
+ for (int i = 0; i < 2; i++) {
+ left_privacy = right_privacy = (i == 0);
+ QuicServerId a_10_https_right_private("a.com", 10, right_privacy);
+ QuicServerId a_11_https_right_private("a.com", 11, right_privacy);
+ QuicServerId b_10_https_right_private("b.com", 10, right_privacy);
+ QuicServerId b_11_https_right_private("b.com", 11, right_privacy);
+
+ 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);
+ QuicServerId new_b_11_https_left_private("b.com", 11, left_privacy);
+
+ EXPECT_EQ(new_a_10_https_left_private, a_10_https_right_private);
+ EXPECT_EQ(new_a_11_https_left_private, a_11_https_right_private);
+ EXPECT_EQ(new_b_10_https_left_private, b_10_https_right_private);
+ EXPECT_EQ(new_b_11_https_left_private, b_11_https_right_private);
+ }
+
+ for (int i = 0; i < 2; i++) {
+ right_privacy = (i == 0);
+ QuicServerId a_10_https_right_private("a.com", 10, right_privacy);
+ QuicServerId a_11_https_right_private("a.com", 11, right_privacy);
+ QuicServerId b_10_https_right_private("b.com", 10, right_privacy);
+ QuicServerId b_11_https_right_private("b.com", 11, right_privacy);
+
+ 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);
+ }
+ 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));
+}
+
+} // namespace
+
+} // namespace quic
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
new file mode 100644
index 00000000000..36070a4c5b5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_session.cc
@@ -0,0 +1,1699 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_server_stats.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+using spdy::SpdyPriority;
+
+namespace quic {
+
+namespace {
+
+class ClosedStreamsCleanUpDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit ClosedStreamsCleanUpDelegate(QuicSession* session)
+ : session_(session) {}
+ ClosedStreamsCleanUpDelegate(const ClosedStreamsCleanUpDelegate&) = delete;
+ ClosedStreamsCleanUpDelegate& operator=(const ClosedStreamsCleanUpDelegate&) =
+ delete;
+
+ void OnAlarm() override { session_->CleanUpClosedStreams(); }
+
+ private:
+ QuicSession* session_;
+};
+
+} // namespace
+
+#define ENDPOINT \
+ (perspective() == Perspective::IS_SERVER ? "Server: " : "Client: ")
+
+QuicSession::QuicSession(QuicConnection* connection,
+ Visitor* owner,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions)
+ : connection_(connection),
+ visitor_(owner),
+ write_blocked_streams_(),
+ config_(config),
+ stream_id_manager_(this,
+ kDefaultMaxStreamsPerConnection,
+ config_.GetMaxIncomingDynamicStreamsToSend()),
+ v99_streamid_manager_(this,
+ kDefaultMaxStreamsPerConnection,
+ config_.GetMaxIncomingDynamicStreamsToSend()),
+ num_dynamic_incoming_streams_(0),
+ num_draining_incoming_streams_(0),
+ num_locally_closed_incoming_streams_highest_offset_(0),
+ error_(QUIC_NO_ERROR),
+ flow_controller_(
+ this,
+ QuicUtils::GetInvalidStreamId(connection->transport_version()),
+ /*is_connection_flow_controller*/ true,
+ kMinimumFlowControlSendWindow,
+ config_.GetInitialSessionFlowControlWindowToSend(),
+ kSessionReceiveWindowLimit,
+ perspective() == Perspective::IS_SERVER,
+ nullptr),
+ currently_writing_stream_id_(0),
+ largest_static_stream_id_(0),
+ is_handshake_confirmed_(false),
+ goaway_sent_(false),
+ goaway_received_(false),
+ control_frame_manager_(this),
+ last_message_id_(0),
+ closed_streams_clean_up_alarm_(nullptr),
+ supported_versions_(supported_versions) {
+ closed_streams_clean_up_alarm_ =
+ QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm(
+ new ClosedStreamsCleanUpDelegate(this)));
+}
+
+void QuicSession::Initialize() {
+ connection_->set_visitor(this);
+ connection_->SetSessionNotifier(this);
+ connection_->SetDataProducer(this);
+ connection_->SetFromConfig(config_);
+
+ DCHECK_EQ(QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ GetMutableCryptoStream()->id());
+ RegisterStaticStream(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ GetMutableCryptoStream());
+}
+
+QuicSession::~QuicSession() {
+ QUIC_LOG_IF(WARNING, !zombie_streams_.empty()) << "Still have zombie streams";
+}
+
+void QuicSession::RegisterStaticStream(QuicStreamId id, QuicStream* stream) {
+ static_stream_map_[id] = stream;
+
+ QUIC_BUG_IF(id >
+ largest_static_stream_id_ +
+ QuicUtils::StreamIdDelta(connection_->transport_version()))
+ << ENDPOINT << "Static stream registered out of order: " << id
+ << " vs: " << largest_static_stream_id_;
+ largest_static_stream_id_ = std::max(id, largest_static_stream_id_);
+
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ v99_streamid_manager_.RegisterStaticStream(id);
+ }
+}
+
+void QuicSession::OnStreamFrame(const QuicStreamFrame& frame) {
+ // TODO(rch) deal with the error case of stream id 0.
+ QuicStreamId stream_id = frame.stream_id;
+ if (stream_id ==
+ QuicUtils::GetInvalidStreamId(connection()->transport_version())) {
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received data for an invalid stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ if (frame.fin && QuicContainsKey(static_stream_map_, stream_id)) {
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to close a static stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ StreamHandler handler = GetOrCreateStreamImpl(stream_id, frame.offset != 0);
+ if (handler.is_pending) {
+ handler.pending->OnStreamFrame(frame);
+ return;
+ }
+
+ if (!handler.stream) {
+ // The stream no longer exists, but we may still be interested in the
+ // final stream byte offset sent by the peer. A frame with a FIN can give
+ // us this offset.
+ if (frame.fin) {
+ QuicStreamOffset final_byte_offset = frame.offset + frame.data_length;
+ OnFinalByteOffsetReceived(stream_id, final_byte_offset);
+ }
+ return;
+ }
+ handler.stream->OnStreamFrame(frame);
+}
+
+void QuicSession::OnCryptoFrame(const QuicCryptoFrame& frame) {
+ GetMutableCryptoStream()->OnCryptoFrame(frame);
+}
+
+bool QuicSession::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
+ // We are not version 99. In theory, if not in version 99 then the framer
+ // could not call OnStopSending... This is just a check that is good when
+ // both a new protocol and a new implementation of that protocol are both
+ // being developed.
+ DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version());
+
+ QuicStreamId stream_id = frame.stream_id;
+ // If Stream ID is invalid then close the connection.
+ if (stream_id ==
+ QuicUtils::GetInvalidStreamId(connection()->transport_version())) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Received STOP_SENDING with invalid stream_id: "
+ << stream_id << " Closing connection";
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received STOP_SENDING for an invalid stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ // Ignore STOP_SENDING for static streams.
+ // TODO(fkastenholz): IETF Quic does not have static streams and does not
+ // make exceptions for them with respect to processing things like
+ // STOP_SENDING.
+ if (QuicContainsKey(static_stream_map_, stream_id)) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Received STOP_SENDING for a static stream, id: "
+ << stream_id << " Closing connection";
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received STOP_SENDING for a static stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ if (visitor_) {
+ visitor_->OnStopSendingReceived(frame);
+ }
+
+ // If stream is closed, ignore the frame
+ if (IsClosedStream(stream_id)) {
+ QUIC_DVLOG(1)
+ << ENDPOINT
+ << "Received STOP_SENDING for closed or non-existent stream, id: "
+ << stream_id << " Ignoring.";
+ return true; // Continue processing the packet.
+ }
+ // If stream is non-existent, close the connection
+ DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id);
+ if (it == dynamic_stream_map_.end()) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Received STOP_SENDING for non-existent stream, id: "
+ << stream_id << " Closing connection";
+ connection()->CloseConnection(
+ IETF_QUIC_PROTOCOL_VIOLATION,
+ "Received STOP_SENDING for a non-existent stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ // Get the QuicStream for this stream. Ignore the STOP_SENDING
+ // if the QuicStream pointer is NULL
+ // QUESTION: IS THIS THE RIGHT THING TO DO? (that is, this would happen IFF
+ // there was an entry in the map, but the pointer is null. sounds more like a
+ // deep programming error rather than a simple protocol problem).
+ QuicStream* stream = it->second.get();
+ if (stream == nullptr) {
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Received STOP_SENDING for NULL QuicStream, stream_id: "
+ << stream_id << ". Ignoring.";
+ return true;
+ }
+ stream->OnStopSending(frame.application_error_code);
+
+ stream->set_stream_error(
+ static_cast<QuicRstStreamErrorCode>(frame.application_error_code));
+ SendRstStreamInner(
+ stream->id(),
+ static_cast<quic::QuicRstStreamErrorCode>(frame.application_error_code),
+ stream->stream_bytes_written(),
+ /*close_write_side_only=*/true);
+
+ return true;
+}
+
+void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) {
+ QuicStreamId stream_id = frame.stream_id;
+ if (stream_id ==
+ QuicUtils::GetInvalidStreamId(connection()->transport_version())) {
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received data for an invalid stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ if (QuicContainsKey(static_stream_map_, stream_id)) {
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ if (visitor_) {
+ visitor_->OnRstStreamReceived(frame);
+ }
+
+ // may_buffer is true here to allow subclasses to buffer streams until the
+ // first byte of payload arrives which would allow sessions to delay
+ // creation of the stream until the type is known.
+ StreamHandler handler = GetOrCreateStreamImpl(stream_id, /*may_buffer=*/true);
+ if (handler.is_pending) {
+ handler.pending->OnRstStreamFrame(frame);
+ ClosePendingStream(stream_id);
+ return;
+ }
+ if (!handler.stream) {
+ HandleRstOnValidNonexistentStream(frame);
+ return; // Errors are handled by GetOrCreateStream.
+ }
+ handler.stream->OnStreamReset(frame);
+}
+
+void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) {
+ goaway_received_ = true;
+}
+
+void QuicSession::OnMessageReceived(QuicStringPiece message) {
+ QUIC_DVLOG(1) << ENDPOINT << "Received message, length: " << message.length()
+ << ", " << message;
+}
+
+// static
+void QuicSession::RecordConnectionCloseAtServer(QuicErrorCode error,
+ ConnectionCloseSource source) {
+ if (error != QUIC_NO_ERROR) {
+ if (source == ConnectionCloseSource::FROM_SELF) {
+ QUIC_SERVER_HISTOGRAM_ENUM(
+ "quic_server_connection_close_errors", error, QUIC_LAST_ERROR,
+ "QuicErrorCode for server-closed connections.");
+ } else {
+ QUIC_SERVER_HISTOGRAM_ENUM(
+ "quic_client_connection_close_errors", error, QUIC_LAST_ERROR,
+ "QuicErrorCode for client-closed connections.");
+ }
+ }
+}
+
+void QuicSession::OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {
+ DCHECK(!connection_->connected());
+ if (perspective() == Perspective::IS_SERVER) {
+ RecordConnectionCloseAtServer(error, source);
+ }
+
+ if (error_ == QUIC_NO_ERROR) {
+ error_ = error;
+ }
+
+ while (!dynamic_stream_map_.empty()) {
+ DynamicStreamMap::iterator it = dynamic_stream_map_.begin();
+ QuicStreamId id = it->first;
+ it->second->OnConnectionClosed(error, source);
+ // The stream should call CloseStream as part of OnConnectionClosed.
+ if (dynamic_stream_map_.find(id) != dynamic_stream_map_.end()) {
+ QUIC_BUG << ENDPOINT << "Stream failed to close under OnConnectionClosed";
+ CloseStream(id);
+ }
+ }
+
+ // Cleanup zombie stream map on connection close.
+ while (!zombie_streams_.empty()) {
+ ZombieStreamMap::iterator it = zombie_streams_.begin();
+ closed_streams_.push_back(std::move(it->second));
+ zombie_streams_.erase(it);
+ }
+
+ closed_streams_clean_up_alarm_->Cancel();
+
+ if (visitor_) {
+ visitor_->OnConnectionClosed(connection_->connection_id(), error,
+ error_details, source);
+ }
+}
+
+void QuicSession::OnWriteBlocked() {
+ if (!connection_->connected()) {
+ return;
+ }
+ if (visitor_) {
+ visitor_->OnWriteBlocked(connection_);
+ }
+}
+
+void QuicSession::OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) {
+ GetMutableCryptoStream()->OnSuccessfulVersionNegotiation(version);
+}
+
+void QuicSession::OnConnectivityProbeReceived(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ if (perspective() == Perspective::IS_SERVER) {
+ // Server only sends back a connectivity probe after received a
+ // connectivity probe from a new peer address.
+ connection_->SendConnectivityProbingResponsePacket(peer_address);
+ }
+}
+
+void QuicSession::OnPathDegrading() {}
+
+bool QuicSession::AllowSelfAddressChange() const {
+ return false;
+}
+
+void QuicSession::OnForwardProgressConfirmed() {}
+
+void QuicSession::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
+ // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't
+ // assume that it still exists.
+ QuicStreamId stream_id = frame.stream_id;
+ if (stream_id ==
+ QuicUtils::GetInvalidStreamId(connection_->transport_version())) {
+ // This is a window update that applies to the connection, rather than an
+ // individual stream.
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Received connection level flow control window "
+ "update with byte offset: "
+ << frame.byte_offset;
+ flow_controller_.UpdateSendWindowOffset(frame.byte_offset);
+ return;
+ }
+ QuicStream* stream = GetOrCreateStream(stream_id);
+ if (stream != nullptr) {
+ stream->OnWindowUpdateFrame(frame);
+ }
+}
+
+void QuicSession::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ // TODO(rjshade): Compare our flow control receive windows for specified
+ // streams: if we have a large window then maybe something
+ // had gone wrong with the flow control accounting.
+ QUIC_DLOG(INFO) << ENDPOINT << "Received BLOCKED frame with stream id: "
+ << frame.stream_id;
+}
+
+bool QuicSession::CheckStreamNotBusyLooping(QuicStream* stream,
+ uint64_t previous_bytes_written,
+ bool previous_fin_sent) {
+ if ( // Stream should not be closed.
+ !stream->write_side_closed() &&
+ // Not connection flow control blocked.
+ !flow_controller_.IsBlocked() &&
+ // Detect lack of forward progress.
+ previous_bytes_written == stream->stream_bytes_written() &&
+ previous_fin_sent == stream->fin_sent()) {
+ stream->set_busy_counter(stream->busy_counter() + 1);
+ QUIC_DVLOG(1) << "Suspected busy loop on stream id " << stream->id()
+ << " stream_bytes_written " << stream->stream_bytes_written()
+ << " fin " << stream->fin_sent() << " count "
+ << stream->busy_counter();
+ // Wait a few iterations before firing, the exact count is
+ // arbitrary, more than a few to cover a few test-only false
+ // positives.
+ if (stream->busy_counter() > 20) {
+ QUIC_LOG(ERROR) << "Detected busy loop on stream id " << stream->id()
+ << " stream_bytes_written "
+ << stream->stream_bytes_written() << " fin "
+ << stream->fin_sent();
+ return false;
+ }
+ } else {
+ stream->set_busy_counter(0);
+ }
+ return true;
+}
+
+bool QuicSession::CheckStreamWriteBlocked(QuicStream* stream) const {
+ if (!stream->write_side_closed() && stream->HasBufferedData() &&
+ !stream->flow_controller()->IsBlocked() &&
+ !write_blocked_streams_.IsStreamBlocked(stream->id())) {
+ QUIC_DLOG(ERROR) << "stream " << stream->id() << " has buffered "
+ << stream->BufferedDataBytes()
+ << " bytes, and is not flow control blocked, "
+ "but it is not in the write block list.";
+ return false;
+ }
+ return true;
+}
+
+void QuicSession::OnCanWrite() {
+ if (!RetransmitLostData()) {
+ // Cannot finish retransmitting lost data, connection is write blocked.
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Cannot finish retransmitting lost data, connection is "
+ "write blocked.";
+ return;
+ }
+ if (session_decides_what_to_write()) {
+ SetTransmissionType(NOT_RETRANSMISSION);
+ }
+ // We limit the number of writes to the number of pending streams. If more
+ // streams become pending, WillingAndAbleToWrite will be true, which will
+ // cause the connection to request resumption before yielding to other
+ // connections.
+ // If we are connection level flow control blocked, then only allow the
+ // crypto and headers streams to try writing as all other streams will be
+ // blocked.
+ size_t num_writes = flow_controller_.IsBlocked()
+ ? write_blocked_streams_.NumBlockedSpecialStreams()
+ : write_blocked_streams_.NumBlockedStreams();
+ if (num_writes == 0 && !control_frame_manager_.WillingToWrite()) {
+ return;
+ }
+
+ QuicConnection::ScopedPacketFlusher flusher(
+ connection_, QuicConnection::SEND_ACK_IF_QUEUED);
+ if (control_frame_manager_.WillingToWrite()) {
+ control_frame_manager_.OnCanWrite();
+ }
+ for (size_t i = 0; i < num_writes; ++i) {
+ if (!(write_blocked_streams_.HasWriteBlockedSpecialStream() ||
+ write_blocked_streams_.HasWriteBlockedDataStreams())) {
+ // Writing one stream removed another!? Something's broken.
+ QUIC_BUG << "WriteBlockedStream is missing";
+ connection_->CloseConnection(QUIC_INTERNAL_ERROR,
+ "WriteBlockedStream is missing",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ return;
+ }
+ if (!connection_->CanWriteStreamData()) {
+ return;
+ }
+ currently_writing_stream_id_ = write_blocked_streams_.PopFront();
+ QuicStream* stream = GetOrCreateStream(currently_writing_stream_id_);
+ if (stream != nullptr && !stream->flow_controller()->IsBlocked()) {
+ // If the stream can't write all bytes it'll re-add itself to the blocked
+ // list.
+ uint64_t previous_bytes_written = stream->stream_bytes_written();
+ bool previous_fin_sent = stream->fin_sent();
+ QUIC_DVLOG(1) << "stream " << stream->id() << " bytes_written "
+ << previous_bytes_written << " fin " << previous_fin_sent;
+ stream->OnCanWrite();
+ DCHECK(CheckStreamWriteBlocked(stream));
+ DCHECK(CheckStreamNotBusyLooping(stream, previous_bytes_written,
+ previous_fin_sent));
+ }
+ currently_writing_stream_id_ = 0;
+ }
+}
+
+bool QuicSession::WillingAndAbleToWrite() const {
+ // Schedule a write when:
+ // 1) control frame manager has pending or new control frames, or
+ // 2) any stream has pending retransmissions, or
+ // 3) If the crypto or headers streams are blocked, or
+ // 4) connection is not flow control blocked and there are write blocked
+ // streams.
+ return control_frame_manager_.WillingToWrite() ||
+ !streams_with_pending_retransmission_.empty() ||
+ write_blocked_streams_.HasWriteBlockedSpecialStream() ||
+ (!flow_controller_.IsBlocked() &&
+ write_blocked_streams_.HasWriteBlockedDataStreams());
+}
+
+bool QuicSession::HasPendingHandshake() const {
+ return QuicContainsKey(
+ streams_with_pending_retransmission_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version())) ||
+ write_blocked_streams_.IsStreamBlocked(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+}
+
+uint64_t QuicSession::GetNumOpenDynamicStreams() const {
+ return dynamic_stream_map_.size() - draining_streams_.size() +
+ locally_closed_streams_highest_offset_.size();
+}
+
+void QuicSession::ProcessUdpPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) {
+ connection_->ProcessUdpPacket(self_address, peer_address, packet);
+}
+
+QuicConsumedData QuicSession::WritevData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ // This check is an attempt to deal with potential memory corruption
+ // in which |id| ends up set to 1 (the crypto stream id). If this happen
+ // it might end up resulting in unencrypted stream data being sent.
+ // While this is impossible to avoid given sufficient corruption, this
+ // seems like a reasonable mitigation.
+ if (id == QuicUtils::GetCryptoStreamId(connection_->transport_version()) &&
+ stream != GetMutableCryptoStream()) {
+ QUIC_BUG << "Stream id mismatch";
+ connection_->CloseConnection(
+ QUIC_INTERNAL_ERROR,
+ "Non-crypto stream attempted to write data as crypto stream.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return QuicConsumedData(0, false);
+ }
+ if (!IsEncryptionEstablished() &&
+ id != QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+ // Do not let streams write without encryption. The calling stream will end
+ // up write blocked until OnCanWrite is next called.
+ return QuicConsumedData(0, false);
+ }
+
+ QuicConsumedData data =
+ connection_->SendStreamData(id, write_length, offset, state);
+ if (offset >= stream->stream_bytes_written()) {
+ // This is new stream data.
+ write_blocked_streams_.UpdateBytesForStream(id, data.bytes_consumed);
+ }
+ return data;
+}
+
+bool QuicSession::WriteControlFrame(const QuicFrame& frame) {
+ return connection_->SendControlFrame(frame);
+}
+
+void QuicSession::SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ SendRstStreamInner(id, error, bytes_written, /*close_write_side_only=*/false);
+}
+
+void QuicSession::SendRstStreamInner(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written,
+ bool close_write_side_only) {
+ if (connection()->connected()) {
+ // Only send if still connected.
+ if (close_write_side_only) {
+ DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version());
+ // Send a RST_STREAM frame.
+ control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written);
+ } else {
+ // Send a RST_STREAM frame plus, if version 99, an IETF
+ // QUIC STOP_SENDING frame. Both sre sent to emulate
+ // the two-way close that Google QUIC's RST_STREAM does.
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ QuicConnection::ScopedPacketFlusher flusher(
+ connection(), QuicConnection::SEND_ACK_IF_QUEUED);
+ control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written);
+ control_frame_manager_.WriteOrBufferStopSending(error, id);
+ } else {
+ control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written);
+ }
+ }
+ connection_->OnStreamReset(id, error);
+ }
+ if (error != QUIC_STREAM_NO_ERROR && QuicContainsKey(zombie_streams_, id)) {
+ OnStreamDoneWaitingForAcks(id);
+ return;
+ }
+
+ if (!close_write_side_only) {
+ CloseStreamInner(id, true);
+ return;
+ }
+ DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version());
+
+ DynamicStreamMap::iterator it = dynamic_stream_map_.find(id);
+ if (it != dynamic_stream_map_.end()) {
+ QuicStream* stream = it->second.get();
+ if (stream) {
+ stream->set_rst_sent(true);
+ stream->CloseWriteSide();
+ }
+ }
+}
+
+void QuicSession::SendGoAway(QuicErrorCode error_code,
+ const std::string& reason) {
+ // GOAWAY frame is not supported in v99.
+ DCHECK_NE(QUIC_VERSION_99, connection_->transport_version());
+ if (goaway_sent_) {
+ return;
+ }
+ goaway_sent_ = true;
+ control_frame_manager_.WriteOrBufferGoAway(
+ error_code, stream_id_manager_.largest_peer_created_stream_id(), reason);
+}
+
+void QuicSession::SendBlocked(QuicStreamId id) {
+ control_frame_manager_.WriteOrBufferBlocked(id);
+}
+
+void QuicSession::SendWindowUpdate(QuicStreamId id,
+ QuicStreamOffset byte_offset) {
+ control_frame_manager_.WriteOrBufferWindowUpdate(id, byte_offset);
+}
+
+void QuicSession::SendMaxStreamId(QuicStreamId max_allowed_incoming_id) {
+ control_frame_manager_.WriteOrBufferMaxStreamId(max_allowed_incoming_id);
+}
+
+void QuicSession::SendStreamIdBlocked(QuicStreamId max_allowed_outgoing_id) {
+ control_frame_manager_.WriteOrBufferStreamIdBlocked(max_allowed_outgoing_id);
+}
+
+void QuicSession::CloseStream(QuicStreamId stream_id) {
+ CloseStreamInner(stream_id, false);
+}
+
+void QuicSession::InsertLocallyClosedStreamsHighestOffset(
+ const QuicStreamId id,
+ QuicStreamOffset offset) {
+ locally_closed_streams_highest_offset_[id] = offset;
+ if (IsIncomingStream(id)) {
+ ++num_locally_closed_incoming_streams_highest_offset_;
+ }
+}
+
+void QuicSession::CloseStreamInner(QuicStreamId stream_id, bool locally_reset) {
+ QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << stream_id;
+
+ DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id);
+ if (it == dynamic_stream_map_.end()) {
+ // When CloseStreamInner has been called recursively (via
+ // QuicStream::OnClose), the stream will already have been deleted
+ // from stream_map_, so return immediately.
+ QUIC_DVLOG(1) << ENDPOINT << "Stream is already closed: " << stream_id;
+ return;
+ }
+ QuicStream* stream = it->second.get();
+
+ // Tell the stream that a RST has been sent.
+ if (locally_reset) {
+ stream->set_rst_sent(true);
+ }
+
+ if (stream->IsWaitingForAcks()) {
+ zombie_streams_[stream->id()] = std::move(it->second);
+ } else {
+ closed_streams_.push_back(std::move(it->second));
+ // Do not retransmit data of a closed stream.
+ streams_with_pending_retransmission_.erase(stream_id);
+ if (!closed_streams_clean_up_alarm_->IsSet()) {
+ closed_streams_clean_up_alarm_->Set(
+ connection_->clock()->ApproximateNow());
+ }
+ }
+
+ // If we haven't received a FIN or RST for this stream, we need to keep track
+ // of the how many bytes the stream's flow controller believes it has
+ // received, for accurate connection level flow control accounting.
+ const bool had_fin_or_rst = stream->HasFinalReceivedByteOffset();
+ if (!had_fin_or_rst) {
+ InsertLocallyClosedStreamsHighestOffset(
+ stream_id, stream->flow_controller()->highest_received_byte_offset());
+ }
+ dynamic_stream_map_.erase(it);
+ if (IsIncomingStream(stream_id)) {
+ --num_dynamic_incoming_streams_;
+ }
+
+ const bool stream_was_draining =
+ draining_streams_.find(stream_id) != draining_streams_.end();
+ if (stream_was_draining) {
+ if (IsIncomingStream(stream_id)) {
+ --num_draining_incoming_streams_;
+ }
+ draining_streams_.erase(stream_id);
+ } else if (connection_->transport_version() == QUIC_VERSION_99) {
+ // Stream was not draining, but we did have a fin or rst, so we can now
+ // free the stream ID if version 99.
+ if (had_fin_or_rst) {
+ v99_streamid_manager_.OnStreamClosed(stream_id);
+ }
+ }
+
+ stream->OnClose();
+
+ if (!stream_was_draining && !IsIncomingStream(stream_id) && had_fin_or_rst &&
+ connection_->transport_version() != QUIC_VERSION_99) {
+ // Streams that first became draining already called OnCanCreate...
+ // This covers the case where the stream went directly to being closed.
+ OnCanCreateNewOutgoingStream();
+ }
+}
+
+void QuicSession::ClosePendingStream(QuicStreamId stream_id) {
+ QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << stream_id;
+
+ if (pending_stream_map_.find(stream_id) == pending_stream_map_.end()) {
+ QUIC_BUG << ENDPOINT << "Stream is already closed: " << stream_id;
+ return;
+ }
+
+ SendRstStream(stream_id, QUIC_RST_ACKNOWLEDGEMENT, 0);
+
+ // The pending stream may have been deleted and removed during SendRstStream.
+ // Remove the stream from pending stream map iff it is still in the map.
+ if (pending_stream_map_.find(stream_id) != pending_stream_map_.end()) {
+ pending_stream_map_.erase(stream_id);
+ }
+
+ --num_dynamic_incoming_streams_;
+
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ v99_streamid_manager_.OnStreamClosed(stream_id);
+ }
+
+ OnCanCreateNewOutgoingStream();
+}
+
+void QuicSession::OnFinalByteOffsetReceived(
+ QuicStreamId stream_id,
+ QuicStreamOffset final_byte_offset) {
+ auto it = locally_closed_streams_highest_offset_.find(stream_id);
+ if (it == locally_closed_streams_highest_offset_.end()) {
+ return;
+ }
+
+ QUIC_DVLOG(1) << ENDPOINT << "Received final byte offset "
+ << final_byte_offset << " for stream " << stream_id;
+ QuicByteCount offset_diff = final_byte_offset - it->second;
+ if (flow_controller_.UpdateHighestReceivedOffset(
+ flow_controller_.highest_received_byte_offset() + offset_diff)) {
+ // If the final offset violates flow control, close the connection now.
+ if (flow_controller_.FlowControlViolation()) {
+ connection_->CloseConnection(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
+ "Connection level flow control violation",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ }
+
+ flow_controller_.AddBytesConsumed(offset_diff);
+ locally_closed_streams_highest_offset_.erase(it);
+ if (IsIncomingStream(stream_id)) {
+ --num_locally_closed_incoming_streams_highest_offset_;
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ v99_streamid_manager_.OnStreamClosed(stream_id);
+ }
+ } else if (connection_->transport_version() != QUIC_VERSION_99) {
+ OnCanCreateNewOutgoingStream();
+ }
+}
+
+bool QuicSession::IsEncryptionEstablished() const {
+ // Once the handshake is confirmed, it never becomes un-confirmed.
+ if (is_handshake_confirmed_) {
+ return true;
+ }
+ return GetCryptoStream()->encryption_established();
+}
+
+bool QuicSession::IsCryptoHandshakeConfirmed() const {
+ return GetCryptoStream()->handshake_confirmed();
+}
+
+void QuicSession::OnConfigNegotiated() {
+ connection_->SetFromConfig(config_);
+
+ uint32_t max_streams = 0;
+ if (config_.HasReceivedMaxIncomingDynamicStreams()) {
+ max_streams = config_.ReceivedMaxIncomingDynamicStreams();
+ }
+ QUIC_DVLOG(1) << "Setting max_open_outgoing_streams_ to " << max_streams;
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ v99_streamid_manager_.SetMaxOpenOutgoingStreams(max_streams);
+ } else {
+ stream_id_manager_.set_max_open_outgoing_streams(max_streams);
+ }
+ if (perspective() == Perspective::IS_SERVER) {
+ if (config_.HasReceivedConnectionOptions()) {
+ // The following variations change the initial receive flow control
+ // window sizes.
+ if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFW6)) {
+ AdjustInitialFlowControlWindows(64 * 1024);
+ }
+ if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFW7)) {
+ AdjustInitialFlowControlWindows(128 * 1024);
+ }
+ if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFW8)) {
+ AdjustInitialFlowControlWindows(256 * 1024);
+ }
+ if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFW9)) {
+ AdjustInitialFlowControlWindows(512 * 1024);
+ }
+ if (ContainsQuicTag(config_.ReceivedConnectionOptions(), kIFWA)) {
+ AdjustInitialFlowControlWindows(1024 * 1024);
+ }
+ }
+
+ config_.SetStatelessResetTokenToSend(GetStatelessResetToken());
+ }
+
+ // A small number of additional incoming streams beyond the limit should be
+ // allowed. This helps avoid early connection termination when FIN/RSTs for
+ // old streams are lost or arrive out of order.
+ // Use a minimum number of additional streams, or a percentage increase,
+ // whichever is larger.
+ uint32_t max_incoming_streams_to_send =
+ config_.GetMaxIncomingDynamicStreamsToSend();
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ v99_streamid_manager_.SetMaxOpenIncomingStreams(
+ max_incoming_streams_to_send);
+ } else {
+ uint32_t max_incoming_streams =
+ std::max(max_incoming_streams_to_send + kMaxStreamsMinimumIncrement,
+ static_cast<uint32_t>(max_incoming_streams_to_send *
+ kMaxStreamsMultiplier));
+ stream_id_manager_.set_max_open_incoming_streams(max_incoming_streams);
+ }
+
+ if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) {
+ // Streams which were created before the SHLO was received (0-RTT
+ // requests) are now informed of the peer's initial flow control window.
+ OnNewStreamFlowControlWindow(
+ config_.ReceivedInitialStreamFlowControlWindowBytes());
+ }
+ if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) {
+ OnNewSessionFlowControlWindow(
+ config_.ReceivedInitialSessionFlowControlWindowBytes());
+ }
+}
+
+void QuicSession::AdjustInitialFlowControlWindows(size_t stream_window) {
+ const float session_window_multiplier =
+ config_.GetInitialStreamFlowControlWindowToSend()
+ ? static_cast<float>(
+ config_.GetInitialSessionFlowControlWindowToSend()) /
+ config_.GetInitialStreamFlowControlWindowToSend()
+ : 1.5;
+
+ QUIC_DVLOG(1) << ENDPOINT << "Set stream receive window to " << stream_window;
+ config_.SetInitialStreamFlowControlWindowToSend(stream_window);
+
+ size_t session_window = session_window_multiplier * stream_window;
+ QUIC_DVLOG(1) << ENDPOINT << "Set session receive window to "
+ << session_window;
+ config_.SetInitialSessionFlowControlWindowToSend(session_window);
+ flow_controller_.UpdateReceiveWindowSize(session_window);
+ // Inform all existing streams about the new window.
+ for (auto const& kv : static_stream_map_) {
+ kv.second->flow_controller()->UpdateReceiveWindowSize(stream_window);
+ }
+ for (auto const& kv : dynamic_stream_map_) {
+ kv.second->flow_controller()->UpdateReceiveWindowSize(stream_window);
+ }
+}
+
+void QuicSession::HandleFrameOnNonexistentOutgoingStream(
+ QuicStreamId stream_id) {
+ DCHECK(!IsClosedStream(stream_id));
+ // Received a frame for a locally-created stream that is not currently
+ // active. This is an error.
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Data for nonexistent stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+void QuicSession::HandleRstOnValidNonexistentStream(
+ const QuicRstStreamFrame& frame) {
+ // If the stream is neither originally in active streams nor created in
+ // GetOrCreateDynamicStream(), it could be a closed stream in which case its
+ // final received byte offset need to be updated.
+ if (IsClosedStream(frame.stream_id)) {
+ // The RST frame contains the final byte offset for the stream: we can now
+ // update the connection level flow controller if needed.
+ OnFinalByteOffsetReceived(frame.stream_id, frame.byte_offset);
+ }
+}
+
+void QuicSession::OnNewStreamFlowControlWindow(QuicStreamOffset new_window) {
+ if (new_window < kMinimumFlowControlSendWindow) {
+ QUIC_LOG_FIRST_N(ERROR, 1)
+ << "Peer sent us an invalid stream flow control send window: "
+ << new_window << ", below default: " << kMinimumFlowControlSendWindow;
+ if (connection_->connected()) {
+ connection_->CloseConnection(
+ QUIC_FLOW_CONTROL_INVALID_WINDOW, "New stream window too low",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+ return;
+ }
+
+ // Inform all existing streams about the new window.
+ for (auto const& kv : static_stream_map_) {
+ kv.second->UpdateSendWindowOffset(new_window);
+ }
+ for (auto const& kv : dynamic_stream_map_) {
+ kv.second->UpdateSendWindowOffset(new_window);
+ }
+}
+
+void QuicSession::OnNewSessionFlowControlWindow(QuicStreamOffset new_window) {
+ if (new_window < kMinimumFlowControlSendWindow) {
+ QUIC_LOG_FIRST_N(ERROR, 1)
+ << "Peer sent us an invalid session flow control send window: "
+ << new_window << ", below default: " << kMinimumFlowControlSendWindow;
+ if (connection_->connected()) {
+ connection_->CloseConnection(
+ QUIC_FLOW_CONTROL_INVALID_WINDOW, "New connection window too low",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+ return;
+ }
+
+ flow_controller_.UpdateSendWindowOffset(new_window);
+}
+
+void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+ switch (event) {
+ // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter
+ // to QuicSession since it is the glue.
+ case ENCRYPTION_FIRST_ESTABLISHED:
+ // Given any streams blocked by encryption a chance to write.
+ OnCanWrite();
+ break;
+
+ case ENCRYPTION_REESTABLISHED:
+ // Retransmit originally packets that were sent, since they can't be
+ // decrypted by the peer.
+ connection_->RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION);
+ // Given any streams blocked by encryption a chance to write.
+ OnCanWrite();
+ break;
+
+ case HANDSHAKE_CONFIRMED:
+ QUIC_BUG_IF(!config_.negotiated())
+ << ENDPOINT << "Handshake confirmed without parameter negotiation.";
+ // Discard originally encrypted packets, since they can't be decrypted by
+ // the peer.
+ NeuterUnencryptedData();
+ is_handshake_confirmed_ = true;
+ break;
+
+ default:
+ QUIC_LOG(ERROR) << ENDPOINT << "Got unknown handshake event: " << event;
+ }
+}
+
+void QuicSession::OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& /*message*/) {}
+
+void QuicSession::OnCryptoHandshakeMessageReceived(
+ const CryptoHandshakeMessage& /*message*/) {}
+
+void QuicSession::RegisterStreamPriority(QuicStreamId id,
+ bool is_static,
+ SpdyPriority priority) {
+ write_blocked_streams()->RegisterStream(id, is_static, priority);
+}
+
+void QuicSession::UnregisterStreamPriority(QuicStreamId id, bool is_static) {
+ write_blocked_streams()->UnregisterStream(id, is_static);
+}
+
+void QuicSession::UpdateStreamPriority(QuicStreamId id,
+ SpdyPriority new_priority) {
+ write_blocked_streams()->UpdateStreamPriority(id, new_priority);
+}
+
+QuicConfig* QuicSession::config() {
+ return &config_;
+}
+
+void QuicSession::ActivateStream(std::unique_ptr<QuicStream> stream) {
+ QuicStreamId stream_id = stream->id();
+ QUIC_DVLOG(1) << ENDPOINT << "num_streams: " << dynamic_stream_map_.size()
+ << ". activating " << stream_id;
+ DCHECK(!QuicContainsKey(dynamic_stream_map_, stream_id));
+ DCHECK(!QuicContainsKey(static_stream_map_, stream_id));
+ dynamic_stream_map_[stream_id] = std::move(stream);
+ if (IsIncomingStream(stream_id)) {
+ ++num_dynamic_incoming_streams_;
+ }
+}
+
+QuicStreamId QuicSession::GetNextOutgoingBidirectionalStreamId() {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.GetNextOutgoingBidirectionalStreamId();
+ }
+ return stream_id_manager_.GetNextOutgoingStreamId();
+}
+
+QuicStreamId QuicSession::GetNextOutgoingUnidirectionalStreamId() {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.GetNextOutgoingUnidirectionalStreamId();
+ }
+ return stream_id_manager_.GetNextOutgoingStreamId();
+}
+
+bool QuicSession::CanOpenNextOutgoingBidirectionalStream() {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.CanOpenNextOutgoingBidirectionalStream();
+ }
+ return stream_id_manager_.CanOpenNextOutgoingStream(
+ GetNumOpenOutgoingStreams());
+}
+
+bool QuicSession::CanOpenNextOutgoingUnidirectionalStream() {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.CanOpenNextOutgoingUnidirectionalStream();
+ }
+ return stream_id_manager_.CanOpenNextOutgoingStream(
+ GetNumOpenOutgoingStreams());
+}
+
+QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) {
+ StreamHandler handler =
+ GetOrCreateStreamImpl(stream_id, /*may_buffer=*/false);
+ DCHECK(!handler.is_pending);
+ return handler.stream;
+}
+
+QuicSession::StreamHandler QuicSession::GetOrCreateStreamImpl(
+ QuicStreamId stream_id,
+ bool may_buffer) {
+ StaticStreamMap::iterator it = static_stream_map_.find(stream_id);
+ if (it != static_stream_map_.end()) {
+ return StreamHandler(it->second);
+ }
+ return GetOrCreateDynamicStreamImpl(stream_id, may_buffer);
+}
+
+void QuicSession::StreamDraining(QuicStreamId stream_id) {
+ DCHECK(QuicContainsKey(dynamic_stream_map_, stream_id));
+ if (!QuicContainsKey(draining_streams_, stream_id)) {
+ draining_streams_.insert(stream_id);
+ if (IsIncomingStream(stream_id)) {
+ ++num_draining_incoming_streams_;
+ }
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ v99_streamid_manager_.OnStreamClosed(stream_id);
+ }
+ }
+ if (!IsIncomingStream(stream_id)) {
+ // Inform application that a stream is available.
+ OnCanCreateNewOutgoingStream();
+ }
+}
+
+bool QuicSession::MaybeIncreaseLargestPeerStreamId(
+ const QuicStreamId stream_id) {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.MaybeIncreaseLargestPeerStreamId(stream_id);
+ }
+ return stream_id_manager_.MaybeIncreaseLargestPeerStreamId(stream_id);
+}
+
+bool QuicSession::ShouldYield(QuicStreamId stream_id) {
+ if (stream_id == currently_writing_stream_id_) {
+ return false;
+ }
+ return write_blocked_streams()->ShouldYield(stream_id);
+}
+
+QuicStream* QuicSession::GetOrCreateDynamicStream(
+ const QuicStreamId stream_id) {
+ StreamHandler handler =
+ GetOrCreateDynamicStreamImpl(stream_id, /*may_buffer=*/false);
+ DCHECK(!handler.is_pending);
+ return handler.stream;
+}
+
+QuicSession::StreamHandler QuicSession::GetOrCreateDynamicStreamImpl(
+ QuicStreamId stream_id,
+ bool may_buffer) {
+ DCHECK(!QuicContainsKey(static_stream_map_, stream_id))
+ << "Attempt to call GetOrCreateDynamicStream for a static stream";
+
+ DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id);
+ if (it != dynamic_stream_map_.end()) {
+ return StreamHandler(it->second.get());
+ }
+
+ if (IsClosedStream(stream_id)) {
+ return StreamHandler();
+ }
+
+ if (!IsIncomingStream(stream_id)) {
+ HandleFrameOnNonexistentOutgoingStream(stream_id);
+ return StreamHandler();
+ }
+
+ auto pending_it = pending_stream_map_.find(stream_id);
+ if (pending_it != pending_stream_map_.end()) {
+ DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version());
+ if (may_buffer) {
+ return StreamHandler(pending_it->second.get());
+ }
+ // The stream limit accounting has already been taken care of
+ // when the PendingStream was created, so there is no need to
+ // do so here. Now we can create the actual stream from the
+ // PendingStream.
+ StreamHandler handler(CreateIncomingStream(std::move(*pending_it->second)));
+ pending_stream_map_.erase(pending_it);
+ return handler;
+ }
+
+ // TODO(fkastenholz): If we are creating a new stream and we have
+ // sent a goaway, we should ignore the stream creation. Need to
+ // add code to A) test if goaway was sent ("if (goaway_sent_)") and
+ // B) reject stream creation ("return nullptr")
+
+ if (!MaybeIncreaseLargestPeerStreamId(stream_id)) {
+ return StreamHandler();
+ }
+
+ if (connection_->transport_version() != QUIC_VERSION_99) {
+ // TODO(fayang): Let LegacyQuicStreamIdManager count open streams and make
+ // CanOpenIncomingStream interface cosistent with that of v99.
+ if (!stream_id_manager_.CanOpenIncomingStream(
+ GetNumOpenIncomingStreams())) {
+ // Refuse to open the stream.
+ SendRstStream(stream_id, QUIC_REFUSED_STREAM, 0);
+ return StreamHandler();
+ }
+ }
+
+ if (connection_->transport_version() == QUIC_VERSION_99 && may_buffer &&
+ ShouldBufferIncomingStream(stream_id)) {
+ ++num_dynamic_incoming_streams_;
+ // Since STREAM frames may arrive out of order, delay creating the
+ // stream object until the first byte arrives. Buffer the frames and
+ // handle flow control accounting in the PendingStream.
+ auto pending = QuicMakeUnique<PendingStream>(stream_id, this);
+ StreamHandler handler(pending.get());
+ pending_stream_map_[stream_id] = std::move(pending);
+ return handler;
+ }
+
+ return StreamHandler(CreateIncomingStream(stream_id));
+}
+
+void QuicSession::set_largest_peer_created_stream_id(
+ QuicStreamId largest_peer_created_stream_id) {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ v99_streamid_manager_.SetLargestPeerCreatedStreamId(
+ largest_peer_created_stream_id);
+ return;
+ }
+ stream_id_manager_.set_largest_peer_created_stream_id(
+ largest_peer_created_stream_id);
+}
+
+bool QuicSession::IsClosedStream(QuicStreamId id) {
+ DCHECK_NE(QuicUtils::GetInvalidStreamId(connection_->transport_version()),
+ id);
+ if (IsOpenStream(id)) {
+ // Stream is active
+ return false;
+ }
+
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return !v99_streamid_manager_.IsAvailableStream(id);
+ }
+
+ return !stream_id_manager_.IsAvailableStream(id);
+}
+
+bool QuicSession::IsOpenStream(QuicStreamId id) {
+ DCHECK_NE(QuicUtils::GetInvalidStreamId(connection_->transport_version()),
+ id);
+ if (QuicContainsKey(static_stream_map_, id) ||
+ QuicContainsKey(dynamic_stream_map_, id) ||
+ QuicContainsKey(pending_stream_map_, id)) {
+ // Stream is active
+ return true;
+ }
+ return false;
+}
+
+size_t QuicSession::GetNumOpenIncomingStreams() const {
+ return num_dynamic_incoming_streams_ - num_draining_incoming_streams_ +
+ num_locally_closed_incoming_streams_highest_offset_;
+}
+
+size_t QuicSession::GetNumOpenOutgoingStreams() const {
+ DCHECK_GE(GetNumDynamicOutgoingStreams() +
+ GetNumLocallyClosedOutgoingStreamsHighestOffset(),
+ GetNumDrainingOutgoingStreams());
+ return GetNumDynamicOutgoingStreams() +
+ GetNumLocallyClosedOutgoingStreamsHighestOffset() -
+ GetNumDrainingOutgoingStreams();
+}
+
+size_t QuicSession::GetNumActiveStreams() const {
+ return dynamic_stream_map_.size() - draining_streams_.size();
+}
+
+size_t QuicSession::GetNumDrainingStreams() const {
+ return draining_streams_.size();
+}
+
+void QuicSession::MarkConnectionLevelWriteBlocked(QuicStreamId id) {
+ if (GetOrCreateStream(id) == nullptr) {
+ QUIC_BUG << "Marking unknown stream " << id << " blocked.";
+ QUIC_LOG_FIRST_N(ERROR, 2) << QuicStackTrace();
+ }
+
+ write_blocked_streams_.AddStream(id);
+}
+
+bool QuicSession::HasDataToWrite() const {
+ return write_blocked_streams_.HasWriteBlockedSpecialStream() ||
+ write_blocked_streams_.HasWriteBlockedDataStreams() ||
+ connection_->HasQueuedData() ||
+ !streams_with_pending_retransmission_.empty() ||
+ control_frame_manager_.WillingToWrite();
+}
+
+void QuicSession::OnAckNeedsRetransmittableFrame() {
+ flow_controller_.SendWindowUpdate();
+}
+
+void QuicSession::SendPing() {
+ control_frame_manager_.WritePing();
+}
+
+size_t QuicSession::GetNumDynamicOutgoingStreams() const {
+ DCHECK_GE(static_cast<size_t>(dynamic_stream_map_.size() +
+ pending_stream_map_.size()),
+ num_dynamic_incoming_streams_);
+ return dynamic_stream_map_.size() + pending_stream_map_.size() -
+ num_dynamic_incoming_streams_;
+}
+
+size_t QuicSession::GetNumDrainingOutgoingStreams() const {
+ DCHECK_GE(draining_streams_.size(), num_draining_incoming_streams_);
+ return draining_streams_.size() - num_draining_incoming_streams_;
+}
+
+size_t QuicSession::GetNumLocallyClosedOutgoingStreamsHighestOffset() const {
+ DCHECK_GE(locally_closed_streams_highest_offset_.size(),
+ num_locally_closed_incoming_streams_highest_offset_);
+ return locally_closed_streams_highest_offset_.size() -
+ num_locally_closed_incoming_streams_highest_offset_;
+}
+
+bool QuicSession::IsConnectionFlowControlBlocked() const {
+ return flow_controller_.IsBlocked();
+}
+
+bool QuicSession::IsStreamFlowControlBlocked() {
+ for (auto const& kv : static_stream_map_) {
+ if (kv.second->flow_controller()->IsBlocked()) {
+ return true;
+ }
+ }
+ for (auto const& kv : dynamic_stream_map_) {
+ if (kv.second->flow_controller()->IsBlocked()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t QuicSession::MaxAvailableBidirectionalStreams() const {
+ if (connection()->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.GetMaxAllowdIncomingBidirectionalStreams();
+ }
+ return stream_id_manager_.MaxAvailableStreams();
+}
+
+size_t QuicSession::MaxAvailableUnidirectionalStreams() const {
+ if (connection()->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.GetMaxAllowdIncomingUnidirectionalStreams();
+ }
+ return stream_id_manager_.MaxAvailableStreams();
+}
+
+bool QuicSession::IsIncomingStream(QuicStreamId id) const {
+ if (connection()->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.IsIncomingStream(id);
+ }
+ return stream_id_manager_.IsIncomingStream(id);
+}
+
+void QuicSession::OnStreamDoneWaitingForAcks(QuicStreamId id) {
+ auto it = zombie_streams_.find(id);
+ if (it == zombie_streams_.end()) {
+ return;
+ }
+
+ closed_streams_.push_back(std::move(it->second));
+ if (!closed_streams_clean_up_alarm_->IsSet()) {
+ closed_streams_clean_up_alarm_->Set(connection_->clock()->ApproximateNow());
+ }
+ zombie_streams_.erase(it);
+ // Do not retransmit data of a closed stream.
+ streams_with_pending_retransmission_.erase(id);
+}
+
+QuicStream* QuicSession::GetStream(QuicStreamId id) const {
+ if (id <= largest_static_stream_id_) {
+ auto static_stream = static_stream_map_.find(id);
+ if (static_stream != static_stream_map_.end()) {
+ return static_stream->second;
+ }
+ }
+
+ auto active_stream = dynamic_stream_map_.find(id);
+ if (active_stream != dynamic_stream_map_.end()) {
+ return active_stream->second.get();
+ }
+ auto zombie_stream = zombie_streams_.find(id);
+ if (zombie_stream != zombie_streams_.end()) {
+ return zombie_stream->second.get();
+ }
+ return nullptr;
+}
+
+bool QuicSession::OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) {
+ if (frame.type == MESSAGE_FRAME) {
+ OnMessageAcked(frame.message_frame->message_id);
+ return true;
+ }
+ if (frame.type == CRYPTO_FRAME) {
+ return GetMutableCryptoStream()->OnCryptoFrameAcked(*frame.crypto_frame,
+ ack_delay_time);
+ }
+ if (frame.type != STREAM_FRAME) {
+ return control_frame_manager_.OnControlFrameAcked(frame);
+ }
+ bool new_stream_data_acked = false;
+ QuicStream* stream = GetStream(frame.stream_frame.stream_id);
+ // Stream can already be reset when sent frame gets acked.
+ if (stream != nullptr) {
+ QuicByteCount newly_acked_length = 0;
+ new_stream_data_acked = stream->OnStreamFrameAcked(
+ frame.stream_frame.offset, frame.stream_frame.data_length,
+ frame.stream_frame.fin, ack_delay_time, &newly_acked_length);
+ if (!stream->HasPendingRetransmission()) {
+ streams_with_pending_retransmission_.erase(stream->id());
+ }
+ }
+ return new_stream_data_acked;
+}
+
+void QuicSession::OnStreamFrameRetransmitted(const QuicStreamFrame& frame) {
+ QuicStream* stream = GetStream(frame.stream_id);
+ if (stream == nullptr) {
+ QUIC_BUG << "Stream: " << frame.stream_id << " is closed when " << frame
+ << " is retransmitted.";
+ connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Attempt to retransmit frame of a closed stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ stream->OnStreamFrameRetransmitted(frame.offset, frame.data_length,
+ frame.fin);
+}
+
+void QuicSession::OnFrameLost(const QuicFrame& frame) {
+ if (frame.type == MESSAGE_FRAME) {
+ OnMessageLost(frame.message_frame->message_id);
+ return;
+ }
+ if (frame.type == CRYPTO_FRAME) {
+ GetMutableCryptoStream()->OnCryptoFrameLost(frame.crypto_frame);
+ return;
+ }
+ if (frame.type != STREAM_FRAME) {
+ control_frame_manager_.OnControlFrameLost(frame);
+ return;
+ }
+ QuicStream* stream = GetStream(frame.stream_frame.stream_id);
+ if (stream == nullptr) {
+ return;
+ }
+ stream->OnStreamFrameLost(frame.stream_frame.offset,
+ 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_.insert(
+ std::make_pair(frame.stream_frame.stream_id, true));
+ }
+}
+
+void QuicSession::RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) {
+ QuicConnection::ScopedPacketFlusher retransmission_flusher(
+ connection_, QuicConnection::NO_ACK);
+ SetTransmissionType(type);
+ for (const QuicFrame& frame : frames) {
+ if (frame.type == MESSAGE_FRAME) {
+ // Do not retransmit MESSAGE frames.
+ continue;
+ }
+ if (frame.type == CRYPTO_FRAME) {
+ GetMutableCryptoStream()->RetransmitData(frame.crypto_frame);
+ continue;
+ }
+ if (frame.type != STREAM_FRAME) {
+ if (!control_frame_manager_.RetransmitControlFrame(frame)) {
+ break;
+ }
+ continue;
+ }
+ QuicStream* stream = GetStream(frame.stream_frame.stream_id);
+ if (stream != nullptr &&
+ !stream->RetransmitStreamData(frame.stream_frame.offset,
+ frame.stream_frame.data_length,
+ frame.stream_frame.fin)) {
+ break;
+ }
+ }
+}
+
+bool QuicSession::IsFrameOutstanding(const QuicFrame& frame) const {
+ if (frame.type == MESSAGE_FRAME) {
+ return false;
+ }
+ if (frame.type == CRYPTO_FRAME) {
+ return GetCryptoStream()->IsFrameOutstanding(
+ frame.crypto_frame->level, frame.crypto_frame->offset,
+ frame.crypto_frame->data_length);
+ }
+ if (frame.type != STREAM_FRAME) {
+ return control_frame_manager_.IsControlFrameOutstanding(frame);
+ }
+ QuicStream* stream = GetStream(frame.stream_frame.stream_id);
+ return stream != nullptr &&
+ stream->IsStreamFrameOutstanding(frame.stream_frame.offset,
+ frame.stream_frame.data_length,
+ frame.stream_frame.fin);
+}
+
+bool QuicSession::HasUnackedCryptoData() const {
+ const QuicCryptoStream* crypto_stream = GetCryptoStream();
+ if (crypto_stream->IsWaitingForAcks()) {
+ return true;
+ }
+ if (GetQuicReloadableFlag(quic_fix_has_pending_crypto_data) &&
+ crypto_stream->HasBufferedData()) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_fix_has_pending_crypto_data);
+ return true;
+ }
+ return false;
+}
+
+WriteStreamDataResult QuicSession::WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ QuicStream* stream = GetStream(id);
+ if (stream == nullptr) {
+ // This causes the connection to be closed because of failed to serialize
+ // packet.
+ QUIC_BUG << "Stream " << id << " does not exist when trying to write data."
+ << " version:" << connection_->transport_version();
+ return STREAM_MISSING;
+ }
+ if (stream->WriteStreamData(offset, data_length, writer)) {
+ return WRITE_SUCCESS;
+ }
+ return WRITE_FAILED;
+}
+
+bool QuicSession::WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ return GetMutableCryptoStream()->WriteCryptoFrame(level, offset, data_length,
+ writer);
+}
+
+QuicUint128 QuicSession::GetStatelessResetToken() const {
+ return QuicUtils::GenerateStatelessResetToken(connection_->connection_id());
+}
+
+bool QuicSession::RetransmitLostData() {
+ QuicConnection::ScopedPacketFlusher retransmission_flusher(
+ connection_, QuicConnection::SEND_ACK_IF_QUEUED);
+ // Retransmit crypto data first.
+ bool uses_crypto_frames =
+ QuicVersionUsesCryptoFrames(connection_->transport_version());
+ QuicCryptoStream* crypto_stream = GetMutableCryptoStream();
+ if (uses_crypto_frames && crypto_stream->HasPendingCryptoRetransmission()) {
+ SetTransmissionType(HANDSHAKE_RETRANSMISSION);
+ crypto_stream->WritePendingCryptoRetransmission();
+ }
+ // Retransmit crypto data in stream 1 frames (version < 47).
+ if (!uses_crypto_frames &&
+ QuicContainsKey(
+ streams_with_pending_retransmission_,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()))) {
+ SetTransmissionType(HANDSHAKE_RETRANSMISSION);
+ // Retransmit crypto data first.
+ QuicStream* crypto_stream = GetStream(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ crypto_stream->OnCanWrite();
+ DCHECK(CheckStreamWriteBlocked(crypto_stream));
+ if (crypto_stream->HasPendingRetransmission()) {
+ // Connection is write blocked.
+ return false;
+ } else {
+ streams_with_pending_retransmission_.erase(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ }
+ }
+ if (control_frame_manager_.HasPendingRetransmission()) {
+ SetTransmissionType(LOSS_RETRANSMISSION);
+ control_frame_manager_.OnCanWrite();
+ if (control_frame_manager_.HasPendingRetransmission()) {
+ return false;
+ }
+ }
+ while (!streams_with_pending_retransmission_.empty()) {
+ if (!connection_->CanWriteStreamData()) {
+ break;
+ }
+ // Retransmit lost data on headers and data streams.
+ const QuicStreamId id = streams_with_pending_retransmission_.begin()->first;
+ QuicStream* stream = GetStream(id);
+ if (stream != nullptr) {
+ SetTransmissionType(LOSS_RETRANSMISSION);
+ stream->OnCanWrite();
+ DCHECK(CheckStreamWriteBlocked(stream));
+ if (stream->HasPendingRetransmission()) {
+ // Connection is write blocked.
+ break;
+ } else if (!streams_with_pending_retransmission_.empty() &&
+ streams_with_pending_retransmission_.begin()->first == id) {
+ // Retransmit lost data may cause connection close. If this stream
+ // has not yet sent fin, a RST_STREAM will be sent and it will be
+ // removed from streams_with_pending_retransmission_.
+ streams_with_pending_retransmission_.pop_front();
+ }
+ } else {
+ QUIC_BUG << "Try to retransmit data of a closed stream";
+ streams_with_pending_retransmission_.pop_front();
+ }
+ }
+
+ return streams_with_pending_retransmission_.empty();
+}
+
+void QuicSession::NeuterUnencryptedData() {
+ if (connection_->session_decides_what_to_write()) {
+ QuicCryptoStream* crypto_stream = GetMutableCryptoStream();
+ crypto_stream->NeuterUnencryptedStreamData();
+ if (!crypto_stream->HasPendingRetransmission()) {
+ streams_with_pending_retransmission_.erase(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ }
+ }
+ connection_->NeuterUnencryptedPackets();
+}
+
+void QuicSession::SetTransmissionType(TransmissionType type) {
+ connection_->SetTransmissionType(type);
+}
+
+MessageResult QuicSession::SendMessage(QuicMemSliceSpan message) {
+ if (!IsEncryptionEstablished()) {
+ return {MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0};
+ }
+ MessageStatus result =
+ connection_->SendMessage(last_message_id_ + 1, message);
+ if (result == MESSAGE_STATUS_SUCCESS) {
+ return {result, ++last_message_id_};
+ }
+ return {result, 0};
+}
+
+void QuicSession::OnMessageAcked(QuicMessageId message_id) {
+ QUIC_DVLOG(1) << ENDPOINT << "message " << message_id << " gets acked.";
+}
+
+void QuicSession::OnMessageLost(QuicMessageId message_id) {
+ QUIC_DVLOG(1) << ENDPOINT << "message " << message_id
+ << " is considered lost";
+}
+
+void QuicSession::CleanUpClosedStreams() {
+ closed_streams_.clear();
+}
+
+bool QuicSession::session_decides_what_to_write() const {
+ return connection_->session_decides_what_to_write();
+}
+
+QuicPacketLength QuicSession::GetCurrentLargestMessagePayload() const {
+ return connection_->GetCurrentLargestMessagePayload();
+}
+
+QuicPacketLength QuicSession::GetGuaranteedLargestMessagePayload() const {
+ return connection_->GetGuaranteedLargestMessagePayload();
+}
+
+void QuicSession::SendStopSending(uint16_t code, QuicStreamId stream_id) {
+ control_frame_manager_.WriteOrBufferStopSending(code, stream_id);
+}
+
+void QuicSession::OnCanCreateNewOutgoingStream() {}
+
+QuicStreamId QuicSession::next_outgoing_bidirectional_stream_id() const {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.next_outgoing_bidirectional_stream_id();
+ }
+ return stream_id_manager_.next_outgoing_stream_id();
+}
+
+QuicStreamId QuicSession::next_outgoing_unidirectional_stream_id() const {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.next_outgoing_unidirectional_stream_id();
+ }
+ return stream_id_manager_.next_outgoing_stream_id();
+}
+
+bool QuicSession::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) {
+ return v99_streamid_manager_.OnMaxStreamIdFrame(frame);
+}
+
+bool QuicSession::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ return v99_streamid_manager_.OnStreamIdBlockedFrame(frame);
+}
+
+size_t QuicSession::max_open_incoming_bidirectional_streams() const {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.GetMaxAllowdIncomingBidirectionalStreams();
+ }
+ return stream_id_manager_.max_open_incoming_streams();
+}
+
+size_t QuicSession::max_open_incoming_unidirectional_streams() const {
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ return v99_streamid_manager_.GetMaxAllowdIncomingUnidirectionalStreams();
+ }
+ return stream_id_manager_.max_open_incoming_streams();
+}
+
+#undef ENDPOINT // undef for jumbo builds
+} // namespace quic
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
new file mode 100644
index 00000000000..a9fb9923624
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_session.h
@@ -0,0 +1,702 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A QuicSession, which demuxes a single connection to individual streams.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_SESSION_H_
+#define QUICHE_QUIC_CORE_QUIC_SESSION_H_
+
+#include <cstddef>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/legacy_quic_stream_id_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_control_frame_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
+#include "net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+class QuicCryptoStream;
+class QuicFlowController;
+class QuicStream;
+class QuicStreamIdManager;
+
+namespace test {
+class QuicSessionPeer;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface,
+ public SessionNotifierInterface,
+ public QuicStreamFrameDataProducer {
+ public:
+ // An interface from the session to the entity owning the session.
+ // This lets the session notify its owner (the Dispatcher) when the connection
+ // is closed, blocked, or added/removed from the time-wait list.
+ class Visitor {
+ public:
+ virtual ~Visitor() {}
+
+ // Called when the connection is closed after the streams have been closed.
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) = 0;
+
+ // Called when the session has become write blocked.
+ virtual void OnWriteBlocked(QuicBlockedWriterInterface* blocked_writer) = 0;
+
+ // Called when the session receives reset on a stream from the peer.
+ virtual void OnRstStreamReceived(const QuicRstStreamFrame& frame) = 0;
+
+ // Called when the session receives a STOP_SENDING for a stream from the
+ // peer.
+ virtual void OnStopSendingReceived(const QuicStopSendingFrame& frame) = 0;
+ };
+
+ // CryptoHandshakeEvent enumerates the events generated by a QuicCryptoStream.
+ enum CryptoHandshakeEvent {
+ // ENCRYPTION_FIRST_ESTABLISHED indicates that a full client hello has been
+ // sent by a client and that subsequent packets will be encrypted. (Client
+ // only.)
+ ENCRYPTION_FIRST_ESTABLISHED,
+ // ENCRYPTION_REESTABLISHED indicates that a client hello was rejected by
+ // the server and thus the encryption key has been updated. Therefore the
+ // connection should resend any packets that were sent under
+ // ENCRYPTION_ZERO_RTT. (Client only.)
+ ENCRYPTION_REESTABLISHED,
+ // HANDSHAKE_CONFIRMED, in a client, indicates the server has accepted
+ // our handshake. In a server it indicates that a full, valid client hello
+ // has been received. (Client and server.)
+ HANDSHAKE_CONFIRMED,
+ };
+
+ // Does not take ownership of |connection| or |visitor|.
+ QuicSession(QuicConnection* connection,
+ Visitor* owner,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions);
+ QuicSession(const QuicSession&) = delete;
+ QuicSession& operator=(const QuicSession&) = delete;
+
+ ~QuicSession() override;
+
+ virtual void Initialize();
+
+ // QuicConnectionVisitorInterface methods:
+ void OnStreamFrame(const QuicStreamFrame& frame) override;
+ void OnCryptoFrame(const QuicCryptoFrame& frame) override;
+ void OnRstStream(const QuicRstStreamFrame& frame) override;
+ void OnGoAway(const QuicGoAwayFrame& frame) override;
+ void OnMessageReceived(QuicStringPiece message) override;
+ void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
+ void OnBlockedFrame(const QuicBlockedFrame& frame) override;
+ void OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) override;
+ void OnWriteBlocked() override;
+ void OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) override;
+ void OnConnectivityProbeReceived(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address) override;
+ void OnCanWrite() override;
+ void OnCongestionWindowChange(QuicTime /*now*/) override {}
+ void OnConnectionMigration(AddressChangeType type) override {}
+ // Adds a connection level WINDOW_UPDATE frame.
+ void OnAckNeedsRetransmittableFrame() override;
+ void SendPing() override;
+ bool WillingAndAbleToWrite() const override;
+ bool HasPendingHandshake() const override;
+ void OnPathDegrading() override;
+ bool AllowSelfAddressChange() const override;
+ void OnForwardProgressConfirmed() override;
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override;
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override;
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override;
+
+ // QuicStreamFrameDataProducer
+ WriteStreamDataResult WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+ bool WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+
+ // SessionNotifierInterface methods:
+ bool OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) override;
+ void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override;
+ void OnFrameLost(const QuicFrame& frame) override;
+ void RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) override;
+ bool IsFrameOutstanding(const QuicFrame& frame) const override;
+ bool HasUnackedCryptoData() const override;
+
+ // Called on every incoming packet. Passes |packet| through to |connection_|.
+ virtual void ProcessUdpPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet);
+
+ // Called by streams when they want to write data to the peer.
+ // Returns a pair with the number of bytes consumed from data, and a boolean
+ // indicating if the fin bit was consumed. This does not indicate the data
+ // has been sent on the wire: it may have been turned into a packet and queued
+ // if the socket was unexpectedly blocked.
+ virtual QuicConsumedData WritevData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state);
+
+ // 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.
+ //
+ // OnMessageAcked and OnMessageLost are called when a particular message gets
+ // acked or lost.
+ //
+ // 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
+ // connection becomes available, for example after getting OnCanWrite()
+ // callback.
+ MessageResult SendMessage(QuicMemSliceSpan message);
+
+ // Called when message with |message_id| gets acked.
+ virtual void OnMessageAcked(QuicMessageId message_id);
+
+ // Called when message with |message_id| is considered as lost.
+ virtual void OnMessageLost(QuicMessageId message_id);
+
+ // Called by control frame manager when it wants to write control frames to
+ // the peer. Returns true if |frame| is consumed, false otherwise.
+ virtual bool WriteControlFrame(const QuicFrame& frame);
+
+ // Called by streams when they want to close the stream in both directions.
+ virtual void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
+
+ // Called when the session wants to go away and not accept any new streams.
+ virtual void SendGoAway(QuicErrorCode error_code, const std::string& reason);
+
+ // Sends a BLOCKED frame.
+ virtual void SendBlocked(QuicStreamId id);
+
+ // Sends a WINDOW_UPDATE frame.
+ virtual void SendWindowUpdate(QuicStreamId id, QuicStreamOffset byte_offset);
+
+ // Send a MAX_STREAM_ID frame.
+ void SendMaxStreamId(QuicStreamId max_allowed_incoming_id);
+
+ // Send a STREAM_ID_BLOCKED frame.
+ void SendStreamIdBlocked(QuicStreamId max_allowed_outgoing_id);
+
+ // Create and transmit a STOP_SENDING frame
+ virtual void SendStopSending(uint16_t code, QuicStreamId stream_id);
+
+ // Removes the stream associated with 'stream_id' from the active stream map.
+ virtual void CloseStream(QuicStreamId stream_id);
+
+ // Returns true if outgoing packets will be encrypted, even if the server
+ // hasn't confirmed the handshake yet.
+ virtual bool IsEncryptionEstablished() const;
+
+ // For a client, returns true if the server has confirmed our handshake. For
+ // a server, returns true if a full, valid client hello has been received.
+ virtual bool IsCryptoHandshakeConfirmed() const;
+
+ // Called by the QuicCryptoStream when a new QuicConfig has been negotiated.
+ virtual void OnConfigNegotiated();
+
+ // Called by the QuicCryptoStream when the handshake enters a new state.
+ //
+ // Clients will call this function in the order:
+ // ENCRYPTION_FIRST_ESTABLISHED
+ // zero or more ENCRYPTION_REESTABLISHED
+ // HANDSHAKE_CONFIRMED
+ //
+ // Servers will simply call it once with HANDSHAKE_CONFIRMED.
+ virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event);
+
+ // Called by the QuicCryptoStream when a handshake message is sent.
+ virtual void OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message);
+
+ // Called by the QuicCryptoStream when a handshake message is received.
+ virtual void OnCryptoHandshakeMessageReceived(
+ const CryptoHandshakeMessage& message);
+
+ // Called by the stream on creation to set priority in the write blocked list.
+ virtual void RegisterStreamPriority(QuicStreamId id,
+ bool is_static,
+ spdy::SpdyPriority priority);
+ // Called by the stream on deletion to clear priority from the write blocked
+ // list.
+ virtual void UnregisterStreamPriority(QuicStreamId id, bool is_static);
+ // Called by the stream on SetPriority to update priority on the write blocked
+ // list.
+ virtual void UpdateStreamPriority(QuicStreamId id,
+ spdy::SpdyPriority new_priority);
+
+ // Returns mutable config for this session. Returned config is owned
+ // by QuicSession.
+ QuicConfig* config();
+
+ // Returns true if the stream existed previously and has been closed.
+ // Returns false if the stream is still active or if the stream has
+ // not yet been created.
+ bool IsClosedStream(QuicStreamId id);
+
+ QuicConnection* connection() { return connection_; }
+ const QuicConnection* connection() const { return connection_; }
+ const QuicSocketAddress& peer_address() const {
+ return connection_->peer_address();
+ }
+ const QuicSocketAddress& self_address() const {
+ return connection_->self_address();
+ }
+ QuicConnectionId connection_id() const {
+ return connection_->connection_id();
+ }
+
+ // Returns the number of currently open streams, excluding the reserved
+ // headers and crypto streams, and never counting unfinished streams.
+ size_t GetNumActiveStreams() const;
+
+ // Returns the number of currently draining streams.
+ size_t GetNumDrainingStreams() const;
+
+ // Returns the number of currently open peer initiated streams, excluding the
+ // reserved headers and crypto streams.
+ size_t GetNumOpenIncomingStreams() const;
+
+ // Returns the number of currently open self initiated streams, excluding the
+ // reserved headers and crypto streams.
+ size_t GetNumOpenOutgoingStreams() const;
+
+ // Add the stream to the session's write-blocked list because it is blocked by
+ // connection-level flow control but not by its own stream-level flow control.
+ // The stream will be given a chance to write when a connection-level
+ // WINDOW_UPDATE arrives.
+ void MarkConnectionLevelWriteBlocked(QuicStreamId id);
+
+ // Called when stream |id| is done waiting for acks either because all data
+ // gets acked or is not interested in data being acked (which happens when
+ // a stream is reset because of an error).
+ void OnStreamDoneWaitingForAcks(QuicStreamId id);
+
+ // Called to cancel retransmission of unencypted crypto stream data.
+ void NeuterUnencryptedData();
+
+ // Returns true if the session has data to be sent, either queued in the
+ // connection, or in a write-blocked stream.
+ bool HasDataToWrite() const;
+
+ // Returns the largest payload that will fit into a single MESSAGE frame.
+ // Because overhead can vary during a connection, this method should be
+ // checked for every message.
+ QuicPacketLength GetCurrentLargestMessagePayload() const;
+
+ // Returns the largest payload that will fit into a single MESSAGE frame at
+ // any point during the connection. This assumes the version and
+ // connection ID lengths do not change.
+ QuicPacketLength GetGuaranteedLargestMessagePayload() const;
+
+ bool goaway_sent() const { return goaway_sent_; }
+
+ bool goaway_received() const { return goaway_received_; }
+
+ QuicErrorCode error() const { return error_; }
+
+ Perspective perspective() const { return connection_->perspective(); }
+
+ QuicFlowController* flow_controller() { return &flow_controller_; }
+
+ // Returns true if connection is flow controller blocked.
+ bool IsConnectionFlowControlBlocked() const;
+
+ // Returns true if any stream is flow controller blocked.
+ bool IsStreamFlowControlBlocked();
+
+ size_t max_open_incoming_bidirectional_streams() const;
+ size_t max_open_incoming_unidirectional_streams() const;
+
+ size_t MaxAvailableBidirectionalStreams() const;
+ size_t MaxAvailableUnidirectionalStreams() const;
+
+ // Returns existing static or dynamic stream with id = |stream_id|. If no
+ // such stream exists, and |stream_id| is a peer-created dynamic stream id,
+ // then a new stream is created and returned. In all other cases, nullptr is
+ // returned.
+ QuicStream* GetOrCreateStream(const QuicStreamId stream_id);
+
+ // Mark a stream as draining.
+ virtual void StreamDraining(QuicStreamId id);
+
+ // Returns true if this stream should yield writes to another blocked stream.
+ bool ShouldYield(QuicStreamId stream_id);
+
+ // Set transmission type of next sending packets.
+ void SetTransmissionType(TransmissionType type);
+
+ // Clean up closed_streams_.
+ void CleanUpClosedStreams();
+
+ bool session_decides_what_to_write() const;
+
+ const ParsedQuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+
+ // Called when new outgoing streams are available to be opened. This occurs
+ // when an extant, open, stream is moved to draining or closed. The default
+ // implementation does nothing.
+ virtual void OnCanCreateNewOutgoingStream();
+
+ QuicStreamId next_outgoing_bidirectional_stream_id() const;
+ QuicStreamId next_outgoing_unidirectional_stream_id() const;
+
+ // Return true if given stream is peer initiated.
+ bool IsIncomingStream(QuicStreamId id) const;
+
+ size_t GetNumLocallyClosedOutgoingStreamsHighestOffset() const;
+
+ size_t num_locally_closed_incoming_streams_highest_offset() const {
+ return num_locally_closed_incoming_streams_highest_offset_;
+ }
+
+ // Does actual work of sending reset-stream or reset-stream&stop-sending
+ // If the connection is not version 99/IETF QUIC, will always send a
+ // RESET_STREAM and close_write_side_only is ignored. If the connection is
+ // IETF QUIC/Version 99 then will send a RESET_STREAM and STOP_SENDING if
+ // close_write_side_only is false, just a RESET_STREAM if
+ // close_write_side_only is true.
+ virtual void SendRstStreamInner(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written,
+ bool close_write_side_only);
+
+ // Record errors when a connection is closed at the server side, should only
+ // be called from server's perspective.
+ // Noop if |error| is QUIC_NO_ERROR.
+ static void RecordConnectionCloseAtServer(QuicErrorCode error,
+ ConnectionCloseSource source);
+
+ protected:
+ using StaticStreamMap = QuicSmallMap<QuicStreamId, QuicStream*, 2>;
+
+ using DynamicStreamMap =
+ QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>;
+
+ using PendingStreamMap =
+ QuicSmallMap<QuicStreamId, std::unique_ptr<PendingStream>, 10>;
+
+ using ClosedStreams = std::vector<std::unique_ptr<QuicStream>>;
+
+ using ZombieStreamMap =
+ QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>;
+
+ // Creates a new stream to handle a peer-initiated stream.
+ // Caller does not own the returned stream.
+ // Returns nullptr and does error handling if the stream can not be created.
+ virtual QuicStream* CreateIncomingStream(QuicStreamId id) = 0;
+ virtual QuicStream* CreateIncomingStream(PendingStream pending) = 0;
+
+ // Return the reserved crypto stream.
+ virtual QuicCryptoStream* GetMutableCryptoStream() = 0;
+
+ // Return the reserved crypto stream as a constant pointer.
+ virtual const QuicCryptoStream* GetCryptoStream() const = 0;
+
+ // Adds |stream| to the dynamic stream map.
+ virtual void ActivateStream(std::unique_ptr<QuicStream> stream);
+
+ // Returns the stream ID for a new outgoing bidirectional/unidirectional
+ // stream, and increments the underlying counter.
+ QuicStreamId GetNextOutgoingBidirectionalStreamId();
+ QuicStreamId GetNextOutgoingUnidirectionalStreamId();
+
+ // Indicates whether the next outgoing bidirectional/unidirectional stream ID
+ // can be allocated or not. The test for version-99/IETF QUIC is whether it
+ // will exceed the maximum-stream-id or not. For non-version-99 (Google) QUIC
+ // it checks whether the next stream would exceed the limit on the number of
+ // open streams.
+ bool CanOpenNextOutgoingBidirectionalStream();
+ bool CanOpenNextOutgoingUnidirectionalStream();
+
+ // Returns the number of open dynamic streams.
+ uint64_t GetNumOpenDynamicStreams() const;
+
+ // Returns existing stream with id = |stream_id|. If no such stream exists,
+ // and |stream_id| is a peer-created id, then a new stream is created and
+ // returned. However if |stream_id| is a locally-created id and no such stream
+ // exists, the connection is closed.
+ // Caller does not own the returned stream.
+ QuicStream* GetOrCreateDynamicStream(QuicStreamId stream_id);
+
+ // Performs the work required to close |stream_id|. If |locally_reset|
+ // then the stream has been reset by this endpoint, not by the peer.
+ virtual void CloseStreamInner(QuicStreamId stream_id, bool locally_reset);
+
+ // When a stream is closed locally, it may not yet know how many bytes the
+ // peer sent on that stream.
+ // When this data arrives (via stream frame w. FIN, trailing headers, or RST)
+ // this method is called, and correctly updates the connection level flow
+ // controller.
+ virtual void OnFinalByteOffsetReceived(QuicStreamId id,
+ QuicStreamOffset final_byte_offset);
+
+ // Returns true if incoming streams should be buffered until the first
+ // byte of the stream arrives.
+ virtual bool ShouldBufferIncomingStream(QuicStreamId id) const {
+ return false;
+ }
+
+ // Register (|id|, |stream|) with the static stream map. Override previous
+ // registrations with the same id.
+ void RegisterStaticStream(QuicStreamId id, QuicStream* stream);
+ const StaticStreamMap& static_streams() const { return static_stream_map_; }
+
+ DynamicStreamMap& dynamic_streams() { return dynamic_stream_map_; }
+ const DynamicStreamMap& dynamic_streams() const {
+ return dynamic_stream_map_;
+ }
+
+ ClosedStreams* closed_streams() { return &closed_streams_; }
+
+ const ZombieStreamMap& zombie_streams() const { return zombie_streams_; }
+
+ void set_largest_peer_created_stream_id(
+ QuicStreamId largest_peer_created_stream_id);
+
+ void set_error(QuicErrorCode error) { error_ = error; }
+ QuicWriteBlockedList* write_blocked_streams() {
+ return &write_blocked_streams_;
+ }
+
+ size_t GetNumDynamicOutgoingStreams() const;
+
+ size_t GetNumDrainingOutgoingStreams() const;
+
+ // Returns true if the stream is still active.
+ bool IsOpenStream(QuicStreamId id);
+
+ // Close connection when receive a frame for a locally-created nonexistant
+ // stream.
+ // Prerequisite: IsClosedStream(stream_id) == false
+ // Server session might need to override this method to allow server push
+ // stream to be promised before creating an active stream.
+ virtual void HandleFrameOnNonexistentOutgoingStream(QuicStreamId stream_id);
+
+ virtual bool MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id);
+
+ void InsertLocallyClosedStreamsHighestOffset(const QuicStreamId id,
+ QuicStreamOffset offset);
+ // If stream is a locally closed stream, this RST will update FIN offset.
+ // Otherwise stream is a preserved stream and the behavior of it depends on
+ // derived class's own implementation.
+ virtual void HandleRstOnValidNonexistentStream(
+ const QuicRstStreamFrame& frame);
+
+ // Returns a stateless reset token which will be included in the public reset
+ // packet.
+ virtual QuicUint128 GetStatelessResetToken() const;
+
+ QuicControlFrameManager& control_frame_manager() {
+ return control_frame_manager_;
+ }
+
+ const LegacyQuicStreamIdManager& stream_id_manager() const {
+ return stream_id_manager_;
+ }
+
+ // A StreamHandler represents an object which can receive a STREAM or
+ // or RST_STREAM frame.
+ struct StreamHandler {
+ StreamHandler() : is_pending(false), stream(nullptr) {}
+
+ // Creates a StreamHandler wrapping a QuicStream.
+ explicit StreamHandler(QuicStream* stream)
+ : is_pending(false), stream(stream) {}
+
+ // Creates a StreamHandler wrapping a PendingStream.
+ explicit StreamHandler(PendingStream* pending)
+ : is_pending(true), pending(pending) {
+ DCHECK(pending != nullptr);
+ }
+
+ // True if this handler contains a non-null PendingStream, false otherwise.
+ bool is_pending;
+ union {
+ QuicStream* stream;
+ PendingStream* pending;
+ };
+ };
+
+ StreamHandler GetOrCreateStreamImpl(QuicStreamId stream_id, bool may_buffer);
+
+ private:
+ friend class test::QuicSessionPeer;
+
+ // Called in OnConfigNegotiated when we receive a new stream level flow
+ // control window in a negotiated config. Closes the connection if invalid.
+ void OnNewStreamFlowControlWindow(QuicStreamOffset new_window);
+
+ // Called in OnConfigNegotiated when we receive a new connection level flow
+ // control window in a negotiated config. Closes the connection if invalid.
+ void OnNewSessionFlowControlWindow(QuicStreamOffset new_window);
+
+ // Debug helper for |OnCanWrite()|, check that OnStreamWrite() makes
+ // forward progress. Returns false if busy loop detected.
+ bool CheckStreamNotBusyLooping(QuicStream* stream,
+ uint64_t previous_bytes_written,
+ bool previous_fin_sent);
+
+ // Debug helper for OnCanWrite. Check that after QuicStream::OnCanWrite(),
+ // if stream has buffered data and is not stream level flow control blocked,
+ // it has to be in the write blocked list.
+ bool CheckStreamWriteBlocked(QuicStream* stream) const;
+
+ // Called in OnConfigNegotiated for Finch trials to measure performance of
+ // starting with larger flow control receive windows.
+ void AdjustInitialFlowControlWindows(size_t stream_window);
+
+ // Find stream with |id|, returns nullptr if the stream does not exist or
+ // closed.
+ QuicStream* GetStream(QuicStreamId id) const;
+
+ StreamHandler GetOrCreateDynamicStreamImpl(QuicStreamId stream_id,
+ bool may_buffer);
+
+ // Let streams and control frame managers retransmit lost data, returns true
+ // if all lost data is retransmitted. Returns false otherwise.
+ bool RetransmitLostData();
+
+ // Closes the pending stream |stream_id| before it has been created.
+ void ClosePendingStream(QuicStreamId stream_id);
+
+ // Keep track of highest received byte offset of locally closed streams, while
+ // waiting for a definitive final highest offset from the peer.
+ std::map<QuicStreamId, QuicStreamOffset>
+ locally_closed_streams_highest_offset_;
+
+ QuicConnection* connection_;
+
+ // May be null.
+ Visitor* visitor_;
+
+ // A list of streams which need to write more data. Stream register
+ // themselves in their constructor, and unregisterm themselves in their
+ // destructors, so the write blocked list must outlive all streams.
+ QuicWriteBlockedList write_blocked_streams_;
+
+ ClosedStreams closed_streams_;
+ // Streams which are closed, but need to be kept alive. Currently, the only
+ // reason is the stream's sent data (including FIN) does not get fully acked.
+ ZombieStreamMap zombie_streams_;
+
+ QuicConfig config_;
+
+ // Static streams, such as crypto and header streams. Owned by child classes
+ // that create these streams.
+ StaticStreamMap static_stream_map_;
+
+ // Map from StreamId to pointers to streams. Owns the streams.
+ DynamicStreamMap dynamic_stream_map_;
+
+ // Map from StreamId to PendingStreams for peer-created unidirectional streams
+ // which are waiting for the first byte of payload to arrive.
+ PendingStreamMap pending_stream_map_;
+
+ // Set of stream ids that are "draining" -- a FIN has been sent and received,
+ // but the stream object still exists because not all the received data has
+ // been consumed.
+ QuicUnorderedSet<QuicStreamId> draining_streams_;
+
+ // TODO(fayang): Consider moving LegacyQuicStreamIdManager into
+ // UberQuicStreamIdManager.
+ // Manages stream IDs for Google QUIC.
+ LegacyQuicStreamIdManager stream_id_manager_;
+
+ // Manages stream IDs for version99/IETF QUIC
+ UberQuicStreamIdManager v99_streamid_manager_;
+
+ // A counter for peer initiated streams which are in the dynamic_stream_map_.
+ size_t num_dynamic_incoming_streams_;
+
+ // A counter for peer initiated streams which are in the draining_streams_.
+ size_t num_draining_incoming_streams_;
+
+ // A counter for peer initiated streams which are in the
+ // locally_closed_streams_highest_offset_.
+ size_t num_locally_closed_incoming_streams_highest_offset_;
+
+ // The latched error with which the connection was closed.
+ QuicErrorCode error_;
+
+ // Used for connection-level flow control.
+ QuicFlowController flow_controller_;
+
+ // The stream id which was last popped in OnCanWrite, or 0, if not under the
+ // call stack of OnCanWrite.
+ QuicStreamId currently_writing_stream_id_;
+
+ // The largest stream id in |static_stream_map_|.
+ QuicStreamId largest_static_stream_id_;
+
+ // Cached value of whether the crypto handshake has been confirmed.
+ bool is_handshake_confirmed_;
+
+ // Whether a GoAway has been sent.
+ bool goaway_sent_;
+
+ // Whether a GoAway has been received.
+ bool goaway_received_;
+
+ QuicControlFrameManager control_frame_manager_;
+
+ // Id of latest successfully sent message.
+ QuicMessageId last_message_id_;
+
+ // 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_;
+
+ // Clean up closed_streams_ when this alarm fires.
+ std::unique_ptr<QuicAlarm> closed_streams_clean_up_alarm_;
+
+ // Supported version list used by the crypto handshake only. Please note, this
+ // list may be a superset of the connection framer's supported versions.
+ ParsedQuicVersionVector supported_versions_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_SESSION_H_
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
new file mode 100644
index 00000000000..a517d4703da
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_session_test.cc
@@ -0,0 +1,2352 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+
+#include <cstdint>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using spdy::kV3HighestPriority;
+using spdy::SpdyPriority;
+using testing::_;
+using testing::AtLeast;
+using testing::InSequence;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+using testing::WithArg;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker {
+ public:
+ explicit TestCryptoStream(QuicSession* session)
+ : QuicCryptoStream(session),
+ QuicCryptoHandshaker(this, session),
+ encryption_established_(false),
+ handshake_confirmed_(false),
+ params_(new QuicCryptoNegotiatedParameters) {}
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& /*message*/) override {
+ encryption_established_ = true;
+ handshake_confirmed_ = true;
+ CryptoHandshakeMessage msg;
+ std::string error_details;
+ session()->config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session()->config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session()->config()->ToHandshakeMessage(&msg);
+ const QuicErrorCode error =
+ session()->config()->ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ session()->OnConfigNegotiated();
+ session()->connection()->SetDefaultEncryptionLevel(
+ ENCRYPTION_FORWARD_SECURE);
+ session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+ }
+
+ // QuicCryptoStream implementation
+ bool encryption_established() const override {
+ return encryption_established_;
+ }
+ bool handshake_confirmed() const override { return handshake_confirmed_; }
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override {
+ return *params_;
+ }
+ CryptoMessageParser* crypto_message_parser() override {
+ return QuicCryptoHandshaker::crypto_message_parser();
+ }
+
+ MOCK_METHOD0(OnCanWrite, void());
+ bool HasPendingCryptoRetransmission() override { return false; }
+
+ MOCK_CONST_METHOD0(HasPendingRetransmission, bool());
+
+ private:
+ using QuicCryptoStream::session;
+
+ bool encryption_established_;
+ bool handshake_confirmed_;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+};
+
+class TestStream : public QuicStream {
+ public:
+ TestStream(QuicStreamId id, QuicSession* session, StreamType type)
+ : QuicStream(id, session, /*is_static=*/false, type) {}
+
+ TestStream(PendingStream pending, StreamType type)
+ : QuicStream(std::move(pending), type) {}
+
+ using QuicStream::CloseReadSide;
+ using QuicStream::CloseWriteSide;
+ using QuicStream::WriteMemSlices;
+ using QuicStream::WritevData;
+
+ void OnDataAvailable() override {}
+
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD3(RetransmitStreamData,
+ bool(QuicStreamOffset, QuicByteCount, bool));
+
+ MOCK_CONST_METHOD0(HasPendingRetransmission, bool());
+ MOCK_METHOD1(OnStopSending, void(uint16_t code));
+};
+
+class TestSession : public QuicSession {
+ public:
+ explicit TestSession(QuicConnection* connection,
+ MockQuicSessionVisitor* session_visitor)
+ : QuicSession(connection,
+ session_visitor,
+ DefaultQuicConfig(),
+ CurrentSupportedVersions()),
+ crypto_stream_(this),
+ writev_consumes_all_data_(false),
+ should_buffer_incoming_streams_(false),
+ num_incoming_streams_created_(0) {
+ Initialize();
+ this->connection()->SetEncrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullEncrypter>(connection->perspective()));
+ }
+
+ ~TestSession() override { delete connection(); }
+
+ TestCryptoStream* GetMutableCryptoStream() override {
+ return &crypto_stream_;
+ }
+
+ const TestCryptoStream* GetCryptoStream() const override {
+ return &crypto_stream_;
+ }
+
+ TestStream* CreateOutgoingBidirectionalStream() {
+ QuicStreamId id = GetNextOutgoingBidirectionalStreamId();
+ if (id ==
+ QuicUtils::GetInvalidStreamId(connection()->transport_version())) {
+ return nullptr;
+ }
+ TestStream* stream = new TestStream(id, this, BIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ TestStream* CreateOutgoingUnidirectionalStream() {
+ TestStream* stream = new TestStream(GetNextOutgoingUnidirectionalStreamId(),
+ this, WRITE_UNIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ TestStream* CreateIncomingStream(QuicStreamId id) override {
+ // Enforce the limit on the number of open streams.
+ if (GetNumOpenIncomingStreams() + 1 >
+ max_open_incoming_bidirectional_streams() &&
+ connection()->transport_version() != QUIC_VERSION_99) {
+ // No need to do this test for version 99; it's done by
+ // QuicSession::GetOrCreateDynamicStream.
+ connection()->CloseConnection(
+ QUIC_TOO_MANY_OPEN_STREAMS, "Too many streams!",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return nullptr;
+ }
+
+ TestStream* stream =
+ new TestStream(id, this,
+ DetermineStreamType(
+ id, connection()->transport_version(), perspective(),
+ /*is_incoming=*/true, BIDIRECTIONAL));
+ ActivateStream(QuicWrapUnique(stream));
+ ++num_incoming_streams_created_;
+ return stream;
+ }
+
+ TestStream* CreateIncomingStream(PendingStream pending) override {
+ QuicStreamId id = pending.id();
+ TestStream* stream =
+ new TestStream(std::move(pending),
+ DetermineStreamType(
+ id, connection()->transport_version(), perspective(),
+ /*is_incoming=*/true, BIDIRECTIONAL));
+ ActivateStream(QuicWrapUnique(stream));
+ ++num_incoming_streams_created_;
+ return stream;
+ }
+
+ bool IsClosedStream(QuicStreamId id) {
+ return QuicSession::IsClosedStream(id);
+ }
+
+ QuicStream* GetOrCreateDynamicStream(QuicStreamId stream_id) {
+ return QuicSession::GetOrCreateDynamicStream(stream_id);
+ }
+
+ bool ShouldKeepConnectionAlive() const override {
+ return GetNumOpenDynamicStreams() > 0;
+ }
+
+ QuicConsumedData WritevData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) override {
+ bool fin = state != NO_FIN;
+ QuicConsumedData consumed(write_length, fin);
+ if (!writev_consumes_all_data_) {
+ consumed =
+ QuicSession::WritevData(stream, id, write_length, offset, state);
+ }
+ if (fin && consumed.fin_consumed) {
+ stream->set_fin_sent(true);
+ }
+ QuicSessionPeer::GetWriteBlockedStreams(this)->UpdateBytesForStream(
+ id, consumed.bytes_consumed);
+ return consumed;
+ }
+
+ MOCK_METHOD0(OnCanCreateNewOutgoingStream, void());
+
+ void set_writev_consumes_all_data(bool val) {
+ writev_consumes_all_data_ = val;
+ }
+
+ QuicConsumedData SendStreamData(QuicStream* stream) {
+ struct iovec iov;
+ if (stream->id() !=
+ QuicUtils::GetCryptoStreamId(connection()->transport_version()) &&
+ this->connection()->encryption_level() != ENCRYPTION_FORWARD_SECURE) {
+ this->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ }
+ MakeIOVector("not empty", &iov);
+ QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9);
+ QuicConsumedData consumed = WritevData(stream, stream->id(), 9, 0, FIN);
+ QuicStreamPeer::SendBuffer(stream).OnStreamDataConsumed(
+ consumed.bytes_consumed);
+ return consumed;
+ }
+
+ bool ClearControlFrame(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ bool SaveFrame(const QuicFrame& frame) {
+ save_frame_ = frame;
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ const QuicFrame& save_frame() { return save_frame_; }
+
+ QuicConsumedData SendLargeFakeData(QuicStream* stream, int bytes) {
+ DCHECK(writev_consumes_all_data_);
+ return WritevData(stream, stream->id(), bytes, 0, FIN);
+ }
+
+ bool ShouldBufferIncomingStream(QuicStreamId id) const override {
+ return should_buffer_incoming_streams_;
+ }
+
+ void set_should_buffer_incoming_streams(bool should_buffer_incoming_streams) {
+ should_buffer_incoming_streams_ = should_buffer_incoming_streams;
+ }
+
+ int num_incoming_streams_created() const {
+ return num_incoming_streams_created_;
+ }
+
+ using QuicSession::ActivateStream;
+ using QuicSession::closed_streams;
+ using QuicSession::zombie_streams;
+
+ private:
+ StrictMock<TestCryptoStream> crypto_stream_;
+
+ bool writev_consumes_all_data_;
+ bool should_buffer_incoming_streams_;
+ QuicFrame save_frame_;
+ int num_incoming_streams_created_;
+};
+
+class QuicSessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
+ protected:
+ explicit QuicSessionTestBase(Perspective perspective)
+ : connection_(
+ new StrictMock<MockQuicConnection>(&helper_,
+ &alarm_factory_,
+ perspective,
+ SupportedVersions(GetParam()))),
+ session_(connection_, &session_visitor_) {
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+ .Times(testing::AnyNumber());
+ }
+
+ void CheckClosedStreams() {
+ QuicStreamId first_stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT);
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ first_stream_id =
+ QuicUtils::GetCryptoStreamId(connection_->transport_version());
+ }
+ for (QuicStreamId i = first_stream_id; i < 100; i++) {
+ if (!QuicContainsKey(closed_streams_, i)) {
+ EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i;
+ } else {
+ EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i;
+ }
+ }
+ }
+
+ void CloseStream(QuicStreamId id) {
+ if (session_.connection()->transport_version() == QUIC_VERSION_99 &&
+ QuicUtils::GetStreamType(id, session_.perspective(),
+ session_.IsIncomingStream(id)) ==
+ READ_UNIDIRECTIONAL) {
+ // Verify reset is not sent for READ_UNIDIRECTIONAL streams.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(0);
+ } else {
+ // Verify reset IS sent for BIDIRECTIONAL streams.
+ if (session_.connection()->transport_version() == QUIC_VERSION_99) {
+ // Once for the RST_STREAM, Once for the STOP_SENDING
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ } else {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ }
+ EXPECT_CALL(*connection_, OnStreamReset(id, _));
+ }
+ session_.CloseStream(id);
+ closed_streams_.insert(id);
+ }
+
+ QuicTransportVersion transport_version() const {
+ return connection_->transport_version();
+ }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT) +
+ QuicUtils::StreamIdDelta(connection_->transport_version()) * n;
+ }
+
+ QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) {
+ return QuicUtils::GetFirstUnidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT) +
+ QuicUtils::StreamIdDelta(connection_->transport_version()) * n;
+ }
+
+ QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_SERVER) +
+ QuicUtils::StreamIdDelta(connection_->transport_version()) * n;
+ }
+
+ QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
+ return QuicUtils::GetFirstUnidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_SERVER) +
+ QuicUtils::StreamIdDelta(connection_->transport_version()) * n;
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ NiceMock<MockQuicSessionVisitor> session_visitor_;
+ StrictMock<MockQuicConnection>* connection_;
+ TestSession session_;
+ std::set<QuicStreamId> closed_streams_;
+};
+
+class QuicSessionTestServer : public QuicSessionTestBase {
+ public:
+ // CheckMultiPathResponse validates that a written packet
+ // contains both expected path responses.
+ WriteResult CheckMultiPathResponse(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ QuicEncryptedPacket packet(buffer, buf_len);
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_))
+ .WillOnce(
+ WithArg<0>(Invoke([this](const QuicPathResponseFrame& frame) {
+ EXPECT_EQ(path_frame_buffer1_, frame.data_buffer);
+ return true;
+ })));
+ EXPECT_CALL(framer_visitor_, OnPathResponseFrame(_))
+ .WillOnce(
+ WithArg<0>(Invoke([this](const QuicPathResponseFrame& frame) {
+ EXPECT_EQ(path_frame_buffer2_, frame.data_buffer);
+ return true;
+ })));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ client_framer_.ProcessPacket(packet);
+ return WriteResult(WRITE_STATUS_OK, 0);
+ }
+
+ protected:
+ QuicSessionTestServer()
+ : QuicSessionTestBase(Perspective::IS_SERVER),
+ path_frame_buffer1_({0, 1, 2, 3, 4, 5, 6, 7}),
+ path_frame_buffer2_({8, 9, 10, 11, 12, 13, 14, 15}),
+ client_framer_(SupportedVersions(GetParam()),
+ QuicTime::Zero(),
+ Perspective::IS_CLIENT,
+ kQuicDefaultConnectionIdLength) {
+ client_framer_.set_visitor(&framer_visitor_);
+ }
+
+ QuicPathFrameBuffer path_frame_buffer1_;
+ QuicPathFrameBuffer path_frame_buffer2_;
+ StrictMock<MockFramerVisitor> framer_visitor_;
+ // Framer used to process packets sent by server.
+ QuicFramer client_framer_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSessionTestServer,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSessionTestServer, PeerAddress) {
+ EXPECT_EQ(QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort),
+ session_.peer_address());
+}
+
+TEST_P(QuicSessionTestServer, SelfAddress) {
+ EXPECT_TRUE(session_.self_address().IsInitialized());
+}
+
+TEST_P(QuicSessionTestServer, DontCallOnWriteBlockedForDisconnectedConnection) {
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _))
+ .WillOnce(
+ Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+ connection_->CloseConnection(QUIC_NO_ERROR, "Everything is fine.",
+ ConnectionCloseBehavior::SILENT_CLOSE);
+ ASSERT_FALSE(connection_->connected());
+
+ EXPECT_CALL(session_visitor_, OnWriteBlocked(_)).Times(0);
+ session_.OnWriteBlocked();
+}
+
+TEST_P(QuicSessionTestServer, IsCryptoHandshakeConfirmed) {
+ EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
+ EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
+}
+
+TEST_P(QuicSessionTestServer, IsClosedStreamDefault) {
+ // Ensure that no streams are initially closed.
+ QuicStreamId first_stream_id = QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT);
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ first_stream_id =
+ QuicUtils::GetCryptoStreamId(connection_->transport_version());
+ }
+ for (QuicStreamId i = first_stream_id; i < 100; i++) {
+ EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i;
+ }
+}
+
+TEST_P(QuicSessionTestServer, AvailableBidirectionalStreams) {
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedBidirectionalId(3)) != nullptr);
+ // Smaller bidirectional streams should be available.
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedBidirectionalId(1)));
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedBidirectionalId(2)));
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedBidirectionalId(2)) != nullptr);
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedBidirectionalId(1)) != nullptr);
+}
+
+TEST_P(QuicSessionTestServer, AvailableUnidirectionalStreams) {
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedUnidirectionalId(3)) != nullptr);
+ // Smaller unidirectional streams should be available.
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedUnidirectionalId(1)));
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedUnidirectionalId(2)));
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedUnidirectionalId(2)) != nullptr);
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedUnidirectionalId(1)) != nullptr);
+}
+
+TEST_P(QuicSessionTestServer, MaxAvailableBidirectionalStreams) {
+ if (transport_version() == QUIC_VERSION_99) {
+ EXPECT_EQ(session_.max_open_incoming_bidirectional_streams(),
+ session_.MaxAvailableBidirectionalStreams());
+ } else {
+ // The protocol specification requires that there can be at least 10 times
+ // as many available streams as the connection's maximum open streams.
+ EXPECT_EQ(session_.max_open_incoming_bidirectional_streams() *
+ kMaxAvailableStreamsMultiplier,
+ session_.MaxAvailableBidirectionalStreams());
+ }
+}
+
+TEST_P(QuicSessionTestServer, MaxAvailableUnidirectionalStreams) {
+ if (transport_version() == QUIC_VERSION_99) {
+ EXPECT_EQ(session_.max_open_incoming_unidirectional_streams(),
+ session_.MaxAvailableUnidirectionalStreams());
+ } else {
+ // The protocol specification requires that there can be at least 10 times
+ // as many available streams as the connection's maximum open streams.
+ EXPECT_EQ(session_.max_open_incoming_unidirectional_streams() *
+ kMaxAvailableStreamsMultiplier,
+ session_.MaxAvailableUnidirectionalStreams());
+ }
+}
+
+TEST_P(QuicSessionTestServer, IsClosedBidirectionalStreamLocallyCreated) {
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0), stream2->id());
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_EQ(GetNthServerInitiatedBidirectionalId(1), stream4->id());
+
+ CheckClosedStreams();
+ CloseStream(GetNthServerInitiatedBidirectionalId(0));
+ CheckClosedStreams();
+ CloseStream(GetNthServerInitiatedBidirectionalId(1));
+ CheckClosedStreams();
+}
+
+TEST_P(QuicSessionTestServer, IsClosedUnidirectionalStreamLocallyCreated) {
+ TestStream* stream2 = session_.CreateOutgoingUnidirectionalStream();
+ EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(0), stream2->id());
+ TestStream* stream4 = session_.CreateOutgoingUnidirectionalStream();
+ EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(1), stream4->id());
+
+ CheckClosedStreams();
+ CloseStream(GetNthServerInitiatedUnidirectionalId(0));
+ CheckClosedStreams();
+ CloseStream(GetNthServerInitiatedUnidirectionalId(1));
+ CheckClosedStreams();
+}
+
+TEST_P(QuicSessionTestServer, IsClosedBidirectionalStreamPeerCreated) {
+ QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0);
+ QuicStreamId stream_id2 = GetNthClientInitiatedBidirectionalId(1);
+ session_.GetOrCreateDynamicStream(stream_id1);
+ session_.GetOrCreateDynamicStream(stream_id2);
+
+ CheckClosedStreams();
+ CloseStream(stream_id1);
+ CheckClosedStreams();
+ CloseStream(stream_id2);
+ // Create a stream, and make another available.
+ QuicStream* stream3 = session_.GetOrCreateDynamicStream(
+ stream_id2 +
+ 2 * QuicUtils::StreamIdDelta(connection_->transport_version()));
+ CheckClosedStreams();
+ // Close one, but make sure the other is still not closed
+ CloseStream(stream3->id());
+ CheckClosedStreams();
+}
+
+TEST_P(QuicSessionTestServer, IsClosedUnidirectionalStreamPeerCreated) {
+ QuicStreamId stream_id1 = GetNthClientInitiatedUnidirectionalId(0);
+ QuicStreamId stream_id2 = GetNthClientInitiatedUnidirectionalId(1);
+ session_.GetOrCreateDynamicStream(stream_id1);
+ session_.GetOrCreateDynamicStream(stream_id2);
+
+ CheckClosedStreams();
+ CloseStream(stream_id1);
+ CheckClosedStreams();
+ CloseStream(stream_id2);
+ // Create a stream, and make another available.
+ QuicStream* stream3 = session_.GetOrCreateDynamicStream(
+ stream_id2 +
+ 2 * QuicUtils::StreamIdDelta(connection_->transport_version()));
+ CheckClosedStreams();
+ // Close one, but make sure the other is still not closed
+ CloseStream(stream3->id());
+ CheckClosedStreams();
+}
+
+TEST_P(QuicSessionTestServer, MaximumAvailableOpenedBidirectionalStreams) {
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
+ session_.GetOrCreateDynamicStream(stream_id);
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_NE(
+ nullptr,
+ session_.GetOrCreateDynamicStream(GetNthClientInitiatedBidirectionalId(
+ session_.max_open_incoming_bidirectional_streams() - 1)));
+}
+
+TEST_P(QuicSessionTestServer, MaximumAvailableOpenedUnidirectionalStreams) {
+ QuicStreamId stream_id = GetNthClientInitiatedUnidirectionalId(0);
+ session_.GetOrCreateDynamicStream(stream_id);
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_NE(
+ nullptr,
+ session_.GetOrCreateDynamicStream(GetNthClientInitiatedUnidirectionalId(
+ session_.max_open_incoming_unidirectional_streams() - 1)));
+}
+
+TEST_P(QuicSessionTestServer, TooManyAvailableBidirectionalStreams) {
+ QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0);
+ QuicStreamId stream_id2;
+ EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id1));
+ // A stream ID which is too large to create.
+ stream_id2 = GetNthClientInitiatedBidirectionalId(
+ session_.MaxAvailableBidirectionalStreams() + 2);
+ if (transport_version() == QUIC_VERSION_99) {
+ // V99 terminates the connection with invalid stream id
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ } else {
+ // other versions terminate the connection with
+ // QUIC_TOO_MANY_AVAILABLE_STREAMS.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _));
+ }
+ EXPECT_EQ(nullptr, session_.GetOrCreateDynamicStream(stream_id2));
+}
+
+TEST_P(QuicSessionTestServer, TooManyAvailableUnidirectionalStreams) {
+ QuicStreamId stream_id1 = GetNthClientInitiatedUnidirectionalId(0);
+ QuicStreamId stream_id2;
+ EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id1));
+ // A stream ID which is too large to create.
+ stream_id2 = GetNthClientInitiatedUnidirectionalId(
+ session_.MaxAvailableUnidirectionalStreams() + 2);
+ if (transport_version() == QUIC_VERSION_99) {
+ // V99 terminates the connection with invalid stream id
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ } else {
+ // other versions terminate the connection with
+ // QUIC_TOO_MANY_AVAILABLE_STREAMS.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _));
+ }
+ EXPECT_EQ(nullptr, session_.GetOrCreateDynamicStream(stream_id2));
+}
+
+TEST_P(QuicSessionTestServer, ManyAvailableBidirectionalStreams) {
+ // When max_open_streams_ is 200, should be able to create 200 streams
+ // out-of-order, that is, creating the one with the largest stream ID first.
+ QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
+ // Create one stream.
+ EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id));
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+
+ // Create the largest stream ID of a threatened total of 200 streams.
+ // GetNth... starts at 0, so for 200 streams, get the 199th.
+ EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedBidirectionalId(199)));
+}
+
+TEST_P(QuicSessionTestServer, ManyAvailableUnidirectionalStreams) {
+ // When max_open_streams_ is 200, should be able to create 200 streams
+ // out-of-order, that is, creating the one with the largest stream ID first.
+ QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
+ QuicStreamId stream_id = GetNthClientInitiatedUnidirectionalId(0);
+ // Create one stream.
+ EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id));
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+
+ // Create the largest stream ID of a threatened total of 200 streams.
+ // GetNth... starts at 0, so for 200 streams, get the 199th.
+ EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(
+ GetNthClientInitiatedUnidirectionalId(199)));
+}
+
+TEST_P(QuicSessionTestServer, DebugDFatalIfMarkingClosedStreamWriteBlocked) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamId closed_stream_id = stream2->id();
+ // Close the stream.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(closed_stream_id, _));
+ stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ std::string msg =
+ QuicStrCat("Marking unknown stream ", closed_stream_id, " blocked.");
+ EXPECT_QUIC_BUG(session_.MarkConnectionLevelWriteBlocked(closed_stream_id),
+ msg);
+}
+
+TEST_P(QuicSessionTestServer, OnCanWrite) {
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ InSequence s;
+
+ // Reregister, to test the loop limit.
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ // 2 will get called a second time as it didn't finish its block
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() {
+ session_.SendStreamData(stream6);
+ }));
+ // 4 will not get called, as we exceeded the loop limit.
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTestServer, TestBatchedWrites) {
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.set_writev_consumes_all_data(true);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ // With two sessions blocked, we should get two write calls. They should both
+ // go to the first stream as it will only write 6k and mark itself blocked
+ // again.
+ InSequence s;
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendLargeFakeData(stream2, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendLargeFakeData(stream2, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ session_.OnCanWrite();
+
+ // We should get one more call for stream2, at which point it has used its
+ // write quota and we move over to stream 4.
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendLargeFakeData(stream2, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendLargeFakeData(stream4, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ }));
+ session_.OnCanWrite();
+
+ // Now let stream 4 do the 2nd of its 3 writes, but add a block for a high
+ // priority stream 6. 4 should be preempted. 6 will write but *not* block so
+ // will cede back to 4.
+ stream6->SetPriority(kV3HighestPriority);
+ EXPECT_CALL(*stream4, OnCanWrite())
+ .WillOnce(Invoke([this, stream4, stream6]() {
+ session_.SendLargeFakeData(stream4, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ }));
+ EXPECT_CALL(*stream6, OnCanWrite())
+ .WillOnce(Invoke([this, stream4, stream6]() {
+ session_.SendStreamData(stream6);
+ session_.SendLargeFakeData(stream4, 6000);
+ }));
+ session_.OnCanWrite();
+
+ // Stream4 alread did 6k worth of writes, so after doing another 12k it should
+ // cede and 2 should resume.
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendLargeFakeData(stream4, 12000);
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ }));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendLargeFakeData(stream2, 6000);
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ }));
+ session_.OnCanWrite();
+}
+
+TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) {
+ // Encryption needs to be established before data can be sent.
+ CryptoHandshakeMessage msg;
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm, GetCongestionWindow())
+ .WillRepeatedly(Return(kMaxOutgoingPacketSize * 10));
+ EXPECT_CALL(*send_algorithm, InRecovery()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendStreamData(stream4);
+ }));
+ EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() {
+ session_.SendStreamData(stream6);
+ }));
+
+ // Expect that we only send one packet, the writes from different streams
+ // should be bundled together.
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+ EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _));
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTestServer, OnCanWriteCongestionControlBlocks) {
+ session_.set_writev_consumes_all_data(true);
+ InSequence s;
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() {
+ session_.SendStreamData(stream6);
+ }));
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false));
+ // stream4->OnCanWrite is not called.
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Still congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false));
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // stream4->OnCanWrite is called once the connection stops being
+ // congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendStreamData(stream4);
+ }));
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTestServer, OnCanWriteWriterBlocks) {
+ // Drive congestion control manually in order to ensure that
+ // application-limited signaling is handled correctly.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true));
+
+ // Drive packet writer manually.
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _)).Times(0);
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+
+ EXPECT_CALL(*stream2, OnCanWrite()).Times(0);
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)).Times(0);
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTestServer, BufferedHandshake) {
+ session_.set_writev_consumes_all_data(true);
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
+
+ // Test that blocking other streams does not change our status.
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ TestStream* stream3 = session_.CreateOutgoingBidirectionalStream();
+ session_.MarkConnectionLevelWriteBlocked(stream3->id());
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ // Blocking (due to buffering of) the Crypto stream is detected.
+ session_.MarkConnectionLevelWriteBlocked(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ InSequence s;
+ // Force most streams to re-register, which is common scenario when we block
+ // the Crypto stream, and only the crypto stream can "really" write.
+
+ // Due to prioritization, we *should* be asked to write the crypto stream
+ // first.
+ // Don't re-register the crypto stream (which signals complete writing).
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(Invoke([this, stream3]() {
+ session_.SendStreamData(stream3);
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendStreamData(stream4);
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ }));
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote.
+}
+
+TEST_P(QuicSessionTestServer, OnCanWriteWithClosedStream) {
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ CloseStream(stream6->id());
+
+ InSequence s;
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() {
+ session_.SendStreamData(stream2);
+ }));
+ EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() {
+ session_.SendStreamData(stream4);
+ }));
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTestServer, OnCanWriteLimitsNumWritesIfFlowControlBlocked) {
+ // Drive congestion control manually in order to ensure that
+ // application-limited signaling is handled correctly.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true));
+
+ // Ensure connection level flow control blockage.
+ QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0);
+ EXPECT_TRUE(session_.flow_controller()->IsBlocked());
+ EXPECT_TRUE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+
+ // Mark the crypto and headers streams as write blocked, we expect them to be
+ // allowed to write later.
+ session_.MarkConnectionLevelWriteBlocked(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()));
+
+ // Create a data stream, and although it is write blocked we never expect it
+ // to be allowed to write as we are connection level flow control blocked.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ session_.MarkConnectionLevelWriteBlocked(stream->id());
+ EXPECT_CALL(*stream, OnCanWrite()).Times(0);
+
+ // The crypto and headers streams should be called even though we are
+ // connection flow control blocked.
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+
+ // After the crypto and header streams perform a write, the connection will be
+ // blocked by the flow control, hence it should become application-limited.
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTestServer, SendGoAway) {
+ if (transport_version() == QUIC_VERSION_99) {
+ // GoAway frames are not in version 99
+ return;
+ }
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(
+ Invoke(connection_, &MockQuicConnection::ReallySendControlFrame));
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+
+ const QuicStreamId kTestStreamId = 5u;
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ EXPECT_CALL(*connection_,
+ OnStreamReset(kTestStreamId, QUIC_STREAM_PEER_GOING_AWAY))
+ .Times(0);
+ EXPECT_TRUE(session_.GetOrCreateDynamicStream(kTestStreamId));
+}
+
+TEST_P(QuicSessionTestServer, DoNotSendGoAwayTwice) {
+ if (transport_version() == QUIC_VERSION_99) {
+ // TODO(b/118808809): Enable this test for version 99 when GOAWAY is
+ // supported.
+ return;
+ }
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+}
+
+TEST_P(QuicSessionTestServer, InvalidGoAway) {
+ if (transport_version() == QUIC_VERSION_99) {
+ // TODO(b/118808809): Enable this test for version 99 when GOAWAY is
+ // supported.
+ return;
+ }
+ QuicGoAwayFrame go_away(kInvalidControlFrameId, QUIC_PEER_GOING_AWAY,
+ session_.next_outgoing_bidirectional_stream_id(), "");
+ session_.OnGoAway(go_away);
+}
+
+// Test that server session will send a connectivity probe in response to a
+// connectivity probe on the same path.
+TEST_P(QuicSessionTestServer, ServerReplyToConnectivityProbe) {
+ QuicSocketAddress old_peer_address =
+ QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort);
+ EXPECT_EQ(old_peer_address, session_.peer_address());
+
+ QuicSocketAddress new_peer_address =
+ QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort + 1);
+
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, new_peer_address, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+ EXPECT_CALL(*connection_, SendConnectivityProbingResponsePacket(_))
+ .WillOnce(Invoke(
+ connection_,
+ &MockQuicConnection::ReallySendConnectivityProbingResponsePacket));
+ if (transport_version() == QUIC_VERSION_99) {
+ // Need to explicitly do this to emulate the reception of a PathChallenge,
+ // which stores its payload for use in generating the response.
+ connection_->OnPathChallengeFrame(
+ QuicPathChallengeFrame(0, path_frame_buffer1_));
+ }
+ session_.OnConnectivityProbeReceived(session_.self_address(),
+ new_peer_address);
+ EXPECT_EQ(old_peer_address, session_.peer_address());
+}
+
+// Same as above, but check that if there are two PATH_CHALLENGE frames in the
+// packet, the response has both of them AND we do not do migration. This for
+// V99 only.
+TEST_P(QuicSessionTestServer, ServerReplyToConnectivityProbes) {
+ if (transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ QuicSocketAddress old_peer_address =
+ QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort);
+ EXPECT_EQ(old_peer_address, session_.peer_address());
+
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ // CheckMultiPathResponse validates that the written packet
+ // contains both path responses.
+ EXPECT_CALL(*writer, WritePacket(_, _, _, old_peer_address, _))
+ .WillOnce(Invoke(this, &QuicSessionTestServer::CheckMultiPathResponse));
+
+ EXPECT_CALL(*connection_, SendConnectivityProbingResponsePacket(_))
+ .WillOnce(Invoke(
+ connection_,
+ &MockQuicConnection::ReallySendConnectivityProbingResponsePacket));
+ QuicConnectionPeer::SetLastHeaderFormat(connection_,
+ IETF_QUIC_SHORT_HEADER_PACKET);
+ // Need to explicitly do this to emulate the reception of a PathChallenge,
+ // which stores its payload for use in generating the response.
+ connection_->OnPathChallengeFrame(
+ QuicPathChallengeFrame(0, path_frame_buffer1_));
+ connection_->OnPathChallengeFrame(
+ QuicPathChallengeFrame(0, path_frame_buffer2_));
+ session_.OnConnectivityProbeReceived(session_.self_address(),
+ old_peer_address);
+}
+
+TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) {
+ EXPECT_EQ(kInitialIdleTimeoutSecs + 3,
+ QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
+ CryptoHandshakeMessage msg;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+ EXPECT_EQ(kMaximumIdleTimeoutSecs + 3,
+ QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
+}
+
+TEST_P(QuicSessionTestServer, OnStreamFrameFinStaticStreamId) {
+ // Send two bytes of payload.
+ QuicStreamFrame data1(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()), true, 0,
+ QuicStringPiece("HT"));
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to close a static stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.OnStreamFrame(data1);
+}
+
+TEST_P(QuicSessionTestServer, OnRstStreamStaticStreamId) {
+ // Send two bytes of payload.
+ QuicRstStreamFrame rst1(
+ kInvalidControlFrameId,
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.OnRstStream(rst1);
+}
+
+TEST_P(QuicSessionTestServer, OnStreamFrameInvalidStreamId) {
+ // Send two bytes of payload.
+ QuicStreamFrame data1(
+ QuicUtils::GetInvalidStreamId(connection_->transport_version()), true, 0,
+ QuicStringPiece("HT"));
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received data for an invalid stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.OnStreamFrame(data1);
+}
+
+TEST_P(QuicSessionTestServer, OnRstStreamInvalidStreamId) {
+ // Send two bytes of payload.
+ QuicRstStreamFrame rst1(
+ kInvalidControlFrameId,
+ QuicUtils::GetInvalidStreamId(connection_->transport_version()),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Received data for an invalid stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+ session_.OnRstStream(rst1);
+}
+
+TEST_P(QuicSessionTestServer, HandshakeUnblocksFlowControlBlockedStream) {
+ // Test that if a stream is flow control blocked, then on receipt of the SHLO
+ // containing a suitable send window offset, the stream becomes unblocked.
+
+ // Ensure that Writev consumes all the data it is given (simulate no socket
+ // blocking).
+ session_.set_writev_consumes_all_data(true);
+
+ // Create a stream, and send enough data to make it flow control blocked.
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ std::string body(kMinimumFlowControlSendWindow, '.');
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AtLeast(1));
+ stream2->WriteOrBufferData(body, false, nullptr);
+ EXPECT_TRUE(stream2->flow_controller()->IsBlocked());
+ EXPECT_TRUE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_TRUE(session_.IsStreamFlowControlBlocked());
+
+ // Now complete the crypto handshake, resulting in an increased flow control
+ // send window.
+ CryptoHandshakeMessage msg;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+ EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked(&session_, stream2->id()));
+ // Stream is now unblocked.
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+}
+
+TEST_P(QuicSessionTestServer, HandshakeUnblocksFlowControlBlockedCryptoStream) {
+ if (QuicVersionUsesCryptoFrames(GetParam().transport_version)) {
+ // 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->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ for (QuicStreamId i = 0;
+ !crypto_stream->flow_controller()->IsBlocked() && 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);
+ crypto_stream->SendHandshakeMessage(crypto_message);
+ char buf[1000];
+ QuicDataWriter writer(1000, buf, NETWORK_BYTE_ORDER);
+ crypto_stream->WriteStreamData(offset, crypto_message.size(), &writer);
+ }
+ EXPECT_TRUE(crypto_stream->flow_controller()->IsBlocked());
+ 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.
+ CryptoHandshakeMessage msg;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
+ 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->flow_controller()->IsBlocked());
+ EXPECT_FALSE(session_.IsConnectionFlowControlBlocked());
+ EXPECT_FALSE(session_.IsStreamFlowControlBlocked());
+}
+
+TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingRstOutOfOrder) {
+ // Test that when we receive an out of order stream RST we correctly adjust
+ // our connection level flow control receive window.
+ // On close, the stream should mark as consumed all bytes between the highest
+ // byte consumed so far and the final byte offset from the RST frame.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+
+ const QuicStreamOffset kByteOffset =
+ 1 + kInitialSessionFlowControlWindowForTest / 2;
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
+ QUIC_STREAM_CANCELLED, kByteOffset);
+ session_.OnRstStream(rst_frame);
+ if (transport_version() == QUIC_VERSION_99) {
+ // The test is predicated on the stream being fully closed. For V99, the
+ // RST_STREAM only does one side (the read side from the perspective of the
+ // node receiving the RST_STREAM). This is needed to fully close the
+ // stream and therefore fulfill all of the expects.
+ QuicStopSendingFrame frame(kInvalidControlFrameId, stream->id(),
+ QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(session_.OnStopSendingFrame(frame));
+ }
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingFinAndLocalReset) {
+ // Test the situation where we receive a FIN on a stream, and before we fully
+ // consume all the data from the sequencer buffer we locally RST the stream.
+ // The bytes between highest consumed byte, and the final byte offset that we
+ // determined when the FIN arrived, should be marked as consumed at the
+ // connection level flow controller when the stream is reset.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+
+ const QuicStreamOffset kByteOffset =
+ kInitialSessionFlowControlWindowForTest / 2 - 1;
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, ".");
+ session_.OnStreamFrame(frame);
+ EXPECT_TRUE(connection_->connected());
+
+ EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed());
+ EXPECT_EQ(kByteOffset + frame.data_length,
+ stream->flow_controller()->highest_received_byte_offset());
+
+ // Reset stream locally.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_EQ(kByteOffset + frame.data_length,
+ session_.flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingFinAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a FIN from the peer, we correctly adjust our connection level flow
+ // control receive window.
+
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64_t kInitialConnectionBytesConsumed = 567;
+ const uint64_t kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+
+ // Now receive a response from the peer with a FIN. We should handle this by
+ // adjusting the connection level flow control receive window to take into
+ // account the total number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ std::string body = "hello";
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, QuicStringPiece(body));
+ session_.OnStreamFrame(frame);
+
+ QuicStreamOffset total_stream_bytes_sent_by_peer =
+ kByteOffset + body.length();
+ EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(
+ kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSessionTestServer, ConnectionFlowControlAccountingRstAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a RST from the peer, we correctly adjust our connection level flow
+ // control receive window.
+
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64_t kInitialConnectionBytesConsumed = 567;
+ const uint64_t kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream));
+
+ // Now receive a RST from the peer. We should handle this by adjusting the
+ // connection level flow control receive window to take into account the total
+ // number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
+ QUIC_STREAM_CANCELLED, kByteOffset);
+ session_.OnRstStream(rst_frame);
+
+ EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSessionTestServer, InvalidStreamFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) stream flow control window from
+ // the peer results in the connection being torn down.
+ const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _));
+ session_.OnConfigNegotiated();
+}
+
+TEST_P(QuicSessionTestServer, InvalidSessionFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) session flow control window
+ // from the peer results in the connection being torn down.
+ const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _));
+ session_.OnConfigNegotiated();
+}
+
+// Test negotiation of custom server initial flow control window.
+TEST_P(QuicSessionTestServer, CustomFlowControlWindow) {
+ QuicTagVector copt;
+ copt.push_back(kIFW7);
+ QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt);
+
+ session_.OnConfigNegotiated();
+ EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize(
+ session_.flow_controller()));
+}
+
+TEST_P(QuicSessionTestServer, FlowControlWithInvalidFinalOffset) {
+ // Test that if we receive a stream RST with a highest byte offset that
+ // violates flow control, that we close the connection.
+ const uint64_t kLargeOffset = kInitialSessionFlowControlWindowForTest + 1;
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _))
+ .Times(2);
+
+ // Check that stream frame + FIN results in connection close.
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ QuicStreamFrame frame(stream->id(), true, kLargeOffset, QuicStringPiece());
+ session_.OnStreamFrame(frame);
+
+ // Check that RST results in connection close.
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
+ QUIC_STREAM_CANCELLED, kLargeOffset);
+ session_.OnRstStream(rst_frame);
+}
+
+TEST_P(QuicSessionTestServer, TooManyUnfinishedStreamsCauseServerRejectStream) {
+ // If a buggy/malicious peer creates too many streams that are not ended
+ // with a FIN or RST then we send an RST to refuse streams. For V99 the
+ // connection is closed.
+ const QuicStreamId kMaxStreams = 5;
+ QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+ const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0);
+ const QuicStreamId kFinalStreamId =
+ GetNthClientInitiatedBidirectionalId(kMaxStreams);
+ // Create kMaxStreams data streams, and close them all without receiving a
+ // FIN or a RST_STREAM from the client.
+ for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId;
+ i += QuicUtils::StreamIdDelta(connection_->transport_version())) {
+ QuicStreamFrame data1(i, false, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ // EXPECT_EQ(1u, session_.GetNumOpenStreams());
+ if (transport_version() == QUIC_VERSION_99) {
+ // Expect two control frames, RST STREAM and STOP SENDING
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ } else {
+ // Expect one control frame, just RST STREAM
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ }
+ // Close stream. Should not make new streams available since
+ // the stream is not finished.
+ EXPECT_CALL(*connection_, OnStreamReset(i, _));
+ session_.CloseStream(i);
+ }
+
+ if (transport_version() == QUIC_VERSION_99) {
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Stream id 24 above 20", _));
+ } else {
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ EXPECT_CALL(*connection_,
+ OnStreamReset(kFinalStreamId, QUIC_REFUSED_STREAM))
+ .Times(1);
+ }
+ // Create one more data streams to exceed limit of open stream.
+ QuicStreamFrame data1(kFinalStreamId, false, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+}
+
+TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpenedOutgoing) {
+ // Verify that a draining stream (which has received a FIN but not consumed
+ // it) does not count against the open quota (because it is closed from the
+ // protocol point of view).
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = stream->id();
+ QuicStreamFrame data1(stream_id, true, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(1);
+ session_.StreamDraining(stream_id);
+}
+
+TEST_P(QuicSessionTestServer, NoPendingStreams) {
+ session_.set_should_buffer_incoming_streams(false);
+
+ QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
+ transport_version(), Perspective::IS_CLIENT);
+ QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ EXPECT_EQ(1, session_.num_incoming_streams_created());
+
+ QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data2);
+ EXPECT_EQ(1, session_.num_incoming_streams_created());
+}
+
+TEST_P(QuicSessionTestServer, PendingStreams) {
+ if (connection_->transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ session_.set_should_buffer_incoming_streams(true);
+
+ QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
+ transport_version(), Perspective::IS_CLIENT);
+ QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ EXPECT_EQ(0, session_.num_incoming_streams_created());
+
+ QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data2);
+ EXPECT_EQ(1, session_.num_incoming_streams_created());
+}
+
+TEST_P(QuicSessionTestServer, RstPendingStreams) {
+ if (connection_->transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+ session_.set_should_buffer_incoming_streams(true);
+
+ QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
+ transport_version(), Perspective::IS_CLIENT);
+ QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ EXPECT_EQ(0, session_.num_incoming_streams_created());
+
+ EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(1);
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(stream_id, QUIC_RST_ACKNOWLEDGEMENT))
+ .Times(1);
+ QuicRstStreamFrame rst1(kInvalidControlFrameId, stream_id,
+ QUIC_ERROR_PROCESSING_STREAM, 12);
+ session_.OnRstStream(rst1);
+ EXPECT_EQ(0, session_.num_incoming_streams_created());
+
+ QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data2);
+ EXPECT_EQ(0, session_.num_incoming_streams_created());
+}
+
+TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpened) {
+ // Verify that a draining stream (which has received a FIN but not consumed
+ // it) does not count against the open quota (because it is closed from the
+ // protocol point of view).
+ if (transport_version() == QUIC_VERSION_99) {
+ // On v99, we will expect to see a MAX_STREAM_ID go out when there are not
+ // enough streams to create the next one.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ } else {
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ }
+ EXPECT_CALL(*connection_, OnStreamReset(_, QUIC_REFUSED_STREAM)).Times(0);
+ const QuicStreamId kMaxStreams = 5;
+ QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+
+ // Create kMaxStreams + 1 data streams, and mark them draining.
+ const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0);
+ const QuicStreamId kFinalStreamId =
+ GetNthClientInitiatedBidirectionalId(2 * kMaxStreams + 1);
+ for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId;
+ i += QuicUtils::StreamIdDelta(connection_->transport_version())) {
+ QuicStreamFrame data1(i, true, 0, QuicStringPiece("HT"));
+ session_.OnStreamFrame(data1);
+ EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams());
+ session_.StreamDraining(i);
+ EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams());
+ }
+}
+
+class QuicSessionTestClient : public QuicSessionTestBase {
+ protected:
+ QuicSessionTestClient() : QuicSessionTestBase(Perspective::IS_CLIENT) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSessionTestClient,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSessionTestClient, AvailableBidirectionalStreamsClient) {
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedBidirectionalId(2)) != nullptr);
+ // Smaller bidirectional streams should be available.
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthServerInitiatedBidirectionalId(0)));
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthServerInitiatedBidirectionalId(1)));
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedBidirectionalId(0)) != nullptr);
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedBidirectionalId(1)) != nullptr);
+ // And 5 should be not available.
+ EXPECT_FALSE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedBidirectionalId(1)));
+}
+
+TEST_P(QuicSessionTestClient, AvailableUnidirectionalStreamsClient) {
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedUnidirectionalId(2)) != nullptr);
+ // Smaller unidirectional streams should be available.
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthServerInitiatedUnidirectionalId(0)));
+ EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthServerInitiatedUnidirectionalId(1)));
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedUnidirectionalId(0)) != nullptr);
+ ASSERT_TRUE(session_.GetOrCreateDynamicStream(
+ GetNthServerInitiatedUnidirectionalId(1)) != nullptr);
+ // And 5 should be not available.
+ EXPECT_FALSE(QuicSessionPeer::IsStreamAvailable(
+ &session_, GetNthClientInitiatedUnidirectionalId(1)));
+}
+
+TEST_P(QuicSessionTestClient, RecordFinAfterReadSideClosed) {
+ // Verify that an incoming FIN is recorded in a stream object even if the read
+ // side has been closed. This prevents an entry from being made in
+ // locally_closed_streams_highest_offset_ (which will never be deleted).
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = stream->id();
+
+ // Close the read side manually.
+ QuicStreamPeer::CloseReadSide(stream);
+
+ // Receive a stream data frame with FIN.
+ QuicStreamFrame frame(stream_id, true, 0, QuicStringPiece());
+ session_.OnStreamFrame(frame);
+ EXPECT_TRUE(stream->fin_received());
+
+ // Reset stream locally.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream));
+
+ EXPECT_TRUE(connection_->connected());
+ EXPECT_TRUE(QuicSessionPeer::IsStreamClosed(&session_, stream_id));
+ EXPECT_FALSE(QuicSessionPeer::IsStreamCreated(&session_, stream_id));
+
+ // The stream is not waiting for the arrival of the peer's final offset as it
+ // was received with the FIN earlier.
+ EXPECT_EQ(
+ 0u,
+ QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(&session_).size());
+}
+
+TEST_P(QuicSessionTestServer, ZombieStreams) {
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamPeer::SetStreamBytesWritten(3, stream2);
+ EXPECT_TRUE(stream2->IsWaitingForAcks());
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _));
+ session_.CloseStream(stream2->id());
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+ ASSERT_EQ(1u, session_.closed_streams()->size());
+ EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id());
+ session_.OnStreamDoneWaitingForAcks(stream2->id());
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+ EXPECT_EQ(1u, session_.closed_streams()->size());
+ EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id());
+}
+
+TEST_P(QuicSessionTestServer, RstStreamReceivedAfterRstStreamSent) {
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamPeer::SetStreamBytesWritten(3, stream2);
+ EXPECT_TRUE(stream2->IsWaitingForAcks());
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _));
+ EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(0);
+ stream2->Reset(quic::QUIC_STREAM_CANCELLED);
+
+ QuicRstStreamFrame rst1(kInvalidControlFrameId, stream2->id(),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ if (transport_version() != QUIC_VERSION_99) {
+ EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(1);
+ }
+ session_.OnRstStream(rst1);
+}
+
+// Regression test of b/71548958.
+TEST_P(QuicSessionTestServer, TestZombieStreams) {
+ session_.set_writev_consumes_all_data(true);
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ std::string body(100, '.');
+ stream2->WriteOrBufferData(body, false, nullptr);
+ EXPECT_TRUE(stream2->IsWaitingForAcks());
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream2).size());
+
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream2->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ // Just for the RST_STREAM
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ if (transport_version() == QUIC_VERSION_99) {
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream2->id(), QUIC_STREAM_CANCELLED));
+ } else {
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream2->id(), QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ stream2->OnStreamReset(rst_frame);
+
+ if (transport_version() == QUIC_VERSION_99) {
+ // The test is predicated on the stream being fully closed. For V99, the
+ // RST_STREAM only does one side (the read side from the perspective of the
+ // node receiving the RST_STREAM). This is needed to fully close the
+ // stream and therefore fulfill all of the expects.
+ QuicStopSendingFrame frame(kInvalidControlFrameId, stream2->id(),
+ QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(session_.OnStopSendingFrame(frame));
+ }
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+ ASSERT_EQ(1u, session_.closed_streams()->size());
+ EXPECT_EQ(stream2->id(), session_.closed_streams()->front()->id());
+
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ if (transport_version() == QUIC_VERSION_99) {
+ // Once for the RST_STREAM, once for the STOP_SENDING
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ } else {
+ // Just for the RST_STREAM
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ }
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream4->id(), QUIC_STREAM_CANCELLED));
+ stream4->WriteOrBufferData(body, false, nullptr);
+ // Note well: Reset() actually closes the stream in both directions. For
+ // GOOGLE QUIC it sends a RST_STREAM (which does a 2-way close), for IETF
+ // QUIC/V99 it sends both a RST_STREAM and a STOP_SENDING (each of which
+ // closes in only one direction).
+ stream4->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream4->id()));
+ EXPECT_EQ(2u, session_.closed_streams()->size());
+}
+
+TEST_P(QuicSessionTestServer, OnStreamFrameLost) {
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+ InSequence s;
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream();
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+
+ QuicStreamFrame frame1(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()), false, 0,
+ 1300);
+ QuicStreamFrame frame2(stream2->id(), false, 0, 9);
+ QuicStreamFrame frame3(stream4->id(), false, 0, 9);
+
+ // Lost data on cryption stream, streams 2 and 4.
+ EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true));
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+ .WillOnce(Return(true));
+ }
+ EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true));
+ session_.OnFrameLost(QuicFrame(frame3));
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ session_.OnFrameLost(QuicFrame(frame1));
+ } else {
+ QuicCryptoFrame crypto_frame(ENCRYPTION_INITIAL, 0, 1300);
+ session_.OnFrameLost(QuicFrame(&crypto_frame));
+ }
+ session_.OnFrameLost(QuicFrame(frame2));
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Mark streams 2 and 4 write blocked.
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+
+ // Lost data is retransmitted before new data, and retransmissions for crypto
+ // stream go first.
+ // Do not check congestion window when crypto stream has lost data.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).Times(0);
+ if (!QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+ EXPECT_CALL(*crypto_stream, HasPendingRetransmission())
+ .WillOnce(Return(false));
+ }
+ // Check congestion window for non crypto streams.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream4, OnCanWrite());
+ EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(false));
+ // Connection is blocked.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false));
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Unblock connection.
+ // Stream 2 retransmits lost data.
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false));
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ // Stream 2 sends new data.
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true));
+ EXPECT_CALL(*stream4, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTestServer, DonotRetransmitDataOfClosedStreams) {
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+ InSequence s;
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+
+ QuicStreamFrame frame1(stream2->id(), false, 0, 9);
+ QuicStreamFrame frame2(stream4->id(), false, 0, 9);
+ QuicStreamFrame frame3(stream6->id(), false, 0, 9);
+
+ EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(true));
+ EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true));
+ EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true));
+ session_.OnFrameLost(QuicFrame(frame3));
+ session_.OnFrameLost(QuicFrame(frame2));
+ session_.OnFrameLost(QuicFrame(frame1));
+
+ session_.MarkConnectionLevelWriteBlocked(stream2->id());
+ session_.MarkConnectionLevelWriteBlocked(stream4->id());
+ session_.MarkConnectionLevelWriteBlocked(stream6->id());
+
+ // Reset stream 4 locally.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream4->id(), _));
+ stream4->Reset(QUIC_STREAM_CANCELLED);
+
+ // Verify stream 4 is removed from streams with lost data list.
+ EXPECT_CALL(*stream6, OnCanWrite());
+ EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(false));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false));
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*stream6, OnCanWrite());
+ session_.OnCanWrite();
+}
+
+TEST_P(QuicSessionTestServer, RetransmitFrames) {
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+ InSequence s;
+
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream4 = session_.CreateOutgoingBidirectionalStream();
+ TestStream* stream6 = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ session_.SendWindowUpdate(stream2->id(), 9);
+
+ QuicStreamFrame frame1(stream2->id(), false, 0, 9);
+ QuicStreamFrame frame2(stream4->id(), false, 0, 9);
+ QuicStreamFrame frame3(stream6->id(), false, 0, 9);
+ QuicWindowUpdateFrame window_update(1, stream2->id(), 9);
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1));
+ frames.push_back(QuicFrame(&window_update));
+ frames.push_back(QuicFrame(frame2));
+ frames.push_back(QuicFrame(frame3));
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+
+ EXPECT_CALL(*stream2, RetransmitStreamData(_, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::ClearControlFrame));
+ EXPECT_CALL(*stream4, RetransmitStreamData(_, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*stream6, RetransmitStreamData(_, _, _)).WillOnce(Return(true));
+ EXPECT_CALL(*send_algorithm, OnApplicationLimited(_));
+ session_.RetransmitFrames(frames, TLP_RETRANSMISSION);
+}
+
+// Regression test of b/110082001.
+TEST_P(QuicSessionTestServer, RetransmitLostDataCausesConnectionClose) {
+ // This test mimics the scenario when a dynamic stream retransmits lost data
+ // and causes connection close.
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamFrame frame(stream->id(), false, 0, 9);
+
+ EXPECT_CALL(*stream, HasPendingRetransmission())
+ .Times(2)
+ .WillOnce(Return(true))
+ .WillOnce(Return(false));
+ session_.OnFrameLost(QuicFrame(frame));
+ // Retransmit stream data causes connection close. Stream has not sent fin
+ // yet, so an RST is sent.
+ EXPECT_CALL(*stream, OnCanWrite())
+ .WillOnce(Invoke(stream, &QuicStream::OnClose));
+ if (transport_version() == QUIC_VERSION_99) {
+ // Once for the RST_STREAM, once for the STOP_SENDING
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(2)
+ .WillRepeatedly(Invoke(&session_, &TestSession::SaveFrame));
+ } else {
+ // Just for the RST_STREAM
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(&session_, &TestSession::SaveFrame));
+ }
+ EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
+ session_.OnCanWrite();
+}
+
+TEST_P(QuicSessionTestServer, SendMessage) {
+ // Cannot send message when encryption is not established.
+ EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
+ quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
+ EXPECT_EQ(MessageResult(MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, 0),
+ session_.SendMessage(
+ MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
+ "", &storage)));
+
+ // Finish handshake.
+ CryptoHandshakeMessage handshake_message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(handshake_message);
+ EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
+
+ QuicStringPiece message;
+ EXPECT_CALL(*connection_, SendMessage(1, _))
+ .WillOnce(Return(MESSAGE_STATUS_SUCCESS));
+ EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1),
+ session_.SendMessage(
+ MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
+ message, &storage)));
+ // Verify message_id increases.
+ EXPECT_CALL(*connection_, SendMessage(2, _))
+ .WillOnce(Return(MESSAGE_STATUS_TOO_LARGE));
+ EXPECT_EQ(MessageResult(MESSAGE_STATUS_TOO_LARGE, 0),
+ session_.SendMessage(
+ MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
+ message, &storage)));
+ // Verify unsent message does not consume a message_id.
+ EXPECT_CALL(*connection_, SendMessage(2, _))
+ .WillOnce(Return(MESSAGE_STATUS_SUCCESS));
+ EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 2),
+ session_.SendMessage(
+ MakeSpan(connection_->helper()->GetStreamSendBufferAllocator(),
+ message, &storage)));
+
+ QuicMessageFrame frame(1);
+ QuicMessageFrame frame2(2);
+ EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame)));
+ EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame2)));
+
+ // Lost message 2.
+ session_.OnMessageLost(2);
+ EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame2)));
+
+ // message 1 gets acked.
+ session_.OnMessageAcked(1);
+ EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame)));
+}
+
+// Regression test of b/115323618.
+TEST_P(QuicSessionTestServer, LocallyResetZombieStreams) {
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ std::string body(100, '.');
+ stream2->CloseReadSide();
+ stream2->WriteOrBufferData(body, true, nullptr);
+ EXPECT_TRUE(stream2->IsWaitingForAcks());
+ // Verify stream2 is a zombie streams.
+ EXPECT_TRUE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+
+ QuicStreamFrame frame(stream2->id(), true, 0, 100);
+ EXPECT_CALL(*stream2, HasPendingRetransmission())
+ .WillRepeatedly(Return(true));
+ session_.OnFrameLost(QuicFrame(frame));
+
+ // Reset stream2 locally.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+ EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _));
+ stream2->Reset(QUIC_STREAM_CANCELLED);
+
+ // Verify stream 2 gets closed.
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+ EXPECT_TRUE(session_.IsClosedStream(stream2->id()));
+ EXPECT_CALL(*stream2, OnCanWrite()).Times(0);
+ session_.OnCanWrite();
+}
+
+TEST_P(QuicSessionTestServer, CleanUpClosedStreamsAlarm) {
+ EXPECT_FALSE(
+ QuicSessionPeer::GetCleanUpClosedStreamsAlarm(&session_)->IsSet());
+
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream2 = session_.CreateOutgoingBidirectionalStream();
+ EXPECT_FALSE(stream2->IsWaitingForAcks());
+
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _));
+ session_.CloseStream(stream2->id());
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+ EXPECT_EQ(1u, session_.closed_streams()->size());
+ EXPECT_TRUE(
+ QuicSessionPeer::GetCleanUpClosedStreamsAlarm(&session_)->IsSet());
+
+ alarm_factory_.FireAlarm(
+ QuicSessionPeer::GetCleanUpClosedStreamsAlarm(&session_));
+ EXPECT_TRUE(session_.closed_streams()->empty());
+}
+
+TEST_P(QuicSessionTestServer, WriteUnidirectionalStream) {
+ session_.set_writev_consumes_all_data(true);
+ TestStream* stream4 = new TestStream(GetNthServerInitiatedUnidirectionalId(1),
+ &session_, WRITE_UNIDIRECTIONAL);
+ session_.ActivateStream(QuicWrapUnique(stream4));
+ std::string body(100, '.');
+ stream4->WriteOrBufferData(body, false, nullptr);
+ EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream4->id()));
+ stream4->WriteOrBufferData(body, true, nullptr);
+ EXPECT_TRUE(QuicContainsKey(session_.zombie_streams(), stream4->id()));
+}
+
+TEST_P(QuicSessionTestServer, ReceivedDataOnWriteUnidirectionalStream) {
+ TestStream* stream4 = new TestStream(GetNthServerInitiatedUnidirectionalId(1),
+ &session_, WRITE_UNIDIRECTIONAL);
+ session_.ActivateStream(QuicWrapUnique(stream4));
+
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM, _, _))
+ .Times(1);
+ QuicStreamFrame stream_frame(GetNthServerInitiatedUnidirectionalId(1), false,
+ 0, 2);
+ session_.OnStreamFrame(stream_frame);
+}
+
+TEST_P(QuicSessionTestServer, ReadUnidirectionalStream) {
+ TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1),
+ &session_, READ_UNIDIRECTIONAL);
+ session_.ActivateStream(QuicWrapUnique(stream4));
+ EXPECT_FALSE(stream4->IsWaitingForAcks());
+ // Discard all incoming data.
+ stream4->StopReading();
+
+ std::string data(100, '.');
+ QuicStreamFrame stream_frame(GetNthClientInitiatedUnidirectionalId(1), false,
+ 0, data);
+ stream4->OnStreamFrame(stream_frame);
+ EXPECT_TRUE(session_.closed_streams()->empty());
+
+ QuicStreamFrame stream_frame2(GetNthClientInitiatedUnidirectionalId(1), true,
+ 100, data);
+ stream4->OnStreamFrame(stream_frame2);
+ EXPECT_EQ(1u, session_.closed_streams()->size());
+}
+
+TEST_P(QuicSessionTestServer, WriteOrBufferDataOnReadUnidirectionalStream) {
+ TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1),
+ &session_, READ_UNIDIRECTIONAL);
+ session_.ActivateStream(QuicWrapUnique(stream4));
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, _, _))
+ .Times(1);
+ std::string body(100, '.');
+ stream4->WriteOrBufferData(body, false, nullptr);
+}
+
+TEST_P(QuicSessionTestServer, WritevDataOnReadUnidirectionalStream) {
+ TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1),
+ &session_, READ_UNIDIRECTIONAL);
+ session_.ActivateStream(QuicWrapUnique(stream4));
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, _, _))
+ .Times(1);
+ std::string body(100, '.');
+ struct iovec iov = {const_cast<char*>(body.data()), body.length()};
+ QuicMemSliceStorage storage(
+ &iov, 1, session_.connection()->helper()->GetStreamSendBufferAllocator(),
+ 1024);
+ stream4->WriteMemSlices(storage.ToSpan(), false);
+}
+
+TEST_P(QuicSessionTestServer, WriteMemSlicesOnReadUnidirectionalStream) {
+ TestStream* stream4 = new TestStream(GetNthClientInitiatedUnidirectionalId(1),
+ &session_, READ_UNIDIRECTIONAL);
+ session_.ActivateStream(QuicWrapUnique(stream4));
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM, _, _))
+ .Times(1);
+ char data[1024];
+ std::vector<std::pair<char*, size_t>> buffers;
+ buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data)));
+ buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data)));
+ QuicTestMemSliceVector vector(buffers);
+ stream4->WriteMemSlices(vector.span(), false);
+}
+
+// Test code that tests that an incoming stream frame with a new (not previously
+// seen) stream id is acceptable. The ID must not be larger than has been
+// advertised. It may be equal to what has been advertised. These tests
+// invoke QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId by calling
+// QuicSession::OnStreamFrame in order to check that all the steps are connected
+// properly and that nothing in the call path interferes with the check.
+// First test make sure that streams with ids below the limit are accepted.
+TEST_P(QuicSessionTestServer, NewStreamIdBelowLimit) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Applicable only to V99
+ return;
+ }
+ QuicStreamId bidirectional_stream_id =
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->advertised_max_allowed_incoming_bidirectional_stream_id() -
+ kV99StreamIdIncrement;
+ QuicStreamFrame bidirectional_stream_frame(bidirectional_stream_id, false, 0,
+ "Random String");
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ session_.OnStreamFrame(bidirectional_stream_frame);
+
+ QuicStreamId unidirectional_stream_id =
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->advertised_max_allowed_incoming_unidirectional_stream_id() -
+ kV99StreamIdIncrement;
+ QuicStreamFrame unidirectional_stream_frame(unidirectional_stream_id, false,
+ 0, "Random String");
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ session_.OnStreamFrame(unidirectional_stream_frame);
+}
+
+// Accept a stream with an ID that equals the limit.
+TEST_P(QuicSessionTestServer, NewStreamIdAtLimit) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Applicable only to V99
+ return;
+ }
+ QuicStreamId bidirectional_stream_id =
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->advertised_max_allowed_incoming_bidirectional_stream_id();
+ QuicStreamFrame bidirectional_stream_frame(bidirectional_stream_id, false, 0,
+ "Random String");
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ session_.OnStreamFrame(bidirectional_stream_frame);
+
+ QuicStreamId unidirectional_stream_id =
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->advertised_max_allowed_incoming_unidirectional_stream_id();
+ QuicStreamFrame unidirectional_stream_frame(unidirectional_stream_id, false,
+ 0, "Random String");
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ session_.OnStreamFrame(unidirectional_stream_frame);
+}
+
+// Close the connection if the id exceeds the limit.
+TEST_P(QuicSessionTestServer, NewStreamIdAboveLimit) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Applicable only to V99
+ return;
+ }
+ QuicStreamId bidirectional_stream_id =
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->advertised_max_allowed_incoming_bidirectional_stream_id() +
+ kV99StreamIdIncrement;
+ QuicStreamFrame bidirectional_stream_frame(bidirectional_stream_id, false, 0,
+ "Random String");
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Stream id 404 above 400", _));
+ session_.OnStreamFrame(bidirectional_stream_frame);
+
+ QuicStreamId unidirectional_stream_id =
+ QuicSessionPeer::v99_streamid_manager(&session_)
+ ->advertised_max_allowed_incoming_unidirectional_stream_id() +
+ kV99StreamIdIncrement;
+ QuicStreamFrame unidirectional_stream_frame(unidirectional_stream_id, false,
+ 0, "Random String");
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Stream id 402 above 398", _));
+ session_.OnStreamFrame(unidirectional_stream_frame);
+}
+
+// Check that the OnStopSendingFrame upcall handles bad input properly
+// First test checks that invalid stream ids are handled.
+TEST_P(QuicSessionTestServer, OnStopSendingInputInvalidStreamId) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Applicable only to V99
+ return;
+ }
+ // Check that "invalid" stream ids are rejected.
+ // Note that the notion of an invalid stream id is Google-specific.
+ QuicStopSendingFrame frame(1, -1, 123);
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Received STOP_SENDING for an invalid stream", _));
+ EXPECT_FALSE(session_.OnStopSendingFrame(frame));
+}
+
+// Second test, streams in the static stream map are not subject to
+// STOP_SENDING; it's ignored.
+TEST_P(QuicSessionTestServer, OnStopSendingInputStaticStreams) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Applicable only to V99
+ return;
+ }
+ // Check that a stream id in the static stream map is ignored.
+ // Note that the notion of a static stream is Google-specific.
+ QuicStopSendingFrame frame(1, 0, 123);
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Received STOP_SENDING for a static stream", _));
+ EXPECT_FALSE(session_.OnStopSendingFrame(frame));
+}
+
+// Third test, if stream id specifies a closed stream:
+// return true and do not close the connection.
+TEST_P(QuicSessionTestServer, OnStopSendingInputClosedStream) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Applicable only to V99
+ return;
+ }
+
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = stream->id();
+ // Expect these as side effect of the close operations.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_, OnStreamReset(_, _));
+ stream->CloseWriteSide();
+ stream->CloseReadSide();
+ QuicStopSendingFrame frame(1, stream_id, 123);
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_TRUE(session_.OnStopSendingFrame(frame));
+}
+
+// Fourth test, if stream id specifies a nonexistent stream, return false and
+// close the connection
+TEST_P(QuicSessionTestServer, OnStopSendingInputNonExistentStream) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Applicable only to V99
+ return;
+ }
+
+ QuicStopSendingFrame frame(1, GetNthServerInitiatedBidirectionalId(123456),
+ 123);
+ EXPECT_CALL(
+ *connection_,
+ CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
+ "Received STOP_SENDING for a non-existent stream", _))
+ .Times(1);
+ EXPECT_FALSE(session_.OnStopSendingFrame(frame));
+}
+
+// For a valid stream, ensure that all works
+TEST_P(QuicSessionTestServer, OnStopSendingInputValidStream) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // Applicable only to V99
+ return;
+ }
+
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+
+ // Ensure that the stream starts out open in both directions.
+ EXPECT_FALSE(stream->write_side_closed());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream));
+
+ QuicStreamId stream_id = stream->id();
+ QuicStopSendingFrame frame(1, stream_id, 123);
+ EXPECT_CALL(*stream, OnStopSending(123));
+ // Expect a reset to come back out.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(
+ *connection_,
+ OnStreamReset(stream_id, static_cast<QuicRstStreamErrorCode>(123)));
+ EXPECT_TRUE(session_.OnStopSendingFrame(frame));
+ // When the STOP_SENDING is received, the node generates a RST_STREAM,
+ // which closes the stream in the write direction. Ensure this.
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream));
+ EXPECT_TRUE(stream->write_side_closed());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.cc b/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.cc
new file mode 100644
index 00000000000..3056c3347c9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+
+namespace quic {
+
+char* SimpleBufferAllocator::New(size_t size) {
+ return new char[size];
+}
+
+char* SimpleBufferAllocator::New(size_t size, bool /* flag_enable */) {
+ return New(size);
+}
+
+void SimpleBufferAllocator::Delete(char* buffer) {
+ delete[] buffer;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..e681e806a7c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_SIMPLE_BUFFER_ALLOCATOR_H_
+#define QUICHE_QUIC_CORE_QUIC_SIMPLE_BUFFER_ALLOCATOR_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE SimpleBufferAllocator : public QuicBufferAllocator {
+ public:
+ char* New(size_t size) override;
+ char* New(size_t size, bool flag_enable) override;
+ void Delete(char* buffer) override;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_SIMPLE_BUFFER_ALLOCATOR_H_
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
new file mode 100644
index 00000000000..747d7333f5f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator_test.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace {
+
+class SimpleBufferAllocatorTest : public QuicTest {};
+
+TEST_F(SimpleBufferAllocatorTest, NewDelete) {
+ SimpleBufferAllocator alloc;
+ char* buf = alloc.New(4);
+ EXPECT_NE(nullptr, buf);
+ alloc.Delete(buf);
+}
+
+TEST_F(SimpleBufferAllocatorTest, DeleteNull) {
+ SimpleBufferAllocator alloc;
+ alloc.Delete(nullptr);
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder.cc b/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder.cc
new file mode 100644
index 00000000000..2527fc90291
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder.cc
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h"
+
+namespace quic {
+
+namespace {
+
+// For convenience, the values of these constants match the values of AF_INET
+// and AF_INET6 on Linux.
+const uint16_t kIPv4 = 2;
+const uint16_t kIPv6 = 10;
+
+} // namespace
+
+QuicSocketAddressCoder::QuicSocketAddressCoder() {}
+
+QuicSocketAddressCoder::QuicSocketAddressCoder(const QuicSocketAddress& address)
+ : address_(address) {}
+
+QuicSocketAddressCoder::~QuicSocketAddressCoder() {}
+
+std::string QuicSocketAddressCoder::Encode() const {
+ std::string serialized;
+ uint16_t address_family;
+ switch (address_.host().address_family()) {
+ case IpAddressFamily::IP_V4:
+ address_family = kIPv4;
+ break;
+ case IpAddressFamily::IP_V6:
+ address_family = kIPv6;
+ break;
+ default:
+ return serialized;
+ }
+ serialized.append(reinterpret_cast<const char*>(&address_family),
+ sizeof(address_family));
+ serialized.append(address_.host().ToPackedString());
+ uint16_t port = address_.port();
+ serialized.append(reinterpret_cast<const char*>(&port), sizeof(port));
+ return serialized;
+}
+
+bool QuicSocketAddressCoder::Decode(const char* data, size_t length) {
+ uint16_t address_family;
+ if (length < sizeof(address_family)) {
+ return false;
+ }
+ memcpy(&address_family, data, sizeof(address_family));
+ data += sizeof(address_family);
+ length -= sizeof(address_family);
+
+ size_t ip_length;
+ switch (address_family) {
+ case kIPv4:
+ ip_length = QuicIpAddress::kIPv4AddressSize;
+ break;
+ case kIPv6:
+ ip_length = QuicIpAddress::kIPv6AddressSize;
+ break;
+ default:
+ return false;
+ }
+ if (length < ip_length) {
+ return false;
+ }
+ std::vector<uint8_t> ip(ip_length);
+ memcpy(&ip[0], data, ip_length);
+ data += ip_length;
+ length -= ip_length;
+
+ uint16_t port;
+ if (length != sizeof(port)) {
+ return false;
+ }
+ memcpy(&port, data, length);
+
+ QuicIpAddress ip_address;
+ ip_address.FromPackedString(reinterpret_cast<const char*>(&ip[0]), ip_length);
+ address_ = QuicSocketAddress(ip_address, port);
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder.h b/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder.h
new file mode 100644
index 00000000000..d8401c62f8d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_SOCKET_ADDRESS_CODER_H_
+#define QUICHE_QUIC_CORE_QUIC_SOCKET_ADDRESS_CODER_H_
+
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+// Serializes and parses a socket address (IP address and port), to be used in
+// the kCADR tag in the ServerHello handshake message and the Public Reset
+// packet.
+class QUIC_EXPORT_PRIVATE QuicSocketAddressCoder {
+ public:
+ QuicSocketAddressCoder();
+ explicit QuicSocketAddressCoder(const QuicSocketAddress& address);
+ QuicSocketAddressCoder(const QuicSocketAddressCoder&) = delete;
+ QuicSocketAddressCoder& operator=(const QuicSocketAddressCoder&) = delete;
+ ~QuicSocketAddressCoder();
+
+ std::string Encode() const;
+
+ bool Decode(const char* data, size_t length);
+
+ QuicIpAddress ip() const { return address_.host(); }
+
+ uint16_t port() const { return address_.port(); }
+
+ private:
+ QuicSocketAddress address_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_SOCKET_ADDRESS_CODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder_test.cc
new file mode 100644
index 00000000000..7ab1383e1c0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_socket_address_coder_test.cc
@@ -0,0 +1,128 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class QuicSocketAddressCoderTest : public QuicTest {};
+
+TEST_F(QuicSocketAddressCoderTest, EncodeIPv4) {
+ QuicIpAddress ip;
+ ip.FromString("4.31.198.44");
+ QuicSocketAddressCoder coder(QuicSocketAddress(ip, 0x1234));
+ std::string serialized = coder.Encode();
+ std::string expected("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8);
+ EXPECT_EQ(expected, serialized);
+}
+
+TEST_F(QuicSocketAddressCoderTest, EncodeIPv6) {
+ QuicIpAddress ip;
+ ip.FromString("2001:700:300:1800::f");
+ QuicSocketAddressCoder coder(QuicSocketAddress(ip, 0x5678));
+ std::string serialized = coder.Encode();
+ std::string expected(
+ "\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56",
+ 20);
+ EXPECT_EQ(expected, serialized);
+}
+
+TEST_F(QuicSocketAddressCoderTest, DecodeIPv4) {
+ std::string serialized("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8);
+ QuicSocketAddressCoder coder;
+ ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(IpAddressFamily::IP_V4, coder.ip().address_family());
+ std::string expected_addr("\x04\x1f\xc6\x2c");
+ EXPECT_EQ(expected_addr, coder.ip().ToPackedString());
+ EXPECT_EQ(0x1234, coder.port());
+}
+
+TEST_F(QuicSocketAddressCoderTest, DecodeIPv6) {
+ std::string serialized(
+ "\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56",
+ 20);
+ QuicSocketAddressCoder coder;
+ ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(IpAddressFamily::IP_V6, coder.ip().address_family());
+ std::string expected_addr(
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f",
+ 16);
+ EXPECT_EQ(expected_addr, coder.ip().ToPackedString());
+ EXPECT_EQ(0x5678, coder.port());
+}
+
+TEST_F(QuicSocketAddressCoderTest, DecodeBad) {
+ std::string serialized(
+ "\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56",
+ 20);
+ QuicSocketAddressCoder coder;
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ // Append junk.
+ serialized.push_back('\0');
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ // Undo.
+ serialized.resize(20);
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+
+ // Set an unknown address family.
+ serialized[0] = '\x03';
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ // Undo.
+ serialized[0] = '\x0a';
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+
+ // Truncate.
+ size_t len = serialized.length();
+ for (size_t i = 0; i < len; i++) {
+ ASSERT_FALSE(serialized.empty());
+ serialized.erase(serialized.length() - 1);
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ }
+ EXPECT_TRUE(serialized.empty());
+}
+
+TEST_F(QuicSocketAddressCoderTest, EncodeAndDecode) {
+ struct {
+ const char* ip_literal;
+ uint16_t port;
+ } test_case[] = {
+ {"93.184.216.119", 0x1234},
+ {"199.204.44.194", 80},
+ {"149.20.4.69", 443},
+ {"127.0.0.1", 8080},
+ {"2001:700:300:1800::", 0x5678},
+ {"::1", 65534},
+ };
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(test_case); i++) {
+ QuicIpAddress ip;
+ ASSERT_TRUE(ip.FromString(test_case[i].ip_literal));
+ QuicSocketAddressCoder encoder(QuicSocketAddress(ip, test_case[i].port));
+ std::string serialized = encoder.Encode();
+
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(encoder.ip(), decoder.ip());
+ EXPECT_EQ(encoder.port(), decoder.port());
+ }
+}
+
+} // namespace test
+} // namespace 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
new file mode 100644
index 00000000000..4fba898377a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream.cc
@@ -0,0 +1,1100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+using spdy::SpdyPriority;
+
+namespace quic {
+
+#define ENDPOINT \
+ (perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ")
+
+namespace {
+
+size_t GetInitialStreamFlowControlWindowToSend(QuicSession* session) {
+ return session->config()->GetInitialStreamFlowControlWindowToSend();
+}
+
+size_t GetReceivedFlowControlWindow(QuicSession* session) {
+ if (session->config()->HasReceivedInitialStreamFlowControlWindowBytes()) {
+ return session->config()->ReceivedInitialStreamFlowControlWindowBytes();
+ }
+
+ return kMinimumFlowControlSendWindow;
+}
+
+} // namespace
+
+// static
+const SpdyPriority QuicStream::kDefaultPriority;
+
+PendingStream::PendingStream(QuicStreamId id, QuicSession* session)
+ : id_(id),
+ session_(session),
+ stream_bytes_read_(0),
+ fin_received_(false),
+ connection_flow_controller_(session->flow_controller()),
+ flow_controller_(session,
+ id,
+ /*is_connection_flow_controller*/ false,
+ GetReceivedFlowControlWindow(session),
+ GetInitialStreamFlowControlWindowToSend(session),
+ kStreamReceiveWindowLimit,
+ session_->flow_controller()->auto_tune_receive_window(),
+ session_->flow_controller()),
+ sequencer_(this) {}
+
+void PendingStream::OnDataAvailable() {
+ QUIC_BUG << "OnDataAvailable should not be called.";
+ CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unexpected data available");
+}
+
+void PendingStream::OnFinRead() {
+ QUIC_BUG << "OnFinRead should not be called.";
+ CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unexpected fin read");
+}
+
+void PendingStream::AddBytesConsumed(QuicByteCount bytes) {
+ QUIC_BUG << "AddBytesConsumed should not be called.";
+ CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, "Unexpected bytes consumed");
+}
+
+void PendingStream::Reset(QuicRstStreamErrorCode error) {
+ session_->SendRstStream(id_, error, 0);
+}
+
+void PendingStream::CloseConnectionWithDetails(QuicErrorCode error,
+ const std::string& details) {
+ session_->connection()->CloseConnection(
+ error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+QuicStreamId PendingStream::id() const {
+ return id_;
+}
+
+const QuicSocketAddress& PendingStream::PeerAddressOfLatestPacket() const {
+ return session_->connection()->last_packet_source_address();
+}
+
+void PendingStream::OnStreamFrame(const QuicStreamFrame& frame) {
+ DCHECK_EQ(frame.stream_id, id_);
+ DCHECK_NE(0u, frame.offset);
+
+ bool is_stream_too_long =
+ (frame.offset > kMaxStreamLength) ||
+ (kMaxStreamLength - frame.offset < frame.data_length);
+ if (is_stream_too_long) {
+ // Close connection if stream becomes too long.
+ QUIC_PEER_BUG
+ << "Receive stream frame reaches max stream length. frame offset "
+ << frame.offset << " length " << frame.data_length;
+ CloseConnectionWithDetails(
+ QUIC_STREAM_LENGTH_OVERFLOW,
+ "Peer sends more data than allowed on this stream.");
+ return;
+ }
+
+ if (frame.fin) {
+ fin_received_ = true;
+ }
+
+ // This count includes duplicate data received.
+ size_t frame_payload_size = frame.data_length;
+ stream_bytes_read_ += frame_payload_size;
+
+ // Flow control is interested in tracking highest received offset.
+ // Only interested in received frames that carry data.
+ if (frame_payload_size > 0 &&
+ MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) {
+ // As the highest received offset has changed, check to see if this is a
+ // violation of flow control.
+ if (flow_controller_.FlowControlViolation() ||
+ connection_flow_controller_->FlowControlViolation()) {
+ CloseConnectionWithDetails(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
+ "Flow control violation after increasing offset");
+ return;
+ }
+ }
+
+ sequencer_.OnStreamFrame(frame);
+}
+
+void PendingStream::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
+ DCHECK_EQ(frame.stream_id, id_);
+
+ if (frame.byte_offset > kMaxStreamLength) {
+ // Peer are not suppose to write bytes more than maxium allowed.
+ CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW,
+ "Reset frame stream offset overflow.");
+ return;
+ }
+ MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
+ if (flow_controller_.FlowControlViolation() ||
+ connection_flow_controller_->FlowControlViolation()) {
+ CloseConnectionWithDetails(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
+ "Flow control violation after increasing offset");
+ return;
+ }
+}
+
+bool PendingStream::MaybeIncreaseHighestReceivedOffset(
+ QuicStreamOffset new_offset) {
+ uint64_t increment =
+ new_offset - flow_controller_.highest_received_byte_offset();
+ if (!flow_controller_.UpdateHighestReceivedOffset(new_offset)) {
+ return false;
+ }
+
+ // If |new_offset| increased the stream flow controller's highest received
+ // offset, increase the connection flow controller's value by the incremental
+ // difference.
+ connection_flow_controller_->UpdateHighestReceivedOffset(
+ connection_flow_controller_->highest_received_byte_offset() + increment);
+ return true;
+}
+
+QuicStream::QuicStream(PendingStream pending, StreamType type)
+ : QuicStream(pending.id_,
+ pending.session_,
+ std::move(pending.sequencer_),
+ /*is_static=*/false,
+ type,
+ pending.stream_bytes_read_,
+ pending.fin_received_,
+ std::move(pending.flow_controller_),
+ pending.connection_flow_controller_) {
+ sequencer_.set_stream(this);
+}
+
+QuicStream::QuicStream(QuicStreamId id,
+ QuicSession* session,
+ bool is_static,
+ StreamType type)
+ : QuicStream(id,
+ session,
+ QuicStreamSequencer(this),
+ is_static,
+ type,
+ 0,
+ false,
+ QuicFlowController(
+ session,
+ id,
+ /*is_connection_flow_controller*/ false,
+ GetReceivedFlowControlWindow(session),
+ GetInitialStreamFlowControlWindowToSend(session),
+ kStreamReceiveWindowLimit,
+ session->flow_controller()->auto_tune_receive_window(),
+ session->flow_controller()),
+ session->flow_controller()) {}
+
+QuicStream::QuicStream(QuicStreamId id,
+ QuicSession* session,
+ QuicStreamSequencer sequencer,
+ bool is_static,
+ StreamType type,
+ uint64_t stream_bytes_read,
+ bool fin_received,
+ QuicFlowController flow_controller,
+ QuicFlowController* connection_flow_controller)
+ : sequencer_(std::move(sequencer)),
+ id_(id),
+ session_(session),
+ priority_(kDefaultPriority),
+ stream_bytes_read_(stream_bytes_read),
+ stream_error_(QUIC_STREAM_NO_ERROR),
+ connection_error_(QUIC_NO_ERROR),
+ read_side_closed_(false),
+ write_side_closed_(false),
+ fin_buffered_(false),
+ fin_sent_(false),
+ fin_outstanding_(false),
+ fin_lost_(false),
+ fin_received_(fin_received),
+ rst_sent_(false),
+ rst_received_(false),
+ perspective_(session_->perspective()),
+ flow_controller_(std::move(flow_controller)),
+ connection_flow_controller_(connection_flow_controller),
+ stream_contributes_to_connection_flow_control_(true),
+ busy_counter_(0),
+ add_random_padding_after_fin_(false),
+ send_buffer_(
+ session->connection()->helper()->GetStreamSendBufferAllocator()),
+ buffered_data_threshold_(GetQuicFlag(FLAGS_quic_buffered_data_threshold)),
+ is_static_(is_static),
+ deadline_(QuicTime::Zero()),
+ type_(session->connection()->transport_version() == QUIC_VERSION_99
+ ? QuicUtils::GetStreamType(id_,
+ perspective_,
+ session->IsIncomingStream(id_))
+ : type) {
+ if (type_ == WRITE_UNIDIRECTIONAL) {
+ set_fin_received(true);
+ CloseReadSide();
+ } else if (type_ == READ_UNIDIRECTIONAL) {
+ set_fin_sent(true);
+ CloseWriteSide();
+ }
+ SetFromConfig();
+ session_->RegisterStreamPriority(id, is_static_, priority_);
+}
+
+QuicStream::~QuicStream() {
+ if (session_ != nullptr && IsWaitingForAcks()) {
+ QUIC_DVLOG(1)
+ << ENDPOINT << "Stream " << id_
+ << " gets destroyed while waiting for acks. stream_bytes_outstanding = "
+ << send_buffer_.stream_bytes_outstanding()
+ << ", fin_outstanding: " << fin_outstanding_;
+ }
+ if (session_ != nullptr) {
+ session_->UnregisterStreamPriority(id(), is_static_);
+ }
+}
+
+void QuicStream::SetFromConfig() {}
+
+void QuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
+ DCHECK_EQ(frame.stream_id, id_);
+
+ DCHECK(!(read_side_closed_ && write_side_closed_));
+
+ if (type_ == WRITE_UNIDIRECTIONAL) {
+ CloseConnectionWithDetails(
+ QUIC_DATA_RECEIVED_ON_WRITE_UNIDIRECTIONAL_STREAM,
+ "Data received on write unidirectional stream");
+ return;
+ }
+
+ bool is_stream_too_long =
+ (frame.offset > kMaxStreamLength) ||
+ (kMaxStreamLength - frame.offset < frame.data_length);
+ if (is_stream_too_long) {
+ // Close connection if stream becomes too long.
+ QUIC_PEER_BUG << "Receive stream frame on stream " << id_
+ << " reaches max stream length. frame offset " << frame.offset
+ << " length " << frame.data_length << ". "
+ << sequencer_.DebugString();
+ CloseConnectionWithDetails(
+ QUIC_STREAM_LENGTH_OVERFLOW,
+ QuicStrCat("Peer sends more data than allowed on stream ", id_,
+ ". frame: offset = ", frame.offset, ", length = ",
+ frame.data_length, ". ", sequencer_.DebugString()));
+ return;
+ }
+ if (frame.fin) {
+ fin_received_ = true;
+ if (fin_sent_) {
+ session_->StreamDraining(id_);
+ }
+ }
+
+ if (read_side_closed_) {
+ QUIC_DLOG(INFO)
+ << ENDPOINT << "Stream " << frame.stream_id
+ << " is closed for reading. Ignoring newly received stream data.";
+ // The subclass does not want to read data: blackhole the data.
+ return;
+ }
+
+ // This count includes duplicate data received.
+ size_t frame_payload_size = frame.data_length;
+ stream_bytes_read_ += frame_payload_size;
+
+ // Flow control is interested in tracking highest received offset.
+ // Only interested in received frames that carry data.
+ if (frame_payload_size > 0 &&
+ MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) {
+ // As the highest received offset has changed, check to see if this is a
+ // violation of flow control.
+ if (flow_controller_.FlowControlViolation() ||
+ connection_flow_controller_->FlowControlViolation()) {
+ CloseConnectionWithDetails(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
+ "Flow control violation after increasing offset");
+ return;
+ }
+ }
+
+ sequencer_.OnStreamFrame(frame);
+}
+
+int QuicStream::num_frames_received() const {
+ return sequencer_.num_frames_received();
+}
+
+int QuicStream::num_duplicate_frames_received() const {
+ return sequencer_.num_duplicate_frames_received();
+}
+
+void QuicStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ rst_received_ = true;
+ if (frame.byte_offset > kMaxStreamLength) {
+ // Peer are not suppose to write bytes more than maxium allowed.
+ CloseConnectionWithDetails(QUIC_STREAM_LENGTH_OVERFLOW,
+ "Reset frame stream offset overflow.");
+ return;
+ }
+ MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
+ if (flow_controller_.FlowControlViolation() ||
+ connection_flow_controller_->FlowControlViolation()) {
+ CloseConnectionWithDetails(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA,
+ "Flow control violation after increasing offset");
+ return;
+ }
+
+ stream_error_ = frame.error_code;
+ // Google QUIC closes both sides of the stream in response to a
+ // RESET_STREAM, IETF QUIC closes only the read side.
+ if (transport_version() != QUIC_VERSION_99) {
+ CloseWriteSide();
+ }
+ CloseReadSide();
+}
+
+void QuicStream::OnConnectionClosed(QuicErrorCode error,
+ ConnectionCloseSource /*source*/) {
+ if (read_side_closed_ && write_side_closed_) {
+ return;
+ }
+ if (error != QUIC_NO_ERROR) {
+ stream_error_ = QUIC_STREAM_CONNECTION_ERROR;
+ connection_error_ = error;
+ }
+
+ CloseWriteSide();
+ CloseReadSide();
+}
+
+void QuicStream::OnFinRead() {
+ DCHECK(sequencer_.IsClosed());
+ // OnFinRead can be called due to a FIN flag in a headers block, so there may
+ // have been no OnStreamFrame call with a FIN in the frame.
+ fin_received_ = true;
+ // If fin_sent_ is true, then CloseWriteSide has already been called, and the
+ // stream will be destroyed by CloseReadSide, so don't need to call
+ // StreamDraining.
+ CloseReadSide();
+}
+
+void QuicStream::Reset(QuicRstStreamErrorCode error) {
+ stream_error_ = error;
+ // Sending a RstStream results in calling CloseStream.
+ session()->SendRstStream(id(), error, stream_bytes_written());
+ rst_sent_ = true;
+}
+
+void QuicStream::CloseConnectionWithDetails(QuicErrorCode error,
+ const std::string& details) {
+ session()->connection()->CloseConnection(
+ error, details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+SpdyPriority QuicStream::priority() const {
+ return priority_;
+}
+
+void QuicStream::SetPriority(SpdyPriority priority) {
+ priority_ = priority;
+ session_->UpdateStreamPriority(id(), priority);
+}
+
+void QuicStream::WriteOrBufferData(
+ QuicStringPiece data,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ if (data.empty() && !fin) {
+ QUIC_BUG << "data.empty() && !fin";
+ return;
+ }
+
+ if (fin_buffered_) {
+ QUIC_BUG << "Fin already buffered";
+ return;
+ }
+ if (write_side_closed_) {
+ QUIC_DLOG(ERROR) << ENDPOINT
+ << "Attempt to write when the write side is closed";
+ if (type_ == READ_UNIDIRECTIONAL) {
+ CloseConnectionWithDetails(
+ QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM,
+ "Try to send data on read unidirectional stream");
+ }
+ return;
+ }
+
+ fin_buffered_ = fin;
+
+ bool had_buffered_data = HasBufferedData();
+ // Do not respect buffered data upper limit as WriteOrBufferData guarantees
+ // all data to be consumed.
+ if (data.length() > 0) {
+ struct iovec iov(QuicUtils::MakeIovec(data));
+ QuicStreamOffset offset = send_buffer_.stream_offset();
+ if (kMaxStreamLength - offset < data.length()) {
+ QUIC_BUG << "Write too many data via stream " << id_;
+ CloseConnectionWithDetails(
+ QUIC_STREAM_LENGTH_OVERFLOW,
+ QuicStrCat("Write too many data via stream ", id_));
+ return;
+ }
+ send_buffer_.SaveStreamData(&iov, 1, 0, data.length());
+ OnDataBuffered(offset, data.length(), ack_listener);
+ }
+ if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) {
+ // Write data if there is no buffered data before.
+ WriteBufferedData();
+ }
+}
+
+void QuicStream::OnCanWrite() {
+ if (HasDeadlinePassed()) {
+ OnDeadlinePassed();
+ return;
+ }
+ if (HasPendingRetransmission()) {
+ WritePendingRetransmission();
+ // Exit early to allow other streams to write pending retransmissions if
+ // any.
+ return;
+ }
+
+ if (write_side_closed_) {
+ QUIC_DLOG(ERROR)
+ << ENDPOINT << "Stream " << id()
+ << " attempting to write new data when the write side is closed";
+ return;
+ }
+ if (HasBufferedData() || (fin_buffered_ && !fin_sent_)) {
+ WriteBufferedData();
+ }
+ if (!fin_buffered_ && !fin_sent_ && CanWriteNewData()) {
+ // Notify upper layer to write new data when buffered data size is below
+ // low water mark.
+ OnCanWriteNewData();
+ }
+}
+
+void QuicStream::MaybeSendBlocked() {
+ if (flow_controller_.ShouldSendBlocked()) {
+ session_->SendBlocked(id_);
+ }
+ if (!stream_contributes_to_connection_flow_control_) {
+ return;
+ }
+ if (connection_flow_controller_->ShouldSendBlocked()) {
+ session_->SendBlocked(QuicUtils::GetInvalidStreamId(transport_version()));
+ }
+ // If the stream is blocked by connection-level flow control but not by
+ // stream-level flow control, add the stream to the write blocked list so that
+ // the stream will be given a chance to write when a connection-level
+ // WINDOW_UPDATE arrives.
+ if (connection_flow_controller_->IsBlocked() &&
+ !flow_controller_.IsBlocked()) {
+ session_->MarkConnectionLevelWriteBlocked(id());
+ }
+}
+
+QuicConsumedData QuicStream::WritevData(const struct iovec* iov,
+ int iov_count,
+ bool fin) {
+ if (write_side_closed_) {
+ QUIC_DLOG(ERROR) << ENDPOINT << "Stream " << id()
+ << "attempting to write when the write side is closed";
+ if (type_ == READ_UNIDIRECTIONAL) {
+ CloseConnectionWithDetails(
+ QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM,
+ "Try to send data on read unidirectional stream");
+ }
+ return QuicConsumedData(0, false);
+ }
+
+ // How much data was provided.
+ size_t write_length = 0;
+ if (iov != nullptr) {
+ for (int i = 0; i < iov_count; ++i) {
+ write_length += iov[i].iov_len;
+ }
+ }
+
+ QuicConsumedData consumed_data(0, false);
+ if (fin_buffered_) {
+ QUIC_BUG << "Fin already buffered";
+ return consumed_data;
+ }
+
+ if (kMaxStreamLength - send_buffer_.stream_offset() < write_length) {
+ QUIC_BUG << "Write too many data via stream " << id_;
+ CloseConnectionWithDetails(
+ QUIC_STREAM_LENGTH_OVERFLOW,
+ QuicStrCat("Write too many data via stream ", id_));
+ return consumed_data;
+ }
+
+ bool had_buffered_data = HasBufferedData();
+ if (CanWriteNewData()) {
+ // Save all data if buffered data size is below low water mark.
+ consumed_data.bytes_consumed = write_length;
+ if (consumed_data.bytes_consumed > 0) {
+ QuicStreamOffset offset = send_buffer_.stream_offset();
+ send_buffer_.SaveStreamData(iov, iov_count, 0, write_length);
+ OnDataBuffered(offset, write_length, nullptr);
+ }
+ }
+ consumed_data.fin_consumed =
+ consumed_data.bytes_consumed == write_length && fin;
+ fin_buffered_ = consumed_data.fin_consumed;
+
+ if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) {
+ // Write data if there is no buffered data before.
+ WriteBufferedData();
+ }
+
+ return consumed_data;
+}
+
+QuicConsumedData QuicStream::WriteMemSlices(QuicMemSliceSpan span, bool fin) {
+ QuicConsumedData consumed_data(0, false);
+ if (span.empty() && !fin) {
+ QUIC_BUG << "span.empty() && !fin";
+ return consumed_data;
+ }
+
+ if (fin_buffered_) {
+ QUIC_BUG << "Fin already buffered";
+ return consumed_data;
+ }
+
+ if (write_side_closed_) {
+ QUIC_DLOG(ERROR) << ENDPOINT << "Stream " << id()
+ << "attempting to write when the write side is closed";
+ if (type_ == READ_UNIDIRECTIONAL) {
+ CloseConnectionWithDetails(
+ QUIC_TRY_TO_WRITE_DATA_ON_READ_UNIDIRECTIONAL_STREAM,
+ "Try to send data on read unidirectional stream");
+ }
+ return consumed_data;
+ }
+
+ bool had_buffered_data = HasBufferedData();
+ if (CanWriteNewData() || span.empty()) {
+ consumed_data.fin_consumed = 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);
+ if (offset > send_buffer_.stream_offset() ||
+ kMaxStreamLength < send_buffer_.stream_offset()) {
+ QUIC_BUG << "Write too many data via stream " << id_;
+ CloseConnectionWithDetails(
+ QUIC_STREAM_LENGTH_OVERFLOW,
+ QuicStrCat("Write too many data via stream ", id_));
+ return consumed_data;
+ }
+ OnDataBuffered(offset, consumed_data.bytes_consumed, nullptr);
+ }
+ }
+ fin_buffered_ = consumed_data.fin_consumed;
+
+ if (!had_buffered_data && (HasBufferedData() || fin_buffered_)) {
+ // Write data if there is no buffered data before.
+ WriteBufferedData();
+ }
+
+ return consumed_data;
+}
+
+bool QuicStream::HasPendingRetransmission() const {
+ return send_buffer_.HasPendingRetransmission() || fin_lost_;
+}
+
+bool QuicStream::IsStreamFrameOutstanding(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin) const {
+ return send_buffer_.IsStreamDataOutstanding(offset, data_length) ||
+ (fin && fin_outstanding_);
+}
+
+QuicConsumedData QuicStream::WritevDataInner(size_t write_length,
+ QuicStreamOffset offset,
+ bool fin) {
+ StreamSendingState state = fin ? FIN : NO_FIN;
+ if (fin && add_random_padding_after_fin_) {
+ state = FIN_AND_PADDING;
+ }
+ return session()->WritevData(this, id(), write_length, offset, state);
+}
+
+void QuicStream::CloseReadSide() {
+ if (read_side_closed_) {
+ return;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Done reading from stream " << id();
+
+ read_side_closed_ = true;
+ sequencer_.ReleaseBuffer();
+
+ if (write_side_closed_) {
+ QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << id();
+ session_->CloseStream(id());
+ }
+}
+
+void QuicStream::CloseWriteSide() {
+ if (write_side_closed_) {
+ return;
+ }
+ QUIC_DVLOG(1) << ENDPOINT << "Done writing to stream " << id();
+
+ write_side_closed_ = true;
+ if (read_side_closed_) {
+ QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << id();
+ session_->CloseStream(id());
+ }
+}
+
+bool QuicStream::HasBufferedData() const {
+ DCHECK_GE(send_buffer_.stream_offset(), stream_bytes_written());
+ return send_buffer_.stream_offset() > stream_bytes_written();
+}
+
+QuicTransportVersion QuicStream::transport_version() const {
+ return session_->connection()->transport_version();
+}
+
+HandshakeProtocol QuicStream::handshake_protocol() const {
+ return session_->connection()->version().handshake_protocol;
+}
+
+void QuicStream::StopReading() {
+ QUIC_DVLOG(1) << ENDPOINT << "Stop reading from stream " << id();
+ sequencer_.StopReading();
+}
+
+const QuicSocketAddress& QuicStream::PeerAddressOfLatestPacket() const {
+ return session_->connection()->last_packet_source_address();
+}
+
+void QuicStream::OnClose() {
+ CloseReadSide();
+ CloseWriteSide();
+
+ if (!fin_sent_ && !rst_sent_) {
+ // For flow control accounting, tell the peer how many bytes have been
+ // written on this stream before termination. Done here if needed, using a
+ // RST_STREAM frame.
+ QUIC_DLOG(INFO) << ENDPOINT << "Sending RST_STREAM in OnClose: " << id();
+ session_->SendRstStream(id(), QUIC_RST_ACKNOWLEDGEMENT,
+ stream_bytes_written());
+ session_->OnStreamDoneWaitingForAcks(id_);
+ rst_sent_ = true;
+ }
+
+ if (flow_controller_.FlowControlViolation() ||
+ connection_flow_controller_->FlowControlViolation()) {
+ return;
+ }
+ // The stream is being closed and will not process any further incoming bytes.
+ // As there may be more bytes in flight, to ensure that both endpoints have
+ // the same connection level flow control state, mark all unreceived or
+ // buffered bytes as consumed.
+ QuicByteCount bytes_to_consume =
+ flow_controller_.highest_received_byte_offset() -
+ flow_controller_.bytes_consumed();
+ AddBytesConsumed(bytes_to_consume);
+}
+
+void QuicStream::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
+ if (flow_controller_.UpdateSendWindowOffset(frame.byte_offset)) {
+ // Let session unblock this stream.
+ session_->MarkConnectionLevelWriteBlocked(id_);
+ }
+}
+
+bool QuicStream::MaybeIncreaseHighestReceivedOffset(
+ QuicStreamOffset new_offset) {
+ uint64_t increment =
+ new_offset - flow_controller_.highest_received_byte_offset();
+ if (!flow_controller_.UpdateHighestReceivedOffset(new_offset)) {
+ return false;
+ }
+
+ // If |new_offset| increased the stream flow controller's highest received
+ // offset, increase the connection flow controller's value by the incremental
+ // difference.
+ if (stream_contributes_to_connection_flow_control_) {
+ connection_flow_controller_->UpdateHighestReceivedOffset(
+ connection_flow_controller_->highest_received_byte_offset() +
+ increment);
+ }
+ return true;
+}
+
+void QuicStream::AddBytesSent(QuicByteCount bytes) {
+ flow_controller_.AddBytesSent(bytes);
+ if (stream_contributes_to_connection_flow_control_) {
+ connection_flow_controller_->AddBytesSent(bytes);
+ }
+}
+
+void QuicStream::AddBytesConsumed(QuicByteCount bytes) {
+ // Only adjust stream level flow controller if still reading.
+ if (!read_side_closed_) {
+ flow_controller_.AddBytesConsumed(bytes);
+ }
+
+ if (stream_contributes_to_connection_flow_control_) {
+ connection_flow_controller_->AddBytesConsumed(bytes);
+ }
+}
+
+void QuicStream::UpdateSendWindowOffset(QuicStreamOffset new_window) {
+ if (flow_controller_.UpdateSendWindowOffset(new_window)) {
+ // Let session unblock this stream.
+ session_->MarkConnectionLevelWriteBlocked(id_);
+ }
+}
+
+void QuicStream::AddRandomPaddingAfterFin() {
+ add_random_padding_after_fin_ = true;
+}
+
+bool QuicStream::OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) {
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " Acking "
+ << "[" << offset << ", " << offset + data_length << "]"
+ << " fin = " << fin_acked;
+ *newly_acked_length = 0;
+ if (!send_buffer_.OnStreamDataAcked(offset, data_length,
+ newly_acked_length)) {
+ CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+ "Trying to ack unsent data.");
+ return false;
+ }
+ if (!fin_sent_ && fin_acked) {
+ CloseConnectionWithDetails(QUIC_INTERNAL_ERROR,
+ "Trying to ack unsent fin.");
+ return false;
+ }
+ // Indicates whether ack listener's OnPacketAcked should be called.
+ const bool new_data_acked =
+ *newly_acked_length > 0 || (fin_acked && fin_outstanding_);
+ if (fin_acked) {
+ fin_outstanding_ = false;
+ fin_lost_ = false;
+ }
+ if (!IsWaitingForAcks()) {
+ session_->OnStreamDoneWaitingForAcks(id_);
+ }
+ return new_data_acked;
+}
+
+void QuicStream::OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted) {
+ send_buffer_.OnStreamDataRetransmitted(offset, data_length);
+ if (fin_retransmitted) {
+ fin_lost_ = false;
+ }
+}
+
+void QuicStream::OnStreamFrameLost(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_lost) {
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " Losting "
+ << "[" << offset << ", " << offset + data_length << "]"
+ << " fin = " << fin_lost;
+ if (data_length > 0) {
+ send_buffer_.OnStreamDataLost(offset, data_length);
+ }
+ if (fin_lost && fin_outstanding_) {
+ fin_lost_ = true;
+ }
+}
+
+bool QuicStream::RetransmitStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin) {
+ if (HasDeadlinePassed()) {
+ OnDeadlinePassed();
+ return true;
+ }
+ QuicIntervalSet<QuicStreamOffset> retransmission(offset,
+ offset + data_length);
+ retransmission.Difference(bytes_acked());
+ bool retransmit_fin = fin && fin_outstanding_;
+ if (retransmission.Empty() && !retransmit_fin) {
+ return true;
+ }
+ QuicConsumedData consumed(0, false);
+ for (const auto& interval : retransmission) {
+ QuicStreamOffset retransmission_offset = interval.min();
+ QuicByteCount retransmission_length = interval.max() - interval.min();
+ const bool can_bundle_fin =
+ retransmit_fin && (retransmission_offset + retransmission_length ==
+ stream_bytes_written());
+ consumed = session()->WritevData(this, id_, retransmission_length,
+ retransmission_offset,
+ can_bundle_fin ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id_
+ << " is forced to retransmit stream data ["
+ << retransmission_offset << ", "
+ << retransmission_offset + retransmission_length
+ << ") and fin: " << can_bundle_fin
+ << ", consumed: " << consumed;
+ OnStreamFrameRetransmitted(retransmission_offset, consumed.bytes_consumed,
+ consumed.fin_consumed);
+ if (can_bundle_fin) {
+ retransmit_fin = !consumed.fin_consumed;
+ }
+ if (consumed.bytes_consumed < retransmission_length ||
+ (can_bundle_fin && !consumed.fin_consumed)) {
+ // Connection is write blocked.
+ return false;
+ }
+ }
+ if (retransmit_fin) {
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id_
+ << " retransmits fin only frame.";
+ consumed = session()->WritevData(this, id_, 0, stream_bytes_written(), FIN);
+ if (!consumed.fin_consumed) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicStream::IsWaitingForAcks() const {
+ return (!rst_sent_ || stream_error_ == QUIC_STREAM_NO_ERROR) &&
+ (send_buffer_.stream_bytes_outstanding() || fin_outstanding_);
+}
+
+size_t QuicStream::ReadableBytes() const {
+ return sequencer_.ReadableBytes();
+}
+
+bool QuicStream::WriteStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ DCHECK_LT(0u, data_length);
+ QUIC_DVLOG(2) << ENDPOINT << "Write stream " << id_ << " data from offset "
+ << offset << " length " << data_length;
+ return send_buffer_.WriteStreamData(offset, data_length, writer);
+}
+
+void QuicStream::WriteBufferedData() {
+ DCHECK(!write_side_closed_ && (HasBufferedData() || fin_buffered_));
+
+ if (session_->ShouldYield(id())) {
+ session_->MarkConnectionLevelWriteBlocked(id());
+ return;
+ }
+
+ // Size of buffered data.
+ size_t write_length = BufferedDataBytes();
+
+ // A FIN with zero data payload should not be flow control blocked.
+ bool fin_with_zero_data = (fin_buffered_ && write_length == 0);
+
+ bool fin = fin_buffered_;
+
+ // How much data flow control permits to be written.
+ QuicByteCount send_window = flow_controller_.SendWindowSize();
+ if (stream_contributes_to_connection_flow_control_) {
+ send_window =
+ std::min(send_window, connection_flow_controller_->SendWindowSize());
+ }
+
+ if (send_window == 0 && !fin_with_zero_data) {
+ // Quick return if nothing can be sent.
+ MaybeSendBlocked();
+ return;
+ }
+
+ if (write_length > send_window) {
+ // Don't send the FIN unless all the data will be sent.
+ fin = false;
+
+ // Writing more data would be a violation of flow control.
+ write_length = static_cast<size_t>(send_window);
+ QUIC_DVLOG(1) << "stream " << id() << " shortens write length to "
+ << write_length << " due to flow control";
+ }
+ if (session_->session_decides_what_to_write()) {
+ session_->SetTransmissionType(NOT_RETRANSMISSION);
+ }
+ QuicConsumedData consumed_data =
+ WritevDataInner(write_length, stream_bytes_written(), fin);
+
+ OnStreamDataConsumed(consumed_data.bytes_consumed);
+
+ AddBytesSent(consumed_data.bytes_consumed);
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id_ << " sends "
+ << stream_bytes_written() << " bytes "
+ << " and has buffered data " << BufferedDataBytes() << " bytes."
+ << " fin is sent: " << consumed_data.fin_consumed
+ << " fin is buffered: " << fin_buffered_;
+
+ // The write may have generated a write error causing this stream to be
+ // closed. If so, simply return without marking the stream write blocked.
+ if (write_side_closed_) {
+ return;
+ }
+
+ if (consumed_data.bytes_consumed == write_length) {
+ if (!fin_with_zero_data) {
+ MaybeSendBlocked();
+ }
+ if (fin && consumed_data.fin_consumed) {
+ fin_sent_ = true;
+ fin_outstanding_ = true;
+ if (fin_received_) {
+ session_->StreamDraining(id_);
+ }
+ CloseWriteSide();
+ } else if (fin && !consumed_data.fin_consumed) {
+ session_->MarkConnectionLevelWriteBlocked(id());
+ }
+ } else {
+ session_->MarkConnectionLevelWriteBlocked(id());
+ }
+ if (consumed_data.bytes_consumed > 0 || consumed_data.fin_consumed) {
+ busy_counter_ = 0;
+ }
+}
+
+uint64_t QuicStream::BufferedDataBytes() const {
+ DCHECK_GE(send_buffer_.stream_offset(), stream_bytes_written());
+ return send_buffer_.stream_offset() - stream_bytes_written();
+}
+
+bool QuicStream::CanWriteNewData() const {
+ return BufferedDataBytes() < buffered_data_threshold_;
+}
+
+bool QuicStream::CanWriteNewDataAfterData(QuicByteCount length) const {
+ return (BufferedDataBytes() + length) < buffered_data_threshold_;
+}
+
+uint64_t QuicStream::stream_bytes_written() const {
+ return send_buffer_.stream_bytes_written();
+}
+
+const QuicIntervalSet<QuicStreamOffset>& QuicStream::bytes_acked() const {
+ return send_buffer_.bytes_acked();
+}
+
+void QuicStream::OnStreamDataConsumed(size_t bytes_consumed) {
+ send_buffer_.OnStreamDataConsumed(bytes_consumed);
+}
+
+void QuicStream::WritePendingRetransmission() {
+ while (HasPendingRetransmission()) {
+ QuicConsumedData consumed(0, false);
+ if (!send_buffer_.HasPendingRetransmission()) {
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id_
+ << " retransmits fin only frame.";
+ consumed =
+ session()->WritevData(this, id_, 0, stream_bytes_written(), FIN);
+ fin_lost_ = !consumed.fin_consumed;
+ if (fin_lost_) {
+ // Connection is write blocked.
+ return;
+ }
+ } else {
+ StreamPendingRetransmission pending =
+ send_buffer_.NextPendingRetransmission();
+ // Determine whether the lost fin can be bundled with the data.
+ const bool can_bundle_fin =
+ fin_lost_ &&
+ (pending.offset + pending.length == stream_bytes_written());
+ consumed =
+ session()->WritevData(this, id_, pending.length, pending.offset,
+ can_bundle_fin ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << ENDPOINT << "stream " << id_
+ << " tries to retransmit stream data [" << pending.offset
+ << ", " << pending.offset + pending.length
+ << ") and fin: " << can_bundle_fin
+ << ", consumed: " << consumed;
+ OnStreamFrameRetransmitted(pending.offset, consumed.bytes_consumed,
+ consumed.fin_consumed);
+ if (consumed.bytes_consumed < pending.length ||
+ (can_bundle_fin && !consumed.fin_consumed)) {
+ // Connection is write blocked.
+ return;
+ }
+ }
+ }
+}
+
+bool QuicStream::MaybeSetTtl(QuicTime::Delta ttl) {
+ if (is_static_) {
+ QUIC_BUG << "Cannot set TTL of a static stream.";
+ return false;
+ }
+ if (deadline_.IsInitialized()) {
+ QUIC_DLOG(WARNING) << "Deadline has already been set.";
+ return false;
+ }
+ if (!session()->session_decides_what_to_write()) {
+ QUIC_DLOG(WARNING) << "This session does not support stream TTL yet.";
+ return false;
+ }
+ QuicTime now = session()->connection()->clock()->ApproximateNow();
+ deadline_ = now + ttl;
+ return true;
+}
+
+bool QuicStream::HasDeadlinePassed() const {
+ if (!deadline_.IsInitialized()) {
+ // No deadline has been set.
+ return false;
+ }
+ DCHECK(session()->session_decides_what_to_write());
+ QuicTime now = session()->connection()->clock()->ApproximateNow();
+ if (now < deadline_) {
+ return false;
+ }
+ // TTL expired.
+ QUIC_DVLOG(1) << "stream " << id() << " deadline has passed";
+ return true;
+}
+
+void QuicStream::OnDeadlinePassed() {
+ Reset(QUIC_STREAM_TTL_EXPIRED);
+}
+
+void QuicStream::SendStopSending(uint16_t code) {
+ if (transport_version() != QUIC_VERSION_99) {
+ // If the connection is not version 99, do nothing.
+ // Do not QUIC_BUG or anything; the application really does not need to know
+ // what version the connection is in.
+ return;
+ }
+ session_->SendStopSending(code, id_);
+}
+
+void QuicStream::OnStopSending(uint16_t code) {}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..40bed1c19c9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream.h
@@ -0,0 +1,541 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The base class for client/server QUIC streams.
+
+// It does not contain the entire interface needed by an application to interact
+// with a QUIC stream. Some parts of the interface must be obtained by
+// accessing the owning session object. A subclass of QuicStream
+// connects the object and the application that generates and consumes the data
+// of the stream.
+
+// The QuicStream object has a dependent QuicStreamSequencer object,
+// which is given the stream frames as they arrive, and provides stream data in
+// order by invoking ProcessRawData().
+
+#ifndef QUICHE_QUIC_CORE_QUIC_STREAM_H_
+#define QUICHE_QUIC_CORE_QUIC_STREAM_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <list>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+namespace quic {
+
+namespace test {
+class QuicStreamPeer;
+} // namespace test
+
+class QuicSession;
+class QuicStream;
+
+// Buffers frames for a stream until the first byte of that frame arrives.
+class QUIC_EXPORT_PRIVATE PendingStream
+ : public QuicStreamSequencer::StreamInterface {
+ public:
+ PendingStream(QuicStreamId id, QuicSession* session);
+ PendingStream(const PendingStream&) = delete;
+ PendingStream(PendingStream&&) = default;
+ ~PendingStream() override = default;
+
+ // QuicStreamSequencer::StreamInterface
+ void OnDataAvailable() override;
+ void OnFinRead() override;
+ void AddBytesConsumed(QuicByteCount bytes) override;
+ void Reset(QuicRstStreamErrorCode error) override;
+ void CloseConnectionWithDetails(QuicErrorCode error,
+ const std::string& details) override;
+ QuicStreamId id() const override;
+ const QuicSocketAddress& PeerAddressOfLatestPacket() const override;
+
+ // Buffers the contents of |frame|. Frame must have a non-zero offset.
+ // If the data violates flow control, the connection will be closed.
+ void OnStreamFrame(const QuicStreamFrame& frame);
+
+ // Stores the final byte offset from |frame|.
+ // If the final offset violates flow control, the connection will be closed.
+ void OnRstStreamFrame(const QuicRstStreamFrame& frame);
+
+ // Returns the number of bytes read on this stream.
+ uint64_t stream_bytes_read() { return stream_bytes_read_; }
+
+ private:
+ friend class QuicStream;
+
+ bool MaybeIncreaseHighestReceivedOffset(QuicStreamOffset new_offset);
+
+ // ID of this stream.
+ QuicStreamId id_;
+
+ // Session which owns this.
+ QuicSession* session_;
+
+ // Bytes read refers to payload bytes only: they do not include framing,
+ // encryption overhead etc.
+ uint64_t stream_bytes_read_;
+
+ // True if a frame containing a fin has been received.
+ bool fin_received_;
+
+ // Connection-level flow controller. Owned by the session.
+ QuicFlowController* connection_flow_controller_;
+ // Stream-level flow controller.
+ QuicFlowController flow_controller_;
+ // Stores the buffered frames.
+ QuicStreamSequencer sequencer_;
+};
+
+class QUIC_EXPORT_PRIVATE QuicStream
+ : public QuicStreamSequencer::StreamInterface {
+ public:
+ // This is somewhat arbitrary. It's possible, but unlikely, we will either
+ // fail to set a priority client-side, or cancel a stream before stripping the
+ // priority from the wire server-side. In either case, start out with a
+ // priority in the middle.
+ static const spdy::SpdyPriority kDefaultPriority = 3;
+ static_assert(kDefaultPriority ==
+ (spdy::kV3LowestPriority + spdy::kV3HighestPriority) / 2,
+ "Unexpected value of kDefaultPriority");
+
+ // Creates a new stream with stream_id |id| associated with |session|. If
+ // |is_static| is true, then the stream will be given precedence
+ // over other streams when determing what streams should write next.
+ // |type| indicates whether the stream is bidirectional, read unidirectional
+ // or write unidirectional.
+ // TODO(fayang): Remove |type| when IETF stream ID numbering fully kicks in.
+ QuicStream(QuicStreamId id,
+ QuicSession* session,
+ bool is_static,
+ StreamType type);
+ QuicStream(PendingStream pending, StreamType type);
+ QuicStream(const QuicStream&) = delete;
+ QuicStream& operator=(const QuicStream&) = delete;
+
+ virtual ~QuicStream();
+
+ // Not in use currently.
+ void SetFromConfig();
+
+ // QuicStreamSequencer::StreamInterface implementation.
+ QuicStreamId id() const override { return id_; }
+ // Called by the stream subclass after it has consumed the final incoming
+ // data.
+ void OnFinRead() override;
+
+ // Called by the subclass or the sequencer to reset the stream from this
+ // end.
+ void Reset(QuicRstStreamErrorCode error) override;
+
+ // Called by the subclass or the sequencer to close the entire connection from
+ // this end.
+ void CloseConnectionWithDetails(QuicErrorCode error,
+ const std::string& details) override;
+
+ // Get peer IP of the lastest packet which connection is dealing/delt with.
+ const QuicSocketAddress& PeerAddressOfLatestPacket() const override;
+
+ // Called by the session when a (potentially duplicate) stream frame has been
+ // received for this stream.
+ virtual void OnStreamFrame(const QuicStreamFrame& frame);
+
+ // Called by the session when the connection becomes writeable to allow the
+ // stream to write any pending data.
+ virtual void OnCanWrite();
+
+ // Called by the session just before the object is destroyed.
+ // The object should not be accessed after OnClose is called.
+ // Sends a RST_STREAM with code QUIC_RST_ACKNOWLEDGEMENT if neither a FIN nor
+ // a RST_STREAM has been sent.
+ virtual void OnClose();
+
+ // Called by the session when the endpoint receives a RST_STREAM from the
+ // peer.
+ virtual void OnStreamReset(const QuicRstStreamFrame& frame);
+
+ // Called by the session when the endpoint receives or sends a connection
+ // close, and should immediately close the stream.
+ virtual void OnConnectionClosed(QuicErrorCode error,
+ ConnectionCloseSource source);
+
+ spdy::SpdyPriority priority() const;
+
+ // Sets priority_ to priority. This should only be called before bytes are
+ // written to the server.
+ void SetPriority(spdy::SpdyPriority priority);
+
+ // Returns true if this stream is still waiting for acks of sent data.
+ // This will return false if all data has been acked, or if the stream
+ // is no longer interested in data being acked (which happens when
+ // a stream is reset because of an error).
+ bool IsWaitingForAcks() const;
+
+ // Number of bytes available to read.
+ size_t ReadableBytes() const;
+
+ QuicRstStreamErrorCode stream_error() const { return stream_error_; }
+ QuicErrorCode connection_error() const { return connection_error_; }
+
+ bool reading_stopped() const {
+ return sequencer_.ignore_read_data() || read_side_closed_;
+ }
+ bool write_side_closed() const { return write_side_closed_; }
+
+ bool rst_received() const { return rst_received_; }
+ bool rst_sent() const { return rst_sent_; }
+ bool fin_received() const { return fin_received_; }
+ bool fin_sent() const { return fin_sent_; }
+ bool fin_outstanding() const { return fin_outstanding_; }
+ bool fin_lost() const { return fin_lost_; }
+
+ uint64_t BufferedDataBytes() const;
+
+ uint64_t stream_bytes_read() const { return stream_bytes_read_; }
+ uint64_t stream_bytes_written() const;
+
+ size_t busy_counter() const { return busy_counter_; }
+ void set_busy_counter(size_t busy_counter) { busy_counter_ = busy_counter; }
+
+ void set_fin_sent(bool fin_sent) { fin_sent_ = fin_sent; }
+ void set_fin_received(bool fin_received) { fin_received_ = fin_received; }
+ void set_rst_sent(bool rst_sent) { rst_sent_ = rst_sent; }
+
+ void set_rst_received(bool rst_received) { rst_received_ = rst_received; }
+ void set_stream_error(QuicRstStreamErrorCode error) { stream_error_ = error; }
+
+ // Adjust the flow control window according to new offset in |frame|.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame);
+
+ int num_frames_received() const;
+ int num_duplicate_frames_received() const;
+
+ QuicFlowController* flow_controller() { return &flow_controller_; }
+
+ // Called when endpoint receives a frame which could increase the highest
+ // offset.
+ // Returns true if the highest offset did increase.
+ bool MaybeIncreaseHighestReceivedOffset(QuicStreamOffset new_offset);
+
+ // Updates the flow controller's send window offset and calls OnCanWrite if
+ // it was blocked before.
+ void UpdateSendWindowOffset(QuicStreamOffset new_offset);
+
+ // Returns true if the stream has received either a RST_STREAM or a FIN -
+ // either of which gives a definitive number of bytes which the peer has
+ // sent. If this is not true on deletion of the stream object, the session
+ // must keep track of the stream's byte offset until a definitive final value
+ // arrives.
+ bool HasFinalReceivedByteOffset() const {
+ return fin_received_ || rst_received_;
+ }
+
+ // Returns true if the stream has queued data waiting to write.
+ bool HasBufferedData() const;
+
+ // Returns the version of QUIC being used for this stream.
+ QuicTransportVersion transport_version() const;
+
+ // Returns the crypto handshake protocol that was used on this stream's
+ // connection.
+ HandshakeProtocol handshake_protocol() const;
+
+ // Sets the sequencer to consume all incoming data itself and not call
+ // OnDataAvailable().
+ // When the FIN is received, the stream will be notified automatically (via
+ // OnFinRead()) (which may happen during the call of StopReading()).
+ // TODO(dworley): There should be machinery to send a RST_STREAM/NO_ERROR and
+ // stop sending stream-level flow-control updates when this end sends FIN.
+ virtual void StopReading();
+
+ // Sends as much of 'data' to the connection as the connection will consume,
+ // and then buffers any remaining data in queued_data_.
+ // If fin is true: if it is immediately passed on to the session,
+ // write_side_closed() becomes true, otherwise fin_buffered_ becomes true.
+ void WriteOrBufferData(
+ QuicStringPiece data,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ // Adds random padding after the fin is consumed for this stream.
+ void AddRandomPaddingAfterFin();
+
+ // Write |data_length| of data starts at |offset| from send buffer.
+ bool WriteStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer);
+
+ // Called when data [offset, offset + data_length) is acked. |fin_acked|
+ // indicates whether the fin is acked. Returns true and updates
+ // |newly_acked_length| if any new stream data (including fin) gets acked.
+ virtual bool OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length);
+
+ // Called when data [offset, offset + data_length) was retransmitted.
+ // |fin_retransmitted| indicates whether fin was retransmitted.
+ virtual void OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted);
+
+ // Called when data [offset, offset + data_length) is considered as lost.
+ // |fin_lost| indicates whether the fin is considered as lost.
+ virtual void OnStreamFrameLost(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_lost);
+
+ // Called to retransmit outstanding portion in data [offset, offset +
+ // data_length) and |fin|. Returns true if all data gets retransmitted.
+ virtual bool RetransmitStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin);
+
+ // Sets deadline of this stream to be now + |ttl|, returns true if the setting
+ // succeeds.
+ bool MaybeSetTtl(QuicTime::Delta ttl);
+
+ // Same as WritevData except data is provided in reference counted memory so
+ // that data copy is avoided.
+ QuicConsumedData WriteMemSlices(QuicMemSliceSpan span, bool fin);
+
+ // Returns true if any stream data is lost (including fin) and needs to be
+ // retransmitted.
+ virtual bool HasPendingRetransmission() const;
+
+ // Returns true if any portion of data [offset, offset + data_length) is
+ // outstanding or fin is outstanding (if |fin| is true). Returns false
+ // otherwise.
+ bool IsStreamFrameOutstanding(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin) const;
+
+ StreamType type() const { return type_; }
+
+ // Creates and sends a STOP_SENDING frame. This can be called regardless of
+ // the version that has been negotiated. If not IETF QUIC/Version 99 then the
+ // method is a noop, relieving the application of the necessity of
+ // understanding the connection's QUIC version and knowing whether it can call
+ // this method or not.
+ void SendStopSending(uint16_t code);
+
+ // Invoked when QUIC receives a STOP_SENDING frame for this stream, informing
+ // the application that the peer has sent a STOP_SENDING. The default
+ // implementation is a noop. Is to be overridden by the application-specific
+ // QuicStream class.
+ virtual void OnStopSending(uint16_t code);
+
+ // Close the write side of the socket. Further writes will fail.
+ // Can be called by the subclass or internally.
+ // Does not send a FIN. May cause the stream to be closed.
+ virtual void CloseWriteSide();
+
+ protected:
+ // Sends as many bytes in the first |count| buffers of |iov| to the connection
+ // as the connection will consume. If FIN is consumed, the write side is
+ // immediately closed.
+ // Returns the number of bytes consumed by the connection.
+ // Please note: Returned consumed data is the amount of data saved in send
+ // buffer. The data is not necessarily consumed by the connection. So write
+ // side is closed when FIN is sent.
+ // TODO(fayang): Let WritevData return boolean.
+ QuicConsumedData WritevData(const struct iovec* iov, int iov_count, bool fin);
+
+ // Allows override of the session level writev, for the force HOL
+ // blocking experiment.
+ virtual QuicConsumedData WritevDataInner(size_t write_length,
+ QuicStreamOffset offset,
+ bool fin);
+
+ // Close the read side of the socket. May cause the stream to be closed.
+ // Subclasses and consumers should use StopReading to terminate reading early
+ // if expecting a FIN. Can be used directly by subclasses if not expecting a
+ // FIN.
+ void CloseReadSide();
+
+ // Called when data of [offset, offset + data_length] is buffered in send
+ // buffer.
+ virtual void OnDataBuffered(
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ const QuicReferenceCountedPointer<QuicAckListenerInterface>&
+ ack_listener) {}
+
+ // True if buffered data in send buffer is below buffered_data_threshold_.
+ bool CanWriteNewData() const;
+
+ // True if buffered data in send buffer is still below
+ // buffered_data_threshold_ even after writing |length| bytes.
+ bool CanWriteNewDataAfterData(QuicByteCount length) const;
+
+ // Called when upper layer can write new data.
+ virtual void OnCanWriteNewData() {}
+
+ // Called when |bytes_consumed| bytes has been consumed.
+ virtual void OnStreamDataConsumed(size_t bytes_consumed);
+
+ // Called by the stream sequencer as bytes are consumed from the buffer.
+ // If the receive window has dropped below the threshold, then send a
+ // WINDOW_UPDATE frame.
+ void AddBytesConsumed(QuicByteCount bytes) override;
+
+ // Writes pending retransmissions if any.
+ virtual void WritePendingRetransmission();
+
+ // This is called when stream tries to retransmit data after deadline_. Make
+ // this virtual so that subclasses can implement their own logics.
+ virtual void OnDeadlinePassed();
+
+ bool fin_buffered() const { return fin_buffered_; }
+
+ const QuicSession* session() const { return session_; }
+ QuicSession* session() { return session_; }
+
+ const QuicStreamSequencer* sequencer() const { return &sequencer_; }
+ QuicStreamSequencer* sequencer() { return &sequencer_; }
+
+ void DisableConnectionFlowControlForThisStream() {
+ stream_contributes_to_connection_flow_control_ = false;
+ }
+
+ const QuicIntervalSet<QuicStreamOffset>& bytes_acked() const;
+
+ const QuicStreamSendBuffer& send_buffer() const { return send_buffer_; }
+
+ QuicStreamSendBuffer& send_buffer() { return send_buffer_; }
+
+ private:
+ friend class test::QuicStreamPeer;
+ friend class QuicStreamUtils;
+
+ QuicStream(QuicStreamId id,
+ QuicSession* session,
+ QuicStreamSequencer sequencer,
+ bool is_static,
+ StreamType type,
+ uint64_t stream_bytes_read,
+ bool fin_received,
+ QuicFlowController flow_controller,
+ QuicFlowController* connection_flow_controller);
+
+ // Subclasses and consumers should use reading_stopped.
+ bool read_side_closed() const { return read_side_closed_; }
+
+ // Calls MaybeSendBlocked on the stream's flow controller and the connection
+ // level flow controller. If the stream is flow control blocked by the
+ // connection-level flow controller but not by the stream-level flow
+ // controller, marks this stream as connection-level write blocked.
+ void MaybeSendBlocked();
+
+ // Write buffered data in send buffer. TODO(fayang): Consider combine
+ // WriteOrBufferData, Writev and WriteBufferedData.
+ void WriteBufferedData();
+
+ // Called when bytes are sent to the peer.
+ void AddBytesSent(QuicByteCount bytes);
+
+ // Returns true if deadline_ has passed.
+ bool HasDeadlinePassed() const;
+
+ QuicStreamSequencer sequencer_;
+ QuicStreamId id_;
+ // Pointer to the owning QuicSession object.
+ QuicSession* session_;
+ // The priority of the stream, once parsed.
+ spdy::SpdyPriority priority_;
+ // Bytes read refers to payload bytes only: they do not include framing,
+ // encryption overhead etc.
+ uint64_t stream_bytes_read_;
+
+ // Stream error code received from a RstStreamFrame or error code sent by the
+ // visitor or sequencer in the RstStreamFrame.
+ QuicRstStreamErrorCode stream_error_;
+ // Connection error code due to which the stream was closed. |stream_error_|
+ // is set to |QUIC_STREAM_CONNECTION_ERROR| when this happens and consumers
+ // should check |connection_error_|.
+ QuicErrorCode connection_error_;
+
+ // True if the read side is closed and further frames should be rejected.
+ bool read_side_closed_;
+ // True if the write side is closed, and further writes should fail.
+ bool write_side_closed_;
+
+ // True if the subclass has written a FIN with WriteOrBufferData, but it was
+ // buffered in queued_data_ rather than being sent to the session.
+ bool fin_buffered_;
+ // True if a FIN has been sent to the session.
+ bool fin_sent_;
+ // True if a FIN is waiting to be acked.
+ bool fin_outstanding_;
+ // True if a FIN is lost.
+ bool fin_lost_;
+
+ // True if this stream has received (and the sequencer has accepted) a
+ // StreamFrame with the FIN set.
+ bool fin_received_;
+
+ // True if an RST_STREAM has been sent to the session.
+ // In combination with fin_sent_, used to ensure that a FIN and/or a
+ // RST_STREAM is always sent to terminate the stream.
+ bool rst_sent_;
+
+ // True if this stream has received a RST_STREAM frame.
+ bool rst_received_;
+
+ // Tracks if the session this stream is running under was created by a
+ // server or a client.
+ Perspective perspective_;
+
+ QuicFlowController flow_controller_;
+
+ // The connection level flow controller. Not owned.
+ QuicFlowController* connection_flow_controller_;
+
+ // Special streams, such as the crypto and headers streams, do not respect
+ // connection level flow control limits (but are stream level flow control
+ // limited).
+ bool stream_contributes_to_connection_flow_control_;
+
+ // A counter incremented when OnCanWrite() is called and no progress is made.
+ // For debugging only.
+ size_t busy_counter_;
+
+ // Indicates whether paddings will be added after the fin is consumed for this
+ // stream.
+ bool add_random_padding_after_fin_;
+
+ // Send buffer of this stream. Send buffer is cleaned up when data gets acked
+ // or discarded.
+ QuicStreamSendBuffer send_buffer_;
+
+ // Latched value of FLAGS_quic_buffered_data_threshold.
+ const QuicByteCount buffered_data_threshold_;
+
+ // If true, then this stream has precedence over other streams for write
+ // scheduling.
+ const bool is_static_;
+
+ // If initialized, reset this stream at this deadline.
+ QuicTime deadline_;
+
+ // Indicates whether this stream is bidirectional, read unidirectional or
+ // write unidirectional.
+ const StreamType type_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h
new file mode 100644
index 00000000000..ab67d54d542
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h
@@ -0,0 +1,39 @@
+// 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_CORE_QUIC_STREAM_FRAME_DATA_PRODUCER_H_
+#define QUICHE_QUIC_CORE_QUIC_STREAM_FRAME_DATA_PRODUCER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+class QuicDataWriter;
+
+// Pure virtual class to retrieve stream data.
+class QUIC_EXPORT_PRIVATE QuicStreamFrameDataProducer {
+ public:
+ virtual ~QuicStreamFrameDataProducer() {}
+
+ // Let |writer| write |data_length| data with |offset| of stream |id|. The
+ // write fails when either stream is closed or corresponding data is failed to
+ // be retrieved. This method allows writing a single stream frame from data
+ // that spans multiple buffers.
+ virtual WriteStreamDataResult WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) = 0;
+
+ // Writes the data for a CRYPTO frame to |writer| for a frame at encryption
+ // level |level| starting at offset |offset| for |data_length| bytes. Returns
+ // whether writing the data was successful.
+ virtual bool WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_STREAM_FRAME_DATA_PRODUCER_H_
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
new file mode 100644
index 00000000000..fb6e14b8957
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.cc
@@ -0,0 +1,351 @@
+// 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 "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+#define ENDPOINT \
+ (session_->perspective() == Perspective::IS_SERVER ? " Server: " \
+ : " Client: ")
+
+QuicStreamIdManager::QuicStreamIdManager(
+ QuicSession* session,
+ QuicStreamId next_outgoing_stream_id,
+ QuicStreamId largest_peer_created_stream_id,
+ QuicStreamId first_incoming_dynamic_stream_id,
+ size_t max_allowed_outgoing_streams,
+ size_t max_allowed_incoming_streams)
+ : session_(session),
+ next_outgoing_stream_id_(next_outgoing_stream_id),
+ largest_peer_created_stream_id_(largest_peer_created_stream_id),
+ max_allowed_outgoing_stream_id_(0),
+ actual_max_allowed_incoming_stream_id_(0),
+ advertised_max_allowed_incoming_stream_id_(0),
+ max_stream_id_window_(max_allowed_incoming_streams /
+ kMaxStreamIdWindowDivisor),
+ max_allowed_incoming_streams_(max_allowed_incoming_streams),
+ first_incoming_dynamic_stream_id_(first_incoming_dynamic_stream_id),
+ first_outgoing_dynamic_stream_id_(next_outgoing_stream_id) {
+ available_incoming_streams_ = max_allowed_incoming_streams_;
+ SetMaxOpenOutgoingStreams(max_allowed_outgoing_streams);
+ SetMaxOpenIncomingStreams(max_allowed_incoming_streams);
+}
+
+QuicStreamIdManager::~QuicStreamIdManager() {
+ QUIC_LOG_IF(WARNING,
+ session_->num_locally_closed_incoming_streams_highest_offset() >
+ max_allowed_incoming_streams_)
+ << "Surprisingly high number of locally closed peer initiated streams"
+ "still waiting for final byte offset: "
+ << session_->num_locally_closed_incoming_streams_highest_offset();
+ QUIC_LOG_IF(WARNING,
+ session_->GetNumLocallyClosedOutgoingStreamsHighestOffset() >
+ max_allowed_outgoing_streams_)
+ << "Surprisingly high number of locally closed self initiated streams"
+ "still waiting for final byte offset: "
+ << session_->GetNumLocallyClosedOutgoingStreamsHighestOffset();
+}
+
+bool QuicStreamIdManager::OnMaxStreamIdFrame(
+ const QuicMaxStreamIdFrame& frame) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(frame.max_stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ // Need to determine whether the stream id matches our client/server
+ // perspective or not. If not, it's an error. If so, update appropriate
+ // maxima.
+ QUIC_CODE_COUNT_N(max_stream_id_received, 2, 2);
+ // TODO(fkastenholz): this test needs to be broader to handle uni- and bi-
+ // directional stream ids when that functionality is supported.
+ if (IsIncomingStream(frame.max_stream_id)) {
+ // TODO(fkastenholz): This, and following, closeConnection may
+ // need modification when proper support for IETF CONNECTION
+ // CLOSE is done.
+ QUIC_CODE_COUNT(max_stream_id_bad_direction);
+ session_->connection()->CloseConnection(
+ QUIC_MAX_STREAM_ID_ERROR,
+ "Received max stream ID with wrong initiator bit setting",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ // If a MAX_STREAM_ID advertises a stream ID that is smaller than previously
+ // advertised, it is to be ignored.
+ if (frame.max_stream_id < max_allowed_outgoing_stream_id_) {
+ QUIC_CODE_COUNT(max_stream_id_ignored);
+ return true;
+ }
+ max_allowed_outgoing_stream_id_ = frame.max_stream_id;
+
+ // Outgoing stream limit has increased, tell the applications
+ session_->OnCanCreateNewOutgoingStream();
+
+ return true;
+}
+
+bool QuicStreamIdManager::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(frame.stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ QUIC_CODE_COUNT_N(stream_id_blocked_received, 2, 2);
+ QuicStreamId id = frame.stream_id;
+ if (!IsIncomingStream(frame.stream_id)) {
+ // Client/server mismatch, close the connection
+ // TODO(fkastenholz): revise when proper IETF Connection Close support is
+ // done.
+ QUIC_CODE_COUNT(stream_id_blocked_bad_direction);
+ session_->connection()->CloseConnection(
+ QUIC_STREAM_ID_BLOCKED_ERROR,
+ "Invalid stream ID directionality specified",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ if (id > advertised_max_allowed_incoming_stream_id_) {
+ // Peer thinks it can send more streams that we've told it.
+ // This is a protocol error.
+ // TODO(fkastenholz): revise when proper IETF Connection Close support is
+ // done.
+ QUIC_CODE_COUNT(stream_id_blocked_id_too_big);
+ session_->connection()->CloseConnection(
+ QUIC_STREAM_ID_BLOCKED_ERROR, "Invalid stream ID specified",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ if (id < actual_max_allowed_incoming_stream_id_) {
+ // Peer thinks it's blocked on an ID that is less than our current
+ // max. Inform the peer of the correct stream ID.
+ SendMaxStreamIdFrame();
+ return true;
+ }
+ // The peer's notion of the maximum ID is correct,
+ // there is nothing to do.
+ QUIC_CODE_COUNT(stream_id_blocked_id_correct);
+ return true;
+}
+
+// TODO(fkastenholz): Many changes will be needed here:
+// -- Use IETF QUIC server/client-initiation sense
+// -- Support both BIDI and UNI streams.
+// -- can not change the max number of streams after config negotiation has
+// been done.
+void QuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_streams) {
+ max_allowed_outgoing_streams_ = max_streams;
+ max_allowed_outgoing_stream_id_ =
+ next_outgoing_stream_id_ + (max_streams - 1) * kV99StreamIdIncrement;
+}
+
+// TODO(fkastenholz): Many changes will be needed here:
+// -- can not change the max number of streams after config negotiation has
+// been done.
+// -- Currently uses the Google Client/server-initiation sense, needs to
+// be IETF.
+// -- Support both BIDI and UNI streams.
+// -- Convert calculation of the maximum ID from Google-QUIC semantics to IETF
+// QUIC semantics.
+void QuicStreamIdManager::SetMaxOpenIncomingStreams(size_t max_streams) {
+ max_allowed_incoming_streams_ = max_streams;
+ // The peer should always believe that it has the negotiated
+ // number of stream ids available for use.
+ available_incoming_streams_ = max_allowed_incoming_streams_;
+
+ // the window is a fraction of the peer's notion of its stream-id space.
+ max_stream_id_window_ =
+ available_incoming_streams_ / kMaxStreamIdWindowDivisor;
+ if (max_stream_id_window_ == 0) {
+ max_stream_id_window_ = 1;
+ }
+
+ actual_max_allowed_incoming_stream_id_ =
+ first_incoming_dynamic_stream_id_ +
+ (max_allowed_incoming_streams_ - 1) * kV99StreamIdIncrement;
+ // To start, we can assume advertised and actual are the same.
+ advertised_max_allowed_incoming_stream_id_ =
+ actual_max_allowed_incoming_stream_id_;
+}
+
+void QuicStreamIdManager::MaybeSendMaxStreamIdFrame() {
+ if (available_incoming_streams_ > max_stream_id_window_) {
+ // window too large, no advertisement
+ return;
+ }
+ // Calculate the number of streams that the peer will believe
+ // it has. The "/kV99StreamIdIncrement" converts from stream-id-
+ // values to number-of-stream-ids.
+ available_incoming_streams_ += (actual_max_allowed_incoming_stream_id_ -
+ advertised_max_allowed_incoming_stream_id_) /
+ kV99StreamIdIncrement;
+ SendMaxStreamIdFrame();
+}
+
+void QuicStreamIdManager::SendMaxStreamIdFrame() {
+ advertised_max_allowed_incoming_stream_id_ =
+ actual_max_allowed_incoming_stream_id_;
+ // And Advertise it.
+ session_->SendMaxStreamId(advertised_max_allowed_incoming_stream_id_);
+}
+
+void QuicStreamIdManager::OnStreamClosed(QuicStreamId stream_id) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ if (!IsIncomingStream(stream_id)) {
+ // Nothing to do for outbound streams with respect to the
+ // stream ID space management.
+ return;
+ }
+ // If the stream is inbound, we can increase the stream ID limit and maybe
+ // advertise the new limit to the peer.
+ if (actual_max_allowed_incoming_stream_id_ >=
+ (kMaxQuicStreamId - kV99StreamIdIncrement)) {
+ // Reached the maximum stream id value that the implementation
+ // supports. Nothing can be done here.
+ return;
+ }
+ actual_max_allowed_incoming_stream_id_ += kV99StreamIdIncrement;
+ MaybeSendMaxStreamIdFrame();
+}
+
+QuicStreamId QuicStreamIdManager::GetNextOutgoingStreamId() {
+ QUIC_BUG_IF(next_outgoing_stream_id_ > max_allowed_outgoing_stream_id_)
+ << "Attempt allocate a new outgoing stream ID would exceed the limit";
+ QuicStreamId id = next_outgoing_stream_id_;
+ next_outgoing_stream_id_ += kV99StreamIdIncrement;
+ return id;
+}
+
+bool QuicStreamIdManager::CanOpenNextOutgoingStream() {
+ DCHECK_EQ(QUIC_VERSION_99, session_->connection()->transport_version());
+ if (next_outgoing_stream_id_ > max_allowed_outgoing_stream_id_) {
+ // Next stream ID would exceed the limit, need to inform the peer.
+ session_->SendStreamIdBlocked(max_allowed_outgoing_stream_id_);
+ QUIC_CODE_COUNT(reached_outgoing_stream_id_limit);
+ return false;
+ }
+ return true;
+}
+
+void QuicStreamIdManager::RegisterStaticStream(QuicStreamId stream_id) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ QuicStreamId first_dynamic_stream_id = stream_id + kV99StreamIdIncrement;
+
+ if (IsIncomingStream(first_dynamic_stream_id)) {
+ // This code is predicated on static stream ids being allocated densely, in
+ // order, and starting with the first stream allowed. QUIC_BUG if this is
+ // not so.
+ QUIC_BUG_IF(stream_id > first_incoming_dynamic_stream_id_)
+ << "Error in incoming static stream allocation, expected to allocate "
+ << first_incoming_dynamic_stream_id_ << " got " << stream_id;
+
+ // This is a stream id for a stream that is started by the peer, deal with
+ // the incoming stream ids. Increase the floor and adjust everything
+ // accordingly.
+ if (stream_id == first_incoming_dynamic_stream_id_) {
+ actual_max_allowed_incoming_stream_id_ += kV99StreamIdIncrement;
+ first_incoming_dynamic_stream_id_ = first_dynamic_stream_id;
+ }
+ return;
+ }
+
+ // This code is predicated on static stream ids being allocated densely, in
+ // order, and starting with the first stream allowed. QUIC_BUG if this is
+ // not so.
+ QUIC_BUG_IF(stream_id > first_outgoing_dynamic_stream_id_)
+ << "Error in outgoing static stream allocation, expected to allocate "
+ << first_outgoing_dynamic_stream_id_ << " got " << stream_id;
+ // This is a stream id for a stream that is started by this node; deal with
+ // the outgoing stream ids. Increase the floor and adjust everything
+ // accordingly.
+ if (stream_id == first_outgoing_dynamic_stream_id_) {
+ max_allowed_outgoing_stream_id_ += kV99StreamIdIncrement;
+ first_outgoing_dynamic_stream_id_ = first_dynamic_stream_id;
+ }
+}
+
+bool QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
+ const QuicStreamId stream_id) {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(stream_id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ available_streams_.erase(stream_id);
+
+ if (largest_peer_created_stream_id_ !=
+ QuicUtils::GetInvalidStreamId(
+ session_->connection()->transport_version()) &&
+ stream_id <= largest_peer_created_stream_id_) {
+ return true;
+ }
+
+ if (stream_id > actual_max_allowed_incoming_stream_id_) {
+ // Desired stream ID is larger than the limit, do not increase.
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "Failed to create a new incoming stream with id:"
+ << stream_id << ". Maximum allowed stream id is "
+ << actual_max_allowed_incoming_stream_id_ << ".";
+ session_->connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID,
+ QuicStrCat("Stream id ", stream_id, " above ",
+ actual_max_allowed_incoming_stream_id_),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+
+ available_incoming_streams_--;
+
+ QuicStreamId id = largest_peer_created_stream_id_ + kV99StreamIdIncrement;
+ if (largest_peer_created_stream_id_ ==
+ QuicUtils::GetInvalidStreamId(
+ session_->connection()->transport_version())) {
+ // Adjust id based on perspective and whether stream_id is bidirectional or
+ // unidirectional.
+ if (QuicUtils::IsBidirectionalStreamId(stream_id)) {
+ // This should only happen on client side because server bidirectional
+ // stream ID manager's largest_peer_created_stream_id_ is initialized to
+ // the crypto stream ID.
+ DCHECK_EQ(Perspective::IS_CLIENT, session_->perspective());
+ id = 1;
+ } else {
+ id = session_->perspective() == Perspective::IS_SERVER ? 2 : 3;
+ }
+ }
+ for (; id < stream_id; id += kV99StreamIdIncrement) {
+ available_streams_.insert(id);
+ }
+ largest_peer_created_stream_id_ = stream_id;
+ return true;
+}
+
+bool QuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ if (!IsIncomingStream(id)) {
+ // Stream IDs under next_ougoing_stream_id_ are either open or previously
+ // open but now closed.
+ return id >= next_outgoing_stream_id_;
+ }
+ // For peer created streams, we also need to consider available streams.
+ return largest_peer_created_stream_id_ ==
+ QuicUtils::GetInvalidStreamId(
+ session_->connection()->transport_version()) ||
+ id > largest_peer_created_stream_id_ ||
+ QuicContainsKey(available_streams_, id);
+}
+
+bool QuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
+ DCHECK_EQ(QuicUtils::IsBidirectionalStreamId(id),
+ QuicUtils::IsBidirectionalStreamId(next_outgoing_stream_id_));
+ return id % kV99StreamIdIncrement !=
+ next_outgoing_stream_id_ % kV99StreamIdIncrement;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.h
new file mode 100644
index 00000000000..6fd75fb4168
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager.h
@@ -0,0 +1,238 @@
+// 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_CORE_QUIC_STREAM_ID_MANAGER_H_
+#define QUICHE_QUIC_CORE_QUIC_STREAM_ID_MANAGER_H_
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+namespace test {
+class QuicSessionPeer;
+class QuicStreamIdManagerPeer;
+} // namespace test
+
+class QuicSession;
+
+// Amount to increment a stream ID value to get the next stream ID in
+// the stream ID space.
+const QuicStreamId kV99StreamIdIncrement = 4;
+
+// This constant controls the size of the window when deciding whether
+// to generate a MAX STREAM ID frame or not. See the discussion of the
+// window, below, for more details.
+const int kMaxStreamIdWindowDivisor = 2;
+
+// This class manages the stream ids for Version 99/IETF QUIC.
+// TODO(fkastenholz): Expand to support bi- and uni-directional stream ids
+// TODO(fkastenholz): Roll in pre-version-99 management
+class QUIC_EXPORT_PRIVATE QuicStreamIdManager {
+ public:
+ QuicStreamIdManager(QuicSession* session,
+ QuicStreamId next_outgoing_stream_id,
+ QuicStreamId largest_peer_created_stream_id,
+ QuicStreamId first_incoming_dynamic_stream_id,
+ size_t max_allowed_outgoing_streams,
+ size_t max_allowed_incoming_streams);
+
+ ~QuicStreamIdManager();
+
+ // Generate a string suitable for sending to the log/etc to show current state
+ // of the stream ID manager.
+ std::string DebugString() const {
+ return QuicStrCat(
+ " { max_allowed_outgoing_stream_id: ", max_allowed_outgoing_stream_id_,
+ ", actual_max_allowed_incoming_stream_id_: ",
+ actual_max_allowed_incoming_stream_id_,
+ ", advertised_max_allowed_incoming_stream_id_: ",
+ advertised_max_allowed_incoming_stream_id_,
+ ", max_stream_id_window_: ", max_stream_id_window_,
+ ", max_allowed_outgoing_streams_: ", max_allowed_outgoing_streams_,
+ ", max_allowed_incoming_streams_: ", max_allowed_incoming_streams_,
+ ", available_incoming_streams_: ", available_incoming_streams_,
+ ", first_incoming_dynamic_stream_id_: ",
+ first_incoming_dynamic_stream_id_,
+ ", first_outgoing_dynamic_stream_id_: ",
+ first_outgoing_dynamic_stream_id_, " }");
+ }
+
+ // Processes the MAX STREAM ID frame, invoked from
+ // QuicSession::OnMaxStreamIdFrame. It has the same semantics as the
+ // QuicFramerVisitorInterface, returning true if the framer should continue
+ // processing the packet, false if not.
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame);
+
+ // Processes the STREAM ID BLOCKED frame, invoked from
+ // QuicSession::OnStreamIdBlockedFrame. It has the same semantics as the
+ // QuicFramerVisitorInterface, returning true if the framer should continue
+ // processing the packet, false if not.
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame);
+
+ // Indicates whether the next outgoing stream ID can be allocated or not. The
+ // test is whether it will exceed the maximum-stream-id or not.
+ bool CanOpenNextOutgoingStream();
+
+ // Generate and send a MAX_STREAM_ID frame.
+ void SendMaxStreamIdFrame();
+
+ // Invoked to deal with releasing a stream ID.
+ void OnStreamClosed(QuicStreamId stream_id);
+
+ // Returns the next outgoing stream id. If it fails (due to running into the
+ // max_allowed_outgoing_stream_id limit) then it returns an invalid stream id.
+ QuicStreamId GetNextOutgoingStreamId();
+
+ // Initialize the maximum allowed incoming stream id and number of streams.
+ void SetMaxOpenIncomingStreams(size_t max_streams);
+
+ // Initialize the maximum allowed outgoing stream id, number of streams, and
+ // MAX_STREAM_ID advertisement window.
+ void SetMaxOpenOutgoingStreams(size_t max_streams);
+
+ // Register a new stream as a static stream. This is used so that the
+ // advertised maximum stream ID can be calculated based on the start of the
+ // dynamic stream space. This method will take any stream ID, one that either
+ // this node or the peer will initiate.
+ void RegisterStaticStream(QuicStreamId stream_id);
+
+ // Check that an incoming stream id is valid -- is below the maximum allowed
+ // stream ID. Note that this method uses the actual maximum, not the most
+ // recently advertised maximum this helps preserve the Google-QUIC semantic
+ // that we actually care about the number of open streams, not the maximum
+ // stream ID. Returns true if the stream ID is valid. If the stream ID fails
+ // the test, will close the connection (per the protocol specification) and
+ // return false. This method also maintains state with regard to the number of
+ // streams that the peer can open (used for generating MAX_STREAM_ID frames).
+ // This method should be called exactly once for each incoming stream
+ // creation.
+ bool MaybeIncreaseLargestPeerStreamId(const QuicStreamId stream_id);
+
+ // Returns true if |id| is still available.
+ bool IsAvailableStream(QuicStreamId id) const;
+
+ // Return true if given stream is peer initiated.
+ bool IsIncomingStream(QuicStreamId id) const;
+
+ size_t max_allowed_outgoing_streams() const {
+ return max_allowed_outgoing_streams_;
+ }
+ size_t max_allowed_incoming_streams() const {
+ return max_allowed_incoming_streams_;
+ }
+ QuicStreamId max_allowed_outgoing_stream_id() const {
+ return max_allowed_outgoing_stream_id_;
+ }
+ QuicStreamId advertised_max_allowed_incoming_stream_id() const {
+ return advertised_max_allowed_incoming_stream_id_;
+ }
+ QuicStreamId actual_max_allowed_incoming_stream_id() const {
+ return actual_max_allowed_incoming_stream_id_;
+ }
+ QuicStreamId max_stream_id_window() const { return max_stream_id_window_; }
+
+ QuicStreamId next_outgoing_stream_id() const {
+ return next_outgoing_stream_id_;
+ }
+
+ QuicStreamId first_incoming_dynamic_stream_id() {
+ return first_incoming_dynamic_stream_id_;
+ }
+ QuicStreamId first_outgoing_dynamic_stream_id() {
+ return first_outgoing_dynamic_stream_id_;
+ }
+ size_t available_incoming_streams() { return available_incoming_streams_; }
+
+ void set_max_allowed_incoming_streams(size_t stream_count) {
+ max_allowed_incoming_streams_ = stream_count;
+ }
+
+ void set_largest_peer_created_stream_id(
+ QuicStreamId largest_peer_created_stream_id) {
+ largest_peer_created_stream_id_ = largest_peer_created_stream_id;
+ }
+
+ private:
+ friend class test::QuicSessionPeer;
+ friend class test::QuicStreamIdManagerPeer;
+
+ // Check whether the MAX_STREAM_ID window has opened up enough and, if so,
+ // generate and send a MAX_STREAM_ID frame.
+ void MaybeSendMaxStreamIdFrame();
+
+ // Back reference to the session containing this Stream ID Manager.
+ // needed to access various session methods, such as perspective()
+ QuicSession* session_;
+
+ // The ID to use for the next outgoing stream.
+ QuicStreamId next_outgoing_stream_id_;
+
+ // Set of stream ids that are less than the largest stream id that has been
+ // received, but are nonetheless available to be created.
+ QuicUnorderedSet<QuicStreamId> available_streams_;
+
+ QuicStreamId largest_peer_created_stream_id_;
+
+ // The maximum stream ID value that we can use. This is initialized based on,
+ // first, the default number of open streams we can do, updated per the number
+ // of streams we receive in the transport parameters, and then finally is
+ // modified whenever a MAX_STREAM_ID frame is received from the peer.
+ QuicStreamId max_allowed_outgoing_stream_id_;
+
+ // Unlike for streams this node initiates, for incoming streams, there are two
+ // maxima; the actual maximum which is the limit the peer must obey and the
+ // maximum that was most recently advertised to the peer in a MAX_STREAM_ID
+ // frame.
+ //
+ // The advertised maximum is never larger than the actual maximum. The actual
+ // maximum increases whenever an incoming stream is closed. The advertised
+ // maximum increases (to the actual maximum) whenever a MAX_STREAM_ID is sent.
+ //
+ // The peer is granted some leeway, incoming streams are accepted as long as
+ // their stream id is not greater than the actual maximum. The protocol
+ // specifies that the advertised maximum is the limit. This implmentation uses
+ // the actual maximum in order to support Google-QUIC semantics, where it's
+ // the number of open streams, not their ID number, that is the real limit.
+ QuicStreamId actual_max_allowed_incoming_stream_id_;
+ QuicStreamId advertised_max_allowed_incoming_stream_id_;
+
+ // max_stream_id_window_ is set to max_allowed_outgoing_streams_ / 2
+ // (half of the number of streams that are allowed). The local node
+ // does not send a MAX_STREAM_ID frame to the peer until the local node
+ // believes that the peer can open fewer than |max_stream_id_window_|
+ // streams. When that is so, the local node sends a MAX_STREAM_ID every time
+ // an inbound stream is closed.
+ QuicStreamId max_stream_id_window_;
+
+ // Maximum number of outgoing and incoming streams that are allowed to be
+ // concurrently opened. Initialized as part of configuration.
+ size_t max_allowed_outgoing_streams_;
+ size_t max_allowed_incoming_streams_;
+
+ // Keep track of the first dynamic stream id (which is the largest static
+ // stream id plus one id). For Google QUIC, static streams are not counted
+ // against the stream count limit. When the number of static streams
+ // increases, the maximum stream id has to increase by a corresponding amount.
+ // These are used as floors from which the relevant maximum is
+ // calculated. Keeping the "first dynamic" rather than the "last static" has
+ // some implementation advantages.
+ QuicStreamId first_incoming_dynamic_stream_id_;
+ QuicStreamId first_outgoing_dynamic_stream_id_;
+
+ // Number of streams that that this node believes that the
+ // peer can open. It is initialized to the same value as
+ // max_allowed_incoming_streams_. It is decremented every
+ // time a new incoming stream is detected. A MAX_STREAM_ID
+ // is sent whenver a stream closes and this counter is less
+ // than the window. When that happens, it is incremented by
+ // the number of streams we make available (the actual max
+ // stream ID - the most recently advertised one)
+ size_t available_incoming_streams_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_STREAM_ID_MANAGER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc
new file mode 100644
index 00000000000..3d0c06c2eb1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_id_manager_test.cc
@@ -0,0 +1,844 @@
+// 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 "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h"
+
+#include <cstdint>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class TestQuicStream : public QuicStream {
+ // TestQuicStream exists simply as a place to hang OnDataAvailable().
+ public:
+ TestQuicStream(QuicStreamId id, QuicSession* session, StreamType type)
+ : QuicStream(id, session, /*is_static=*/false, type) {}
+
+ void OnDataAvailable() override {}
+};
+
+class TestQuicSession : public MockQuicSession {
+ public:
+ TestQuicSession(QuicConnection* connection)
+ : MockQuicSession(connection, /*create_mock_crypto_stream=*/true) {
+ Initialize();
+ }
+
+ TestQuicStream* CreateIncomingStream(QuicStreamId id) override {
+ TestQuicStream* stream = new TestQuicStream(
+ id, this,
+ DetermineStreamType(id, connection()->transport_version(),
+ perspective(),
+ /*is_incoming=*/true, BIDIRECTIONAL));
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ bool SaveFrame(const QuicFrame& frame) {
+ save_frame_ = frame;
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ const QuicFrame& save_frame() { return save_frame_; }
+
+ bool ClearControlFrame(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ TestQuicStream* CreateOutgoingBidirectionalStream() {
+ if (!CanOpenNextOutgoingBidirectionalStream()) {
+ return nullptr;
+ }
+ QuicStreamId id = GetNextOutgoingBidirectionalStreamId();
+ TestQuicStream* stream = new TestQuicStream(id, this, BIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ TestQuicStream* CreateOutgoingUnidirectionalStream() {
+ if (!CanOpenNextOutgoingUnidirectionalStream()) {
+ return nullptr;
+ }
+ QuicStreamId id = GetNextOutgoingUnidirectionalStreamId();
+ TestQuicStream* stream = new TestQuicStream(id, this, WRITE_UNIDIRECTIONAL);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+
+ private:
+ QuicFrame save_frame_;
+};
+
+class QuicStreamIdManagerTestBase : public QuicTestWithParam<bool> {
+ protected:
+ explicit QuicStreamIdManagerTestBase(Perspective perspective)
+ : connection_(new StrictMock<MockQuicConnection>(
+ &helper_,
+ &alarm_factory_,
+ perspective,
+ ParsedQuicVersionVector(
+ {{PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99}}))) {
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ session_ = QuicMakeUnique<TestQuicSession>(connection_);
+ stream_id_manager_ =
+ GetParam() ? QuicSessionPeer::v99_bidirectional_stream_id_manager(
+ session_.get())
+ : QuicSessionPeer::v99_unidirectional_stream_id_manager(
+ session_.get());
+ }
+
+ QuicTransportVersion transport_version() const {
+ return connection_->transport_version();
+ }
+
+ void CloseStream(QuicStreamId id) { session_->CloseStream(id); }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT) +
+ kV99StreamIdIncrement * n;
+ }
+
+ QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) {
+ return QuicUtils::GetFirstUnidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT) +
+ kV99StreamIdIncrement * n;
+ }
+
+ QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_SERVER) +
+ kV99StreamIdIncrement * n;
+ }
+
+ QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
+ return QuicUtils::GetFirstUnidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_SERVER) +
+ kV99StreamIdIncrement * n;
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ std::unique_ptr<TestQuicSession> session_;
+ QuicStreamIdManager* stream_id_manager_;
+};
+
+// Following tests are either client-specific (they depend, in some way, on
+// client-specific attributes, such as the initial stream ID) or are
+// server/client independent (arbitrarily all such tests have been placed here).
+
+class QuicStreamIdManagerTestClient : public QuicStreamIdManagerTestBase {
+ protected:
+ QuicStreamIdManagerTestClient()
+ : QuicStreamIdManagerTestBase(Perspective::IS_CLIENT) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestClient, testing::Bool());
+
+// Check that the parameters used by the stream ID manager are properly
+// initialized.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerClientInitialization) {
+ // These fields are inited via the QuicSession constructor to default
+ // values defined as a constant.
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ stream_id_manager_->max_allowed_incoming_streams());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ stream_id_manager_->max_allowed_outgoing_streams());
+
+ // The window for advertising updates to the MAX STREAM ID is half the number
+ // of streams allowed.
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection / kMaxStreamIdWindowDivisor,
+ stream_id_manager_->max_stream_id_window());
+
+ // This test runs as a client, so it initiates (that is to say, outgoing)
+ // even-numbered stream IDs. Also, our implementation starts allocating
+ // stream IDs at 0 (for clients) 1 (for servers) -- before taking statically
+ // allocated streams into account. The -1 in the calculation is
+ // because the value being tested is the maximum allowed stream ID, not the
+ // first unallowed stream id.
+ const QuicStreamId kExpectedMaxOutgoingStreamId =
+ (GetParam() ? session_->next_outgoing_bidirectional_stream_id()
+ : session_->next_outgoing_unidirectional_stream_id()) +
+ ((kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement);
+ EXPECT_EQ(kExpectedMaxOutgoingStreamId,
+ stream_id_manager_->max_allowed_outgoing_stream_id());
+
+ // Same for IDs of incoming streams...
+ const QuicStreamId kExpectedMaxIncomingStreamId =
+ stream_id_manager_->first_incoming_dynamic_stream_id() +
+ (kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement;
+ EXPECT_EQ(kExpectedMaxIncomingStreamId,
+ stream_id_manager_->actual_max_allowed_incoming_stream_id());
+ EXPECT_EQ(kExpectedMaxIncomingStreamId,
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+}
+
+// This test checks that the initialization for the maximum allowed outgoing
+// stream id is correct.
+TEST_P(QuicStreamIdManagerTestClient, CheckMaxAllowedOutgoing) {
+ const size_t kNumOutgoingStreams = 124;
+ stream_id_manager_->SetMaxOpenOutgoingStreams(kNumOutgoingStreams);
+ EXPECT_EQ(kNumOutgoingStreams,
+ stream_id_manager_->max_allowed_outgoing_streams());
+
+ // Check that the maximum available stream is properly set.
+ size_t expected_max_outgoing_id =
+ (GetParam() ? session_->next_outgoing_bidirectional_stream_id()
+ : session_->next_outgoing_unidirectional_stream_id()) +
+ ((kNumOutgoingStreams - 1) * kV99StreamIdIncrement);
+ EXPECT_EQ(expected_max_outgoing_id,
+ stream_id_manager_->max_allowed_outgoing_stream_id());
+}
+
+// This test checks that the initialization for the maximum allowed incoming
+// stream id is correct.
+TEST_P(QuicStreamIdManagerTestClient, CheckMaxAllowedIncoming) {
+ const size_t kStreamCount = 245;
+ stream_id_manager_->SetMaxOpenIncomingStreams(kStreamCount);
+ EXPECT_EQ(kStreamCount, stream_id_manager_->max_allowed_incoming_streams());
+ // Check that the window is 1/2 (integer math) of the stream count.
+ EXPECT_EQ(kStreamCount / 2, stream_id_manager_->max_stream_id_window());
+
+ // Actual- and advertised- maxima start out equal.
+ EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+
+ // Check that the maximum stream ID is properly calculated.
+ EXPECT_EQ(stream_id_manager_->first_incoming_dynamic_stream_id() +
+ ((kStreamCount - 1) * kV99StreamIdIncrement),
+ stream_id_manager_->actual_max_allowed_incoming_stream_id());
+}
+
+// This test checks that the stream advertisement window is set to 1
+// if the number of stream ids is 1. This is a special case in the code.
+TEST_P(QuicStreamIdManagerTestClient, CheckMaxStreamIdWindow1) {
+ stream_id_manager_->SetMaxOpenIncomingStreams(1);
+ EXPECT_EQ(1u, stream_id_manager_->max_allowed_incoming_streams());
+ // If streamid_count/2==0 (integer math) force it to 1.
+ EXPECT_EQ(1u, stream_id_manager_->max_stream_id_window());
+}
+
+// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is less than the
+// stream ID most recently advertised in a MAX_STREAM_ID frame. This should
+// cause a MAX_STREAM_ID frame with the most recently advertised stream id to be
+// sent.
+TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedOk) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+ QuicStreamId stream_id =
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id() -
+ kV99StreamIdIncrement;
+ QuicStreamIdBlockedFrame frame(0, stream_id);
+ session_->OnStreamIdBlockedFrame(frame);
+
+ // We should see a MAX_STREAM_ID frame.
+ EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type);
+
+ // and it should advertise the current max-allowed value.
+ EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+ session_->save_frame().max_stream_id_frame.max_stream_id);
+}
+
+// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is equal to
+// stream ID most recently advertised in a MAX_STREAM_ID frame. No
+// MAX_STREAM_ID should be generated.
+TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedNoOp) {
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ QuicStreamId stream_id =
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id();
+ QuicStreamIdBlockedFrame frame(0, stream_id);
+ session_->OnStreamIdBlockedFrame(frame);
+}
+
+// Check the case of the stream ID in a STREAM_ID_BLOCKED frame is greater than
+// the stream ID most recently advertised in a MAX_STREAM_ID frame. Expect a
+// connection close with an error.
+TEST_P(QuicStreamIdManagerTestClient, ProcessStreamIdBlockedTooBig) {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ QuicStreamId stream_id =
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id() +
+ kV99StreamIdIncrement;
+ QuicStreamIdBlockedFrame frame(0, stream_id);
+ session_->OnStreamIdBlockedFrame(frame);
+}
+
+// Same basic tests as above, but calls
+// QuicStreamIdManager::MaybeIncreaseLargestPeerStreamId directly, avoiding the
+// call chain. The intent is that if there is a problem, the following tests
+// will point to either the stream ID manager or the call chain. They also
+// provide specific, small scale, tests of a public QuicStreamIdManager method.
+// First test make sure that streams with ids below the limit are accepted.
+TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdValidBelowLimit) {
+ QuicStreamId stream_id =
+ stream_id_manager_->actual_max_allowed_incoming_stream_id() -
+ kV99StreamIdIncrement;
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+}
+
+// Accept a stream with an ID that equals the limit.
+TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdValidAtLimit) {
+ QuicStreamId stream_id =
+ stream_id_manager_->actual_max_allowed_incoming_stream_id();
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+}
+
+// Close the connection if the id exceeds the limit.
+TEST_P(QuicStreamIdManagerTestClient, IsIncomingStreamIdInValidAboveLimit) {
+ QuicStreamId stream_id =
+ stream_id_manager_->actual_max_allowed_incoming_stream_id() +
+ kV99StreamIdIncrement;
+ std::string error_details =
+ GetParam() ? "Stream id 401 above 397" : "Stream id 403 above 399";
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _));
+ EXPECT_FALSE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+}
+
+// Test that a client will reject a MAX_STREAM_ID that specifies a
+// server-initiated stream ID.
+TEST_P(QuicStreamIdManagerTestClient, RejectServerMaxStreamId) {
+ QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id();
+
+ // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the
+ // current MAX.
+ id += (kV99StreamIdIncrement * 2);
+
+ // Make it an odd (server-initiated) ID.
+ id |= 0x1;
+ EXPECT_FALSE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id));
+
+ // Make the frame and process it; should result in the connection being
+ // closed.
+ QuicMaxStreamIdFrame frame(0, id);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAM_ID_ERROR, _, _));
+ session_->OnMaxStreamIdFrame(frame);
+}
+
+// Test that a client will reject a STREAM_ID_BLOCKED that specifies a
+// client-initiated stream ID. STREAM_ID_BLOCKED from a server should specify an
+// odd (server-initiated_ ID). Generate one with an odd ID and check that the
+// connection is closed.
+TEST_P(QuicStreamIdManagerTestClient, RejectServerStreamIdBlocked) {
+ QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id();
+
+ // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the
+ // current MAX.
+ id += (kV99StreamIdIncrement * 2);
+ // Make sure it's odd, like a client-initiated ID.
+ id &= ~0x01;
+ EXPECT_TRUE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id));
+
+ // Generate and process the frame; connection should be closed.
+ QuicStreamIdBlockedFrame frame(0, id);
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+ session_->OnStreamIdBlockedFrame(frame);
+}
+
+// Test functionality for reception of a MAX STREAM ID frame. This code is
+// client/server-agnostic.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerClientOnMaxStreamIdFrame) {
+ // Get the current maximum allowed outgoing stream ID.
+ QuicStreamId initial_stream_id =
+ stream_id_manager_->max_allowed_outgoing_stream_id();
+ QuicMaxStreamIdFrame frame;
+
+ // If the stream ID in the frame is < the current maximum then
+ // the frame should be ignored.
+ frame.max_stream_id = initial_stream_id - kV99StreamIdIncrement;
+ EXPECT_TRUE(stream_id_manager_->OnMaxStreamIdFrame(frame));
+ EXPECT_EQ(initial_stream_id,
+ stream_id_manager_->max_allowed_outgoing_stream_id());
+
+ // A stream ID greater than the current limit should increase the limit.
+ frame.max_stream_id = initial_stream_id + kV99StreamIdIncrement;
+ EXPECT_TRUE(stream_id_manager_->OnMaxStreamIdFrame(frame));
+ EXPECT_EQ(initial_stream_id + kV99StreamIdIncrement,
+ stream_id_manager_->max_allowed_outgoing_stream_id());
+}
+
+// Test functionality for reception of a STREAM ID BLOCKED frame.
+// This code is client/server-agnostic.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerOnStreamIdBlockedFrame) {
+ // Get the current maximum allowed incoming stream ID.
+ QuicStreamId advertised_stream_id =
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id();
+ QuicStreamIdBlockedFrame frame;
+
+ // If the peer is saying it's blocked on the stream ID that
+ // we've advertised, it's a noop since the peer has the correct information.
+ frame.stream_id = advertised_stream_id;
+ EXPECT_TRUE(stream_id_manager_->OnStreamIdBlockedFrame(frame));
+
+ // If the peer is saying it's blocked on a stream ID that is larger
+ // than what we've advertised, the connection should get closed.
+ frame.stream_id = advertised_stream_id + kV99StreamIdIncrement;
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+ EXPECT_FALSE(stream_id_manager_->OnStreamIdBlockedFrame(frame));
+
+ // If the peer is saying it's blocked on a stream ID that is less than
+ // what we've advertised, we send a MAX STREAM ID frame and update
+ // the advertised value.
+ // First, need to bump up the actual max so there is room for the MAX
+ // STREAM_ID frame to send a larger ID.
+ QuicStreamId actual_stream_id =
+ stream_id_manager_->actual_max_allowed_incoming_stream_id();
+ stream_id_manager_->OnStreamClosed(
+ stream_id_manager_->first_incoming_dynamic_stream_id());
+ EXPECT_EQ(actual_stream_id + kV99StreamIdIncrement,
+ stream_id_manager_->actual_max_allowed_incoming_stream_id());
+ EXPECT_GT(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+
+ // Now simulate receiving a STTREAM_ID_BLOCKED frame...
+ // Changing the actual maximum, above, forces a MAX STREAM ID frame to be
+ // sent, so the logic for that (SendMaxStreamIdFrame(), etc) is tested.
+ frame.stream_id = advertised_stream_id;
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(1)
+ .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+ EXPECT_TRUE(stream_id_manager_->OnStreamIdBlockedFrame(frame));
+ EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+ EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type);
+ EXPECT_EQ(stream_id_manager_->advertised_max_allowed_incoming_stream_id(),
+ session_->save_frame().max_stream_id_frame.max_stream_id);
+
+ // Ensure a client initiated stream ID is rejected.
+ frame.stream_id = GetParam() ? GetNthClientInitiatedBidirectionalId(1)
+ : GetNthClientInitiatedUnidirectionalId(1);
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+ EXPECT_FALSE(stream_id_manager_->OnStreamIdBlockedFrame(frame));
+}
+
+// Test GetNextOutgoingStream. This is client/server agnostic.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerGetNextOutgoingFrame) {
+ // Number of streams we can open and the first one we should get when
+ // opening...
+ int number_of_streams = kDefaultMaxStreamsPerConnection;
+ QuicStreamId stream_id =
+ GetParam() ? session_->next_outgoing_bidirectional_stream_id()
+ : session_->next_outgoing_unidirectional_stream_id();
+
+ while (number_of_streams) {
+ EXPECT_TRUE(stream_id_manager_->CanOpenNextOutgoingStream());
+ EXPECT_EQ(stream_id, stream_id_manager_->GetNextOutgoingStreamId());
+ stream_id += kV99StreamIdIncrement;
+ number_of_streams--;
+ }
+ EXPECT_EQ(stream_id - kV99StreamIdIncrement,
+ stream_id_manager_->max_allowed_outgoing_stream_id());
+
+ // If we try to check that the next outgoing stream id is available it should
+ // A) fail and B) generate a STREAM_ID_BLOCKED frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(1)
+ .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+ EXPECT_FALSE(stream_id_manager_->CanOpenNextOutgoingStream());
+ EXPECT_EQ(STREAM_ID_BLOCKED_FRAME, session_->save_frame().type);
+ EXPECT_EQ(stream_id_manager_->max_allowed_outgoing_stream_id(),
+ session_->save_frame().max_stream_id_frame.max_stream_id);
+ // If we try to get the next id (above the limit), it should cause a quic-bug.
+ EXPECT_QUIC_BUG(
+ stream_id_manager_->GetNextOutgoingStreamId(),
+ "Attempt allocate a new outgoing stream ID would exceed the limit");
+}
+
+// Ensure that MaybeIncreaseLargestPeerStreamId works properly. This is
+// server/client agnostic.
+TEST_P(QuicStreamIdManagerTestClient,
+ StreamIdManagerServerMaybeIncreaseLargestPeerStreamId) {
+ EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(
+ stream_id_manager_->actual_max_allowed_incoming_stream_id()));
+ QuicStreamId server_initiated_stream_id =
+ GetParam() ? GetNthServerInitiatedBidirectionalId(0)
+ : GetNthServerInitiatedUnidirectionalId(0);
+ EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(
+ server_initiated_stream_id));
+ // A bad stream ID results in a closed connection.
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _));
+ EXPECT_FALSE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(
+ stream_id_manager_->actual_max_allowed_incoming_stream_id() +
+ kV99StreamIdIncrement));
+}
+
+// Test the MAX STREAM ID Window functionality.
+// Free up Stream ID space. Do not expect to see a MAX_STREAM_ID
+// until |window| stream ids are available.
+TEST_P(QuicStreamIdManagerTestClient, StreamIdManagerServerMaxStreamId) {
+ // Test that a MAX_STREAM_ID frame is generated when the peer has less than
+ // |max_stream_id_window_| streams left that it can initiate.
+
+ // First, open, and then close, max_stream_id_window_ streams. This will
+ // max_stream_id_window_ streams available for the peer -- no MAX_STREAM_ID
+ // should be sent. The -1 is because the check in
+ // QuicStreamIdManager::MaybeSendMaxStreamIdFrame sends a MAX_STREAM_ID if the
+ // number of available streams at the peer is <= |max_stream_id_window_|
+ int stream_count = stream_id_manager_->max_stream_id_window() - 1;
+
+ QuicStreamId advertised_max =
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id();
+ QuicStreamId expected_actual_max_id =
+ stream_id_manager_->actual_max_allowed_incoming_stream_id();
+
+ // Should not get a control-frame transmission since the peer should have
+ // "plenty" of stream IDs to use.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0);
+ // This test runs as a client, so the first stream to release is 2, a
+ // server-initiated stream.
+ QuicStreamId stream_id = GetParam()
+ ? GetNthServerInitiatedBidirectionalId(0)
+ : GetNthServerInitiatedUnidirectionalId(0);
+ size_t old_available_incoming_streams =
+ stream_id_manager_->available_incoming_streams();
+
+ while (stream_count) {
+ EXPECT_TRUE(
+ stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+
+ old_available_incoming_streams--;
+ EXPECT_EQ(old_available_incoming_streams,
+ stream_id_manager_->available_incoming_streams());
+
+ stream_count--;
+ stream_id += kV99StreamIdIncrement;
+ }
+
+ // Now close them, still should get no MAX_STREAM_ID
+ stream_count = stream_id_manager_->max_stream_id_window();
+ stream_id = GetParam() ? GetNthServerInitiatedBidirectionalId(0)
+ : GetNthServerInitiatedUnidirectionalId(0);
+ while (stream_count) {
+ stream_id_manager_->OnStreamClosed(stream_id);
+ stream_count--;
+ stream_id += kV99StreamIdIncrement;
+ expected_actual_max_id += kV99StreamIdIncrement;
+ EXPECT_EQ(expected_actual_max_id,
+ stream_id_manager_->actual_max_allowed_incoming_stream_id());
+ // Advertised maximum should remain the same.
+ EXPECT_EQ(advertised_max,
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+ }
+
+ // This should not change.
+ EXPECT_EQ(old_available_incoming_streams,
+ stream_id_manager_->available_incoming_streams());
+
+ // Now whenever we close a stream we should get a MAX_STREAM_ID frame.
+ // Above code closed all the open streams, so we have to open/close
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(1)
+ .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+ EXPECT_TRUE(stream_id_manager_->MaybeIncreaseLargestPeerStreamId(stream_id));
+ stream_id_manager_->OnStreamClosed(stream_id);
+ stream_id += kV99StreamIdIncrement;
+
+ // Check that the MAX STREAM ID was sent and has the correct values.
+ EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type);
+ EXPECT_EQ(stream_id_manager_->advertised_max_allowed_incoming_stream_id(),
+ session_->save_frame().max_stream_id_frame.max_stream_id);
+}
+
+// Test that registering static stream IDs causes the stream ID limit to rise
+// accordingly. This is server/client agnostic.
+TEST_P(QuicStreamIdManagerTestClient, TestStaticStreamAdjustment) {
+ QuicStreamId first_dynamic =
+ stream_id_manager_->first_incoming_dynamic_stream_id();
+ QuicStreamId expected_max_incoming =
+ stream_id_manager_->actual_max_allowed_incoming_stream_id();
+
+ // First test will register the first dynamic stream id as being for a static
+ // stream. This takes one stream ID out of the low-end of the dynamic range
+ // so therefore the high end should go up by 1 ID.
+ expected_max_incoming += kV99StreamIdIncrement;
+ stream_id_manager_->RegisterStaticStream(first_dynamic);
+ EXPECT_EQ(expected_max_incoming,
+ stream_id_manager_->actual_max_allowed_incoming_stream_id());
+
+ // Now be extreme, increase static by 100 stream ids. A discontinuous
+ // jump is not allowed; make sure.
+ first_dynamic += kV99StreamIdIncrement * 100;
+ expected_max_incoming += kV99StreamIdIncrement * 100;
+ std::string bug_detail =
+ GetParam() ? "allocate 5 got 401" : "allocate 7 got 403";
+ EXPECT_QUIC_BUG(
+ stream_id_manager_->RegisterStaticStream(first_dynamic),
+ "Error in incoming static stream allocation, expected to " + bug_detail);
+}
+
+// Following tests all are server-specific. They depend, in some way, on
+// server-specific attributes, such as the initial stream ID.
+
+class QuicStreamIdManagerTestServer : public QuicStreamIdManagerTestBase {
+ protected:
+ QuicStreamIdManagerTestServer()
+ : QuicStreamIdManagerTestBase(Perspective::IS_SERVER) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests, QuicStreamIdManagerTestServer, testing::Bool());
+
+// This test checks that the initialization for the maximum allowed outgoing
+// stream id is correct.
+TEST_P(QuicStreamIdManagerTestServer, CheckMaxAllowedOutgoing) {
+ const size_t kIncomingStreamCount = 123;
+ stream_id_manager_->SetMaxOpenOutgoingStreams(kIncomingStreamCount);
+ EXPECT_EQ(kIncomingStreamCount,
+ stream_id_manager_->max_allowed_outgoing_streams());
+
+ // Check that the max outgoing stream id is properly calculated
+ EXPECT_EQ(stream_id_manager_->GetNextOutgoingStreamId() +
+ ((kIncomingStreamCount - 1) * kV99StreamIdIncrement),
+ stream_id_manager_->max_allowed_outgoing_stream_id());
+}
+
+// This test checks that the initialization for the maximum allowed incoming
+// stream id is correct.
+TEST_P(QuicStreamIdManagerTestServer, CheckMaxAllowedIncoming) {
+ const size_t kIncomingStreamCount = 245;
+ stream_id_manager_->SetMaxOpenIncomingStreams(kIncomingStreamCount);
+ EXPECT_EQ(kIncomingStreamCount,
+ stream_id_manager_->max_allowed_incoming_streams());
+
+ // Check that the window is 1/2 (integer math) of the stream count.
+ EXPECT_EQ((kIncomingStreamCount / 2),
+ stream_id_manager_->max_stream_id_window());
+
+ // Actual- and advertised- maxima start out equal.
+ EXPECT_EQ(stream_id_manager_->actual_max_allowed_incoming_stream_id(),
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+
+ // First stream ID the client should use should be 3, this means that the max
+ // stream id is 491 -- ((number of stream ids-1) * 2) + first available id.
+ EXPECT_EQ(stream_id_manager_->first_incoming_dynamic_stream_id() +
+ ((kIncomingStreamCount - 1) * kV99StreamIdIncrement),
+ stream_id_manager_->actual_max_allowed_incoming_stream_id());
+}
+
+// Test that a MAX_STREAM_ID frame is generated when half the stream ids become
+// available. This has a useful side effect of testing that when streams are
+// closed, the number of available stream ids increases.
+TEST_P(QuicStreamIdManagerTestServer, MaxStreamIdSlidingWindow) {
+ // Ignore OnStreamReset calls.
+ EXPECT_CALL(*connection_, OnStreamReset(_, _)).WillRepeatedly(Return());
+ // Capture control frames for analysis.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(session_.get(), &TestQuicSession::SaveFrame));
+ // Simulate config being negotiated, causing the limits all to be initialized.
+ session_->OnConfigNegotiated();
+ QuicStreamId first_advert =
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id();
+
+ // Open/close enough streams to shrink the window without causing a MAX STREAM
+ // ID to be generated. The window will open (and a MAX STREAM ID generated)
+ // when max_stream_id_window() stream IDs have been made available. The loop
+ // will make that many stream IDs available, so the last CloseStream should
+ // cause a MAX STREAM ID frame to be generated.
+ int i = static_cast<int>(stream_id_manager_->max_stream_id_window());
+ QuicStreamId id = stream_id_manager_->first_incoming_dynamic_stream_id();
+ while (i) {
+ QuicStream* stream = session_->GetOrCreateStream(id);
+ EXPECT_NE(nullptr, stream);
+ // have to set the stream's fin-received flag to true so that it
+ // does not go into the has-not-received-byte-offset state, leading
+ // to the stream being added to the locally_closed_streams_highest_offset_
+ // map, and therefore not counting as truly being closed. The test requires
+ // that the stream truly close, so that new streams become available,
+ // causing the MAX_STREAM_ID to be sent.
+ stream->set_fin_received(true);
+ EXPECT_EQ(id, stream->id());
+ if (GetParam()) {
+ // Only send reset for incoming bidirectional streams.
+ EXPECT_CALL(*session_, SendRstStream(_, _, _));
+ }
+ CloseStream(stream->id());
+ i--;
+ id += kV99StreamIdIncrement;
+ }
+ EXPECT_EQ(MAX_STREAM_ID_FRAME, session_->save_frame().type);
+ QuicStreamId second_advert =
+ session_->save_frame().max_stream_id_frame.max_stream_id;
+ EXPECT_EQ(first_advert + (stream_id_manager_->max_stream_id_window() *
+ kV99StreamIdIncrement),
+ second_advert);
+}
+
+// Tast that an attempt to create an outgoing stream does not exceed the limit
+// and that it generates an appropriate STREAM_ID_BLOCKED frame.
+TEST_P(QuicStreamIdManagerTestServer, NewStreamDoesNotExceedLimit) {
+ size_t stream_count = stream_id_manager_->max_allowed_outgoing_streams();
+ EXPECT_NE(0u, stream_count);
+ TestQuicStream* stream;
+ while (stream_count) {
+ stream = GetParam() ? session_->CreateOutgoingBidirectionalStream()
+ : session_->CreateOutgoingUnidirectionalStream();
+ EXPECT_NE(stream, nullptr);
+ stream_count--;
+ }
+ // Quis Custodiet Ipsos Custodes.
+ EXPECT_EQ(stream->id(), stream_id_manager_->max_allowed_outgoing_stream_id());
+ // Create another, it should fail. Should also send a STREAM_ID_BLOCKED
+ // control frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ stream = GetParam() ? session_->CreateOutgoingBidirectionalStream()
+ : session_->CreateOutgoingUnidirectionalStream();
+ EXPECT_EQ(nullptr, stream);
+}
+
+// Test that a server will reject a MAX_STREAM_ID that specifies a
+// client-initiated stream ID.
+TEST_P(QuicStreamIdManagerTestServer, RejectClientMaxStreamId) {
+ QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id();
+
+ // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the
+ // current MAX.
+ id += (kV99StreamIdIncrement * 2);
+
+ // Turn it into a client-initiated ID (even).
+ id &= ~0x1;
+ EXPECT_TRUE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id));
+
+ // Generate a MAX_STREAM_ID frame and process it; the connection should close.
+ QuicMaxStreamIdFrame frame(0, id);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAM_ID_ERROR, _, _));
+ session_->OnMaxStreamIdFrame(frame);
+}
+
+// Test that a server will reject a STREAM_ID_BLOCKED that specifies a
+// server-initiated stream ID. STREAM_ID_BLOCKED from a client should specify an
+// even (client-initiated_ ID) generate one with an odd ID and check that the
+// connection is closed.
+TEST_P(QuicStreamIdManagerTestServer, RejectClientStreamIdBlocked) {
+ QuicStreamId id = stream_id_manager_->max_allowed_outgoing_stream_id();
+
+ // Ensure that the ID that will be in the MAX_STREAM_ID is larger than the
+ // current MAX.
+ id += (kV99StreamIdIncrement * 2);
+
+ // Make the ID odd, so it looks like the client is trying to specify a
+ // server-initiated ID.
+ id |= 0x1;
+ EXPECT_FALSE(QuicUtils::IsClientInitiatedStreamId(QUIC_VERSION_99, id));
+
+ // Generate a STREAM_ID_BLOCKED frame and process it; the connection should
+ // close.
+ QuicStreamIdBlockedFrame frame(0, id);
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_STREAM_ID_BLOCKED_ERROR, _, _));
+ session_->OnStreamIdBlockedFrame(frame);
+}
+
+// Check that the parameters used by the stream ID manager are properly
+// initialized
+TEST_P(QuicStreamIdManagerTestServer, StreamIdManagerServerInitialization) {
+ // These fields are inited via the QuicSession constructor to default
+ // values defined as a constant.
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ stream_id_manager_->max_allowed_incoming_streams());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ stream_id_manager_->max_allowed_outgoing_streams());
+
+ // The window for advertising updates to the MAX STREAM ID is half the number
+ // of stream allowed.
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection / kMaxStreamIdWindowDivisor,
+ stream_id_manager_->max_stream_id_window());
+
+ // This test runs as a server, so it initiates (that is to say, outgoing)
+ // even-numbered stream IDs. The -1 in the calculation is because the value
+ // being tested is the maximum allowed stream ID, not the first unallowed
+ // stream id.
+ const QuicStreamId kExpectedMaxOutgoingStreamId =
+ (GetParam() ? session_->next_outgoing_bidirectional_stream_id()
+ : session_->next_outgoing_unidirectional_stream_id()) +
+ ((kDefaultMaxStreamsPerConnection - 1) * kV99StreamIdIncrement);
+ EXPECT_EQ(kExpectedMaxOutgoingStreamId,
+ stream_id_manager_->max_allowed_outgoing_stream_id());
+
+ // Same for IDs of incoming streams... But they are client initiated, so are
+ // even.
+ const QuicStreamId kExpectedMaxIncomingStreamId =
+ GetParam() ? GetNthClientInitiatedBidirectionalId(
+ kDefaultMaxStreamsPerConnection - 1)
+ : GetNthClientInitiatedUnidirectionalId(
+ kDefaultMaxStreamsPerConnection - 1);
+ EXPECT_EQ(kExpectedMaxIncomingStreamId,
+ stream_id_manager_->actual_max_allowed_incoming_stream_id());
+ EXPECT_EQ(kExpectedMaxIncomingStreamId,
+ stream_id_manager_->advertised_max_allowed_incoming_stream_id());
+}
+
+TEST_P(QuicStreamIdManagerTestServer, AvailableStreams) {
+ stream_id_manager_->MaybeIncreaseLargestPeerStreamId(
+ GetParam() ? GetNthClientInitiatedBidirectionalId(3)
+ : GetNthClientInitiatedUnidirectionalId(3));
+ EXPECT_TRUE(stream_id_manager_->IsAvailableStream(
+ GetParam() ? GetNthClientInitiatedBidirectionalId(1)
+ : GetNthClientInitiatedUnidirectionalId(1)));
+ EXPECT_TRUE(stream_id_manager_->IsAvailableStream(
+ GetParam() ? GetNthClientInitiatedBidirectionalId(2)
+ : GetNthClientInitiatedUnidirectionalId(2)));
+}
+
+// Tests that if MaybeIncreaseLargestPeerStreamId is given an extremely
+// large stream ID (larger than the limit) it is rejected.
+// This is a regression for Chromium bugs 909987 and 910040
+TEST_P(QuicStreamIdManagerTestServer, ExtremeMaybeIncreaseLargestPeerStreamId) {
+ QuicStreamId too_big_stream_id =
+ stream_id_manager_->actual_max_allowed_incoming_stream_id() +
+ kV99StreamIdIncrement * 20;
+
+ std::string error_details =
+ GetParam() ? "Stream id 480 above 400" : "Stream id 478 above 398";
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _));
+
+ EXPECT_FALSE(
+ stream_id_manager_->MaybeIncreaseLargestPeerStreamId(too_big_stream_id));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..ead192dd6fe
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.cc
@@ -0,0 +1,308 @@
+// 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.
+
+#include <algorithm>
+
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+struct CompareOffset {
+ bool operator()(const BufferedSlice& slice, QuicStreamOffset offset) const {
+ return slice.offset + slice.slice.length() < offset;
+ }
+};
+
+} // namespace
+
+BufferedSlice::BufferedSlice(QuicMemSlice mem_slice, QuicStreamOffset offset)
+ : slice(std::move(mem_slice)), offset(offset) {}
+
+BufferedSlice::BufferedSlice(BufferedSlice&& other) = default;
+
+BufferedSlice& BufferedSlice::operator=(BufferedSlice&& other) = default;
+
+BufferedSlice::~BufferedSlice() {}
+
+bool StreamPendingRetransmission::operator==(
+ const StreamPendingRetransmission& other) const {
+ return offset == other.offset && length == other.length;
+}
+
+QuicStreamSendBuffer::QuicStreamSendBuffer(QuicBufferAllocator* allocator)
+ : stream_offset_(0),
+ allocator_(allocator),
+ stream_bytes_written_(0),
+ stream_bytes_outstanding_(0),
+ write_index_(-1) {}
+
+QuicStreamSendBuffer::~QuicStreamSendBuffer() {}
+
+void QuicStreamSendBuffer::SaveStreamData(const struct iovec* iov,
+ int iov_count,
+ size_t iov_offset,
+ QuicByteCount data_length) {
+ DCHECK_LT(0u, data_length);
+ // Latch the maximum data slice size.
+ const QuicByteCount max_data_slice_size =
+ GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size);
+ while (data_length > 0) {
+ size_t slice_len = std::min(data_length, max_data_slice_size);
+ QuicMemSlice slice(allocator_, slice_len);
+ QuicUtils::CopyToBuffer(iov, iov_count, iov_offset, slice_len,
+ const_cast<char*>(slice.data()));
+ SaveMemSlice(std::move(slice));
+ data_length -= slice_len;
+ iov_offset += slice_len;
+ }
+}
+
+void QuicStreamSendBuffer::SaveMemSlice(QuicMemSlice slice) {
+ QUIC_DVLOG(2) << "Save slice offset " << stream_offset_ << " length "
+ << slice.length();
+ if (slice.empty()) {
+ QUIC_BUG << "Try to save empty MemSlice to send buffer.";
+ return;
+ }
+ size_t length = slice.length();
+ buffered_slices_.emplace_back(std::move(slice), stream_offset_);
+ if (write_index_ == -1) {
+ write_index_ = buffered_slices_.size() - 1;
+ }
+ stream_offset_ += length;
+}
+
+QuicByteCount QuicStreamSendBuffer::SaveMemSliceSpan(QuicMemSliceSpan span) {
+ return span.ConsumeAll(
+ [&](QuicMemSlice slice) { SaveMemSlice(std::move(slice)); });
+}
+
+void QuicStreamSendBuffer::OnStreamDataConsumed(size_t bytes_consumed) {
+ stream_bytes_written_ += bytes_consumed;
+ stream_bytes_outstanding_ += bytes_consumed;
+}
+
+bool QuicStreamSendBuffer::WriteStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ bool write_index_hit = false;
+ QuicDeque<BufferedSlice>::iterator slice_it =
+ write_index_ == -1
+ ? buffered_slices_.begin()
+ // Assume with write_index, write mostly starts from indexed slice.
+ : buffered_slices_.begin() + write_index_;
+ if (write_index_ != -1) {
+ if (offset >= slice_it->offset + slice_it->slice.length()) {
+ QUIC_BUG << "Tried to write data out of sequence.";
+ return false;
+ }
+ // Determine if write actually happens at indexed slice.
+ if (offset >= slice_it->offset) {
+ write_index_hit = true;
+ } else {
+ // Write index missed, move iterator to the beginning.
+ slice_it = buffered_slices_.begin();
+ }
+ }
+
+ for (; slice_it != buffered_slices_.end(); ++slice_it) {
+ if (data_length == 0 || offset < slice_it->offset) {
+ break;
+ }
+ if (offset >= slice_it->offset + slice_it->slice.length()) {
+ continue;
+ }
+ QuicByteCount slice_offset = offset - slice_it->offset;
+ QuicByteCount available_bytes_in_slice =
+ slice_it->slice.length() - slice_offset;
+ QuicByteCount copy_length = std::min(data_length, available_bytes_in_slice);
+ if (!writer->WriteBytes(slice_it->slice.data() + slice_offset,
+ copy_length)) {
+ QUIC_BUG << "Writer fails to write.";
+ return false;
+ }
+ offset += copy_length;
+ data_length -= copy_length;
+
+ if (write_index_hit && copy_length == available_bytes_in_slice) {
+ // Finished writing all data in current slice, advance write index for
+ // next write.
+ ++write_index_;
+ }
+ }
+
+ if (write_index_hit &&
+ static_cast<size_t>(write_index_) == buffered_slices_.size()) {
+ // Already write to the end off buffer.
+ QUIC_DVLOG(2) << "Finish writing out all buffered data.";
+ write_index_ = -1;
+ }
+
+ return data_length == 0;
+}
+
+bool QuicStreamSendBuffer::OnStreamDataAcked(
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicByteCount* newly_acked_length) {
+ *newly_acked_length = 0;
+ if (data_length == 0) {
+ return true;
+ }
+ if (bytes_acked_.Empty() || offset >= bytes_acked_.rbegin()->max() ||
+ bytes_acked_.IsDisjoint(
+ QuicInterval<QuicStreamOffset>(offset, offset + data_length))) {
+ // Optimization for the typical case, when all data is newly acked.
+ if (stream_bytes_outstanding_ < data_length) {
+ return false;
+ }
+ bytes_acked_.Add(offset, offset + data_length);
+ *newly_acked_length = data_length;
+ stream_bytes_outstanding_ -= data_length;
+ pending_retransmissions_.Difference(offset, offset + data_length);
+ if (!FreeMemSlices(offset, offset + data_length)) {
+ return false;
+ }
+ CleanUpBufferedSlices();
+ return true;
+ }
+ // Exit if no new data gets acked.
+ if (bytes_acked_.Contains(offset, offset + data_length)) {
+ return true;
+ }
+ // Execute the slow path if newly acked data fill in existing holes.
+ QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+ newly_acked.Difference(bytes_acked_);
+ for (const auto& interval : newly_acked) {
+ *newly_acked_length += (interval.max() - interval.min());
+ }
+ if (stream_bytes_outstanding_ < *newly_acked_length) {
+ return false;
+ }
+ stream_bytes_outstanding_ -= *newly_acked_length;
+ bytes_acked_.Add(offset, offset + data_length);
+ pending_retransmissions_.Difference(offset, offset + data_length);
+ if (newly_acked.Empty()) {
+ return true;
+ }
+ if (!FreeMemSlices(newly_acked.begin()->min(), newly_acked.rbegin()->max())) {
+ return false;
+ }
+ CleanUpBufferedSlices();
+ return true;
+}
+
+void QuicStreamSendBuffer::OnStreamDataLost(QuicStreamOffset offset,
+ QuicByteCount data_length) {
+ if (data_length == 0) {
+ return;
+ }
+ QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length);
+ bytes_lost.Difference(bytes_acked_);
+ if (bytes_lost.Empty()) {
+ return;
+ }
+ for (const auto& lost : bytes_lost) {
+ pending_retransmissions_.Add(lost.min(), lost.max());
+ }
+}
+
+void QuicStreamSendBuffer::OnStreamDataRetransmitted(
+ QuicStreamOffset offset,
+ QuicByteCount data_length) {
+ if (data_length == 0) {
+ return;
+ }
+ pending_retransmissions_.Difference(offset, offset + data_length);
+}
+
+bool QuicStreamSendBuffer::HasPendingRetransmission() const {
+ return !pending_retransmissions_.Empty();
+}
+
+StreamPendingRetransmission QuicStreamSendBuffer::NextPendingRetransmission()
+ const {
+ if (HasPendingRetransmission()) {
+ const auto pending = pending_retransmissions_.begin();
+ return {pending->min(), pending->max() - pending->min()};
+ }
+ QUIC_BUG << "NextPendingRetransmission is called unexpected with no "
+ "pending retransmissions.";
+ return {0, 0};
+}
+
+bool QuicStreamSendBuffer::FreeMemSlices(QuicStreamOffset start,
+ QuicStreamOffset end) {
+ auto it = buffered_slices_.begin();
+ // Find it, such that buffered_slices_[it - 1].end < start <=
+ // buffered_slices_[it].end.
+ if (it == buffered_slices_.end() || it->slice.empty()) {
+ QUIC_BUG << "Trying to ack stream data [" << start << ", " << end << "), "
+ << (it == buffered_slices_.end()
+ ? "and there is no outstanding data."
+ : "and the first slice is empty.");
+ return false;
+ }
+ if (start >= it->offset + it->slice.length() || start < it->offset) {
+ // Slow path that not the earliest outstanding data gets acked.
+ it = std::lower_bound(buffered_slices_.begin(), buffered_slices_.end(),
+ start, CompareOffset());
+ }
+ if (it == buffered_slices_.end() || it->slice.empty()) {
+ QUIC_BUG << "Offset " << start
+ << " does not exist or it has already been acked.";
+ return false;
+ }
+ for (; it != buffered_slices_.end(); ++it) {
+ if (it->offset >= end) {
+ break;
+ }
+ if (!it->slice.empty() &&
+ bytes_acked_.Contains(it->offset, it->offset + it->slice.length())) {
+ it->slice.Reset();
+ }
+ }
+ return true;
+}
+
+void QuicStreamSendBuffer::CleanUpBufferedSlices() {
+ while (!buffered_slices_.empty() && buffered_slices_.front().slice.empty()) {
+ // Remove data which stops waiting for acks. Please note, mem slices can
+ // be released out of order, but send buffer is cleaned up in order.
+ QUIC_BUG_IF(write_index_ == 0)
+ << "Fail to advance current_write_slice_. It points to the slice "
+ "whose data has all be written and ACK'ed or ignored. "
+ "current_write_slice_ offset "
+ << buffered_slices_[write_index_].offset << " length "
+ << buffered_slices_[write_index_].slice.length();
+ if (write_index_ > 0) {
+ // If write index is pointing to any slice, reduce the index as the
+ // slices are all shifted to the left by one.
+ --write_index_;
+ }
+ buffered_slices_.pop_front();
+ }
+}
+
+bool QuicStreamSendBuffer::IsStreamDataOutstanding(
+ QuicStreamOffset offset,
+ QuicByteCount data_length) const {
+ return data_length > 0 &&
+ !bytes_acked_.Contains(offset, offset + data_length);
+}
+
+size_t QuicStreamSendBuffer::size() const {
+ return buffered_slices_.size();
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..74e9d0d5ee2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h
@@ -0,0 +1,170 @@
+// 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_CORE_QUIC_STREAM_SEND_BUFFER_H_
+#define QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_H_
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+
+namespace quic {
+
+namespace test {
+class QuicStreamSendBufferPeer;
+class QuicStreamPeer;
+} // namespace test
+
+class QuicDataWriter;
+
+// BufferedSlice comprises information of a piece of stream data stored in
+// contiguous memory space. Please note, BufferedSlice is constructed when
+// stream data is saved in send buffer and is removed when stream data is fully
+// acked. It is move-only.
+struct BufferedSlice {
+ BufferedSlice(QuicMemSlice mem_slice, QuicStreamOffset offset);
+ BufferedSlice(BufferedSlice&& other);
+ BufferedSlice& operator=(BufferedSlice&& other);
+
+ BufferedSlice(const BufferedSlice& other) = delete;
+ BufferedSlice& operator=(const BufferedSlice& other) = delete;
+ ~BufferedSlice();
+
+ // Stream data of this data slice.
+ QuicMemSlice slice;
+ // Location of this data slice in the stream.
+ QuicStreamOffset offset;
+};
+
+struct StreamPendingRetransmission {
+ StreamPendingRetransmission(QuicStreamOffset offset, QuicByteCount length)
+ : offset(offset), length(length) {}
+
+ // Starting offset of this pending retransmission.
+ QuicStreamOffset offset;
+ // Length of this pending retransmission.
+ QuicByteCount length;
+
+ QUIC_EXPORT_PRIVATE bool operator==(
+ const StreamPendingRetransmission& other) const;
+};
+
+// QuicStreamSendBuffer contains a list of QuicStreamDataSlices. New data slices
+// are added to the tail of the list. Data slices are removed from the head of
+// the list when they get fully acked. Stream data can be retrieved and acked
+// across slice boundaries.
+class QUIC_EXPORT_PRIVATE QuicStreamSendBuffer {
+ public:
+ explicit QuicStreamSendBuffer(QuicBufferAllocator* allocator);
+ QuicStreamSendBuffer(const QuicStreamSendBuffer& other) = delete;
+ QuicStreamSendBuffer(QuicStreamSendBuffer&& other) = default;
+ ~QuicStreamSendBuffer();
+
+ // Save |data_length| of data starts at |iov_offset| in |iov| to send buffer.
+ void SaveStreamData(const struct iovec* iov,
+ int iov_count,
+ size_t iov_offset,
+ QuicByteCount data_length);
+
+ // Save |slice| to send buffer.
+ void SaveMemSlice(QuicMemSlice slice);
+
+ // Save all slices in |span| to send buffer. Return total bytes saved.
+ QuicByteCount SaveMemSliceSpan(QuicMemSliceSpan span);
+
+ // Called when |bytes_consumed| bytes has been consumed by the stream.
+ void OnStreamDataConsumed(size_t bytes_consumed);
+
+ // Write |data_length| of data starts at |offset|.
+ bool WriteStreamData(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer);
+
+ // Called when data [offset, offset + data_length) is acked or removed as
+ // stream is canceled. Removes fully acked data slice from send buffer. Set
+ // |newly_acked_length|. Returns false if trying to ack unsent data.
+ bool OnStreamDataAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicByteCount* newly_acked_length);
+
+ // Called when data [offset, offset + data_length) is considered as lost.
+ void OnStreamDataLost(QuicStreamOffset offset, QuicByteCount data_length);
+
+ // Called when data [offset, offset + length) was retransmitted.
+ void OnStreamDataRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length);
+
+ // Returns true if there is pending retransmissions.
+ bool HasPendingRetransmission() const;
+
+ // Returns next pending retransmissions.
+ StreamPendingRetransmission NextPendingRetransmission() const;
+
+ // Returns true if data [offset, offset + data_length) is outstanding and
+ // waiting to be acked. Returns false otherwise.
+ bool IsStreamDataOutstanding(QuicStreamOffset offset,
+ QuicByteCount data_length) const;
+
+ // Number of data slices in send buffer.
+ size_t size() const;
+
+ QuicStreamOffset stream_offset() const { return stream_offset_; }
+
+ uint64_t stream_bytes_written() const { return stream_bytes_written_; }
+
+ uint64_t stream_bytes_outstanding() const {
+ return stream_bytes_outstanding_;
+ }
+
+ const QuicIntervalSet<QuicStreamOffset>& bytes_acked() const {
+ return bytes_acked_;
+ }
+
+ const QuicIntervalSet<QuicStreamOffset>& pending_retransmissions() const {
+ return pending_retransmissions_;
+ }
+
+ private:
+ friend class test::QuicStreamSendBufferPeer;
+ friend class test::QuicStreamPeer;
+
+ // Called when data within offset [start, end) gets acked. Frees fully
+ // acked buffered slices if any. Returns false if the corresponding data does
+ // not exist or has been acked.
+ bool FreeMemSlices(QuicStreamOffset start, QuicStreamOffset end);
+
+ // Cleanup empty slices in order from buffered_slices_.
+ void CleanUpBufferedSlices();
+
+ QuicDeque<BufferedSlice> buffered_slices_;
+
+ // Offset of next inserted byte.
+ QuicStreamOffset stream_offset_;
+
+ QuicBufferAllocator* allocator_;
+
+ // Bytes that have been consumed by the stream.
+ uint64_t stream_bytes_written_;
+
+ // Bytes that have been consumed and are waiting to be acked.
+ uint64_t stream_bytes_outstanding_;
+
+ // Offsets of data that has been acked.
+ QuicIntervalSet<QuicStreamOffset> bytes_acked_;
+
+ // Data considered as lost and needs to be retransmitted.
+ QuicIntervalSet<QuicStreamOffset> pending_retransmissions_;
+
+ // Index of slice which contains data waiting to be written for the first
+ // time. -1 if send buffer is empty or all data has been written.
+ int32_t write_index_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEND_BUFFER_H_
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
new file mode 100644
index 00000000000..57e2bd1802a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_send_buffer_test.cc
@@ -0,0 +1,325 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+struct iovec MakeIovec(QuicStringPiece data) {
+ struct iovec iov = {const_cast<char*>(data.data()),
+ static_cast<size_t>(data.size())};
+ return iov;
+}
+
+class QuicStreamSendBufferTest : public QuicTest {
+ public:
+ QuicStreamSendBufferTest() : send_buffer_(&allocator_) {
+ EXPECT_EQ(0u, send_buffer_.size());
+ EXPECT_EQ(0u, send_buffer_.stream_bytes_written());
+ EXPECT_EQ(0u, send_buffer_.stream_bytes_outstanding());
+ std::string data1(1536, 'a');
+ std::string data2 = std::string(256, 'b') + std::string(256, 'c');
+ struct iovec iov[2];
+ iov[0] = MakeIovec(QuicStringPiece(data1));
+ iov[1] = MakeIovec(QuicStringPiece(data2));
+
+ QuicMemSlice slice1(&allocator_, 1024);
+ memset(const_cast<char*>(slice1.data()), 'c', 1024);
+ QuicMemSlice slice2(&allocator_, 768);
+ memset(const_cast<char*>(slice2.data()), 'd', 768);
+
+ // Index starts from not pointing to any slice.
+ EXPECT_EQ(nullptr,
+ QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_));
+
+ // Save all data.
+ SetQuicFlag(&FLAGS_quic_send_buffer_max_data_slice_size, 1024);
+ send_buffer_.SaveStreamData(iov, 2, 0, 2048);
+ send_buffer_.SaveMemSlice(std::move(slice1));
+ EXPECT_TRUE(slice1.empty());
+ send_buffer_.SaveMemSlice(std::move(slice2));
+ EXPECT_TRUE(slice2.empty());
+
+ EXPECT_EQ(4u, send_buffer_.size());
+ // At this point, the whole buffer looks like:
+ // | a * 1536 |b * 256| c * 1280 | d * 768 |
+ // | slice1 | slice2 | slice3 | slice4 |
+ }
+
+ void WriteAllData() {
+ // Write all data.
+ char buf[4000];
+ QuicDataWriter writer(4000, buf, HOST_BYTE_ORDER);
+ send_buffer_.WriteStreamData(0, 3840u, &writer);
+
+ send_buffer_.OnStreamDataConsumed(3840u);
+ EXPECT_EQ(3840u, send_buffer_.stream_bytes_written());
+ EXPECT_EQ(3840u, send_buffer_.stream_bytes_outstanding());
+ }
+
+ SimpleBufferAllocator allocator_;
+ QuicStreamSendBuffer send_buffer_;
+};
+
+TEST_F(QuicStreamSendBufferTest, CopyDataToBuffer) {
+ char buf[4000];
+ QuicDataWriter writer(4000, buf, HOST_BYTE_ORDER);
+ std::string copy1(1024, 'a');
+ std::string copy2 =
+ std::string(512, 'a') + std::string(256, 'b') + std::string(256, 'c');
+ std::string copy3(1024, 'c');
+ std::string copy4(768, 'd');
+
+ ASSERT_TRUE(send_buffer_.WriteStreamData(0, 1024, &writer));
+ EXPECT_EQ(copy1, QuicStringPiece(buf, 1024));
+ ASSERT_TRUE(send_buffer_.WriteStreamData(1024, 1024, &writer));
+ EXPECT_EQ(copy2, QuicStringPiece(buf + 1024, 1024));
+ ASSERT_TRUE(send_buffer_.WriteStreamData(2048, 1024, &writer));
+ EXPECT_EQ(copy3, QuicStringPiece(buf + 2048, 1024));
+ ASSERT_TRUE(send_buffer_.WriteStreamData(3072, 768, &writer));
+ EXPECT_EQ(copy4, QuicStringPiece(buf + 3072, 768));
+
+ // Test data piece across boundries.
+ QuicDataWriter writer2(4000, buf, HOST_BYTE_ORDER);
+ std::string copy5 =
+ std::string(536, 'a') + std::string(256, 'b') + std::string(232, 'c');
+ ASSERT_TRUE(send_buffer_.WriteStreamData(1000, 1024, &writer2));
+ EXPECT_EQ(copy5, QuicStringPiece(buf, 1024));
+ ASSERT_TRUE(send_buffer_.WriteStreamData(2500, 1024, &writer2));
+ std::string copy6 = std::string(572, 'c') + std::string(452, 'd');
+ EXPECT_EQ(copy6, QuicStringPiece(buf + 1024, 1024));
+
+ // Invalid data copy.
+ QuicDataWriter writer3(4000, buf, HOST_BYTE_ORDER);
+ EXPECT_FALSE(send_buffer_.WriteStreamData(3000, 1024, &writer3));
+ EXPECT_QUIC_BUG(send_buffer_.WriteStreamData(0, 4000, &writer3),
+ "Writer fails to write.");
+
+ send_buffer_.OnStreamDataConsumed(3840);
+ EXPECT_EQ(3840u, send_buffer_.stream_bytes_written());
+ EXPECT_EQ(3840u, send_buffer_.stream_bytes_outstanding());
+}
+
+TEST_F(QuicStreamSendBufferTest, RemoveStreamFrame) {
+ WriteAllData();
+
+ QuicByteCount newly_acked_length;
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1024, 1024, &newly_acked_length));
+ EXPECT_EQ(1024u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2048, 1024, &newly_acked_length));
+ EXPECT_EQ(1024u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1024, &newly_acked_length));
+ EXPECT_EQ(1024u, newly_acked_length);
+
+ // Send buffer is cleaned up in order.
+ EXPECT_EQ(1u, send_buffer_.size());
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(3072, 768, &newly_acked_length));
+ EXPECT_EQ(768u, newly_acked_length);
+ EXPECT_EQ(0u, send_buffer_.size());
+}
+
+TEST_F(QuicStreamSendBufferTest, RemoveStreamFrameAcrossBoundries) {
+ WriteAllData();
+
+ QuicByteCount newly_acked_length;
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2024, 576, &newly_acked_length));
+ EXPECT_EQ(576u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1000, &newly_acked_length));
+ EXPECT_EQ(1000u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1000, 1024, &newly_acked_length));
+ EXPECT_EQ(1024u, newly_acked_length);
+ // Send buffer is cleaned up in order.
+ EXPECT_EQ(2u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2600, 1024, &newly_acked_length));
+ EXPECT_EQ(1024u, newly_acked_length);
+ EXPECT_EQ(1u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(3624, 216, &newly_acked_length));
+ EXPECT_EQ(216u, newly_acked_length);
+ EXPECT_EQ(0u, send_buffer_.size());
+}
+
+TEST_F(QuicStreamSendBufferTest, AckStreamDataMultipleTimes) {
+ WriteAllData();
+ QuicByteCount newly_acked_length;
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(100, 1500, &newly_acked_length));
+ EXPECT_EQ(1500u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2000, 500, &newly_acked_length));
+ EXPECT_EQ(500u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 2600, &newly_acked_length));
+ EXPECT_EQ(600u, newly_acked_length);
+ // Send buffer is cleaned up in order.
+ EXPECT_EQ(2u, send_buffer_.size());
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2200, 1640, &newly_acked_length));
+ EXPECT_EQ(1240u, newly_acked_length);
+ EXPECT_EQ(0u, send_buffer_.size());
+
+ EXPECT_FALSE(send_buffer_.OnStreamDataAcked(4000, 100, &newly_acked_length));
+}
+
+TEST_F(QuicStreamSendBufferTest, AckStreamDataOutOfOrder) {
+ WriteAllData();
+ QuicByteCount newly_acked_length;
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(500, 1000, &newly_acked_length));
+ EXPECT_EQ(1000u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+ EXPECT_EQ(3840u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_));
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(1200, 1000, &newly_acked_length));
+ EXPECT_EQ(700u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+ // Slice 2 gets fully acked.
+ EXPECT_EQ(2816u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_));
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(2000, 1840, &newly_acked_length));
+ EXPECT_EQ(1640u, newly_acked_length);
+ EXPECT_EQ(4u, send_buffer_.size());
+ // Slices 3 and 4 get fully acked.
+ EXPECT_EQ(1024u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_));
+
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1000, &newly_acked_length));
+ EXPECT_EQ(500u, newly_acked_length);
+ EXPECT_EQ(0u, send_buffer_.size());
+ EXPECT_EQ(0u, QuicStreamSendBufferPeer::TotalLength(&send_buffer_));
+}
+
+TEST_F(QuicStreamSendBufferTest, PendingRetransmission) {
+ WriteAllData();
+ EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(0, 3840));
+ EXPECT_FALSE(send_buffer_.HasPendingRetransmission());
+ // Lost data [0, 1200).
+ send_buffer_.OnStreamDataLost(0, 1200);
+ // Lost data [1500, 2000).
+ send_buffer_.OnStreamDataLost(1500, 500);
+ EXPECT_TRUE(send_buffer_.HasPendingRetransmission());
+
+ EXPECT_EQ(StreamPendingRetransmission(0, 1200),
+ send_buffer_.NextPendingRetransmission());
+ // Retransmit data [0, 500).
+ send_buffer_.OnStreamDataRetransmitted(0, 500);
+ EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(0, 500));
+ EXPECT_EQ(StreamPendingRetransmission(500, 700),
+ send_buffer_.NextPendingRetransmission());
+ // Ack data [500, 1200).
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(500, 700, &newly_acked_length));
+ EXPECT_FALSE(send_buffer_.IsStreamDataOutstanding(500, 700));
+ EXPECT_TRUE(send_buffer_.HasPendingRetransmission());
+ EXPECT_EQ(StreamPendingRetransmission(1500, 500),
+ send_buffer_.NextPendingRetransmission());
+ // Retransmit data [1500, 2000).
+ send_buffer_.OnStreamDataRetransmitted(1500, 500);
+ EXPECT_FALSE(send_buffer_.HasPendingRetransmission());
+
+ // Lost [200, 800).
+ send_buffer_.OnStreamDataLost(200, 600);
+ EXPECT_TRUE(send_buffer_.HasPendingRetransmission());
+ // Verify [200, 500) is considered as lost, as [500, 800) has been acked.
+ EXPECT_EQ(StreamPendingRetransmission(200, 300),
+ send_buffer_.NextPendingRetransmission());
+
+ // Verify 0 length data is not outstanding.
+ EXPECT_FALSE(send_buffer_.IsStreamDataOutstanding(100, 0));
+ // Verify partially acked data is outstanding.
+ EXPECT_TRUE(send_buffer_.IsStreamDataOutstanding(400, 800));
+}
+
+TEST_F(QuicStreamSendBufferTest, CurrentWriteIndex) {
+ char buf[4000];
+ QuicDataWriter writer(4000, buf, HOST_BYTE_ORDER);
+ // With data buffered, index points to the 1st slice of data.
+ EXPECT_EQ(0u,
+ QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset);
+ ASSERT_TRUE(send_buffer_.WriteStreamData(0, 1024, &writer));
+ // Wrote all data on 1st slice, index points to next slice.
+ EXPECT_EQ(1024u,
+ QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset);
+ ASSERT_TRUE(send_buffer_.WriteStreamData(1024, 512, &writer));
+ // Last write didn't finish a whole slice. Index remains.
+ EXPECT_EQ(1024u,
+ QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset);
+ send_buffer_.OnStreamDataConsumed(1024);
+
+ // If data in 1st slice gets ACK'ed, it shouldn't change the indexed slice
+ QuicByteCount newly_acked_length;
+ EXPECT_TRUE(send_buffer_.OnStreamDataAcked(0, 1024, &newly_acked_length));
+ EXPECT_EQ(1024u,
+ QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset);
+
+ ASSERT_TRUE(
+ send_buffer_.WriteStreamData(1024 + 512, 3840 - 1024 - 512, &writer));
+ // After writing all buffered data, index become invalid again.
+ EXPECT_EQ(nullptr,
+ QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_));
+ QuicMemSlice slice(&allocator_, 60);
+ memset(const_cast<char*>(slice.data()), 'e', 60);
+ send_buffer_.SaveMemSlice(std::move(slice));
+ // With new data, index points to the new data.
+ EXPECT_EQ(3840u,
+ QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer_)->offset);
+}
+
+TEST_F(QuicStreamSendBufferTest, SaveMemSliceSpan) {
+ SimpleBufferAllocator allocator;
+ QuicStreamSendBuffer send_buffer(&allocator);
+
+ char data[1024];
+ std::vector<std::pair<char*, size_t>> buffers;
+ for (size_t i = 0; i < 10; ++i) {
+ buffers.push_back(std::make_pair(data, 1024));
+ }
+ QuicTestMemSliceVector vector(buffers);
+
+ EXPECT_EQ(10 * 1024u, send_buffer.SaveMemSliceSpan(vector.span()));
+ EXPECT_EQ(10u, send_buffer.size());
+}
+
+TEST_F(QuicStreamSendBufferTest, SaveEmptyMemSliceSpan) {
+ SimpleBufferAllocator allocator;
+ QuicStreamSendBuffer send_buffer(&allocator);
+
+ char data[1024];
+ std::vector<std::pair<char*, size_t>> buffers;
+ for (size_t i = 0; i < 10; ++i) {
+ buffers.push_back(std::make_pair(data, 1024));
+ }
+ buffers.push_back(std::make_pair(nullptr, 0));
+ QuicTestMemSliceVector vector(buffers);
+
+ EXPECT_EQ(10 * 1024u, send_buffer.SaveMemSliceSpan(vector.span()));
+ // Verify the empty slice does not get saved.
+ EXPECT_EQ(10u, send_buffer.size());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc
new file mode 100644
index 00000000000..9832fc98ef0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.cc
@@ -0,0 +1,274 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QuicStreamSequencer::QuicStreamSequencer(StreamInterface* quic_stream)
+ : stream_(quic_stream),
+ buffered_frames_(kStreamReceiveWindowLimit),
+ close_offset_(std::numeric_limits<QuicStreamOffset>::max()),
+ blocked_(false),
+ num_frames_received_(0),
+ num_duplicate_frames_received_(0),
+ ignore_read_data_(false),
+ level_triggered_(false),
+ stop_reading_when_level_triggered_(
+ GetQuicReloadableFlag(quic_stop_reading_when_level_triggered)) {}
+
+QuicStreamSequencer::~QuicStreamSequencer() {}
+
+void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
+ ++num_frames_received_;
+ const QuicStreamOffset byte_offset = frame.offset;
+ const size_t data_len = frame.data_length;
+
+ if (frame.fin) {
+ CloseStreamAtOffset(frame.offset + data_len);
+ if (data_len == 0) {
+ return;
+ }
+ }
+ OnFrameData(byte_offset, data_len, frame.data_buffer);
+}
+
+void QuicStreamSequencer::OnCryptoFrame(const QuicCryptoFrame& frame) {
+ ++num_frames_received_;
+ OnFrameData(frame.offset, frame.data_length, frame.data_buffer);
+}
+
+void QuicStreamSequencer::OnFrameData(QuicStreamOffset byte_offset,
+ size_t data_len,
+ const char* data_buffer) {
+ const size_t previous_readable_bytes = buffered_frames_.ReadableBytes();
+ size_t bytes_written;
+ std::string error_details;
+ QuicErrorCode result = buffered_frames_.OnStreamData(
+ byte_offset, QuicStringPiece(data_buffer, data_len), &bytes_written,
+ &error_details);
+ if (result != QUIC_NO_ERROR) {
+ std::string details = QuicStrCat(
+ "Stream ", stream_->id(), ": ", QuicErrorCodeToString(result), ": ",
+ error_details,
+ "\nPeer Address: ", stream_->PeerAddressOfLatestPacket().ToString());
+ QUIC_LOG_FIRST_N(WARNING, 50) << QuicErrorCodeToString(result);
+ QUIC_LOG_FIRST_N(WARNING, 50) << details;
+ stream_->CloseConnectionWithDetails(result, details);
+ return;
+ }
+
+ if (bytes_written == 0) {
+ ++num_duplicate_frames_received_;
+ // Silently ignore duplicates.
+ return;
+ }
+
+ if (blocked_) {
+ return;
+ }
+
+ if (level_triggered_) {
+ if (buffered_frames_.ReadableBytes() > previous_readable_bytes) {
+ // Readable bytes has changed, let stream decide if to inform application
+ // or not.
+ if (stop_reading_when_level_triggered_ && ignore_read_data_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_stop_reading_when_level_triggered);
+ FlushBufferedFrames();
+ } else {
+ stream_->OnDataAvailable();
+ }
+ }
+ return;
+ }
+ const bool stream_unblocked =
+ previous_readable_bytes == 0 && buffered_frames_.ReadableBytes() > 0;
+ if (stream_unblocked) {
+ if (ignore_read_data_) {
+ FlushBufferedFrames();
+ } else {
+ stream_->OnDataAvailable();
+ }
+ }
+}
+
+void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) {
+ const QuicStreamOffset kMaxOffset =
+ std::numeric_limits<QuicStreamOffset>::max();
+
+ // If there is a scheduled close, the new offset should match it.
+ if (close_offset_ != kMaxOffset && offset != close_offset_) {
+ stream_->Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS);
+ return;
+ }
+
+ close_offset_ = offset;
+
+ MaybeCloseStream();
+}
+
+bool QuicStreamSequencer::MaybeCloseStream() {
+ if (blocked_ || !IsClosed()) {
+ return false;
+ }
+
+ QUIC_DVLOG(1) << "Passing up termination, as we've processed "
+ << buffered_frames_.BytesConsumed() << " of " << close_offset_
+ << " bytes.";
+ // This will cause the stream to consume the FIN.
+ // Technically it's an error if |num_bytes_consumed| isn't exactly
+ // equal to |close_offset|, but error handling seems silly at this point.
+ if (ignore_read_data_) {
+ // The sequencer is discarding stream data and must notify the stream on
+ // receipt of a FIN because the consumer won't.
+ stream_->OnFinRead();
+ } else {
+ stream_->OnDataAvailable();
+ }
+ buffered_frames_.Clear();
+ return true;
+}
+
+int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) const {
+ DCHECK(!blocked_);
+ return buffered_frames_.GetReadableRegions(iov, iov_len);
+}
+
+bool QuicStreamSequencer::GetReadableRegion(iovec* iov) const {
+ DCHECK(!blocked_);
+ return buffered_frames_.GetReadableRegion(iov);
+}
+
+bool QuicStreamSequencer::PrefetchNextRegion(iovec* iov) {
+ DCHECK(!blocked_);
+ return buffered_frames_.PrefetchNextRegion(iov);
+}
+
+void QuicStreamSequencer::Read(std::string* buffer) {
+ DCHECK(!blocked_);
+ buffer->resize(buffer->size() + ReadableBytes());
+ iovec iov;
+ iov.iov_len = ReadableBytes();
+ iov.iov_base = &(*buffer)[buffer->size() - iov.iov_len];
+ Readv(&iov, 1);
+}
+
+int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) {
+ DCHECK(!blocked_);
+ std::string error_details;
+ size_t bytes_read;
+ QuicErrorCode read_error =
+ buffered_frames_.Readv(iov, iov_len, &bytes_read, &error_details);
+ if (read_error != QUIC_NO_ERROR) {
+ std::string details =
+ QuicStrCat("Stream ", stream_->id(), ": ", error_details);
+ stream_->CloseConnectionWithDetails(read_error, details);
+ return static_cast<int>(bytes_read);
+ }
+
+ stream_->AddBytesConsumed(bytes_read);
+ return static_cast<int>(bytes_read);
+}
+
+bool QuicStreamSequencer::HasBytesToRead() const {
+ return buffered_frames_.HasBytesToRead();
+}
+
+size_t QuicStreamSequencer::ReadableBytes() const {
+ return buffered_frames_.ReadableBytes();
+}
+
+bool QuicStreamSequencer::IsClosed() const {
+ return buffered_frames_.BytesConsumed() >= close_offset_;
+}
+
+void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) {
+ DCHECK(!blocked_);
+ bool result = buffered_frames_.MarkConsumed(num_bytes_consumed);
+ if (!result) {
+ QUIC_BUG << "Invalid argument to MarkConsumed."
+ << " expect to consume: " << num_bytes_consumed
+ << ", but not enough bytes available. " << DebugString();
+ stream_->Reset(QUIC_ERROR_PROCESSING_STREAM);
+ return;
+ }
+ stream_->AddBytesConsumed(num_bytes_consumed);
+}
+
+void QuicStreamSequencer::SetBlockedUntilFlush() {
+ blocked_ = true;
+}
+
+void QuicStreamSequencer::SetUnblocked() {
+ blocked_ = false;
+ if (IsClosed() || HasBytesToRead()) {
+ stream_->OnDataAvailable();
+ }
+}
+
+void QuicStreamSequencer::StopReading() {
+ if (ignore_read_data_) {
+ return;
+ }
+ ignore_read_data_ = true;
+ FlushBufferedFrames();
+}
+
+void QuicStreamSequencer::ReleaseBuffer() {
+ buffered_frames_.ReleaseWholeBuffer();
+}
+
+void QuicStreamSequencer::ReleaseBufferIfEmpty() {
+ if (buffered_frames_.Empty()) {
+ buffered_frames_.ReleaseWholeBuffer();
+ }
+}
+
+void QuicStreamSequencer::FlushBufferedFrames() {
+ DCHECK(ignore_read_data_);
+ size_t bytes_flushed = buffered_frames_.FlushBufferedFrames();
+ QUIC_DVLOG(1) << "Flushing buffered data at offset "
+ << buffered_frames_.BytesConsumed() << " length "
+ << bytes_flushed << " for stream " << stream_->id();
+ stream_->AddBytesConsumed(bytes_flushed);
+ MaybeCloseStream();
+}
+
+size_t QuicStreamSequencer::NumBytesBuffered() const {
+ return buffered_frames_.BytesBuffered();
+}
+
+QuicStreamOffset QuicStreamSequencer::NumBytesConsumed() const {
+ return buffered_frames_.BytesConsumed();
+}
+
+const std::string QuicStreamSequencer::DebugString() const {
+ // clang-format off
+ return QuicStrCat("QuicStreamSequencer:",
+ "\n bytes buffered: ", NumBytesBuffered(),
+ "\n bytes consumed: ", NumBytesConsumed(),
+ "\n has bytes to read: ", HasBytesToRead() ? "true" : "false",
+ "\n frames received: ", num_frames_received(),
+ "\n close offset bytes: ", close_offset_,
+ "\n is closed: ", IsClosed() ? "true" : "false");
+ // clang-format on
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h
new file mode 100644
index 00000000000..7cecf4de70a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer.h
@@ -0,0 +1,211 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_H_
+#define QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_H_
+
+#include <cstddef>
+#include <map>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class QuicStreamSequencerPeer;
+} // namespace test
+
+// Buffers frames until we have something which can be passed
+// up to the next layer.
+class QUIC_EXPORT_PRIVATE QuicStreamSequencer {
+ public:
+ // Interface that thie Sequencer uses to communicate with the Stream.
+ class StreamInterface {
+ public:
+ virtual ~StreamInterface() = default;
+
+ // Called when new data is available to be read from the sequencer.
+ virtual void OnDataAvailable() = 0;
+ // Called when the end of the stream has been read.
+ virtual void OnFinRead() = 0;
+ // Called when bytes have been consumed from the sequencer.
+ virtual void AddBytesConsumed(QuicByteCount bytes) = 0;
+ // TODO(rch): Clean up this interface via OnUnrecoverableError and
+ // remove PeerAddressOfLatestPacket().
+ // Called when an error has occurred which should result in the stream
+ // being reset.
+ virtual void Reset(QuicRstStreamErrorCode error) = 0;
+ // Called when an error has occurred which should result in the connection
+ // being closed.
+ virtual void CloseConnectionWithDetails(QuicErrorCode error,
+ const std::string& details) = 0;
+
+ // Returns the stream id of this stream.
+ virtual QuicStreamId id() const = 0;
+ // Returns the peer address of the last packet received for this stream.
+ virtual const QuicSocketAddress& PeerAddressOfLatestPacket() const = 0;
+ };
+
+ explicit QuicStreamSequencer(StreamInterface* quic_stream);
+ QuicStreamSequencer(const QuicStreamSequencer&) = delete;
+ QuicStreamSequencer(QuicStreamSequencer&&) = default;
+ QuicStreamSequencer& operator=(const QuicStreamSequencer&) = delete;
+ virtual ~QuicStreamSequencer();
+
+ // If the frame is the next one we need in order to process in-order data,
+ // ProcessData will be immediately called on the stream until all buffered
+ // data is processed or the stream fails to consume data. Any unconsumed
+ // data will be buffered. If the frame is not the next in line, it will be
+ // buffered.
+ void OnStreamFrame(const QuicStreamFrame& frame);
+
+ // If the frame is the next one we need in order to process in-order data,
+ // ProcessData will be immediately called on the crypto stream until all
+ // buffered data is processed or the crypto stream fails to consume data. Any
+ // unconsumed data will be buffered. If the frame is not the next in line, it
+ // will be buffered.
+ void OnCryptoFrame(const QuicCryptoFrame& frame);
+
+ // Once data is buffered, it's up to the stream to read it when the stream
+ // can handle more data. The following three functions make that possible.
+
+ // Fills in up to iov_len iovecs with the next readable regions. Returns the
+ // number of iovs used. Non-destructive of the underlying data.
+ int GetReadableRegions(iovec* iov, size_t iov_len) const;
+
+ // Fills in one iovec with the next readable region. Returns false if there
+ // is no readable region available.
+ bool GetReadableRegion(iovec* iov) const;
+
+ // Fill in one iovec with the next unread region for the quic spdy stream.
+ // Returns false if no readable region is available.
+ bool PrefetchNextRegion(iovec* iov);
+
+ // Copies the data into the iov_len buffers provided. Returns the number of
+ // bytes read. Any buffered data no longer in use will be released.
+ // TODO(rch): remove this method and instead implement it as a helper method
+ // based on GetReadableRegions and MarkConsumed.
+ int Readv(const struct iovec* iov, size_t iov_len);
+
+ // Consumes |num_bytes| data. Used in conjunction with |GetReadableRegions|
+ // to do zero-copy reads.
+ void MarkConsumed(size_t num_bytes);
+
+ // Appends all of the readable data to |buffer| and marks all of the appended
+ // data as consumed.
+ void Read(std::string* buffer);
+
+ // Returns true if the sequncer has bytes available for reading.
+ bool HasBytesToRead() const;
+
+ // Number of bytes available to read.
+ size_t ReadableBytes() const;
+
+ // Returns true if the sequencer has delivered the fin.
+ bool IsClosed() const;
+
+ // Calls |OnDataAvailable| on |stream_| if there is buffered data that can
+ // be processed, and causes |OnDataAvailable| to be called as new data
+ // arrives.
+ void SetUnblocked();
+
+ // Blocks processing of frames until |SetUnblocked| is called.
+ void SetBlockedUntilFlush();
+
+ // Sets the sequencer to discard all incoming data itself and not call
+ // |stream_->OnDataAvailable()|. |stream_->OnFinRead()| will be called
+ // automatically when the FIN is consumed (which may be immediately).
+ void StopReading();
+
+ // Free the memory of underlying buffer.
+ void ReleaseBuffer();
+
+ // Free the memory of underlying buffer when no bytes remain in it.
+ void ReleaseBufferIfEmpty();
+
+ // Number of bytes in the buffer right now.
+ size_t NumBytesBuffered() const;
+
+ // Number of bytes has been consumed.
+ QuicStreamOffset NumBytesConsumed() const;
+
+ QuicStreamOffset close_offset() const { return close_offset_; }
+
+ int num_frames_received() const { return num_frames_received_; }
+
+ int num_duplicate_frames_received() const {
+ return num_duplicate_frames_received_;
+ }
+
+ bool ignore_read_data() const { return ignore_read_data_; }
+
+ void set_level_triggered(bool level_triggered) {
+ level_triggered_ = level_triggered;
+ }
+
+ bool level_triggered() const { return level_triggered_; }
+
+ void set_stream(StreamInterface* stream) { stream_ = stream; }
+
+ // Returns string describing internal state.
+ const std::string DebugString() const;
+
+ private:
+ friend class test::QuicStreamSequencerPeer;
+
+ // Deletes and records as consumed any buffered data that is now in-sequence.
+ // (To be called only after StopReading has been called.)
+ void FlushBufferedFrames();
+
+ // Wait until we've seen 'offset' bytes, and then terminate the stream.
+ void CloseStreamAtOffset(QuicStreamOffset offset);
+
+ // If we've received a FIN and have processed all remaining data, then inform
+ // the stream of FIN, and clear buffers.
+ bool MaybeCloseStream();
+
+ // Shared implementation between OnStreamFrame and OnCryptoFrame.
+ void OnFrameData(QuicStreamOffset byte_offset,
+ size_t data_len,
+ const char* data_buffer);
+
+ // The stream which owns this sequencer.
+ StreamInterface* stream_;
+
+ // Stores received data in offset order.
+ QuicStreamSequencerBuffer buffered_frames_;
+
+ // The offset, if any, we got a stream termination for. When this many bytes
+ // have been processed, the sequencer will be closed.
+ QuicStreamOffset close_offset_;
+
+ // If true, the sequencer is blocked from passing data to the stream and will
+ // buffer all new incoming data until FlushBufferedFrames is called.
+ bool blocked_;
+
+ // Count of the number of frames received.
+ int num_frames_received_;
+
+ // Count of the number of duplicate frames received.
+ int num_duplicate_frames_received_;
+
+ // If true, all incoming data will be discarded.
+ bool ignore_read_data_;
+
+ // If false, only call OnDataAvailable() when it becomes newly unblocked.
+ // Otherwise, call OnDataAvailable() when number of readable bytes changes.
+ bool level_triggered_;
+
+ // Latched value of quic_stop_reading_when_level_triggered flag. When true,
+ // the sequencer will discard incoming data (but not FIN bits) after
+ // StopReading is called, even in level_triggered_ mode.
+ const bool stop_reading_when_level_triggered_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc
new file mode 100644
index 00000000000..d42ee20de5d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.cc
@@ -0,0 +1,528 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+namespace {
+
+size_t CalculateBlockCount(size_t max_capacity_bytes) {
+ return (max_capacity_bytes + QuicStreamSequencerBuffer::kBlockSizeBytes - 1) /
+ QuicStreamSequencerBuffer::kBlockSizeBytes;
+}
+
+// Upper limit of how many gaps allowed in buffer, which ensures a reasonable
+// number of iterations needed to find the right gap to fill when a frame
+// arrives.
+const size_t kMaxNumDataIntervalsAllowed = 2 * kMaxPacketGap;
+
+} // namespace
+
+QuicStreamSequencerBuffer::QuicStreamSequencerBuffer(size_t max_capacity_bytes)
+ : max_buffer_capacity_bytes_(max_capacity_bytes),
+ blocks_count_(CalculateBlockCount(max_capacity_bytes)),
+ total_bytes_read_(0),
+ blocks_(nullptr),
+ total_bytes_prefetched_(0),
+ faster_interval_add_in_sequence_buffer_(
+ GetQuicReloadableFlag(quic_faster_interval_add_in_sequence_buffer)) {
+ Clear();
+}
+
+QuicStreamSequencerBuffer::~QuicStreamSequencerBuffer() {
+ Clear();
+}
+
+void QuicStreamSequencerBuffer::Clear() {
+ if (blocks_ != nullptr) {
+ for (size_t i = 0; i < blocks_count_; ++i) {
+ if (blocks_[i] != nullptr) {
+ RetireBlock(i);
+ }
+ }
+ }
+ num_bytes_buffered_ = 0;
+ bytes_received_.Clear();
+ bytes_received_.Add(0, total_bytes_read_);
+}
+
+bool QuicStreamSequencerBuffer::RetireBlock(size_t idx) {
+ if (blocks_[idx] == nullptr) {
+ QUIC_BUG << "Try to retire block twice";
+ return false;
+ }
+ delete blocks_[idx];
+ blocks_[idx] = nullptr;
+ QUIC_DVLOG(1) << "Retired block with index: " << idx;
+ return true;
+}
+
+QuicErrorCode QuicStreamSequencerBuffer::OnStreamData(
+ QuicStreamOffset starting_offset,
+ QuicStringPiece data,
+ size_t* const bytes_buffered,
+ std::string* error_details) {
+ *bytes_buffered = 0;
+ size_t size = data.size();
+ if (size == 0) {
+ *error_details = "Received empty stream frame without FIN.";
+ return QUIC_EMPTY_STREAM_FRAME_NO_FIN;
+ }
+ // Write beyond the current range this buffer is covering.
+ if (starting_offset + size > total_bytes_read_ + max_buffer_capacity_bytes_ ||
+ starting_offset + size < starting_offset) {
+ *error_details = "Received data beyond available range.";
+ return QUIC_INTERNAL_ERROR;
+ }
+ if (bytes_received_.Empty() ||
+ starting_offset >= bytes_received_.rbegin()->max() ||
+ bytes_received_.IsDisjoint(QuicInterval<QuicStreamOffset>(
+ starting_offset, starting_offset + size))) {
+ // Optimization for the typical case, when all data is newly received.
+ if (faster_interval_add_in_sequence_buffer_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_faster_interval_add_in_sequence_buffer);
+ bytes_received_.AddOptimizedForAppend(starting_offset,
+ starting_offset + size);
+ if (bytes_received_.Size() >= kMaxNumDataIntervalsAllowed) {
+ // This frame is going to create more intervals than allowed. Stop
+ // processing.
+ *error_details = "Too many data intervals received for this stream.";
+ return QUIC_TOO_MANY_STREAM_DATA_INTERVALS;
+ }
+ } else {
+ if (!bytes_received_.Empty() &&
+ starting_offset == bytes_received_.rbegin()->max()) {
+ // Extend the right edge of last interval.
+ // TODO(fayang): Encapsulate this into a future version of
+ // QuicIntervalSet if this is more efficient than Add.
+ const_cast<QuicInterval<QuicStreamOffset>*>(
+ &(*bytes_received_.rbegin()))
+ ->SetMax(starting_offset + size);
+ } else {
+ bytes_received_.Add(starting_offset, starting_offset + size);
+ if (bytes_received_.Size() >= kMaxNumDataIntervalsAllowed) {
+ // This frame is going to create more intervals than allowed. Stop
+ // processing.
+ *error_details = "Too many data intervals received for this stream.";
+ return QUIC_TOO_MANY_STREAM_DATA_INTERVALS;
+ }
+ }
+ }
+ size_t bytes_copy = 0;
+ if (!CopyStreamData(starting_offset, data, &bytes_copy, error_details)) {
+ return QUIC_STREAM_SEQUENCER_INVALID_STATE;
+ }
+ *bytes_buffered += bytes_copy;
+ num_bytes_buffered_ += *bytes_buffered;
+ return QUIC_NO_ERROR;
+ }
+ // Slow path, received data overlaps with received data.
+ QuicIntervalSet<QuicStreamOffset> newly_received(starting_offset,
+ starting_offset + size);
+ newly_received.Difference(bytes_received_);
+ if (newly_received.Empty()) {
+ return QUIC_NO_ERROR;
+ }
+ bytes_received_.Add(starting_offset, starting_offset + size);
+ if (bytes_received_.Size() >= kMaxNumDataIntervalsAllowed) {
+ // This frame is going to create more intervals than allowed. Stop
+ // processing.
+ *error_details = "Too many data intervals received for this stream.";
+ return QUIC_TOO_MANY_STREAM_DATA_INTERVALS;
+ }
+ for (const auto& interval : newly_received) {
+ const QuicStreamOffset copy_offset = interval.min();
+ const QuicByteCount copy_length = interval.max() - interval.min();
+ size_t bytes_copy = 0;
+ if (!CopyStreamData(copy_offset,
+ data.substr(copy_offset - starting_offset, copy_length),
+ &bytes_copy, error_details)) {
+ return QUIC_STREAM_SEQUENCER_INVALID_STATE;
+ }
+ *bytes_buffered += bytes_copy;
+ }
+ num_bytes_buffered_ += *bytes_buffered;
+ return QUIC_NO_ERROR;
+}
+
+bool QuicStreamSequencerBuffer::CopyStreamData(QuicStreamOffset offset,
+ QuicStringPiece data,
+ size_t* bytes_copy,
+ std::string* error_details) {
+ *bytes_copy = 0;
+ size_t source_remaining = data.size();
+ if (source_remaining == 0) {
+ return true;
+ }
+ const char* source = data.data();
+ // Write data block by block. If corresponding block has not created yet,
+ // create it first.
+ // Stop when all data are written or reaches the logical end of the buffer.
+ while (source_remaining > 0) {
+ const size_t write_block_num = GetBlockIndex(offset);
+ const size_t write_block_offset = GetInBlockOffset(offset);
+ DCHECK_GT(blocks_count_, write_block_num);
+
+ size_t block_capacity = GetBlockCapacity(write_block_num);
+ size_t bytes_avail = block_capacity - write_block_offset;
+
+ // If this write meets the upper boundary of the buffer,
+ // reduce the available free bytes.
+ if (offset + bytes_avail > total_bytes_read_ + max_buffer_capacity_bytes_) {
+ bytes_avail = total_bytes_read_ + max_buffer_capacity_bytes_ - offset;
+ }
+
+ if (blocks_ == nullptr) {
+ blocks_.reset(new BufferBlock*[blocks_count_]());
+ for (size_t i = 0; i < blocks_count_; ++i) {
+ blocks_[i] = nullptr;
+ }
+ }
+
+ if (write_block_num >= blocks_count_) {
+ *error_details = QuicStrCat(
+ "QuicStreamSequencerBuffer error: OnStreamData() exceed array bounds."
+ "write offset = ",
+ offset, " write_block_num = ", write_block_num,
+ " blocks_count_ = ", blocks_count_);
+ return false;
+ }
+ if (blocks_ == nullptr) {
+ *error_details =
+ "QuicStreamSequencerBuffer error: OnStreamData() blocks_ is null";
+ return false;
+ }
+ if (blocks_[write_block_num] == nullptr) {
+ // TODO(danzh): Investigate if using a freelist would improve performance.
+ // Same as RetireBlock().
+ blocks_[write_block_num] = new BufferBlock();
+ }
+
+ const size_t bytes_to_copy =
+ std::min<size_t>(bytes_avail, source_remaining);
+ char* dest = blocks_[write_block_num]->buffer + write_block_offset;
+ QUIC_DVLOG(1) << "Write at offset: " << offset
+ << " length: " << bytes_to_copy;
+
+ if (dest == nullptr || source == nullptr) {
+ *error_details = QuicStrCat(
+ "QuicStreamSequencerBuffer error: OnStreamData()"
+ " dest == nullptr: ",
+ (dest == nullptr), " source == nullptr: ", (source == nullptr),
+ " Writing at offset ", offset, " Gaps: ", GapsDebugString(),
+ " Remaining frames: ", ReceivedFramesDebugString(),
+ " total_bytes_read_ = ", total_bytes_read_);
+ return false;
+ }
+ memcpy(dest, source, bytes_to_copy);
+ source += bytes_to_copy;
+ source_remaining -= bytes_to_copy;
+ offset += bytes_to_copy;
+ *bytes_copy += bytes_to_copy;
+ }
+ return true;
+}
+
+QuicErrorCode QuicStreamSequencerBuffer::Readv(const iovec* dest_iov,
+ size_t dest_count,
+ size_t* bytes_read,
+ std::string* error_details) {
+ *bytes_read = 0;
+ for (size_t i = 0; i < dest_count && ReadableBytes() > 0; ++i) {
+ char* dest = reinterpret_cast<char*>(dest_iov[i].iov_base);
+ DCHECK(dest != nullptr);
+ size_t dest_remaining = dest_iov[i].iov_len;
+ while (dest_remaining > 0 && ReadableBytes() > 0) {
+ size_t block_idx = NextBlockToRead();
+ size_t start_offset_in_block = ReadOffset();
+ size_t block_capacity = GetBlockCapacity(block_idx);
+ size_t bytes_available_in_block = std::min<size_t>(
+ ReadableBytes(), block_capacity - start_offset_in_block);
+ size_t bytes_to_copy =
+ std::min<size_t>(bytes_available_in_block, dest_remaining);
+ DCHECK_GT(bytes_to_copy, 0u);
+ if (blocks_[block_idx] == nullptr || dest == nullptr) {
+ *error_details = QuicStrCat(
+ "QuicStreamSequencerBuffer error:"
+ " Readv() dest == nullptr: ",
+ (dest == nullptr), " blocks_[", block_idx,
+ "] == nullptr: ", (blocks_[block_idx] == nullptr),
+ " Gaps: ", GapsDebugString(),
+ " Remaining frames: ", ReceivedFramesDebugString(),
+ " total_bytes_read_ = ", total_bytes_read_);
+ return QUIC_STREAM_SEQUENCER_INVALID_STATE;
+ }
+ memcpy(dest, blocks_[block_idx]->buffer + start_offset_in_block,
+ bytes_to_copy);
+ dest += bytes_to_copy;
+ dest_remaining -= bytes_to_copy;
+ num_bytes_buffered_ -= bytes_to_copy;
+ total_bytes_read_ += bytes_to_copy;
+ *bytes_read += bytes_to_copy;
+
+ // Retire the block if all the data is read out and no other data is
+ // stored in this block.
+ // In case of failing to retire a block which is ready to retire, return
+ // immediately.
+ if (bytes_to_copy == bytes_available_in_block) {
+ bool retire_successfully = RetireBlockIfEmpty(block_idx);
+ if (!retire_successfully) {
+ *error_details = QuicStrCat(
+ "QuicStreamSequencerBuffer error: fail to retire block ",
+ block_idx,
+ " as the block is already released, total_bytes_read_ = ",
+ total_bytes_read_, " Gaps: ", GapsDebugString());
+ return QUIC_STREAM_SEQUENCER_INVALID_STATE;
+ }
+ }
+ }
+ }
+ total_bytes_prefetched_ =
+ std::max(total_bytes_prefetched_, total_bytes_read_);
+
+ return QUIC_NO_ERROR;
+}
+
+int QuicStreamSequencerBuffer::GetReadableRegions(struct iovec* iov,
+ int iov_count) const {
+ DCHECK(iov != nullptr);
+ DCHECK_GT(iov_count, 0);
+
+ if (ReadableBytes() == 0) {
+ iov[0].iov_base = nullptr;
+ iov[0].iov_len = 0;
+ return 0;
+ }
+
+ size_t start_block_idx = NextBlockToRead();
+ QuicStreamOffset readable_offset_end = FirstMissingByte() - 1;
+ DCHECK_GE(readable_offset_end + 1, total_bytes_read_);
+ size_t end_block_offset = GetInBlockOffset(readable_offset_end);
+ size_t end_block_idx = GetBlockIndex(readable_offset_end);
+
+ // If readable region is within one block, deal with it seperately.
+ if (start_block_idx == end_block_idx && ReadOffset() <= end_block_offset) {
+ iov[0].iov_base = blocks_[start_block_idx]->buffer + ReadOffset();
+ iov[0].iov_len = ReadableBytes();
+ QUIC_DVLOG(1) << "Got only a single block with index: " << start_block_idx;
+ return 1;
+ }
+
+ // Get first block
+ iov[0].iov_base = blocks_[start_block_idx]->buffer + ReadOffset();
+ iov[0].iov_len = GetBlockCapacity(start_block_idx) - ReadOffset();
+ QUIC_DVLOG(1) << "Got first block " << start_block_idx << " with len "
+ << iov[0].iov_len;
+ DCHECK_GT(readable_offset_end + 1, total_bytes_read_ + iov[0].iov_len)
+ << "there should be more available data";
+
+ // Get readable regions of the rest blocks till either 2nd to last block
+ // before gap is met or |iov| is filled. For these blocks, one whole block is
+ // a region.
+ int iov_used = 1;
+ size_t block_idx = (start_block_idx + iov_used) % blocks_count_;
+ while (block_idx != end_block_idx && iov_used < iov_count) {
+ DCHECK(nullptr != blocks_[block_idx]);
+ iov[iov_used].iov_base = blocks_[block_idx]->buffer;
+ iov[iov_used].iov_len = GetBlockCapacity(block_idx);
+ QUIC_DVLOG(1) << "Got block with index: " << block_idx;
+ ++iov_used;
+ block_idx = (start_block_idx + iov_used) % blocks_count_;
+ }
+
+ // Deal with last block if |iov| can hold more.
+ if (iov_used < iov_count) {
+ DCHECK(nullptr != blocks_[block_idx]);
+ iov[iov_used].iov_base = blocks_[end_block_idx]->buffer;
+ iov[iov_used].iov_len = end_block_offset + 1;
+ QUIC_DVLOG(1) << "Got last block with index: " << end_block_idx;
+ ++iov_used;
+ }
+ return iov_used;
+}
+
+bool QuicStreamSequencerBuffer::GetReadableRegion(iovec* iov) const {
+ return GetReadableRegions(iov, 1) == 1;
+}
+
+bool QuicStreamSequencerBuffer::PrefetchNextRegion(iovec* iov) {
+ DCHECK(iov != nullptr);
+
+ if (total_bytes_prefetched_ == FirstMissingByte()) {
+ return false;
+ }
+
+ size_t start_block_idx = GetBlockIndex(total_bytes_prefetched_);
+ size_t start_block_offset = GetInBlockOffset(total_bytes_prefetched_);
+ QuicStreamOffset readable_offset_end = FirstMissingByte() - 1;
+ size_t end_block_offset = GetInBlockOffset(readable_offset_end);
+ size_t end_block_idx = GetBlockIndex(readable_offset_end);
+
+ if (start_block_idx != end_block_idx) {
+ iov->iov_base = blocks_[start_block_idx]->buffer + start_block_offset;
+ iov->iov_len = GetBlockCapacity(start_block_idx) - start_block_offset;
+ total_bytes_prefetched_ += iov->iov_len;
+ return true;
+ }
+ iov->iov_base = blocks_[end_block_idx]->buffer + start_block_offset;
+ iov->iov_len = end_block_offset - start_block_offset + 1;
+ total_bytes_prefetched_ += iov->iov_len;
+ return true;
+}
+
+bool QuicStreamSequencerBuffer::MarkConsumed(size_t bytes_used) {
+ if (bytes_used > ReadableBytes()) {
+ return false;
+ }
+ size_t bytes_to_consume = bytes_used;
+ while (bytes_to_consume > 0) {
+ size_t block_idx = NextBlockToRead();
+ size_t offset_in_block = ReadOffset();
+ size_t bytes_available = std::min<size_t>(
+ ReadableBytes(), GetBlockCapacity(block_idx) - offset_in_block);
+ size_t bytes_read = std::min<size_t>(bytes_to_consume, bytes_available);
+ total_bytes_read_ += bytes_read;
+ num_bytes_buffered_ -= bytes_read;
+ bytes_to_consume -= bytes_read;
+ // If advanced to the end of current block and end of buffer hasn't wrapped
+ // to this block yet.
+ if (bytes_available == bytes_read) {
+ RetireBlockIfEmpty(block_idx);
+ }
+ }
+ total_bytes_prefetched_ =
+ std::max(total_bytes_read_, total_bytes_prefetched_);
+ return true;
+}
+
+size_t QuicStreamSequencerBuffer::FlushBufferedFrames() {
+ size_t prev_total_bytes_read = total_bytes_read_;
+ total_bytes_read_ = NextExpectedByte();
+ Clear();
+ return total_bytes_read_ - prev_total_bytes_read;
+}
+
+void QuicStreamSequencerBuffer::ReleaseWholeBuffer() {
+ Clear();
+ blocks_.reset(nullptr);
+}
+
+size_t QuicStreamSequencerBuffer::ReadableBytes() const {
+ return FirstMissingByte() - total_bytes_read_;
+}
+
+bool QuicStreamSequencerBuffer::HasBytesToRead() const {
+ return ReadableBytes() > 0;
+}
+
+QuicStreamOffset QuicStreamSequencerBuffer::BytesConsumed() const {
+ return total_bytes_read_;
+}
+
+size_t QuicStreamSequencerBuffer::BytesBuffered() const {
+ return num_bytes_buffered_;
+}
+
+size_t QuicStreamSequencerBuffer::GetBlockIndex(QuicStreamOffset offset) const {
+ return (offset % max_buffer_capacity_bytes_) / kBlockSizeBytes;
+}
+
+size_t QuicStreamSequencerBuffer::GetInBlockOffset(
+ QuicStreamOffset offset) const {
+ return (offset % max_buffer_capacity_bytes_) % kBlockSizeBytes;
+}
+
+size_t QuicStreamSequencerBuffer::ReadOffset() const {
+ return GetInBlockOffset(total_bytes_read_);
+}
+
+size_t QuicStreamSequencerBuffer::NextBlockToRead() const {
+ return GetBlockIndex(total_bytes_read_);
+}
+
+bool QuicStreamSequencerBuffer::RetireBlockIfEmpty(size_t block_index) {
+ DCHECK(ReadableBytes() == 0 || GetInBlockOffset(total_bytes_read_) == 0)
+ << "RetireBlockIfEmpty() should only be called when advancing to next "
+ << "block or a gap has been reached.";
+ // If the whole buffer becomes empty, the last piece of data has been read.
+ if (Empty()) {
+ return RetireBlock(block_index);
+ }
+
+ // Check where the logical end of this buffer is.
+ // Not empty if the end of circular buffer has been wrapped to this block.
+ if (GetBlockIndex(NextExpectedByte() - 1) == block_index) {
+ return true;
+ }
+
+ // Read index remains in this block, which means a gap has been reached.
+ if (NextBlockToRead() == block_index) {
+ if (bytes_received_.Size() > 1) {
+ auto it = bytes_received_.begin();
+ ++it;
+ if (GetBlockIndex(it->min()) == block_index) {
+ // Do not retire the block if next data interval is in this block.
+ return true;
+ }
+ } else {
+ QUIC_BUG << "Read stopped at where it shouldn't.";
+ return false;
+ }
+ }
+ return RetireBlock(block_index);
+}
+
+bool QuicStreamSequencerBuffer::Empty() const {
+ return bytes_received_.Empty() ||
+ (bytes_received_.Size() == 1 && total_bytes_read_ > 0 &&
+ bytes_received_.begin()->max() == total_bytes_read_);
+}
+
+size_t QuicStreamSequencerBuffer::GetBlockCapacity(size_t block_index) const {
+ if ((block_index + 1) == blocks_count_) {
+ size_t result = max_buffer_capacity_bytes_ % kBlockSizeBytes;
+ if (result == 0) { // whole block
+ result = kBlockSizeBytes;
+ }
+ return result;
+ } else {
+ return kBlockSizeBytes;
+ }
+}
+
+std::string QuicStreamSequencerBuffer::GapsDebugString() {
+ // TODO(vasilvv): this should return the complement of |bytes_received_|.
+ return bytes_received_.ToString();
+}
+
+std::string QuicStreamSequencerBuffer::ReceivedFramesDebugString() {
+ return bytes_received_.ToString();
+}
+
+QuicStreamOffset QuicStreamSequencerBuffer::FirstMissingByte() const {
+ if (bytes_received_.Empty() || bytes_received_.begin()->min() > 0) {
+ // Offset 0 is not received yet.
+ return 0;
+ }
+ return bytes_received_.begin()->max();
+}
+
+QuicStreamOffset QuicStreamSequencerBuffer::NextExpectedByte() const {
+ if (bytes_received_.Empty()) {
+ return 0;
+ }
+ return bytes_received_.rbegin()->max();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h
new file mode 100644
index 00000000000..6f4717d8ca6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h
@@ -0,0 +1,248 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_
+#define QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_
+
+// QuicStreamSequencerBuffer is a circular stream buffer with random write and
+// in-sequence read. It consists of a vector of pointers pointing
+// to memory blocks created as needed and an interval set recording received
+// data.
+// - Data are written in with offset indicating where it should be in the
+// stream, and the buffer grown as needed (up to the maximum buffer capacity),
+// without expensive copying (extra blocks are allocated).
+// - Data can be read from the buffer if there is no gap before it,
+// and the buffer shrinks as the data are consumed.
+// - An upper limit on the number of blocks in the buffer provides an upper
+// bound on memory use.
+//
+// This class is thread-unsafe.
+//
+// QuicStreamSequencerBuffer maintains a concept of the readable region, which
+// contains all written data that has not been read.
+// It promises stability of the underlying memory addresses in the readable
+// region, so pointers into it can be maintained, and the offset of a pointer
+// from the start of the read region can be calculated.
+//
+// Expected Use:
+// QuicStreamSequencerBuffer buffer(2.5 * 8 * 1024);
+// std::string source(1024, 'a');
+// QuicStringPiece string_piece(source.data(), source.size());
+// size_t written = 0;
+// buffer.OnStreamData(800, string_piece, GetEpollClockNow(), &written);
+// source = std::string{800, 'b'};
+// QuicStringPiece string_piece1(source.data(), 800);
+// // Try to write to [1, 801), but should fail due to overlapping,
+// // res should be QUIC_INVALID_STREAM_DATA
+// auto res = buffer.OnStreamData(1, string_piece1, &written));
+// // write to [0, 800), res should be QUIC_NO_ERROR
+// auto res = buffer.OnStreamData(0, string_piece1, GetEpollClockNow(),
+// &written);
+//
+// // Read into a iovec array with total capacity of 120 bytes.
+// char dest[120];
+// iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40},
+// iovec{dest + 80, 40}};
+// size_t read = buffer.Readv(iovecs, 3);
+//
+// // Get single readable region.
+// iovec iov;
+// buffer.GetReadableRegion(iov);
+//
+// // Get readable regions from [256, 1024) and consume some of it.
+// iovec iovs[2];
+// int iov_count = buffer.GetReadableRegions(iovs, 2);
+// // Consume some bytes in iovs, returning number of bytes having been
+// consumed.
+// size_t consumed = consume_iovs(iovs, iov_count);
+// buffer.MarkConsumed(consumed);
+
+#include <cstddef>
+#include <functional>
+#include <list>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+namespace test {
+class QuicStreamSequencerBufferPeer;
+} // namespace test
+
+class QUIC_EXPORT_PRIVATE QuicStreamSequencerBuffer {
+ public:
+ // Size of blocks used by this buffer.
+ // Choose 8K to make block large enough to hold multiple frames, each of
+ // which could be up to 1.5 KB.
+ static const size_t kBlockSizeBytes = 8 * 1024; // 8KB
+
+ // The basic storage block used by this buffer.
+ struct BufferBlock {
+ char buffer[kBlockSizeBytes];
+ };
+
+ explicit QuicStreamSequencerBuffer(size_t max_capacity_bytes);
+ QuicStreamSequencerBuffer(const QuicStreamSequencerBuffer&) = delete;
+ QuicStreamSequencerBuffer(QuicStreamSequencerBuffer&&) = default;
+ QuicStreamSequencerBuffer& operator=(const QuicStreamSequencerBuffer&) =
+ delete;
+ ~QuicStreamSequencerBuffer();
+
+ // Free the space used to buffer data.
+ void Clear();
+
+ // Returns true if there is nothing to read in this buffer.
+ bool Empty() const;
+
+ // Called to buffer new data received for this stream. If the data was
+ // successfully buffered, returns QUIC_NO_ERROR and stores the number of
+ // bytes buffered in |bytes_buffered|. Returns an error otherwise.
+ QuicErrorCode OnStreamData(QuicStreamOffset offset,
+ QuicStringPiece data,
+ size_t* bytes_buffered,
+ std::string* error_details);
+
+ // Reads from this buffer into given iovec array, up to number of iov_len
+ // iovec objects and returns the number of bytes read.
+ QuicErrorCode Readv(const struct iovec* dest_iov,
+ size_t dest_count,
+ size_t* bytes_read,
+ std::string* error_details);
+
+ // Returns the readable region of valid data in iovec format. The readable
+ // region is the buffer region where there is valid data not yet read by
+ // client.
+ // Returns the number of iovec entries in |iov| which were populated.
+ // If the region is empty, one iovec entry with 0 length
+ // is returned, and the function returns 0. If there are more readable
+ // regions than |iov_size|, the function only processes the first
+ // |iov_size| of them.
+ int GetReadableRegions(struct iovec* iov, int iov_len) const;
+
+ // Fills in one iovec with data from the next readable region.
+ // Returns false if there is no readable region available.
+ bool GetReadableRegion(iovec* iov) const;
+
+ // Called to return the next region that has not been returned by this method
+ // previously.
+ // If this method is to be used along with Readv() or MarkConsumed(), make
+ // sure that they are consuming less data than is read by this method.
+ // This method only returns reference of underlying data. The caller is
+ // responsible for copying and consuming the data.
+ // Returns true if the data is read, false otherwise.
+ bool PrefetchNextRegion(iovec* iov);
+
+ // Called after GetReadableRegions() to free up |bytes_used| space if these
+ // bytes are processed.
+ // Pre-requisite: bytes_used <= available bytes to read.
+ bool MarkConsumed(size_t bytes_buffered);
+
+ // Deletes and records as consumed any buffered data and clear the buffer.
+ // (To be called only after sequencer's StopReading has been called.)
+ size_t FlushBufferedFrames();
+
+ // Free the memory of buffered data.
+ void ReleaseWholeBuffer();
+
+ // Whether there are bytes can be read out.
+ bool HasBytesToRead() const;
+
+ // Count how many bytes have been consumed (read out of buffer).
+ QuicStreamOffset BytesConsumed() const;
+
+ // Count how many bytes are in buffer at this moment.
+ size_t BytesBuffered() const;
+
+ // Returns number of bytes available to be read out.
+ size_t ReadableBytes() const;
+
+ private:
+ friend class test::QuicStreamSequencerBufferPeer;
+
+ // Copies |data| to blocks_, sets |bytes_copy|. Returns true if the copy is
+ // successful. Otherwise, sets |error_details| and returns false.
+ bool CopyStreamData(QuicStreamOffset offset,
+ QuicStringPiece data,
+ size_t* bytes_copy,
+ std::string* error_details);
+
+ // Dispose the given buffer block.
+ // After calling this method, blocks_[index] is set to nullptr
+ // in order to indicate that no memory set is allocated for that block.
+ // Returns true on success, false otherwise.
+ bool RetireBlock(size_t index);
+
+ // Should only be called after the indexed block is read till the end of the
+ // block or missing data has been reached.
+ // If the block at |block_index| contains no buffered data, the block
+ // should be retired.
+ // Returns true on success, or false otherwise.
+ bool RetireBlockIfEmpty(size_t block_index);
+
+ // Calculate the capacity of block at specified index.
+ // Return value should be either kBlockSizeBytes for non-trailing blocks and
+ // max_buffer_capacity % kBlockSizeBytes for trailing block.
+ size_t GetBlockCapacity(size_t index) const;
+
+ // Does not check if offset is within reasonable range.
+ size_t GetBlockIndex(QuicStreamOffset offset) const;
+
+ // Given an offset in the stream, return the offset from the beginning of the
+ // block which contains this data.
+ size_t GetInBlockOffset(QuicStreamOffset offset) const;
+
+ // Get offset relative to index 0 in logical 1st block to start next read.
+ size_t ReadOffset() const;
+
+ // Get the index of the logical 1st block to start next read.
+ size_t NextBlockToRead() const;
+
+ // Returns offset of first missing byte.
+ QuicStreamOffset FirstMissingByte() const;
+
+ // Returns offset of highest received byte + 1.
+ QuicStreamOffset NextExpectedByte() const;
+
+ // Return |gaps_| as a string: [1024, 1500) [1800, 2048)... for debugging.
+ std::string GapsDebugString();
+
+ // Return all received frames as a string in same format as GapsDebugString();
+ std::string ReceivedFramesDebugString();
+
+ // The maximum total capacity of this buffer in byte, as constructed.
+ const size_t max_buffer_capacity_bytes_;
+
+ // How many blocks this buffer would need when it reaches full capacity.
+ const size_t blocks_count_;
+
+ // Number of bytes read out of buffer.
+ QuicStreamOffset total_bytes_read_;
+
+ // An ordered, variable-length list of blocks, with the length limited
+ // such that the number of blocks never exceeds blocks_count_.
+ // Each list entry can hold up to kBlockSizeBytes bytes.
+ std::unique_ptr<BufferBlock*[]> blocks_;
+
+ // Number of bytes in buffer.
+ size_t num_bytes_buffered_;
+
+ // Currently received data.
+ QuicIntervalSet<QuicStreamOffset> bytes_received_;
+
+ // Total number of bytes that have been prefetched.
+ QuicStreamOffset total_bytes_prefetched_;
+
+ // Latched value of --quic_faster_interval_add_in_sequence_buffer.
+ const bool faster_interval_add_in_sequence_buffer_;
+};
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_STREAM_SEQUENCER_BUFFER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc
new file mode 100644
index 00000000000..aa30b5107ef
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer_test.cc
@@ -0,0 +1,1086 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+namespace test {
+
+char GetCharFromIOVecs(size_t offset, iovec iov[], size_t count) {
+ size_t start_offset = 0;
+ for (size_t i = 0; i < count; i++) {
+ if (iov[i].iov_len == 0) {
+ continue;
+ }
+ size_t end_offset = start_offset + iov[i].iov_len - 1;
+ if (offset >= start_offset && offset <= end_offset) {
+ const char* buf = reinterpret_cast<const char*>(iov[i].iov_base);
+ return buf[offset - start_offset];
+ }
+ start_offset += iov[i].iov_len;
+ }
+ LOG(ERROR) << "Could not locate char at offset " << offset << " in " << count
+ << " iovecs";
+ for (size_t i = 0; i < count; ++i) {
+ LOG(ERROR) << " iov[" << i << "].iov_len = " << iov[i].iov_len;
+ }
+ return '\0';
+}
+
+const size_t kMaxNumGapsAllowed = 2 * kMaxPacketGap;
+
+static const size_t kBlockSizeBytes =
+ QuicStreamSequencerBuffer::kBlockSizeBytes;
+typedef QuicStreamSequencerBuffer::BufferBlock BufferBlock;
+
+namespace {
+
+class QuicStreamSequencerBufferTest : public QuicTest {
+ public:
+ void SetUp() override { Initialize(); }
+
+ void ResetMaxCapacityBytes(size_t max_capacity_bytes) {
+ max_capacity_bytes_ = max_capacity_bytes;
+ Initialize();
+ }
+
+ protected:
+ void Initialize() {
+ buffer_ = QuicMakeUnique<QuicStreamSequencerBuffer>((max_capacity_bytes_));
+ helper_ = QuicMakeUnique<QuicStreamSequencerBufferPeer>((buffer_.get()));
+ }
+
+ // Use 2.5 here to make sure the buffer has more than one block and its end
+ // doesn't align with the end of a block in order to test all the offset
+ // calculation.
+ size_t max_capacity_bytes_ = 2.5 * kBlockSizeBytes;
+
+ std::unique_ptr<QuicStreamSequencerBuffer> buffer_;
+ std::unique_ptr<QuicStreamSequencerBufferPeer> helper_;
+ std::string error_details_;
+};
+
+TEST_F(QuicStreamSequencerBufferTest, InitializeWithMaxRecvWindowSize) {
+ ResetMaxCapacityBytes(16 * 1024 * 1024); // 16MB
+ EXPECT_EQ(2 * 1024u, // 16MB / 8KB = 2K
+ helper_->block_count());
+ EXPECT_EQ(max_capacity_bytes_, helper_->max_buffer_capacity());
+ EXPECT_TRUE(helper_->CheckInitialState());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, InitializationWithDifferentSizes) {
+ const size_t kCapacity = 2 * QuicStreamSequencerBuffer::kBlockSizeBytes;
+ ResetMaxCapacityBytes(kCapacity);
+ EXPECT_EQ(max_capacity_bytes_, helper_->max_buffer_capacity());
+ EXPECT_TRUE(helper_->CheckInitialState());
+
+ const size_t kCapacity1 = 8 * QuicStreamSequencerBuffer::kBlockSizeBytes;
+ ResetMaxCapacityBytes(kCapacity1);
+ EXPECT_EQ(kCapacity1, helper_->max_buffer_capacity());
+ EXPECT_TRUE(helper_->CheckInitialState());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, ClearOnEmpty) {
+ buffer_->Clear();
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamData0length) {
+ size_t written;
+ QuicErrorCode error =
+ buffer_->OnStreamData(800, "", &written, &error_details_);
+ EXPECT_EQ(error, QUIC_EMPTY_STREAM_FRAME_NO_FIN);
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamDataWithinBlock) {
+ EXPECT_FALSE(helper_->IsBufferAllocated());
+ std::string source(1024, 'a');
+ size_t written;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(800, source, &written, &error_details_));
+ BufferBlock* block_ptr = helper_->GetBlock(0);
+ for (size_t i = 0; i < source.size(); ++i) {
+ ASSERT_EQ('a', block_ptr->buffer[helper_->GetInBlockOffset(800) + i]);
+ }
+ EXPECT_EQ(2, helper_->IntervalSize());
+ EXPECT_EQ(0u, helper_->ReadableBytes());
+ EXPECT_EQ(1u, helper_->bytes_received().Size());
+ EXPECT_EQ(800u, helper_->bytes_received().begin()->min());
+ EXPECT_EQ(1824u, helper_->bytes_received().begin()->max());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+ EXPECT_TRUE(helper_->IsBufferAllocated());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, Move) {
+ EXPECT_FALSE(helper_->IsBufferAllocated());
+ std::string source(1024, 'a');
+ size_t written;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(800, source, &written, &error_details_));
+ BufferBlock* block_ptr = helper_->GetBlock(0);
+ for (size_t i = 0; i < source.size(); ++i) {
+ ASSERT_EQ('a', block_ptr->buffer[helper_->GetInBlockOffset(800) + i]);
+ }
+
+ QuicStreamSequencerBuffer buffer2(std::move(*buffer_));
+ QuicStreamSequencerBufferPeer helper2(&buffer2);
+
+ EXPECT_FALSE(helper_->IsBufferAllocated());
+
+ EXPECT_EQ(2, helper2.IntervalSize());
+ EXPECT_EQ(0u, helper2.ReadableBytes());
+ EXPECT_EQ(1u, helper2.bytes_received().Size());
+ EXPECT_EQ(800u, helper2.bytes_received().begin()->min());
+ EXPECT_EQ(1824u, helper2.bytes_received().begin()->max());
+ EXPECT_TRUE(helper2.CheckBufferInvariants());
+ EXPECT_TRUE(helper2.IsBufferAllocated());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamDataInvalidSource) {
+ // Pass in an invalid source, expects to return error.
+ QuicStringPiece source;
+ source = QuicStringPiece(nullptr, 1024);
+ size_t written;
+ EXPECT_EQ(QUIC_STREAM_SEQUENCER_INVALID_STATE,
+ buffer_->OnStreamData(800, source, &written, &error_details_));
+ EXPECT_EQ(0u, error_details_.find(QuicStrCat(
+ "QuicStreamSequencerBuffer error: OnStreamData() "
+ "dest == nullptr: ",
+ false, " source == nullptr: ", true)));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamDataWithOverlap) {
+ std::string source(1024, 'a');
+ // Write something into [800, 1824)
+ size_t written;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(800, source, &written, &error_details_));
+ // Try to write to [0, 1024) and [1024, 2048).
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(0, source, &written, &error_details_));
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(1024, source, &written, &error_details_));
+}
+
+TEST_F(QuicStreamSequencerBufferTest,
+ OnStreamDataOverlapAndDuplicateCornerCases) {
+ std::string source(1024, 'a');
+ // Write something into [800, 1824)
+ size_t written;
+ buffer_->OnStreamData(800, source, &written, &error_details_);
+ source = std::string(800, 'b');
+ std::string one_byte = "c";
+ // Write [1, 801).
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(1, source, &written, &error_details_));
+ // Write [0, 800).
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(0, source, &written, &error_details_));
+ // Write [1823, 1824).
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(1823, one_byte, &written, &error_details_));
+ EXPECT_EQ(0u, written);
+ // write one byte to [1824, 1825)
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(1824, one_byte, &written, &error_details_));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamDataWithoutOverlap) {
+ std::string source(1024, 'a');
+ // Write something into [800, 1824).
+ size_t written;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(800, source, &written, &error_details_));
+ source = std::string(100, 'b');
+ // Write something into [kBlockSizeBytes * 2 - 20, kBlockSizeBytes * 2 + 80).
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(kBlockSizeBytes * 2 - 20, source, &written,
+ &error_details_));
+ EXPECT_EQ(3, helper_->IntervalSize());
+ EXPECT_EQ(1024u + 100u, buffer_->BytesBuffered());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamDataInLongStreamWithOverlap) {
+ // Assume a stream has already buffered almost 4GB.
+ uint64_t total_bytes_read = pow(2, 32) - 1;
+ helper_->set_total_bytes_read(total_bytes_read);
+ helper_->AddBytesReceived(0, total_bytes_read);
+
+ // Three new out of order frames arrive.
+ const size_t kBytesToWrite = 100;
+ std::string source(kBytesToWrite, 'a');
+ size_t written;
+ // Frame [2^32 + 500, 2^32 + 600).
+ QuicStreamOffset offset = pow(2, 32) + 500;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(offset, source, &written, &error_details_));
+ EXPECT_EQ(2, helper_->IntervalSize());
+
+ // Frame [2^32 + 700, 2^32 + 800).
+ offset = pow(2, 32) + 700;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(offset, source, &written, &error_details_));
+ EXPECT_EQ(3, helper_->IntervalSize());
+
+ // Another frame [2^32 + 300, 2^32 + 400).
+ offset = pow(2, 32) + 300;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(offset, source, &written, &error_details_));
+ EXPECT_EQ(4, helper_->IntervalSize());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamDataTillEnd) {
+ // Write 50 bytes to the end.
+ const size_t kBytesToWrite = 50;
+ std::string source(kBytesToWrite, 'a');
+ size_t written;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(max_capacity_bytes_ - kBytesToWrite, source,
+ &written, &error_details_));
+ EXPECT_EQ(50u, buffer_->BytesBuffered());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamDataTillEndCorner) {
+ // Write 1 byte to the end.
+ const size_t kBytesToWrite = 1;
+ std::string source(kBytesToWrite, 'a');
+ size_t written;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->OnStreamData(max_capacity_bytes_ - kBytesToWrite, source,
+ &written, &error_details_));
+ EXPECT_EQ(1u, buffer_->BytesBuffered());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, OnStreamDataBeyondCapacity) {
+ std::string source(60, 'a');
+ size_t written;
+ EXPECT_EQ(QUIC_INTERNAL_ERROR,
+ buffer_->OnStreamData(max_capacity_bytes_ - 50, source, &written,
+ &error_details_));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+
+ source = "b";
+ EXPECT_EQ(QUIC_INTERNAL_ERROR,
+ buffer_->OnStreamData(max_capacity_bytes_, source, &written,
+ &error_details_));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+
+ EXPECT_EQ(QUIC_INTERNAL_ERROR,
+ buffer_->OnStreamData(max_capacity_bytes_ * 1000, source, &written,
+ &error_details_));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+
+ // Disallow current_gap != gaps_.end()
+ EXPECT_EQ(QUIC_INTERNAL_ERROR,
+ buffer_->OnStreamData(static_cast<QuicStreamOffset>(-1), source,
+ &written, &error_details_));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+
+ // Disallow offset + size overflow
+ source = "bbb";
+ EXPECT_EQ(QUIC_INTERNAL_ERROR,
+ buffer_->OnStreamData(static_cast<QuicStreamOffset>(-2), source,
+ &written, &error_details_));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+ EXPECT_EQ(0u, buffer_->BytesBuffered());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, Readv100Bytes) {
+ std::string source(1024, 'a');
+ // Write something into [kBlockSizeBytes, kBlockSizeBytes + 1024).
+ size_t written;
+ buffer_->OnStreamData(kBlockSizeBytes, source, &written, &error_details_);
+ EXPECT_FALSE(buffer_->HasBytesToRead());
+ source = std::string(100, 'b');
+ // Write something into [0, 100).
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ EXPECT_TRUE(buffer_->HasBytesToRead());
+ // Read into a iovec array with total capacity of 120 bytes.
+ char dest[120];
+ iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40}, iovec{dest + 80, 40}};
+ size_t read;
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 3, &read, &error_details_));
+ QUIC_LOG(ERROR) << error_details_;
+ EXPECT_EQ(100u, read);
+ EXPECT_EQ(100u, buffer_->BytesConsumed());
+ EXPECT_EQ(source, std::string(dest, read));
+ // The first block should be released as its data has been read out.
+ EXPECT_EQ(nullptr, helper_->GetBlock(0));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, ReadvAcrossBlocks) {
+ std::string source(kBlockSizeBytes + 50, 'a');
+ // Write 1st block to full and extand 50 bytes to next block.
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ EXPECT_EQ(source.size(), helper_->ReadableBytes());
+ // Iteratively read 512 bytes from buffer_-> Overwrite dest[] each time.
+ char dest[512];
+ while (helper_->ReadableBytes()) {
+ std::fill(dest, dest + 512, 0);
+ iovec iovecs[2]{iovec{dest, 256}, iovec{dest + 256, 256}};
+ size_t read;
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 2, &read, &error_details_));
+ }
+ // The last read only reads the rest 50 bytes in 2nd block.
+ EXPECT_EQ(std::string(50, 'a'), std::string(dest, 50));
+ EXPECT_EQ(0, dest[50]) << "Dest[50] shouln't be filled.";
+ EXPECT_EQ(source.size(), buffer_->BytesConsumed());
+ EXPECT_TRUE(buffer_->Empty());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, ClearAfterRead) {
+ std::string source(kBlockSizeBytes + 50, 'a');
+ // Write 1st block to full with 'a'.
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ // Read first 512 bytes from buffer to make space at the beginning.
+ char dest[512]{0};
+ const iovec iov{dest, 512};
+ size_t read;
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_));
+ // Clear() should make buffer empty while preserving BytesConsumed()
+ buffer_->Clear();
+ EXPECT_TRUE(buffer_->Empty());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest,
+ OnStreamDataAcrossLastBlockAndFillCapacity) {
+ std::string source(kBlockSizeBytes + 50, 'a');
+ // Write 1st block to full with 'a'.
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ // Read first 512 bytes from buffer to make space at the beginning.
+ char dest[512]{0};
+ const iovec iov{dest, 512};
+ size_t read;
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_));
+ EXPECT_EQ(source.size(), written);
+
+ // Write more than half block size of bytes in the last block with 'b', which
+ // will wrap to the beginning and reaches the full capacity.
+ source = std::string(0.5 * kBlockSizeBytes + 512, 'b');
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->OnStreamData(2 * kBlockSizeBytes, source,
+ &written, &error_details_));
+ EXPECT_EQ(source.size(), written);
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest,
+ OnStreamDataAcrossLastBlockAndExceedCapacity) {
+ std::string source(kBlockSizeBytes + 50, 'a');
+ // Write 1st block to full.
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ // Read first 512 bytes from buffer to make space at the beginning.
+ char dest[512]{0};
+ const iovec iov{dest, 512};
+ size_t read;
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_));
+
+ // Try to write from [max_capacity_bytes_ - 0.5 * kBlockSizeBytes,
+ // max_capacity_bytes_ + 512 + 1). But last bytes exceeds current capacity.
+ source = std::string(0.5 * kBlockSizeBytes + 512 + 1, 'b');
+ EXPECT_EQ(QUIC_INTERNAL_ERROR,
+ buffer_->OnStreamData(2 * kBlockSizeBytes, source, &written,
+ &error_details_));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, ReadvAcrossLastBlock) {
+ // Write to full capacity and read out 512 bytes at beginning and continue
+ // appending 256 bytes.
+ std::string source(max_capacity_bytes_, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[512]{0};
+ const iovec iov{dest, 512};
+ size_t read;
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_));
+ source = std::string(256, 'b');
+ buffer_->OnStreamData(max_capacity_bytes_, source, &written, &error_details_);
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+
+ // Read all data out.
+ std::unique_ptr<char[]> dest1{new char[max_capacity_bytes_]};
+ dest1[0] = 0;
+ const iovec iov1{dest1.get(), max_capacity_bytes_};
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov1, 1, &read, &error_details_));
+ EXPECT_EQ(max_capacity_bytes_ - 512 + 256, read);
+ EXPECT_EQ(max_capacity_bytes_ + 256, buffer_->BytesConsumed());
+ EXPECT_TRUE(buffer_->Empty());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, ReadvEmpty) {
+ char dest[512]{0};
+ iovec iov{dest, 512};
+ size_t read;
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(&iov, 1, &read, &error_details_));
+ EXPECT_EQ(0u, read);
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionsEmpty) {
+ iovec iovs[2];
+ int iov_count = buffer_->GetReadableRegions(iovs, 2);
+ EXPECT_EQ(0, iov_count);
+ EXPECT_EQ(nullptr, iovs[iov_count].iov_base);
+ EXPECT_EQ(0u, iovs[iov_count].iov_len);
+}
+
+TEST_F(QuicStreamSequencerBufferTest, ReleaseWholeBuffer) {
+ // Tests that buffer is not deallocated unless ReleaseWholeBuffer() is called.
+ std::string source(100, 'b');
+ // Write something into [0, 100).
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ EXPECT_TRUE(buffer_->HasBytesToRead());
+ char dest[120];
+ iovec iovecs[3]{iovec{dest, 40}, iovec{dest + 40, 40}, iovec{dest + 80, 40}};
+ size_t read;
+ EXPECT_EQ(QUIC_NO_ERROR, buffer_->Readv(iovecs, 3, &read, &error_details_));
+ EXPECT_EQ(100u, read);
+ EXPECT_EQ(100u, buffer_->BytesConsumed());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+ EXPECT_TRUE(helper_->IsBufferAllocated());
+ buffer_->ReleaseWholeBuffer();
+ EXPECT_FALSE(helper_->IsBufferAllocated());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionsBlockedByGap) {
+ // Write into [1, 1024).
+ std::string source(1023, 'a');
+ size_t written;
+ buffer_->OnStreamData(1, source, &written, &error_details_);
+ // Try to get readable regions, but none is there.
+ iovec iovs[2];
+ int iov_count = buffer_->GetReadableRegions(iovs, 2);
+ EXPECT_EQ(0, iov_count);
+}
+
+TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionsTillEndOfBlock) {
+ // Write first block to full with [0, 256) 'a' and the rest 'b' then read out
+ // [0, 256)
+ std::string source(kBlockSizeBytes, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[256];
+ helper_->Read(dest, 256);
+ // Get readable region from [256, 1024)
+ iovec iovs[2];
+ int iov_count = buffer_->GetReadableRegions(iovs, 2);
+ EXPECT_EQ(1, iov_count);
+ EXPECT_EQ(std::string(kBlockSizeBytes - 256, 'a'),
+ std::string(reinterpret_cast<const char*>(iovs[0].iov_base),
+ iovs[0].iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionsWithinOneBlock) {
+ // Write into [0, 1024) and then read out [0, 256)
+ std::string source(1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[256];
+ helper_->Read(dest, 256);
+ // Get readable region from [256, 1024)
+ iovec iovs[2];
+ int iov_count = buffer_->GetReadableRegions(iovs, 2);
+ EXPECT_EQ(1, iov_count);
+ EXPECT_EQ(std::string(1024 - 256, 'a'),
+ std::string(reinterpret_cast<const char*>(iovs[0].iov_base),
+ iovs[0].iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest,
+ GetReadableRegionsAcrossBlockWithLongIOV) {
+ // Write into [0, 2 * kBlockSizeBytes + 1024) and then read out [0, 1024)
+ std::string source(2 * kBlockSizeBytes + 1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[1024];
+ helper_->Read(dest, 1024);
+
+ iovec iovs[4];
+ int iov_count = buffer_->GetReadableRegions(iovs, 4);
+ EXPECT_EQ(3, iov_count);
+ EXPECT_EQ(kBlockSizeBytes - 1024, iovs[0].iov_len);
+ EXPECT_EQ(kBlockSizeBytes, iovs[1].iov_len);
+ EXPECT_EQ(1024u, iovs[2].iov_len);
+}
+
+TEST_F(QuicStreamSequencerBufferTest,
+ GetReadableRegionsWithMultipleIOVsAcrossEnd) {
+ // Write into [0, 2 * kBlockSizeBytes + 1024) and then read out [0, 1024)
+ // and then append 1024 + 512 bytes.
+ std::string source(2.5 * kBlockSizeBytes - 1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[1024];
+ helper_->Read(dest, 1024);
+ // Write across the end.
+ source = std::string(1024 + 512, 'b');
+ buffer_->OnStreamData(2.5 * kBlockSizeBytes - 1024, source, &written,
+ &error_details_);
+ // Use short iovec's.
+ iovec iovs[2];
+ int iov_count = buffer_->GetReadableRegions(iovs, 2);
+ EXPECT_EQ(2, iov_count);
+ EXPECT_EQ(kBlockSizeBytes - 1024, iovs[0].iov_len);
+ EXPECT_EQ(kBlockSizeBytes, iovs[1].iov_len);
+ // Use long iovec's and wrap the end of buffer.
+ iovec iovs1[5];
+ EXPECT_EQ(4, buffer_->GetReadableRegions(iovs1, 5));
+ EXPECT_EQ(0.5 * kBlockSizeBytes, iovs1[2].iov_len);
+ EXPECT_EQ(512u, iovs1[3].iov_len);
+ EXPECT_EQ(std::string(512, 'b'),
+ std::string(reinterpret_cast<const char*>(iovs1[3].iov_base),
+ iovs1[3].iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionEmpty) {
+ iovec iov;
+ EXPECT_FALSE(buffer_->GetReadableRegion(&iov));
+ EXPECT_EQ(nullptr, iov.iov_base);
+ EXPECT_EQ(0u, iov.iov_len);
+}
+
+TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionBeforeGap) {
+ // Write into [1, 1024).
+ std::string source(1023, 'a');
+ size_t written;
+ buffer_->OnStreamData(1, source, &written, &error_details_);
+ // GetReadableRegion should return false because range [0,1) hasn't been
+ // filled yet.
+ iovec iov;
+ EXPECT_FALSE(buffer_->GetReadableRegion(&iov));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionTillEndOfBlock) {
+ // Write into [0, kBlockSizeBytes + 1) and then read out [0, 256)
+ std::string source(kBlockSizeBytes + 1, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[256];
+ helper_->Read(dest, 256);
+ // Get readable region from [256, 1024)
+ iovec iov;
+ EXPECT_TRUE(buffer_->GetReadableRegion(&iov));
+ EXPECT_EQ(
+ std::string(kBlockSizeBytes - 256, 'a'),
+ std::string(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, GetReadableRegionTillGap) {
+ // Write into [0, kBlockSizeBytes - 1) and then read out [0, 256)
+ std::string source(kBlockSizeBytes - 1, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[256];
+ helper_->Read(dest, 256);
+ // Get readable region from [256, 1023)
+ iovec iov;
+ EXPECT_TRUE(buffer_->GetReadableRegion(&iov));
+ EXPECT_EQ(
+ std::string(kBlockSizeBytes - 1 - 256, 'a'),
+ std::string(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PrefetchEmptyBuffer) {
+ iovec iov;
+ EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PrefetchInitialBuffer) {
+ std::string source(kBlockSizeBytes, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ iovec iov;
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(source, std::string(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferWithOffset) {
+ std::string source(1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ iovec iov;
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(source, std::string(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len));
+ // The second frame goes into the same bucket.
+ std::string source2(800, 'a');
+ buffer_->OnStreamData(1024, source2, &written, &error_details_);
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(source2, std::string(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferWithMultipleBucket) {
+ const size_t data_size = 1024;
+ std::string source(data_size, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ iovec iov;
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(source, std::string(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len));
+ std::string source2(kBlockSizeBytes, 'b');
+ buffer_->OnStreamData(data_size, source2, &written, &error_details_);
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(
+ std::string(kBlockSizeBytes - data_size, 'b'),
+ std::string(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len));
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(
+ std::string(data_size, 'b'),
+ std::string(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PrefetchBufferAfterBlockRetired) {
+ std::string source(kBlockSizeBytes, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ iovec iov;
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(source, std::string(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len));
+ // Read the whole block so it's retired.
+ char dest[kBlockSizeBytes];
+ helper_->Read(dest, kBlockSizeBytes);
+
+ std::string source2(300, 'b');
+ buffer_->OnStreamData(kBlockSizeBytes, source2, &written, &error_details_);
+
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(source2, std::string(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PrefetchContinously) {
+ std::string source(kBlockSizeBytes, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ iovec iov;
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(source, std::string(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len));
+ std::string source2(kBlockSizeBytes, 'b');
+ buffer_->OnStreamData(kBlockSizeBytes, source2, &written, &error_details_);
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(source2, std::string(reinterpret_cast<const char*>(iov.iov_base),
+ iov.iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, ConsumeMoreThanPrefetch) {
+ std::string source(100, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[30];
+ helper_->Read(dest, 30);
+ iovec iov;
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(
+ std::string(70, 'a'),
+ std::string(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len));
+ std::string source2(100, 'b');
+ buffer_->OnStreamData(100, source2, &written, &error_details_);
+ buffer_->MarkConsumed(100);
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(
+ std::string(70, 'b'),
+ std::string(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, PrefetchMoreThanBufferHas) {
+ std::string source(100, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ iovec iov;
+ EXPECT_TRUE(buffer_->PrefetchNextRegion(&iov));
+ EXPECT_EQ(
+ std::string(100, 'a'),
+ std::string(reinterpret_cast<const char*>(iov.iov_base), iov.iov_len));
+ EXPECT_FALSE(buffer_->PrefetchNextRegion(&iov));
+}
+
+TEST_F(QuicStreamSequencerBufferTest, MarkConsumedInOneBlock) {
+ // Write into [0, 1024) and then read out [0, 256)
+ std::string source(1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[256];
+ helper_->Read(dest, 256);
+
+ EXPECT_TRUE(buffer_->MarkConsumed(512));
+ EXPECT_EQ(256u + 512u, buffer_->BytesConsumed());
+ EXPECT_EQ(256u, helper_->ReadableBytes());
+ buffer_->MarkConsumed(256);
+ EXPECT_TRUE(buffer_->Empty());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, MarkConsumedNotEnoughBytes) {
+ // Write into [0, 1024) and then read out [0, 256)
+ std::string source(1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[256];
+ helper_->Read(dest, 256);
+
+ // Consume 1st 512 bytes
+ EXPECT_TRUE(buffer_->MarkConsumed(512));
+ EXPECT_EQ(256u + 512u, buffer_->BytesConsumed());
+ EXPECT_EQ(256u, helper_->ReadableBytes());
+ // Try to consume one bytes more than available. Should return false.
+ EXPECT_FALSE(buffer_->MarkConsumed(257));
+ EXPECT_EQ(256u + 512u, buffer_->BytesConsumed());
+ iovec iov;
+ EXPECT_TRUE(buffer_->GetReadableRegion(&iov));
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, MarkConsumedAcrossBlock) {
+ // Write into [0, 2 * kBlockSizeBytes + 1024) and then read out [0, 1024)
+ std::string source(2 * kBlockSizeBytes + 1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[1024];
+ helper_->Read(dest, 1024);
+
+ buffer_->MarkConsumed(2 * kBlockSizeBytes);
+ EXPECT_EQ(source.size(), buffer_->BytesConsumed());
+ EXPECT_TRUE(buffer_->Empty());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, MarkConsumedAcrossEnd) {
+ // Write into [0, 2.5 * kBlockSizeBytes - 1024) and then read out [0, 1024)
+ // and then append 1024 + 512 bytes.
+ std::string source(2.5 * kBlockSizeBytes - 1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[1024];
+ helper_->Read(dest, 1024);
+ source = std::string(1024 + 512, 'b');
+ buffer_->OnStreamData(2.5 * kBlockSizeBytes - 1024, source, &written,
+ &error_details_);
+ EXPECT_EQ(1024u, buffer_->BytesConsumed());
+
+ // Consume to the end of 2nd block.
+ buffer_->MarkConsumed(2 * kBlockSizeBytes - 1024);
+ EXPECT_EQ(2 * kBlockSizeBytes, buffer_->BytesConsumed());
+ // Consume across the physical end of buffer
+ buffer_->MarkConsumed(0.5 * kBlockSizeBytes + 500);
+ EXPECT_EQ(max_capacity_bytes_ + 500, buffer_->BytesConsumed());
+ EXPECT_EQ(12u, helper_->ReadableBytes());
+ // Consume to the logical end of buffer
+ buffer_->MarkConsumed(12);
+ EXPECT_EQ(max_capacity_bytes_ + 512, buffer_->BytesConsumed());
+ EXPECT_TRUE(buffer_->Empty());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, FlushBufferedFrames) {
+ // Write into [0, 2.5 * kBlockSizeBytes - 1024) and then read out [0, 1024).
+ std::string source(max_capacity_bytes_ - 1024, 'a');
+ size_t written;
+ buffer_->OnStreamData(0, source, &written, &error_details_);
+ char dest[1024];
+ helper_->Read(dest, 1024);
+ EXPECT_EQ(1024u, buffer_->BytesConsumed());
+ // Write [1024, 512) to the physical beginning.
+ source = std::string(512, 'b');
+ buffer_->OnStreamData(max_capacity_bytes_, source, &written, &error_details_);
+ EXPECT_EQ(512u, written);
+ EXPECT_EQ(max_capacity_bytes_ - 1024 + 512, buffer_->FlushBufferedFrames());
+ EXPECT_EQ(max_capacity_bytes_ + 512, buffer_->BytesConsumed());
+ EXPECT_TRUE(buffer_->Empty());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+ // Clear buffer at this point should still preserve BytesConsumed().
+ buffer_->Clear();
+ EXPECT_EQ(max_capacity_bytes_ + 512, buffer_->BytesConsumed());
+ EXPECT_TRUE(helper_->CheckBufferInvariants());
+}
+
+TEST_F(QuicStreamSequencerBufferTest, TooManyGaps) {
+ // Make sure max capacity is large enough that it is possible to have more
+ // than |kMaxNumGapsAllowed| number of gaps.
+ max_capacity_bytes_ = 3 * kBlockSizeBytes;
+ // Feed buffer with 1-byte discontiguous frames. e.g. [1,2), [3,4), [5,6)...
+ for (QuicStreamOffset begin = 1; begin <= max_capacity_bytes_; begin += 2) {
+ size_t written;
+ QuicErrorCode rs =
+ buffer_->OnStreamData(begin, "a", &written, &error_details_);
+
+ QuicStreamOffset last_straw = 2 * kMaxNumGapsAllowed - 1;
+ if (begin == last_straw) {
+ EXPECT_EQ(QUIC_TOO_MANY_STREAM_DATA_INTERVALS, rs);
+ EXPECT_EQ("Too many data intervals received for this stream.",
+ error_details_);
+ break;
+ }
+ }
+}
+
+class QuicStreamSequencerBufferRandomIOTest
+ : public QuicStreamSequencerBufferTest {
+ public:
+ typedef std::pair<QuicStreamOffset, size_t> OffsetSizePair;
+
+ void SetUp() override {
+ // Test against a larger capacity then above tests. Also make sure the last
+ // block is partially available to use.
+ max_capacity_bytes_ = 6.25 * kBlockSizeBytes;
+ // Stream to be buffered should be larger than the capacity to test wrap
+ // around.
+ bytes_to_buffer_ = 2 * max_capacity_bytes_;
+ Initialize();
+
+ uint64_t seed = QuicRandom::GetInstance()->RandUint64();
+ QUIC_LOG(INFO) << "**** The current seed is " << seed << " ****";
+ rng_.set_seed(seed);
+ }
+
+ // Create an out-of-order source stream with given size to populate
+ // shuffled_buf_.
+ void CreateSourceAndShuffle(size_t max_chunk_size_bytes) {
+ max_chunk_size_bytes_ = max_chunk_size_bytes;
+ std::unique_ptr<OffsetSizePair[]> chopped_stream(
+ new OffsetSizePair[bytes_to_buffer_]);
+
+ // Split stream into small chunks with random length. chopped_stream will be
+ // populated with segmented stream chunks.
+ size_t start_chopping_offset = 0;
+ size_t iterations = 0;
+ while (start_chopping_offset < bytes_to_buffer_) {
+ size_t max_chunk = std::min<size_t>(
+ max_chunk_size_bytes_, bytes_to_buffer_ - start_chopping_offset);
+ size_t chunk_size = rng_.RandUint64() % max_chunk + 1;
+ chopped_stream[iterations] =
+ OffsetSizePair(start_chopping_offset, chunk_size);
+ start_chopping_offset += chunk_size;
+ ++iterations;
+ }
+ DCHECK(start_chopping_offset == bytes_to_buffer_);
+ size_t chunk_num = iterations;
+
+ // Randomly change the sequence of in-ordered OffsetSizePairs to make a
+ // out-of-order array of OffsetSizePairs.
+ for (int i = chunk_num - 1; i >= 0; --i) {
+ size_t random_idx = rng_.RandUint64() % (i + 1);
+ QUIC_DVLOG(1) << "chunk offset " << chopped_stream[random_idx].first
+ << " size " << chopped_stream[random_idx].second;
+ shuffled_buf_.push_front(chopped_stream[random_idx]);
+ chopped_stream[random_idx] = chopped_stream[i];
+ }
+ }
+
+ // Write the currently first chunk of data in the out-of-order stream into
+ // QuicStreamSequencerBuffer. If current chuck cannot be written into buffer
+ // because it goes beyond current capacity, move it to the end of
+ // shuffled_buf_ and write it later.
+ void WriteNextChunkToBuffer() {
+ OffsetSizePair& chunk = shuffled_buf_.front();
+ QuicStreamOffset offset = chunk.first;
+ const size_t num_to_write = chunk.second;
+ std::unique_ptr<char[]> write_buf{new char[max_chunk_size_bytes_]};
+ for (size_t i = 0; i < num_to_write; ++i) {
+ write_buf[i] = (offset + i) % 256;
+ }
+ QuicStringPiece string_piece_w(write_buf.get(), num_to_write);
+ size_t written;
+ auto result = buffer_->OnStreamData(offset, string_piece_w, &written,
+ &error_details_);
+ if (result == QUIC_NO_ERROR) {
+ shuffled_buf_.pop_front();
+ total_bytes_written_ += num_to_write;
+ } else {
+ // This chunk offset exceeds window size.
+ shuffled_buf_.push_back(chunk);
+ shuffled_buf_.pop_front();
+ }
+ QUIC_DVLOG(1) << " write at offset: " << offset
+ << " len to write: " << num_to_write
+ << " write result: " << result
+ << " left over: " << shuffled_buf_.size();
+ }
+
+ protected:
+ std::list<OffsetSizePair> shuffled_buf_;
+ size_t max_chunk_size_bytes_;
+ QuicStreamOffset bytes_to_buffer_;
+ size_t total_bytes_written_ = 0;
+ size_t total_bytes_read_ = 0;
+ SimpleRandom rng_;
+};
+
+TEST_F(QuicStreamSequencerBufferRandomIOTest, RandomWriteAndReadv) {
+ // Set kMaxReadSize larger than kBlockSizeBytes to test both small and large
+ // read.
+ const size_t kMaxReadSize = kBlockSizeBytes * 2;
+ // kNumReads is larger than 1 to test how multiple read destinations work.
+ const size_t kNumReads = 2;
+ // Since write and read operation have equal possibility to be called. Bytes
+ // to be written into and read out of should roughly the same.
+ const size_t kMaxWriteSize = kNumReads * kMaxReadSize;
+ size_t iterations = 0;
+
+ CreateSourceAndShuffle(kMaxWriteSize);
+
+ while ((!shuffled_buf_.empty() || total_bytes_read_ < bytes_to_buffer_) &&
+ iterations <= 2 * bytes_to_buffer_) {
+ uint8_t next_action =
+ shuffled_buf_.empty() ? uint8_t{1} : rng_.RandUint64() % 2;
+ QUIC_DVLOG(1) << "iteration: " << iterations;
+ switch (next_action) {
+ case 0: { // write
+ WriteNextChunkToBuffer();
+ ASSERT_TRUE(helper_->CheckBufferInvariants());
+ break;
+ }
+ case 1: { // readv
+ std::unique_ptr<char[][kMaxReadSize]> read_buf{
+ new char[kNumReads][kMaxReadSize]};
+ iovec dest_iov[kNumReads];
+ size_t num_to_read = 0;
+ for (size_t i = 0; i < kNumReads; ++i) {
+ dest_iov[i].iov_base =
+ reinterpret_cast<void*>(const_cast<char*>(read_buf[i]));
+ dest_iov[i].iov_len = rng_.RandUint64() % kMaxReadSize;
+ num_to_read += dest_iov[i].iov_len;
+ }
+ size_t actually_read;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->Readv(dest_iov, kNumReads, &actually_read,
+ &error_details_));
+ ASSERT_LE(actually_read, num_to_read);
+ QUIC_DVLOG(1) << " read from offset: " << total_bytes_read_
+ << " size: " << num_to_read
+ << " actual read: " << actually_read;
+ for (size_t i = 0; i < actually_read; ++i) {
+ char ch = (i + total_bytes_read_) % 256;
+ ASSERT_EQ(ch, GetCharFromIOVecs(i, dest_iov, kNumReads))
+ << " at iteration " << iterations;
+ }
+ total_bytes_read_ += actually_read;
+ ASSERT_EQ(total_bytes_read_, buffer_->BytesConsumed());
+ ASSERT_TRUE(helper_->CheckBufferInvariants());
+ break;
+ }
+ }
+ ++iterations;
+ ASSERT_LE(total_bytes_read_, total_bytes_written_);
+ }
+ EXPECT_LT(iterations, bytes_to_buffer_) << "runaway test";
+ EXPECT_LE(bytes_to_buffer_, total_bytes_read_)
+ << "iterations: " << iterations;
+ EXPECT_LE(bytes_to_buffer_, total_bytes_written_);
+}
+
+TEST_F(QuicStreamSequencerBufferRandomIOTest, RandomWriteAndConsumeInPlace) {
+ // The value 4 is chosen such that the max write size is no larger than the
+ // maximum buffer capacity.
+ const size_t kMaxNumReads = 4;
+ // Adjust write amount be roughly equal to that GetReadableRegions() can get.
+ const size_t kMaxWriteSize = kMaxNumReads * kBlockSizeBytes;
+ ASSERT_LE(kMaxWriteSize, max_capacity_bytes_);
+ size_t iterations = 0;
+
+ CreateSourceAndShuffle(kMaxWriteSize);
+
+ while ((!shuffled_buf_.empty() || total_bytes_read_ < bytes_to_buffer_) &&
+ iterations <= 2 * bytes_to_buffer_) {
+ uint8_t next_action =
+ shuffled_buf_.empty() ? uint8_t{1} : rng_.RandUint64() % 2;
+ QUIC_DVLOG(1) << "iteration: " << iterations;
+ switch (next_action) {
+ case 0: { // write
+ WriteNextChunkToBuffer();
+ ASSERT_TRUE(helper_->CheckBufferInvariants());
+ break;
+ }
+ case 1: { // GetReadableRegions and then MarkConsumed
+ size_t num_read = rng_.RandUint64() % kMaxNumReads + 1;
+ iovec dest_iov[kMaxNumReads];
+ ASSERT_TRUE(helper_->CheckBufferInvariants());
+ size_t actually_num_read =
+ buffer_->GetReadableRegions(dest_iov, num_read);
+ ASSERT_LE(actually_num_read, num_read);
+ size_t avail_bytes = 0;
+ for (size_t i = 0; i < actually_num_read; ++i) {
+ avail_bytes += dest_iov[i].iov_len;
+ }
+ // process random number of bytes (check the value of each byte).
+ size_t bytes_to_process = rng_.RandUint64() % (avail_bytes + 1);
+ size_t bytes_processed = 0;
+ for (size_t i = 0; i < actually_num_read; ++i) {
+ size_t bytes_in_block = std::min<size_t>(
+ bytes_to_process - bytes_processed, dest_iov[i].iov_len);
+ if (bytes_in_block == 0) {
+ break;
+ }
+ for (size_t j = 0; j < bytes_in_block; ++j) {
+ ASSERT_LE(bytes_processed, bytes_to_process);
+ char char_expected =
+ (buffer_->BytesConsumed() + bytes_processed) % 256;
+ ASSERT_EQ(char_expected,
+ reinterpret_cast<const char*>(dest_iov[i].iov_base)[j])
+ << " at iteration " << iterations;
+ ++bytes_processed;
+ }
+ }
+
+ buffer_->MarkConsumed(bytes_processed);
+
+ QUIC_DVLOG(1) << "iteration " << iterations << ": try to get "
+ << num_read << " readable regions, actually get "
+ << actually_num_read
+ << " from offset: " << total_bytes_read_
+ << "\nprocesse bytes: " << bytes_processed;
+ total_bytes_read_ += bytes_processed;
+ ASSERT_EQ(total_bytes_read_, buffer_->BytesConsumed());
+ ASSERT_TRUE(helper_->CheckBufferInvariants());
+ break;
+ }
+ }
+ ++iterations;
+ ASSERT_LE(total_bytes_read_, total_bytes_written_);
+ }
+ EXPECT_LT(iterations, bytes_to_buffer_) << "runaway test";
+ EXPECT_LE(bytes_to_buffer_, total_bytes_read_)
+ << "iterations: " << iterations;
+ EXPECT_LE(bytes_to_buffer_, total_bytes_written_);
+}
+
+} // anonymous namespace
+
+} // namespace test
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc
new file mode 100644
index 00000000000..47b1a5cc9e2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_sequencer_test.cc
@@ -0,0 +1,763 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::InSequence;
+
+namespace quic {
+namespace test {
+
+class MockStream : public QuicStreamSequencer::StreamInterface {
+ public:
+ MOCK_METHOD0(OnFinRead, void());
+ MOCK_METHOD0(OnDataAvailable, void());
+ MOCK_METHOD2(CloseConnectionWithDetails,
+ void(QuicErrorCode error, const std::string& details));
+ MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error));
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD1(AddBytesConsumed, void(QuicByteCount bytes));
+
+ QuicStreamId id() const override { return 1; }
+
+ const QuicSocketAddress& PeerAddressOfLatestPacket() const override {
+ return peer_address_;
+ }
+
+ protected:
+ QuicSocketAddress peer_address_ =
+ QuicSocketAddress(QuicIpAddress::Any4(), 65535);
+};
+
+namespace {
+
+static const char kPayload[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+class QuicStreamSequencerTest : public QuicTest {
+ public:
+ void ConsumeData(size_t num_bytes) {
+ char buffer[1024];
+ ASSERT_GT(QUIC_ARRAYSIZE(buffer), num_bytes);
+ struct iovec iov;
+ iov.iov_base = buffer;
+ iov.iov_len = num_bytes;
+ ASSERT_EQ(static_cast<int>(num_bytes), sequencer_->Readv(&iov, 1));
+ }
+
+ protected:
+ QuicStreamSequencerTest()
+ : stream_(), sequencer_(new QuicStreamSequencer(&stream_)) {}
+
+ // Verify that the data in first region match with the expected[0].
+ bool VerifyReadableRegion(const std::vector<std::string>& expected) {
+ return VerifyReadableRegion(*sequencer_, expected);
+ }
+
+ // Verify that the data in each of currently readable regions match with each
+ // item given in |expected|.
+ bool VerifyReadableRegions(const std::vector<std::string>& expected) {
+ return VerifyReadableRegions(*sequencer_, expected);
+ }
+
+ bool VerifyIovecs(iovec* iovecs,
+ size_t num_iovecs,
+ const std::vector<std::string>& expected) {
+ return VerifyIovecs(*sequencer_, iovecs, num_iovecs, expected);
+ }
+
+ bool VerifyReadableRegion(const QuicStreamSequencer& sequencer,
+ const std::vector<std::string>& expected) {
+ iovec iovecs[1];
+ if (sequencer.GetReadableRegions(iovecs, 1)) {
+ return (VerifyIovecs(sequencer, iovecs, 1,
+ std::vector<std::string>{expected[0]}));
+ }
+ return false;
+ }
+
+ // Verify that the data in each of currently readable regions match with each
+ // item given in |expected|.
+ bool VerifyReadableRegions(const QuicStreamSequencer& sequencer,
+ const std::vector<std::string>& expected) {
+ iovec iovecs[5];
+ size_t num_iovecs =
+ sequencer.GetReadableRegions(iovecs, QUIC_ARRAYSIZE(iovecs));
+ return VerifyReadableRegion(sequencer, expected) &&
+ VerifyIovecs(sequencer, iovecs, num_iovecs, expected);
+ }
+
+ bool VerifyIovecs(const QuicStreamSequencer& sequencer,
+ iovec* iovecs,
+ size_t num_iovecs,
+ const std::vector<std::string>& expected) {
+ int start_position = 0;
+ for (size_t i = 0; i < num_iovecs; ++i) {
+ if (!VerifyIovec(iovecs[i],
+ expected[0].substr(start_position, iovecs[i].iov_len))) {
+ return false;
+ }
+ start_position += iovecs[i].iov_len;
+ }
+ return true;
+ }
+
+ bool VerifyIovec(const iovec& iovec, QuicStringPiece expected) {
+ if (iovec.iov_len != expected.length()) {
+ QUIC_LOG(ERROR) << "Invalid length: " << iovec.iov_len << " vs "
+ << expected.length();
+ return false;
+ }
+ if (memcmp(iovec.iov_base, expected.data(), expected.length()) != 0) {
+ QUIC_LOG(ERROR) << "Invalid data: " << static_cast<char*>(iovec.iov_base)
+ << " vs " << expected;
+ return false;
+ }
+ return true;
+ }
+
+ void OnFinFrame(QuicStreamOffset byte_offset, const char* data) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.offset = byte_offset;
+ frame.data_buffer = data;
+ frame.data_length = strlen(data);
+ frame.fin = true;
+ sequencer_->OnStreamFrame(frame);
+ }
+
+ void OnFrame(QuicStreamOffset byte_offset, const char* data) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.offset = byte_offset;
+ frame.data_buffer = data;
+ frame.data_length = strlen(data);
+ frame.fin = false;
+ sequencer_->OnStreamFrame(frame);
+ }
+
+ size_t NumBufferedBytes() {
+ return QuicStreamSequencerPeer::GetNumBufferedBytes(sequencer_.get());
+ }
+
+ testing::StrictMock<MockStream> stream_;
+ std::unique_ptr<QuicStreamSequencer> sequencer_;
+};
+
+// TODO(rch): reorder these tests so they build on each other.
+
+TEST_F(QuicStreamSequencerTest, RejectOldFrame) {
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(3);
+ }));
+
+ OnFrame(0, "abc");
+
+ EXPECT_EQ(0u, NumBufferedBytes());
+ EXPECT_EQ(3u, sequencer_->NumBytesConsumed());
+ // Ignore this - it matches a past packet number and we should not see it
+ // again.
+ OnFrame(0, "def");
+ EXPECT_EQ(0u, NumBufferedBytes());
+}
+
+TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) {
+ EXPECT_CALL(stream_, OnDataAvailable());
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(3u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+
+ // Ignore this - it matches a buffered frame.
+ // Right now there's no checking that the payload is consistent.
+ OnFrame(0, "def");
+ EXPECT_EQ(3u, NumBufferedBytes());
+}
+
+TEST_F(QuicStreamSequencerTest, FullFrameConsumed) {
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(3);
+ }));
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(0u, NumBufferedBytes());
+ EXPECT_EQ(3u, sequencer_->NumBytesConsumed());
+}
+
+TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) {
+ sequencer_->SetBlockedUntilFlush();
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(3u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(3);
+ }));
+ sequencer_->SetUnblocked();
+ EXPECT_EQ(0u, NumBufferedBytes());
+ EXPECT_EQ(3u, sequencer_->NumBytesConsumed());
+
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(3);
+ }));
+ EXPECT_FALSE(sequencer_->IsClosed());
+ OnFinFrame(3, "def");
+ EXPECT_TRUE(sequencer_->IsClosed());
+}
+
+TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) {
+ sequencer_->SetBlockedUntilFlush();
+
+ OnFinFrame(0, "abc");
+ EXPECT_EQ(3u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(3);
+ }));
+ EXPECT_FALSE(sequencer_->IsClosed());
+ sequencer_->SetUnblocked();
+ EXPECT_TRUE(sequencer_->IsClosed());
+ EXPECT_EQ(0u, NumBufferedBytes());
+ EXPECT_EQ(3u, sequencer_->NumBytesConsumed());
+}
+
+TEST_F(QuicStreamSequencerTest, EmptyFrame) {
+ EXPECT_CALL(stream_,
+ CloseConnectionWithDetails(QUIC_EMPTY_STREAM_FRAME_NO_FIN, _));
+ OnFrame(0, "");
+ EXPECT_EQ(0u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+}
+
+TEST_F(QuicStreamSequencerTest, EmptyFinFrame) {
+ EXPECT_CALL(stream_, OnDataAvailable());
+ OnFinFrame(0, "");
+ EXPECT_EQ(0u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+}
+
+TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) {
+ EXPECT_CALL(stream_, AddBytesConsumed(2));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(2);
+ }));
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(1u, NumBufferedBytes());
+ EXPECT_EQ(2u, sequencer_->NumBytesConsumed());
+}
+
+TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) {
+ EXPECT_CALL(stream_, OnDataAvailable());
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(3u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+}
+
+TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) {
+ OnFrame(3, "abc");
+ EXPECT_EQ(3u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+}
+
+TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) {
+ // Buffer the first
+ OnFrame(6, "ghi");
+ EXPECT_EQ(3u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+ EXPECT_EQ(3u, sequencer_->NumBytesBuffered());
+ // Buffer the second
+ OnFrame(3, "def");
+ EXPECT_EQ(6u, NumBufferedBytes());
+ EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
+ EXPECT_EQ(6u, sequencer_->NumBytesBuffered());
+
+ EXPECT_CALL(stream_, AddBytesConsumed(9));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(9);
+ }));
+
+ // Now process all of them at once.
+ OnFrame(0, "abc");
+ EXPECT_EQ(9u, sequencer_->NumBytesConsumed());
+ EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
+
+ EXPECT_EQ(0u, NumBufferedBytes());
+}
+
+TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) {
+ InSequence s;
+
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(3);
+ }));
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ OnFinFrame(0, "abc");
+
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+}
+
+TEST_F(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) {
+ OnFinFrame(6, "");
+ EXPECT_EQ(6u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ OnFrame(3, "def");
+ EXPECT_CALL(stream_, AddBytesConsumed(6));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(6);
+ }));
+ EXPECT_FALSE(sequencer_->IsClosed());
+ OnFrame(0, "abc");
+ EXPECT_TRUE(sequencer_->IsClosed());
+}
+
+TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) {
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
+ ConsumeData(3);
+ }));
+ EXPECT_FALSE(sequencer_->IsClosed());
+ OnFrame(0, "abc");
+ EXPECT_TRUE(sequencer_->IsClosed());
+}
+
+TEST_F(QuicStreamSequencerTest, TerminateWithReadv) {
+ char buffer[3];
+
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ EXPECT_FALSE(sequencer_->IsClosed());
+
+ EXPECT_CALL(stream_, OnDataAvailable());
+ OnFrame(0, "abc");
+
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ iovec iov = {&buffer[0], 3};
+ int bytes_read = sequencer_->Readv(&iov, 1);
+ EXPECT_EQ(3, bytes_read);
+ EXPECT_TRUE(sequencer_->IsClosed());
+}
+
+TEST_F(QuicStreamSequencerTest, MutipleOffsets) {
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
+ OnFinFrame(5, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
+ OnFinFrame(1, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+}
+
+class QuicSequencerRandomTest : public QuicStreamSequencerTest {
+ public:
+ typedef std::pair<int, std::string> Frame;
+ typedef std::vector<Frame> FrameList;
+
+ void CreateFrames() {
+ int payload_size = QUIC_ARRAYSIZE(kPayload) - 1;
+ int remaining_payload = payload_size;
+ while (remaining_payload != 0) {
+ int size = std::min(OneToN(6), remaining_payload);
+ int index = payload_size - remaining_payload;
+ list_.push_back(
+ std::make_pair(index, std::string(kPayload + index, size)));
+ remaining_payload -= size;
+ }
+ }
+
+ QuicSequencerRandomTest() {
+ uint64_t seed = QuicRandom::GetInstance()->RandUint64();
+ QUIC_LOG(INFO) << "**** The current seed is " << seed << " ****";
+ random_.set_seed(seed);
+
+ CreateFrames();
+ }
+
+ int OneToN(int n) { return random_.RandUint64() % n + 1; }
+
+ void ReadAvailableData() {
+ // Read all available data
+ char output[QUIC_ARRAYSIZE(kPayload) + 1];
+ iovec iov;
+ iov.iov_base = output;
+ iov.iov_len = QUIC_ARRAYSIZE(output);
+ int bytes_read = sequencer_->Readv(&iov, 1);
+ EXPECT_NE(0, bytes_read);
+ output_.append(output, bytes_read);
+ }
+
+ std::string output_;
+ // Data which peek at using GetReadableRegion if we back up.
+ std::string peeked_;
+ SimpleRandom random_;
+ FrameList list_;
+};
+
+// All frames are processed as soon as we have sequential data.
+// Infinite buffering, so all frames are acked right away.
+TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) {
+ EXPECT_CALL(stream_, OnDataAvailable())
+ .Times(AnyNumber())
+ .WillRepeatedly(
+ Invoke(this, &QuicSequencerRandomTest::ReadAvailableData));
+ QuicByteCount total_bytes_consumed = 0;
+ EXPECT_CALL(stream_, AddBytesConsumed(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(
+ testing::Invoke([&total_bytes_consumed](QuicByteCount bytes) {
+ total_bytes_consumed += bytes;
+ }));
+
+ while (!list_.empty()) {
+ int index = OneToN(list_.size()) - 1;
+ QUIC_LOG(ERROR) << "Sending index " << index << " " << list_[index].second;
+ OnFrame(list_[index].first, list_[index].second.data());
+
+ list_.erase(list_.begin() + index);
+ }
+
+ ASSERT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, output_.size());
+ EXPECT_EQ(kPayload, output_);
+ EXPECT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, total_bytes_consumed);
+}
+
+TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingBackup) {
+ char buffer[10];
+ iovec iov[2];
+ iov[0].iov_base = &buffer[0];
+ iov[0].iov_len = 5;
+ iov[1].iov_base = &buffer[5];
+ iov[1].iov_len = 5;
+
+ EXPECT_CALL(stream_, OnDataAvailable()).Times(AnyNumber());
+ QuicByteCount total_bytes_consumed = 0;
+ EXPECT_CALL(stream_, AddBytesConsumed(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(
+ testing::Invoke([&total_bytes_consumed](QuicByteCount bytes) {
+ total_bytes_consumed += bytes;
+ }));
+
+ while (output_.size() != QUIC_ARRAYSIZE(kPayload) - 1) {
+ if (!list_.empty() && OneToN(2) == 1) { // Send data
+ int index = OneToN(list_.size()) - 1;
+ OnFrame(list_[index].first, list_[index].second.data());
+ list_.erase(list_.begin() + index);
+ } else { // Read data
+ bool has_bytes = sequencer_->HasBytesToRead();
+ iovec peek_iov[20];
+ int iovs_peeked = sequencer_->GetReadableRegions(peek_iov, 20);
+ if (has_bytes) {
+ ASSERT_LT(0, iovs_peeked);
+ ASSERT_TRUE(sequencer_->GetReadableRegion(peek_iov));
+ } else {
+ ASSERT_EQ(0, iovs_peeked);
+ ASSERT_FALSE(sequencer_->GetReadableRegion(peek_iov));
+ }
+ int total_bytes_to_peek = QUIC_ARRAYSIZE(buffer);
+ for (int i = 0; i < iovs_peeked; ++i) {
+ int bytes_to_peek =
+ std::min<int>(peek_iov[i].iov_len, total_bytes_to_peek);
+ peeked_.append(static_cast<char*>(peek_iov[i].iov_base), bytes_to_peek);
+ total_bytes_to_peek -= bytes_to_peek;
+ if (total_bytes_to_peek == 0) {
+ break;
+ }
+ }
+ int bytes_read = sequencer_->Readv(iov, 2);
+ output_.append(buffer, bytes_read);
+ ASSERT_EQ(output_.size(), peeked_.size());
+ }
+ }
+ EXPECT_EQ(std::string(kPayload), output_);
+ EXPECT_EQ(std::string(kPayload), peeked_);
+ EXPECT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, total_bytes_consumed);
+}
+
+// Same as above, just using a different method for reading.
+TEST_F(QuicStreamSequencerTest, MarkConsumed) {
+ InSequence s;
+ EXPECT_CALL(stream_, OnDataAvailable());
+
+ OnFrame(0, "abc");
+ OnFrame(3, "def");
+ OnFrame(6, "ghi");
+
+ // abcdefghi buffered.
+ EXPECT_EQ(9u, sequencer_->NumBytesBuffered());
+
+ // Peek into the data.
+ std::vector<std::string> expected = {"abcdefghi"};
+ ASSERT_TRUE(VerifyReadableRegions(expected));
+
+ // Consume 1 byte.
+ EXPECT_CALL(stream_, AddBytesConsumed(1));
+ sequencer_->MarkConsumed(1);
+ // Verify data.
+ std::vector<std::string> expected2 = {"bcdefghi"};
+ ASSERT_TRUE(VerifyReadableRegions(expected2));
+ EXPECT_EQ(8u, sequencer_->NumBytesBuffered());
+
+ // Consume 2 bytes.
+ EXPECT_CALL(stream_, AddBytesConsumed(2));
+ sequencer_->MarkConsumed(2);
+ // Verify data.
+ std::vector<std::string> expected3 = {"defghi"};
+ ASSERT_TRUE(VerifyReadableRegions(expected3));
+ EXPECT_EQ(6u, sequencer_->NumBytesBuffered());
+
+ // Consume 5 bytes.
+ EXPECT_CALL(stream_, AddBytesConsumed(5));
+ sequencer_->MarkConsumed(5);
+ // Verify data.
+ std::vector<std::string> expected4{"i"};
+ ASSERT_TRUE(VerifyReadableRegions(expected4));
+ EXPECT_EQ(1u, sequencer_->NumBytesBuffered());
+}
+
+TEST_F(QuicStreamSequencerTest, MarkConsumedError) {
+ EXPECT_CALL(stream_, OnDataAvailable());
+
+ OnFrame(0, "abc");
+ OnFrame(9, "jklmnopqrstuvwxyz");
+
+ // Peek into the data. Only the first chunk should be readable because of the
+ // missing data.
+ std::vector<std::string> expected{"abc"};
+ ASSERT_TRUE(VerifyReadableRegions(expected));
+
+ // Now, attempt to mark consumed more data than was readable and expect the
+ // stream to be closed.
+ EXPECT_CALL(stream_, Reset(QUIC_ERROR_PROCESSING_STREAM));
+ EXPECT_QUIC_BUG(sequencer_->MarkConsumed(4),
+ "Invalid argument to MarkConsumed."
+ " expect to consume: 4, but not enough bytes available.");
+}
+
+TEST_F(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) {
+ InSequence s;
+ EXPECT_CALL(stream_, OnDataAvailable());
+
+ OnFrame(0, "abc");
+ OnFrame(3, "def");
+ // Missing packet: 6, ghi.
+ OnFrame(9, "jkl");
+
+ std::vector<std::string> expected = {"abcdef"};
+ ASSERT_TRUE(VerifyReadableRegions(expected));
+
+ EXPECT_CALL(stream_, AddBytesConsumed(6));
+ sequencer_->MarkConsumed(6);
+}
+
+TEST_F(QuicStreamSequencerTest, Move) {
+ InSequence s;
+ EXPECT_CALL(stream_, OnDataAvailable());
+
+ OnFrame(0, "abc");
+ OnFrame(3, "def");
+ OnFrame(6, "ghi");
+
+ // abcdefghi buffered.
+ EXPECT_EQ(9u, sequencer_->NumBytesBuffered());
+
+ // Peek into the data.
+ std::vector<std::string> expected = {"abcdefghi"};
+ ASSERT_TRUE(VerifyReadableRegions(expected));
+
+ QuicStreamSequencer sequencer2(std::move(*sequencer_));
+ ASSERT_TRUE(VerifyReadableRegions(sequencer2, expected));
+}
+
+TEST_F(QuicStreamSequencerTest, OverlappingFramesReceived) {
+ // The peer should never send us non-identical stream frames which contain
+ // overlapping byte ranges - if they do, we close the connection.
+ QuicStreamId id = 1;
+
+ QuicStreamFrame frame1(id, false, 1, QuicStringPiece("hello"));
+ sequencer_->OnStreamFrame(frame1);
+
+ QuicStreamFrame frame2(id, false, 2, QuicStringPiece("hello"));
+ EXPECT_CALL(stream_,
+ CloseConnectionWithDetails(QUIC_OVERLAPPING_STREAM_DATA, _))
+ .Times(0);
+ sequencer_->OnStreamFrame(frame2);
+}
+
+TEST_F(QuicStreamSequencerTest, DataAvailableOnOverlappingFrames) {
+ QuicStreamId id = 1;
+ const std::string data(1000, '.');
+
+ // Received [0, 1000).
+ QuicStreamFrame frame1(id, false, 0, data);
+ EXPECT_CALL(stream_, OnDataAvailable());
+ sequencer_->OnStreamFrame(frame1);
+ // Consume [0, 500).
+ EXPECT_CALL(stream_, AddBytesConsumed(500));
+ QuicStreamSequencerTest::ConsumeData(500);
+ EXPECT_EQ(500u, sequencer_->NumBytesConsumed());
+ EXPECT_EQ(500u, sequencer_->NumBytesBuffered());
+
+ // Received [500, 1500).
+ QuicStreamFrame frame2(id, false, 500, data);
+ // Do not call OnDataAvailable as there are readable bytes left in the buffer.
+ EXPECT_CALL(stream_, OnDataAvailable()).Times(0);
+ sequencer_->OnStreamFrame(frame2);
+ // Consume [1000, 1500).
+ EXPECT_CALL(stream_, AddBytesConsumed(1000));
+ QuicStreamSequencerTest::ConsumeData(1000);
+ EXPECT_EQ(1500u, sequencer_->NumBytesConsumed());
+ EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
+
+ // Received [1498, 1503).
+ QuicStreamFrame frame3(id, false, 1498, QuicStringPiece("hello"));
+ EXPECT_CALL(stream_, OnDataAvailable());
+ sequencer_->OnStreamFrame(frame3);
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ QuicStreamSequencerTest::ConsumeData(3);
+ EXPECT_EQ(1503u, sequencer_->NumBytesConsumed());
+ EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
+
+ // Received [1000, 1005).
+ QuicStreamFrame frame4(id, false, 1000, QuicStringPiece("hello"));
+ EXPECT_CALL(stream_, OnDataAvailable()).Times(0);
+ sequencer_->OnStreamFrame(frame4);
+ EXPECT_EQ(1503u, sequencer_->NumBytesConsumed());
+ EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
+}
+
+TEST_F(QuicStreamSequencerTest, OnDataAvailableWhenReadableBytesIncrease) {
+ sequencer_->set_level_triggered(true);
+ QuicStreamId id = 1;
+
+ // Received [0, 5).
+ QuicStreamFrame frame1(id, false, 0, "hello");
+ EXPECT_CALL(stream_, OnDataAvailable());
+ sequencer_->OnStreamFrame(frame1);
+ EXPECT_EQ(5u, sequencer_->NumBytesBuffered());
+
+ // Without consuming the buffer bytes, continue receiving [5, 11).
+ QuicStreamFrame frame2(id, false, 5, " world");
+ // OnDataAvailable should still be called because there are more data to read.
+ EXPECT_CALL(stream_, OnDataAvailable());
+ sequencer_->OnStreamFrame(frame2);
+ EXPECT_EQ(11u, sequencer_->NumBytesBuffered());
+
+ // Without consuming the buffer bytes, continue receiving [12, 13).
+ QuicStreamFrame frame3(id, false, 5, "a");
+ // OnDataAvailable shouldn't be called becasue there are still only 11 bytes
+ // available.
+ EXPECT_CALL(stream_, OnDataAvailable()).Times(0);
+ sequencer_->OnStreamFrame(frame3);
+ EXPECT_EQ(11u, sequencer_->NumBytesBuffered());
+}
+
+TEST_F(QuicStreamSequencerTest, ReadSingleFrame) {
+ EXPECT_CALL(stream_, OnDataAvailable());
+ OnFrame(0u, "abc");
+ std::string actual;
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ sequencer_->Read(&actual);
+ EXPECT_EQ("abc", actual);
+ EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
+}
+
+TEST_F(QuicStreamSequencerTest, ReadMultipleFramesWithMissingFrame) {
+ EXPECT_CALL(stream_, OnDataAvailable());
+ OnFrame(0u, "abc");
+ OnFrame(3u, "def");
+ OnFrame(6u, "ghi");
+ OnFrame(10u, "xyz"); // Byte 9 is missing.
+ std::string actual;
+ EXPECT_CALL(stream_, AddBytesConsumed(9));
+ sequencer_->Read(&actual);
+ EXPECT_EQ("abcdefghi", actual);
+ EXPECT_EQ(3u, sequencer_->NumBytesBuffered());
+}
+
+TEST_F(QuicStreamSequencerTest, ReadAndAppendToString) {
+ EXPECT_CALL(stream_, OnDataAvailable());
+ OnFrame(0u, "def");
+ OnFrame(3u, "ghi");
+ std::string actual = "abc";
+ EXPECT_CALL(stream_, AddBytesConsumed(6));
+ sequencer_->Read(&actual);
+ EXPECT_EQ("abcdefghi", actual);
+ EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
+}
+
+TEST_F(QuicStreamSequencerTest, StopReading) {
+ EXPECT_CALL(stream_, OnDataAvailable()).Times(0);
+ EXPECT_CALL(stream_, OnFinRead());
+
+ EXPECT_CALL(stream_, AddBytesConsumed(0));
+ sequencer_->StopReading();
+
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ OnFrame(0u, "abc");
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ OnFrame(3u, "def");
+ EXPECT_CALL(stream_, AddBytesConsumed(3));
+ OnFinFrame(6u, "ghi");
+}
+
+TEST_F(QuicStreamSequencerTest, StopReadingWithLevelTriggered) {
+ if (GetQuicReloadableFlag(quic_stop_reading_when_level_triggered)) {
+ EXPECT_CALL(stream_, AddBytesConsumed(0));
+ EXPECT_CALL(stream_, AddBytesConsumed(3)).Times(3);
+ EXPECT_CALL(stream_, OnDataAvailable()).Times(0);
+ EXPECT_CALL(stream_, OnFinRead());
+ } else {
+ EXPECT_CALL(stream_, AddBytesConsumed(0));
+ EXPECT_CALL(stream_, OnDataAvailable()).Times(3);
+ }
+
+ sequencer_->set_level_triggered(true);
+ sequencer_->StopReading();
+
+ OnFrame(0u, "abc");
+ OnFrame(3u, "def");
+ OnFinFrame(6u, "ghi");
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..d1b442ed888
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_stream_test.cc
@@ -0,0 +1,1549 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::InSequence;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+const char kData1[] = "FooAndBar";
+const char kData2[] = "EepAndBaz";
+const size_t kDataLen = 9;
+
+class TestStream : public QuicStream {
+ public:
+ TestStream(QuicStreamId id, QuicSession* session, StreamType type)
+ : QuicStream(id, session, /*is_static=*/false, type) {}
+
+ TestStream(PendingStream pending, StreamType type)
+ : QuicStream(std::move(pending), type) {}
+
+ void OnDataAvailable() override {}
+
+ MOCK_METHOD0(OnCanWriteNewData, void());
+
+ using QuicStream::CanWriteNewData;
+ using QuicStream::CanWriteNewDataAfterData;
+ using QuicStream::CloseWriteSide;
+ using QuicStream::fin_buffered;
+ using QuicStream::OnClose;
+ using QuicStream::WriteMemSlices;
+ using QuicStream::WriteOrBufferData;
+ using QuicStream::WritevData;
+
+ private:
+ std::string data_;
+};
+
+class QuicStreamTestBase : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ QuicStreamTestBase()
+ : initial_flow_control_window_bytes_(kMaxOutgoingPacketSize),
+ zero_(QuicTime::Delta::Zero()),
+ supported_versions_(AllSupportedVersions()) {}
+
+ void Initialize() {
+ ParsedQuicVersionVector version_vector;
+ version_vector.push_back(GetParam());
+ connection_ = new StrictMock<MockQuicConnection>(
+ &helper_, &alarm_factory_, Perspective::IS_SERVER, version_vector);
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_);
+
+ // New streams rely on having the peer's flow control receive window
+ // negotiated in the config.
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ session_->config(), initial_flow_control_window_bytes_);
+
+ stream_ = new TestStream(kTestStreamId, session_.get(), BIDIRECTIONAL);
+ EXPECT_NE(nullptr, stream_);
+ // session_ now owns stream_.
+ session_->ActivateStream(QuicWrapUnique(stream_));
+ // Ignore resetting when session_ is terminated.
+ EXPECT_CALL(*session_, SendRstStream(kTestStreamId, _, _))
+ .Times(AnyNumber());
+ write_blocked_list_ =
+ QuicSessionPeer::GetWriteBlockedStreams(session_.get());
+ }
+
+ bool fin_sent() { return stream_->fin_sent(); }
+ bool rst_sent() { return stream_->rst_sent(); }
+
+ void set_initial_flow_control_window_bytes(uint32_t val) {
+ initial_flow_control_window_bytes_ = val;
+ }
+
+ bool HasWriteBlockedStreams() {
+ return write_blocked_list_->HasWriteBlockedSpecialStream() ||
+ write_blocked_list_->HasWriteBlockedDataStreams();
+ }
+
+ QuicConsumedData CloseStreamOnWriteError(QuicStream* /*stream*/,
+ QuicStreamId id,
+ size_t /*write_length*/,
+ QuicStreamOffset /*offset*/,
+ StreamSendingState /*state*/) {
+ session_->CloseStream(id);
+ return QuicConsumedData(1, false);
+ }
+
+ bool ClearControlFrame(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ bool ClearResetStreamFrame(const QuicFrame& frame) {
+ EXPECT_EQ(RST_STREAM_FRAME, frame.type);
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ bool ClearStopSendingFrame(const QuicFrame& frame) {
+ EXPECT_EQ(STOP_SENDING_FRAME, frame.type);
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ protected:
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnection* connection_;
+ std::unique_ptr<MockQuicSession> session_;
+ TestStream* stream_;
+ QuicWriteBlockedList* write_blocked_list_;
+ uint32_t initial_flow_control_window_bytes_;
+ QuicTime::Delta zero_;
+ ParsedQuicVersionVector supported_versions_;
+ const QuicStreamId kTestStreamId =
+ QuicUtils::GetHeadersStreamId(GetParam().transport_version) +
+ QuicUtils::StreamIdDelta(GetParam().transport_version);
+};
+
+// Non parameterized QuicStreamTest used for tests that do not
+// have any dependencies on the quic version.
+class QuicStreamTest : public QuicStreamTestBase {};
+
+// Index value of 1 has the test run with supported-version[1], which is some
+// version OTHER than 99.
+INSTANTIATE_TEST_SUITE_P(
+ QuicStreamTests,
+ QuicStreamTest,
+ ::testing::ValuesIn(ParsedVersionOfIndex(AllSupportedVersions(), 1)));
+
+// Make a parameterized version of the QuicStreamTest for those tests
+// that need to differentiate based on version number.
+class QuicParameterizedStreamTest : public QuicStreamTestBase {};
+INSTANTIATE_TEST_SUITE_P(QuicParameterizedStreamTests,
+ QuicParameterizedStreamTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicStreamTest, PendingStreamTooMuchData) {
+ Initialize();
+
+ PendingStream pending(kTestStreamId + 2, session_.get());
+ // Receive a stream frame that violates flow control: the byte offset is
+ // higher than the receive window offset.
+ QuicStreamFrame frame(kTestStreamId + 2, false,
+ kInitialSessionFlowControlWindowForTest + 1,
+ QuicStringPiece("."));
+
+ // Stream should not accept the frame, and the connection should be closed.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _));
+ pending.OnStreamFrame(frame);
+}
+
+TEST_P(QuicStreamTest, PendingStreamTooMuchDataInRstStream) {
+ Initialize();
+
+ PendingStream pending(kTestStreamId + 2, session_.get());
+ // Receive a rst stream frame that violates flow control: the byte offset is
+ // higher than the receive window offset.
+ QuicRstStreamFrame frame(kInvalidControlFrameId, kTestStreamId + 2,
+ QUIC_STREAM_CANCELLED,
+ kInitialSessionFlowControlWindowForTest + 1);
+
+ // Pending stream should not accept the frame, and the connection should be
+ // closed.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _));
+ pending.OnRstStreamFrame(frame);
+}
+
+TEST_P(QuicStreamTest, PendingStreamRstStream) {
+ Initialize();
+
+ PendingStream pending(kTestStreamId + 2, session_.get());
+ QuicStreamOffset final_byte_offset = 7;
+ QuicRstStreamFrame frame(kInvalidControlFrameId, kTestStreamId + 2,
+ QUIC_STREAM_CANCELLED, final_byte_offset);
+
+ // Pending stream should accept the frame and not close the connection.
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ pending.OnRstStreamFrame(frame);
+}
+
+TEST_P(QuicStreamTest, FromPendingStream) {
+ Initialize();
+
+ PendingStream pending(kTestStreamId + 2, session_.get());
+
+ QuicStreamFrame frame(kTestStreamId + 2, false, 2, QuicStringPiece("."));
+ pending.OnStreamFrame(frame);
+ pending.OnStreamFrame(frame);
+ QuicStreamFrame frame2(kTestStreamId + 2, true, 3, QuicStringPiece("."));
+ pending.OnStreamFrame(frame2);
+
+ TestStream stream(std::move(pending), StreamType::READ_UNIDIRECTIONAL);
+ EXPECT_EQ(3, stream.num_frames_received());
+ EXPECT_EQ(3u, stream.stream_bytes_read());
+ EXPECT_EQ(1, stream.num_duplicate_frames_received());
+ EXPECT_EQ(true, stream.fin_received());
+ EXPECT_EQ(frame2.offset + 1,
+ stream.flow_controller()->highest_received_byte_offset());
+ EXPECT_EQ(frame2.offset + 1,
+ session_->flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicStreamTest, FromPendingStreamThenData) {
+ Initialize();
+
+ PendingStream pending(kTestStreamId + 2, session_.get());
+
+ QuicStreamFrame frame(kTestStreamId + 2, false, 2, QuicStringPiece("."));
+ pending.OnStreamFrame(frame);
+
+ auto stream =
+ new TestStream(std::move(pending), StreamType::READ_UNIDIRECTIONAL);
+ session_->ActivateStream(QuicWrapUnique(stream));
+
+ QuicStreamFrame frame2(kTestStreamId + 2, true, 3, QuicStringPiece("."));
+ stream->OnStreamFrame(frame2);
+
+ EXPECT_EQ(2, stream->num_frames_received());
+ EXPECT_EQ(2u, stream->stream_bytes_read());
+ EXPECT_EQ(true, stream->fin_received());
+ EXPECT_EQ(frame2.offset + 1,
+ stream->flow_controller()->highest_received_byte_offset());
+ EXPECT_EQ(frame2.offset + 1,
+ session_->flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicStreamTest, WriteAllData) {
+ Initialize();
+
+ size_t length =
+ 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->transport_version(), PACKET_8BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0u);
+ connection_->SetMaxPacketLength(length);
+
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(Invoke(&(MockQuicSession::ConsumeData)));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_FALSE(HasWriteBlockedStreams());
+}
+
+TEST_P(QuicStreamTest, NoBlockingIfNoDataOrFin) {
+ Initialize();
+
+ // Write no data and no fin. If we consume nothing we should not be write
+ // blocked.
+ EXPECT_QUIC_BUG(stream_->WriteOrBufferData(QuicStringPiece(), false, nullptr),
+ "");
+ EXPECT_FALSE(HasWriteBlockedStreams());
+}
+
+TEST_P(QuicStreamTest, BlockIfOnlySomeDataConsumed) {
+ Initialize();
+
+ // Write some data and no fin. If we consume some but not all of the data,
+ // we should be write blocked a not all the data was consumed.
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 1u, 0u,
+ NO_FIN);
+ }));
+ stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), false, nullptr);
+ ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
+ EXPECT_EQ(1u, stream_->BufferedDataBytes());
+}
+
+TEST_P(QuicStreamTest, BlockIfFinNotConsumedWithData) {
+ Initialize();
+
+ // Write some data and no fin. If we consume all the data but not the fin,
+ // we should be write blocked because the fin was not consumed.
+ // (This should never actually happen as the fin should be sent out with the
+ // last data)
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 2u, 0u,
+ NO_FIN);
+ }));
+ stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), true, nullptr);
+ ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
+}
+
+TEST_P(QuicStreamTest, BlockIfSoloFinNotConsumed) {
+ Initialize();
+
+ // Write no data and a fin. If we consume nothing we should be write blocked,
+ // as the fin was not consumed.
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(QuicStringPiece(), true, nullptr);
+ ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
+}
+
+TEST_P(QuicStreamTest, CloseOnPartialWrite) {
+ Initialize();
+
+ // Write some data and no fin. However, while writing the data
+ // close the stream and verify that MarkConnectionLevelWriteBlocked does not
+ // crash with an unknown stream.
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(Invoke(this, &QuicStreamTest::CloseStreamOnWriteError));
+ stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), false, nullptr);
+ ASSERT_EQ(0u, write_blocked_list_->NumBlockedStreams());
+}
+
+TEST_P(QuicStreamTest, WriteOrBufferData) {
+ Initialize();
+
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length =
+ 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ connection_->transport_version(), PACKET_8BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ !kIncludeDiversificationNonce, PACKET_4BYTE_PACKET_NUMBER,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0,
+ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0u);
+ connection_->SetMaxPacketLength(length);
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(),
+ kDataLen - 1, 0u, NO_FIN);
+ }));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_EQ(1u, stream_->BufferedDataBytes());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Queue a bytes_consumed write.
+ stream_->WriteOrBufferData(kData2, false, nullptr);
+ EXPECT_EQ(10u, stream_->BufferedDataBytes());
+ // Make sure we get the tail of the first write followed by the bytes_consumed
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(),
+ kDataLen - 1, kDataLen - 1, NO_FIN);
+ }));
+ stream_->OnCanWrite();
+
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 2u,
+ 2 * kDataLen - 2, NO_FIN);
+ }));
+ stream_->OnCanWrite();
+}
+
+TEST_P(QuicStreamTest, WriteOrBufferDataReachStreamLimit) {
+ Initialize();
+ std::string data("aaaaa");
+ QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - data.length(),
+ stream_);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Invoke(&(MockQuicSession::ConsumeData)));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _));
+ EXPECT_QUIC_BUG(stream_->WriteOrBufferData("a", false, nullptr),
+ "Write too many data via stream");
+}
+
+TEST_P(QuicStreamTest, ConnectionCloseAfterStreamClose) {
+ Initialize();
+
+ QuicStreamPeer::CloseReadSide(stream_);
+ stream_->CloseWriteSide();
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
+ EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error());
+ stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR,
+ ConnectionCloseSource::FROM_SELF);
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
+ EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error());
+}
+
+TEST_P(QuicStreamTest, RstAlwaysSentIfNoFinSent) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if no FIN has been sent, we send a RST.
+
+ Initialize();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Write some data, with no FIN.
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 1u, 0u,
+ NO_FIN);
+ }));
+ stream_->WriteOrBufferData(QuicStringPiece(kData1, 1), false, nullptr);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Now close the stream, and expect that we send a RST.
+ EXPECT_CALL(*session_, SendRstStream(_, _, _));
+ stream_->OnClose();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+}
+
+TEST_P(QuicStreamTest, RstNotSentIfFinSent) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if a FIN has been sent, we don't also send a RST.
+
+ Initialize();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Write some data, with FIN.
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 1u, 0u,
+ FIN);
+ }));
+ stream_->WriteOrBufferData(QuicStringPiece(kData1, 1), true, nullptr);
+ EXPECT_TRUE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Now close the stream, and expect that we do not send a RST.
+ stream_->OnClose();
+ EXPECT_TRUE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+}
+
+TEST_P(QuicStreamTest, OnlySendOneRst) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if a stream sends a RST, it doesn't send an additional RST during
+ // OnClose() (this shouldn't be harmful, but we shouldn't do it anyway...)
+
+ Initialize();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Reset the stream.
+ const int expected_resets = 1;
+ EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(expected_resets);
+ stream_->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+
+ // Now close the stream (any further resets being sent would break the
+ // expectation above).
+ stream_->OnClose();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+}
+
+TEST_P(QuicStreamTest, StreamFlowControlMultipleWindowUpdates) {
+ set_initial_flow_control_window_bytes(1000);
+
+ Initialize();
+
+ // If we receive multiple WINDOW_UPDATES (potentially out of order), then we
+ // want to make sure we latch the largest offset we see.
+
+ // Initially should be default.
+ EXPECT_EQ(
+ initial_flow_control_window_bytes_,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+
+ // Check a single WINDOW_UPDATE results in correct offset.
+ QuicWindowUpdateFrame window_update_1(kInvalidControlFrameId, stream_->id(),
+ 1234);
+ stream_->OnWindowUpdateFrame(window_update_1);
+ EXPECT_EQ(
+ window_update_1.byte_offset,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+
+ // Now send a few more WINDOW_UPDATES and make sure that only the largest is
+ // remembered.
+ QuicWindowUpdateFrame window_update_2(kInvalidControlFrameId, stream_->id(),
+ 1);
+ QuicWindowUpdateFrame window_update_3(kInvalidControlFrameId, stream_->id(),
+ 9999);
+ QuicWindowUpdateFrame window_update_4(kInvalidControlFrameId, stream_->id(),
+ 5678);
+ stream_->OnWindowUpdateFrame(window_update_2);
+ stream_->OnWindowUpdateFrame(window_update_3);
+ stream_->OnWindowUpdateFrame(window_update_4);
+ EXPECT_EQ(
+ window_update_3.byte_offset,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+}
+
+TEST_P(QuicStreamTest, FrameStats) {
+ Initialize();
+
+ EXPECT_EQ(0, stream_->num_frames_received());
+ EXPECT_EQ(0, stream_->num_duplicate_frames_received());
+ QuicStreamFrame frame(stream_->id(), false, 0, QuicStringPiece("."));
+ stream_->OnStreamFrame(frame);
+ EXPECT_EQ(1, stream_->num_frames_received());
+ EXPECT_EQ(0, stream_->num_duplicate_frames_received());
+ stream_->OnStreamFrame(frame);
+ EXPECT_EQ(2, stream_->num_frames_received());
+ EXPECT_EQ(1, stream_->num_duplicate_frames_received());
+}
+
+// Verify that when we receive a packet which violates flow control (i.e. sends
+// too much data on the stream) that the stream sequencer never sees this frame,
+// as we check for violation and close the connection early.
+TEST_P(QuicStreamTest, StreamSequencerNeverSeesPacketsViolatingFlowControl) {
+ Initialize();
+
+ // Receive a stream frame that violates flow control: the byte offset is
+ // higher than the receive window offset.
+ QuicStreamFrame frame(stream_->id(), false,
+ kInitialSessionFlowControlWindowForTest + 1,
+ QuicStringPiece("."));
+ EXPECT_GT(frame.offset, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream should not accept the frame, and the connection should be closed.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _));
+ stream_->OnStreamFrame(frame);
+}
+
+// Verify that after the consumer calls StopReading(), the stream still sends
+// flow control updates.
+TEST_P(QuicStreamTest, StopReadingSendsFlowControl) {
+ Initialize();
+
+ stream_->StopReading();
+
+ // Connection should not get terminated due to flow control errors.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _))
+ .Times(0);
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Invoke(this, &QuicStreamTest::ClearControlFrame));
+
+ std::string data(1000, 'x');
+ for (QuicStreamOffset offset = 0;
+ offset < 2 * kInitialStreamFlowControlWindowForTest;
+ offset += data.length()) {
+ QuicStreamFrame frame(stream_->id(), false, offset, data);
+ stream_->OnStreamFrame(frame);
+ }
+ EXPECT_LT(
+ kInitialStreamFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowOffset(stream_->flow_controller()));
+}
+
+TEST_P(QuicStreamTest, FinalByteOffsetFromFin) {
+ Initialize();
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+
+ QuicStreamFrame stream_frame_no_fin(stream_->id(), false, 1234,
+ QuicStringPiece("."));
+ stream_->OnStreamFrame(stream_frame_no_fin);
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+
+ QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234,
+ QuicStringPiece("."));
+ stream_->OnStreamFrame(stream_frame_with_fin);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
+TEST_P(QuicStreamTest, FinalByteOffsetFromRst) {
+ Initialize();
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
+TEST_P(QuicStreamTest, InvalidFinalByteOffsetFromRst) {
+ Initialize();
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 0xFFFFFFFFFFFF);
+ // Stream should not accept the frame, and the connection should be closed.
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _));
+ stream_->OnStreamReset(rst_frame);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+ stream_->OnClose();
+}
+
+TEST_P(QuicStreamTest, FinalByteOffsetFromZeroLengthStreamFrame) {
+ // When receiving Trailers, an empty stream frame is created with the FIN set,
+ // and is passed to OnStreamFrame. The Trailers may be sent in advance of
+ // queued body bytes being sent, and thus the final byte offset may exceed
+ // current flow control limits. Flow control should only be concerned with
+ // data that has actually been sent/received, so verify that flow control
+ // ignores such a stream frame.
+ Initialize();
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+ const QuicStreamOffset kByteOffsetExceedingFlowControlWindow =
+ kInitialSessionFlowControlWindowForTest + 1;
+ const QuicStreamOffset current_stream_flow_control_offset =
+ QuicFlowControllerPeer::ReceiveWindowOffset(stream_->flow_controller());
+ const QuicStreamOffset current_connection_flow_control_offset =
+ QuicFlowControllerPeer::ReceiveWindowOffset(session_->flow_controller());
+ ASSERT_GT(kByteOffsetExceedingFlowControlWindow,
+ current_stream_flow_control_offset);
+ ASSERT_GT(kByteOffsetExceedingFlowControlWindow,
+ current_connection_flow_control_offset);
+ QuicStreamFrame zero_length_stream_frame_with_fin(
+ stream_->id(), /*fin=*/true, kByteOffsetExceedingFlowControlWindow,
+ QuicStringPiece());
+ EXPECT_EQ(0, zero_length_stream_frame_with_fin.data_length);
+
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ stream_->OnStreamFrame(zero_length_stream_frame_with_fin);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+
+ // The flow control receive offset values should not have changed.
+ EXPECT_EQ(
+ current_stream_flow_control_offset,
+ QuicFlowControllerPeer::ReceiveWindowOffset(stream_->flow_controller()));
+ EXPECT_EQ(
+ current_connection_flow_control_offset,
+ QuicFlowControllerPeer::ReceiveWindowOffset(session_->flow_controller()));
+}
+
+TEST_P(QuicStreamTest, OnStreamResetOffsetOverflow) {
+ Initialize();
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, kMaxStreamLength + 1);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _));
+ stream_->OnStreamReset(rst_frame);
+}
+
+TEST_P(QuicStreamTest, OnStreamFrameUpperLimit) {
+ Initialize();
+
+ // Modify receive window offset and sequencer buffer total_bytes_read_ to
+ // avoid flow control violation.
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kMaxStreamLength + 5u);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kMaxStreamLength + 5u);
+ QuicStreamSequencerPeer::SetFrameBufferTotalBytesRead(
+ QuicStreamPeer::sequencer(stream_), kMaxStreamLength - 10u);
+
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _))
+ .Times(0);
+ QuicStreamFrame stream_frame(stream_->id(), false, kMaxStreamLength - 1,
+ QuicStringPiece("."));
+ stream_->OnStreamFrame(stream_frame);
+ QuicStreamFrame stream_frame2(stream_->id(), true, kMaxStreamLength,
+ QuicStringPiece(""));
+ stream_->OnStreamFrame(stream_frame2);
+}
+
+TEST_P(QuicStreamTest, StreamTooLong) {
+ Initialize();
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _))
+ .Times(1);
+ QuicStreamFrame stream_frame(stream_->id(), false, kMaxStreamLength,
+ QuicStringPiece("."));
+ EXPECT_QUIC_PEER_BUG(stream_->OnStreamFrame(stream_frame),
+ QuicStrCat("Receive stream frame on stream ",
+ stream_->id(), " reaches max stream length"));
+}
+
+TEST_P(QuicParameterizedStreamTest, SetDrainingIncomingOutgoing) {
+ // Don't have incoming data consumed.
+ Initialize();
+
+ // Incoming data with FIN.
+ QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234,
+ QuicStringPiece("."));
+ stream_->OnStreamFrame(stream_frame_with_fin);
+ // The FIN has been received but not consumed.
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_FALSE(stream_->reading_stopped());
+
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+
+ // Outgoing data with FIN.
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 2u, 0u,
+ FIN);
+ }));
+ stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), true, nullptr);
+ EXPECT_TRUE(stream_->write_side_closed());
+
+ EXPECT_EQ(1u, QuicSessionPeer::GetDrainingStreams(session_.get())
+ ->count(kTestStreamId));
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+}
+
+TEST_P(QuicParameterizedStreamTest, SetDrainingOutgoingIncoming) {
+ // Don't have incoming data consumed.
+ Initialize();
+
+ // Outgoing data with FIN.
+ EXPECT_CALL(*session_, WritevData(stream_, kTestStreamId, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 2u, 0u,
+ FIN);
+ }));
+ stream_->WriteOrBufferData(QuicStringPiece(kData1, 2), true, nullptr);
+ EXPECT_TRUE(stream_->write_side_closed());
+
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+
+ // Incoming data with FIN.
+ QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234,
+ QuicStringPiece("."));
+ stream_->OnStreamFrame(stream_frame_with_fin);
+ // The FIN has been received but not consumed.
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_FALSE(stream_->reading_stopped());
+
+ EXPECT_EQ(1u, QuicSessionPeer::GetDrainingStreams(session_.get())
+ ->count(kTestStreamId));
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+}
+
+TEST_P(QuicStreamTest, EarlyResponseFinHandling) {
+ // Verify that if the server completes the response before reading the end of
+ // the request, the received FIN is recorded.
+
+ Initialize();
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+
+ // Receive data for the request.
+ QuicStreamFrame frame1(stream_->id(), false, 0, QuicStringPiece("Start"));
+ stream_->OnStreamFrame(frame1);
+ // When QuicSimpleServerStream sends the response, it calls
+ // QuicStream::CloseReadSide() first.
+ QuicStreamPeer::CloseReadSide(stream_);
+ // Send data and FIN for the response.
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_));
+ // Receive remaining data and FIN for the request.
+ QuicStreamFrame frame2(stream_->id(), true, 0, QuicStringPiece("End"));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_TRUE(stream_->fin_received());
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
+TEST_P(QuicStreamTest, StreamWaitsForAcks) {
+ Initialize();
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ // Stream is not waiting for acks initially.
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // Send kData1.
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(9u, newly_acked_length);
+ // Stream is not waiting for acks as all sent data is acked.
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // Send kData2.
+ stream_->WriteOrBufferData(kData2, false, nullptr);
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Send FIN.
+ stream_->WriteOrBufferData("", true, nullptr);
+ // Fin only frame is not stored in send buffer.
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // kData2 is retransmitted.
+ stream_->OnStreamFrameRetransmitted(9, 9, false);
+
+ // kData2 is acked.
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(9u, newly_acked_length);
+ // Stream is waiting for acks as FIN is not acked.
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // FIN is acked.
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 0, true, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(0u, newly_acked_length);
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+}
+
+TEST_P(QuicStreamTest, StreamDataGetAckedOutOfOrder) {
+ Initialize();
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ // Send data.
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ stream_->WriteOrBufferData("", true, nullptr);
+ EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(9u, newly_acked_length);
+ EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(9u, newly_acked_length);
+ EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(9u, newly_acked_length);
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+ // FIN is not acked yet.
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(0u, newly_acked_length);
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+TEST_P(QuicStreamTest, CancelStream) {
+ Initialize();
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Cancel stream.
+ stream_->Reset(QUIC_STREAM_NO_ERROR);
+ // stream still waits for acks as the error code is QUIC_STREAM_NO_ERROR, and
+ // data is going to be retransmitted.
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_CALL(*session_,
+ SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, 9));
+ stream_->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Stream stops waiting for acks as data is not going to be retransmitted.
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+TEST_P(QuicStreamTest, RstFrameReceivedStreamNotFinishSending) {
+ Initialize();
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ // RST_STREAM received.
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 9);
+ EXPECT_CALL(*session_,
+ SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9));
+ stream_->OnStreamReset(rst_frame);
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Stream stops waiting for acks as it does not finish sending and rst is
+ // sent.
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+TEST_P(QuicStreamTest, RstFrameReceivedStreamFinishSending) {
+ Initialize();
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ stream_->WriteOrBufferData(kData1, true, nullptr);
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+ // RST_STREAM received.
+ EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(0);
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ // Stream still waits for acks as it finishes sending and has unacked data.
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+}
+
+TEST_P(QuicStreamTest, ConnectionClosed) {
+ Initialize();
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+ EXPECT_CALL(*session_,
+ SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9));
+ stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR,
+ ConnectionCloseSource::FROM_SELF);
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Stream stops waiting for acks as connection is going to close.
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+TEST_P(QuicStreamTest, CanWriteNewDataAfterData) {
+ SetQuicFlag(&FLAGS_quic_buffered_data_threshold, 100);
+ Initialize();
+ EXPECT_TRUE(stream_->CanWriteNewDataAfterData(99));
+ EXPECT_FALSE(stream_->CanWriteNewDataAfterData(100));
+}
+
+TEST_P(QuicStreamTest, WriteBufferedData) {
+ // Set buffered data low water mark to be 100.
+ SetQuicFlag(&FLAGS_quic_buffered_data_threshold, 100);
+ // Do not stream level flow control block this stream.
+ set_initial_flow_control_window_bytes(500000);
+
+ Initialize();
+ std::string data(1024, 'a');
+ EXPECT_TRUE(stream_->CanWriteNewData());
+
+ // Testing WriteOrBufferData.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 100u, 0u,
+ NO_FIN);
+ }));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ stream_->WriteOrBufferData(data, false, nullptr);
+ stream_->WriteOrBufferData(data, false, nullptr);
+ // Verify all data is saved.
+ EXPECT_EQ(3 * data.length() - 100, stream_->BufferedDataBytes());
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 100, 100u,
+ NO_FIN);
+ }));
+ // Buffered data size > threshold, do not ask upper layer for more data.
+ EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(0);
+ stream_->OnCanWrite();
+ EXPECT_EQ(3 * data.length() - 200, stream_->BufferedDataBytes());
+ EXPECT_FALSE(stream_->CanWriteNewData());
+
+ // Send buffered data to make buffered data size < threshold.
+ size_t data_to_write = 3 * data.length() - 200 -
+ GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this, data_to_write]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(),
+ data_to_write, 200u, NO_FIN);
+ }));
+ // Buffered data size < threshold, ask upper layer for more data.
+ EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1);
+ stream_->OnCanWrite();
+ EXPECT_EQ(GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1,
+ stream_->BufferedDataBytes());
+ EXPECT_TRUE(stream_->CanWriteNewData());
+
+ // Flush all buffered data.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1);
+ stream_->OnCanWrite();
+ EXPECT_EQ(0u, stream_->BufferedDataBytes());
+ EXPECT_FALSE(stream_->HasBufferedData());
+ EXPECT_TRUE(stream_->CanWriteNewData());
+
+ // Testing Writev.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ struct iovec iov = {const_cast<char*>(data.data()), data.length()};
+ QuicMemSliceStorage storage(
+ &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(),
+ 1024);
+ QuicConsumedData consumed = stream_->WriteMemSlices(storage.ToSpan(), false);
+
+ // There is no buffered data before, all data should be consumed without
+ // respecting buffered data upper limit.
+ EXPECT_EQ(data.length(), consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_EQ(data.length(), stream_->BufferedDataBytes());
+ EXPECT_FALSE(stream_->CanWriteNewData());
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(0);
+ QuicMemSliceStorage storage2(
+ &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(),
+ 1024);
+ consumed = stream_->WriteMemSlices(storage2.ToSpan(), false);
+ // No Data can be consumed as buffered data is beyond upper limit.
+ EXPECT_EQ(0u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_EQ(data.length(), stream_->BufferedDataBytes());
+
+ data_to_write =
+ data.length() - GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this, data_to_write]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(),
+ data_to_write, 0u, NO_FIN);
+ }));
+
+ EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1);
+ stream_->OnCanWrite();
+ EXPECT_EQ(GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1,
+ stream_->BufferedDataBytes());
+ EXPECT_TRUE(stream_->CanWriteNewData());
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(0);
+ // All data can be consumed as buffered data is below upper limit.
+ QuicMemSliceStorage storage3(
+ &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(),
+ 1024);
+ consumed = stream_->WriteMemSlices(storage3.ToSpan(), false);
+ EXPECT_EQ(data.length(), consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_EQ(data.length() + GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1,
+ stream_->BufferedDataBytes());
+ EXPECT_FALSE(stream_->CanWriteNewData());
+}
+
+TEST_P(QuicStreamTest, WritevDataReachStreamLimit) {
+ Initialize();
+ std::string data("aaaaa");
+ QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - data.length(),
+ stream_);
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Invoke(&(MockQuicSession::ConsumeData)));
+ struct iovec iov = {const_cast<char*>(data.data()), 5u};
+ QuicMemSliceStorage storage(
+ &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(),
+ 1024);
+ QuicConsumedData consumed = stream_->WriteMemSlices(storage.ToSpan(), false);
+ EXPECT_EQ(data.length(), consumed.bytes_consumed);
+ struct iovec iov2 = {const_cast<char*>(data.data()), 1u};
+ QuicMemSliceStorage storage2(
+ &iov2, 1,
+ session_->connection()->helper()->GetStreamSendBufferAllocator(), 1024);
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _));
+ EXPECT_QUIC_BUG(stream_->WriteMemSlices(storage2.ToSpan(), false),
+ "Write too many data via stream");
+}
+
+TEST_P(QuicStreamTest, WriteMemSlices) {
+ // Set buffered data low water mark to be 100.
+ SetQuicFlag(&FLAGS_quic_buffered_data_threshold, 100);
+ // Do not flow control block this stream.
+ set_initial_flow_control_window_bytes(500000);
+
+ Initialize();
+ char data[1024];
+ std::vector<std::pair<char*, size_t>> buffers;
+ buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data)));
+ buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data)));
+ QuicTestMemSliceVector vector1(buffers);
+ QuicTestMemSliceVector vector2(buffers);
+ QuicMemSliceSpan span1 = vector1.span();
+ QuicMemSliceSpan span2 = vector2.span();
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 100u, 0u,
+ NO_FIN);
+ }));
+ // There is no buffered data before, all data should be consumed.
+ QuicConsumedData consumed = stream_->WriteMemSlices(span1, false);
+ EXPECT_EQ(2048u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_EQ(2 * QUIC_ARRAYSIZE(data) - 100, stream_->BufferedDataBytes());
+ EXPECT_FALSE(stream_->fin_buffered());
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(0);
+ // No Data can be consumed as buffered data is beyond upper limit.
+ consumed = stream_->WriteMemSlices(span2, true);
+ EXPECT_EQ(0u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_EQ(2 * QUIC_ARRAYSIZE(data) - 100, stream_->BufferedDataBytes());
+ EXPECT_FALSE(stream_->fin_buffered());
+
+ size_t data_to_write = 2 * QUIC_ARRAYSIZE(data) - 100 -
+ GetQuicFlag(FLAGS_quic_buffered_data_threshold) + 1;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this, data_to_write]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(),
+ data_to_write, 100u, NO_FIN);
+ }));
+ EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(1);
+ stream_->OnCanWrite();
+ EXPECT_EQ(GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1,
+ stream_->BufferedDataBytes());
+ // Try to write slices2 again.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _)).Times(0);
+ consumed = stream_->WriteMemSlices(span2, true);
+ EXPECT_EQ(2048u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_EQ(2 * QUIC_ARRAYSIZE(data) +
+ GetQuicFlag(FLAGS_quic_buffered_data_threshold) - 1,
+ stream_->BufferedDataBytes());
+ EXPECT_TRUE(stream_->fin_buffered());
+
+ // Flush all buffered data.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnCanWrite();
+ EXPECT_CALL(*stream_, OnCanWriteNewData()).Times(0);
+ EXPECT_FALSE(stream_->HasBufferedData());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicStreamTest, WriteMemSlicesReachStreamLimit) {
+ Initialize();
+ QuicStreamPeer::SetStreamBytesWritten(kMaxStreamLength - 5u, stream_);
+ char data[5];
+ std::vector<std::pair<char*, size_t>> buffers;
+ buffers.push_back(std::make_pair(data, QUIC_ARRAYSIZE(data)));
+ QuicTestMemSliceVector vector1(buffers);
+ QuicMemSliceSpan span1 = vector1.span();
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 5u, 0u,
+ NO_FIN);
+ }));
+ // There is no buffered data before, all data should be consumed.
+ QuicConsumedData consumed = stream_->WriteMemSlices(span1, false);
+ EXPECT_EQ(5u, consumed.bytes_consumed);
+
+ std::vector<std::pair<char*, size_t>> buffers2;
+ buffers2.push_back(std::make_pair(data, 1u));
+ QuicTestMemSliceVector vector2(buffers);
+ QuicMemSliceSpan span2 = vector2.span();
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _));
+ EXPECT_QUIC_BUG(stream_->WriteMemSlices(span2, false),
+ "Write too many data via stream");
+}
+
+TEST_P(QuicStreamTest, StreamDataGetAckedMultipleTimes) {
+ Initialize();
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ // Send [0, 27) and fin.
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ stream_->WriteOrBufferData(kData1, true, nullptr);
+ EXPECT_EQ(3u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+ // Ack [0, 9), [5, 22) and [18, 26)
+ // Verify [0, 9) 9 bytes are acked.
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(0, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(9u, newly_acked_length);
+ EXPECT_EQ(2u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Verify [9, 22) 13 bytes are acked.
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(5, 17, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(13u, newly_acked_length);
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ // Verify [22, 26) 4 bytes are acked.
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(18, 8, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(4u, newly_acked_length);
+ EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+ // Ack [0, 27). Verify [26, 27) 1 byte is acked.
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(26, 1, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(1u, newly_acked_length);
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_TRUE(stream_->IsWaitingForAcks());
+
+ // Ack Fin.
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(27, 0, true, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(0u, newly_acked_length);
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+
+ // Ack [10, 27) and fin. No new data is acked.
+ EXPECT_FALSE(stream_->OnStreamFrameAcked(
+ 10, 17, true, QuicTime::Delta::Zero(), &newly_acked_length));
+ EXPECT_EQ(0u, newly_acked_length);
+ EXPECT_EQ(0u, QuicStreamPeer::SendBuffer(stream_).size());
+ EXPECT_FALSE(stream_->IsWaitingForAcks());
+}
+
+TEST_P(QuicStreamTest, OnStreamFrameLost) {
+ Initialize();
+
+ // Send [0, 9).
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_FALSE(stream_->HasBufferedData());
+ EXPECT_TRUE(stream_->IsStreamFrameOutstanding(0, 9, false));
+
+ // Try to send [9, 27), but connection is blocked.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(kData2, false, nullptr);
+ stream_->WriteOrBufferData(kData2, false, nullptr);
+ EXPECT_TRUE(stream_->HasBufferedData());
+ EXPECT_FALSE(stream_->HasPendingRetransmission());
+
+ // Lost [0, 9). When stream gets a chance to write, only lost data is
+ // transmitted.
+ stream_->OnStreamFrameLost(0, 9, false);
+ EXPECT_TRUE(stream_->HasPendingRetransmission());
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnCanWrite();
+ EXPECT_FALSE(stream_->HasPendingRetransmission());
+ EXPECT_TRUE(stream_->HasBufferedData());
+
+ // This OnCanWrite causes [9, 27) to be sent.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnCanWrite();
+ EXPECT_FALSE(stream_->HasBufferedData());
+
+ // Send a fin only frame.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData("", true, nullptr);
+
+ // Lost [9, 27) and fin.
+ stream_->OnStreamFrameLost(9, 18, false);
+ stream_->OnStreamFrameLost(27, 0, true);
+ EXPECT_TRUE(stream_->HasPendingRetransmission());
+
+ // Ack [9, 18).
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_TRUE(stream_->OnStreamFrameAcked(9, 9, false, QuicTime::Delta::Zero(),
+ &newly_acked_length));
+ EXPECT_EQ(9u, newly_acked_length);
+ EXPECT_FALSE(stream_->IsStreamFrameOutstanding(9, 3, false));
+ EXPECT_TRUE(stream_->HasPendingRetransmission());
+ // This OnCanWrite causes [18, 27) and fin to be retransmitted. Verify fin can
+ // be bundled with data.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 9u, 18u,
+ FIN);
+ }));
+ stream_->OnCanWrite();
+ EXPECT_FALSE(stream_->HasPendingRetransmission());
+ // Lost [9, 18) again, but it is not considered as lost because kData2
+ // has been acked.
+ stream_->OnStreamFrameLost(9, 9, false);
+ EXPECT_FALSE(stream_->HasPendingRetransmission());
+ EXPECT_TRUE(stream_->IsStreamFrameOutstanding(27, 0, true));
+}
+
+TEST_P(QuicStreamTest, CannotBundleLostFin) {
+ Initialize();
+
+ // Send [0, 18) and fin.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ stream_->WriteOrBufferData(kData2, true, nullptr);
+
+ // Lost [0, 9) and fin.
+ stream_->OnStreamFrameLost(0, 9, false);
+ stream_->OnStreamFrameLost(18, 0, true);
+
+ // Retransmit lost data. Verify [0, 9) and fin are retransmitted in two
+ // frames.
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 9u, 0u,
+ NO_FIN);
+ }));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_P(QuicStreamTest, MarkConnectionLevelWriteBlockedOnWindowUpdateFrame) {
+ // Set a small initial control window size.
+ set_initial_flow_control_window_bytes(100);
+ Initialize();
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &QuicStreamTest::ClearControlFrame));
+ std::string data(1024, '.');
+ stream_->WriteOrBufferData(data, false, nullptr);
+ EXPECT_FALSE(HasWriteBlockedStreams());
+
+ QuicWindowUpdateFrame window_update(kInvalidControlFrameId, stream_->id(),
+ 1234);
+
+ stream_->OnWindowUpdateFrame(window_update);
+ // Verify stream is marked connection level write blocked.
+ EXPECT_TRUE(HasWriteBlockedStreams());
+ EXPECT_TRUE(stream_->HasBufferedData());
+}
+
+// Regression test for b/73282665.
+TEST_P(QuicStreamTest,
+ MarkConnectionLevelWriteBlockedOnWindowUpdateFrameWithNoBufferedData) {
+ // Set a small initial flow control window size.
+ const uint32_t kSmallWindow = 100;
+ set_initial_flow_control_window_bytes(kSmallWindow);
+ Initialize();
+
+ std::string data(kSmallWindow, '.');
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &QuicStreamTest::ClearControlFrame));
+ stream_->WriteOrBufferData(data, false, nullptr);
+ EXPECT_FALSE(HasWriteBlockedStreams());
+
+ QuicWindowUpdateFrame window_update(kInvalidControlFrameId, stream_->id(),
+ 120);
+ stream_->OnWindowUpdateFrame(window_update);
+ EXPECT_FALSE(stream_->HasBufferedData());
+ // Verify stream is marked as blocked although there is no buffered data.
+ EXPECT_TRUE(HasWriteBlockedStreams());
+}
+
+TEST_P(QuicStreamTest, RetransmitStreamData) {
+ Initialize();
+ InSequence s;
+
+ // Send [0, 18) with fin.
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), _, _, _))
+ .Times(2)
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ stream_->WriteOrBufferData(kData1, true, nullptr);
+ // Ack [10, 13).
+ QuicByteCount newly_acked_length = 0;
+ stream_->OnStreamFrameAcked(10, 3, false, QuicTime::Delta::Zero(),
+ &newly_acked_length);
+ EXPECT_EQ(3u, newly_acked_length);
+ // Retransmit [0, 18) with fin, and only [0, 8) is consumed.
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), 10, 0, NO_FIN))
+ .WillOnce(InvokeWithoutArgs([this]() {
+ return MockQuicSession::ConsumeData(stream_, stream_->id(), 8, 0u,
+ NO_FIN);
+ }));
+ EXPECT_FALSE(stream_->RetransmitStreamData(0, 18, true));
+
+ // Retransmit [0, 18) with fin, and all is consumed.
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), 10, 0, NO_FIN))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), 5, 13, FIN))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_TRUE(stream_->RetransmitStreamData(0, 18, true));
+
+ // Retransmit [0, 8) with fin, and all is consumed.
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), 8, 0, NO_FIN))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), 0, 18, FIN))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_TRUE(stream_->RetransmitStreamData(0, 8, true));
+}
+
+TEST_P(QuicStreamTest, ResetStreamOnTtlExpiresRetransmitLostData) {
+ Initialize();
+
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), 200, 0, FIN))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ std::string body(200, 'a');
+ stream_->WriteOrBufferData(body, true, nullptr);
+
+ // Set TTL to be 1 s.
+ QuicTime::Delta ttl = QuicTime::Delta::FromSeconds(1);
+ ASSERT_TRUE(stream_->MaybeSetTtl(ttl));
+ // Verify data gets retransmitted because TTL does not expire.
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), 100, 0, NO_FIN))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ EXPECT_TRUE(stream_->RetransmitStreamData(0, 100, false));
+ stream_->OnStreamFrameLost(100, 100, true);
+ EXPECT_TRUE(stream_->HasPendingRetransmission());
+
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ // Verify stream gets reset because TTL expires.
+ EXPECT_CALL(*session_, SendRstStream(_, QUIC_STREAM_TTL_EXPIRED, _)).Times(1);
+ stream_->OnCanWrite();
+}
+
+TEST_P(QuicStreamTest, ResetStreamOnTtlExpiresEarlyRetransmitData) {
+ Initialize();
+
+ EXPECT_CALL(*session_, WritevData(_, stream_->id(), 200, 0, FIN))
+ .WillOnce(Invoke(MockQuicSession::ConsumeData));
+ std::string body(200, 'a');
+ stream_->WriteOrBufferData(body, true, nullptr);
+
+ // Set TTL to be 1 s.
+ QuicTime::Delta ttl = QuicTime::Delta::FromSeconds(1);
+ ASSERT_TRUE(stream_->MaybeSetTtl(ttl));
+
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ // Verify stream gets reset because TTL expires.
+ EXPECT_CALL(*session_, SendRstStream(_, QUIC_STREAM_TTL_EXPIRED, _)).Times(1);
+ stream_->RetransmitStreamData(0, 100, false);
+}
+
+// Test that QuicStream::StopSending A) is a no-op if the connection is not in
+// version 99, B) that it properly invokes QuicSession::StopSending, and C) that
+// the correct data is passed along, including getting the stream ID.
+TEST_P(QuicParameterizedStreamTest, CheckStopSending) {
+ Initialize();
+ const int kStopSendingCode = 123;
+ // These must start as false.
+ EXPECT_FALSE(stream_->write_side_closed());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ // Expect to actually see a stop sending if and only if we are in version 99.
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ EXPECT_CALL(*session_, SendStopSending(kStopSendingCode, stream_->id()))
+ .Times(1);
+ } else {
+ EXPECT_CALL(*session_, SendStopSending(_, _)).Times(0);
+ }
+ stream_->SendStopSending(kStopSendingCode);
+ // Sending a STOP_SENDING does not actually close the local stream.
+ // Our implementation waits for the responding RESET_STREAM to effect the
+ // closes. Therefore, read- and write-side closes should both be false.
+ EXPECT_FALSE(stream_->write_side_closed());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+}
+
+// Test that OnStreamReset does one-way (read) closes if version 99, two way
+// (read and write) if not version 99.
+TEST_P(QuicStreamTest, OnStreamResetReadOrReadWrite) {
+ Initialize();
+ EXPECT_FALSE(stream_->write_side_closed());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ if (connection_->transport_version() == QUIC_VERSION_99) {
+ // Version 99/IETF QUIC should close just the read side.
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_FALSE(stream_->write_side_closed());
+ } else {
+ // Google QUIC should close both sides of the stream.
+ EXPECT_TRUE(stream_->write_side_closed());
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_));
+ }
+}
+
+// Test that receiving a STOP_SENDING just closes the write side of the stream.
+// If not V99, the test is a noop (no STOP_SENDING in Google QUIC).
+TEST_P(QuicStreamTest, OnStopSendingReadOrReadWrite) {
+ Initialize();
+ if (connection_->transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ EXPECT_FALSE(stream_->write_side_closed());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+
+ // Simulate receipt of a STOP_SENDING.
+ stream_->OnStopSending(123);
+
+ // Should close just the read side.
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+// SendOnlyRstStream must only send a RESET_STREAM (no bundled STOP_SENDING).
+TEST_P(QuicStreamTest, SendOnlyRstStream) {
+ Initialize();
+ if (connection_->transport_version() != QUIC_VERSION_99) {
+ return;
+ }
+
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD));
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .Times(1)
+ .WillOnce(Invoke(this, &QuicStreamTest::ClearResetStreamFrame));
+
+ QuicSessionPeer::SendRstStreamInner(session_.get(), stream_->id(),
+ QUIC_BAD_APPLICATION_PAYLOAD,
+ stream_->stream_bytes_written(),
+ /*close_write_side_only=*/true);
+
+ // ResetStreamOnly should just close the write side.
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.cc b/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.cc
new file mode 100644
index 00000000000..dd94a342c51
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicSustainedBandwidthRecorder::QuicSustainedBandwidthRecorder()
+ : has_estimate_(false),
+ is_recording_(false),
+ bandwidth_estimate_recorded_during_slow_start_(false),
+ bandwidth_estimate_(QuicBandwidth::Zero()),
+ max_bandwidth_estimate_(QuicBandwidth::Zero()),
+ max_bandwidth_timestamp_(0),
+ start_time_(QuicTime::Zero()) {}
+
+void QuicSustainedBandwidthRecorder::RecordEstimate(bool in_recovery,
+ bool in_slow_start,
+ QuicBandwidth bandwidth,
+ QuicTime estimate_time,
+ QuicWallTime wall_time,
+ QuicTime::Delta srtt) {
+ if (in_recovery) {
+ is_recording_ = false;
+ QUIC_DVLOG(1) << "Stopped recording at: "
+ << estimate_time.ToDebuggingValue();
+ return;
+ }
+
+ if (!is_recording_) {
+ // This is the first estimate of a new recording period.
+ start_time_ = estimate_time;
+ is_recording_ = true;
+ QUIC_DVLOG(1) << "Started recording at: " << start_time_.ToDebuggingValue();
+ return;
+ }
+
+ // If we have been recording for at least 3 * srtt, then record the latest
+ // bandwidth estimate as a valid sustained bandwidth estimate.
+ if (estimate_time - start_time_ >= 3 * srtt) {
+ has_estimate_ = true;
+ bandwidth_estimate_recorded_during_slow_start_ = in_slow_start;
+ bandwidth_estimate_ = bandwidth;
+ QUIC_DVLOG(1) << "New sustained bandwidth estimate (KBytes/s): "
+ << bandwidth_estimate_.ToKBytesPerSecond();
+ }
+
+ // Check for an increase in max bandwidth.
+ if (bandwidth > max_bandwidth_estimate_) {
+ max_bandwidth_estimate_ = bandwidth;
+ max_bandwidth_timestamp_ = wall_time.ToUNIXSeconds();
+ QUIC_DVLOG(1) << "New max bandwidth estimate (KBytes/s): "
+ << max_bandwidth_estimate_.ToKBytesPerSecond();
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h b/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h
new file mode 100644
index 00000000000..5d868b22ffd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h
@@ -0,0 +1,95 @@
+// Copyright 2014 The Chromium 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_SUSTAINED_BANDWIDTH_RECORDER_H_
+#define QUICHE_QUIC_CORE_QUIC_SUSTAINED_BANDWIDTH_RECORDER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace test {
+class QuicSustainedBandwidthRecorderPeer;
+} // namespace test
+
+// This class keeps track of a sustained bandwidth estimate to ultimately send
+// to the client in a server config update message. A sustained bandwidth
+// estimate is only marked as valid if the QuicSustainedBandwidthRecorder has
+// been given uninterrupted reliable estimates over a certain period of time.
+class QUIC_EXPORT_PRIVATE QuicSustainedBandwidthRecorder {
+ public:
+ QuicSustainedBandwidthRecorder();
+ QuicSustainedBandwidthRecorder(const QuicSustainedBandwidthRecorder&) =
+ delete;
+ QuicSustainedBandwidthRecorder& operator=(
+ const QuicSustainedBandwidthRecorder&) = delete;
+
+ // As long as |in_recovery| is consistently false, multiple calls to this
+ // method over a 3 * srtt period results in storage of a valid sustained
+ // bandwidth estimate.
+ // |time_now| is used as a max bandwidth timestamp if needed.
+ void RecordEstimate(bool in_recovery,
+ bool in_slow_start,
+ QuicBandwidth bandwidth,
+ QuicTime estimate_time,
+ QuicWallTime wall_time,
+ QuicTime::Delta srtt);
+
+ bool HasEstimate() const { return has_estimate_; }
+
+ QuicBandwidth BandwidthEstimate() const {
+ DCHECK(has_estimate_);
+ return bandwidth_estimate_;
+ }
+
+ QuicBandwidth MaxBandwidthEstimate() const {
+ DCHECK(has_estimate_);
+ return max_bandwidth_estimate_;
+ }
+
+ int64_t MaxBandwidthTimestamp() const {
+ DCHECK(has_estimate_);
+ return max_bandwidth_timestamp_;
+ }
+
+ bool EstimateRecordedDuringSlowStart() const {
+ DCHECK(has_estimate_);
+ return bandwidth_estimate_recorded_during_slow_start_;
+ }
+
+ private:
+ friend class test::QuicSustainedBandwidthRecorderPeer;
+
+ // True if we have been able to calculate sustained bandwidth, over at least
+ // one recording period (3 * rtt).
+ bool has_estimate_;
+
+ // True if the last call to RecordEstimate had a reliable estimate.
+ bool is_recording_;
+
+ // True if the current sustained bandwidth estimate was generated while in
+ // slow start.
+ bool bandwidth_estimate_recorded_during_slow_start_;
+
+ // The latest sustained bandwidth estimate.
+ QuicBandwidth bandwidth_estimate_;
+
+ // The maximum sustained bandwidth seen over the lifetime of the connection.
+ QuicBandwidth max_bandwidth_estimate_;
+
+ // Timestamp indicating when the max_bandwidth_estimate_ was seen.
+ int64_t max_bandwidth_timestamp_;
+
+ // Timestamp marking the beginning of the latest recording period.
+ QuicTime start_time_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_SUSTAINED_BANDWIDTH_RECORDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder_test.cc
new file mode 100644
index 00000000000..28bbad8fc77
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder_test.cc
@@ -0,0 +1,133 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicSustainedBandwidthRecorderTest : public QuicTest {};
+
+TEST_F(QuicSustainedBandwidthRecorderTest, BandwidthEstimates) {
+ QuicSustainedBandwidthRecorder recorder;
+ EXPECT_FALSE(recorder.HasEstimate());
+
+ QuicTime estimate_time = QuicTime::Zero();
+ QuicWallTime wall_time = QuicWallTime::Zero();
+ QuicTime::Delta srtt = QuicTime::Delta::FromMilliseconds(150);
+ const int kBandwidthBitsPerSecond = 12345678;
+ QuicBandwidth bandwidth =
+ QuicBandwidth::FromBitsPerSecond(kBandwidthBitsPerSecond);
+
+ bool in_recovery = false;
+ bool in_slow_start = false;
+
+ // This triggers recording, but should not yield a valid estimate yet.
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_FALSE(recorder.HasEstimate());
+
+ // Send a second reading, again this should not result in a valid estimate,
+ // as not enough time has passed.
+ estimate_time = estimate_time + srtt;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_FALSE(recorder.HasEstimate());
+
+ // Now 3 * kSRTT has elapsed since first recording, expect a valid estimate.
+ estimate_time = estimate_time + srtt;
+ estimate_time = estimate_time + srtt;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_TRUE(recorder.HasEstimate());
+ EXPECT_EQ(recorder.BandwidthEstimate(), bandwidth);
+ EXPECT_EQ(recorder.BandwidthEstimate(), recorder.MaxBandwidthEstimate());
+
+ // Resetting, and sending a different estimate will only change output after
+ // a further 3 * kSRTT has passed.
+ QuicBandwidth second_bandwidth =
+ QuicBandwidth::FromBitsPerSecond(2 * kBandwidthBitsPerSecond);
+ // Reset the recorder by passing in a measurement while in recovery.
+ in_recovery = true;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ in_recovery = false;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_EQ(recorder.BandwidthEstimate(), bandwidth);
+
+ estimate_time = estimate_time + 3 * srtt;
+ const int64_t kSeconds = 556677;
+ QuicWallTime second_bandwidth_wall_time =
+ QuicWallTime::FromUNIXSeconds(kSeconds);
+ recorder.RecordEstimate(in_recovery, in_slow_start, second_bandwidth,
+ estimate_time, second_bandwidth_wall_time, srtt);
+ EXPECT_EQ(recorder.BandwidthEstimate(), second_bandwidth);
+ EXPECT_EQ(recorder.BandwidthEstimate(), recorder.MaxBandwidthEstimate());
+ EXPECT_EQ(recorder.MaxBandwidthTimestamp(), kSeconds);
+
+ // Reset again, this time recording a lower bandwidth than before.
+ QuicBandwidth third_bandwidth =
+ QuicBandwidth::FromBitsPerSecond(0.5 * kBandwidthBitsPerSecond);
+ // Reset the recorder by passing in an unreliable measurement.
+ recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
+ estimate_time, wall_time, srtt);
+ recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
+ estimate_time, wall_time, srtt);
+ EXPECT_EQ(recorder.BandwidthEstimate(), third_bandwidth);
+
+ estimate_time = estimate_time + 3 * srtt;
+ recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
+ estimate_time, wall_time, srtt);
+ EXPECT_EQ(recorder.BandwidthEstimate(), third_bandwidth);
+
+ // Max bandwidth should not have changed.
+ EXPECT_LT(third_bandwidth, second_bandwidth);
+ EXPECT_EQ(recorder.MaxBandwidthEstimate(), second_bandwidth);
+ EXPECT_EQ(recorder.MaxBandwidthTimestamp(), kSeconds);
+}
+
+TEST_F(QuicSustainedBandwidthRecorderTest, SlowStart) {
+ // Verify that slow start status is correctly recorded.
+ QuicSustainedBandwidthRecorder recorder;
+ EXPECT_FALSE(recorder.HasEstimate());
+
+ QuicTime estimate_time = QuicTime::Zero();
+ QuicWallTime wall_time = QuicWallTime::Zero();
+ QuicTime::Delta srtt = QuicTime::Delta::FromMilliseconds(150);
+ const int kBandwidthBitsPerSecond = 12345678;
+ QuicBandwidth bandwidth =
+ QuicBandwidth::FromBitsPerSecond(kBandwidthBitsPerSecond);
+
+ bool in_recovery = false;
+ bool in_slow_start = true;
+
+ // This triggers recording, but should not yield a valid estimate yet.
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+
+ // Now 3 * kSRTT has elapsed since first recording, expect a valid estimate.
+ estimate_time = estimate_time + 3 * srtt;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_TRUE(recorder.HasEstimate());
+ EXPECT_TRUE(recorder.EstimateRecordedDuringSlowStart());
+
+ // Now send another estimate, this time not in slow start.
+ estimate_time = estimate_time + 3 * srtt;
+ in_slow_start = false;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_TRUE(recorder.HasEstimate());
+ EXPECT_FALSE(recorder.EstimateRecordedDuringSlowStart());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_tag.cc b/chromium/net/third_party/quiche/src/quic/core/quic_tag.cc
new file mode 100644
index 00000000000..109803da105
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_tag.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_tag.h"
+
+#include <algorithm>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+bool FindMutualQuicTag(const QuicTagVector& our_tags,
+ const QuicTagVector& their_tags,
+ QuicTag* out_result,
+ size_t* out_index) {
+ const size_t num_our_tags = our_tags.size();
+ const size_t num_their_tags = their_tags.size();
+ for (size_t i = 0; i < num_our_tags; i++) {
+ for (size_t j = 0; j < num_their_tags; j++) {
+ if (our_tags[i] == their_tags[j]) {
+ *out_result = our_tags[i];
+ if (out_index != nullptr) {
+ *out_index = j;
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+std::string QuicTagToString(QuicTag tag) {
+ if (GetQuicReloadableFlag(quic_print_tag_hex) && tag == 0) {
+ return "0";
+ }
+ char chars[sizeof tag];
+ bool ascii = true;
+ const QuicTag orig_tag = tag;
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(chars); i++) {
+ chars[i] = static_cast<char>(tag);
+ if ((chars[i] == 0 || chars[i] == '\xff') &&
+ i == QUIC_ARRAYSIZE(chars) - 1) {
+ chars[i] = ' ';
+ }
+ if (!isprint(static_cast<unsigned char>(chars[i]))) {
+ ascii = false;
+ break;
+ }
+ tag >>= 8;
+ }
+
+ if (ascii) {
+ return std::string(chars, sizeof(chars));
+ }
+
+ if (!GetQuicReloadableFlag(quic_print_tag_hex)) {
+ return QuicTextUtils::Uint64ToString(orig_tag);
+ }
+
+ QUIC_RELOADABLE_FLAG_COUNT(quic_print_tag_hex);
+
+ return QuicTextUtils::HexEncode(reinterpret_cast<const char*>(&orig_tag),
+ sizeof(orig_tag));
+}
+
+uint32_t MakeQuicTag(char a, char b, char c, char d) {
+ return static_cast<uint32_t>(a) | static_cast<uint32_t>(b) << 8 |
+ static_cast<uint32_t>(c) << 16 | static_cast<uint32_t>(d) << 24;
+}
+
+bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag) {
+ return std::find(tag_vector.begin(), tag_vector.end(), tag) !=
+ tag_vector.end();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_tag.h b/chromium/net/third_party/quiche/src/quic/core/quic_tag.h
new file mode 100644
index 00000000000..71fcdd597f4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_tag.h
@@ -0,0 +1,53 @@
+// 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 QUICHE_QUIC_CORE_QUIC_TAG_H_
+#define QUICHE_QUIC_CORE_QUIC_TAG_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// A QuicTag is a 32-bit used as identifiers in the QUIC handshake. The use of
+// a uint32_t seeks to provide a balance between the tyranny of magic number
+// registries and the verbosity of strings. As far as the wire protocol is
+// concerned, these are opaque, 32-bit values.
+//
+// Tags will often be referred to by their ASCII equivalent, e.g. EXMP. This is
+// just a mnemonic for the value 0x504d5845 (little-endian version of the ASCII
+// string E X M P).
+typedef uint32_t QuicTag;
+typedef std::map<QuicTag, std::string> QuicTagValueMap;
+typedef std::vector<QuicTag> QuicTagVector;
+
+// MakeQuicTag returns a value given the four bytes. For example:
+// MakeQuicTag('C', 'H', 'L', 'O');
+QUIC_EXPORT_PRIVATE QuicTag MakeQuicTag(char a, char b, char c, char d);
+
+// Returns true if |tag_vector| contains |tag|.
+QUIC_EXPORT_PRIVATE bool ContainsQuicTag(const QuicTagVector& tag_vector,
+ QuicTag tag);
+
+// Sets |out_result| to the first tag in |our_tags| that is also in |their_tags|
+// and returns true. If there is no intersection it returns false.
+//
+// If |out_index| is non-nullptr and a match is found then the index of that
+// match in |their_tags| is written to |out_index|.
+QUIC_EXPORT_PRIVATE bool FindMutualQuicTag(const QuicTagVector& our_tags,
+ const QuicTagVector& their_tags,
+ QuicTag* out_result,
+ size_t* out_index);
+
+// A utility function that converts a tag to a string. It will try to maintain
+// the human friendly name if possible (i.e. kABCD -> "ABCD"), or will just
+// treat it as a number if not.
+QUIC_EXPORT_PRIVATE std::string QuicTagToString(QuicTag tag);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_TAG_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_tag_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_tag_test.cc
new file mode 100644
index 00000000000..20af4eeeb3a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_tag_test.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_tag.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicTagTest : public QuicTest {};
+
+TEST_F(QuicTagTest, TagToString) {
+ EXPECT_EQ("SCFG", QuicTagToString(kSCFG));
+ EXPECT_EQ("SNO ", QuicTagToString(kServerNonceTag));
+ EXPECT_EQ("CRT ", QuicTagToString(kCertificateTag));
+ EXPECT_EQ("CHLO", QuicTagToString(MakeQuicTag('C', 'H', 'L', 'O')));
+ if (!GetQuicReloadableFlag(quic_print_tag_hex)) {
+ EXPECT_EQ("525092931", QuicTagToString(MakeQuicTag('C', 'H', 'L', '\x1f')));
+ return;
+ }
+ // A tag that contains a non-printing character will be printed as hex.
+ EXPECT_EQ("43484c1f", QuicTagToString(MakeQuicTag('C', 'H', 'L', '\x1f')));
+}
+
+TEST_F(QuicTagTest, MakeQuicTag) {
+ QuicTag tag = MakeQuicTag('A', 'B', 'C', 'D');
+ char bytes[4];
+ memcpy(bytes, &tag, 4);
+ EXPECT_EQ('A', bytes[0]);
+ EXPECT_EQ('B', bytes[1]);
+ EXPECT_EQ('C', bytes[2]);
+ EXPECT_EQ('D', bytes[3]);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time.cc
new file mode 100644
index 00000000000..25cd56092e3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+
+#include <cinttypes>
+#include <cstdlib>
+#include <limits>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+
+std::string QuicTime::Delta::ToDebugValue() const {
+ const int64_t one_ms = 1000;
+ const int64_t one_s = 1000 * one_ms;
+
+ int64_t absolute_value = std::abs(time_offset_);
+
+ // For debugging purposes, always display the value with the highest precision
+ // available.
+ if (absolute_value > one_s && absolute_value % one_s == 0) {
+ return QuicStringPrintf("%" PRId64 "s", time_offset_ / one_s);
+ }
+ if (absolute_value > one_ms && absolute_value % one_ms == 0) {
+ return QuicStringPrintf("%" PRId64 "ms", time_offset_ / one_ms);
+ }
+ return QuicStringPrintf("%" PRId64 "us", time_offset_);
+}
+
+uint64_t QuicWallTime::ToUNIXSeconds() const {
+ return microseconds_ / 1000000;
+}
+
+uint64_t QuicWallTime::ToUNIXMicroseconds() const {
+ return microseconds_;
+}
+
+bool QuicWallTime::IsAfter(QuicWallTime other) const {
+ return microseconds_ > other.microseconds_;
+}
+
+bool QuicWallTime::IsBefore(QuicWallTime other) const {
+ return microseconds_ < other.microseconds_;
+}
+
+bool QuicWallTime::IsZero() const {
+ return microseconds_ == 0;
+}
+
+QuicTime::Delta QuicWallTime::AbsoluteDifference(QuicWallTime other) const {
+ uint64_t d;
+
+ if (microseconds_ > other.microseconds_) {
+ d = microseconds_ - other.microseconds_;
+ } else {
+ d = other.microseconds_ - microseconds_;
+ }
+
+ if (d > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+ d = std::numeric_limits<int64_t>::max();
+ }
+ return QuicTime::Delta::FromMicroseconds(d);
+}
+
+QuicWallTime QuicWallTime::Add(QuicTime::Delta delta) const {
+ uint64_t microseconds = microseconds_ + delta.ToMicroseconds();
+ if (microseconds < microseconds_) {
+ microseconds = std::numeric_limits<uint64_t>::max();
+ }
+ return QuicWallTime(microseconds);
+}
+
+// TODO(ianswett) Test this.
+QuicWallTime QuicWallTime::Subtract(QuicTime::Delta delta) const {
+ uint64_t microseconds = microseconds_ - delta.ToMicroseconds();
+ if (microseconds > microseconds_) {
+ microseconds = 0;
+ }
+ return QuicWallTime(microseconds);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time.h b/chromium/net/third_party/quiche/src/quic/core/quic_time.h
new file mode 100644
index 00000000000..cd33649be5c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time.h
@@ -0,0 +1,283 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// QuicTime represents one point in time, stored in microsecond resolution.
+// QuicTime is monotonically increasing, even across system clock adjustments.
+// The epoch (time 0) of QuicTime is unspecified.
+//
+// This implementation wraps a int64_t of usec since the epoch. While
+// the epoch is the Unix epoch, do not depend on this fact because other
+// implementations, like Chrome's, do NOT have the same epoch.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_TIME_H_
+#define QUICHE_QUIC_CORE_QUIC_TIME_H_
+
+#include <cmath>
+#include <cstdint>
+#include <limits>
+#include <ostream>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+// TODO(vasilvv): replace with ABSL_MUST_USE_RESULT once we're using absl.
+#if defined(__clang__)
+#define QUIC_TIME_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define QUIC_TIME_WARN_UNUSED_RESULT
+#endif /* defined(__clang__) */
+
+namespace quic {
+
+class QuicClock;
+
+// A QuicTime is a purely relative time. QuicTime values from different clocks
+// cannot be compared to each other. If you need an absolute time, see
+// QuicWallTime, below.
+class QUIC_EXPORT_PRIVATE QuicTime {
+ public:
+ // A QuicTime::Delta represents the signed difference between two points in
+ // time, stored in microsecond resolution.
+ class QUIC_EXPORT_PRIVATE Delta {
+ public:
+ // Create a object with an offset of 0.
+ static constexpr Delta Zero() { return Delta(0); }
+
+ // Create a object with infinite offset time.
+ static constexpr Delta Infinite() { return Delta(kQuicInfiniteTimeUs); }
+
+ // Converts a number of seconds to a time offset.
+ static constexpr Delta FromSeconds(int64_t secs) {
+ return Delta(secs * 1000 * 1000);
+ }
+
+ // Converts a number of milliseconds to a time offset.
+ static constexpr Delta FromMilliseconds(int64_t ms) {
+ return Delta(ms * 1000);
+ }
+
+ // Converts a number of microseconds to a time offset.
+ static constexpr Delta FromMicroseconds(int64_t us) { return Delta(us); }
+
+ // Converts the time offset to a rounded number of seconds.
+ inline int64_t ToSeconds() const { return time_offset_ / 1000 / 1000; }
+
+ // Converts the time offset to a rounded number of milliseconds.
+ inline int64_t ToMilliseconds() const { return time_offset_ / 1000; }
+
+ // Converts the time offset to a rounded number of microseconds.
+ inline int64_t ToMicroseconds() const { return time_offset_; }
+
+ inline bool IsZero() const { return time_offset_ == 0; }
+
+ inline bool IsInfinite() const {
+ return time_offset_ == kQuicInfiniteTimeUs;
+ }
+
+ std::string ToDebugValue() const;
+
+ private:
+ friend inline bool operator==(QuicTime::Delta lhs, QuicTime::Delta rhs);
+ friend inline bool operator<(QuicTime::Delta lhs, QuicTime::Delta rhs);
+ friend inline QuicTime::Delta operator<<(QuicTime::Delta lhs, size_t rhs);
+ friend inline QuicTime::Delta operator>>(QuicTime::Delta lhs, size_t rhs);
+
+ friend inline QuicTime::Delta operator+(QuicTime::Delta lhs,
+ QuicTime::Delta rhs);
+ friend inline QuicTime::Delta operator-(QuicTime::Delta lhs,
+ QuicTime::Delta rhs);
+ friend inline QuicTime::Delta operator*(QuicTime::Delta lhs, int rhs);
+ friend inline QuicTime::Delta operator*(QuicTime::Delta lhs, double rhs);
+
+ friend inline QuicTime operator+(QuicTime lhs, QuicTime::Delta rhs);
+ friend inline QuicTime operator-(QuicTime lhs, QuicTime::Delta rhs);
+ friend inline QuicTime::Delta operator-(QuicTime lhs, QuicTime rhs);
+
+ static const int64_t kQuicInfiniteTimeUs =
+ std::numeric_limits<int64_t>::max();
+
+ explicit constexpr Delta(int64_t time_offset) : time_offset_(time_offset) {}
+
+ int64_t time_offset_;
+ friend class QuicTime;
+ };
+
+ // Creates a new QuicTime with an internal value of 0. IsInitialized()
+ // will return false for these times.
+ static constexpr QuicTime Zero() { return QuicTime(0); }
+
+ // Creates a new QuicTime with an infinite time.
+ static constexpr QuicTime Infinite() {
+ return QuicTime(Delta::kQuicInfiniteTimeUs);
+ }
+
+ QuicTime(const QuicTime& other) = default;
+
+ QuicTime& operator=(const QuicTime& other) {
+ time_ = other.time_;
+ return *this;
+ }
+
+ // Produce the internal value to be used when logging. This value
+ // represents the number of microseconds since some epoch. It may
+ // be the UNIX epoch on some platforms. On others, it may
+ // be a CPU ticks based value.
+ inline int64_t ToDebuggingValue() const { return time_; }
+
+ inline bool IsInitialized() const { return 0 != time_; }
+
+ private:
+ friend class QuicClock;
+
+ friend inline bool operator==(QuicTime lhs, QuicTime rhs);
+ friend inline bool operator<(QuicTime lhs, QuicTime rhs);
+ friend inline QuicTime operator+(QuicTime lhs, QuicTime::Delta rhs);
+ friend inline QuicTime operator-(QuicTime lhs, QuicTime::Delta rhs);
+ friend inline QuicTime::Delta operator-(QuicTime lhs, QuicTime rhs);
+
+ explicit constexpr QuicTime(int64_t time) : time_(time) {}
+
+ int64_t time_;
+};
+
+// A QuicWallTime represents an absolute time that is globally consistent. In
+// practice, clock-skew means that comparing values from different machines
+// requires some flexibility.
+class QUIC_EXPORT_PRIVATE QuicWallTime {
+ public:
+ // FromUNIXSeconds constructs a QuicWallTime from a count of the seconds
+ // since the UNIX epoch.
+ static constexpr QuicWallTime FromUNIXSeconds(uint64_t seconds) {
+ return QuicWallTime(seconds * 1000000);
+ }
+
+ static constexpr QuicWallTime FromUNIXMicroseconds(uint64_t microseconds) {
+ return QuicWallTime(microseconds);
+ }
+
+ // Zero returns a QuicWallTime set to zero. IsZero will return true for this
+ // value.
+ static constexpr QuicWallTime Zero() { return QuicWallTime(0); }
+
+ // Returns the number of seconds since the UNIX epoch.
+ uint64_t ToUNIXSeconds() const;
+ // Returns the number of microseconds since the UNIX epoch.
+ uint64_t ToUNIXMicroseconds() const;
+
+ bool IsAfter(QuicWallTime other) const;
+ bool IsBefore(QuicWallTime other) const;
+
+ // IsZero returns true if this object is the result of calling |Zero|.
+ bool IsZero() const;
+
+ // AbsoluteDifference returns the absolute value of the time difference
+ // between |this| and |other|.
+ QuicTime::Delta AbsoluteDifference(QuicWallTime other) const;
+
+ // Add returns a new QuicWallTime that represents the time of |this| plus
+ // |delta|.
+ QUIC_TIME_WARN_UNUSED_RESULT QuicWallTime Add(QuicTime::Delta delta) const;
+
+ // Subtract returns a new QuicWallTime that represents the time of |this|
+ // minus |delta|.
+ QUIC_TIME_WARN_UNUSED_RESULT QuicWallTime
+ Subtract(QuicTime::Delta delta) const;
+
+ private:
+ explicit constexpr QuicWallTime(uint64_t microseconds)
+ : microseconds_(microseconds) {}
+
+ uint64_t microseconds_;
+};
+
+// Non-member relational operators for QuicTime::Delta.
+inline bool operator==(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return lhs.time_offset_ == rhs.time_offset_;
+}
+inline bool operator!=(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return !(lhs == rhs);
+}
+inline bool operator<(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return lhs.time_offset_ < rhs.time_offset_;
+}
+inline bool operator>(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return rhs < lhs;
+}
+inline bool operator<=(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return !(rhs < lhs);
+}
+inline bool operator>=(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return !(lhs < rhs);
+}
+inline QuicTime::Delta operator>>(QuicTime::Delta lhs, size_t rhs) {
+ return QuicTime::Delta(lhs.time_offset_ >> rhs);
+}
+
+// Non-member relational operators for QuicTime.
+inline bool operator==(QuicTime lhs, QuicTime rhs) {
+ return lhs.time_ == rhs.time_;
+}
+inline bool operator!=(QuicTime lhs, QuicTime rhs) {
+ return !(lhs == rhs);
+}
+inline bool operator<(QuicTime lhs, QuicTime rhs) {
+ return lhs.time_ < rhs.time_;
+}
+inline bool operator>(QuicTime lhs, QuicTime rhs) {
+ return rhs < lhs;
+}
+inline bool operator<=(QuicTime lhs, QuicTime rhs) {
+ return !(rhs < lhs);
+}
+inline bool operator>=(QuicTime lhs, QuicTime rhs) {
+ return !(lhs < rhs);
+}
+
+// Override stream output operator for gtest or CHECK macros.
+inline std::ostream& operator<<(std::ostream& output, const QuicTime t) {
+ output << t.ToDebuggingValue();
+ return output;
+}
+
+// Non-member arithmetic operators for QuicTime::Delta.
+inline QuicTime::Delta operator+(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return QuicTime::Delta(lhs.time_offset_ + rhs.time_offset_);
+}
+inline QuicTime::Delta operator-(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return QuicTime::Delta(lhs.time_offset_ - rhs.time_offset_);
+}
+inline QuicTime::Delta operator*(QuicTime::Delta lhs, int rhs) {
+ return QuicTime::Delta(lhs.time_offset_ * rhs);
+}
+inline QuicTime::Delta operator*(QuicTime::Delta lhs, double rhs) {
+ return QuicTime::Delta(
+ static_cast<int64_t>(std::llround(lhs.time_offset_ * rhs)));
+}
+inline QuicTime::Delta operator*(int lhs, QuicTime::Delta rhs) {
+ return rhs * lhs;
+}
+inline QuicTime::Delta operator*(double lhs, QuicTime::Delta rhs) {
+ return rhs * lhs;
+}
+
+// Non-member arithmetic operators for QuicTime and QuicTime::Delta.
+inline QuicTime operator+(QuicTime lhs, QuicTime::Delta rhs) {
+ return QuicTime(lhs.time_ + rhs.time_offset_);
+}
+inline QuicTime operator-(QuicTime lhs, QuicTime::Delta rhs) {
+ return QuicTime(lhs.time_ - rhs.time_offset_);
+}
+inline QuicTime::Delta operator-(QuicTime lhs, QuicTime rhs) {
+ return QuicTime::Delta(lhs.time_ - rhs.time_);
+}
+
+// Override stream output operator for gtest.
+inline std::ostream& operator<<(std::ostream& output,
+ const QuicTime::Delta delta) {
+ output << delta.ToDebugValue();
+ return output;
+}
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_TIME_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_time_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_time_test.cc
new file mode 100644
index 00000000000..97205325a95
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_test.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+namespace test {
+
+class QuicTimeDeltaTest : public QuicTest {};
+
+TEST_F(QuicTimeDeltaTest, Zero) {
+ EXPECT_TRUE(QuicTime::Delta::Zero().IsZero());
+ EXPECT_FALSE(QuicTime::Delta::Zero().IsInfinite());
+ EXPECT_FALSE(QuicTime::Delta::FromMilliseconds(1).IsZero());
+}
+
+TEST_F(QuicTimeDeltaTest, Infinite) {
+ EXPECT_TRUE(QuicTime::Delta::Infinite().IsInfinite());
+ EXPECT_FALSE(QuicTime::Delta::Zero().IsInfinite());
+ EXPECT_FALSE(QuicTime::Delta::FromMilliseconds(1).IsInfinite());
+}
+
+TEST_F(QuicTimeDeltaTest, FromTo) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1),
+ QuicTime::Delta::FromMicroseconds(1000));
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(1),
+ QuicTime::Delta::FromMilliseconds(1000));
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(1),
+ QuicTime::Delta::FromMicroseconds(1000000));
+
+ EXPECT_EQ(1, QuicTime::Delta::FromMicroseconds(1000).ToMilliseconds());
+ EXPECT_EQ(2, QuicTime::Delta::FromMilliseconds(2000).ToSeconds());
+ EXPECT_EQ(1000, QuicTime::Delta::FromMilliseconds(1).ToMicroseconds());
+ EXPECT_EQ(1, QuicTime::Delta::FromMicroseconds(1000).ToMilliseconds());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2000).ToMicroseconds(),
+ QuicTime::Delta::FromSeconds(2).ToMicroseconds());
+}
+
+TEST_F(QuicTimeDeltaTest, Add) {
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000),
+ QuicTime::Delta::Zero() + QuicTime::Delta::FromMilliseconds(2));
+}
+
+TEST_F(QuicTimeDeltaTest, Subtract) {
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMilliseconds(2) -
+ QuicTime::Delta::FromMilliseconds(1));
+}
+
+TEST_F(QuicTimeDeltaTest, Multiply) {
+ int i = 2;
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ QuicTime::Delta::FromMilliseconds(2) * i);
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ i * QuicTime::Delta::FromMilliseconds(2));
+ double d = 2;
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ QuicTime::Delta::FromMilliseconds(2) * d);
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ d * QuicTime::Delta::FromMilliseconds(2));
+
+ // Ensure we are rounding correctly within a single-bit level of precision.
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(5),
+ QuicTime::Delta::FromMicroseconds(9) * 0.5);
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2),
+ QuicTime::Delta::FromMicroseconds(12) * 0.2);
+}
+
+TEST_F(QuicTimeDeltaTest, Max) {
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000),
+ std::max(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMicroseconds(2000)));
+}
+
+TEST_F(QuicTimeDeltaTest, NotEqual) {
+ EXPECT_TRUE(QuicTime::Delta::FromSeconds(0) !=
+ QuicTime::Delta::FromSeconds(1));
+ EXPECT_FALSE(QuicTime::Delta::FromSeconds(0) !=
+ QuicTime::Delta::FromSeconds(0));
+}
+
+TEST_F(QuicTimeDeltaTest, DebugValue) {
+ const QuicTime::Delta one_us = QuicTime::Delta::FromMicroseconds(1);
+ const QuicTime::Delta one_ms = QuicTime::Delta::FromMilliseconds(1);
+ const QuicTime::Delta one_s = QuicTime::Delta::FromSeconds(1);
+
+ EXPECT_EQ("3s", (3 * one_s).ToDebugValue());
+ EXPECT_EQ("3ms", (3 * one_ms).ToDebugValue());
+ EXPECT_EQ("3us", (3 * one_us).ToDebugValue());
+
+ EXPECT_EQ("3001us", (3 * one_ms + one_us).ToDebugValue());
+ EXPECT_EQ("3001ms", (3 * one_s + one_ms).ToDebugValue());
+ EXPECT_EQ("3000001us", (3 * one_s + one_us).ToDebugValue());
+}
+
+class QuicTimeTest : public QuicTest {
+ protected:
+ MockClock clock_;
+};
+
+TEST_F(QuicTimeTest, Initialized) {
+ EXPECT_FALSE(QuicTime::Zero().IsInitialized());
+ EXPECT_TRUE((QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(1))
+ .IsInitialized());
+}
+
+TEST_F(QuicTimeTest, CopyConstruct) {
+ QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1234);
+ EXPECT_NE(time_1, QuicTime(QuicTime::Zero()));
+ EXPECT_EQ(time_1, QuicTime(time_1));
+}
+
+TEST_F(QuicTimeTest, CopyAssignment) {
+ QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1234);
+ QuicTime time_2 = QuicTime::Zero();
+ EXPECT_NE(time_1, time_2);
+ time_2 = time_1;
+ EXPECT_EQ(time_1, time_2);
+}
+
+TEST_F(QuicTimeTest, Add) {
+ QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1);
+ QuicTime time_2 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2);
+
+ QuicTime::Delta diff = time_2 - time_1;
+
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), diff);
+ EXPECT_EQ(1000, diff.ToMicroseconds());
+ EXPECT_EQ(1, diff.ToMilliseconds());
+}
+
+TEST_F(QuicTimeTest, Subtract) {
+ QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1);
+ QuicTime time_2 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2);
+
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), time_2 - time_1);
+}
+
+TEST_F(QuicTimeTest, SubtractDelta) {
+ QuicTime time = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2);
+ EXPECT_EQ(QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1),
+ time - QuicTime::Delta::FromMilliseconds(1));
+}
+
+TEST_F(QuicTimeTest, Max) {
+ QuicTime time_1 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1);
+ QuicTime time_2 = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2);
+
+ EXPECT_EQ(time_2, std::max(time_1, time_2));
+}
+
+TEST_F(QuicTimeTest, MockClock) {
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+
+ QuicTime now = clock_.ApproximateNow();
+ QuicTime time = QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(1000);
+
+ EXPECT_EQ(now, time);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ now = clock_.ApproximateNow();
+
+ EXPECT_NE(now, time);
+
+ time = time + QuicTime::Delta::FromMilliseconds(1);
+ EXPECT_EQ(now, time);
+}
+
+TEST_F(QuicTimeTest, LE) {
+ const QuicTime zero = QuicTime::Zero();
+ const QuicTime one = zero + QuicTime::Delta::FromSeconds(1);
+ EXPECT_TRUE(zero <= zero);
+ EXPECT_TRUE(zero <= one);
+ EXPECT_TRUE(one <= one);
+ EXPECT_FALSE(one <= zero);
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..6fba2ebe5e0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+
+#include <errno.h>
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+// A very simple alarm that just informs the QuicTimeWaitListManager to clean
+// up old connection_ids. This alarm should be cancelled and deleted before
+// the QuicTimeWaitListManager is deleted.
+class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit ConnectionIdCleanUpAlarm(
+ QuicTimeWaitListManager* time_wait_list_manager)
+ : time_wait_list_manager_(time_wait_list_manager) {}
+ ConnectionIdCleanUpAlarm(const ConnectionIdCleanUpAlarm&) = delete;
+ ConnectionIdCleanUpAlarm& operator=(const ConnectionIdCleanUpAlarm&) = delete;
+
+ void OnAlarm() override {
+ time_wait_list_manager_->CleanUpOldConnectionIds();
+ }
+
+ private:
+ // Not owned.
+ QuicTimeWaitListManager* time_wait_list_manager_;
+};
+
+QuicTimeWaitListManager::QuicTimeWaitListManager(
+ QuicPacketWriter* writer,
+ Visitor* visitor,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory)
+ : time_wait_period_(
+ QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds)),
+ connection_id_clean_up_alarm_(
+ alarm_factory->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
+ clock_(clock),
+ writer_(writer),
+ visitor_(visitor) {
+ SetConnectionIdCleanUpAlarm();
+}
+
+QuicTimeWaitListManager::~QuicTimeWaitListManager() {
+ connection_id_clean_up_alarm_->Cancel();
+}
+
+void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ TimeWaitAction action,
+ EncryptionLevel encryption_level,
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets) {
+ DCHECK(action != SEND_TERMINATION_PACKETS || termination_packets != nullptr);
+ DCHECK(action != DO_NOTHING || ietf_quic);
+ int num_packets = 0;
+ auto it = connection_id_map_.find(connection_id);
+ const bool new_connection_id = it == connection_id_map_.end();
+ if (!new_connection_id) { // Replace record if it is reinserted.
+ num_packets = it->second.num_packets;
+ connection_id_map_.erase(it);
+ }
+ TrimTimeWaitListIfNeeded();
+ DCHECK_LT(num_connections(),
+ static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections));
+ ConnectionIdData data(num_packets, ietf_quic, clock_->ApproximateNow(),
+ action);
+ if (termination_packets != nullptr) {
+ data.encryption_level = encryption_level;
+ data.termination_packets.swap(*termination_packets);
+ }
+ connection_id_map_.emplace(std::make_pair(connection_id, std::move(data)));
+ if (new_connection_id) {
+ visitor_->OnConnectionAddedToTimeWaitList(connection_id);
+ }
+}
+
+bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
+ QuicConnectionId connection_id) const {
+ return QuicContainsKey(connection_id_map_, connection_id);
+}
+
+void QuicTimeWaitListManager::OnBlockedWriterCanWrite() {
+ writer_->SetWritable();
+ while (!pending_packets_queue_.empty()) {
+ QueuedPacket* queued_packet = pending_packets_queue_.front().get();
+ if (!WriteToWire(queued_packet)) {
+ return;
+ }
+ pending_packets_queue_.pop_front();
+ }
+}
+
+void QuicTimeWaitListManager::ProcessPacket(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId connection_id,
+ PacketHeaderFormat header_format,
+ std::unique_ptr<QuicPerPacketContext> packet_context) {
+ DCHECK(IsConnectionIdInTimeWait(connection_id));
+ // TODO(satyamshekhar): Think about handling packets from different peer
+ // addresses.
+ auto it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
+ // Increment the received packet count.
+ ConnectionIdData* connection_data = &it->second;
+ ++(connection_data->num_packets);
+
+ if (!ShouldSendResponse(connection_data->num_packets)) {
+ QUIC_DLOG(INFO) << "Processing " << connection_id << " in time wait state: "
+ << "throttled";
+ return;
+ }
+
+ QUIC_DLOG(INFO) << "Processing " << connection_id << " in time wait state: "
+ << "header format=" << header_format
+ << " ietf=" << connection_data->ietf_quic
+ << ", action=" << connection_data->action
+ << ", number termination packets="
+ << connection_data->termination_packets.size()
+ << ", encryption level=" << connection_data->encryption_level;
+ switch (connection_data->action) {
+ case SEND_TERMINATION_PACKETS:
+ if (connection_data->termination_packets.empty()) {
+ QUIC_BUG << "There are no termination packets.";
+ return;
+ }
+ switch (header_format) {
+ case IETF_QUIC_LONG_HEADER_PACKET:
+ if (!connection_data->ietf_quic) {
+ QUIC_CODE_COUNT(quic_received_long_header_packet_for_gquic);
+ }
+ if (connection_data->encryption_level == ENCRYPTION_FORWARD_SECURE) {
+ QUIC_CODE_COUNT(
+ quic_forward_secure_termination_packets_for_long_header);
+ }
+ break;
+ case IETF_QUIC_SHORT_HEADER_PACKET:
+ if (!connection_data->ietf_quic) {
+ QUIC_CODE_COUNT(quic_received_short_header_packet_for_gquic);
+ }
+ if (connection_data->encryption_level == ENCRYPTION_INITIAL) {
+ QUIC_CODE_COUNT(
+ quic_encryption_none_termination_packets_for_short_header);
+ if (GetQuicReloadableFlag(quic_always_reset_short_header_packets)) {
+ QUIC_RELOADABLE_FLAG_COUNT(
+ quic_always_reset_short_header_packets);
+ // Send stateless reset in response to short header packets,
+ // because ENCRYPTION_INITIAL termination packets will not be
+ // processed by clients.
+ SendPublicReset(self_address, peer_address, connection_id,
+ connection_data->ietf_quic,
+ std::move(packet_context));
+ return;
+ }
+ } else if (connection_data->encryption_level == ENCRYPTION_ZERO_RTT) {
+ QUIC_CODE_COUNT(quic_zero_rtt_termination_packets_for_short_header);
+ }
+ break;
+ case GOOGLE_QUIC_PACKET:
+ if (connection_data->ietf_quic) {
+ QUIC_CODE_COUNT(quic_received_gquic_packet_for_ietf_quic);
+ }
+ break;
+ }
+
+ for (const auto& packet : connection_data->termination_packets) {
+ SendOrQueuePacket(QuicMakeUnique<QueuedPacket>(
+ self_address, peer_address, packet->Clone()),
+ packet_context.get());
+ }
+ return;
+ case SEND_STATELESS_RESET:
+ if (header_format == IETF_QUIC_LONG_HEADER_PACKET) {
+ QUIC_CODE_COUNT(quic_stateless_reset_long_header_packet);
+ }
+ SendPublicReset(self_address, peer_address, connection_id,
+ connection_data->ietf_quic, std::move(packet_context));
+ return;
+ case DO_NOTHING:
+ QUIC_CODE_COUNT(quic_time_wait_list_do_nothing);
+ DCHECK(connection_data->ietf_quic);
+ }
+}
+
+void QuicTimeWaitListManager::SendVersionNegotiationPacket(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ std::unique_ptr<QuicPerPacketContext> packet_context) {
+ SendOrQueuePacket(QuicMakeUnique<QueuedPacket>(
+ self_address, peer_address,
+ QuicFramer::BuildVersionNegotiationPacket(
+ connection_id, ietf_quic, supported_versions)),
+ packet_context.get());
+}
+
+// Returns true if the number of packets received for this connection_id is a
+// power of 2 to throttle the number of public reset packets we send to a peer.
+bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
+ return (received_packet_count & (received_packet_count - 1)) == 0;
+}
+
+void QuicTimeWaitListManager::SendPublicReset(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ std::unique_ptr<QuicPerPacketContext> packet_context) {
+ if (ietf_quic) {
+ SendOrQueuePacket(QuicMakeUnique<QueuedPacket>(
+ self_address, peer_address,
+ BuildIetfStatelessResetPacket(connection_id)),
+ packet_context.get());
+ return;
+ }
+ QuicPublicResetPacket packet;
+ packet.connection_id = connection_id;
+ // TODO(satyamshekhar): generate a valid nonce for this connection_id.
+ packet.nonce_proof = 1010101;
+ // TODO(wub): This is wrong for proxied sessions. Fix it.
+ packet.client_address = peer_address;
+ GetEndpointId(&packet.endpoint_id);
+ // Takes ownership of the packet.
+ SendOrQueuePacket(QuicMakeUnique<QueuedPacket>(self_address, peer_address,
+ BuildPublicReset(packet)),
+ packet_context.get());
+}
+
+std::unique_ptr<QuicEncryptedPacket> QuicTimeWaitListManager::BuildPublicReset(
+ const QuicPublicResetPacket& packet) {
+ return QuicFramer::BuildPublicResetPacket(packet);
+}
+
+std::unique_ptr<QuicEncryptedPacket>
+QuicTimeWaitListManager::BuildIetfStatelessResetPacket(
+ QuicConnectionId connection_id) {
+ return QuicFramer::BuildIetfStatelessResetPacket(
+ connection_id, GetStatelessResetToken(connection_id));
+}
+
+// Either sends the packet and deletes it or makes pending queue the
+// owner of the packet.
+bool QuicTimeWaitListManager::SendOrQueuePacket(
+ std::unique_ptr<QueuedPacket> packet,
+ const QuicPerPacketContext* /*packet_context*/) {
+ if (WriteToWire(packet.get())) {
+ // Allow the packet to be deleted upon leaving this function.
+ return true;
+ }
+ pending_packets_queue_.push_back(std::move(packet));
+ return false;
+}
+
+bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked(this);
+ return false;
+ }
+ WriteResult result = writer_->WritePacket(
+ queued_packet->packet()->data(), queued_packet->packet()->length(),
+ queued_packet->self_address().host(), queued_packet->peer_address(),
+ nullptr);
+
+ // If using a batch writer and the packet is buffered, flush it.
+ if (writer_->IsBatchMode() && result.status == WRITE_STATUS_OK &&
+ result.bytes_written == 0) {
+ result = writer_->Flush();
+ }
+
+ if (IsWriteBlockedStatus(result.status)) {
+ // If blocked and unbuffered, return false to retry sending.
+ DCHECK(writer_->IsWriteBlocked());
+ visitor_->OnWriteBlocked(this);
+ return result.status == WRITE_STATUS_BLOCKED_DATA_BUFFERED;
+ } else if (IsWriteError(result.status)) {
+ QUIC_LOG_FIRST_N(WARNING, 1)
+ << "Received unknown error while sending termination packet to "
+ << queued_packet->peer_address().ToString() << ": "
+ << strerror(result.error_code);
+ }
+ return true;
+}
+
+void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
+ QuicTime::Delta next_alarm_interval = QuicTime::Delta::Zero();
+ if (!connection_id_map_.empty()) {
+ QuicTime oldest_connection_id =
+ connection_id_map_.begin()->second.time_added;
+ QuicTime now = clock_->ApproximateNow();
+ if (now - oldest_connection_id < time_wait_period_) {
+ next_alarm_interval = oldest_connection_id + time_wait_period_ - now;
+ } else {
+ QUIC_LOG(ERROR)
+ << "ConnectionId lingered for longer than time_wait_period_";
+ }
+ } else {
+ // No connection_ids added so none will expire before time_wait_period_.
+ next_alarm_interval = time_wait_period_;
+ }
+
+ connection_id_clean_up_alarm_->Update(
+ clock_->ApproximateNow() + next_alarm_interval, QuicTime::Delta::Zero());
+}
+
+bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
+ QuicTime expiration_time) {
+ if (connection_id_map_.empty()) {
+ return false;
+ }
+ auto it = connection_id_map_.begin();
+ QuicTime oldest_connection_id_time = it->second.time_added;
+ if (oldest_connection_id_time > expiration_time) {
+ // Too recent, don't retire.
+ return false;
+ }
+ // This connection_id has lived its age, retire it now.
+ QUIC_DLOG(INFO) << "Connection " << it->first
+ << " expired from time wait list";
+ connection_id_map_.erase(it);
+ return true;
+}
+
+void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
+ QuicTime now = clock_->ApproximateNow();
+ QuicTime expiration = now - time_wait_period_;
+
+ while (MaybeExpireOldestConnection(expiration)) {
+ }
+
+ SetConnectionIdCleanUpAlarm();
+}
+
+void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() {
+ if (FLAGS_quic_time_wait_list_max_connections < 0) {
+ return;
+ }
+ while (num_connections() >=
+ static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections)) {
+ MaybeExpireOldestConnection(QuicTime::Infinite());
+ }
+}
+
+QuicTimeWaitListManager::ConnectionIdData::ConnectionIdData(
+ int num_packets,
+ bool ietf_quic,
+ QuicTime time_added,
+ TimeWaitAction action)
+ : num_packets(num_packets),
+ ietf_quic(ietf_quic),
+ time_added(time_added),
+ encryption_level(ENCRYPTION_INITIAL),
+ action(action) {}
+
+QuicTimeWaitListManager::ConnectionIdData::ConnectionIdData(
+ ConnectionIdData&& other) = default;
+
+QuicTimeWaitListManager::ConnectionIdData::~ConnectionIdData() = default;
+
+QuicUint128 QuicTimeWaitListManager::GetStatelessResetToken(
+ QuicConnectionId connection_id) const {
+ return QuicUtils::GenerateStatelessResetToken(connection_id);
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..dee5d11de43
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h
@@ -0,0 +1,275 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Handles packets for connection_ids in time wait state by discarding the
+// packet and sending the peers termination packets with exponential backoff.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_
+#define QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_
+
+#include <cstddef>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_blocked_writer_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+namespace test {
+class QuicDispatcherPeer;
+class QuicTimeWaitListManagerPeer;
+} // namespace test
+
+// Maintains a list of all connection_ids that have been recently closed. A
+// connection_id lives in this state for time_wait_period_. All packets received
+// for connection_ids in this state are handed over to the
+// QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a
+// public reset packet, a copy of the previously sent connection close packet,
+// or nothing to the peer which sent a packet with the connection_id in time
+// wait state. After the connection_id expires its time wait period, a new
+// connection/session will be created if a packet is received for this
+// connection_id.
+class QuicTimeWaitListManager : public QuicBlockedWriterInterface {
+ public:
+ // Specifies what the time wait list manager should do when processing packets
+ // of a time wait connection.
+ enum TimeWaitAction : uint8_t {
+ // Send specified termination packets, error if termination packet is
+ // unavailable.
+ SEND_TERMINATION_PACKETS,
+ // Send stateless reset (public reset for GQUIC).
+ SEND_STATELESS_RESET,
+
+ DO_NOTHING,
+ };
+
+ class Visitor : public QuicSession::Visitor {
+ public:
+ // Called after the given connection is added to the time-wait list.
+ virtual void OnConnectionAddedToTimeWaitList(
+ QuicConnectionId connection_id) = 0;
+ };
+
+ // writer - the entity that writes to the socket. (Owned by the caller)
+ // visitor - the entity that manages blocked writers. (Owned by the caller)
+ // clock - provide a clock (Owned by the caller)
+ // alarm_factory - used to run clean up alarms. (Owned by the caller)
+ QuicTimeWaitListManager(QuicPacketWriter* writer,
+ Visitor* visitor,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory);
+ QuicTimeWaitListManager(const QuicTimeWaitListManager&) = delete;
+ QuicTimeWaitListManager& operator=(const QuicTimeWaitListManager&) = delete;
+ ~QuicTimeWaitListManager() override;
+
+ // Adds the given connection_id to time wait state for time_wait_period_.
+ // If |termination_packets| are provided, copies of these packets will be sent
+ // when a packet with this connection ID is processed. Any termination packets
+ // will be move from |termination_packets| and will become owned by the
+ // manager. |action| specifies what the time wait list manager should do when
+ // processing packets of the connection.
+ virtual void AddConnectionIdToTimeWait(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ TimeWaitAction action,
+ EncryptionLevel encryption_level,
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets);
+
+ // Returns true if the connection_id is in time wait state, false otherwise.
+ // Packets received for this connection_id should not lead to creation of new
+ // QuicSessions.
+ bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const;
+
+ // Called when a packet is received for a connection_id that is in time wait
+ // state. Sends a public reset packet to the peer which sent this
+ // connection_id. Sending of the public reset packet is throttled by using
+ // exponential back off. DCHECKs for the connection_id to be in time wait
+ // state. virtual to override in tests.
+ virtual void ProcessPacket(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId connection_id,
+ PacketHeaderFormat header_format,
+ std::unique_ptr<QuicPerPacketContext> packet_context);
+
+ // Called by the dispatcher when the underlying socket becomes writable again,
+ // since we might need to send pending public reset packets which we didn't
+ // send because the underlying socket was write blocked.
+ void OnBlockedWriterCanWrite() override;
+
+ bool IsWriterBlocked() const override {
+ return writer_ != nullptr && writer_->IsWriteBlocked();
+ }
+
+ // Used to delete connection_id entries that have outlived their time wait
+ // period.
+ void CleanUpOldConnectionIds();
+
+ // If necessary, trims the oldest connections from the time-wait list until
+ // the size is under the configured maximum.
+ void TrimTimeWaitListIfNeeded();
+
+ // The number of connections on the time-wait list.
+ size_t num_connections() const { return connection_id_map_.size(); }
+
+ // Sends a version negotiation packet for |connection_id| announcing support
+ // for |supported_versions| to |peer_address| from |self_address|.
+ virtual void SendVersionNegotiationPacket(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ std::unique_ptr<QuicPerPacketContext> packet_context);
+
+ // Return a non-owning pointer to the packet writer.
+ QuicPacketWriter* writer() { return writer_; }
+
+ protected:
+ virtual std::unique_ptr<QuicEncryptedPacket> BuildPublicReset(
+ const QuicPublicResetPacket& packet);
+
+ // Creates a public reset packet and sends it or queues it to be sent later.
+ virtual void SendPublicReset(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ std::unique_ptr<QuicPerPacketContext> packet_context);
+
+ virtual void GetEndpointId(std::string* endpoint_id) {}
+
+ // Returns a stateless reset token which will be included in the public reset
+ // packet.
+ virtual QuicUint128 GetStatelessResetToken(
+ QuicConnectionId connection_id) const;
+
+ // Internal structure to store pending termination packets.
+ class QueuedPacket {
+ public:
+ QueuedPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ std::unique_ptr<QuicEncryptedPacket> packet)
+ : self_address_(self_address),
+ peer_address_(peer_address),
+ packet_(std::move(packet)) {}
+ QueuedPacket(const QueuedPacket&) = delete;
+ QueuedPacket& operator=(const QueuedPacket&) = delete;
+
+ const QuicSocketAddress& self_address() const { return self_address_; }
+ const QuicSocketAddress& peer_address() const { return peer_address_; }
+ QuicEncryptedPacket* packet() { return packet_.get(); }
+
+ private:
+ // Server address on which a packet was received for a connection_id in
+ // time wait state.
+ const QuicSocketAddress self_address_;
+ // Address of the peer to send this packet to.
+ const QuicSocketAddress peer_address_;
+ // The pending termination packet that is to be sent to the peer.
+ std::unique_ptr<QuicEncryptedPacket> packet_;
+ };
+
+ // Called right after |packet| is serialized. Either sends the packet and
+ // deletes it or makes pending_packets_queue_ the owner of the packet.
+ // Subclasses overriding this method should call this class's base
+ // implementation at the end of the override.
+ // Return true if |packet| is sent, false if it is queued.
+ virtual bool SendOrQueuePacket(std::unique_ptr<QueuedPacket> packet,
+ const QuicPerPacketContext* packet_context);
+
+ const QuicDeque<std::unique_ptr<QueuedPacket>>& pending_packets_queue()
+ const {
+ return pending_packets_queue_;
+ }
+
+ private:
+ friend class test::QuicDispatcherPeer;
+ friend class test::QuicTimeWaitListManagerPeer;
+
+ // Decides if a packet should be sent for this connection_id based on the
+ // number of received packets.
+ bool ShouldSendResponse(int received_packet_count);
+
+ // Sends the packet out. Returns true if the packet was successfully consumed.
+ // If the writer got blocked and did not buffer the packet, we'll need to keep
+ // the packet and retry sending. In case of all other errors we drop the
+ // packet.
+ bool WriteToWire(QueuedPacket* packet);
+
+ // Register the alarm server to wake up at appropriate time.
+ void SetConnectionIdCleanUpAlarm();
+
+ // Removes the oldest connection from the time-wait list if it was added prior
+ // to "expiration_time". To unconditionally remove the oldest connection, use
+ // a QuicTime::Delta:Infinity(). This function modifies the
+ // connection_id_map_. If you plan to call this function in a loop, any
+ // iterators that you hold before the call to this function may be invalid
+ // afterward. Returns true if the oldest connection was expired. Returns
+ // false if the map is empty or the oldest connection has not expired.
+ bool MaybeExpireOldestConnection(QuicTime expiration_time);
+
+ std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket(
+ QuicConnectionId connection_id);
+
+ // A map from a recently closed connection_id to the number of packets
+ // received after the termination of the connection bound to the
+ // connection_id.
+ struct ConnectionIdData {
+ ConnectionIdData(int num_packets,
+ bool ietf_quic,
+ QuicTime time_added,
+ TimeWaitAction action);
+
+ ConnectionIdData(const ConnectionIdData& other) = delete;
+ ConnectionIdData(ConnectionIdData&& other);
+
+ ~ConnectionIdData();
+
+ int num_packets;
+ bool ietf_quic;
+ QuicTime time_added;
+ // Encryption level of termination_packets.
+ EncryptionLevel encryption_level;
+ // These packets may contain CONNECTION_CLOSE frames, or SREJ messages.
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
+ TimeWaitAction action;
+ };
+
+ // QuicLinkedHashMap allows lookup by ConnectionId and traversal in add order.
+ typedef QuicLinkedHashMap<QuicConnectionId,
+ ConnectionIdData,
+ QuicConnectionIdHash>
+ ConnectionIdMap;
+ ConnectionIdMap connection_id_map_;
+
+ // Pending termination packets that need to be sent out to the peer when we
+ // are given a chance to write by the dispatcher.
+ QuicDeque<std::unique_ptr<QueuedPacket>> pending_packets_queue_;
+
+ // Time period for which connection_ids should remain in time wait state.
+ const QuicTime::Delta time_wait_period_;
+
+ // Alarm to clean up connection_ids that have out lived their duration in
+ // time wait state.
+ std::unique_ptr<QuicAlarm> connection_id_clean_up_alarm_;
+
+ // Clock to efficiently measure approximate time.
+ const QuicClock* clock_;
+
+ // Interface that writes given buffer to the socket.
+ QuicPacketWriter* writer_;
+
+ // Interface that manages blocked writers.
+ Visitor* visitor_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_TIME_WAIT_LIST_MANAGER_H_
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
new file mode 100644
index 00000000000..aa0c911ee9b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_time_wait_list_manager_test.cc
@@ -0,0 +1,583 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+
+#include <cerrno>
+#include <memory>
+#include <ostream>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h"
+
+using testing::_;
+using testing::Args;
+using testing::Assign;
+using testing::DoAll;
+using testing::Matcher;
+using testing::NiceMock;
+using testing::Return;
+using testing::ReturnPointee;
+using testing::StrictMock;
+using testing::Truly;
+
+namespace quic {
+namespace test {
+namespace {
+
+class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor {
+ public:
+ FramerVisitorCapturingPublicReset(QuicConnectionId connection_id)
+ : connection_id_(connection_id) {}
+ ~FramerVisitorCapturingPublicReset() override = default;
+
+ void OnPublicResetPacket(const QuicPublicResetPacket& public_reset) override {
+ public_reset_packet_ = public_reset;
+ }
+
+ const QuicPublicResetPacket public_reset_packet() {
+ return public_reset_packet_;
+ }
+
+ bool IsValidStatelessResetToken(QuicUint128 token) const override {
+ return token == QuicUtils::GenerateStatelessResetToken(connection_id_);
+ }
+
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {
+ stateless_reset_packet_ = packet;
+ }
+
+ const QuicIetfStatelessResetPacket stateless_reset_packet() {
+ return stateless_reset_packet_;
+ }
+
+ private:
+ QuicPublicResetPacket public_reset_packet_;
+ QuicIetfStatelessResetPacket stateless_reset_packet_;
+ QuicConnectionId connection_id_;
+};
+
+class MockAlarmFactory;
+class MockAlarm : public QuicAlarm {
+ public:
+ explicit MockAlarm(QuicArenaScopedPtr<Delegate> delegate,
+ int alarm_index,
+ MockAlarmFactory* factory)
+ : QuicAlarm(std::move(delegate)),
+ alarm_index_(alarm_index),
+ factory_(factory) {}
+ virtual ~MockAlarm() {}
+
+ void SetImpl() override;
+ void CancelImpl() override;
+
+ private:
+ int alarm_index_;
+ MockAlarmFactory* factory_;
+};
+
+class MockAlarmFactory : public QuicAlarmFactory {
+ public:
+ ~MockAlarmFactory() override {}
+
+ // Creates a new platform-specific alarm which will be configured to notify
+ // |delegate| when the alarm fires. Returns an alarm allocated on the heap.
+ // Caller takes ownership of the new alarm, which will not yet be "set" to
+ // fire.
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override {
+ return new MockAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate),
+ alarm_index_++, this);
+ }
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override {
+ if (arena != nullptr) {
+ return arena->New<MockAlarm>(std::move(delegate), alarm_index_++, this);
+ }
+ return QuicArenaScopedPtr<MockAlarm>(
+ new MockAlarm(std::move(delegate), alarm_index_++, this));
+ }
+ MOCK_METHOD2(OnAlarmSet, void(int, QuicTime));
+ MOCK_METHOD1(OnAlarmCancelled, void(int));
+
+ private:
+ int alarm_index_ = 0;
+};
+
+void MockAlarm::SetImpl() {
+ factory_->OnAlarmSet(alarm_index_, deadline());
+}
+
+void MockAlarm::CancelImpl() {
+ factory_->OnAlarmCancelled(alarm_index_);
+}
+
+class QuicTimeWaitListManagerTest : public QuicTest {
+ protected:
+ QuicTimeWaitListManagerTest()
+ : time_wait_list_manager_(&writer_, &visitor_, &clock_, &alarm_factory_),
+ connection_id_(TestConnectionId(45)),
+ peer_address_(TestPeerIPAddress(), kTestPort),
+ writer_is_blocked_(false) {}
+
+ ~QuicTimeWaitListManagerTest() override = default;
+
+ void SetUp() override {
+ EXPECT_CALL(writer_, IsWriteBlocked())
+ .WillRepeatedly(ReturnPointee(&writer_is_blocked_));
+ }
+
+ void AddConnectionId(QuicConnectionId connection_id,
+ QuicTimeWaitListManager::TimeWaitAction action) {
+ AddConnectionId(connection_id, QuicVersionMax(), action, nullptr);
+ }
+
+ void AddStatelessConnectionId(QuicConnectionId connection_id) {
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
+ termination_packets.push_back(std::unique_ptr<QuicEncryptedPacket>(
+ new QuicEncryptedPacket(nullptr, 0, false)));
+ time_wait_list_manager_.AddConnectionIdToTimeWait(
+ connection_id, false, QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
+ ENCRYPTION_INITIAL, &termination_packets);
+ }
+
+ void AddConnectionId(
+ QuicConnectionId connection_id,
+ ParsedQuicVersion version,
+ QuicTimeWaitListManager::TimeWaitAction action,
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>* packets) {
+ time_wait_list_manager_.AddConnectionIdToTimeWait(
+ connection_id, version.transport_version > QUIC_VERSION_43, action,
+ ENCRYPTION_INITIAL, packets);
+ }
+
+ bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) {
+ return time_wait_list_manager_.IsConnectionIdInTimeWait(connection_id);
+ }
+
+ void ProcessPacket(QuicConnectionId connection_id) {
+ time_wait_list_manager_.ProcessPacket(
+ self_address_, peer_address_, connection_id, GOOGLE_QUIC_PACKET,
+ QuicMakeUnique<QuicPerPacketContext>());
+ }
+
+ QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ uint64_t packet_number) {
+ return quic::test::ConstructEncryptedPacket(destination_connection_id,
+ source_connection_id, false,
+ false, packet_number, "data");
+ }
+
+ MockClock clock_;
+ MockAlarmFactory alarm_factory_;
+ NiceMock<MockPacketWriter> writer_;
+ StrictMock<MockQuicSessionVisitor> visitor_;
+ QuicTimeWaitListManager time_wait_list_manager_;
+ QuicConnectionId connection_id_;
+ QuicSocketAddress self_address_;
+ QuicSocketAddress peer_address_;
+ bool writer_is_blocked_;
+};
+
+bool ValidPublicResetPacketPredicate(
+ QuicConnectionId expected_connection_id,
+ const testing::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));
+ framer.ProcessPacket(encrypted);
+ QuicPublicResetPacket packet = visitor.public_reset_packet();
+ bool public_reset_is_valid =
+ expected_connection_id == packet.connection_id &&
+ TestPeerIPAddress() == packet.client_address.host() &&
+ kTestPort == packet.client_address.port();
+
+ QuicIetfStatelessResetPacket stateless_reset =
+ visitor.stateless_reset_packet();
+
+ QuicUint128 expected_stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(expected_connection_id);
+
+ bool stateless_reset_is_valid =
+ stateless_reset.stateless_reset_token == expected_stateless_reset_token;
+
+ return public_reset_is_valid || stateless_reset_is_valid;
+}
+
+Matcher<const testing::tuple<const char*, int>> PublicResetPacketEq(
+ QuicConnectionId connection_id) {
+ return Truly(
+ [connection_id](const testing::tuple<const char*, int> packet_buffer) {
+ return ValidPublicResetPacketPredicate(connection_id, packet_buffer);
+ });
+}
+
+TEST_F(QuicTimeWaitListManagerTest, CheckConnectionIdInTimeWait) {
+ EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_));
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ AddConnectionId(connection_id_, QuicTimeWaitListManager::DO_NOTHING);
+ EXPECT_EQ(1u, time_wait_list_manager_.num_connections());
+ EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_));
+}
+
+TEST_F(QuicTimeWaitListManagerTest, CheckStatelessConnectionIdInTimeWait) {
+ EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_));
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ AddStatelessConnectionId(connection_id_);
+ EXPECT_EQ(1u, time_wait_list_manager_.num_connections());
+ EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_));
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendVersionNegotiationPacket) {
+ std::unique_ptr<QuicEncryptedPacket> packet(
+ QuicFramer::BuildVersionNegotiationPacket(connection_id_, false,
+ AllSupportedVersions()));
+ EXPECT_CALL(writer_, WritePacket(_, packet->length(), self_address_.host(),
+ peer_address_, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+
+ time_wait_list_manager_.SendVersionNegotiationPacket(
+ connection_id_, false, AllSupportedVersions(), self_address_,
+ peer_address_, QuicMakeUnique<QuicPerPacketContext>());
+ EXPECT_EQ(0u, time_wait_list_manager_.num_connections());
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendConnectionClose) {
+ const size_t kConnectionCloseLength = 100;
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
+ termination_packets.push_back(
+ std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket(
+ new char[kConnectionCloseLength], kConnectionCloseLength, true)));
+ AddConnectionId(connection_id_, QuicVersionMax(),
+ QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
+ &termination_packets);
+ EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength,
+ self_address_.host(), peer_address_, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+
+ ProcessPacket(connection_id_);
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendTwoConnectionCloses) {
+ const size_t kConnectionCloseLength = 100;
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
+ termination_packets.push_back(
+ std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket(
+ new char[kConnectionCloseLength], kConnectionCloseLength, true)));
+ termination_packets.push_back(
+ std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket(
+ new char[kConnectionCloseLength], kConnectionCloseLength, true)));
+ AddConnectionId(connection_id_, QuicVersionMax(),
+ QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
+ &termination_packets);
+ EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength,
+ self_address_.host(), peer_address_, _))
+ .Times(2)
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+
+ ProcessPacket(connection_id_);
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendPublicReset) {
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ AddConnectionId(connection_id_,
+ QuicTimeWaitListManager::SEND_STATELESS_RESET);
+ EXPECT_CALL(writer_,
+ WritePacket(_, _, self_address_.host(), peer_address_, _))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id_)))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ ProcessPacket(connection_id_);
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendPublicResetWithExponentialBackOff) {
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ AddConnectionId(connection_id_,
+ QuicTimeWaitListManager::SEND_STATELESS_RESET);
+ EXPECT_EQ(1u, time_wait_list_manager_.num_connections());
+ for (int packet_number = 1; packet_number < 101; ++packet_number) {
+ if ((packet_number & (packet_number - 1)) == 0) {
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+ }
+ ProcessPacket(connection_id_);
+ // Send public reset with exponential back off.
+ if ((packet_number & (packet_number - 1)) == 0) {
+ EXPECT_TRUE(QuicTimeWaitListManagerPeer::ShouldSendResponse(
+ &time_wait_list_manager_, packet_number));
+ } else {
+ EXPECT_FALSE(QuicTimeWaitListManagerPeer::ShouldSendResponse(
+ &time_wait_list_manager_, packet_number));
+ }
+ }
+}
+
+TEST_F(QuicTimeWaitListManagerTest, NoPublicResetForStatelessConnections) {
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ AddStatelessConnectionId(connection_id_);
+
+ EXPECT_CALL(writer_,
+ WritePacket(_, _, self_address_.host(), peer_address_, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+
+ ProcessPacket(connection_id_);
+}
+
+TEST_F(QuicTimeWaitListManagerTest, CleanUpOldConnectionIds) {
+ const size_t kConnectionIdCount = 100;
+ const size_t kOldConnectionIdCount = 31;
+
+ // Add connection_ids such that their expiry time is time_wait_period_.
+ for (uint64_t conn_id = 1; conn_id <= kOldConnectionIdCount; ++conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id));
+ AddConnectionId(connection_id, QuicTimeWaitListManager::DO_NOTHING);
+ }
+ EXPECT_EQ(kOldConnectionIdCount, time_wait_list_manager_.num_connections());
+
+ // Add remaining connection_ids such that their add time is
+ // 2 * time_wait_period_.
+ const QuicTime::Delta time_wait_period =
+ QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_);
+ clock_.AdvanceTime(time_wait_period);
+ for (uint64_t conn_id = kOldConnectionIdCount + 1;
+ conn_id <= kConnectionIdCount; ++conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id));
+ AddConnectionId(connection_id, QuicTimeWaitListManager::DO_NOTHING);
+ }
+ EXPECT_EQ(kConnectionIdCount, time_wait_list_manager_.num_connections());
+
+ QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39);
+ // Now set the current time as time_wait_period + offset usecs.
+ clock_.AdvanceTime(offset);
+ // After all the old connection_ids are cleaned up, check the next alarm
+ // interval.
+ QuicTime next_alarm_time = clock_.Now() + time_wait_period - offset;
+ EXPECT_CALL(alarm_factory_, OnAlarmSet(_, next_alarm_time));
+
+ time_wait_list_manager_.CleanUpOldConnectionIds();
+ for (uint64_t conn_id = 1; conn_id <= kConnectionIdCount; ++conn_id) {
+ QuicConnectionId connection_id = TestConnectionId(conn_id);
+ EXPECT_EQ(conn_id > kOldConnectionIdCount,
+ IsConnectionIdInTimeWait(connection_id))
+ << "kOldConnectionIdCount: " << kOldConnectionIdCount
+ << " connection_id: " << connection_id;
+ }
+ EXPECT_EQ(kConnectionIdCount - kOldConnectionIdCount,
+ time_wait_list_manager_.num_connections());
+}
+
+TEST_F(QuicTimeWaitListManagerTest, SendQueuedPackets) {
+ QuicConnectionId connection_id = TestConnectionId(1);
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id));
+ AddConnectionId(connection_id, QuicTimeWaitListManager::SEND_STATELESS_RESET);
+ std::unique_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
+ connection_id, EmptyQuicConnectionId(), /*packet_number=*/234));
+ // Let first write through.
+ EXPECT_CALL(writer_,
+ WritePacket(_, _, self_address_.host(), peer_address_, _))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id)))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length())));
+ ProcessPacket(connection_id);
+
+ // write block for the next packet.
+ EXPECT_CALL(writer_,
+ WritePacket(_, _, self_address_.host(), peer_address_, _))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id)))
+ .WillOnce(DoAll(Assign(&writer_is_blocked_, true),
+ Return(WriteResult(WRITE_STATUS_BLOCKED, EAGAIN))));
+ EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_));
+ ProcessPacket(connection_id);
+ // 3rd packet. No public reset should be sent;
+ ProcessPacket(connection_id);
+
+ // write packet should not be called since we are write blocked but the
+ // should be queued.
+ QuicConnectionId other_connection_id = TestConnectionId(2);
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(other_connection_id));
+ AddConnectionId(other_connection_id,
+ QuicTimeWaitListManager::SEND_STATELESS_RESET);
+ std::unique_ptr<QuicEncryptedPacket> other_packet(ConstructEncryptedPacket(
+ other_connection_id, EmptyQuicConnectionId(), /*packet_number=*/23423));
+ EXPECT_CALL(writer_, WritePacket(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(visitor_, OnWriteBlocked(&time_wait_list_manager_));
+ ProcessPacket(other_connection_id);
+ EXPECT_EQ(2u, time_wait_list_manager_.num_connections());
+
+ // Now expect all the write blocked public reset packets to be sent again.
+ writer_is_blocked_ = false;
+ EXPECT_CALL(writer_,
+ WritePacket(_, _, self_address_.host(), peer_address_, _))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id)))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length())));
+ EXPECT_CALL(writer_,
+ WritePacket(_, _, self_address_.host(), peer_address_, _))
+ .With(Args<0, 1>(PublicResetPacketEq(other_connection_id)))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, packet->length())));
+ time_wait_list_manager_.OnBlockedWriterCanWrite();
+}
+
+TEST_F(QuicTimeWaitListManagerTest, AddConnectionIdTwice) {
+ // Add connection_ids such that their expiry time is time_wait_period_.
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ AddConnectionId(connection_id_, QuicTimeWaitListManager::DO_NOTHING);
+ EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_));
+ const size_t kConnectionCloseLength = 100;
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
+ termination_packets.push_back(
+ std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket(
+ new char[kConnectionCloseLength], kConnectionCloseLength, true)));
+ AddConnectionId(connection_id_, QuicVersionMax(),
+ QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
+ &termination_packets);
+ EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id_));
+ EXPECT_EQ(1u, time_wait_list_manager_.num_connections());
+
+ EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength,
+ self_address_.host(), peer_address_, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+
+ ProcessPacket(connection_id_);
+
+ const QuicTime::Delta time_wait_period =
+ QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_);
+
+ QuicTime::Delta offset = QuicTime::Delta::FromMicroseconds(39);
+ clock_.AdvanceTime(offset + time_wait_period);
+ // Now set the current time as time_wait_period + offset usecs.
+ QuicTime next_alarm_time = clock_.Now() + time_wait_period;
+ EXPECT_CALL(alarm_factory_, OnAlarmSet(_, next_alarm_time));
+
+ time_wait_list_manager_.CleanUpOldConnectionIds();
+ EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id_));
+ EXPECT_EQ(0u, time_wait_list_manager_.num_connections());
+}
+
+TEST_F(QuicTimeWaitListManagerTest, ConnectionIdsOrderedByTime) {
+ // Simple randomization: the values of connection_ids are randomly swapped.
+ // If the container is broken, the test will be 50% flaky.
+ const uint64_t conn_id1 = QuicRandom::GetInstance()->RandUint64() % 2;
+ const QuicConnectionId connection_id1 = TestConnectionId(conn_id1);
+ const QuicConnectionId connection_id2 = TestConnectionId(1 - conn_id1);
+
+ // 1 will hash lower than 2, but we add it later. They should come out in the
+ // add order, not hash order.
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id1));
+ AddConnectionId(connection_id1, QuicTimeWaitListManager::DO_NOTHING);
+ clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(10));
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id2));
+ AddConnectionId(connection_id2, QuicTimeWaitListManager::DO_NOTHING);
+ EXPECT_EQ(2u, time_wait_list_manager_.num_connections());
+
+ const QuicTime::Delta time_wait_period =
+ QuicTimeWaitListManagerPeer::time_wait_period(&time_wait_list_manager_);
+ clock_.AdvanceTime(time_wait_period - QuicTime::Delta::FromMicroseconds(9));
+
+ EXPECT_CALL(alarm_factory_, OnAlarmSet(_, _));
+
+ time_wait_list_manager_.CleanUpOldConnectionIds();
+ EXPECT_FALSE(IsConnectionIdInTimeWait(connection_id1));
+ EXPECT_TRUE(IsConnectionIdInTimeWait(connection_id2));
+ EXPECT_EQ(1u, time_wait_list_manager_.num_connections());
+}
+
+TEST_F(QuicTimeWaitListManagerTest, MaxConnectionsTest) {
+ // Basically, shut off time-based eviction.
+ FLAGS_quic_time_wait_list_seconds = 10000000000;
+ FLAGS_quic_time_wait_list_max_connections = 5;
+
+ uint64_t current_conn_id = 0;
+ // Add exactly the maximum number of connections
+ for (int64_t i = 0; i < FLAGS_quic_time_wait_list_max_connections; ++i) {
+ ++current_conn_id;
+ QuicConnectionId current_connection_id = TestConnectionId(current_conn_id);
+ EXPECT_FALSE(IsConnectionIdInTimeWait(current_connection_id));
+ EXPECT_CALL(visitor_,
+ OnConnectionAddedToTimeWaitList(current_connection_id));
+ AddConnectionId(current_connection_id, QuicTimeWaitListManager::DO_NOTHING);
+ EXPECT_EQ(current_conn_id, time_wait_list_manager_.num_connections());
+ EXPECT_TRUE(IsConnectionIdInTimeWait(current_connection_id));
+ }
+
+ // Now keep adding. Since we're already at the max, every new connection-id
+ // will evict the oldest one.
+ for (int64_t i = 0; i < FLAGS_quic_time_wait_list_max_connections; ++i) {
+ ++current_conn_id;
+ QuicConnectionId current_connection_id = TestConnectionId(current_conn_id);
+ const QuicConnectionId id_to_evict = TestConnectionId(
+ current_conn_id - FLAGS_quic_time_wait_list_max_connections);
+ EXPECT_TRUE(IsConnectionIdInTimeWait(id_to_evict));
+ EXPECT_FALSE(IsConnectionIdInTimeWait(current_connection_id));
+ EXPECT_CALL(visitor_,
+ OnConnectionAddedToTimeWaitList(current_connection_id));
+ AddConnectionId(current_connection_id, QuicTimeWaitListManager::DO_NOTHING);
+ EXPECT_EQ(static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections),
+ time_wait_list_manager_.num_connections());
+ EXPECT_FALSE(IsConnectionIdInTimeWait(id_to_evict));
+ EXPECT_TRUE(IsConnectionIdInTimeWait(current_connection_id));
+ }
+}
+
+// Regression test for b/116200989.
+TEST_F(QuicTimeWaitListManagerTest,
+ SendStatelessResetInResponseToShortHeaders) {
+ // This test mimics a scenario where an ENCRYPTION_INITIAL connection close is
+ // added as termination packet for an IETF connection ID. However, a short
+ // header packet is received later.
+ const size_t kConnectionCloseLength = 100;
+ EXPECT_CALL(visitor_, OnConnectionAddedToTimeWaitList(connection_id_));
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
+ termination_packets.push_back(
+ std::unique_ptr<QuicEncryptedPacket>(new QuicEncryptedPacket(
+ new char[kConnectionCloseLength], kConnectionCloseLength, true)));
+ // Add an ENCRYPTION_INITIAL termination packet.
+ time_wait_list_manager_.AddConnectionIdToTimeWait(
+ connection_id_, /*ietf_quic=*/true,
+ QuicTimeWaitListManager::SEND_TERMINATION_PACKETS, ENCRYPTION_INITIAL,
+ &termination_packets);
+
+ if (GetQuicReloadableFlag(quic_always_reset_short_header_packets)) {
+ // Termination packet is not encrypted, instead, send stateless reset.
+ EXPECT_CALL(writer_,
+ WritePacket(_, _, self_address_.host(), peer_address_, _))
+ .With(Args<0, 1>(PublicResetPacketEq(connection_id_)))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
+ } else {
+ // An unprocessable connection close is sent to peer.
+ EXPECT_CALL(writer_, WritePacket(_, kConnectionCloseLength,
+ self_address_.host(), peer_address_, _))
+ .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 1)));
+ }
+ // Processes IETF short header packet.
+ time_wait_list_manager_.ProcessPacket(
+ self_address_, peer_address_, connection_id_,
+ IETF_QUIC_SHORT_HEADER_PACKET, QuicMakeUnique<QuicPerPacketContext>());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc
new file mode 100644
index 00000000000..5fdab2d6f37
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.cc
@@ -0,0 +1,332 @@
+// 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 "net/third_party/quiche/src/quic/core/quic_trace_visitor.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+
+namespace quic {
+
+quic_trace::EncryptionLevel EncryptionLevelToProto(EncryptionLevel level) {
+ switch (level) {
+ case ENCRYPTION_INITIAL:
+ return quic_trace::ENCRYPTION_INITIAL;
+ case ENCRYPTION_HANDSHAKE:
+ return quic_trace::ENCRYPTION_HANDSHAKE;
+ case ENCRYPTION_ZERO_RTT:
+ return quic_trace::ENCRYPTION_0RTT;
+ case ENCRYPTION_FORWARD_SECURE:
+ return quic_trace::ENCRYPTION_1RTT;
+ case NUM_ENCRYPTION_LEVELS:
+ QUIC_BUG << "Invalid encryption level specified";
+ return quic_trace::ENCRYPTION_UNKNOWN;
+ }
+}
+
+QuicTraceVisitor::QuicTraceVisitor(const QuicConnection* connection)
+ : connection_(connection),
+ start_time_(connection_->clock()->ApproximateNow()) {
+ std::string binary_connection_id(connection->connection_id().data(),
+ connection->connection_id().length());
+ // We assume that the connection ID in gQUIC is equivalent to the
+ // server-chosen client-selected ID.
+ switch (connection->perspective()) {
+ case Perspective::IS_CLIENT:
+ trace_.set_destination_connection_id(binary_connection_id);
+ break;
+ case Perspective::IS_SERVER:
+ trace_.set_source_connection_id(binary_connection_id);
+ break;
+ }
+}
+
+void QuicTraceVisitor::OnPacketSent(const SerializedPacket& serialized_packet,
+ QuicPacketNumber /*original_packet_number*/,
+ TransmissionType /*transmission_type*/,
+ QuicTime sent_time) {
+ quic_trace::Event* event = trace_.add_events();
+ event->set_event_type(quic_trace::PACKET_SENT);
+ event->set_time_us(ConvertTimestampToRecordedFormat(sent_time));
+ event->set_packet_number(serialized_packet.packet_number.ToUint64());
+ event->set_packet_size(serialized_packet.encrypted_length);
+ event->set_encryption_level(
+ EncryptionLevelToProto(serialized_packet.encryption_level));
+
+ for (const QuicFrame& frame : serialized_packet.retransmittable_frames) {
+ switch (frame.type) {
+ case STREAM_FRAME:
+ case RST_STREAM_FRAME:
+ case CONNECTION_CLOSE_FRAME:
+ case WINDOW_UPDATE_FRAME:
+ case BLOCKED_FRAME:
+ case PING_FRAME:
+ PopulateFrameInfo(frame, event->add_frames());
+ break;
+
+ case PADDING_FRAME:
+ case MTU_DISCOVERY_FRAME:
+ case STOP_WAITING_FRAME:
+ case ACK_FRAME:
+ QUIC_BUG
+ << "Frames of type are not retransmittable and are not supposed "
+ "to be in retransmittable_frames";
+ break;
+
+ // New IETF frames, not used in current gQUIC version.
+ case NEW_CONNECTION_ID_FRAME:
+ case RETIRE_CONNECTION_ID_FRAME:
+ case MAX_STREAM_ID_FRAME:
+ case STREAM_ID_BLOCKED_FRAME:
+ case PATH_RESPONSE_FRAME:
+ case PATH_CHALLENGE_FRAME:
+ case STOP_SENDING_FRAME:
+ case MESSAGE_FRAME:
+ case CRYPTO_FRAME:
+ case NEW_TOKEN_FRAME:
+ break;
+
+ // Ignore gQUIC-specific frames.
+ case GOAWAY_FRAME:
+ break;
+
+ case NUM_FRAME_TYPES:
+ QUIC_BUG << "Unknown frame type encountered";
+ break;
+ }
+ }
+
+ // Output PCC DebugState on packet sent for analysis.
+ if (connection_->sent_packet_manager()
+ .GetSendAlgorithm()
+ ->GetCongestionControlType() == kPCC) {
+ PopulateTransportState(event->mutable_transport_state());
+ }
+}
+
+void QuicTraceVisitor::PopulateFrameInfo(const QuicFrame& frame,
+ quic_trace::Frame* frame_record) {
+ switch (frame.type) {
+ case STREAM_FRAME: {
+ frame_record->set_frame_type(quic_trace::STREAM);
+
+ quic_trace::StreamFrameInfo* info =
+ frame_record->mutable_stream_frame_info();
+ info->set_stream_id(frame.stream_frame.stream_id);
+ info->set_fin(frame.stream_frame.fin);
+ info->set_offset(frame.stream_frame.offset);
+ info->set_length(frame.stream_frame.data_length);
+ break;
+ }
+
+ case ACK_FRAME: {
+ frame_record->set_frame_type(quic_trace::ACK);
+
+ quic_trace::AckInfo* info = frame_record->mutable_ack_info();
+ info->set_ack_delay_us(frame.ack_frame->ack_delay_time.ToMicroseconds());
+ for (const auto& interval : frame.ack_frame->packets) {
+ quic_trace::AckBlock* block = info->add_acked_packets();
+ // We record intervals as [a, b], whereas the in-memory representation
+ // we currently use is [a, b).
+ block->set_first_packet(interval.min().ToUint64());
+ block->set_last_packet(interval.max().ToUint64() - 1);
+ }
+ break;
+ }
+
+ case RST_STREAM_FRAME: {
+ frame_record->set_frame_type(quic_trace::RESET_STREAM);
+
+ quic_trace::ResetStreamInfo* info =
+ frame_record->mutable_reset_stream_info();
+ info->set_stream_id(frame.rst_stream_frame->stream_id);
+ info->set_final_offset(frame.rst_stream_frame->byte_offset);
+ info->set_application_error_code(frame.rst_stream_frame->error_code);
+ break;
+ }
+
+ case CONNECTION_CLOSE_FRAME: {
+ frame_record->set_frame_type(quic_trace::CONNECTION_CLOSE);
+
+ quic_trace::CloseInfo* info = frame_record->mutable_close_info();
+ info->set_error_code(frame.connection_close_frame->quic_error_code);
+ info->set_reason_phrase(frame.connection_close_frame->error_details);
+ info->set_close_type(static_cast<quic_trace::CloseType>(
+ frame.connection_close_frame->close_type));
+ info->set_transport_close_frame_type(
+ frame.connection_close_frame->transport_close_frame_type);
+ break;
+ }
+
+ case GOAWAY_FRAME:
+ // Do not bother logging this since the frame in question is
+ // gQUIC-specific.
+ break;
+
+ case WINDOW_UPDATE_FRAME: {
+ bool is_connection = frame.window_update_frame->stream_id == 0;
+ frame_record->set_frame_type(is_connection ? quic_trace::MAX_DATA
+ : quic_trace::MAX_STREAM_DATA);
+
+ quic_trace::FlowControlInfo* info =
+ frame_record->mutable_flow_control_info();
+ info->set_max_data(frame.window_update_frame->byte_offset);
+ if (!is_connection) {
+ info->set_stream_id(frame.window_update_frame->stream_id);
+ }
+ break;
+ }
+
+ case BLOCKED_FRAME: {
+ bool is_connection = frame.blocked_frame->stream_id == 0;
+ frame_record->set_frame_type(is_connection ? quic_trace::BLOCKED
+ : quic_trace::STREAM_BLOCKED);
+
+ quic_trace::FlowControlInfo* info =
+ frame_record->mutable_flow_control_info();
+ if (!is_connection) {
+ info->set_stream_id(frame.window_update_frame->stream_id);
+ }
+ break;
+ }
+
+ case PING_FRAME:
+ case MTU_DISCOVERY_FRAME:
+ frame_record->set_frame_type(quic_trace::PING);
+ break;
+
+ case PADDING_FRAME:
+ frame_record->set_frame_type(quic_trace::PADDING);
+ break;
+
+ case STOP_WAITING_FRAME:
+ // We're going to pretend those do not exist.
+ break;
+
+ // New IETF frames, not used in current gQUIC version.
+ case NEW_CONNECTION_ID_FRAME:
+ case RETIRE_CONNECTION_ID_FRAME:
+ case MAX_STREAM_ID_FRAME:
+ case STREAM_ID_BLOCKED_FRAME:
+ case PATH_RESPONSE_FRAME:
+ case PATH_CHALLENGE_FRAME:
+ case STOP_SENDING_FRAME:
+ case MESSAGE_FRAME:
+ case CRYPTO_FRAME:
+ case NEW_TOKEN_FRAME:
+ break;
+
+ case NUM_FRAME_TYPES:
+ QUIC_BUG << "Unknown frame type encountered";
+ break;
+ }
+}
+
+void QuicTraceVisitor::OnIncomingAck(
+ const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time,
+ QuicPacketNumber /*largest_observed*/,
+ bool /*rtt_updated*/,
+ QuicPacketNumber /*least_unacked_sent_packet*/) {
+ quic_trace::Event* event = trace_.add_events();
+ event->set_time_us(ConvertTimestampToRecordedFormat(ack_receive_time));
+ event->set_packet_number(connection_->GetLargestReceivedPacket().ToUint64());
+ event->set_event_type(quic_trace::PACKET_RECEIVED);
+
+ // TODO(vasilvv): consider removing this copy.
+ QuicAckFrame copy_of_ack = ack_frame;
+ PopulateFrameInfo(QuicFrame(&copy_of_ack), event->add_frames());
+ PopulateTransportState(event->mutable_transport_state());
+}
+
+void QuicTraceVisitor::OnPacketLoss(QuicPacketNumber lost_packet_number,
+ TransmissionType transmission_type,
+ QuicTime detection_time) {
+ quic_trace::Event* event = trace_.add_events();
+ event->set_time_us(ConvertTimestampToRecordedFormat(detection_time));
+ event->set_event_type(quic_trace::PACKET_LOST);
+ event->set_packet_number(lost_packet_number.ToUint64());
+ PopulateTransportState(event->mutable_transport_state());
+}
+
+void QuicTraceVisitor::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ const QuicTime& receive_time) {
+ quic_trace::Event* event = trace_.add_events();
+ event->set_time_us(ConvertTimestampToRecordedFormat(receive_time));
+ event->set_event_type(quic_trace::PACKET_RECEIVED);
+ event->set_packet_number(connection_->GetLargestReceivedPacket().ToUint64());
+
+ // TODO(vasilvv): consider removing this copy.
+ QuicWindowUpdateFrame copy_of_update = frame;
+ PopulateFrameInfo(QuicFrame(&copy_of_update), event->add_frames());
+}
+
+void QuicTraceVisitor::OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) {
+ uint32_t tag = QuicEndian::HostToNet32(CreateQuicVersionLabel(version));
+ std::string binary_tag(reinterpret_cast<const char*>(&tag), sizeof(tag));
+ trace_.set_protocol_version(binary_tag);
+}
+
+void QuicTraceVisitor::OnApplicationLimited() {
+ quic_trace::Event* event = trace_.add_events();
+ event->set_time_us(
+ ConvertTimestampToRecordedFormat(connection_->clock()->ApproximateNow()));
+ event->set_event_type(quic_trace::APPLICATION_LIMITED);
+}
+
+void QuicTraceVisitor::OnAdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) {
+ quic_trace::Event* event = trace_.add_events();
+ event->set_time_us(
+ ConvertTimestampToRecordedFormat(connection_->clock()->ApproximateNow()));
+ event->set_event_type(quic_trace::EXTERNAL_PARAMETERS);
+
+ quic_trace::ExternalNetworkParameters* parameters =
+ event->mutable_external_network_parameters();
+ if (!bandwidth.IsZero()) {
+ parameters->set_bandwidth_bps(bandwidth.ToBitsPerSecond());
+ }
+ if (!rtt.IsZero()) {
+ parameters->set_rtt_us(rtt.ToMicroseconds());
+ }
+}
+
+uint64_t QuicTraceVisitor::ConvertTimestampToRecordedFormat(
+ QuicTime timestamp) {
+ if (timestamp < start_time_) {
+ QUIC_BUG << "Timestamp went back in time while recording a trace";
+ return 0;
+ }
+
+ return (timestamp - start_time_).ToMicroseconds();
+}
+
+void QuicTraceVisitor::PopulateTransportState(
+ quic_trace::TransportState* state) {
+ const RttStats* rtt_stats = connection_->sent_packet_manager().GetRttStats();
+ state->set_min_rtt_us(rtt_stats->min_rtt().ToMicroseconds());
+ state->set_smoothed_rtt_us(rtt_stats->smoothed_rtt().ToMicroseconds());
+ state->set_last_rtt_us(rtt_stats->latest_rtt().ToMicroseconds());
+
+ state->set_cwnd_bytes(
+ connection_->sent_packet_manager().GetCongestionWindowInBytes());
+ QuicByteCount in_flight =
+ connection_->sent_packet_manager().GetBytesInFlight();
+ state->set_in_flight_bytes(in_flight);
+ state->set_pacing_rate_bps(connection_->sent_packet_manager()
+ .GetSendAlgorithm()
+ ->PacingRate(in_flight)
+ .ToBitsPerSecond());
+
+ if (connection_->sent_packet_manager()
+ .GetSendAlgorithm()
+ ->GetCongestionControlType() == kPCC) {
+ state->set_congestion_control_state(
+ connection_->sent_packet_manager().GetSendAlgorithm()->GetDebugState());
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.h b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.h
new file mode 100644
index 00000000000..a70b150d653
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor.h
@@ -0,0 +1,70 @@
+// 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_CORE_QUIC_TRACE_VISITOR_H_
+#define QUICHE_QUIC_CORE_QUIC_TRACE_VISITOR_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "third_party/quic_trace/lib/quic_trace.pb.h"
+
+namespace quic {
+
+// Records a QUIC trace protocol buffer for a QuicConnection. It's the
+// responsibility of the user of this visitor to process or store the resulting
+// trace, which can be accessed via trace().
+class QuicTraceVisitor : public QuicConnectionDebugVisitor {
+ public:
+ explicit QuicTraceVisitor(const QuicConnection* connection);
+
+ void OnPacketSent(const SerializedPacket& serialized_packet,
+ QuicPacketNumber original_packet_number,
+ TransmissionType transmission_type,
+ QuicTime sent_time) override;
+
+ void OnIncomingAck(const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time,
+ QuicPacketNumber largest_observed,
+ bool rtt_updated,
+ QuicPacketNumber least_unacked_sent_packet) override;
+
+ void OnPacketLoss(QuicPacketNumber lost_packet_number,
+ TransmissionType transmission_type,
+ QuicTime detection_time) override;
+
+ void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ const QuicTime& receive_time) override;
+
+ void OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) override;
+
+ void OnApplicationLimited() override;
+
+ void OnAdjustNetworkParameters(QuicBandwidth bandwidth,
+ QuicTime::Delta rtt) override;
+
+ // Returns a mutable pointer to the trace. The trace is owned by the
+ // visitor, but can be moved using Swap() method after the connection is
+ // finished.
+ quic_trace::Trace* trace() { return &trace_; }
+
+ private:
+ // Converts QuicTime into a microsecond delta w.r.t. the beginning of the
+ // connection.
+ uint64_t ConvertTimestampToRecordedFormat(QuicTime timestamp);
+ // Populates a quic_trace::Frame message from |frame|.
+ void PopulateFrameInfo(const QuicFrame& frame,
+ quic_trace::Frame* frame_record);
+ // Populates a quic_trace::TransportState message from the associated
+ // connection.
+ void PopulateTransportState(quic_trace::TransportState* state);
+
+ quic_trace::Trace trace_;
+ const QuicConnection* connection_;
+ const QuicTime start_time_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_TRACE_VISITOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor_test.cc
new file mode 100644
index 00000000000..1a581534a9c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_trace_visitor_test.cc
@@ -0,0 +1,168 @@
+// 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 "net/third_party/quiche/src/quic/core/quic_trace_visitor.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+
+namespace quic {
+namespace {
+
+const QuicByteCount kTransferSize = 1000 * kMaxOutgoingPacketSize;
+const QuicByteCount kTestStreamNumber = 3;
+const QuicTime::Delta kDelay = QuicTime::Delta::FromMilliseconds(20);
+
+// The trace for this test is generated using a simulator transfer.
+class QuicTraceVisitorTest : public QuicTest {
+ public:
+ QuicTraceVisitorTest() {
+ QuicConnectionId connection_id = test::TestConnectionId();
+ simulator::Simulator simulator;
+ simulator::QuicEndpoint client(&simulator, "Client", "Server",
+ Perspective::IS_CLIENT, connection_id);
+ simulator::QuicEndpoint server(&simulator, "Server", "Client",
+ Perspective::IS_SERVER, connection_id);
+
+ const QuicBandwidth kBandwidth = QuicBandwidth::FromKBitsPerSecond(1000);
+ const QuicByteCount kBdp = kBandwidth * (2 * kDelay);
+
+ // Create parameters such that some loss is observed.
+ simulator::Switch network_switch(&simulator, "Switch", 8, 0.5 * kBdp);
+ simulator::SymmetricLink client_link(&client, network_switch.port(1),
+ 2 * kBandwidth, kDelay);
+ simulator::SymmetricLink server_link(&server, network_switch.port(2),
+ kBandwidth, kDelay);
+
+ QuicTraceVisitor visitor(client.connection());
+ client.connection()->set_debug_visitor(&visitor);
+
+ // Transfer about a megabyte worth of data from client to server.
+ const QuicTime::Delta kDeadline =
+ 3 * kBandwidth.TransferTime(kTransferSize);
+ client.AddBytesToTransfer(kTransferSize);
+ bool simulator_result = simulator.RunUntilOrTimeout(
+ [&]() { return server.bytes_received() >= kTransferSize; }, kDeadline);
+ CHECK(simulator_result);
+
+ // Save the trace and ensure some loss was observed.
+ trace_.Swap(visitor.trace());
+ CHECK_NE(0u, client.connection()->GetStats().packets_retransmitted);
+ packets_sent_ = client.connection()->GetStats().packets_sent;
+ }
+
+ std::vector<quic_trace::Event> AllEventsWithType(
+ quic_trace::EventType event_type) {
+ std::vector<quic_trace::Event> result;
+ for (const auto& event : trace_.events()) {
+ if (event.event_type() == event_type) {
+ result.push_back(event);
+ }
+ }
+ return result;
+ }
+
+ protected:
+ quic_trace::Trace trace_;
+ QuicPacketCount packets_sent_;
+};
+
+TEST_F(QuicTraceVisitorTest, ConnectionId) {
+ char expected_cid[] = {0, 0, 0, 0, 0, 0, 0, 42};
+ EXPECT_EQ(std::string(expected_cid, sizeof(expected_cid)),
+ trace_.destination_connection_id());
+}
+
+TEST_F(QuicTraceVisitorTest, Version) {
+ std::string version = trace_.protocol_version();
+ ASSERT_EQ(4u, version.size());
+ EXPECT_EQ('Q', version[0]);
+}
+
+// Check that basic metadata about sent packets is recorded.
+TEST_F(QuicTraceVisitorTest, SentPacket) {
+ auto sent_packets = AllEventsWithType(quic_trace::PACKET_SENT);
+ EXPECT_EQ(packets_sent_, sent_packets.size());
+ ASSERT_GT(sent_packets.size(), 0u);
+
+ EXPECT_EQ(sent_packets[0].packet_size(), kDefaultMaxPacketSize);
+ EXPECT_EQ(sent_packets[0].packet_number(), 1u);
+}
+
+// Ensure that every stream frame that was sent is recorded.
+TEST_F(QuicTraceVisitorTest, SentStream) {
+ auto sent_packets = AllEventsWithType(quic_trace::PACKET_SENT);
+
+ QuicIntervalSet<QuicStreamOffset> offsets;
+ for (const quic_trace::Event& packet : sent_packets) {
+ for (const quic_trace::Frame& frame : packet.frames()) {
+ if (frame.frame_type() != quic_trace::STREAM) {
+ continue;
+ }
+
+ const quic_trace::StreamFrameInfo& info = frame.stream_frame_info();
+ if (info.stream_id() != kTestStreamNumber) {
+ continue;
+ }
+
+ ASSERT_GT(info.length(), 0u);
+ offsets.Add(info.offset(), info.offset() + info.length());
+ }
+ }
+
+ ASSERT_EQ(1u, offsets.Size());
+ EXPECT_EQ(0u, offsets.begin()->min());
+ EXPECT_EQ(kTransferSize, offsets.rbegin()->max());
+}
+
+// Ensure that all packets are either acknowledged or lost.
+TEST_F(QuicTraceVisitorTest, AckPackets) {
+ QuicIntervalSet<QuicPacketNumber> packets;
+ for (const quic_trace::Event& packet : trace_.events()) {
+ if (packet.event_type() == quic_trace::PACKET_RECEIVED) {
+ for (const quic_trace::Frame& frame : packet.frames()) {
+ if (frame.frame_type() != quic_trace::ACK) {
+ continue;
+ }
+
+ const quic_trace::AckInfo& info = frame.ack_info();
+ for (const auto& block : info.acked_packets()) {
+ packets.Add(QuicPacketNumber(block.first_packet()),
+ QuicPacketNumber(block.last_packet()) + 1);
+ }
+ }
+ }
+ if (packet.event_type() == quic_trace::PACKET_LOST) {
+ packets.Add(QuicPacketNumber(packet.packet_number()),
+ QuicPacketNumber(packet.packet_number()) + 1);
+ }
+ }
+
+ ASSERT_EQ(1u, packets.Size());
+ EXPECT_EQ(QuicPacketNumber(1u), packets.begin()->min());
+ // We leave some room (20 packets) for the packets which did not receive
+ // conclusive status at the end of simulation.
+ EXPECT_GT(packets.rbegin()->max(), QuicPacketNumber(packets_sent_ - 20));
+}
+
+TEST_F(QuicTraceVisitorTest, TransportState) {
+ auto acks = AllEventsWithType(quic_trace::PACKET_RECEIVED);
+ ASSERT_EQ(1, acks[0].frames_size());
+ ASSERT_EQ(quic_trace::ACK, acks[0].frames(0).frame_type());
+
+ // Check that min-RTT at the end is a reasonable approximation.
+ EXPECT_LE((4 * kDelay).ToMicroseconds() * 1.,
+ acks.rbegin()->transport_state().min_rtt_us());
+ EXPECT_GE((4 * kDelay).ToMicroseconds() * 1.25,
+ acks.rbegin()->transport_state().min_rtt_us());
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_transmission_info.cc b/chromium/net/third_party/quiche/src/quic/core/quic_transmission_info.cc
new file mode 100644
index 00000000000..fb5cf670063
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_transmission_info.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h"
+
+namespace quic {
+
+QuicTransmissionInfo::QuicTransmissionInfo()
+ : encryption_level(ENCRYPTION_INITIAL),
+ packet_number_length(PACKET_1BYTE_PACKET_NUMBER),
+ bytes_sent(0),
+ sent_time(QuicTime::Zero()),
+ transmission_type(NOT_RETRANSMISSION),
+ in_flight(false),
+ state(OUTSTANDING),
+ has_crypto_handshake(false),
+ num_padding_bytes(0) {}
+
+QuicTransmissionInfo::QuicTransmissionInfo(
+ EncryptionLevel level,
+ QuicPacketNumberLength packet_number_length,
+ TransmissionType transmission_type,
+ QuicTime sent_time,
+ QuicPacketLength bytes_sent,
+ bool has_crypto_handshake,
+ int num_padding_bytes)
+ : encryption_level(level),
+ packet_number_length(packet_number_length),
+ bytes_sent(bytes_sent),
+ sent_time(sent_time),
+ transmission_type(transmission_type),
+ in_flight(false),
+ state(OUTSTANDING),
+ has_crypto_handshake(has_crypto_handshake),
+ num_padding_bytes(num_padding_bytes) {}
+
+QuicTransmissionInfo::QuicTransmissionInfo(const QuicTransmissionInfo& other) =
+ default;
+
+QuicTransmissionInfo::~QuicTransmissionInfo() {}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_transmission_info.h b/chromium/net/third_party/quiche/src/quic/core/quic_transmission_info.h
new file mode 100644
index 00000000000..74b18d2c4ac
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_transmission_info.h
@@ -0,0 +1,67 @@
+// 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 QUICHE_QUIC_CORE_QUIC_TRANSMISSION_INFO_H_
+#define QUICHE_QUIC_CORE_QUIC_TRANSMISSION_INFO_H_
+
+#include <list>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Stores details of a single sent packet.
+struct QUIC_EXPORT_PRIVATE QuicTransmissionInfo {
+ // Used by STL when assigning into a map.
+ QuicTransmissionInfo();
+
+ // Constructs a Transmission with a new all_transmissions set
+ // containing |packet_number|.
+ QuicTransmissionInfo(EncryptionLevel level,
+ QuicPacketNumberLength packet_number_length,
+ TransmissionType transmission_type,
+ QuicTime sent_time,
+ QuicPacketLength bytes_sent,
+ bool has_crypto_handshake,
+ int num_padding_bytes);
+
+ QuicTransmissionInfo(const QuicTransmissionInfo& other);
+
+ ~QuicTransmissionInfo();
+
+ QuicFrames retransmittable_frames;
+ EncryptionLevel encryption_level;
+ QuicPacketNumberLength packet_number_length;
+ QuicPacketLength bytes_sent;
+ QuicTime sent_time;
+ // Reason why this packet was transmitted.
+ TransmissionType transmission_type;
+ // In flight packets have not been abandoned or lost.
+ bool in_flight;
+ // State of this packet.
+ SentPacketState state;
+ // True if the packet contains stream data from the crypto stream.
+ bool has_crypto_handshake;
+ // Non-zero if the packet needs padding if it's retransmitted.
+ int16_t num_padding_bytes;
+ // Stores the packet number of the next retransmission of this packet.
+ // Zero if the packet has not been retransmitted.
+ // TODO(fayang): rename this to first_sent_after_loss_ when deprecating
+ // QUIC_VERSION_41.
+ QuicPacketNumber retransmission;
+ // The largest_acked in the ack frame, if the packet contains an ack.
+ QuicPacketNumber largest_acked;
+};
+// TODO(ianswett): Add static_assert when size of this struct is reduced below
+// 64 bytes.
+// NOTE(vlovich): Existing static_assert removed because padding differences on
+// 64-bit iOS resulted in an 88-byte struct that is greater than the 84-byte
+// limit on other platforms. Removing per ianswett's request.
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_TRANSMISSION_INFO_H_
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
new file mode 100644
index 00000000000..c9e21d6190f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.cc
@@ -0,0 +1,121 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+QuicConsumedData::QuicConsumedData(size_t bytes_consumed, bool fin_consumed)
+ : bytes_consumed(bytes_consumed), fin_consumed(fin_consumed) {}
+
+std::ostream& operator<<(std::ostream& os, const QuicConsumedData& s) {
+ os << "bytes_consumed: " << s.bytes_consumed
+ << " fin_consumed: " << s.fin_consumed;
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const Perspective& s) {
+ if (s == Perspective::IS_SERVER) {
+ os << "IS_SERVER";
+ } else {
+ os << "IS_CLIENT";
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const AckedPacket& acked_packet) {
+ os << "{ packet_number: " << acked_packet.packet_number
+ << ", bytes_acked: " << acked_packet.bytes_acked << ", receive_timestamp: "
+ << acked_packet.receive_timestamp.ToDebuggingValue() << "} ";
+ return os;
+}
+
+std::string HistogramEnumString(WriteStatus enum_value) {
+ switch (enum_value) {
+ case WRITE_STATUS_OK:
+ return "OK";
+ case WRITE_STATUS_BLOCKED:
+ return "BLOCKED";
+ case WRITE_STATUS_BLOCKED_DATA_BUFFERED:
+ return "BLOCKED_DATA_BUFFERED";
+ case WRITE_STATUS_ERROR:
+ return "ERROR";
+ case WRITE_STATUS_MSG_TOO_BIG:
+ return "MSG_TOO_BIG";
+ case WRITE_STATUS_NUM_VALUES:
+ return "NUM_VALUES";
+ }
+ QUIC_DLOG(ERROR) << "Invalid WriteStatus value: " << enum_value;
+ return "<invalid>";
+}
+
+WriteResult::WriteResult() : status(WRITE_STATUS_ERROR), bytes_written(0) {}
+
+WriteResult::WriteResult(WriteStatus status, int bytes_written_or_error_code)
+ : status(status), bytes_written(bytes_written_or_error_code) {}
+
+std::ostream& operator<<(std::ostream& os, const WriteResult& s) {
+ os << "{ status: " << s.status;
+ if (s.status == WRITE_STATUS_OK) {
+ os << ", bytes_written: " << s.bytes_written;
+ } else {
+ os << ", error_code: " << s.error_code;
+ }
+ os << " }";
+ return os;
+}
+
+MessageResult::MessageResult(MessageStatus status, QuicMessageId message_id)
+ : status(status), message_id(message_id) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicIetfTransportErrorCodes& c) {
+ if (static_cast<uint16_t>(c) >= 0xff00u) {
+ os << "Private value: " << c;
+ return os;
+ }
+
+ switch (c) {
+ case NO_IETF_QUIC_ERROR:
+ os << "NO_IETF_QUIC_ERROR";
+ break;
+ case INTERNAL_ERROR:
+ os << "INTERNAL_ERROR";
+ break;
+ case SERVER_BUSY_ERROR:
+ os << "SERVER_BUSY_ERROR";
+ break;
+ case FLOW_CONTROL_ERROR:
+ os << "FLOW_CONTROL_ERROR";
+ break;
+ case STREAM_LIMIT_ERROR:
+ os << "STREAM_LIMIT_ERROR";
+ break;
+ case STREAM_STATE_ERROR:
+ os << "STREAM_STATE_ERROR";
+ break;
+ case FINAL_SIZE_ERROR:
+ os << "FINAL_SIZE_ERROR";
+ break;
+ case FRAME_ENCODING_ERROR:
+ os << "FRAME_ENCODING_ERROR";
+ break;
+ case TRANSPORT_PARAMETER_ERROR:
+ os << "TRANSPORT_PARAMETER_ERROR";
+ break;
+ case VERSION_NEGOTIATION_ERROR:
+ os << "VERSION_NEGOTIATION_ERROR";
+ break;
+ case PROTOCOL_VIOLATION:
+ os << "PROTOCOL_VIOLATION";
+ break;
+ case INVALID_MIGRATION:
+ os << "INVALID_MIGRATION";
+ break;
+ // No default -- compiler will catch any adds/changes then.
+ }
+ return os;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..6f4cbe615c2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_types.h
@@ -0,0 +1,605 @@
+// Copyright 2014 The Chromium 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_TYPES_H_
+#define QUICHE_QUIC_CORE_QUIC_TYPES_H_
+
+#include <array>
+#include <cstddef>
+#include <map>
+#include <ostream>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_number.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+typedef uint16_t QuicPacketLength;
+typedef uint32_t QuicControlFrameId;
+typedef uint32_t QuicHeaderId;
+typedef uint32_t QuicMessageId;
+
+// TODO(fkastenholz): Should update this to 64 bits for V99.
+typedef uint32_t QuicStreamId;
+
+typedef uint64_t QuicByteCount;
+typedef uint64_t QuicPacketCount;
+typedef uint64_t QuicPublicResetNonceProof;
+typedef uint64_t QuicStreamOffset;
+typedef std::array<char, 32> DiversificationNonce;
+typedef std::vector<std::pair<QuicPacketNumber, QuicTime>> PacketTimeVector;
+
+typedef uint64_t QuicIetfStreamDataLength;
+typedef uint64_t QuicIetfStreamId;
+typedef uint64_t QuicIetfStreamOffset;
+
+const size_t kQuicPathFrameBufferSize = 8;
+typedef std::array<uint8_t, kQuicPathFrameBufferSize> QuicPathFrameBuffer;
+
+// Application error code used in the QUIC Stop Sending frame.
+typedef uint16_t QuicApplicationErrorCode;
+
+// The connection id sequence number specifies the order that connection
+// ids must be used in. This is also the sequence number carried in
+// the IETF QUIC NEW_CONNECTION_ID and RETIRE_CONNECTION_ID frames.
+typedef uint64_t QuicConnectionIdSequenceNumber;
+
+// A struct for functions which consume data payloads and fins.
+struct QUIC_EXPORT_PRIVATE QuicConsumedData {
+ QuicConsumedData(size_t bytes_consumed, bool fin_consumed);
+
+ // By default, gtest prints the raw bytes of an object. The bool data
+ // member causes this object to have padding bytes, which causes the
+ // default gtest object printer to read uninitialize memory. So we need
+ // to teach gtest how to print this object.
+ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os,
+ const QuicConsumedData& s);
+
+ // How many bytes were consumed.
+ size_t bytes_consumed;
+
+ // True if an incoming fin was consumed.
+ bool fin_consumed;
+};
+
+// QuicAsyncStatus enumerates the possible results of an asynchronous
+// operation.
+enum QuicAsyncStatus {
+ QUIC_SUCCESS = 0,
+ QUIC_FAILURE = 1,
+ // QUIC_PENDING results from an operation that will occur asynchronously. When
+ // the operation is complete, a callback's |Run| method will be called.
+ QUIC_PENDING = 2,
+};
+
+// TODO(wtc): see if WriteStatus can be replaced by QuicAsyncStatus.
+enum WriteStatus {
+ WRITE_STATUS_OK,
+ // Write is blocked, caller needs to retry.
+ WRITE_STATUS_BLOCKED,
+ // Write is blocked but the packet data is buffered, caller should not retry.
+ WRITE_STATUS_BLOCKED_DATA_BUFFERED,
+ // To make the IsWriteError(WriteStatus) function work properly:
+ // - Non-errors MUST be added before WRITE_STATUS_ERROR.
+ // - Errors MUST be added after WRITE_STATUS_ERROR.
+ WRITE_STATUS_ERROR,
+ WRITE_STATUS_MSG_TOO_BIG,
+ WRITE_STATUS_NUM_VALUES,
+};
+
+std::string HistogramEnumString(WriteStatus enum_value);
+
+inline std::string HistogramEnumDescription(WriteStatus /*dummy*/) {
+ return "status";
+}
+
+inline bool IsWriteBlockedStatus(WriteStatus status) {
+ return status == WRITE_STATUS_BLOCKED ||
+ status == WRITE_STATUS_BLOCKED_DATA_BUFFERED;
+}
+
+inline bool IsWriteError(WriteStatus status) {
+ return status >= WRITE_STATUS_ERROR;
+}
+
+// A struct used to return the result of write calls including either the number
+// of bytes written or the error code, depending upon the status.
+struct QUIC_EXPORT_PRIVATE WriteResult {
+ WriteResult(WriteStatus status, int bytes_written_or_error_code);
+ WriteResult();
+
+ bool operator==(const WriteResult& other) const {
+ if (status != other.status) {
+ return false;
+ }
+ switch (status) {
+ case WRITE_STATUS_OK:
+ return bytes_written == other.bytes_written;
+ case WRITE_STATUS_BLOCKED:
+ case WRITE_STATUS_BLOCKED_DATA_BUFFERED:
+ return true;
+ default:
+ return error_code == other.error_code;
+ }
+ }
+
+ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os,
+ const WriteResult& s);
+
+ WriteStatus status;
+ union {
+ int bytes_written; // only valid when status is WRITE_STATUS_OK
+ int error_code; // only valid when status is WRITE_STATUS_ERROR
+ };
+};
+
+enum TransmissionType : int8_t {
+ NOT_RETRANSMISSION,
+ FIRST_TRANSMISSION_TYPE = NOT_RETRANSMISSION,
+ HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts.
+ ALL_UNACKED_RETRANSMISSION, // Retransmits all unacked packets.
+ ALL_INITIAL_RETRANSMISSION, // Retransmits all initially encrypted packets.
+ LOSS_RETRANSMISSION, // Retransmits due to loss detection.
+ RTO_RETRANSMISSION, // Retransmits due to retransmit time out.
+ TLP_RETRANSMISSION, // Tail loss probes.
+ PROBING_RETRANSMISSION, // Retransmission in order to probe bandwidth.
+ LAST_TRANSMISSION_TYPE = PROBING_RETRANSMISSION,
+};
+
+enum HasRetransmittableData : uint8_t {
+ NO_RETRANSMITTABLE_DATA,
+ HAS_RETRANSMITTABLE_DATA,
+};
+
+enum IsHandshake : uint8_t { NOT_HANDSHAKE, IS_HANDSHAKE };
+
+enum class Perspective : uint8_t { IS_SERVER, IS_CLIENT };
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+ const Perspective& s);
+
+// Describes whether a ConnectionClose was originated by the peer.
+enum class ConnectionCloseSource { FROM_PEER, FROM_SELF };
+
+// Should a connection be closed silently or not.
+enum class ConnectionCloseBehavior {
+ SILENT_CLOSE,
+ SEND_CONNECTION_CLOSE_PACKET,
+ SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK
+};
+
+enum QuicFrameType : uint8_t {
+ // Regular frame types. The values set here cannot change without the
+ // introduction of a new QUIC version.
+ PADDING_FRAME = 0,
+ RST_STREAM_FRAME = 1,
+ CONNECTION_CLOSE_FRAME = 2,
+ GOAWAY_FRAME = 3,
+ WINDOW_UPDATE_FRAME = 4,
+ BLOCKED_FRAME = 5,
+ STOP_WAITING_FRAME = 6,
+ PING_FRAME = 7,
+ CRYPTO_FRAME = 8,
+
+ // STREAM and ACK frames are special frames. They are encoded differently on
+ // the wire and their values do not need to be stable.
+ STREAM_FRAME,
+ ACK_FRAME,
+ // The path MTU discovery frame is encoded as a PING frame on the wire.
+ MTU_DISCOVERY_FRAME,
+
+ // These are for IETF-specific frames for which there is no mapping
+ // from Google QUIC frames. These are valid/allowed if and only if IETF-
+ // QUIC has been negotiated. Values are not important, they are not
+ // the values that are in the packets (see QuicIetfFrameType, below).
+ NEW_CONNECTION_ID_FRAME,
+ MAX_STREAM_ID_FRAME,
+ STREAM_ID_BLOCKED_FRAME,
+ PATH_RESPONSE_FRAME,
+ PATH_CHALLENGE_FRAME,
+ STOP_SENDING_FRAME,
+ MESSAGE_FRAME,
+ NEW_TOKEN_FRAME,
+ RETIRE_CONNECTION_ID_FRAME,
+
+ NUM_FRAME_TYPES
+};
+
+// Ietf frame types. These are defined in the IETF QUIC Specification.
+// Explicit values are given in the enum so that we can be sure that
+// the symbol will map to the correct stream type.
+// All types are defined here, even if we have not yet implmented the
+// quic/core/stream/.... stuff needed.
+// Note: The protocol specifies that frame types are varint-62 encoded,
+// further stating that the shortest encoding must be used. The current set of
+// frame types all have values less than 0x40 (64) so can be encoded in a single
+// byte, with the two most significant bits being 0. Thus, the following
+// enumerations are valid as both the numeric values of frame types AND their
+// encodings.
+enum QuicIetfFrameType : uint8_t {
+ IETF_PADDING = 0x00,
+ IETF_PING = 0x01,
+ IETF_ACK = 0x02,
+ IETF_ACK_ECN = 0x03,
+ IETF_RST_STREAM = 0x04,
+ IETF_STOP_SENDING = 0x05,
+ IETF_CRYPTO = 0x06,
+ IETF_NEW_TOKEN = 0x07,
+ // the low-3 bits of the stream frame type value are actually flags
+ // declaring what parts of the frame are/are-not present, as well as
+ // some other control information. The code would then do something
+ // along the lines of "if ((frame_type & 0xf8) == 0x08)" to determine
+ // whether the frame is a stream frame or not, and then examine each
+ // bit specifically when/as needed.
+ IETF_STREAM = 0x08,
+ // 0x09 through 0x0f are various flag settings of the IETF_STREAM frame.
+ IETF_MAX_DATA = 0x10,
+ IETF_MAX_STREAM_DATA = 0x11,
+ IETF_MAX_STREAMS_BIDIRECTIONAL = 0x12,
+ IETF_MAX_STREAMS_UNIDIRECTIONAL = 0x13,
+ IETF_BLOCKED = 0x14, // TODO(fkastenholz): Should, eventually, be renamed to
+ // IETF_DATA_BLOCKED
+ IETF_STREAM_BLOCKED = 0x15, // TODO(fkastenholz): Should, eventually, be
+ // renamed to IETF_STREAM_DATA_BLOCKED
+ IETF_STREAMS_BLOCKED_BIDIRECTIONAL = 0x16,
+ IETF_STREAMS_BLOCKED_UNIDIRECTIONAL = 0x17,
+ IETF_NEW_CONNECTION_ID = 0x18,
+ IETF_RETIRE_CONNECTION_ID = 0x19,
+ IETF_PATH_CHALLENGE = 0x1a,
+ IETF_PATH_RESPONSE = 0x1b,
+ // Both of the following are "Connection Close" frames,
+ // the first signals transport-layer errors, the second application-layer
+ // errors.
+ IETF_CONNECTION_CLOSE = 0x1c,
+ IETF_APPLICATION_CLOSE = 0x1d,
+
+ // MESSAGE frame type is not yet determined, use 0x2x temporarily to give
+ // stream frame some wiggle room.
+ IETF_EXTENSION_MESSAGE_NO_LENGTH = 0x20,
+ IETF_EXTENSION_MESSAGE = 0x21,
+};
+// Masks for the bits that indicate the frame is a Stream frame vs the
+// bits used as flags.
+#define IETF_STREAM_FRAME_TYPE_MASK 0xfffffffffffffff8
+#define IETF_STREAM_FRAME_FLAG_MASK 0x07
+#define IS_IETF_STREAM_FRAME(_stype_) \
+ (((_stype_)&IETF_STREAM_FRAME_TYPE_MASK) == IETF_STREAM)
+
+// These are the values encoded in the low-order 3 bits of the
+// IETF_STREAMx frame type.
+#define IETF_STREAM_FRAME_FIN_BIT 0x01
+#define IETF_STREAM_FRAME_LEN_BIT 0x02
+#define IETF_STREAM_FRAME_OFF_BIT 0x04
+
+enum QuicVariableLengthIntegerLength : uint8_t {
+ // Length zero means the variable length integer is not present.
+ VARIABLE_LENGTH_INTEGER_LENGTH_0 = 0,
+ VARIABLE_LENGTH_INTEGER_LENGTH_1 = 1,
+ VARIABLE_LENGTH_INTEGER_LENGTH_2 = 2,
+ VARIABLE_LENGTH_INTEGER_LENGTH_4 = 4,
+ VARIABLE_LENGTH_INTEGER_LENGTH_8 = 8,
+};
+
+// By default we write the IETF long header length using the 2-byte encoding
+// of variable length integers, even when the length is below 64, which allows
+// us to fill in the length before knowing what the length actually is.
+const QuicVariableLengthIntegerLength kQuicDefaultLongHeaderLengthLength =
+ VARIABLE_LENGTH_INTEGER_LENGTH_2;
+
+enum QuicPacketNumberLength : uint8_t {
+ PACKET_1BYTE_PACKET_NUMBER = 1,
+ PACKET_2BYTE_PACKET_NUMBER = 2,
+ PACKET_3BYTE_PACKET_NUMBER = 3, // Used in version > QUIC_VERSION_44.
+ PACKET_4BYTE_PACKET_NUMBER = 4,
+ // TODO(rch): Remove this when we remove QUIC_VERSION_39.
+ PACKET_6BYTE_PACKET_NUMBER = 6,
+ PACKET_8BYTE_PACKET_NUMBER = 8
+};
+
+// Used to indicate a QuicSequenceNumberLength using two flag bits.
+enum QuicPacketNumberLengthFlags {
+ PACKET_FLAGS_1BYTE_PACKET = 0, // 00
+ PACKET_FLAGS_2BYTE_PACKET = 1, // 01
+ PACKET_FLAGS_4BYTE_PACKET = 1 << 1, // 10
+ PACKET_FLAGS_8BYTE_PACKET = 1 << 1 | 1, // 11
+};
+
+// The public flags are specified in one byte.
+enum QuicPacketPublicFlags {
+ PACKET_PUBLIC_FLAGS_NONE = 0,
+
+ // Bit 0: Does the packet header contains version info?
+ PACKET_PUBLIC_FLAGS_VERSION = 1 << 0,
+
+ // Bit 1: Is this packet a public reset packet?
+ PACKET_PUBLIC_FLAGS_RST = 1 << 1,
+
+ // Bit 2: indicates the header includes a nonce.
+ PACKET_PUBLIC_FLAGS_NONCE = 1 << 2,
+
+ // Bit 3: indicates whether a ConnectionID is included.
+ PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID = 0,
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 1 << 3,
+
+ // QUIC_VERSION_32 and earlier use two bits for an 8 byte
+ // connection id.
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD = 1 << 3 | 1 << 2,
+
+ // Bits 4 and 5 describe the packet number length as follows:
+ // --00----: 1 byte
+ // --01----: 2 bytes
+ // --10----: 4 bytes
+ // --11----: 6 bytes
+ PACKET_PUBLIC_FLAGS_1BYTE_PACKET = PACKET_FLAGS_1BYTE_PACKET << 4,
+ PACKET_PUBLIC_FLAGS_2BYTE_PACKET = PACKET_FLAGS_2BYTE_PACKET << 4,
+ PACKET_PUBLIC_FLAGS_4BYTE_PACKET = PACKET_FLAGS_4BYTE_PACKET << 4,
+ PACKET_PUBLIC_FLAGS_6BYTE_PACKET = PACKET_FLAGS_8BYTE_PACKET << 4,
+
+ // Reserved, unimplemented flags:
+
+ // Bit 7: indicates the presence of a second flags byte.
+ PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7,
+
+ // All bits set (bits 6 and 7 are not currently used): 00111111
+ PACKET_PUBLIC_FLAGS_MAX = (1 << 6) - 1,
+};
+
+// The private flags are specified in one byte.
+enum QuicPacketPrivateFlags {
+ PACKET_PRIVATE_FLAGS_NONE = 0,
+
+ // Bit 0: Does this packet contain an entropy bit?
+ PACKET_PRIVATE_FLAGS_ENTROPY = 1 << 0,
+
+ // (bits 1-7 are not used): 00000001
+ PACKET_PRIVATE_FLAGS_MAX = (1 << 1) - 1
+};
+
+// Defines for all types of congestion control algorithms that can be used in
+// QUIC. Note that this is separate from the congestion feedback type -
+// some congestion control algorithms may use the same feedback type
+// (Reno and Cubic are the classic example for that).
+enum CongestionControlType { kCubicBytes, kRenoBytes, kBBR, kPCC, kGoogCC };
+
+enum LossDetectionType : uint8_t {
+ kNack, // Used to mimic TCP's loss detection.
+ kTime, // Time based loss detection.
+ kAdaptiveTime, // Adaptive time based loss detection.
+ kLazyFack, // Nack based but with FACK disabled for the first ack.
+};
+
+// EncryptionLevel enumerates the stages of encryption that a QUIC connection
+// progresses through. When retransmitting a packet, the encryption level needs
+// to be specified so that it is retransmitted at a level which the peer can
+// understand.
+enum EncryptionLevel : int8_t {
+ ENCRYPTION_INITIAL = 0,
+ ENCRYPTION_HANDSHAKE = 1,
+ ENCRYPTION_ZERO_RTT = 2,
+ ENCRYPTION_FORWARD_SECURE = 3,
+
+ NUM_ENCRYPTION_LEVELS,
+};
+
+enum AddressChangeType : uint8_t {
+ // IP address and port remain unchanged.
+ NO_CHANGE,
+ // Port changed, but IP address remains unchanged.
+ PORT_CHANGE,
+ // IPv4 address changed, but within the /24 subnet (port may have changed.)
+ IPV4_SUBNET_CHANGE,
+ // IPv4 address changed, excluding /24 subnet change (port may have changed.)
+ IPV4_TO_IPV4_CHANGE,
+ // IP address change from an IPv4 to an IPv6 address (port may have changed.)
+ IPV4_TO_IPV6_CHANGE,
+ // IP address change from an IPv6 to an IPv4 address (port may have changed.)
+ IPV6_TO_IPV4_CHANGE,
+ // IP address change from an IPv6 to an IPv6 address (port may have changed.)
+ IPV6_TO_IPV6_CHANGE,
+};
+
+enum StreamSendingState {
+ // Sender has more data to send on this stream.
+ NO_FIN,
+ // Sender is done sending on this stream.
+ FIN,
+ // Sender is done sending on this stream and random padding needs to be
+ // appended after all stream frames.
+ FIN_AND_PADDING,
+};
+
+enum SentPacketState : uint8_t {
+ // The packet has been sent and waiting to be acked.
+ OUTSTANDING,
+ FIRST_PACKET_STATE = OUTSTANDING,
+ // The packet was never sent.
+ NEVER_SENT,
+ // The packet has been acked.
+ ACKED,
+ // This packet is not expected to be acked.
+ UNACKABLE,
+
+ // States below are corresponding to retransmission types in TransmissionType.
+
+ // This packet has been retransmitted when retransmission timer fires in
+ // HANDSHAKE mode.
+ HANDSHAKE_RETRANSMITTED,
+ // This packet is considered as lost, this is used for LOST_RETRANSMISSION.
+ LOST,
+ // This packet has been retransmitted when TLP fires.
+ TLP_RETRANSMITTED,
+ // This packet has been retransmitted when RTO fires.
+ RTO_RETRANSMITTED,
+ // This packet has been retransmitted for probing purpose.
+ PROBE_RETRANSMITTED,
+ LAST_PACKET_STATE = PROBE_RETRANSMITTED,
+};
+
+enum PacketHeaderFormat : uint8_t {
+ IETF_QUIC_LONG_HEADER_PACKET,
+ IETF_QUIC_SHORT_HEADER_PACKET,
+ GOOGLE_QUIC_PACKET,
+};
+
+// Information about a newly acknowledged packet.
+struct AckedPacket {
+ AckedPacket(QuicPacketNumber packet_number,
+ QuicPacketLength bytes_acked,
+ QuicTime receive_timestamp)
+ : packet_number(packet_number),
+ bytes_acked(bytes_acked),
+ receive_timestamp(receive_timestamp) {}
+
+ friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const AckedPacket& acked_packet);
+
+ QuicPacketNumber packet_number;
+ // Number of bytes sent in the packet that was acknowledged.
+ QuicPacketLength bytes_acked;
+ // The time |packet_number| was received by the peer, according to the
+ // optional timestamp the peer included in the ACK frame which acknowledged
+ // |packet_number|. Zero if no timestamp was available for this packet.
+ QuicTime receive_timestamp;
+};
+
+// A vector of acked packets.
+typedef std::vector<AckedPacket> AckedPacketVector;
+
+// Information about a newly lost packet.
+struct LostPacket {
+ LostPacket(QuicPacketNumber packet_number, QuicPacketLength bytes_lost)
+ : packet_number(packet_number), bytes_lost(bytes_lost) {}
+
+ QuicPacketNumber packet_number;
+ // Number of bytes sent in the packet that was lost.
+ QuicPacketLength bytes_lost;
+};
+
+// A vector of lost packets.
+typedef std::vector<LostPacket> LostPacketVector;
+
+enum QuicIetfTransportErrorCodes : uint16_t {
+ NO_IETF_QUIC_ERROR = 0x0,
+ INTERNAL_ERROR = 0x1,
+ SERVER_BUSY_ERROR = 0x2,
+ FLOW_CONTROL_ERROR = 0x3,
+ STREAM_LIMIT_ERROR = 0x4,
+ STREAM_STATE_ERROR = 0x5,
+ FINAL_SIZE_ERROR = 0x6,
+ FRAME_ENCODING_ERROR = 0x7,
+ TRANSPORT_PARAMETER_ERROR = 0x8,
+ VERSION_NEGOTIATION_ERROR = 0x9,
+ PROTOCOL_VIOLATION = 0xA,
+ INVALID_MIGRATION = 0xC,
+};
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& os,
+ const QuicIetfTransportErrorCodes& c);
+
+// Please note, this value cannot used directly for packet serialization.
+enum QuicLongHeaderType : uint8_t {
+ VERSION_NEGOTIATION,
+ INITIAL,
+ ZERO_RTT_PROTECTED,
+ HANDSHAKE,
+ RETRY,
+
+ INVALID_PACKET_TYPE,
+};
+
+enum QuicPacketHeaderTypeFlags : uint8_t {
+ // Bit 2: Reserved for experimentation for short header.
+ FLAGS_EXPERIMENTATION_BIT = 1 << 2,
+ // Bit 3: Google QUIC Demultiplexing bit, the short header always sets this
+ // bit to 0, allowing to distinguish Google QUIC packets from short header
+ // packets.
+ FLAGS_DEMULTIPLEXING_BIT = 1 << 3,
+ // Bits 4 and 5: Reserved bits for short header.
+ FLAGS_SHORT_HEADER_RESERVED_1 = 1 << 4,
+ FLAGS_SHORT_HEADER_RESERVED_2 = 1 << 5,
+ // Bit 6: the 'QUIC' bit.
+ FLAGS_FIXED_BIT = 1 << 6,
+ // Bit 7: Indicates the header is long or short header.
+ FLAGS_LONG_HEADER = 1 << 7,
+};
+
+enum MessageStatus {
+ MESSAGE_STATUS_SUCCESS,
+ MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED, // Failed to send message because
+ // encryption is not established
+ // yet.
+ MESSAGE_STATUS_UNSUPPORTED, // Failed to send message because MESSAGE frame
+ // is not supported by the connection.
+ MESSAGE_STATUS_BLOCKED, // Failed to send message because connection is
+ // congestion control blocked or underlying socket is
+ // write blocked.
+ MESSAGE_STATUS_TOO_LARGE, // Failed to send message because the message is
+ // too large to fit into a single packet.
+ MESSAGE_STATUS_INTERNAL_ERROR, // Failed to send message because connection
+ // reaches an invalid state.
+};
+
+// Used to return the result of SendMessage calls
+struct QUIC_EXPORT_PRIVATE MessageResult {
+ MessageResult(MessageStatus status, QuicMessageId message_id);
+
+ bool operator==(const MessageResult& other) const {
+ return status == other.status && message_id == other.message_id;
+ }
+
+ MessageStatus status;
+ // Only valid when status is MESSAGE_STATUS_SUCCESS.
+ QuicMessageId message_id;
+};
+
+enum WriteStreamDataResult {
+ WRITE_SUCCESS,
+ STREAM_MISSING, // Trying to write data of a nonexistent stream (e.g.
+ // closed).
+ WRITE_FAILED, // Trying to write nonexistent data of a stream
+};
+
+enum StreamType {
+ // Bidirectional streams allow for data to be sent in both directions.
+ BIDIRECTIONAL,
+
+ // Unidirectional streams carry data in one direction only.
+ WRITE_UNIDIRECTIONAL,
+ READ_UNIDIRECTIONAL,
+};
+
+// A packet number space is the context in which a packet can be processed and
+// acknowledged.
+enum PacketNumberSpace : uint8_t {
+ INITIAL_DATA = 0, // Only used in IETF QUIC.
+ HANDSHAKE_DATA = 1,
+ APPLICATION_DATA = 2,
+
+ NUM_PACKET_NUMBER_SPACES,
+};
+
+enum AckMode { TCP_ACKING, ACK_DECIMATION, ACK_DECIMATION_WITH_REORDERING };
+
+// Used to return the result of processing a received ACK frame.
+enum AckResult {
+ PACKETS_NEWLY_ACKED,
+ NO_PACKETS_NEWLY_ACKED,
+ UNSENT_PACKETS_ACKED, // Peer acks unsent packets.
+ UNACKABLE_PACKETS_ACKED, // Peer acks packets that are not expected to be
+ // acked. For example, encryption is reestablished,
+ // and all sent encrypted packets cannot be
+ // decrypted by the peer. Version gets negotiated,
+ // and all sent packets in the different version
+ // cannot be processed by the peer.
+ PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE,
+};
+
+} // 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
new file mode 100644
index 00000000000..1a0b1af24c4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.cc
@@ -0,0 +1,568 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+
+#include <limits>
+#include <type_traits>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+
+namespace quic {
+
+namespace {
+bool WillStreamFrameLengthSumWrapAround(QuicPacketLength lhs,
+ QuicPacketLength rhs) {
+ static_assert(
+ std::is_unsigned<QuicPacketLength>::value,
+ "This function assumes QuicPacketLength is an unsigned integer type.");
+ return std::numeric_limits<QuicPacketLength>::max() - lhs < rhs;
+}
+} // namespace
+
+QuicUnackedPacketMap::QuicUnackedPacketMap(Perspective perspective)
+ : perspective_(perspective),
+ least_unacked_(FirstSendingPacketNumber()),
+ bytes_in_flight_(0),
+ pending_crypto_packet_count_(0),
+ last_crypto_packet_sent_time_(QuicTime::Zero()),
+ session_notifier_(nullptr),
+ session_decides_what_to_write_(false),
+ use_uber_loss_algorithm_(
+ GetQuicReloadableFlag(quic_use_uber_loss_algorithm)),
+ supports_multiple_packet_number_spaces_(false) {
+ if (use_uber_loss_algorithm_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_use_uber_loss_algorithm);
+ }
+}
+
+QuicUnackedPacketMap::~QuicUnackedPacketMap() {
+ for (QuicTransmissionInfo& transmission_info : unacked_packets_) {
+ DeleteFrames(&(transmission_info.retransmittable_frames));
+ }
+}
+
+void QuicUnackedPacketMap::AddSentPacket(SerializedPacket* packet,
+ QuicPacketNumber old_packet_number,
+ TransmissionType transmission_type,
+ QuicTime sent_time,
+ bool set_in_flight) {
+ QuicPacketNumber packet_number = packet->packet_number;
+ QuicPacketLength bytes_sent = packet->encrypted_length;
+ QUIC_BUG_IF(largest_sent_packet_.IsInitialized() &&
+ largest_sent_packet_ >= packet_number)
+ << "largest_sent_packet_: " << largest_sent_packet_
+ << ", packet_number: " << packet_number;
+ DCHECK_GE(packet_number, least_unacked_ + unacked_packets_.size());
+ while (least_unacked_ + unacked_packets_.size() < packet_number) {
+ unacked_packets_.push_back(QuicTransmissionInfo());
+ unacked_packets_.back().state = NEVER_SENT;
+ }
+
+ const bool has_crypto_handshake =
+ packet->has_crypto_handshake == IS_HANDSHAKE;
+ QuicTransmissionInfo info(
+ packet->encryption_level, packet->packet_number_length, transmission_type,
+ sent_time, bytes_sent, has_crypto_handshake, packet->num_padding_bytes);
+ info.largest_acked = packet->largest_acked;
+ largest_sent_largest_acked_.UpdateMax(packet->largest_acked);
+ if (old_packet_number.IsInitialized()) {
+ TransferRetransmissionInfo(old_packet_number, packet_number,
+ transmission_type, &info);
+ }
+
+ largest_sent_packet_ = packet_number;
+ if (supports_multiple_packet_number_spaces_) {
+ largest_sent_packets_[GetPacketNumberSpace(packet->encryption_level)] =
+ packet_number;
+ }
+ if (set_in_flight) {
+ bytes_in_flight_ += bytes_sent;
+ info.in_flight = true;
+ if (use_uber_loss_algorithm_) {
+ largest_sent_retransmittable_packets_[GetPacketNumberSpace(
+ info.encryption_level)] = packet_number;
+ } else {
+ largest_sent_retransmittable_packet_ = packet_number;
+ }
+ }
+ unacked_packets_.push_back(info);
+ // Swap the retransmittable frames to avoid allocations.
+ // TODO(ianswett): Could use emplace_back when Chromium can.
+ if (!old_packet_number.IsInitialized()) {
+ if (has_crypto_handshake) {
+ ++pending_crypto_packet_count_;
+ last_crypto_packet_sent_time_ = sent_time;
+ }
+
+ packet->retransmittable_frames.swap(
+ unacked_packets_.back().retransmittable_frames);
+ }
+}
+
+void QuicUnackedPacketMap::RemoveObsoletePackets() {
+ while (!unacked_packets_.empty()) {
+ if (!IsPacketUseless(least_unacked_, unacked_packets_.front())) {
+ break;
+ }
+ if (session_decides_what_to_write_) {
+ DeleteFrames(&unacked_packets_.front().retransmittable_frames);
+ }
+ unacked_packets_.pop_front();
+ ++least_unacked_;
+ }
+}
+
+void QuicUnackedPacketMap::TransferRetransmissionInfo(
+ QuicPacketNumber old_packet_number,
+ QuicPacketNumber new_packet_number,
+ TransmissionType transmission_type,
+ QuicTransmissionInfo* info) {
+ if (old_packet_number < least_unacked_) {
+ // This can happen when a retransmission packet is queued because of write
+ // blocked socket, and the original packet gets acked before the
+ // retransmission gets sent.
+ return;
+ }
+ if (old_packet_number > largest_sent_packet_) {
+ QUIC_BUG << "Old QuicTransmissionInfo never existed for :"
+ << old_packet_number << " largest_sent:" << largest_sent_packet_;
+ return;
+ }
+ DCHECK_GE(new_packet_number, least_unacked_ + unacked_packets_.size());
+ DCHECK_NE(NOT_RETRANSMISSION, transmission_type);
+
+ QuicTransmissionInfo* transmission_info =
+ &unacked_packets_.at(old_packet_number - least_unacked_);
+ QuicFrames* frames = &transmission_info->retransmittable_frames;
+ if (session_notifier_ != nullptr) {
+ for (const QuicFrame& frame : *frames) {
+ if (frame.type == STREAM_FRAME) {
+ session_notifier_->OnStreamFrameRetransmitted(frame.stream_frame);
+ }
+ }
+ }
+
+ // Swap the frames and preserve num_padding_bytes and has_crypto_handshake.
+ frames->swap(info->retransmittable_frames);
+ info->has_crypto_handshake = transmission_info->has_crypto_handshake;
+ transmission_info->has_crypto_handshake = false;
+ info->num_padding_bytes = transmission_info->num_padding_bytes;
+
+ // Don't link old transmissions to new ones when version or
+ // encryption changes.
+ if (transmission_type == ALL_INITIAL_RETRANSMISSION ||
+ transmission_type == ALL_UNACKED_RETRANSMISSION) {
+ transmission_info->state = UNACKABLE;
+ } else {
+ transmission_info->retransmission = new_packet_number;
+ }
+ // Proactively remove obsolete packets so the least unacked can be raised.
+ RemoveObsoletePackets();
+}
+
+bool QuicUnackedPacketMap::HasRetransmittableFrames(
+ QuicPacketNumber packet_number) const {
+ DCHECK_GE(packet_number, least_unacked_);
+ DCHECK_LT(packet_number, least_unacked_ + unacked_packets_.size());
+ return HasRetransmittableFrames(
+ unacked_packets_[packet_number - least_unacked_]);
+}
+
+bool QuicUnackedPacketMap::HasRetransmittableFrames(
+ const QuicTransmissionInfo& info) const {
+ if (!session_decides_what_to_write_) {
+ return !info.retransmittable_frames.empty();
+ }
+
+ if (!QuicUtils::IsAckable(info.state)) {
+ return false;
+ }
+
+ for (const auto& frame : info.retransmittable_frames) {
+ if (session_notifier_->IsFrameOutstanding(frame)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void QuicUnackedPacketMap::RemoveRetransmittability(
+ QuicTransmissionInfo* info) {
+ if (session_decides_what_to_write_) {
+ DeleteFrames(&info->retransmittable_frames);
+ info->retransmission.Clear();
+ return;
+ }
+ while (info->retransmission.IsInitialized()) {
+ const QuicPacketNumber retransmission = info->retransmission;
+ info->retransmission.Clear();
+ info = &unacked_packets_[retransmission - least_unacked_];
+ }
+
+ if (info->has_crypto_handshake) {
+ DCHECK(HasRetransmittableFrames(*info));
+ DCHECK_LT(0u, pending_crypto_packet_count_);
+ --pending_crypto_packet_count_;
+ info->has_crypto_handshake = false;
+ }
+ DeleteFrames(&info->retransmittable_frames);
+}
+
+void QuicUnackedPacketMap::RemoveRetransmittability(
+ QuicPacketNumber packet_number) {
+ DCHECK_GE(packet_number, least_unacked_);
+ DCHECK_LT(packet_number, least_unacked_ + unacked_packets_.size());
+ QuicTransmissionInfo* info =
+ &unacked_packets_[packet_number - least_unacked_];
+ RemoveRetransmittability(info);
+}
+
+void QuicUnackedPacketMap::IncreaseLargestAcked(
+ QuicPacketNumber largest_acked) {
+ DCHECK(!largest_acked_.IsInitialized() || largest_acked_ <= largest_acked);
+ largest_acked_ = largest_acked;
+}
+
+void QuicUnackedPacketMap::MaybeUpdateLargestAckedOfPacketNumberSpace(
+ PacketNumberSpace packet_number_space,
+ QuicPacketNumber packet_number) {
+ DCHECK(use_uber_loss_algorithm_);
+ largest_acked_packets_[packet_number_space].UpdateMax(packet_number);
+}
+
+bool QuicUnackedPacketMap::IsPacketUsefulForMeasuringRtt(
+ QuicPacketNumber packet_number,
+ const QuicTransmissionInfo& info) const {
+ // Packet can be used for RTT measurement if it may yet be acked as the
+ // largest observed packet by the receiver.
+ return QuicUtils::IsAckable(info.state) &&
+ (!largest_acked_.IsInitialized() || packet_number > largest_acked_);
+}
+
+bool QuicUnackedPacketMap::IsPacketUsefulForCongestionControl(
+ const QuicTransmissionInfo& info) const {
+ // Packet contributes to congestion control if it is considered inflight.
+ return info.in_flight;
+}
+
+bool QuicUnackedPacketMap::IsPacketUsefulForRetransmittableData(
+ const QuicTransmissionInfo& info) const {
+ if (!session_decides_what_to_write_) {
+ // Packet may have retransmittable frames, or the data may have been
+ // retransmitted with a new packet number.
+ // Allow for an extra 1 RTT before stopping to track old packets.
+ return (info.retransmission.IsInitialized() &&
+ (!largest_acked_.IsInitialized() ||
+ info.retransmission > largest_acked_)) ||
+ HasRetransmittableFrames(info);
+ }
+
+ // Wait for 1 RTT before giving up on the lost packet.
+ return info.retransmission.IsInitialized() &&
+ (!largest_acked_.IsInitialized() ||
+ info.retransmission > largest_acked_);
+}
+
+bool QuicUnackedPacketMap::IsPacketUseless(
+ QuicPacketNumber packet_number,
+ const QuicTransmissionInfo& info) const {
+ return !IsPacketUsefulForMeasuringRtt(packet_number, info) &&
+ !IsPacketUsefulForCongestionControl(info) &&
+ !IsPacketUsefulForRetransmittableData(info);
+}
+
+bool QuicUnackedPacketMap::IsUnacked(QuicPacketNumber packet_number) const {
+ if (packet_number < least_unacked_ ||
+ packet_number >= least_unacked_ + unacked_packets_.size()) {
+ return false;
+ }
+ return !IsPacketUseless(packet_number,
+ unacked_packets_[packet_number - least_unacked_]);
+}
+
+void QuicUnackedPacketMap::RemoveFromInFlight(QuicTransmissionInfo* info) {
+ if (info->in_flight) {
+ QUIC_BUG_IF(bytes_in_flight_ < info->bytes_sent);
+ bytes_in_flight_ -= info->bytes_sent;
+ info->in_flight = false;
+ }
+}
+
+void QuicUnackedPacketMap::RemoveFromInFlight(QuicPacketNumber packet_number) {
+ DCHECK_GE(packet_number, least_unacked_);
+ DCHECK_LT(packet_number, least_unacked_ + unacked_packets_.size());
+ QuicTransmissionInfo* info =
+ &unacked_packets_[packet_number - least_unacked_];
+ RemoveFromInFlight(info);
+}
+
+void QuicUnackedPacketMap::CancelRetransmissionsForStream(
+ QuicStreamId stream_id) {
+ DCHECK(!session_decides_what_to_write_);
+ QuicPacketNumber packet_number = least_unacked_;
+ for (auto it = unacked_packets_.begin(); it != unacked_packets_.end();
+ ++it, ++packet_number) {
+ QuicFrames* frames = &it->retransmittable_frames;
+ if (frames->empty()) {
+ continue;
+ }
+ RemoveFramesForStream(frames, stream_id);
+ if (frames->empty()) {
+ RemoveRetransmittability(packet_number);
+ }
+ }
+}
+
+bool QuicUnackedPacketMap::HasInFlightPackets() const {
+ return bytes_in_flight_ > 0;
+}
+
+const QuicTransmissionInfo& QuicUnackedPacketMap::GetTransmissionInfo(
+ QuicPacketNumber packet_number) const {
+ return unacked_packets_[packet_number - least_unacked_];
+}
+
+QuicTransmissionInfo* QuicUnackedPacketMap::GetMutableTransmissionInfo(
+ QuicPacketNumber packet_number) {
+ return &unacked_packets_[packet_number - least_unacked_];
+}
+
+QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const {
+ auto it = unacked_packets_.rbegin();
+ while (it != unacked_packets_.rend()) {
+ if (it->in_flight) {
+ QUIC_BUG_IF(it->sent_time == QuicTime::Zero())
+ << "Sent time can never be zero for a packet in flight.";
+ return it->sent_time;
+ }
+ ++it;
+ }
+ QUIC_BUG << "GetLastPacketSentTime requires in flight packets.";
+ return QuicTime::Zero();
+}
+
+QuicTime QuicUnackedPacketMap::GetLastCryptoPacketSentTime() const {
+ return last_crypto_packet_sent_time_;
+}
+
+size_t QuicUnackedPacketMap::GetNumUnackedPacketsDebugOnly() const {
+ size_t unacked_packet_count = 0;
+ QuicPacketNumber packet_number = least_unacked_;
+ for (auto it = unacked_packets_.begin(); it != unacked_packets_.end();
+ ++it, ++packet_number) {
+ if (!IsPacketUseless(packet_number, *it)) {
+ ++unacked_packet_count;
+ }
+ }
+ return unacked_packet_count;
+}
+
+bool QuicUnackedPacketMap::HasMultipleInFlightPackets() const {
+ if (bytes_in_flight_ > kDefaultTCPMSS) {
+ return true;
+ }
+ size_t num_in_flight = 0;
+ for (auto it = unacked_packets_.rbegin(); it != unacked_packets_.rend();
+ ++it) {
+ if (it->in_flight) {
+ ++num_in_flight;
+ }
+ if (num_in_flight > 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuicUnackedPacketMap::HasPendingCryptoPackets() const {
+ if (!session_decides_what_to_write_) {
+ return pending_crypto_packet_count_ > 0;
+ }
+ return session_notifier_->HasUnackedCryptoData();
+}
+
+bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const {
+ DCHECK(!GetQuicReloadableFlag(quic_optimize_inflight_check));
+ for (auto it = unacked_packets_.rbegin(); it != unacked_packets_.rend();
+ ++it) {
+ if (it->in_flight && HasRetransmittableFrames(*it)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicPacketNumber QuicUnackedPacketMap::GetLeastUnacked() const {
+ return least_unacked_;
+}
+
+void QuicUnackedPacketMap::SetSessionNotifier(
+ SessionNotifierInterface* session_notifier) {
+ session_notifier_ = session_notifier;
+}
+
+bool QuicUnackedPacketMap::NotifyFramesAcked(const QuicTransmissionInfo& info,
+ QuicTime::Delta ack_delay) {
+ if (session_notifier_ == nullptr) {
+ return false;
+ }
+ bool new_data_acked = false;
+ for (const QuicFrame& frame : info.retransmittable_frames) {
+ if (session_notifier_->OnFrameAcked(frame, ack_delay)) {
+ new_data_acked = true;
+ }
+ }
+ return new_data_acked;
+}
+
+void QuicUnackedPacketMap::NotifyFramesLost(const QuicTransmissionInfo& info,
+ TransmissionType type) {
+ DCHECK(session_decides_what_to_write_);
+ for (const QuicFrame& frame : info.retransmittable_frames) {
+ session_notifier_->OnFrameLost(frame);
+ }
+}
+
+void QuicUnackedPacketMap::RetransmitFrames(const QuicTransmissionInfo& info,
+ TransmissionType type) {
+ DCHECK(session_decides_what_to_write_);
+ session_notifier_->RetransmitFrames(info.retransmittable_frames, type);
+}
+
+void QuicUnackedPacketMap::MaybeAggregateAckedStreamFrame(
+ const QuicTransmissionInfo& info,
+ QuicTime::Delta ack_delay) {
+ if (session_notifier_ == nullptr) {
+ return;
+ }
+ for (const auto& frame : info.retransmittable_frames) {
+ // Determine whether acked stream frame can be aggregated.
+ const bool can_aggregate =
+ frame.type == STREAM_FRAME &&
+ frame.stream_frame.stream_id == aggregated_stream_frame_.stream_id &&
+ frame.stream_frame.offset == aggregated_stream_frame_.offset +
+ aggregated_stream_frame_.data_length &&
+ // We would like to increment aggregated_stream_frame_.data_length by
+ // frame.stream_frame.data_length, so we need to make sure their sum is
+ // representable by QuicPacketLength, which is the type of the former.
+ !WillStreamFrameLengthSumWrapAround(
+ aggregated_stream_frame_.data_length,
+ frame.stream_frame.data_length);
+
+ if (can_aggregate) {
+ // Aggregate stream frame.
+ aggregated_stream_frame_.data_length += frame.stream_frame.data_length;
+ aggregated_stream_frame_.fin = frame.stream_frame.fin;
+ if (aggregated_stream_frame_.fin) {
+ // Notify session notifier aggregated stream frame gets acked if fin is
+ // acked.
+ NotifyAggregatedStreamFrameAcked(ack_delay);
+ }
+ continue;
+ }
+
+ NotifyAggregatedStreamFrameAcked(ack_delay);
+ if (frame.type != STREAM_FRAME || frame.stream_frame.fin) {
+ session_notifier_->OnFrameAcked(frame, ack_delay);
+ continue;
+ }
+
+ // Delay notifying session notifier stream frame gets acked in case it can
+ // be aggregated with following acked ones.
+ aggregated_stream_frame_.stream_id = frame.stream_frame.stream_id;
+ aggregated_stream_frame_.offset = frame.stream_frame.offset;
+ aggregated_stream_frame_.data_length = frame.stream_frame.data_length;
+ aggregated_stream_frame_.fin = frame.stream_frame.fin;
+ }
+}
+
+void QuicUnackedPacketMap::NotifyAggregatedStreamFrameAcked(
+ QuicTime::Delta ack_delay) {
+ if (aggregated_stream_frame_.stream_id == static_cast<QuicStreamId>(-1) ||
+ session_notifier_ == nullptr) {
+ // Aggregated stream frame is empty.
+ return;
+ }
+ session_notifier_->OnFrameAcked(QuicFrame(aggregated_stream_frame_),
+ ack_delay);
+ // Clear aggregated stream frame.
+ aggregated_stream_frame_.stream_id = -1;
+}
+
+PacketNumberSpace QuicUnackedPacketMap::GetPacketNumberSpace(
+ QuicPacketNumber packet_number) const {
+ DCHECK(use_uber_loss_algorithm_);
+ return GetPacketNumberSpace(
+ GetTransmissionInfo(packet_number).encryption_level);
+}
+
+PacketNumberSpace QuicUnackedPacketMap::GetPacketNumberSpace(
+ EncryptionLevel encryption_level) const {
+ DCHECK(use_uber_loss_algorithm_);
+ if (supports_multiple_packet_number_spaces_) {
+ return QuicUtils::GetPacketNumberSpace(encryption_level);
+ }
+ if (perspective_ == Perspective::IS_CLIENT) {
+ return encryption_level == ENCRYPTION_INITIAL ? HANDSHAKE_DATA
+ : APPLICATION_DATA;
+ }
+ return encryption_level == ENCRYPTION_FORWARD_SECURE ? APPLICATION_DATA
+ : HANDSHAKE_DATA;
+}
+
+QuicPacketNumber QuicUnackedPacketMap::GetLargestAckedOfPacketNumberSpace(
+ PacketNumberSpace packet_number_space) const {
+ DCHECK(use_uber_loss_algorithm_);
+ if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) {
+ QUIC_BUG << "Invalid packet number space: " << packet_number_space;
+ return QuicPacketNumber();
+ }
+ return largest_acked_packets_[packet_number_space];
+}
+
+QuicPacketNumber
+QuicUnackedPacketMap::GetLargestSentRetransmittableOfPacketNumberSpace(
+ PacketNumberSpace packet_number_space) const {
+ DCHECK(use_uber_loss_algorithm_);
+ if (packet_number_space >= NUM_PACKET_NUMBER_SPACES) {
+ QUIC_BUG << "Invalid packet number space: " << packet_number_space;
+ return QuicPacketNumber();
+ }
+ return largest_sent_retransmittable_packets_[packet_number_space];
+}
+
+void QuicUnackedPacketMap::SetSessionDecideWhatToWrite(
+ bool session_decides_what_to_write) {
+ if (largest_sent_packet_.IsInitialized()) {
+ QUIC_BUG << "Cannot change session_decide_what_to_write with packets sent.";
+ return;
+ }
+ session_decides_what_to_write_ = session_decides_what_to_write;
+}
+
+void QuicUnackedPacketMap::EnableMultiplePacketNumberSpacesSupport() {
+ if (supports_multiple_packet_number_spaces_) {
+ QUIC_BUG << "Multiple packet number spaces has already been enabled";
+ return;
+ }
+ if (largest_sent_packet_.IsInitialized()) {
+ QUIC_BUG << "Try to enable multiple packet number spaces support after any "
+ "packet has been sent.";
+ return;
+ }
+
+ supports_multiple_packet_number_spaces_ = true;
+}
+
+QuicPacketNumber QuicUnackedPacketMap::GetLargestSentPacketOfPacketNumberSpace(
+ EncryptionLevel encryption_level) const {
+ DCHECK(supports_multiple_packet_number_spaces_);
+ return largest_sent_packets_[GetPacketNumberSpace(encryption_level)];
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..1a37a1fb5d9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h
@@ -0,0 +1,318 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_UNACKED_PACKET_MAP_H_
+#define QUICHE_QUIC_CORE_QUIC_UNACKED_PACKET_MAP_H_
+
+#include <cstddef>
+#include <deque>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h"
+#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+namespace test {
+class QuicUnackedPacketMapPeer;
+} // namespace test
+
+// Class which tracks unacked packets for three purposes:
+// 1) Track retransmittable data, including multiple transmissions of frames.
+// 2) Track packets and bytes in flight for congestion control.
+// 3) Track sent time of packets to provide RTT measurements from acks.
+class QUIC_EXPORT_PRIVATE QuicUnackedPacketMap {
+ public:
+ QuicUnackedPacketMap(Perspective perspective);
+ QuicUnackedPacketMap(const QuicUnackedPacketMap&) = delete;
+ QuicUnackedPacketMap& operator=(const QuicUnackedPacketMap&) = delete;
+ ~QuicUnackedPacketMap();
+
+ // Adds |serialized_packet| to the map and marks it as sent at |sent_time|.
+ // Marks the packet as in flight if |set_in_flight| is true.
+ // Packets marked as in flight are expected to be marked as missing when they
+ // don't arrive, indicating the need for retransmission.
+ // |old_packet_number| is the packet number of the previous transmission,
+ // or 0 if there was none.
+ // Any AckNotifierWrappers in |serialized_packet| are swapped from the
+ // serialized packet into the QuicTransmissionInfo.
+ void AddSentPacket(SerializedPacket* serialized_packet,
+ QuicPacketNumber old_packet_number,
+ TransmissionType transmission_type,
+ QuicTime sent_time,
+ bool set_in_flight);
+
+ // Returns true if the packet |packet_number| is unacked.
+ bool IsUnacked(QuicPacketNumber packet_number) const;
+
+ // Notifies session_notifier that frames have been acked. Returns true if any
+ // new data gets acked, returns false otherwise.
+ bool NotifyFramesAcked(const QuicTransmissionInfo& info,
+ QuicTime::Delta ack_delay);
+
+ // Notifies session_notifier that frames in |info| are considered as lost.
+ void NotifyFramesLost(const QuicTransmissionInfo& info,
+ TransmissionType type);
+
+ // Notifies session_notifier to retransmit frames in |info| with
+ // |transmission_type|.
+ void RetransmitFrames(const QuicTransmissionInfo& info,
+ TransmissionType type);
+
+ // Marks |info| as no longer in flight.
+ void RemoveFromInFlight(QuicTransmissionInfo* info);
+
+ // Marks |packet_number| as no longer in flight.
+ void RemoveFromInFlight(QuicPacketNumber packet_number);
+
+ // No longer retransmit data for |stream_id|.
+ void CancelRetransmissionsForStream(QuicStreamId stream_id);
+
+ // Returns true if |packet_number| has retransmittable frames. This will
+ // return false if all frames of this packet are either non-retransmittable or
+ // have been acked.
+ bool HasRetransmittableFrames(QuicPacketNumber packet_number) const;
+
+ // Returns true if |info| has retransmittable frames. This will return false
+ // if all frames of this packet are either non-retransmittable or have been
+ // acked.
+ bool HasRetransmittableFrames(const QuicTransmissionInfo& info) const;
+
+ // Returns true if there are any unacked packets which have retransmittable
+ // frames.
+ bool HasUnackedRetransmittableFrames() const;
+
+ // Returns true if there are no packets present in the unacked packet map.
+ bool empty() const { return unacked_packets_.empty(); }
+
+ // Returns the largest packet number that has been sent.
+ QuicPacketNumber largest_sent_packet() const { return largest_sent_packet_; }
+
+ // Returns the largest retransmittable packet number that has been sent.
+ QuicPacketNumber largest_sent_retransmittable_packet() const {
+ DCHECK(!use_uber_loss_algorithm_);
+ return largest_sent_retransmittable_packet_;
+ }
+
+ QuicPacketNumber largest_sent_largest_acked() const {
+ return largest_sent_largest_acked_;
+ }
+
+ // Returns the largest packet number that has been acked.
+ QuicPacketNumber largest_acked() const { return largest_acked_; }
+
+ // Returns the sum of bytes from all packets in flight.
+ QuicByteCount bytes_in_flight() const { return bytes_in_flight_; }
+
+ // Returns the smallest packet number of a serialized packet which has not
+ // been acked by the peer. If there are no unacked packets, returns 0.
+ QuicPacketNumber GetLeastUnacked() const;
+
+ // This can not be a QuicDeque since pointers into this are
+ // assumed to be stable.
+ typedef std::deque<QuicTransmissionInfo> UnackedPacketMap;
+
+ typedef UnackedPacketMap::const_iterator const_iterator;
+ typedef UnackedPacketMap::iterator iterator;
+
+ const_iterator begin() const { return unacked_packets_.begin(); }
+ const_iterator end() const { return unacked_packets_.end(); }
+ iterator begin() { return unacked_packets_.begin(); }
+ iterator end() { return unacked_packets_.end(); }
+
+ // Returns true if there are unacked packets that are in flight.
+ bool HasInFlightPackets() const;
+
+ // Returns the QuicTransmissionInfo associated with |packet_number|, which
+ // must be unacked.
+ const QuicTransmissionInfo& GetTransmissionInfo(
+ QuicPacketNumber packet_number) const;
+
+ // Returns mutable QuicTransmissionInfo associated with |packet_number|, which
+ // must be unacked.
+ QuicTransmissionInfo* GetMutableTransmissionInfo(
+ QuicPacketNumber packet_number);
+
+ // Returns the time that the last unacked packet was sent.
+ QuicTime GetLastPacketSentTime() const;
+
+ // Returns the time that the last unacked crypto packet was sent.
+ QuicTime GetLastCryptoPacketSentTime() const;
+
+ // Returns the number of unacked packets.
+ size_t GetNumUnackedPacketsDebugOnly() const;
+
+ // Returns true if there are multiple packets in flight.
+ bool HasMultipleInFlightPackets() const;
+
+ // Returns true if there are any pending crypto packets.
+ // TODO(fayang): Remove this method and call session_notifier_'s
+ // HasUnackedCryptoData() when session_decides_what_to_write_ is default true.
+ bool HasPendingCryptoPackets() const;
+
+ // Removes any retransmittable frames from this transmission or an associated
+ // transmission. It removes now useless transmissions, and disconnects any
+ // other packets from other transmissions.
+ void RemoveRetransmittability(QuicTransmissionInfo* info);
+
+ // Looks up the QuicTransmissionInfo by |packet_number| and calls
+ // RemoveRetransmittability.
+ void RemoveRetransmittability(QuicPacketNumber packet_number);
+
+ // Increases the largest acked. Any packets less or equal to
+ // |largest_acked| are discarded if they are only for the RTT purposes.
+ void IncreaseLargestAcked(QuicPacketNumber largest_acked);
+
+ // Called when |packet_number| gets acked. Maybe increase the largest acked of
+ // |packet_number_space|.
+ void MaybeUpdateLargestAckedOfPacketNumberSpace(
+ PacketNumberSpace packet_number_space,
+ QuicPacketNumber packet_number);
+
+ // Remove any packets no longer needed for retransmission, congestion, or
+ // RTT measurement purposes.
+ void RemoveObsoletePackets();
+
+ // Try to aggregate acked contiguous stream frames. For noncontiguous stream
+ // frames or control frames, notify the session notifier they get acked
+ // immediately.
+ void MaybeAggregateAckedStreamFrame(const QuicTransmissionInfo& info,
+ QuicTime::Delta ack_delay);
+
+ // Notify the session notifier of any stream data aggregated in
+ // aggregated_stream_frame_. No effect if the stream frame has an invalid
+ // stream id.
+ void NotifyAggregatedStreamFrameAcked(QuicTime::Delta ack_delay);
+
+ // Returns packet number space that |packet_number| belongs to. Please use
+ // GetPacketNumberSpace(EncryptionLevel) whenever encryption level is
+ // available.
+ PacketNumberSpace GetPacketNumberSpace(QuicPacketNumber packet_number) const;
+
+ // Returns packet number space of |encryption_level|.
+ PacketNumberSpace GetPacketNumberSpace(
+ EncryptionLevel encryption_level) const;
+
+ // Returns largest acked packet number of |packet_number_space|.
+ QuicPacketNumber GetLargestAckedOfPacketNumberSpace(
+ PacketNumberSpace packet_number_space) const;
+
+ // Returns largest sent retransmittable packet number of
+ // |packet_number_space|.
+ QuicPacketNumber GetLargestSentRetransmittableOfPacketNumberSpace(
+ PacketNumberSpace packet_number_space) const;
+
+ // Returns largest sent packet number of |encryption_level|.
+ QuicPacketNumber GetLargestSentPacketOfPacketNumberSpace(
+ EncryptionLevel encryption_level) const;
+
+ // Called to start/stop letting session decide what to write.
+ void SetSessionDecideWhatToWrite(bool session_decides_what_to_write);
+
+ void SetSessionNotifier(SessionNotifierInterface* session_notifier);
+
+ void EnableMultiplePacketNumberSpacesSupport();
+
+ bool session_decides_what_to_write() const {
+ return session_decides_what_to_write_;
+ }
+
+ bool use_uber_loss_algorithm() const { return use_uber_loss_algorithm_; }
+
+ Perspective perspective() const { return perspective_; }
+
+ bool supports_multiple_packet_number_spaces() const {
+ return supports_multiple_packet_number_spaces_;
+ }
+
+ private:
+ friend class test::QuicUnackedPacketMapPeer;
+
+ // Called when a packet is retransmitted with a new packet number.
+ // |old_packet_number| will remain unacked, but will have no
+ // retransmittable data associated with it. Retransmittable frames will be
+ // transferred to |info| and all_transmissions will be populated.
+ void TransferRetransmissionInfo(QuicPacketNumber old_packet_number,
+ QuicPacketNumber new_packet_number,
+ TransmissionType transmission_type,
+ QuicTransmissionInfo* info);
+
+ // Returns true if packet may be useful for an RTT measurement.
+ bool IsPacketUsefulForMeasuringRtt(QuicPacketNumber packet_number,
+ const QuicTransmissionInfo& info) const;
+
+ // Returns true if packet may be useful for congestion control purposes.
+ bool IsPacketUsefulForCongestionControl(
+ const QuicTransmissionInfo& info) const;
+
+ // Returns true if packet may be associated with retransmittable data
+ // directly or through retransmissions.
+ bool IsPacketUsefulForRetransmittableData(
+ const QuicTransmissionInfo& info) const;
+
+ // Returns true if the packet no longer has a purpose in the map.
+ bool IsPacketUseless(QuicPacketNumber packet_number,
+ const QuicTransmissionInfo& info) const;
+
+ const Perspective perspective_;
+
+ QuicPacketNumber largest_sent_packet_;
+ // Only used when supports_multiple_packet_number_spaces_ is true.
+ QuicPacketNumber largest_sent_packets_[NUM_PACKET_NUMBER_SPACES];
+ // The largest sent packet we expect to receive an ack for.
+ // TODO(fayang): Remove largest_sent_retransmittable_packet_ when deprecating
+ // quic_use_uber_loss_algorithm.
+ QuicPacketNumber largest_sent_retransmittable_packet_;
+ // The largest sent packet we expect to receive an ack for per packet number
+ // space. Only used if use_uber_loss_algorithm_ is true.
+ QuicPacketNumber
+ largest_sent_retransmittable_packets_[NUM_PACKET_NUMBER_SPACES];
+ // The largest sent largest_acked in an ACK frame.
+ QuicPacketNumber largest_sent_largest_acked_;
+ // The largest received largest_acked from an ACK frame.
+ QuicPacketNumber largest_acked_;
+ // The largest received largest_acked from ACK frame per packet number space.
+ // Only used if use_uber_loss_algorithm_ is true.
+ QuicPacketNumber largest_acked_packets_[NUM_PACKET_NUMBER_SPACES];
+
+ // Newly serialized retransmittable packets are added to this map, which
+ // contains owning pointers to any contained frames. If a packet is
+ // retransmitted, this map will contain entries for both the old and the new
+ // packet. The old packet's retransmittable frames entry will be nullptr,
+ // while the new packet's entry will contain the frames to retransmit.
+ // If the old packet is acked before the new packet, then the old entry will
+ // be removed from the map and the new entry's retransmittable frames will be
+ // set to nullptr.
+ UnackedPacketMap unacked_packets_;
+ // The packet at the 0th index of unacked_packets_.
+ QuicPacketNumber least_unacked_;
+
+ QuicByteCount bytes_in_flight_;
+ // Number of retransmittable crypto handshake packets.
+ size_t pending_crypto_packet_count_;
+
+ // Time that the last unacked crypto packet was sent.
+ QuicTime last_crypto_packet_sent_time_;
+
+ // Aggregates acked stream data across multiple acked sent packets to save CPU
+ // by reducing the number of calls to the session notifier.
+ QuicStreamFrame aggregated_stream_frame_;
+
+ // Receives notifications of frames being retransmitted or acknowledged.
+ SessionNotifierInterface* session_notifier_;
+
+ // If true, let session decides what to write.
+ bool session_decides_what_to_write_;
+
+ // Latched value of quic_use_uber_loss_algorithm.
+ const bool use_uber_loss_algorithm_;
+
+ // If true, supports multiple packet number spaces.
+ bool supports_multiple_packet_number_spaces_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_UNACKED_PACKET_MAP_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map_test.cc
new file mode 100644
index 00000000000..331a0e8842e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_unacked_packet_map_test.cc
@@ -0,0 +1,758 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_transmission_info.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+// Default packet length.
+const uint32_t kDefaultLength = 1000;
+
+struct TestParams {
+ TestParams(Perspective perspective, bool session_decides_what_to_write)
+ : perspective(perspective),
+ session_decides_what_to_write(session_decides_what_to_write) {}
+
+ friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
+ os << "{ Perspective: " << p.perspective
+ << " session_decides_what_to_write: " << p.session_decides_what_to_write
+ << " }";
+ return os;
+ }
+
+ Perspective perspective;
+ bool session_decides_what_to_write;
+};
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ for (Perspective perspective :
+ {Perspective::IS_CLIENT, Perspective::IS_SERVER}) {
+ for (bool session_decides_what_to_write : {true, false}) {
+ params.push_back(TestParams(perspective, session_decides_what_to_write));
+ }
+ }
+ return params;
+}
+
+class QuicUnackedPacketMapTest : public QuicTestWithParam<TestParams> {
+ protected:
+ QuicUnackedPacketMapTest()
+ : unacked_packets_(GetParam().perspective),
+ now_(QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1000)) {
+ unacked_packets_.SetSessionNotifier(&notifier_);
+ unacked_packets_.SetSessionDecideWhatToWrite(
+ GetParam().session_decides_what_to_write);
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(notifier_, OnStreamFrameRetransmitted(_))
+ .Times(testing::AnyNumber());
+ }
+
+ ~QuicUnackedPacketMapTest() override {}
+
+ SerializedPacket CreateRetransmittablePacket(uint64_t packet_number) {
+ return CreateRetransmittablePacketForStream(
+ packet_number, QuicUtils::GetHeadersStreamId(
+ CurrentSupportedVersions()[0].transport_version));
+ }
+
+ SerializedPacket CreateRetransmittablePacketForStream(
+ uint64_t packet_number,
+ QuicStreamId stream_id) {
+ SerializedPacket packet(QuicPacketNumber(packet_number),
+ PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+ false, false);
+ QuicStreamFrame frame;
+ frame.stream_id = stream_id;
+ packet.retransmittable_frames.push_back(QuicFrame(frame));
+ return packet;
+ }
+
+ SerializedPacket CreateNonRetransmittablePacket(uint64_t packet_number) {
+ return SerializedPacket(QuicPacketNumber(packet_number),
+ PACKET_1BYTE_PACKET_NUMBER, nullptr, kDefaultLength,
+ false, false);
+ }
+
+ void VerifyInFlightPackets(uint64_t* packets, size_t num_packets) {
+ unacked_packets_.RemoveObsoletePackets();
+ if (num_packets == 0) {
+ EXPECT_FALSE(unacked_packets_.HasInFlightPackets());
+ EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
+ return;
+ }
+ if (num_packets == 1) {
+ EXPECT_TRUE(unacked_packets_.HasInFlightPackets());
+ EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
+ ASSERT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[0])));
+ EXPECT_TRUE(
+ unacked_packets_.GetTransmissionInfo(QuicPacketNumber(packets[0]))
+ .in_flight);
+ }
+ for (size_t i = 0; i < num_packets; ++i) {
+ ASSERT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[i])));
+ EXPECT_TRUE(
+ unacked_packets_.GetTransmissionInfo(QuicPacketNumber(packets[i]))
+ .in_flight);
+ }
+ size_t in_flight_count = 0;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ if (it->in_flight) {
+ ++in_flight_count;
+ }
+ }
+ EXPECT_EQ(num_packets, in_flight_count);
+ }
+
+ void VerifyUnackedPackets(uint64_t* packets, size_t num_packets) {
+ unacked_packets_.RemoveObsoletePackets();
+ if (num_packets == 0) {
+ EXPECT_TRUE(unacked_packets_.empty());
+ if (!GetQuicReloadableFlag(quic_optimize_inflight_check)) {
+ EXPECT_FALSE(unacked_packets_.HasUnackedRetransmittableFrames());
+ }
+ return;
+ }
+ EXPECT_FALSE(unacked_packets_.empty());
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(packets[i])))
+ << packets[i];
+ }
+ EXPECT_EQ(num_packets, unacked_packets_.GetNumUnackedPacketsDebugOnly());
+ }
+
+ void VerifyRetransmittablePackets(uint64_t* packets, size_t num_packets) {
+ unacked_packets_.RemoveObsoletePackets();
+ size_t num_retransmittable_packets = 0;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ if (unacked_packets_.HasRetransmittableFrames(*it)) {
+ ++num_retransmittable_packets;
+ }
+ }
+ EXPECT_EQ(num_packets, num_retransmittable_packets);
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(unacked_packets_.HasRetransmittableFrames(
+ QuicPacketNumber(packets[i])))
+ << " packets[" << i << "]:" << packets[i];
+ }
+ }
+
+ void UpdatePacketState(uint64_t packet_number, SentPacketState state) {
+ unacked_packets_
+ .GetMutableTransmissionInfo(QuicPacketNumber(packet_number))
+ ->state = state;
+ }
+
+ void RetransmitAndSendPacket(uint64_t old_packet_number,
+ uint64_t new_packet_number,
+ TransmissionType transmission_type) {
+ DCHECK(unacked_packets_.HasRetransmittableFrames(
+ QuicPacketNumber(old_packet_number)));
+ if (!unacked_packets_.session_decides_what_to_write()) {
+ SerializedPacket packet(
+ CreateNonRetransmittablePacket(new_packet_number));
+ unacked_packets_.AddSentPacket(&packet,
+ QuicPacketNumber(old_packet_number),
+ transmission_type, now_, true);
+ return;
+ }
+ QuicTransmissionInfo* info = unacked_packets_.GetMutableTransmissionInfo(
+ QuicPacketNumber(old_packet_number));
+ QuicStreamId stream_id = QuicUtils::GetHeadersStreamId(
+ CurrentSupportedVersions()[0].transport_version);
+ for (const auto& frame : info->retransmittable_frames) {
+ if (frame.type == STREAM_FRAME) {
+ stream_id = frame.stream_frame.stream_id;
+ break;
+ }
+ }
+ UpdatePacketState(
+ old_packet_number,
+ QuicUtils::RetransmissionTypeToPacketState(transmission_type));
+ info->retransmission = QuicPacketNumber(new_packet_number);
+ SerializedPacket packet(
+ CreateRetransmittablePacketForStream(new_packet_number, stream_id));
+ unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+ transmission_type, now_, true);
+ }
+ QuicUnackedPacketMap unacked_packets_;
+ QuicTime now_;
+ StrictMock<MockSessionNotifier> notifier_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicUnackedPacketMapTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicUnackedPacketMapTest, RttOnly) {
+ // Acks are only tracked for RTT measurement purposes.
+ SerializedPacket packet(CreateNonRetransmittablePacket(1));
+ unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, false);
+
+ uint64_t unacked[] = {1};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(1));
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyInFlightPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_P(QuicUnackedPacketMapTest, RetransmittableInflightAndRtt) {
+ // Simulate a retransmittable packet being sent and acked.
+ SerializedPacket packet(CreateRetransmittablePacket(1));
+ unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+
+ uint64_t unacked[] = {1};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(unacked, QUIC_ARRAYSIZE(unacked));
+
+ unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1));
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(1));
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyInFlightPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_P(QuicUnackedPacketMapTest, StopRetransmission) {
+ const QuicStreamId stream_id = 2;
+ SerializedPacket packet(CreateRetransmittablePacketForStream(1, stream_id));
+ unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+
+ uint64_t unacked[] = {1};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ uint64_t retransmittable[] = {1};
+ VerifyRetransmittablePackets(retransmittable,
+ QUIC_ARRAYSIZE(retransmittable));
+
+ if (unacked_packets_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ } else {
+ unacked_packets_.CancelRetransmissionsForStream(stream_id);
+ }
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_P(QuicUnackedPacketMapTest, StopRetransmissionOnOtherStream) {
+ const QuicStreamId stream_id = 2;
+ SerializedPacket packet(CreateRetransmittablePacketForStream(1, stream_id));
+ unacked_packets_.AddSentPacket(&packet, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+
+ uint64_t unacked[] = {1};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ uint64_t retransmittable[] = {1};
+ VerifyRetransmittablePackets(retransmittable,
+ QUIC_ARRAYSIZE(retransmittable));
+
+ // Stop retransmissions on another stream and verify the packet is unchanged.
+ if (!unacked_packets_.session_decides_what_to_write()) {
+ unacked_packets_.CancelRetransmissionsForStream(stream_id + 2);
+ }
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(retransmittable,
+ QUIC_ARRAYSIZE(retransmittable));
+}
+
+TEST_P(QuicUnackedPacketMapTest, StopRetransmissionAfterRetransmission) {
+ const QuicStreamId stream_id = 2;
+ SerializedPacket packet1(CreateRetransmittablePacketForStream(1, stream_id));
+ unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ RetransmitAndSendPacket(1, 2, LOSS_RETRANSMISSION);
+
+ uint64_t unacked[] = {1, 2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ std::vector<uint64_t> retransmittable;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ retransmittable = {1, 2};
+ } else {
+ retransmittable = {2};
+ }
+ VerifyRetransmittablePackets(&retransmittable[0], retransmittable.size());
+
+ if (unacked_packets_.session_decides_what_to_write()) {
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ } else {
+ unacked_packets_.CancelRetransmissionsForStream(stream_id);
+ }
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_P(QuicUnackedPacketMapTest, RetransmittedPacket) {
+ // Simulate a retransmittable packet being sent, retransmitted, and the first
+ // transmission being acked.
+ SerializedPacket packet1(CreateRetransmittablePacket(1));
+ unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ RetransmitAndSendPacket(1, 2, LOSS_RETRANSMISSION);
+
+ uint64_t unacked[] = {1, 2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ std::vector<uint64_t> retransmittable;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ retransmittable = {1, 2};
+ } else {
+ retransmittable = {2};
+ }
+ VerifyRetransmittablePackets(&retransmittable[0], retransmittable.size());
+
+ EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+ unacked_packets_.RemoveRetransmittability(QuicPacketNumber(1));
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ uint64_t unacked2[] = {1};
+ VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
+ VerifyInFlightPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyInFlightPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_P(QuicUnackedPacketMapTest, RetransmitThreeTimes) {
+ // Simulate a retransmittable packet being sent and retransmitted twice.
+ SerializedPacket packet1(CreateRetransmittablePacket(1));
+ unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ SerializedPacket packet2(CreateRetransmittablePacket(2));
+ unacked_packets_.AddSentPacket(&packet2, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+
+ uint64_t unacked[] = {1, 2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ uint64_t retransmittable[] = {1, 2};
+ VerifyRetransmittablePackets(retransmittable,
+ QUIC_ARRAYSIZE(retransmittable));
+
+ // Early retransmit 1 as 3 and send new data as 4.
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ unacked_packets_.RemoveRetransmittability(QuicPacketNumber(2));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
+ RetransmitAndSendPacket(1, 3, LOSS_RETRANSMISSION);
+ SerializedPacket packet4(CreateRetransmittablePacket(4));
+ unacked_packets_.AddSentPacket(&packet4, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+
+ uint64_t unacked2[] = {1, 3, 4};
+ VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
+ uint64_t pending2[] = {3, 4};
+ VerifyInFlightPackets(pending2, QUIC_ARRAYSIZE(pending2));
+ std::vector<uint64_t> retransmittable2;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ retransmittable2 = {1, 3, 4};
+ } else {
+ retransmittable2 = {3, 4};
+ }
+ VerifyRetransmittablePackets(&retransmittable2[0], retransmittable2.size());
+
+ // Early retransmit 3 (formerly 1) as 5, and remove 1 from unacked.
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(4));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+ unacked_packets_.RemoveRetransmittability(QuicPacketNumber(4));
+ RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION);
+ SerializedPacket packet6(CreateRetransmittablePacket(6));
+ unacked_packets_.AddSentPacket(&packet6, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+
+ std::vector<uint64_t> unacked3;
+ std::vector<uint64_t> retransmittable3;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ unacked3 = {3, 5, 6};
+ retransmittable3 = {3, 5, 6};
+ } else {
+ unacked3 = {3, 5, 6};
+ retransmittable3 = {5, 6};
+ }
+ VerifyUnackedPackets(&unacked3[0], unacked3.size());
+ VerifyRetransmittablePackets(&retransmittable3[0], retransmittable3.size());
+ uint64_t pending3[] = {3, 5, 6};
+ VerifyInFlightPackets(pending3, QUIC_ARRAYSIZE(pending3));
+
+ // Early retransmit 5 as 7 and ensure in flight packet 3 is not removed.
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(6));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(6));
+ unacked_packets_.RemoveRetransmittability(QuicPacketNumber(6));
+ RetransmitAndSendPacket(5, 7, LOSS_RETRANSMISSION);
+
+ std::vector<uint64_t> unacked4;
+ std::vector<uint64_t> retransmittable4;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ unacked4 = {3, 5, 7};
+ retransmittable4 = {3, 5, 7};
+ } else {
+ unacked4 = {3, 5, 7};
+ retransmittable4 = {7};
+ }
+ VerifyUnackedPackets(&unacked4[0], unacked4.size());
+ VerifyRetransmittablePackets(&retransmittable4[0], retransmittable4.size());
+ uint64_t pending4[] = {3, 5, 7};
+ VerifyInFlightPackets(pending4, QUIC_ARRAYSIZE(pending4));
+
+ // Remove the older two transmissions from in flight.
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+ uint64_t pending5[] = {7};
+ VerifyInFlightPackets(pending5, QUIC_ARRAYSIZE(pending5));
+}
+
+TEST_P(QuicUnackedPacketMapTest, RetransmitFourTimes) {
+ // Simulate a retransmittable packet being sent and retransmitted twice.
+ SerializedPacket packet1(CreateRetransmittablePacket(1));
+ unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ SerializedPacket packet2(CreateRetransmittablePacket(2));
+ unacked_packets_.AddSentPacket(&packet2, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+
+ uint64_t unacked[] = {1, 2};
+ VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ VerifyInFlightPackets(unacked, QUIC_ARRAYSIZE(unacked));
+ uint64_t retransmittable[] = {1, 2};
+ VerifyRetransmittablePackets(retransmittable,
+ QUIC_ARRAYSIZE(retransmittable));
+
+ // Early retransmit 1 as 3.
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(2));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+ unacked_packets_.RemoveRetransmittability(QuicPacketNumber(2));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
+ RetransmitAndSendPacket(1, 3, LOSS_RETRANSMISSION);
+
+ uint64_t unacked2[] = {1, 3};
+ VerifyUnackedPackets(unacked2, QUIC_ARRAYSIZE(unacked2));
+ uint64_t pending2[] = {3};
+ VerifyInFlightPackets(pending2, QUIC_ARRAYSIZE(pending2));
+ std::vector<uint64_t> retransmittable2;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ retransmittable2 = {1, 3};
+ } else {
+ retransmittable2 = {3};
+ }
+ VerifyRetransmittablePackets(&retransmittable2[0], retransmittable2.size());
+
+ // TLP 3 (formerly 1) as 4, and don't remove 1 from unacked.
+ RetransmitAndSendPacket(3, 4, TLP_RETRANSMISSION);
+ SerializedPacket packet5(CreateRetransmittablePacket(5));
+ unacked_packets_.AddSentPacket(&packet5, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+
+ uint64_t unacked3[] = {1, 3, 4, 5};
+ VerifyUnackedPackets(unacked3, QUIC_ARRAYSIZE(unacked3));
+ uint64_t pending3[] = {3, 4, 5};
+ VerifyInFlightPackets(pending3, QUIC_ARRAYSIZE(pending3));
+ std::vector<uint64_t> retransmittable3;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ retransmittable3 = {1, 3, 4, 5};
+ } else {
+ retransmittable3 = {4, 5};
+ }
+ VerifyRetransmittablePackets(&retransmittable3[0], retransmittable3.size());
+
+ // Early retransmit 4 as 6 and ensure in flight packet 3 is removed.
+ unacked_packets_.IncreaseLargestAcked(QuicPacketNumber(5));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+ unacked_packets_.RemoveRetransmittability(QuicPacketNumber(5));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(3));
+ unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+ RetransmitAndSendPacket(4, 6, LOSS_RETRANSMISSION);
+
+ std::vector<uint64_t> unacked4;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ unacked4 = {4, 6};
+ } else {
+ unacked4 = {4, 6};
+ }
+ VerifyUnackedPackets(&unacked4[0], unacked4.size());
+ uint64_t pending4[] = {6};
+ VerifyInFlightPackets(pending4, QUIC_ARRAYSIZE(pending4));
+ std::vector<uint64_t> retransmittable4;
+ if (unacked_packets_.session_decides_what_to_write()) {
+ retransmittable4 = {4, 6};
+ } else {
+ retransmittable4 = {6};
+ }
+ VerifyRetransmittablePackets(&retransmittable4[0], retransmittable4.size());
+}
+
+TEST_P(QuicUnackedPacketMapTest, SendWithGap) {
+ // Simulate a retransmittable packet being sent, retransmitted, and the first
+ // transmission being acked.
+ SerializedPacket packet1(CreateRetransmittablePacket(1));
+ unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ SerializedPacket packet3(CreateRetransmittablePacket(3));
+ unacked_packets_.AddSentPacket(&packet3, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ RetransmitAndSendPacket(3, 5, LOSS_RETRANSMISSION);
+
+ EXPECT_EQ(QuicPacketNumber(1u), unacked_packets_.GetLeastUnacked());
+ EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(1)));
+ EXPECT_FALSE(unacked_packets_.IsUnacked(QuicPacketNumber(2)));
+ EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(3)));
+ EXPECT_FALSE(unacked_packets_.IsUnacked(QuicPacketNumber(4)));
+ EXPECT_TRUE(unacked_packets_.IsUnacked(QuicPacketNumber(5)));
+ EXPECT_EQ(QuicPacketNumber(5u), unacked_packets_.largest_sent_packet());
+}
+
+TEST_P(QuicUnackedPacketMapTest, AggregateContiguousAckedStreamFrames) {
+ testing::InSequence s;
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0);
+ unacked_packets_.NotifyAggregatedStreamFrameAcked(QuicTime::Delta::Zero());
+
+ QuicTransmissionInfo info1;
+ QuicStreamFrame stream_frame1(3, false, 0, 100);
+ info1.retransmittable_frames.push_back(QuicFrame(stream_frame1));
+
+ QuicTransmissionInfo info2;
+ QuicStreamFrame stream_frame2(3, false, 100, 100);
+ info2.retransmittable_frames.push_back(QuicFrame(stream_frame2));
+
+ QuicTransmissionInfo info3;
+ QuicStreamFrame stream_frame3(3, false, 200, 100);
+ info3.retransmittable_frames.push_back(QuicFrame(stream_frame3));
+
+ QuicTransmissionInfo info4;
+ QuicStreamFrame stream_frame4(3, true, 300, 0);
+ info4.retransmittable_frames.push_back(QuicFrame(stream_frame4));
+
+ // Verify stream frames are aggregated.
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(info1,
+ QuicTime::Delta::Zero());
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(info2,
+ QuicTime::Delta::Zero());
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(info3,
+ QuicTime::Delta::Zero());
+
+ // Verify aggregated stream frame gets acked since fin is acked.
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(info4,
+ QuicTime::Delta::Zero());
+}
+
+// Regression test for b/112930090.
+TEST_P(QuicUnackedPacketMapTest, CannotAggregateIfDataLengthOverflow) {
+ QuicByteCount kMaxAggregatedDataLength =
+ std::numeric_limits<decltype(QuicStreamFrame().data_length)>::max();
+ QuicStreamId stream_id = 2;
+
+ // acked_stream_length=512 covers the case where a frame will cause the
+ // aggregated frame length to be exactly 64K.
+ // acked_stream_length=1300 covers the case where a frame will cause the
+ // aggregated frame length to exceed 64K.
+ for (const QuicPacketLength acked_stream_length : {512, 1300}) {
+ ++stream_id;
+ QuicStreamOffset offset = 0;
+ // Expected length of the aggregated stream frame.
+ QuicByteCount aggregated_data_length = 0;
+
+ while (offset < 1e6) {
+ QuicTransmissionInfo info;
+ QuicStreamFrame stream_frame(stream_id, false, offset,
+ acked_stream_length);
+ info.retransmittable_frames.push_back(QuicFrame(stream_frame));
+
+ const QuicStreamFrame& aggregated_stream_frame =
+ QuicUnackedPacketMapPeer::GetAggregatedStreamFrame(unacked_packets_);
+ if (aggregated_stream_frame.data_length + acked_stream_length <=
+ kMaxAggregatedDataLength) {
+ // Verify the acked stream frame can be aggregated.
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(
+ info, QuicTime::Delta::Zero());
+ aggregated_data_length += acked_stream_length;
+ testing::Mock::VerifyAndClearExpectations(&notifier_);
+ } else {
+ // Verify the acked stream frame cannot be aggregated because
+ // data_length is overflow.
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(
+ info, QuicTime::Delta::Zero());
+ aggregated_data_length = acked_stream_length;
+ testing::Mock::VerifyAndClearExpectations(&notifier_);
+ }
+
+ EXPECT_EQ(aggregated_data_length, aggregated_stream_frame.data_length);
+ offset += acked_stream_length;
+ }
+
+ // Ack the last frame of the stream.
+ QuicTransmissionInfo info;
+ QuicStreamFrame stream_frame(stream_id, true, offset, acked_stream_length);
+ info.retransmittable_frames.push_back(QuicFrame(stream_frame));
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(info,
+ QuicTime::Delta::Zero());
+ testing::Mock::VerifyAndClearExpectations(&notifier_);
+ }
+}
+
+TEST_P(QuicUnackedPacketMapTest, CannotAggregateAckedControlFrames) {
+ testing::InSequence s;
+ QuicWindowUpdateFrame window_update(1, 5, 100);
+ QuicStreamFrame stream_frame1(3, false, 0, 100);
+ QuicStreamFrame stream_frame2(3, false, 100, 100);
+ QuicBlockedFrame blocked(2, 5);
+ QuicGoAwayFrame go_away(3, QUIC_PEER_GOING_AWAY, 5, "Going away.");
+
+ QuicTransmissionInfo info1;
+ info1.retransmittable_frames.push_back(QuicFrame(&window_update));
+ info1.retransmittable_frames.push_back(QuicFrame(stream_frame1));
+ info1.retransmittable_frames.push_back(QuicFrame(stream_frame2));
+
+ QuicTransmissionInfo info2;
+ info2.retransmittable_frames.push_back(QuicFrame(&blocked));
+ info2.retransmittable_frames.push_back(QuicFrame(&go_away));
+
+ // Verify 2 contiguous stream frames are aggregated.
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(info1,
+ QuicTime::Delta::Zero());
+ // Verify aggregated stream frame gets acked.
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(3);
+ unacked_packets_.MaybeAggregateAckedStreamFrame(info2,
+ QuicTime::Delta::Zero());
+
+ EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0);
+ unacked_packets_.NotifyAggregatedStreamFrameAcked(QuicTime::Delta::Zero());
+}
+
+TEST_P(QuicUnackedPacketMapTest, LargestSentPacketMultiplePacketNumberSpaces) {
+ if (!GetQuicReloadableFlag(quic_use_uber_loss_algorithm)) {
+ return;
+ }
+ unacked_packets_.EnableMultiplePacketNumberSpacesSupport();
+ EXPECT_FALSE(unacked_packets_
+ .GetLargestSentPacketOfPacketNumberSpace(ENCRYPTION_INITIAL)
+ .IsInitialized());
+ // Send packet 1.
+ SerializedPacket packet1(CreateRetransmittablePacket(1));
+ packet1.encryption_level = ENCRYPTION_INITIAL;
+ unacked_packets_.AddSentPacket(&packet1, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ EXPECT_EQ(QuicPacketNumber(1u), unacked_packets_.largest_sent_packet());
+ EXPECT_EQ(QuicPacketNumber(1),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_INITIAL));
+ EXPECT_FALSE(
+ unacked_packets_
+ .GetLargestSentPacketOfPacketNumberSpace(ENCRYPTION_HANDSHAKE)
+ .IsInitialized());
+ // Send packet 2.
+ SerializedPacket packet2(CreateRetransmittablePacket(2));
+ packet2.encryption_level = ENCRYPTION_HANDSHAKE;
+ unacked_packets_.AddSentPacket(&packet2, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ EXPECT_EQ(QuicPacketNumber(2u), unacked_packets_.largest_sent_packet());
+ EXPECT_EQ(QuicPacketNumber(1),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(2),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_HANDSHAKE));
+ EXPECT_FALSE(unacked_packets_
+ .GetLargestSentPacketOfPacketNumberSpace(ENCRYPTION_ZERO_RTT)
+ .IsInitialized());
+ // Send packet 3.
+ SerializedPacket packet3(CreateRetransmittablePacket(3));
+ packet3.encryption_level = ENCRYPTION_ZERO_RTT;
+ unacked_packets_.AddSentPacket(&packet3, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ EXPECT_EQ(QuicPacketNumber(3u), unacked_packets_.largest_sent_packet());
+ EXPECT_EQ(QuicPacketNumber(1),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(2),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(3),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_ZERO_RTT));
+ // Verify forward secure belongs to the same packet number space as encryption
+ // zero rtt.
+ EXPECT_EQ(QuicPacketNumber(3),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_FORWARD_SECURE));
+
+ // Send packet 4.
+ SerializedPacket packet4(CreateRetransmittablePacket(4));
+ packet4.encryption_level = ENCRYPTION_FORWARD_SECURE;
+ unacked_packets_.AddSentPacket(&packet4, QuicPacketNumber(),
+ NOT_RETRANSMISSION, now_, true);
+ EXPECT_EQ(QuicPacketNumber(4u), unacked_packets_.largest_sent_packet());
+ EXPECT_EQ(QuicPacketNumber(1),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_INITIAL));
+ EXPECT_EQ(QuicPacketNumber(2),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(4),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_ZERO_RTT));
+ // Verify forward secure belongs to the same packet number space as encryption
+ // zero rtt.
+ EXPECT_EQ(QuicPacketNumber(4),
+ unacked_packets_.GetLargestSentPacketOfPacketNumberSpace(
+ ENCRYPTION_FORWARD_SECURE));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..413a2446642
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.cc
@@ -0,0 +1,581 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_prefetch.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+namespace {
+
+// We know that >= GCC 4.8 and Clang have a __uint128_t intrinsic. Other
+// compilers don't necessarily, notably MSVC.
+#if defined(__x86_64__) && \
+ ((defined(__GNUC__) && \
+ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \
+ defined(__clang__))
+#define QUIC_UTIL_HAS_UINT128 1
+#endif
+
+#ifdef QUIC_UTIL_HAS_UINT128
+QuicUint128 IncrementalHashFast(QuicUint128 uhash, QuicStringPiece data) {
+ // This code ends up faster than the naive implementation for 2 reasons:
+ // 1. QuicUint128 is sufficiently complicated that the compiler
+ // cannot transform the multiplication by kPrime into a shift-multiply-add;
+ // it has go through all of the instructions for a 128-bit multiply.
+ // 2. Because there are so fewer instructions (around 13), the hot loop fits
+ // nicely in the instruction queue of many Intel CPUs.
+ // kPrime = 309485009821345068724781371
+ static const QuicUint128 kPrime =
+ (static_cast<QuicUint128>(16777216) << 64) + 315;
+ auto hi = QuicUint128High64(uhash);
+ auto lo = QuicUint128Low64(uhash);
+ QuicUint128 xhash = (static_cast<QuicUint128>(hi) << 64) + lo;
+ const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data());
+ for (size_t i = 0; i < data.length(); ++i) {
+ xhash = (xhash ^ static_cast<uint32_t>(octets[i])) * kPrime;
+ }
+ return MakeQuicUint128(QuicUint128High64(xhash), QuicUint128Low64(xhash));
+}
+#endif
+
+#ifndef QUIC_UTIL_HAS_UINT128
+// Slow implementation of IncrementalHash. In practice, only used by Chromium.
+QuicUint128 IncrementalHashSlow(QuicUint128 hash, QuicStringPiece data) {
+ // kPrime = 309485009821345068724781371
+ static const QuicUint128 kPrime = MakeQuicUint128(16777216, 315);
+ const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data());
+ for (size_t i = 0; i < data.length(); ++i) {
+ hash = hash ^ MakeQuicUint128(0, octets[i]);
+ hash = hash * kPrime;
+ }
+ return hash;
+}
+#endif
+
+QuicUint128 IncrementalHash(QuicUint128 hash, QuicStringPiece data) {
+#ifdef QUIC_UTIL_HAS_UINT128
+ return IncrementalHashFast(hash, data);
+#else
+ return IncrementalHashSlow(hash, data);
+#endif
+}
+
+} // namespace
+
+// static
+uint64_t QuicUtils::FNV1a_64_Hash(QuicStringPiece data) {
+ static const uint64_t kOffset = UINT64_C(14695981039346656037);
+ static const uint64_t kPrime = UINT64_C(1099511628211);
+
+ const uint8_t* octets = reinterpret_cast<const uint8_t*>(data.data());
+
+ uint64_t hash = kOffset;
+
+ for (size_t i = 0; i < data.length(); ++i) {
+ hash = hash ^ octets[i];
+ hash = hash * kPrime;
+ }
+
+ return hash;
+}
+
+// static
+QuicUint128 QuicUtils::FNV1a_128_Hash(QuicStringPiece data) {
+ return FNV1a_128_Hash_Three(data, QuicStringPiece(), QuicStringPiece());
+}
+
+// static
+QuicUint128 QuicUtils::FNV1a_128_Hash_Two(QuicStringPiece data1,
+ QuicStringPiece data2) {
+ return FNV1a_128_Hash_Three(data1, data2, QuicStringPiece());
+}
+
+// static
+QuicUint128 QuicUtils::FNV1a_128_Hash_Three(QuicStringPiece data1,
+ QuicStringPiece data2,
+ QuicStringPiece data3) {
+ // The two constants are defined as part of the hash algorithm.
+ // see http://www.isthe.com/chongo/tech/comp/fnv/
+ // kOffset = 144066263297769815596495629667062367629
+ const QuicUint128 kOffset = MakeQuicUint128(UINT64_C(7809847782465536322),
+ UINT64_C(7113472399480571277));
+
+ QuicUint128 hash = IncrementalHash(kOffset, data1);
+ if (data2.empty()) {
+ return hash;
+ }
+
+ hash = IncrementalHash(hash, data2);
+ if (data3.empty()) {
+ return hash;
+ }
+ return IncrementalHash(hash, data3);
+}
+
+// static
+void QuicUtils::SerializeUint128Short(QuicUint128 v, uint8_t* out) {
+ const uint64_t lo = QuicUint128Low64(v);
+ const uint64_t hi = QuicUint128High64(v);
+ // This assumes that the system is little-endian.
+ memcpy(out, &lo, sizeof(lo));
+ memcpy(out + sizeof(lo), &hi, sizeof(hi) / 2);
+}
+
+#define RETURN_STRING_LITERAL(x) \
+ case x: \
+ return #x;
+
+// static
+const char* QuicUtils::EncryptionLevelToString(EncryptionLevel level) {
+ switch (level) {
+ RETURN_STRING_LITERAL(ENCRYPTION_INITIAL);
+ RETURN_STRING_LITERAL(ENCRYPTION_HANDSHAKE);
+ RETURN_STRING_LITERAL(ENCRYPTION_ZERO_RTT);
+ RETURN_STRING_LITERAL(ENCRYPTION_FORWARD_SECURE);
+ RETURN_STRING_LITERAL(NUM_ENCRYPTION_LEVELS);
+ }
+ return "INVALID_ENCRYPTION_LEVEL";
+}
+
+// static
+const char* QuicUtils::TransmissionTypeToString(TransmissionType type) {
+ switch (type) {
+ RETURN_STRING_LITERAL(NOT_RETRANSMISSION);
+ RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMISSION);
+ RETURN_STRING_LITERAL(LOSS_RETRANSMISSION);
+ RETURN_STRING_LITERAL(ALL_UNACKED_RETRANSMISSION);
+ RETURN_STRING_LITERAL(ALL_INITIAL_RETRANSMISSION);
+ RETURN_STRING_LITERAL(RTO_RETRANSMISSION);
+ RETURN_STRING_LITERAL(TLP_RETRANSMISSION);
+ RETURN_STRING_LITERAL(PROBING_RETRANSMISSION);
+ }
+ return "INVALID_TRANSMISSION_TYPE";
+}
+
+std::string QuicUtils::AddressChangeTypeToString(AddressChangeType type) {
+ switch (type) {
+ RETURN_STRING_LITERAL(NO_CHANGE);
+ RETURN_STRING_LITERAL(PORT_CHANGE);
+ RETURN_STRING_LITERAL(IPV4_SUBNET_CHANGE);
+ RETURN_STRING_LITERAL(IPV4_TO_IPV6_CHANGE);
+ RETURN_STRING_LITERAL(IPV6_TO_IPV4_CHANGE);
+ RETURN_STRING_LITERAL(IPV6_TO_IPV6_CHANGE);
+ RETURN_STRING_LITERAL(IPV4_TO_IPV4_CHANGE);
+ }
+ return "INVALID_ADDRESS_CHANGE_TYPE";
+}
+
+const char* QuicUtils::SentPacketStateToString(SentPacketState state) {
+ switch (state) {
+ RETURN_STRING_LITERAL(OUTSTANDING);
+ RETURN_STRING_LITERAL(NEVER_SENT);
+ RETURN_STRING_LITERAL(ACKED);
+ RETURN_STRING_LITERAL(UNACKABLE);
+ RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMITTED);
+ RETURN_STRING_LITERAL(LOST);
+ RETURN_STRING_LITERAL(TLP_RETRANSMITTED);
+ RETURN_STRING_LITERAL(RTO_RETRANSMITTED);
+ RETURN_STRING_LITERAL(PROBE_RETRANSMITTED);
+ }
+ return "INVALID_SENT_PACKET_STATE";
+}
+
+// static
+const char* QuicUtils::QuicLongHeaderTypetoString(QuicLongHeaderType type) {
+ switch (type) {
+ RETURN_STRING_LITERAL(VERSION_NEGOTIATION);
+ RETURN_STRING_LITERAL(INITIAL);
+ RETURN_STRING_LITERAL(RETRY);
+ RETURN_STRING_LITERAL(HANDSHAKE);
+ RETURN_STRING_LITERAL(ZERO_RTT_PROTECTED);
+ default:
+ return "INVALID_PACKET_TYPE";
+ }
+}
+
+// static
+const char* QuicUtils::AckResultToString(AckResult result) {
+ switch (result) {
+ RETURN_STRING_LITERAL(PACKETS_NEWLY_ACKED);
+ RETURN_STRING_LITERAL(NO_PACKETS_NEWLY_ACKED);
+ RETURN_STRING_LITERAL(UNSENT_PACKETS_ACKED);
+ RETURN_STRING_LITERAL(UNACKABLE_PACKETS_ACKED);
+ RETURN_STRING_LITERAL(PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE);
+ }
+ return "INVALID_ACK_RESULT";
+}
+
+// static
+AddressChangeType QuicUtils::DetermineAddressChangeType(
+ const QuicSocketAddress& old_address,
+ const QuicSocketAddress& new_address) {
+ if (!old_address.IsInitialized() || !new_address.IsInitialized() ||
+ old_address == new_address) {
+ return NO_CHANGE;
+ }
+
+ if (old_address.host() == new_address.host()) {
+ return PORT_CHANGE;
+ }
+
+ bool old_ip_is_ipv4 = old_address.host().IsIPv4() ? true : false;
+ bool migrating_ip_is_ipv4 = new_address.host().IsIPv4() ? true : false;
+ if (old_ip_is_ipv4 && !migrating_ip_is_ipv4) {
+ return IPV4_TO_IPV6_CHANGE;
+ }
+
+ if (!old_ip_is_ipv4) {
+ return migrating_ip_is_ipv4 ? IPV6_TO_IPV4_CHANGE : IPV6_TO_IPV6_CHANGE;
+ }
+
+ const int kSubnetMaskLength = 24;
+ if (old_address.host().InSameSubnet(new_address.host(), kSubnetMaskLength)) {
+ // Subnet part does not change (here, we use /24), which is considered to be
+ // caused by NATs.
+ return IPV4_SUBNET_CHANGE;
+ }
+
+ return IPV4_TO_IPV4_CHANGE;
+}
+
+// static
+void QuicUtils::CopyToBuffer(const struct iovec* iov,
+ int iov_count,
+ size_t iov_offset,
+ size_t buffer_length,
+ char* buffer) {
+ int iovnum = 0;
+ while (iovnum < iov_count && iov_offset >= iov[iovnum].iov_len) {
+ iov_offset -= iov[iovnum].iov_len;
+ ++iovnum;
+ }
+ DCHECK_LE(iovnum, iov_count);
+ DCHECK_LE(iov_offset, iov[iovnum].iov_len);
+ if (iovnum >= iov_count || buffer_length == 0) {
+ return;
+ }
+
+ // Unroll the first iteration that handles iov_offset.
+ const size_t iov_available = iov[iovnum].iov_len - iov_offset;
+ size_t copy_len = std::min(buffer_length, iov_available);
+
+ // Try to prefetch the next iov if there is at least one more after the
+ // current. Otherwise, it looks like an irregular access that the hardware
+ // prefetcher won't speculatively prefetch. Only prefetch one iov because
+ // generally, the iov_offset is not 0, input iov consists of 2K buffers and
+ // the output buffer is ~1.4K.
+ if (copy_len == iov_available && iovnum + 1 < iov_count) {
+ char* next_base = static_cast<char*>(iov[iovnum + 1].iov_base);
+ // Prefetch 2 cachelines worth of data to get the prefetcher started; leave
+ // it to the hardware prefetcher after that.
+ QuicPrefetchT0(next_base);
+ if (iov[iovnum + 1].iov_len >= 64) {
+ QuicPrefetchT0(next_base + QUIC_CACHELINE_SIZE);
+ }
+ }
+
+ const char* src = static_cast<char*>(iov[iovnum].iov_base) + iov_offset;
+ while (true) {
+ memcpy(buffer, src, copy_len);
+ buffer_length -= copy_len;
+ buffer += copy_len;
+ if (buffer_length == 0 || ++iovnum >= iov_count) {
+ break;
+ }
+ src = static_cast<char*>(iov[iovnum].iov_base);
+ copy_len = std::min(buffer_length, iov[iovnum].iov_len);
+ }
+ QUIC_BUG_IF(buffer_length > 0) << "Failed to copy entire length to buffer.";
+}
+
+// static
+struct iovec QuicUtils::MakeIovec(QuicStringPiece data) {
+ struct iovec iov = {const_cast<char*>(data.data()),
+ static_cast<size_t>(data.size())};
+ return iov;
+}
+
+// static
+bool QuicUtils::IsAckable(SentPacketState state) {
+ return state != NEVER_SENT && state != ACKED && state != UNACKABLE;
+}
+
+// static
+bool QuicUtils::IsRetransmittableFrame(QuicFrameType type) {
+ switch (type) {
+ case ACK_FRAME:
+ case PADDING_FRAME:
+ case STOP_WAITING_FRAME:
+ case MTU_DISCOVERY_FRAME:
+ return false;
+ default:
+ return true;
+ }
+}
+
+// static
+bool QuicUtils::IsHandshakeFrame(const QuicFrame& frame,
+ QuicTransportVersion transport_version) {
+ if (!QuicVersionUsesCryptoFrames(transport_version)) {
+ return frame.type == STREAM_FRAME &&
+ frame.stream_frame.stream_id == GetCryptoStreamId(transport_version);
+ } else {
+ return frame.type == CRYPTO_FRAME;
+ }
+}
+
+// static
+SentPacketState QuicUtils::RetransmissionTypeToPacketState(
+ TransmissionType retransmission_type) {
+ switch (retransmission_type) {
+ case ALL_UNACKED_RETRANSMISSION:
+ case ALL_INITIAL_RETRANSMISSION:
+ return UNACKABLE;
+ case HANDSHAKE_RETRANSMISSION:
+ return HANDSHAKE_RETRANSMITTED;
+ case LOSS_RETRANSMISSION:
+ return LOST;
+ case TLP_RETRANSMISSION:
+ return TLP_RETRANSMITTED;
+ case RTO_RETRANSMISSION:
+ return RTO_RETRANSMITTED;
+ case PROBING_RETRANSMISSION:
+ return PROBE_RETRANSMITTED;
+ default:
+ QUIC_BUG << QuicUtils::TransmissionTypeToString(retransmission_type)
+ << " is not a retransmission_type";
+ return UNACKABLE;
+ }
+}
+
+// static
+bool QuicUtils::IsIetfPacketHeader(uint8_t first_byte) {
+ return (first_byte & FLAGS_LONG_HEADER) || (first_byte & FLAGS_FIXED_BIT) ||
+ !(first_byte & FLAGS_DEMULTIPLEXING_BIT);
+}
+
+// static
+bool QuicUtils::IsIetfPacketShortHeader(uint8_t first_byte) {
+ return IsIetfPacketHeader(first_byte) && !(first_byte & FLAGS_LONG_HEADER);
+}
+
+// static
+QuicStreamId QuicUtils::GetInvalidStreamId(QuicTransportVersion version) {
+ return version == QUIC_VERSION_99 ? std::numeric_limits<QuicStreamId>::max()
+ : 0;
+}
+
+// static
+QuicStreamId QuicUtils::GetCryptoStreamId(QuicTransportVersion version) {
+ // TODO(nharper): Change this to return GetInvalidStreamId for version 47 or
+ // greater. Currently, too many things break with that change.
+ return version == QUIC_VERSION_99 ? 0 : 1;
+}
+
+// static
+QuicStreamId QuicUtils::GetHeadersStreamId(QuicTransportVersion version) {
+ return version == QUIC_VERSION_99 ? 4 : 3;
+}
+
+// static
+bool QuicUtils::IsClientInitiatedStreamId(QuicTransportVersion version,
+ QuicStreamId id) {
+ if (id == GetInvalidStreamId(version)) {
+ return false;
+ }
+ return version == QUIC_VERSION_99 ? id % 2 == 0 : id % 2 != 0;
+}
+
+// static
+bool QuicUtils::IsServerInitiatedStreamId(QuicTransportVersion version,
+ QuicStreamId id) {
+ if (id == GetInvalidStreamId(version)) {
+ return false;
+ }
+ return version == QUIC_VERSION_99 ? id % 2 != 0 : id % 2 == 0;
+}
+
+// static
+bool QuicUtils::IsBidirectionalStreamId(QuicStreamId id) {
+ return id % 4 < 2;
+}
+
+// static
+StreamType QuicUtils::GetStreamType(QuicStreamId id,
+ Perspective perspective,
+ bool peer_initiated) {
+ if (IsBidirectionalStreamId(id)) {
+ return BIDIRECTIONAL;
+ }
+
+ if (peer_initiated) {
+ if (perspective == Perspective::IS_SERVER) {
+ DCHECK_EQ(2u, id % 4);
+ } else {
+ DCHECK_EQ(Perspective::IS_CLIENT, perspective);
+ DCHECK_EQ(3u, id % 4);
+ }
+ return READ_UNIDIRECTIONAL;
+ }
+
+ if (perspective == Perspective::IS_SERVER) {
+ DCHECK_EQ(3u, id % 4);
+ } else {
+ DCHECK_EQ(Perspective::IS_CLIENT, perspective);
+ DCHECK_EQ(2u, id % 4);
+ }
+ return WRITE_UNIDIRECTIONAL;
+}
+
+// static
+QuicStreamId QuicUtils::StreamIdDelta(QuicTransportVersion version) {
+ return version == QUIC_VERSION_99 ? 4 : 2;
+}
+
+// static
+QuicStreamId QuicUtils::GetFirstBidirectionalStreamId(
+ QuicTransportVersion version,
+ Perspective perspective) {
+ if (perspective == Perspective::IS_CLIENT) {
+ return version == QUIC_VERSION_99 ? 4 : 3;
+ }
+ return version == QUIC_VERSION_99 ? 1 : 2;
+}
+
+// static
+QuicStreamId QuicUtils::GetFirstUnidirectionalStreamId(
+ QuicTransportVersion version,
+ Perspective perspective) {
+ if (perspective == Perspective::IS_CLIENT) {
+ return version == QUIC_VERSION_99 ? 2 : 3;
+ }
+ return version == QUIC_VERSION_99 ? 3 : 2;
+}
+
+// static
+QuicConnectionId QuicUtils::CreateRandomConnectionId() {
+ return CreateRandomConnectionId(kQuicDefaultConnectionIdLength,
+ QuicRandom::GetInstance());
+}
+
+// static
+QuicConnectionId QuicUtils::CreateRandomConnectionId(QuicRandom* random) {
+ return CreateRandomConnectionId(kQuicDefaultConnectionIdLength, random);
+}
+// static
+QuicConnectionId QuicUtils::CreateRandomConnectionId(
+ uint8_t connection_id_length) {
+ return CreateRandomConnectionId(connection_id_length,
+ QuicRandom::GetInstance());
+}
+
+// static
+QuicConnectionId QuicUtils::CreateRandomConnectionId(
+ uint8_t connection_id_length,
+ QuicRandom* random) {
+ if (connection_id_length == 0) {
+ return EmptyQuicConnectionId();
+ }
+ if (connection_id_length > kQuicMaxConnectionIdLength) {
+ QUIC_BUG << "Tried to CreateRandomConnectionId of invalid length "
+ << static_cast<int>(connection_id_length);
+ connection_id_length = kQuicMaxConnectionIdLength;
+ }
+ char connection_id_bytes[kQuicMaxConnectionIdLength];
+ random->RandBytes(connection_id_bytes, connection_id_length);
+ return QuicConnectionId(static_cast<char*>(connection_id_bytes),
+ connection_id_length);
+}
+
+// static
+bool QuicUtils::VariableLengthConnectionIdAllowedForVersion(
+ QuicTransportVersion version) {
+ return version >= QUIC_VERSION_47;
+}
+
+// static
+QuicConnectionId QuicUtils::CreateZeroConnectionId(
+ QuicTransportVersion version) {
+ if (!VariableLengthConnectionIdAllowedForVersion(version)) {
+ char connection_id_bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ return QuicConnectionId(static_cast<char*>(connection_id_bytes),
+ QUIC_ARRAYSIZE(connection_id_bytes));
+ }
+ return EmptyQuicConnectionId();
+}
+
+// static
+bool QuicUtils::IsConnectionIdValidForVersion(QuicConnectionId connection_id,
+ QuicTransportVersion version) {
+ if (VariableLengthConnectionIdAllowedForVersion(version)) {
+ return true;
+ }
+ return connection_id.length() == kQuicDefaultConnectionIdLength;
+}
+
+QuicUint128 QuicUtils::GenerateStatelessResetToken(
+ QuicConnectionId connection_id) {
+ uint64_t data_bytes[3] = {0, 0, 0};
+ static_assert(sizeof(data_bytes) >= kQuicMaxConnectionIdLength,
+ "kQuicMaxConnectionIdLength changed");
+ memcpy(data_bytes, connection_id.data(), connection_id.length());
+ // This is designed so that the common case of 64bit connection IDs
+ // produces a stateless reset token that is equal to the connection ID
+ // interpreted as a 64bit unsigned integer, to facilitate debugging.
+ return MakeQuicUint128(
+ QuicEndian::NetToHost64(sizeof(uint64_t) ^ connection_id.length() ^
+ data_bytes[1] ^ data_bytes[2]),
+ QuicEndian::NetToHost64(data_bytes[0]));
+}
+
+// static
+PacketNumberSpace QuicUtils::GetPacketNumberSpace(
+ EncryptionLevel encryption_level) {
+ switch (encryption_level) {
+ case ENCRYPTION_INITIAL:
+ return INITIAL_DATA;
+ case ENCRYPTION_HANDSHAKE:
+ return HANDSHAKE_DATA;
+ case ENCRYPTION_ZERO_RTT:
+ case ENCRYPTION_FORWARD_SECURE:
+ return APPLICATION_DATA;
+ default:
+ QUIC_BUG << "Try to get packet number space of encryption level: "
+ << EncryptionLevelToString(encryption_level);
+ return NUM_PACKET_NUMBER_SPACES;
+ }
+}
+
+// static
+EncryptionLevel QuicUtils::GetEncryptionLevel(
+ PacketNumberSpace packet_number_space) {
+ switch (packet_number_space) {
+ case INITIAL_DATA:
+ return ENCRYPTION_INITIAL;
+ case HANDSHAKE_DATA:
+ return ENCRYPTION_HANDSHAKE;
+ case APPLICATION_DATA:
+ return ENCRYPTION_FORWARD_SECURE;
+ default:
+ DCHECK(false);
+ return NUM_ENCRYPTION_LEVELS;
+ }
+}
+
+#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
new file mode 100644
index 00000000000..cacbc60219a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_UTILS_H_
+#define QUICHE_QUIC_CORE_QUIC_UTILS_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_iovec.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicUtils {
+ public:
+ QuicUtils() = delete;
+
+ // Returns the 64 bit FNV1a hash of the data. See
+ // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
+ static uint64_t FNV1a_64_Hash(QuicStringPiece data);
+
+ // Returns the 128 bit FNV1a hash of the data. See
+ // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
+ static QuicUint128 FNV1a_128_Hash(QuicStringPiece data);
+
+ // Returns the 128 bit FNV1a hash of the two sequences of data. See
+ // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
+ static QuicUint128 FNV1a_128_Hash_Two(QuicStringPiece data1,
+ QuicStringPiece data2);
+
+ // Returns the 128 bit FNV1a hash of the three sequences of data. See
+ // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
+ static QuicUint128 FNV1a_128_Hash_Three(QuicStringPiece data1,
+ QuicStringPiece data2,
+ QuicStringPiece data3);
+
+ // SerializeUint128 writes the first 96 bits of |v| in little-endian form
+ // to |out|.
+ static void SerializeUint128Short(QuicUint128 v, uint8_t* out);
+
+ // Returns the level of encryption as a char*
+ static const char* EncryptionLevelToString(EncryptionLevel level);
+
+ // Returns TransmissionType as a char*
+ static const char* TransmissionTypeToString(TransmissionType type);
+
+ // Returns AddressChangeType as a string.
+ static std::string AddressChangeTypeToString(AddressChangeType type);
+
+ // Returns SentPacketState as a char*.
+ static const char* SentPacketStateToString(SentPacketState state);
+
+ // Returns QuicLongHeaderType as a char*.
+ static const char* QuicLongHeaderTypetoString(QuicLongHeaderType type);
+
+ // Returns AckResult as a char*.
+ static const char* AckResultToString(AckResult result);
+
+ // Determines and returns change type of address change from |old_address| to
+ // |new_address|.
+ static AddressChangeType DetermineAddressChangeType(
+ const QuicSocketAddress& old_address,
+ const QuicSocketAddress& new_address);
+
+ // Copies |buffer_length| bytes from iov starting at offset |iov_offset| into
+ // buffer. |iov| must be at least iov_offset+length total length and buffer
+ // must be at least |length| long.
+ static void CopyToBuffer(const struct iovec* iov,
+ int iov_count,
+ size_t iov_offset,
+ size_t buffer_length,
+ char* buffer);
+
+ // Creates an iovec pointing to the same data as |data|.
+ static struct iovec MakeIovec(QuicStringPiece data);
+
+ // Returns true if a packet is ackable. A packet is unackable if it can never
+ // be acked. Occurs when a packet is never sent, after it is acknowledged
+ // once, or if it's a crypto packet we never expect to receive an ack for.
+ static bool IsAckable(SentPacketState state);
+
+ // Returns true if frame with |type| is retransmittable. A retransmittable
+ // frame should be retransmitted if it is detected as lost.
+ static bool IsRetransmittableFrame(QuicFrameType type);
+
+ // Returns true if |frame| is a handshake frame in version |version|.
+ static bool IsHandshakeFrame(const QuicFrame& frame,
+ QuicTransportVersion transport_version);
+
+ // Returns packet state corresponding to |retransmission_type|.
+ static SentPacketState RetransmissionTypeToPacketState(
+ TransmissionType retransmission_type);
+
+ // Returns true if header with |first_byte| is considered as an IETF QUIC
+ // packet header.
+ static bool IsIetfPacketHeader(uint8_t first_byte);
+
+ // Returns true if header with |first_byte| is considered as an IETF QUIC
+ // short packet header.
+ static bool IsIetfPacketShortHeader(uint8_t first_byte);
+
+ // Returns ID to denote an invalid stream of |version|.
+ static QuicStreamId GetInvalidStreamId(QuicTransportVersion version);
+
+ // Returns crypto stream ID of |version|.
+ static QuicStreamId GetCryptoStreamId(QuicTransportVersion version);
+
+ // Returns headers stream ID of |version|.
+ static QuicStreamId GetHeadersStreamId(QuicTransportVersion version);
+
+ // Returns true if |id| is considered as client initiated stream ID.
+ static bool IsClientInitiatedStreamId(QuicTransportVersion version,
+ QuicStreamId id);
+
+ // Returns true if |id| is considered as server initiated stream ID.
+ static bool IsServerInitiatedStreamId(QuicTransportVersion version,
+ QuicStreamId id);
+
+ // Returns true if |id| is considered as bidirectional stream ID. Only used in
+ // v99.
+ static bool IsBidirectionalStreamId(QuicStreamId id);
+
+ // Returns stream type. Either |perspective| or |peer_initiated| would be
+ // enough together with |id|. This method enforces that the three parameters
+ // are consistent. Only used in v99.
+ static StreamType GetStreamType(QuicStreamId id,
+ Perspective perspective,
+ bool peer_initiated);
+
+ // Returns the delta between consecutive stream IDs of the same type.
+ static QuicStreamId StreamIdDelta(QuicTransportVersion version);
+
+ // Returns the first initiated bidirectional stream ID of |perspective|.
+ static QuicStreamId GetFirstBidirectionalStreamId(
+ QuicTransportVersion version,
+ Perspective perspective);
+
+ // Returns the first initiated unidirectional stream ID of |perspective|.
+ static QuicStreamId GetFirstUnidirectionalStreamId(
+ QuicTransportVersion version,
+ Perspective perspective);
+
+ // Generates a random 64bit connection ID.
+ static QuicConnectionId CreateRandomConnectionId();
+
+ // Generates a random 64bit connection ID using the provided QuicRandom.
+ static QuicConnectionId CreateRandomConnectionId(QuicRandom* random);
+
+ // Generates a random connection ID of the given length.
+ static QuicConnectionId CreateRandomConnectionId(
+ uint8_t connection_id_length);
+
+ // Generates a random connection ID of the given length using the provided
+ // QuicRandom.
+ static QuicConnectionId CreateRandomConnectionId(uint8_t connection_id_length,
+ QuicRandom* random);
+
+ // Returns true if the QUIC version allows variable length connection IDs.
+ static bool VariableLengthConnectionIdAllowedForVersion(
+ QuicTransportVersion version);
+
+ // Returns true if the connection ID is valid for this QUIC version.
+ static bool IsConnectionIdValidForVersion(QuicConnectionId connection_id,
+ QuicTransportVersion version);
+
+ // Returns a connection ID suitable for QUIC use-cases that do not need the
+ // connection ID for multiplexing. If the version allows variable lengths,
+ // a connection of length zero is returned, otherwise 64bits set to zero.
+ static QuicConnectionId CreateZeroConnectionId(QuicTransportVersion version);
+
+ // Generates a 128bit stateless reset token based on a connection ID.
+ static QuicUint128 GenerateStatelessResetToken(
+ QuicConnectionId connection_id);
+
+ // Determines packet number space from |encryption_level|.
+ static PacketNumberSpace GetPacketNumberSpace(
+ EncryptionLevel encryption_level);
+
+ // Determines encryption level to send packets in |packet_number_space|.
+ static EncryptionLevel GetEncryptionLevel(
+ PacketNumberSpace packet_number_space);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_UTILS_H_
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
new file mode 100644
index 00000000000..d0ce7e80a6f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_utils_test.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicUtilsTest : public QuicTest {};
+
+TEST_F(QuicUtilsTest, DetermineAddressChangeType) {
+ const std::string kIPv4String1 = "1.2.3.4";
+ const std::string kIPv4String2 = "1.2.3.5";
+ const std::string kIPv4String3 = "1.1.3.5";
+ const std::string kIPv6String1 = "2001:700:300:1800::f";
+ const std::string kIPv6String2 = "2001:700:300:1800:1:1:1:f";
+ QuicSocketAddress old_address;
+ QuicSocketAddress new_address;
+ QuicIpAddress address;
+
+ EXPECT_EQ(NO_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+ ASSERT_TRUE(address.FromString(kIPv4String1));
+ old_address = QuicSocketAddress(address, 1234);
+ EXPECT_EQ(NO_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+ new_address = QuicSocketAddress(address, 1234);
+ EXPECT_EQ(NO_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+
+ new_address = QuicSocketAddress(address, 5678);
+ EXPECT_EQ(PORT_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+ ASSERT_TRUE(address.FromString(kIPv6String1));
+ old_address = QuicSocketAddress(address, 1234);
+ new_address = QuicSocketAddress(address, 5678);
+ EXPECT_EQ(PORT_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+
+ ASSERT_TRUE(address.FromString(kIPv4String1));
+ old_address = QuicSocketAddress(address, 1234);
+ ASSERT_TRUE(address.FromString(kIPv6String1));
+ new_address = QuicSocketAddress(address, 1234);
+ EXPECT_EQ(IPV4_TO_IPV6_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+
+ old_address = QuicSocketAddress(address, 1234);
+ ASSERT_TRUE(address.FromString(kIPv4String1));
+ new_address = QuicSocketAddress(address, 1234);
+ EXPECT_EQ(IPV6_TO_IPV4_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+
+ ASSERT_TRUE(address.FromString(kIPv6String2));
+ new_address = QuicSocketAddress(address, 1234);
+ EXPECT_EQ(IPV6_TO_IPV6_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+
+ ASSERT_TRUE(address.FromString(kIPv4String1));
+ old_address = QuicSocketAddress(address, 1234);
+ ASSERT_TRUE(address.FromString(kIPv4String2));
+ new_address = QuicSocketAddress(address, 1234);
+ EXPECT_EQ(IPV4_SUBNET_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+ ASSERT_TRUE(address.FromString(kIPv4String3));
+ new_address = QuicSocketAddress(address, 1234);
+ EXPECT_EQ(IPV4_TO_IPV4_CHANGE,
+ QuicUtils::DetermineAddressChangeType(old_address, new_address));
+}
+
+QuicUint128 IncrementalHashReference(const void* data, size_t len) {
+ // The two constants are defined as part of the hash algorithm.
+ // see http://www.isthe.com/chongo/tech/comp/fnv/
+ // hash = 144066263297769815596495629667062367629
+ QuicUint128 hash = MakeQuicUint128(UINT64_C(7809847782465536322),
+ UINT64_C(7113472399480571277));
+ // kPrime = 309485009821345068724781371
+ const QuicUint128 kPrime = MakeQuicUint128(16777216, 315);
+ const uint8_t* octets = reinterpret_cast<const uint8_t*>(data);
+ for (size_t i = 0; i < len; ++i) {
+ hash = hash ^ MakeQuicUint128(0, octets[i]);
+ hash = hash * kPrime;
+ }
+ return hash;
+}
+
+TEST_F(QuicUtilsTest, ReferenceTest) {
+ std::vector<uint8_t> data(32);
+ for (size_t i = 0; i < data.size(); ++i) {
+ data[i] = i % 255;
+ }
+ EXPECT_EQ(IncrementalHashReference(data.data(), data.size()),
+ QuicUtils::FNV1a_128_Hash(QuicStringPiece(
+ reinterpret_cast<const char*>(data.data()), data.size())));
+}
+
+TEST_F(QuicUtilsTest, IsUnackable) {
+ for (size_t i = FIRST_PACKET_STATE; i <= LAST_PACKET_STATE; ++i) {
+ if (i == NEVER_SENT || i == ACKED || i == UNACKABLE) {
+ EXPECT_FALSE(QuicUtils::IsAckable(static_cast<SentPacketState>(i)));
+ } else {
+ EXPECT_TRUE(QuicUtils::IsAckable(static_cast<SentPacketState>(i)));
+ }
+ }
+}
+
+TEST_F(QuicUtilsTest, RetransmissionTypeToPacketState) {
+ for (size_t i = FIRST_TRANSMISSION_TYPE; i <= LAST_TRANSMISSION_TYPE; ++i) {
+ if (i == NOT_RETRANSMISSION) {
+ continue;
+ }
+ SentPacketState state = QuicUtils::RetransmissionTypeToPacketState(
+ static_cast<TransmissionType>(i));
+ if (i == HANDSHAKE_RETRANSMISSION) {
+ EXPECT_EQ(HANDSHAKE_RETRANSMITTED, state);
+ } else if (i == LOSS_RETRANSMISSION) {
+ EXPECT_EQ(LOST, state);
+ } else if (i == ALL_UNACKED_RETRANSMISSION ||
+ i == ALL_INITIAL_RETRANSMISSION) {
+ EXPECT_EQ(UNACKABLE, state);
+ } else if (i == TLP_RETRANSMISSION) {
+ EXPECT_EQ(TLP_RETRANSMITTED, state);
+ } else if (i == RTO_RETRANSMISSION) {
+ EXPECT_EQ(RTO_RETRANSMITTED, state);
+ } else if (i == PROBING_RETRANSMISSION) {
+ EXPECT_EQ(PROBE_RETRANSMITTED, state);
+ } else {
+ DCHECK(false)
+ << "No corresponding packet state according to transmission type: "
+ << i;
+ }
+ }
+}
+
+TEST_F(QuicUtilsTest, IsIetfPacketHeader) {
+ // IETF QUIC short header
+ uint8_t first_byte = 0;
+ EXPECT_TRUE(QuicUtils::IsIetfPacketHeader(first_byte));
+ EXPECT_TRUE(QuicUtils::IsIetfPacketShortHeader(first_byte));
+
+ // IETF QUIC long header
+ first_byte |= (FLAGS_LONG_HEADER | FLAGS_DEMULTIPLEXING_BIT);
+ EXPECT_TRUE(QuicUtils::IsIetfPacketHeader(first_byte));
+ EXPECT_FALSE(QuicUtils::IsIetfPacketShortHeader(first_byte));
+
+ // IETF QUIC long header, version negotiation.
+ first_byte = 0;
+ first_byte |= FLAGS_LONG_HEADER;
+ EXPECT_TRUE(QuicUtils::IsIetfPacketHeader(first_byte));
+ EXPECT_FALSE(QuicUtils::IsIetfPacketShortHeader(first_byte));
+
+ // GQUIC
+ first_byte = 0;
+ first_byte |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID;
+ EXPECT_FALSE(QuicUtils::IsIetfPacketHeader(first_byte));
+ EXPECT_FALSE(QuicUtils::IsIetfPacketShortHeader(first_byte));
+}
+
+TEST_F(QuicUtilsTest, RandomConnectionId) {
+ MockRandom random(33);
+ QuicConnectionId connection_id = QuicUtils::CreateRandomConnectionId(&random);
+ EXPECT_EQ(connection_id.length(), sizeof(uint64_t));
+ char connection_id_bytes[sizeof(uint64_t)];
+ random.RandBytes(connection_id_bytes, QUIC_ARRAYSIZE(connection_id_bytes));
+ EXPECT_EQ(connection_id,
+ QuicConnectionId(static_cast<char*>(connection_id_bytes),
+ QUIC_ARRAYSIZE(connection_id_bytes)));
+ EXPECT_NE(connection_id, EmptyQuicConnectionId());
+ EXPECT_NE(connection_id, TestConnectionId());
+ EXPECT_NE(connection_id, TestConnectionId(1));
+ EXPECT_NE(connection_id, TestConnectionIdNineBytesLong(1));
+ EXPECT_EQ(QuicUtils::CreateRandomConnectionId().length(),
+ kQuicDefaultConnectionIdLength);
+}
+
+TEST_F(QuicUtilsTest, RandomConnectionIdVariableLength) {
+ MockRandom random(1337);
+ const uint8_t connection_id_length = 9;
+ QuicConnectionId connection_id =
+ QuicUtils::CreateRandomConnectionId(connection_id_length, &random);
+ EXPECT_EQ(connection_id.length(), connection_id_length);
+ char connection_id_bytes[connection_id_length];
+ random.RandBytes(connection_id_bytes, QUIC_ARRAYSIZE(connection_id_bytes));
+ EXPECT_EQ(connection_id,
+ QuicConnectionId(static_cast<char*>(connection_id_bytes),
+ QUIC_ARRAYSIZE(connection_id_bytes)));
+ EXPECT_NE(connection_id, EmptyQuicConnectionId());
+ EXPECT_NE(connection_id, TestConnectionId());
+ EXPECT_NE(connection_id, TestConnectionId(1));
+ EXPECT_NE(connection_id, TestConnectionIdNineBytesLong(1));
+ EXPECT_EQ(QuicUtils::CreateRandomConnectionId(connection_id_length).length(),
+ connection_id_length);
+}
+
+TEST_F(QuicUtilsTest, VariableLengthConnectionId) {
+ EXPECT_FALSE(
+ QuicUtils::VariableLengthConnectionIdAllowedForVersion(QUIC_VERSION_39));
+ EXPECT_TRUE(QuicUtils::IsConnectionIdValidForVersion(
+ QuicUtils::CreateZeroConnectionId(QUIC_VERSION_39), QUIC_VERSION_39));
+ EXPECT_TRUE(QuicUtils::IsConnectionIdValidForVersion(
+ QuicUtils::CreateZeroConnectionId(QUIC_VERSION_99), QUIC_VERSION_99));
+ EXPECT_NE(QuicUtils::CreateZeroConnectionId(QUIC_VERSION_39),
+ EmptyQuicConnectionId());
+ EXPECT_EQ(QuicUtils::CreateZeroConnectionId(QUIC_VERSION_99),
+ EmptyQuicConnectionId());
+ EXPECT_FALSE(QuicUtils::IsConnectionIdValidForVersion(EmptyQuicConnectionId(),
+ QUIC_VERSION_39));
+}
+
+TEST_F(QuicUtilsTest, StatelessResetToken) {
+ QuicConnectionId connection_id1a = test::TestConnectionId(1);
+ QuicConnectionId connection_id1b = test::TestConnectionId(1);
+ QuicConnectionId connection_id2 = test::TestConnectionId(2);
+ QuicUint128 token1a = QuicUtils::GenerateStatelessResetToken(connection_id1a);
+ QuicUint128 token1b = QuicUtils::GenerateStatelessResetToken(connection_id1b);
+ QuicUint128 token2 = QuicUtils::GenerateStatelessResetToken(connection_id2);
+ EXPECT_EQ(token1a, token1b);
+ EXPECT_NE(token1a, token2);
+ EXPECT_EQ(token1a, MakeQuicUint128(0, 1));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..62b2e44dd7a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+#include <algorithm>
+
+namespace quic {
+
+QuicVersionManager::QuicVersionManager(
+ ParsedQuicVersionVector supported_versions)
+ : enable_version_99_(GetQuicReloadableFlag(quic_enable_version_99)),
+ enable_version_47_(GetQuicReloadableFlag(quic_enable_version_47)),
+ enable_version_46_(GetQuicReloadableFlag(quic_enable_version_46)),
+ enable_version_44_(GetQuicReloadableFlag(quic_enable_version_44)),
+ enable_version_43_(GetQuicReloadableFlag(quic_enable_version_43)),
+ disable_version_39_(GetQuicReloadableFlag(quic_disable_version_39)),
+ allowed_supported_versions_(std::move(supported_versions)) {
+ RefilterSupportedVersions();
+}
+
+QuicVersionManager::~QuicVersionManager() {}
+
+const QuicTransportVersionVector&
+QuicVersionManager::GetSupportedTransportVersions() {
+ MaybeRefilterSupportedVersions();
+ return filtered_transport_versions_;
+}
+
+const ParsedQuicVersionVector& QuicVersionManager::GetSupportedVersions() {
+ MaybeRefilterSupportedVersions();
+ return filtered_supported_versions_;
+}
+
+void QuicVersionManager::MaybeRefilterSupportedVersions() {
+ if (enable_version_99_ != GetQuicReloadableFlag(quic_enable_version_99) ||
+ enable_version_47_ != GetQuicReloadableFlag(quic_enable_version_47) ||
+ enable_version_46_ != GetQuicReloadableFlag(quic_enable_version_46) ||
+ enable_version_44_ != GetQuicReloadableFlag(quic_enable_version_44) ||
+ enable_version_43_ != GetQuicReloadableFlag(quic_enable_version_43) ||
+ disable_version_39_ != GetQuicReloadableFlag(quic_disable_version_39)) {
+ enable_version_99_ = GetQuicReloadableFlag(quic_enable_version_99);
+ enable_version_47_ = GetQuicReloadableFlag(quic_enable_version_47);
+ enable_version_46_ = GetQuicReloadableFlag(quic_enable_version_46);
+ enable_version_44_ = GetQuicReloadableFlag(quic_enable_version_44);
+ enable_version_43_ = GetQuicReloadableFlag(quic_enable_version_43);
+ disable_version_39_ = GetQuicReloadableFlag(quic_disable_version_39);
+ RefilterSupportedVersions();
+ }
+}
+
+void QuicVersionManager::RefilterSupportedVersions() {
+ filtered_supported_versions_ =
+ FilterSupportedVersions(allowed_supported_versions_);
+ filtered_transport_versions_.clear();
+ for (ParsedQuicVersion version : filtered_supported_versions_) {
+ auto transport_version = version.transport_version;
+ if (std::find(filtered_transport_versions_.begin(),
+ filtered_transport_versions_.end(),
+ transport_version) == filtered_transport_versions_.end()) {
+ filtered_transport_versions_.push_back(transport_version);
+ }
+ }
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..4fedae35baa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager.h
@@ -0,0 +1,66 @@
+// 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 QUICHE_QUIC_CORE_QUIC_VERSION_MANAGER_H_
+#define QUICHE_QUIC_CORE_QUIC_VERSION_MANAGER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Used to generate filtered supported versions based on flags.
+class QUIC_EXPORT_PRIVATE QuicVersionManager {
+ public:
+ // |supported_versions| should be sorted in the order of preference (typically
+ // highest supported version to the lowest supported version).
+ explicit QuicVersionManager(ParsedQuicVersionVector supported_versions);
+ virtual ~QuicVersionManager();
+
+ // Returns currently supported QUIC versions.
+ // TODO(nharper): Remove this method once it is unused.
+ const QuicTransportVersionVector& GetSupportedTransportVersions();
+
+ // Returns currently supported QUIC versions. This vector has the same order
+ // as the versions passed to the constructor.
+ const ParsedQuicVersionVector& GetSupportedVersions();
+
+ protected:
+ // Maybe refilter filtered_supported_versions_ based on flags.
+ void MaybeRefilterSupportedVersions();
+
+ // Refilters filtered_supported_versions_.
+ virtual void RefilterSupportedVersions();
+
+ const QuicTransportVersionVector& filtered_supported_versions() const {
+ return filtered_transport_versions_;
+ }
+
+ private:
+ // quic_enable_version_99 flag
+ bool enable_version_99_;
+ // quic_enable_version_47 flag
+ bool enable_version_47_;
+ // quic_enable_version_46 flag
+ bool enable_version_46_;
+ // quic_enable_version_44 flag
+ bool enable_version_44_;
+ // quic_enable_version_43 flag
+ bool enable_version_43_;
+ // quic_disable_version_39 flag
+ bool disable_version_39_;
+ // The list of versions that may be supported.
+ ParsedQuicVersionVector allowed_supported_versions_;
+ // This vector contains QUIC versions which are currently supported based on
+ // flags.
+ ParsedQuicVersionVector filtered_supported_versions_;
+ // This vector contains the transport versions from
+ // |filtered_supported_versions_|. No guarantees are made that the same
+ // transport version isn't repeated.
+ QuicTransportVersionVector filtered_transport_versions_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_VERSION_MANAGER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc
new file mode 100644
index 00000000000..7c7a08d655c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_version_manager_test.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicVersionManagerTest : public QuicTest {};
+
+TEST_F(QuicVersionManagerTest, QuicVersionManager) {
+ static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u,
+ "Supported versions out of sync");
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ SetQuicReloadableFlag(quic_enable_version_47, false);
+ SetQuicReloadableFlag(quic_enable_version_46, false);
+ SetQuicReloadableFlag(quic_enable_version_44, false);
+ SetQuicReloadableFlag(quic_enable_version_43, false);
+ SetQuicReloadableFlag(quic_disable_version_39, true);
+ QuicVersionManager manager(AllSupportedVersions());
+
+ EXPECT_EQ(FilterSupportedTransportVersions(AllSupportedTransportVersions()),
+ manager.GetSupportedTransportVersions());
+
+ EXPECT_TRUE(manager.GetSupportedTransportVersions().empty());
+
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_39}),
+ manager.GetSupportedTransportVersions());
+
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_43, QUIC_VERSION_39}),
+ manager.GetSupportedTransportVersions());
+
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ EXPECT_EQ(QuicTransportVersionVector(
+ {QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39}),
+ manager.GetSupportedTransportVersions());
+
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_46, QUIC_VERSION_44,
+ QUIC_VERSION_43, QUIC_VERSION_39}),
+ manager.GetSupportedTransportVersions());
+
+ SetQuicReloadableFlag(quic_enable_version_47, true);
+ EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_47, QUIC_VERSION_46,
+ QUIC_VERSION_44, QUIC_VERSION_43,
+ QUIC_VERSION_39}),
+ manager.GetSupportedTransportVersions());
+
+ SetQuicReloadableFlag(quic_enable_version_99, true);
+ EXPECT_EQ(QuicTransportVersionVector({QUIC_VERSION_99, QUIC_VERSION_47,
+ QUIC_VERSION_46, QUIC_VERSION_44,
+ QUIC_VERSION_43, QUIC_VERSION_39}),
+ manager.GetSupportedTransportVersions());
+
+ // Ensure that all versions are now supported.
+ EXPECT_EQ(FilterSupportedTransportVersions(AllSupportedTransportVersions()),
+ manager.GetSupportedTransportVersions());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..a37f3a7b433
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.cc
@@ -0,0 +1,439 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_tag.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+namespace {
+
+// Constructs a version label from the 4 bytes such that the on-the-wire
+// order will be: d, c, b, a.
+QuicVersionLabel MakeVersionLabel(char a, char b, char c, char d) {
+ return MakeQuicTag(d, c, b, a);
+}
+
+} // namespace
+
+ParsedQuicVersion::ParsedQuicVersion(HandshakeProtocol handshake_protocol,
+ QuicTransportVersion transport_version)
+ : handshake_protocol(handshake_protocol),
+ transport_version(transport_version) {
+ if (handshake_protocol == PROTOCOL_TLS1_3 &&
+ !FLAGS_quic_supports_tls_handshake) {
+ QUIC_BUG << "TLS use attempted when not enabled";
+ }
+}
+
+bool ParsedQuicVersion::KnowsWhichDecrypterToUse() const {
+ return transport_version == QUIC_VERSION_99 ||
+ handshake_protocol == PROTOCOL_TLS1_3;
+}
+
+std::ostream& operator<<(std::ostream& os, const ParsedQuicVersion& version) {
+ os << ParsedQuicVersionToString(version);
+ return os;
+}
+
+QuicVersionLabel CreateQuicVersionLabel(ParsedQuicVersion parsed_version) {
+ char proto = 0;
+ switch (parsed_version.handshake_protocol) {
+ case PROTOCOL_QUIC_CRYPTO:
+ proto = 'Q';
+ break;
+ case PROTOCOL_TLS1_3:
+ proto = 'T';
+ break;
+ default:
+ QUIC_LOG(ERROR) << "Invalid HandshakeProtocol: "
+ << parsed_version.handshake_protocol;
+ return 0;
+ }
+ switch (parsed_version.transport_version) {
+ case QUIC_VERSION_39:
+ return MakeVersionLabel(proto, '0', '3', '9');
+ case QUIC_VERSION_43:
+ return MakeVersionLabel(proto, '0', '4', '3');
+ case QUIC_VERSION_44:
+ return MakeVersionLabel(proto, '0', '4', '4');
+ case QUIC_VERSION_46:
+ return MakeVersionLabel(proto, '0', '4', '6');
+ case QUIC_VERSION_47:
+ return MakeVersionLabel(proto, '0', '4', '7');
+ case QUIC_VERSION_99:
+ if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 &&
+ GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) {
+ return 0xff000000 + GetQuicFlag(FLAGS_quic_ietf_draft_version);
+ }
+ return MakeVersionLabel(proto, '0', '9', '9');
+ default:
+ // This shold be an ERROR because we should never attempt to convert an
+ // invalid QuicTransportVersion to be written to the wire.
+ QUIC_LOG(ERROR) << "Unsupported QuicTransportVersion: "
+ << parsed_version.transport_version;
+ return 0;
+ }
+}
+
+QuicVersionLabelVector CreateQuicVersionLabelVector(
+ const ParsedQuicVersionVector& versions) {
+ QuicVersionLabelVector out;
+ out.reserve(versions.size());
+ for (const auto& version : versions) {
+ out.push_back(CreateQuicVersionLabel(version));
+ }
+ return out;
+}
+
+ParsedQuicVersion ParseQuicVersionLabel(QuicVersionLabel version_label) {
+ std::vector<HandshakeProtocol> protocols = {PROTOCOL_QUIC_CRYPTO};
+ if (FLAGS_quic_supports_tls_handshake) {
+ protocols.push_back(PROTOCOL_TLS1_3);
+ }
+ for (QuicTransportVersion version : kSupportedTransportVersions) {
+ for (HandshakeProtocol handshake : protocols) {
+ if (version_label ==
+ CreateQuicVersionLabel(ParsedQuicVersion(handshake, version))) {
+ return ParsedQuicVersion(handshake, version);
+ }
+ }
+ }
+ // Reading from the client so this should not be considered an ERROR.
+ QUIC_DLOG(INFO) << "Unsupported QuicVersionLabel version: "
+ << QuicVersionLabelToString(version_label);
+ return UnsupportedQuicVersion();
+}
+
+ParsedQuicVersion ParseQuicVersionString(std::string version_string) {
+ if (version_string.empty()) {
+ return UnsupportedQuicVersion();
+ }
+ int quic_version_number = 0;
+ if (QuicTextUtils::StringToInt(version_string, &quic_version_number) &&
+ quic_version_number > 0) {
+ return ParsedQuicVersion(
+ PROTOCOL_QUIC_CRYPTO,
+ static_cast<QuicTransportVersion>(quic_version_number));
+ }
+
+ std::vector<HandshakeProtocol> protocols = {PROTOCOL_QUIC_CRYPTO};
+ if (FLAGS_quic_supports_tls_handshake) {
+ protocols.push_back(PROTOCOL_TLS1_3);
+ }
+ for (QuicTransportVersion version : kSupportedTransportVersions) {
+ for (HandshakeProtocol handshake : protocols) {
+ const ParsedQuicVersion parsed_version =
+ ParsedQuicVersion(handshake, version);
+ if (version_string == ParsedQuicVersionToString(parsed_version)) {
+ return parsed_version;
+ }
+ }
+ }
+ // Still recognize T099 even if flag quic_ietf_draft_version has been changed.
+ if (FLAGS_quic_supports_tls_handshake && version_string == "T099") {
+ return ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99);
+ }
+ // Reading from the client so this should not be considered an ERROR.
+ QUIC_DLOG(INFO) << "Unsupported QUIC version string: \"" << version_string
+ << "\".";
+ return UnsupportedQuicVersion();
+}
+
+QuicTransportVersionVector AllSupportedTransportVersions() {
+ QuicTransportVersionVector supported_versions;
+ for (QuicTransportVersion version : kSupportedTransportVersions) {
+ supported_versions.push_back(version);
+ }
+ return supported_versions;
+}
+
+ParsedQuicVersionVector AllSupportedVersions() {
+ ParsedQuicVersionVector supported_versions;
+ for (HandshakeProtocol protocol : kSupportedHandshakeProtocols) {
+ if (protocol == PROTOCOL_TLS1_3 && !FLAGS_quic_supports_tls_handshake) {
+ continue;
+ }
+ for (QuicTransportVersion version : kSupportedTransportVersions) {
+ if (protocol == PROTOCOL_TLS1_3 &&
+ !QuicVersionUsesCryptoFrames(version)) {
+ // The TLS handshake is only deployable if CRYPTO frames are also used,
+ // which are added in v47.
+ continue;
+ }
+ supported_versions.push_back(ParsedQuicVersion(protocol, version));
+ }
+ }
+ return supported_versions;
+}
+
+// TODO(nharper): Remove this function when it is no longer in use.
+QuicTransportVersionVector CurrentSupportedTransportVersions() {
+ return FilterSupportedTransportVersions(AllSupportedTransportVersions());
+}
+
+ParsedQuicVersionVector CurrentSupportedVersions() {
+ return FilterSupportedVersions(AllSupportedVersions());
+}
+
+// TODO(nharper): Remove this function when it is no longer in use.
+QuicTransportVersionVector FilterSupportedTransportVersions(
+ QuicTransportVersionVector versions) {
+ ParsedQuicVersionVector parsed_versions;
+ for (QuicTransportVersion version : versions) {
+ parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+ ParsedQuicVersionVector filtered_parsed_versions =
+ FilterSupportedVersions(parsed_versions);
+ QuicTransportVersionVector filtered_versions;
+ for (ParsedQuicVersion version : filtered_parsed_versions) {
+ filtered_versions.push_back(version.transport_version);
+ }
+ return filtered_versions;
+}
+
+ParsedQuicVersionVector FilterSupportedVersions(
+ ParsedQuicVersionVector versions) {
+ ParsedQuicVersionVector filtered_versions;
+ filtered_versions.reserve(versions.size());
+ for (ParsedQuicVersion version : versions) {
+ if (version.transport_version == QUIC_VERSION_99) {
+ if (GetQuicReloadableFlag(quic_enable_version_99) &&
+ GetQuicReloadableFlag(quic_enable_version_47) &&
+ GetQuicReloadableFlag(quic_enable_version_46) &&
+ GetQuicReloadableFlag(quic_enable_version_44) &&
+ GetQuicReloadableFlag(quic_enable_version_43)) {
+ filtered_versions.push_back(version);
+ }
+ } else if (version.transport_version == QUIC_VERSION_47) {
+ if (GetQuicReloadableFlag(quic_enable_version_47) &&
+ GetQuicReloadableFlag(quic_enable_version_46) &&
+ GetQuicReloadableFlag(quic_enable_version_44) &&
+ GetQuicReloadableFlag(quic_enable_version_43)) {
+ filtered_versions.push_back(version);
+ }
+ } else if (version.transport_version == QUIC_VERSION_46) {
+ if (GetQuicReloadableFlag(quic_enable_version_46) &&
+ GetQuicReloadableFlag(quic_enable_version_44) &&
+ GetQuicReloadableFlag(quic_enable_version_43)) {
+ filtered_versions.push_back(version);
+ }
+ } else if (version.transport_version == QUIC_VERSION_44) {
+ if (GetQuicReloadableFlag(quic_enable_version_44) &&
+ GetQuicReloadableFlag(quic_enable_version_43)) {
+ filtered_versions.push_back(version);
+ }
+ } else if (version.transport_version == QUIC_VERSION_43) {
+ if (GetQuicReloadableFlag(quic_enable_version_43)) {
+ filtered_versions.push_back(version);
+ }
+ } else if (version.transport_version == QUIC_VERSION_39) {
+ if (!GetQuicReloadableFlag(quic_disable_version_39)) {
+ filtered_versions.push_back(version);
+ }
+ } else {
+ filtered_versions.push_back(version);
+ }
+ }
+ return filtered_versions;
+}
+
+QuicTransportVersionVector VersionOfIndex(
+ const QuicTransportVersionVector& versions,
+ int index) {
+ QuicTransportVersionVector version;
+ int version_count = versions.size();
+ if (index >= 0 && index < version_count) {
+ version.push_back(versions[index]);
+ } else {
+ version.push_back(QUIC_VERSION_UNSUPPORTED);
+ }
+ return version;
+}
+
+ParsedQuicVersionVector ParsedVersionOfIndex(
+ const ParsedQuicVersionVector& versions,
+ int index) {
+ ParsedQuicVersionVector version;
+ int version_count = versions.size();
+ if (index >= 0 && index < version_count) {
+ version.push_back(versions[index]);
+ } else {
+ version.push_back(UnsupportedQuicVersion());
+ }
+ return version;
+}
+
+QuicTransportVersionVector ParsedVersionsToTransportVersions(
+ const ParsedQuicVersionVector& versions) {
+ QuicTransportVersionVector transport_versions;
+ transport_versions.resize(versions.size());
+ for (size_t i = 0; i < versions.size(); ++i) {
+ transport_versions[i] = versions[i].transport_version;
+ }
+ return transport_versions;
+}
+
+QuicVersionLabel QuicVersionToQuicVersionLabel(
+ QuicTransportVersion transport_version) {
+ return CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, transport_version));
+}
+
+std::string QuicVersionLabelToString(QuicVersionLabel version_label) {
+ return QuicTagToString(QuicEndian::HostToNet32(version_label));
+}
+
+std::string QuicVersionLabelVectorToString(
+ const QuicVersionLabelVector& version_labels,
+ const std::string& separator,
+ size_t skip_after_nth_version) {
+ std::string result;
+ for (size_t i = 0; i < version_labels.size(); ++i) {
+ if (i != 0) {
+ result.append(separator);
+ }
+
+ if (i > skip_after_nth_version) {
+ result.append("...");
+ break;
+ }
+ result.append(QuicVersionLabelToString(version_labels[i]));
+ }
+ return result;
+}
+
+QuicTransportVersion QuicVersionLabelToQuicVersion(
+ QuicVersionLabel version_label) {
+ return ParseQuicVersionLabel(version_label).transport_version;
+}
+
+HandshakeProtocol QuicVersionLabelToHandshakeProtocol(
+ QuicVersionLabel version_label) {
+ return ParseQuicVersionLabel(version_label).handshake_protocol;
+}
+
+#define RETURN_STRING_LITERAL(x) \
+ case x: \
+ return #x
+
+std::string QuicVersionToString(QuicTransportVersion transport_version) {
+ switch (transport_version) {
+ RETURN_STRING_LITERAL(QUIC_VERSION_39);
+ RETURN_STRING_LITERAL(QUIC_VERSION_43);
+ RETURN_STRING_LITERAL(QUIC_VERSION_44);
+ RETURN_STRING_LITERAL(QUIC_VERSION_46);
+ RETURN_STRING_LITERAL(QUIC_VERSION_47);
+ RETURN_STRING_LITERAL(QUIC_VERSION_99);
+ default:
+ return "QUIC_VERSION_UNSUPPORTED";
+ }
+}
+
+std::string ParsedQuicVersionToString(ParsedQuicVersion version) {
+ return QuicVersionLabelToString(CreateQuicVersionLabel(version));
+}
+
+std::string QuicTransportVersionVectorToString(
+ const QuicTransportVersionVector& versions) {
+ std::string result = "";
+ for (size_t i = 0; i < versions.size(); ++i) {
+ if (i != 0) {
+ result.append(",");
+ }
+ result.append(QuicVersionToString(versions[i]));
+ }
+ return result;
+}
+
+std::string ParsedQuicVersionVectorToString(
+ const ParsedQuicVersionVector& versions,
+ const std::string& separator,
+ size_t skip_after_nth_version) {
+ std::string result;
+ for (size_t i = 0; i < versions.size(); ++i) {
+ if (i != 0) {
+ result.append(separator);
+ }
+ if (i > skip_after_nth_version) {
+ result.append("...");
+ break;
+ }
+ result.append(ParsedQuicVersionToString(versions[i]));
+ }
+ return result;
+}
+
+ParsedQuicVersion UnsupportedQuicVersion() {
+ return ParsedQuicVersion(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED);
+}
+
+std::string AlpnForVersion(ParsedQuicVersion parsed_version) {
+ if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 &&
+ parsed_version.transport_version == QUIC_VERSION_99 &&
+ GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) {
+ return "h3-" + QuicTextUtils::Uint64ToString(
+ GetQuicFlag(FLAGS_quic_ietf_draft_version));
+ }
+ return "h3-google-" + ParsedQuicVersionToString(parsed_version);
+}
+
+void QuicVersionInitializeSupportForIetfDraft(int32_t draft_version) {
+ if (draft_version < 0 || draft_version >= 256) {
+ QUIC_LOG(FATAL) << "Invalid IETF draft version " << draft_version;
+ return;
+ }
+
+ SetQuicFlag(&FLAGS_quic_ietf_draft_version, draft_version);
+
+ if (draft_version == 0) {
+ return;
+ }
+
+ // Enable necessary flags.
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ SetQuicReloadableFlag(quic_deprecate_ack_bundling_mode, true);
+ SetQuicReloadableFlag(quic_rpm_decides_when_to_send_acks, true);
+ SetQuicReloadableFlag(quic_use_uber_loss_algorithm, true);
+ SetQuicReloadableFlag(quic_use_uber_received_packet_manager, true);
+ SetQuicReloadableFlag(quic_validate_packet_number_post_decryption, true);
+ SetQuicReloadableFlag(quic_print_tag_hex, true);
+ SetQuicRestartFlag(quic_enable_accept_random_ipn, true);
+}
+
+void QuicEnableVersion(ParsedQuicVersion parsed_version) {
+ if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3) {
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ }
+ static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u,
+ "Supported versions out of sync");
+ if (parsed_version.transport_version >= QUIC_VERSION_99) {
+ SetQuicReloadableFlag(quic_enable_version_99, true);
+ }
+ if (parsed_version.transport_version >= QUIC_VERSION_47) {
+ SetQuicReloadableFlag(quic_enable_version_47, true);
+ }
+ if (parsed_version.transport_version >= QUIC_VERSION_46) {
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ }
+ if (parsed_version.transport_version >= QUIC_VERSION_44) {
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ }
+ if (parsed_version.transport_version >= QUIC_VERSION_43) {
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ }
+}
+
+#undef RETURN_STRING_LITERAL // undef for jumbo builds
+} // namespace quic
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
new file mode 100644
index 00000000000..160adeb7f59
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions.h
@@ -0,0 +1,378 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Definitions and utility functions related to handling of QUIC versions.
+//
+// QUIC version is a four-byte tag that can be represented in memory as a
+// QuicVersionLabel type (which is an alias to uint32_t). In actuality, all
+// versions supported by this implementation have the following format:
+// [QT]0\d\d
+// e.g. Q046. Q or T distinguishes the type of handshake used (Q for QUIC
+// Crypto handshake, T for TLS-based handshake), and the two digits at the end
+// is the actual numeric value of transport version used by the code.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_VERSIONS_H_
+#define QUICHE_QUIC_CORE_QUIC_VERSIONS_H_
+
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_tag.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// The available versions of QUIC. The numeric value of the enum is guaranteed
+// to match the number in the name. The versions not currently supported are
+// documented in comments.
+//
+// See go/new-quic-version for more details on how to roll out new versions.
+enum QuicTransportVersion {
+ // Special case to indicate unknown/unsupported QUIC version.
+ QUIC_VERSION_UNSUPPORTED = 0,
+
+ // Version 1 was the first version of QUIC that supported versioning.
+ // Version 2 decoupled versioning of non-cryptographic parameters from the
+ // SCFG.
+ // Version 3 moved public flags into the beginning of the packet.
+ // Version 4 added support for variable-length connection IDs.
+ // Version 5 made specifying FEC groups optional.
+ // Version 6 introduced variable-length packet numbers.
+ // Version 7 introduced a lower-overhead encoding for stream frames.
+ // Version 8 made salt length equal to digest length for the RSA-PSS
+ // signatures.
+ // Version 9 added stream priority.
+ // Version 10 redid the frame type numbering.
+ // Version 11 reduced the length of null encryption authentication tag
+ // from 16 to 12 bytes.
+ // Version 12 made the sequence numbers in the ACK frames variable-sized.
+ // Version 13 added the dedicated header stream.
+ // Version 14 added byte_offset to RST_STREAM frame.
+ // Version 15 added a list of packets recovered using FEC to the ACK frame.
+ // Version 16 added STOP_WAITING frame.
+ // Version 17 added per-stream flow control.
+ // Version 18 added PING frame.
+ // Version 19 added connection-level flow control
+ // Version 20 allowed to set stream- and connection-level flow control windows
+ // to different values.
+ // Version 21 made header and crypto streams flow-controlled.
+ // Version 22 added support for SCUP (server config update) messages.
+ // Version 23 added timestamps into the ACK frame.
+ // Version 24 added SPDY/4 header compression.
+ // Version 25 added support for SPDY/4 header keys and removed error_details
+ // from RST_STREAM frame.
+ // Version 26 added XLCT (expected leaf certificate) tag into CHLO.
+ // Version 27 added a nonce into SHLO.
+ // Version 28 allowed receiver to refuse creating a requested stream.
+ // Version 29 added support for QUIC_STREAM_NO_ERROR.
+ // Version 30 added server-side support for certificate transparency.
+ // Version 31 incorporated the hash of CHLO into the crypto proof supplied by
+ // the server.
+ // Version 32 removed FEC-related fields from wire format.
+ // Version 33 added diversification nonces.
+ // Version 34 removed entropy bits from packets and ACK frames, removed
+ // private flag from packet header and changed the ACK format to
+ // specify ranges of packets acknowledged rather than missing
+ // ranges.
+ // Version 35 allows endpoints to independently set stream limit.
+ // Version 36 added support for forced head-of-line blocking experiments.
+ // Version 37 added perspective into null encryption.
+ // Version 38 switched to IETF padding frame format and support for NSTP (no
+ // stop waiting frame) connection option.
+
+ QUIC_VERSION_39 = 39, // Integers and floating numbers are written in big
+ // endian. Dot not ack acks. Send a connection level
+ // WINDOW_UPDATE every 20 sent packets which do not
+ // contain retransmittable frames.
+
+ // Version 40 was an attempt to convert QUIC to IETF frame format; it was
+ // never shipped due to a bug.
+ // Version 41 was a bugfix for version 40. The working group changed the wire
+ // format before it shipped, which caused it to be never shipped
+ // and all the changes from it to be reverted. No changes from v40
+ // or v41 are present in subsequent versions.
+ // Version 42 allowed receiving overlapping stream data.
+
+ QUIC_VERSION_43 = 43, // PRIORITY frames are sent by client and accepted by
+ // server.
+ QUIC_VERSION_44 = 44, // Use IETF header format.
+
+ // Version 45 added MESSAGE frame.
+
+ QUIC_VERSION_46 = 46, // Use IETF draft-17 header format with demultiplexing
+ // bit.
+ QUIC_VERSION_47 = 47, // Allow variable-length QUIC connection IDs.
+ QUIC_VERSION_99 = 99, // Dumping ground for IETF QUIC changes which are not
+ // yet ready for production.
+};
+
+// The crypto handshake protocols that can be used with QUIC.
+enum HandshakeProtocol {
+ PROTOCOL_UNSUPPORTED,
+ PROTOCOL_QUIC_CRYPTO,
+ PROTOCOL_TLS1_3,
+};
+
+// A parsed QUIC version label which determines that handshake protocol
+// and the transport version.
+struct QUIC_EXPORT_PRIVATE ParsedQuicVersion {
+ HandshakeProtocol handshake_protocol;
+ QuicTransportVersion transport_version;
+
+ ParsedQuicVersion(HandshakeProtocol handshake_protocol,
+ QuicTransportVersion transport_version);
+
+ ParsedQuicVersion(const ParsedQuicVersion& other)
+ : handshake_protocol(other.handshake_protocol),
+ transport_version(other.transport_version) {}
+
+ ParsedQuicVersion& operator=(const ParsedQuicVersion& other) {
+ if (this != &other) {
+ handshake_protocol = other.handshake_protocol;
+ transport_version = other.transport_version;
+ }
+ return *this;
+ }
+
+ bool operator==(const ParsedQuicVersion& other) const {
+ return handshake_protocol == other.handshake_protocol &&
+ transport_version == other.transport_version;
+ }
+
+ bool operator!=(const ParsedQuicVersion& other) const {
+ return handshake_protocol != other.handshake_protocol ||
+ transport_version != other.transport_version;
+ }
+
+ bool KnowsWhichDecrypterToUse() const;
+};
+
+QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion();
+
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+ const ParsedQuicVersion& version);
+
+using ParsedQuicVersionVector = std::vector<ParsedQuicVersion>;
+
+// Representation of the on-the-wire QUIC version number. Will be written/read
+// to the wire in network-byte-order.
+using QuicVersionLabel = uint32_t;
+using QuicVersionLabelVector = std::vector<QuicVersionLabel>;
+
+// This vector contains QUIC versions which we currently support.
+// This should be ordered such that the highest supported version is the first
+// element, with subsequent elements in descending order (versions can be
+// skipped as necessary).
+//
+// See go/new-quic-version for more details on how to roll out new versions.
+static const QuicTransportVersion kSupportedTransportVersions[] = {
+ QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46,
+ QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39,
+};
+
+// This vector contains all crypto handshake protocols that are supported.
+static const HandshakeProtocol kSupportedHandshakeProtocols[] = {
+ PROTOCOL_QUIC_CRYPTO, PROTOCOL_TLS1_3};
+
+typedef std::vector<QuicTransportVersion> QuicTransportVersionVector;
+
+// Returns a vector of QUIC versions in kSupportedTransportVersions.
+QUIC_EXPORT_PRIVATE QuicTransportVersionVector AllSupportedTransportVersions();
+
+// Returns a vector of QUIC versions that is the cartesian product of
+// kSupportedTransportVersions and kSupportedHandshakeProtocols.
+QUIC_EXPORT_PRIVATE ParsedQuicVersionVector AllSupportedVersions();
+
+// Returns a vector of QUIC versions from kSupportedTransportVersions which
+// exclude any versions which are disabled by flags.
+QUIC_EXPORT_PRIVATE QuicTransportVersionVector
+CurrentSupportedTransportVersions();
+
+// Returns a vector of QUIC versions that is the cartesian product of
+// kSupportedTransportVersions and kSupportedHandshakeProtocols, with any
+// versions disabled by flags excluded.
+QUIC_EXPORT_PRIVATE ParsedQuicVersionVector CurrentSupportedVersions();
+
+// Returns a vector of QUIC versions from |versions| which exclude any versions
+// which are disabled by flags.
+QUIC_EXPORT_PRIVATE QuicTransportVersionVector
+FilterSupportedTransportVersions(QuicTransportVersionVector versions);
+
+// Returns a vector of QUIC versions from |versions| which exclude any versions
+// which are disabled by flags.
+QUIC_EXPORT_PRIVATE ParsedQuicVersionVector
+FilterSupportedVersions(ParsedQuicVersionVector versions);
+
+// Returns QUIC version of |index| in result of |versions|. Returns
+// QUIC_VERSION_UNSUPPORTED if |index| is out of bounds.
+QUIC_EXPORT_PRIVATE QuicTransportVersionVector
+VersionOfIndex(const QuicTransportVersionVector& versions, int index);
+
+// Returns QUIC version of |index| in result of |versions|. Returns
+// UnsupportedQuicVersion() if |index| is out of bounds.
+QUIC_EXPORT_PRIVATE ParsedQuicVersionVector
+ParsedVersionOfIndex(const ParsedQuicVersionVector& versions, int index);
+
+// Returns a vector of QuicTransportVersions corresponding to just the transport
+// versions in |versions|. If the input vector contains multiple parsed versions
+// with different handshake protocols (but the same transport version), that
+// transport version will appear in the resulting vector multiple times.
+QUIC_EXPORT_PRIVATE QuicTransportVersionVector
+ParsedVersionsToTransportVersions(const ParsedQuicVersionVector& versions);
+
+// QuicVersionLabel is written to and read from the wire, but we prefer to use
+// the more readable ParsedQuicVersion at other levels.
+// Helper function which translates from a QuicVersionLabel to a
+// ParsedQuicVersion.
+QUIC_EXPORT_PRIVATE ParsedQuicVersion
+ParseQuicVersionLabel(QuicVersionLabel version_label);
+
+// Parses a QUIC version string such as "Q043" or "T099".
+// Also supports parsing numbers such as "44".
+QUIC_EXPORT_PRIVATE ParsedQuicVersion
+ParseQuicVersionString(std::string version_string);
+
+// Constructs a QuicVersionLabel from the provided ParsedQuicVersion.
+QUIC_EXPORT_PRIVATE QuicVersionLabel
+CreateQuicVersionLabel(ParsedQuicVersion parsed_version);
+
+// Constructs a QuicVersionLabelVector from the provided
+// ParsedQuicVersionVector.
+QUIC_EXPORT_PRIVATE QuicVersionLabelVector
+CreateQuicVersionLabelVector(const ParsedQuicVersionVector& versions);
+
+// QuicVersionLabel is written to and read from the wire, but we prefer to use
+// the more readable QuicTransportVersion at other levels.
+// Helper function which translates from a QuicTransportVersion to a
+// QuicVersionLabel. Returns 0 if |version| is unsupported.
+QUIC_EXPORT_PRIVATE QuicVersionLabel
+QuicVersionToQuicVersionLabel(QuicTransportVersion transport_version);
+
+// Helper function which translates from a QuicVersionLabel to a string.
+QUIC_EXPORT_PRIVATE std::string QuicVersionLabelToString(
+ QuicVersionLabel version_label);
+
+// Returns |separator|-separated list of string representations of
+// QuicVersionLabel values in the supplied |version_labels| vector. The values
+// after the (0-based) |skip_after_nth_version|'th are skipped.
+QUIC_EXPORT_PRIVATE std::string QuicVersionLabelVectorToString(
+ const QuicVersionLabelVector& version_labels,
+ const std::string& separator,
+ size_t skip_after_nth_version);
+
+// Returns comma separated list of string representations of QuicVersionLabel
+// values in the supplied |version_labels| vector.
+QUIC_EXPORT_PRIVATE inline std::string QuicVersionLabelVectorToString(
+ const QuicVersionLabelVector& version_labels) {
+ return QuicVersionLabelVectorToString(version_labels, ",",
+ std::numeric_limits<size_t>::max());
+}
+
+// Returns appropriate QuicTransportVersion from a QuicVersionLabel.
+// Returns QUIC_VERSION_UNSUPPORTED if |version_label| cannot be understood.
+QUIC_EXPORT_PRIVATE QuicTransportVersion
+QuicVersionLabelToQuicVersion(QuicVersionLabel version_label);
+
+// Returns the HandshakeProtocol used with the given |version_label|, returning
+// PROTOCOL_UNSUPPORTED if it is unknown.
+QUIC_EXPORT_PRIVATE HandshakeProtocol
+QuicVersionLabelToHandshakeProtocol(QuicVersionLabel version_label);
+
+// Helper function which translates from a QuicTransportVersion to a string.
+// Returns strings corresponding to enum names (e.g. QUIC_VERSION_6).
+QUIC_EXPORT_PRIVATE std::string QuicVersionToString(
+ QuicTransportVersion transport_version);
+
+// Helper function which translates from a ParsedQuicVersion to a string.
+// Returns strings corresponding to the on-the-wire tag.
+QUIC_EXPORT_PRIVATE std::string ParsedQuicVersionToString(
+ ParsedQuicVersion version);
+
+// Returns comma separated list of string representations of
+// QuicTransportVersion enum values in the supplied |versions| vector.
+QUIC_EXPORT_PRIVATE std::string QuicTransportVersionVectorToString(
+ const QuicTransportVersionVector& versions);
+
+// Returns comma separated list of string representations of ParsedQuicVersion
+// values in the supplied |versions| vector.
+QUIC_EXPORT_PRIVATE std::string ParsedQuicVersionVectorToString(
+ const ParsedQuicVersionVector& versions);
+
+// Returns |separator|-separated list of string representations of
+// ParsedQuicVersion values in the supplied |versions| vector. The values after
+// the (0-based) |skip_after_nth_version|'th are skipped.
+QUIC_EXPORT_PRIVATE std::string ParsedQuicVersionVectorToString(
+ const ParsedQuicVersionVector& versions,
+ const std::string& separator,
+ size_t skip_after_nth_version);
+
+// Returns comma separated list of string representations of ParsedQuicVersion
+// values in the supplied |versions| vector.
+QUIC_EXPORT_PRIVATE inline std::string ParsedQuicVersionVectorToString(
+ const ParsedQuicVersionVector& versions) {
+ return ParsedQuicVersionVectorToString(versions, ",",
+ std::numeric_limits<size_t>::max());
+}
+
+// Returns true if QuicSpdyStream encodes body using HTTP/3 specification and
+// sends data frame header along with body.
+QUIC_EXPORT_PRIVATE inline bool VersionHasDataFrameHeader(
+ QuicTransportVersion transport_version) {
+ return transport_version == QUIC_VERSION_99;
+}
+
+// Returns true if QuicSpdySession instantiates a QPACK encoder and decoder.
+// TODO(123528590): Implement the following features and gate them on this
+// function as well, optionally renaming this function as appropriate.
+// Send HEADERS on the request/response stream instead of the headers stream.
+// Send PUSH_PROMISE on the request/response stream instead of headers stream.
+// Send PRIORITY on the request/response stream instead of the headers stream.
+// Do not instantiate the headers stream object.
+QUIC_EXPORT_PRIVATE inline bool VersionUsesQpack(
+ QuicTransportVersion transport_version) {
+ const bool uses_qpack = (transport_version == QUIC_VERSION_99);
+ if (uses_qpack) {
+ DCHECK(VersionHasDataFrameHeader(transport_version));
+ }
+ return uses_qpack;
+}
+
+// Returns whether the transport_version supports the variable length integer
+// length field as defined by IETF QUIC draft-13 and later.
+QUIC_EXPORT_PRIVATE inline bool QuicVersionHasLongHeaderLengths(
+ QuicTransportVersion transport_version) {
+ // TODO(dschinazi) if we enable long header lengths before v99, we need to
+ // add support for fixing up lengths in QuicFramer::BuildDataPacket.
+ return transport_version == QUIC_VERSION_99;
+}
+
+// Returns whether |transport_version| uses CRYPTO frames for the handshake
+// instead of stream 1.
+QUIC_EXPORT_PRIVATE inline bool QuicVersionUsesCryptoFrames(
+ QuicTransportVersion transport_version) {
+ return transport_version == QUIC_VERSION_99;
+}
+
+// Returns whether |transport_version| has HTTP/3 Control stream.
+QUIC_EXPORT_PRIVATE inline bool VersionHasControlStreams(
+ QuicTransportVersion transport_version) {
+ return transport_version == QUIC_VERSION_99;
+}
+
+// Returns the ALPN string to use in TLS for this version of QUIC.
+QUIC_EXPORT_PRIVATE std::string AlpnForVersion(
+ ParsedQuicVersion parsed_version);
+
+// Initializes support for the provided IETF draft version by setting flags
+// and the version label.
+QUIC_EXPORT_PRIVATE void QuicVersionInitializeSupportForIetfDraft(
+ int32_t draft_version);
+
+// Enables the flags required to support this version of QUIC.
+QUIC_EXPORT_PRIVATE void QuicEnableVersion(ParsedQuicVersion parsed_version);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_VERSIONS_H_
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
new file mode 100644
index 00000000000..b04db0caff1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_versions_test.cc
@@ -0,0 +1,635 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mock_log.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using testing::_;
+
+class QuicVersionsTest : public QuicTest {
+ protected:
+ QuicVersionLabel MakeVersionLabel(char a, char b, char c, char d) {
+ return MakeQuicTag(d, c, b, a);
+ }
+};
+
+TEST_F(QuicVersionsTest, QuicVersionToQuicVersionLabel) {
+ // If you add a new version to the QuicTransportVersion enum you will need to
+ // add a new case to QuicVersionToQuicVersionLabel, otherwise this test will
+ // fail.
+
+ // Any logs would indicate an unsupported version which we don't expect.
+ CREATE_QUIC_MOCK_LOG(log);
+ EXPECT_QUIC_LOG_CALL(log).Times(0);
+ log.StartCapturingLogs();
+
+ // Explicitly test a specific version.
+ EXPECT_EQ(MakeQuicTag('9', '3', '0', 'Q'),
+ QuicVersionToQuicVersionLabel(QUIC_VERSION_39));
+
+ // Loop over all supported versions and make sure that we never hit the
+ // default case (i.e. all supported versions should be successfully converted
+ // to valid QuicVersionLabels).
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) {
+ QuicTransportVersion version = kSupportedTransportVersions[i];
+ EXPECT_LT(0u, QuicVersionToQuicVersionLabel(version));
+ }
+}
+
+TEST_F(QuicVersionsTest, QuicVersionToQuicVersionLabelUnsupported) {
+ // TODO(rjshade): Change to DFATAL once we actually support multiple versions,
+ // and QuicConnectionTest::SendVersionNegotiationPacket can be changed to use
+ // mis-matched versions rather than relying on QUIC_VERSION_UNSUPPORTED.
+ CREATE_QUIC_MOCK_LOG(log);
+ log.StartCapturingLogs();
+
+ if (QUIC_LOG_ERROR_IS_ON()) {
+ EXPECT_QUIC_LOG_CALL_CONTAINS(log, ERROR,
+ "Unsupported QuicTransportVersion: 0");
+ }
+
+ EXPECT_EQ(0u, QuicVersionToQuicVersionLabel(QUIC_VERSION_UNSUPPORTED));
+}
+
+TEST_F(QuicVersionsTest, QuicVersionLabelToQuicTransportVersion) {
+ // If you add a new version to the QuicTransportVersion enum you will need to
+ // add a new case to QuicVersionLabelToQuicTransportVersion, otherwise this
+ // test will fail.
+
+ // Any logs would indicate an unsupported version which we don't expect.
+ CREATE_QUIC_MOCK_LOG(log);
+ EXPECT_QUIC_LOG_CALL(log).Times(0);
+ log.StartCapturingLogs();
+
+ // Explicitly test specific versions.
+ EXPECT_EQ(QUIC_VERSION_39,
+ QuicVersionLabelToQuicVersion(MakeQuicTag('9', '3', '0', 'Q')));
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) {
+ QuicTransportVersion version = kSupportedTransportVersions[i];
+
+ // Get the label from the version (we can loop over QuicVersions easily).
+ QuicVersionLabel version_label = QuicVersionToQuicVersionLabel(version);
+ EXPECT_LT(0u, version_label);
+
+ // Now try converting back.
+ QuicTransportVersion label_to_transport_version =
+ QuicVersionLabelToQuicVersion(version_label);
+ EXPECT_EQ(version, label_to_transport_version);
+ EXPECT_NE(QUIC_VERSION_UNSUPPORTED, label_to_transport_version);
+ }
+}
+
+TEST_F(QuicVersionsTest, QuicVersionLabelToQuicVersionUnsupported) {
+ CREATE_QUIC_MOCK_LOG(log);
+ if (QUIC_DLOG_INFO_IS_ON()) {
+ EXPECT_QUIC_LOG_CALL_CONTAINS(log, INFO,
+ "Unsupported QuicVersionLabel version: EKAF")
+ .Times(1);
+ }
+ log.StartCapturingLogs();
+
+ EXPECT_EQ(QUIC_VERSION_UNSUPPORTED,
+ QuicVersionLabelToQuicVersion(MakeQuicTag('F', 'A', 'K', 'E')));
+}
+
+TEST_F(QuicVersionsTest, QuicVersionLabelToHandshakeProtocol) {
+ CREATE_QUIC_MOCK_LOG(log);
+ EXPECT_QUIC_LOG_CALL(log).Times(0);
+ log.StartCapturingLogs();
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) {
+ QuicVersionLabel version_label =
+ QuicVersionToQuicVersionLabel(kSupportedTransportVersions[i]);
+ EXPECT_EQ(PROTOCOL_QUIC_CRYPTO,
+ QuicVersionLabelToHandshakeProtocol(version_label));
+ }
+
+ // Test a TLS version:
+ FLAGS_quic_supports_tls_handshake = true;
+ QuicTag tls_tag = MakeQuicTag('3', '4', '0', 'T');
+ EXPECT_EQ(PROTOCOL_TLS1_3, QuicVersionLabelToHandshakeProtocol(tls_tag));
+
+ FLAGS_quic_supports_tls_handshake = false;
+ if (QUIC_DLOG_INFO_IS_ON()) {
+ EXPECT_QUIC_LOG_CALL_CONTAINS(log, INFO,
+ "Unsupported QuicVersionLabel version: T043")
+ .Times(1);
+ }
+ EXPECT_EQ(PROTOCOL_UNSUPPORTED, QuicVersionLabelToHandshakeProtocol(tls_tag));
+}
+
+TEST_F(QuicVersionsTest, ParseQuicVersionLabel) {
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
+ ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '3', '9')));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43),
+ ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '3')));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44),
+ ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '4')));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46),
+ ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '6')));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47),
+ ParseQuicVersionLabel(MakeVersionLabel('Q', '0', '4', '7')));
+
+ // Test a TLS version:
+ FLAGS_quic_supports_tls_handshake = true;
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_39),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '9')));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '3')));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4')));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6')));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '7')));
+
+ FLAGS_quic_supports_tls_handshake = false;
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '5')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '9')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '3')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '5')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '7')));
+}
+
+TEST_F(QuicVersionsTest, ParseQuicVersionString) {
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39),
+ ParseQuicVersionString("Q039"));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43),
+ ParseQuicVersionString("Q043"));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44),
+ ParseQuicVersionString("Q044"));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46),
+ ParseQuicVersionString("Q046"));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47),
+ ParseQuicVersionString("Q047"));
+
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString(""));
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("Q 47"));
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("Q047 "));
+
+ // Test a TLS version:
+ FLAGS_quic_supports_tls_handshake = true;
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_39),
+ ParseQuicVersionString("T039"));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43),
+ ParseQuicVersionString("T043"));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44),
+ ParseQuicVersionString("T044"));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46),
+ ParseQuicVersionString("T046"));
+ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47),
+ ParseQuicVersionString("T047"));
+
+ FLAGS_quic_supports_tls_handshake = false;
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("T035"));
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("T039"));
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("T043"));
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("T044"));
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("T045"));
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("T046"));
+ EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("T047"));
+}
+
+TEST_F(QuicVersionsTest, CreateQuicVersionLabel) {
+ EXPECT_EQ(MakeVersionLabel('Q', '0', '3', '9'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39)));
+ EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '3'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43)));
+ EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '4'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_44)));
+ EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '6'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46)));
+ EXPECT_EQ(MakeVersionLabel('Q', '0', '4', '7'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47)));
+
+ // Test a TLS version:
+ FLAGS_quic_supports_tls_handshake = true;
+ EXPECT_EQ(MakeVersionLabel('T', '0', '3', '9'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_39)));
+ EXPECT_EQ(MakeVersionLabel('T', '0', '4', '3'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_43)));
+ EXPECT_EQ(MakeVersionLabel('T', '0', '4', '4'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_44)));
+ EXPECT_EQ(MakeVersionLabel('T', '0', '4', '6'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_46)));
+ EXPECT_EQ(MakeVersionLabel('T', '0', '4', '7'),
+ CreateQuicVersionLabel(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47)));
+
+ FLAGS_quic_supports_tls_handshake = false;
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '5')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '3', '9')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '3')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '4')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '6')));
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParseQuicVersionLabel(MakeVersionLabel('T', '0', '4', '7')));
+}
+
+TEST_F(QuicVersionsTest, QuicVersionLabelToString) {
+ QuicVersionLabelVector version_labels = {
+ MakeVersionLabel('Q', '0', '3', '5'),
+ MakeVersionLabel('Q', '0', '3', '7'),
+ MakeVersionLabel('T', '0', '3', '8'),
+ };
+
+ EXPECT_EQ("Q035", QuicVersionLabelToString(version_labels[0]));
+ EXPECT_EQ("T038", QuicVersionLabelToString(version_labels[2]));
+
+ EXPECT_EQ("Q035,Q037,T038", QuicVersionLabelVectorToString(version_labels));
+ EXPECT_EQ("Q035:Q037:T038",
+ QuicVersionLabelVectorToString(version_labels, ":", 2));
+ EXPECT_EQ("Q035|Q037|...",
+ QuicVersionLabelVectorToString(version_labels, "|", 1));
+}
+
+TEST_F(QuicVersionsTest, QuicVersionToString) {
+ EXPECT_EQ("QUIC_VERSION_39", QuicVersionToString(QUIC_VERSION_39));
+ EXPECT_EQ("QUIC_VERSION_UNSUPPORTED",
+ QuicVersionToString(QUIC_VERSION_UNSUPPORTED));
+
+ QuicTransportVersion single_version[] = {QUIC_VERSION_39};
+ QuicTransportVersionVector versions_vector;
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(single_version); ++i) {
+ versions_vector.push_back(single_version[i]);
+ }
+ EXPECT_EQ("QUIC_VERSION_39",
+ QuicTransportVersionVectorToString(versions_vector));
+
+ QuicTransportVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED,
+ QUIC_VERSION_39};
+ versions_vector.clear();
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(multiple_versions); ++i) {
+ versions_vector.push_back(multiple_versions[i]);
+ }
+ EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_39",
+ QuicTransportVersionVectorToString(versions_vector));
+
+ // Make sure that all supported versions are present in QuicVersionToString.
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kSupportedTransportVersions); ++i) {
+ QuicTransportVersion version = kSupportedTransportVersions[i];
+ EXPECT_NE("QUIC_VERSION_UNSUPPORTED", QuicVersionToString(version));
+ }
+}
+
+TEST_F(QuicVersionsTest, ParsedQuicVersionToString) {
+ ParsedQuicVersion unsupported = UnsupportedQuicVersion();
+ ParsedQuicVersion version39(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_39);
+ EXPECT_EQ("Q039", ParsedQuicVersionToString(version39));
+ EXPECT_EQ("0", ParsedQuicVersionToString(unsupported));
+
+ ParsedQuicVersionVector versions_vector = {version39};
+ EXPECT_EQ("Q039", ParsedQuicVersionVectorToString(versions_vector));
+
+ versions_vector = {unsupported, version39};
+ EXPECT_EQ("0,Q039", ParsedQuicVersionVectorToString(versions_vector));
+ EXPECT_EQ("0:Q039", ParsedQuicVersionVectorToString(versions_vector, ":",
+ versions_vector.size()));
+ EXPECT_EQ("0|...", ParsedQuicVersionVectorToString(versions_vector, "|", 0));
+
+ // Make sure that all supported versions are present in
+ // ParsedQuicVersionToString.
+ FLAGS_quic_supports_tls_handshake = true;
+ for (QuicTransportVersion transport_version : kSupportedTransportVersions) {
+ for (HandshakeProtocol protocol : kSupportedHandshakeProtocols) {
+ EXPECT_NE("0", ParsedQuicVersionToString(
+ ParsedQuicVersion(protocol, transport_version)));
+ }
+ }
+}
+TEST_F(QuicVersionsTest, AllSupportedTransportVersions) {
+ QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+ ASSERT_EQ(QUIC_ARRAYSIZE(kSupportedTransportVersions), all_versions.size());
+ for (size_t i = 0; i < all_versions.size(); ++i) {
+ EXPECT_EQ(kSupportedTransportVersions[i], all_versions[i]);
+ }
+}
+
+TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsAllVersions) {
+ QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_47, true);
+ SetQuicReloadableFlag(quic_enable_version_99, true);
+ ParsedQuicVersionVector parsed_versions;
+ for (QuicTransportVersion version : all_versions) {
+ parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+ QuicTransportVersionVector expected_versions = {
+ QUIC_VERSION_99, QUIC_VERSION_47, QUIC_VERSION_46,
+ QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39};
+ ParsedQuicVersionVector expected_parsed_versions;
+ for (QuicTransportVersion version : expected_versions) {
+ expected_parsed_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+
+ ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
+ ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
+}
+
+TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo99) {
+ QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_47, true);
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ ParsedQuicVersionVector parsed_versions;
+ for (QuicTransportVersion version : all_versions) {
+ parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+ QuicTransportVersionVector expected_versions = {
+ QUIC_VERSION_47, QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43,
+ QUIC_VERSION_39};
+ ParsedQuicVersionVector expected_parsed_versions;
+ for (QuicTransportVersion version : expected_versions) {
+ expected_parsed_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+
+ ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
+ ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
+}
+
+TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo47) {
+ QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_47, false);
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ ParsedQuicVersionVector parsed_versions;
+ for (QuicTransportVersion version : all_versions) {
+ parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+ QuicTransportVersionVector expected_versions = {
+ QUIC_VERSION_46, QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39};
+ ParsedQuicVersionVector expected_parsed_versions;
+ for (QuicTransportVersion version : expected_versions) {
+ expected_parsed_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+
+ ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
+ ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
+}
+
+TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo46) {
+ QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ SetQuicReloadableFlag(quic_enable_version_46, false);
+ SetQuicReloadableFlag(quic_enable_version_47, false);
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ ParsedQuicVersionVector parsed_versions;
+ for (QuicTransportVersion version : all_versions) {
+ parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+ QuicTransportVersionVector expected_versions = {
+ QUIC_VERSION_44, QUIC_VERSION_43, QUIC_VERSION_39};
+ ParsedQuicVersionVector expected_parsed_versions;
+ for (QuicTransportVersion version : expected_versions) {
+ expected_parsed_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+
+ ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
+ ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
+}
+
+TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo44) {
+ QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ SetQuicReloadableFlag(quic_enable_version_44, false);
+ SetQuicReloadableFlag(quic_enable_version_46, false);
+ SetQuicReloadableFlag(quic_enable_version_47, false);
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ ParsedQuicVersionVector parsed_versions;
+ for (QuicTransportVersion version : all_versions) {
+ parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+ QuicTransportVersionVector expected_versions = {QUIC_VERSION_43,
+ QUIC_VERSION_39};
+ ParsedQuicVersionVector expected_parsed_versions;
+ for (QuicTransportVersion version : expected_versions) {
+ expected_parsed_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+
+ ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
+ ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
+}
+
+TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo43) {
+ QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, false);
+ SetQuicReloadableFlag(quic_enable_version_44, false);
+ SetQuicReloadableFlag(quic_enable_version_46, false);
+ SetQuicReloadableFlag(quic_enable_version_47, false);
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ ParsedQuicVersionVector parsed_versions;
+ for (QuicTransportVersion version : all_versions) {
+ parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+ QuicTransportVersionVector expected_versions = {QUIC_VERSION_39};
+ ParsedQuicVersionVector expected_parsed_versions;
+ for (QuicTransportVersion version : expected_versions) {
+ expected_parsed_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+
+ ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
+ ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
+}
+
+TEST_F(QuicVersionsTest, FilterSupportedTransportVersionsNo39) {
+ QuicTransportVersionVector all_versions = AllSupportedTransportVersions();
+ SetQuicReloadableFlag(quic_disable_version_39, true);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ SetQuicReloadableFlag(quic_enable_version_44, false);
+ SetQuicReloadableFlag(quic_enable_version_46, false);
+ SetQuicReloadableFlag(quic_enable_version_47, false);
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+ ParsedQuicVersionVector parsed_versions;
+ for (QuicTransportVersion version : all_versions) {
+ parsed_versions.push_back(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+ QuicTransportVersionVector expected_versions = {QUIC_VERSION_43};
+ ParsedQuicVersionVector expected_parsed_versions;
+ for (QuicTransportVersion version : expected_versions) {
+ expected_parsed_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, version));
+ }
+
+ ASSERT_EQ(expected_versions, FilterSupportedTransportVersions(all_versions));
+ ASSERT_EQ(expected_parsed_versions, FilterSupportedVersions(parsed_versions));
+}
+
+TEST_F(QuicVersionsTest, LookUpVersionByIndex) {
+ QuicTransportVersionVector all_versions = {QUIC_VERSION_39};
+ int version_count = all_versions.size();
+ for (int i = -5; i <= version_count + 1; ++i) {
+ if (i >= 0 && i < version_count) {
+ EXPECT_EQ(all_versions[i], VersionOfIndex(all_versions, i)[0]);
+ } else {
+ EXPECT_EQ(QUIC_VERSION_UNSUPPORTED, VersionOfIndex(all_versions, i)[0]);
+ }
+ }
+}
+
+TEST_F(QuicVersionsTest, LookUpParsedVersionByIndex) {
+ ParsedQuicVersionVector all_versions = AllSupportedVersions();
+ int version_count = all_versions.size();
+ for (int i = -5; i <= version_count + 1; ++i) {
+ if (i >= 0 && i < version_count) {
+ EXPECT_EQ(all_versions[i], ParsedVersionOfIndex(all_versions, i)[0]);
+ } else {
+ EXPECT_EQ(UnsupportedQuicVersion(),
+ ParsedVersionOfIndex(all_versions, i)[0]);
+ }
+ }
+}
+
+TEST_F(QuicVersionsTest, ParsedVersionsToTransportVersions) {
+ ParsedQuicVersionVector all_versions = AllSupportedVersions();
+ QuicTransportVersionVector transport_versions =
+ ParsedVersionsToTransportVersions(all_versions);
+ ASSERT_EQ(all_versions.size(), transport_versions.size());
+ for (size_t i = 0; i < all_versions.size(); ++i) {
+ EXPECT_EQ(transport_versions[i], all_versions[i].transport_version);
+ }
+}
+
+// This test may appear to be so simplistic as to be unnecessary,
+// yet a typo was made in doing the #defines and it was caught
+// only in some test far removed from here... Better safe than sorry.
+TEST_F(QuicVersionsTest, CheckVersionNumbersForTypos) {
+ static_assert(QUIC_ARRAYSIZE(kSupportedTransportVersions) == 6u,
+ "Supported versions out of sync");
+ EXPECT_EQ(QUIC_VERSION_39, 39);
+ EXPECT_EQ(QUIC_VERSION_43, 43);
+ EXPECT_EQ(QUIC_VERSION_44, 44);
+ EXPECT_EQ(QUIC_VERSION_46, 46);
+ EXPECT_EQ(QUIC_VERSION_47, 47);
+ EXPECT_EQ(QUIC_VERSION_99, 99);
+}
+
+TEST_F(QuicVersionsTest, AlpnForVersion) {
+ FLAGS_quic_supports_tls_handshake = true;
+ ParsedQuicVersion parsed_version_q047 =
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47);
+ ParsedQuicVersion parsed_version_t047 =
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47);
+ ParsedQuicVersion parsed_version_t099 =
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99);
+ FLAGS_quic_supports_tls_handshake = false;
+
+ EXPECT_EQ("h3-google-Q047", AlpnForVersion(parsed_version_q047));
+ EXPECT_EQ("h3-google-T047", AlpnForVersion(parsed_version_t047));
+ EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099));
+}
+
+TEST_F(QuicVersionsTest, InitializeSupportForIetfDraft) {
+ FLAGS_quic_supports_tls_handshake = true;
+ ParsedQuicVersion parsed_version_t099 =
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99);
+ FLAGS_quic_supports_tls_handshake = false;
+ EXPECT_EQ(MakeVersionLabel('T', '0', '9', '9'),
+ CreateQuicVersionLabel(parsed_version_t099));
+ EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099));
+
+ QuicVersionInitializeSupportForIetfDraft(0);
+ EXPECT_EQ(MakeVersionLabel('T', '0', '9', '9'),
+ CreateQuicVersionLabel(parsed_version_t099));
+ EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099));
+ EXPECT_FALSE(FLAGS_quic_supports_tls_handshake);
+
+ QuicVersionInitializeSupportForIetfDraft(18);
+ EXPECT_TRUE(FLAGS_quic_supports_tls_handshake);
+ EXPECT_EQ(MakeVersionLabel(0xff, 0, 0, 18),
+ CreateQuicVersionLabel(parsed_version_t099));
+ EXPECT_EQ("h3-18", AlpnForVersion(parsed_version_t099));
+
+ QuicVersionInitializeSupportForIetfDraft(0);
+ EXPECT_EQ(MakeVersionLabel('T', '0', '9', '9'),
+ CreateQuicVersionLabel(parsed_version_t099));
+ EXPECT_EQ("h3-google-T099", AlpnForVersion(parsed_version_t099));
+}
+
+TEST_F(QuicVersionsTest, QuicEnableVersion) {
+ FLAGS_quic_supports_tls_handshake = true;
+ ParsedQuicVersion parsed_version_q047 =
+ ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_47);
+ ParsedQuicVersion parsed_version_t047 =
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_47);
+ ParsedQuicVersion parsed_version_t099 =
+ ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99);
+ FLAGS_quic_supports_tls_handshake = false;
+ SetQuicReloadableFlag(quic_disable_version_39, false);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+ SetQuicReloadableFlag(quic_enable_version_44, true);
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_47, false);
+ SetQuicReloadableFlag(quic_enable_version_99, false);
+
+ QuicEnableVersion(parsed_version_q047);
+ EXPECT_FALSE(FLAGS_quic_supports_tls_handshake);
+ EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_47));
+ EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_99));
+
+ QuicEnableVersion(parsed_version_t047);
+ EXPECT_TRUE(FLAGS_quic_supports_tls_handshake);
+ EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_47));
+ EXPECT_FALSE(GetQuicReloadableFlag(quic_enable_version_99));
+
+ QuicEnableVersion(parsed_version_t099);
+ EXPECT_TRUE(FLAGS_quic_supports_tls_handshake);
+ EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_47));
+ EXPECT_TRUE(GetQuicReloadableFlag(quic_enable_version_99));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.cc b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.cc
new file mode 100644
index 00000000000..e3929557caf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+QuicWriteBlockedList::QuicWriteBlockedList() : last_priority_popped_(0) {
+ memset(batch_write_stream_id_, 0, sizeof(batch_write_stream_id_));
+ memset(bytes_left_for_batch_write_, 0, sizeof(bytes_left_for_batch_write_));
+}
+
+QuicWriteBlockedList::~QuicWriteBlockedList() {}
+
+} // 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
new file mode 100644
index 00000000000..83c2e2069be
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list.h
@@ -0,0 +1,261 @@
+// Copyright 2014 The Chromium 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_WRITE_BLOCKED_LIST_H_
+#define QUICHE_QUIC_CORE_QUIC_WRITE_BLOCKED_LIST_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h"
+
+namespace quic {
+
+// Keeps tracks of the QUIC streams that have data to write, sorted by
+// priority. QUIC stream priority order is:
+// Crypto stream > Headers stream > Data streams by requested priority.
+class QUIC_EXPORT_PRIVATE QuicWriteBlockedList {
+ private:
+ typedef spdy::PriorityWriteScheduler<QuicStreamId> QuicPriorityWriteScheduler;
+
+ public:
+ explicit QuicWriteBlockedList();
+ QuicWriteBlockedList(const QuicWriteBlockedList&) = delete;
+ QuicWriteBlockedList& operator=(const QuicWriteBlockedList&) = delete;
+ ~QuicWriteBlockedList();
+
+ bool HasWriteBlockedDataStreams() const {
+ return priority_write_scheduler_.HasReadyStreams();
+ }
+
+ bool HasWriteBlockedSpecialStream() const {
+ return static_stream_collection_.num_blocked() > 0;
+ }
+
+ size_t NumBlockedSpecialStreams() const {
+ return static_stream_collection_.num_blocked();
+ }
+
+ size_t NumBlockedStreams() const {
+ return NumBlockedSpecialStreams() +
+ priority_write_scheduler_.NumReadyStreams();
+ }
+
+ bool ShouldYield(QuicStreamId id) const {
+ for (const auto& stream : static_stream_collection_) {
+ if (stream.id == id) {
+ // Static streams should never yield to data streams, or to lower
+ // priority static stream.
+ return false;
+ }
+ if (stream.is_blocked) {
+ return true; // All data streams yield to static streams.
+ }
+ }
+
+ return priority_write_scheduler_.ShouldYield(id);
+ }
+
+ // Pops the highest priorty stream, special casing crypto and headers streams.
+ // Latches the most recently popped data stream for batch writing purposes.
+ QuicStreamId PopFront() {
+ QuicStreamId static_stream_id;
+ if (static_stream_collection_.UnblockFirstBlocked(&static_stream_id)) {
+ return static_stream_id;
+ }
+
+ const auto id_and_precedence =
+ priority_write_scheduler_.PopNextReadyStreamAndPrecedence();
+ const QuicStreamId id = std::get<0>(id_and_precedence);
+ const spdy::SpdyPriority priority =
+ std::get<1>(id_and_precedence).spdy3_priority();
+
+ if (!priority_write_scheduler_.HasReadyStreams()) {
+ // If no streams are blocked, don't bother latching. This stream will be
+ // the first popped for its priority anyway.
+ batch_write_stream_id_[priority] = 0;
+ last_priority_popped_ = priority;
+ } else if (batch_write_stream_id_[priority] != id) {
+ // If newly latching this batch write stream, let it write 16k.
+ batch_write_stream_id_[priority] = id;
+ bytes_left_for_batch_write_[priority] = 16000;
+ last_priority_popped_ = priority;
+ }
+
+ return id;
+ }
+
+ void RegisterStream(QuicStreamId stream_id,
+ bool is_static_stream,
+ spdy::SpdyPriority priority) {
+ DCHECK(!priority_write_scheduler_.StreamRegistered(stream_id));
+ if (is_static_stream) {
+ static_stream_collection_.Register(stream_id);
+ return;
+ }
+
+ priority_write_scheduler_.RegisterStream(
+ stream_id, spdy::SpdyStreamPrecedence(priority));
+ }
+
+ void UnregisterStream(QuicStreamId stream_id, bool is_static) {
+ if (is_static) {
+ static_stream_collection_.Unregister(stream_id);
+ return;
+ }
+ priority_write_scheduler_.UnregisterStream(stream_id);
+ }
+
+ void UpdateStreamPriority(QuicStreamId stream_id,
+ spdy::SpdyPriority new_priority) {
+ DCHECK(!static_stream_collection_.IsRegistered(stream_id));
+ priority_write_scheduler_.UpdateStreamPrecedence(
+ stream_id, spdy::SpdyStreamPrecedence(new_priority));
+ }
+
+ void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) {
+ if (batch_write_stream_id_[last_priority_popped_] == stream_id) {
+ // If this was the last data stream popped by PopFront, update the
+ // bytes remaining in its batch write.
+ bytes_left_for_batch_write_[last_priority_popped_] -=
+ static_cast<int32_t>(bytes);
+ }
+ }
+
+ // Pushes a stream to the back of the list for its priority level *unless* it
+ // is latched for doing batched writes in which case it goes to the front of
+ // the list for its priority level.
+ // Headers and crypto streams are special cased to always resume first.
+ void AddStream(QuicStreamId stream_id) {
+ if (static_stream_collection_.SetBlocked(stream_id)) {
+ return;
+ }
+
+ bool push_front =
+ stream_id == batch_write_stream_id_[last_priority_popped_] &&
+ bytes_left_for_batch_write_[last_priority_popped_] > 0;
+ priority_write_scheduler_.MarkStreamReady(stream_id, push_front);
+ }
+
+ // Returns true if stream with |stream_id| is write blocked.
+ bool IsStreamBlocked(QuicStreamId stream_id) const {
+ for (const auto& stream : static_stream_collection_) {
+ if (stream.id == stream_id) {
+ return stream.is_blocked;
+ }
+ }
+
+ return priority_write_scheduler_.IsStreamReady(stream_id);
+ }
+
+ private:
+ QuicPriorityWriteScheduler priority_write_scheduler_;
+
+ // If performing batch writes, this will be the stream ID of the stream doing
+ // batch writes for this priority level. We will allow this stream to write
+ // until it has written kBatchWriteSize bytes, it has no more data to write,
+ // or a higher priority stream preempts.
+ QuicStreamId batch_write_stream_id_[spdy::kV3LowestPriority + 1];
+ // Set to kBatchWriteSize when we set a new batch_write_stream_id_ for a given
+ // priority. This is decremented with each write the stream does until it is
+ // done with its batch write.
+ int32_t bytes_left_for_batch_write_[spdy::kV3LowestPriority + 1];
+ // Tracks the last priority popped for UpdateBytesForStream.
+ spdy::SpdyPriority last_priority_popped_;
+
+ // A StaticStreamCollection is a vector of <QuicStreamId, bool> pairs plus a
+ // eagerly-computed number of blocked static streams.
+ class StaticStreamCollection {
+ public:
+ struct StreamIdBlockedPair {
+ QuicStreamId id;
+ bool is_blocked;
+ };
+
+ // Optimized for the typical case of 2 static streams per session.
+ typedef QuicInlinedVector<StreamIdBlockedPair, 2> StreamsVector;
+
+ StreamsVector::const_iterator begin() const { return streams_.cbegin(); }
+
+ StreamsVector::const_iterator end() const { return streams_.cend(); }
+
+ size_t num_blocked() const { return num_blocked_; }
+
+ // Add |id| to the collection in unblocked state.
+ void Register(QuicStreamId id) {
+ DCHECK(!IsRegistered(id));
+ streams_.push_back({id, false});
+ }
+
+ // True if |id| is in the collection, regardless of its state.
+ bool IsRegistered(QuicStreamId id) const {
+ for (const auto& stream : streams_) {
+ if (stream.id == id) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Remove |id| from the collection, if it is in the blocked state, reduce
+ // |num_blocked_| by 1.
+ void Unregister(QuicStreamId id) {
+ for (auto it = streams_.begin(); it != streams_.end(); ++it) {
+ if (it->id == id) {
+ if (it->is_blocked) {
+ --num_blocked_;
+ }
+ streams_.erase(it);
+ return;
+ }
+ }
+ DCHECK(false) << "Erasing a non-exist stream with id " << id;
+ }
+
+ // Set |id| to be blocked. If |id| is not already blocked, increase
+ // |num_blocked_| by 1.
+ // Return true if |id| is in the collection.
+ bool SetBlocked(QuicStreamId id) {
+ for (auto& stream : streams_) {
+ if (stream.id == id) {
+ if (!stream.is_blocked) {
+ stream.is_blocked = true;
+ ++num_blocked_;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Unblock the first blocked stream in the collection.
+ // If no stream is blocked, return false. Otherwise return true, set *id to
+ // the unblocked stream id and reduce |num_blocked_| by 1.
+ bool UnblockFirstBlocked(QuicStreamId* id) {
+ for (auto& stream : streams_) {
+ if (stream.is_blocked) {
+ --num_blocked_;
+ stream.is_blocked = false;
+ *id = stream.id;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ size_t num_blocked_ = 0;
+ StreamsVector streams_;
+ };
+
+ StaticStreamCollection static_stream_collection_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QUIC_WRITE_BLOCKED_LIST_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc
new file mode 100644
index 00000000000..a9319e333e6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc
@@ -0,0 +1,233 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using spdy::kV3HighestPriority;
+using spdy::kV3LowestPriority;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicWriteBlockedListTest : public QuicTest {};
+
+TEST_F(QuicWriteBlockedListTest, PriorityOrder) {
+ QuicWriteBlockedList write_blocked_list;
+
+ // Mark streams blocked in roughly reverse priority order, and
+ // verify that streams are sorted.
+ write_blocked_list.RegisterStream(40, false, kV3LowestPriority);
+ write_blocked_list.RegisterStream(23, false, kV3HighestPriority);
+ write_blocked_list.RegisterStream(17, false, kV3HighestPriority);
+ write_blocked_list.RegisterStream(1, true, kV3HighestPriority);
+ write_blocked_list.RegisterStream(3, true, kV3HighestPriority);
+
+ write_blocked_list.AddStream(40);
+ EXPECT_TRUE(write_blocked_list.IsStreamBlocked(40));
+ write_blocked_list.AddStream(23);
+ EXPECT_TRUE(write_blocked_list.IsStreamBlocked(23));
+ write_blocked_list.AddStream(17);
+ EXPECT_TRUE(write_blocked_list.IsStreamBlocked(17));
+ write_blocked_list.AddStream(3);
+ EXPECT_TRUE(write_blocked_list.IsStreamBlocked(3));
+ write_blocked_list.AddStream(1);
+ EXPECT_TRUE(write_blocked_list.IsStreamBlocked(1));
+
+ EXPECT_EQ(5u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedSpecialStream());
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedSpecialStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+ // The Crypto stream is highest priority.
+ EXPECT_EQ(1u, write_blocked_list.PopFront());
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedSpecialStreams());
+ EXPECT_FALSE(write_blocked_list.IsStreamBlocked(1));
+ // Followed by the Headers stream.
+ EXPECT_EQ(3u, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedSpecialStreams());
+ EXPECT_FALSE(write_blocked_list.IsStreamBlocked(3));
+ // Streams with same priority are popped in the order they were inserted.
+ EXPECT_EQ(23u, write_blocked_list.PopFront());
+ EXPECT_FALSE(write_blocked_list.IsStreamBlocked(23));
+ EXPECT_EQ(17u, write_blocked_list.PopFront());
+ EXPECT_FALSE(write_blocked_list.IsStreamBlocked(17));
+ // Low priority stream appears last.
+ EXPECT_EQ(40u, write_blocked_list.PopFront());
+ EXPECT_FALSE(write_blocked_list.IsStreamBlocked(40));
+
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedSpecialStream());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST_F(QuicWriteBlockedListTest, CryptoStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.RegisterStream(1, true, kV3HighestPriority);
+ write_blocked_list.AddStream(1);
+
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedSpecialStream());
+ EXPECT_EQ(1u, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedSpecialStream());
+}
+
+TEST_F(QuicWriteBlockedListTest, HeadersStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.RegisterStream(3, true, kV3HighestPriority);
+ write_blocked_list.AddStream(3);
+
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedSpecialStream());
+ EXPECT_EQ(3u, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedSpecialStream());
+}
+
+TEST_F(QuicWriteBlockedListTest, VerifyHeadersStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.RegisterStream(5, false, kV3HighestPriority);
+ write_blocked_list.RegisterStream(3, true, kV3HighestPriority);
+ write_blocked_list.AddStream(5);
+ write_blocked_list.AddStream(3);
+
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedSpecialStream());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+ // In newer QUIC versions, there is a headers stream which is
+ // higher priority than data streams.
+ EXPECT_EQ(3u, write_blocked_list.PopFront());
+ EXPECT_EQ(5u, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedSpecialStream());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST_F(QuicWriteBlockedListTest, NoDuplicateEntries) {
+ // Test that QuicWriteBlockedList doesn't allow duplicate entries.
+ QuicWriteBlockedList write_blocked_list;
+
+ // Try to add a stream to the write blocked list multiple times at the same
+ // priority.
+ const QuicStreamId kBlockedId = 3 + 2;
+ write_blocked_list.RegisterStream(kBlockedId, false, kV3HighestPriority);
+ write_blocked_list.AddStream(kBlockedId);
+ write_blocked_list.AddStream(kBlockedId);
+ write_blocked_list.AddStream(kBlockedId);
+
+ // This should only result in one blocked stream being added.
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+
+ // There should only be one stream to pop off the front.
+ EXPECT_EQ(kBlockedId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST_F(QuicWriteBlockedListTest, BatchingWrites) {
+ QuicWriteBlockedList write_blocked_list;
+
+ const QuicStreamId id1 = 3 + 2;
+ const QuicStreamId id2 = id1 + 2;
+ const QuicStreamId id3 = id2 + 2;
+ write_blocked_list.RegisterStream(id1, false, kV3LowestPriority);
+ write_blocked_list.RegisterStream(id2, false, kV3LowestPriority);
+ write_blocked_list.RegisterStream(id3, false, kV3HighestPriority);
+
+ write_blocked_list.AddStream(id1);
+ write_blocked_list.AddStream(id2);
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+
+ // The first stream we push back should stay at the front until 16k is
+ // written.
+ EXPECT_EQ(id1, write_blocked_list.PopFront());
+ write_blocked_list.UpdateBytesForStream(id1, 15999);
+ write_blocked_list.AddStream(id1);
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+ EXPECT_EQ(id1, write_blocked_list.PopFront());
+
+ // Once 16k is written the first stream will yield to the next.
+ write_blocked_list.UpdateBytesForStream(id1, 1);
+ write_blocked_list.AddStream(id1);
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+ EXPECT_EQ(id2, write_blocked_list.PopFront());
+
+ // Set the new stream to have written all but one byte.
+ write_blocked_list.UpdateBytesForStream(id2, 15999);
+ write_blocked_list.AddStream(id2);
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+
+ // Ensure higher priority streams are popped first.
+ write_blocked_list.AddStream(id3);
+ EXPECT_EQ(id3, write_blocked_list.PopFront());
+
+ // Higher priority streams will always be popped first, even if using their
+ // byte quota
+ write_blocked_list.UpdateBytesForStream(id3, 20000);
+ write_blocked_list.AddStream(id3);
+ EXPECT_EQ(id3, write_blocked_list.PopFront());
+
+ // Once the higher priority stream is out of the way, id2 will resume its 16k
+ // write, with only 1 byte remaining of its guaranteed write allocation.
+ EXPECT_EQ(id2, write_blocked_list.PopFront());
+ write_blocked_list.UpdateBytesForStream(id2, 1);
+ write_blocked_list.AddStream(id2);
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+ EXPECT_EQ(id1, write_blocked_list.PopFront());
+}
+
+TEST_F(QuicWriteBlockedListTest, Ceding) {
+ QuicWriteBlockedList write_blocked_list;
+
+ write_blocked_list.RegisterStream(15, false, kV3HighestPriority);
+ write_blocked_list.RegisterStream(16, false, kV3HighestPriority);
+ write_blocked_list.RegisterStream(5, false, 5);
+ write_blocked_list.RegisterStream(4, false, 5);
+ write_blocked_list.RegisterStream(7, false, 7);
+ write_blocked_list.RegisterStream(1, true, kV3HighestPriority);
+ write_blocked_list.RegisterStream(3, true, kV3HighestPriority);
+
+ // When nothing is on the list, nothing yields.
+ EXPECT_FALSE(write_blocked_list.ShouldYield(5));
+
+ write_blocked_list.AddStream(5);
+ // 5 should not yield to itself.
+ EXPECT_FALSE(write_blocked_list.ShouldYield(5));
+ // 4 and 7 are equal or lower priority and should yield to 5.
+ EXPECT_TRUE(write_blocked_list.ShouldYield(4));
+ EXPECT_TRUE(write_blocked_list.ShouldYield(7));
+ // 15, headers and crypto should preempt 5.
+ EXPECT_FALSE(write_blocked_list.ShouldYield(15));
+ EXPECT_FALSE(write_blocked_list.ShouldYield(3));
+ EXPECT_FALSE(write_blocked_list.ShouldYield(1));
+
+ // Block a high priority stream.
+ write_blocked_list.AddStream(15);
+ // 16 should yield (same priority) but headers and crypto will still not.
+ EXPECT_TRUE(write_blocked_list.ShouldYield(16));
+ EXPECT_FALSE(write_blocked_list.ShouldYield(3));
+ EXPECT_FALSE(write_blocked_list.ShouldYield(1));
+
+ // Block the headers stream. All streams but crypto and headers should yield.
+ write_blocked_list.AddStream(3);
+ EXPECT_TRUE(write_blocked_list.ShouldYield(16));
+ EXPECT_TRUE(write_blocked_list.ShouldYield(15));
+ EXPECT_FALSE(write_blocked_list.ShouldYield(3));
+ EXPECT_FALSE(write_blocked_list.ShouldYield(1));
+
+ // Block the crypto stream. All streams but crypto should yield.
+ write_blocked_list.AddStream(1);
+ EXPECT_TRUE(write_blocked_list.ShouldYield(16));
+ EXPECT_TRUE(write_blocked_list.ShouldYield(15));
+ EXPECT_TRUE(write_blocked_list.ShouldYield(3));
+ EXPECT_FALSE(write_blocked_list.ShouldYield(1));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/session_notifier_interface.h b/chromium/net/third_party/quiche/src/quic/core/session_notifier_interface.h
new file mode 100644
index 00000000000..83fc0f1985e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/session_notifier_interface.h
@@ -0,0 +1,43 @@
+// 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_CORE_SESSION_NOTIFIER_INTERFACE_H_
+#define QUICHE_QUIC_CORE_SESSION_NOTIFIER_INTERFACE_H_
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+
+namespace quic {
+
+// Pure virtual class to be notified when a packet containing a frame is acked
+// or lost.
+class QUIC_EXPORT_PRIVATE SessionNotifierInterface {
+ public:
+ virtual ~SessionNotifierInterface() {}
+
+ // Called when |frame| is acked. Returns true if any new data gets acked,
+ // returns false otherwise.
+ virtual bool OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) = 0;
+
+ // Called when |frame| is retransmitted.
+ virtual void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) = 0;
+
+ // Called when |frame| is considered as lost.
+ virtual void OnFrameLost(const QuicFrame& frame) = 0;
+
+ // Called to retransmit |frames| with transmission |type|.
+ virtual void RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) = 0;
+
+ // Returns true if |frame| is outstanding and waiting to be acked.
+ virtual bool IsFrameOutstanding(const QuicFrame& frame) const = 0;
+
+ // Returns true if crypto stream is waiting for acks.
+ virtual bool HasUnackedCryptoData() const = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_SESSION_NOTIFIER_INTERFACE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/stateless_rejector.cc b/chromium/net/third_party/quiche/src/quic/core/stateless_rejector.cc
new file mode 100644
index 00000000000..e2ef51cb845
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/stateless_rejector.cc
@@ -0,0 +1,162 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/stateless_rejector.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+
+namespace quic {
+
+class StatelessRejector::ValidateCallback
+ : public ValidateClientHelloResultCallback {
+ public:
+ explicit ValidateCallback(
+ std::unique_ptr<StatelessRejector> rejector,
+ std::unique_ptr<StatelessRejector::ProcessDoneCallback> cb)
+ : rejector_(std::move(rejector)), cb_(std::move(cb)) {}
+
+ ~ValidateCallback() override = default;
+
+ void Run(QuicReferenceCountedPointer<Result> result,
+ std::unique_ptr<ProofSource::Details> /* proof_source_details */)
+ override {
+ StatelessRejector* rejector_ptr = rejector_.get();
+ rejector_ptr->ProcessClientHello(std::move(result), std::move(rejector_),
+ std::move(cb_));
+ }
+
+ private:
+ std::unique_ptr<StatelessRejector> rejector_;
+ std::unique_ptr<StatelessRejector::ProcessDoneCallback> cb_;
+};
+
+StatelessRejector::StatelessRejector(
+ ParsedQuicVersion version,
+ const ParsedQuicVersionVector& versions,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicClock* clock,
+ QuicRandom* random,
+ QuicByteCount chlo_packet_size,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& server_address)
+ : state_(UNKNOWN),
+ error_(QUIC_INTERNAL_ERROR),
+ version_(version),
+ versions_(versions),
+ connection_id_(EmptyQuicConnectionId()),
+ chlo_packet_size_(chlo_packet_size),
+ client_address_(client_address),
+ server_address_(server_address),
+ clock_(clock),
+ random_(random),
+ crypto_config_(crypto_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ signed_config_(new QuicSignedServerConfig),
+ params_(new QuicCryptoNegotiatedParameters) {}
+
+StatelessRejector::~StatelessRejector() = default;
+
+void StatelessRejector::OnChlo(QuicTransportVersion version,
+ QuicConnectionId connection_id,
+ QuicConnectionId server_designated_connection_id,
+ const CryptoHandshakeMessage& message) {
+ DCHECK_EQ(kCHLO, message.tag());
+ DCHECK_NE(connection_id, server_designated_connection_id);
+ DCHECK_EQ(state_, UNKNOWN);
+
+ if (!GetQuicReloadableFlag(enable_quic_stateless_reject_support) ||
+ !GetQuicReloadableFlag(quic_use_cheap_stateless_rejects) ||
+ !QuicCryptoServerStream::DoesPeerSupportStatelessRejects(message)) {
+ state_ = UNSUPPORTED;
+ return;
+ }
+
+ connection_id_ = connection_id;
+ server_designated_connection_id_ = server_designated_connection_id;
+ chlo_ = message; // Note: copies the message
+}
+
+void StatelessRejector::Process(std::unique_ptr<StatelessRejector> rejector,
+ std::unique_ptr<ProcessDoneCallback> done_cb) {
+ QUIC_BUG_IF(rejector->state() != UNKNOWN) << "StatelessRejector::Process "
+ "called for a rejector which "
+ "has already made a decision";
+ StatelessRejector* rejector_ptr = rejector.get();
+ rejector_ptr->crypto_config_->ValidateClientHello(
+ rejector_ptr->chlo_, rejector_ptr->client_address_.host(),
+ rejector_ptr->server_address_, rejector_ptr->version_.transport_version,
+ rejector_ptr->clock_, rejector_ptr->signed_config_,
+ std::unique_ptr<ValidateCallback>(
+ new ValidateCallback(std::move(rejector), std::move(done_cb))));
+}
+
+class StatelessRejector::ProcessClientHelloCallback
+ : public ProcessClientHelloResultCallback {
+ public:
+ ProcessClientHelloCallback(
+ std::unique_ptr<StatelessRejector> rejector,
+ std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb)
+ : rejector_(std::move(rejector)), done_cb_(std::move(done_cb)) {}
+
+ void Run(QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> /* proof_source_details */)
+ override {
+ StatelessRejector* rejector_ptr = rejector_.get();
+ rejector_ptr->ProcessClientHelloDone(
+ error, error_details, std::move(message), std::move(rejector_),
+ std::move(done_cb_));
+ }
+
+ private:
+ std::unique_ptr<StatelessRejector> rejector_;
+ std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb_;
+};
+
+void StatelessRejector::ProcessClientHello(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result,
+ std::unique_ptr<StatelessRejector> rejector,
+ std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb) {
+ std::unique_ptr<ProcessClientHelloCallback> cb(
+ new ProcessClientHelloCallback(std::move(rejector), std::move(done_cb)));
+ crypto_config_->ProcessClientHello(
+ result,
+ /*reject_only=*/true, connection_id_, server_address_, client_address_,
+ version_, versions_,
+ /*use_stateless_rejects=*/true, server_designated_connection_id_, clock_,
+ random_, compressed_certs_cache_, params_, signed_config_,
+ QuicCryptoStream::CryptoMessageFramingOverhead(version_.transport_version,
+ connection_id_),
+ chlo_packet_size_, std::move(cb));
+}
+
+void StatelessRejector::ProcessClientHelloDone(
+ QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<StatelessRejector> rejector,
+ std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb) {
+ reply_ = std::move(message);
+
+ if (error != QUIC_NO_ERROR) {
+ error_ = error;
+ error_details_ = error_details;
+ state_ = FAILED;
+ } else if (reply_->tag() == kSREJ) {
+ state_ = REJECTED;
+ } else {
+ state_ = ACCEPTED;
+ }
+ done_cb->Run(std::move(rejector));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/stateless_rejector.h b/chromium/net/third_party/quiche/src/quic/core/stateless_rejector.h
new file mode 100644
index 00000000000..18dbdcb45c0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/stateless_rejector.h
@@ -0,0 +1,123 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_STATELESS_REJECTOR_H_
+#define QUICHE_QUIC_CORE_STATELESS_REJECTOR_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+// The StatelessRejector receives CHLO messages and generates an SREJ
+// message in response, if the CHLO can be statelessly rejected.
+class StatelessRejector {
+ public:
+ enum State {
+ UNKNOWN, // State has not yet been determined
+ UNSUPPORTED, // Stateless rejects are not supported
+ FAILED, // There was an error processing the CHLO.
+ ACCEPTED, // The CHLO was accepted
+ REJECTED, // The CHLO was rejected.
+ };
+
+ StatelessRejector(ParsedQuicVersion version,
+ const ParsedQuicVersionVector& versions,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicClock* clock,
+ QuicRandom* random,
+ QuicByteCount chlo_packet_size,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& server_address);
+ StatelessRejector(const StatelessRejector&) = delete;
+ StatelessRejector& operator=(const StatelessRejector&) = delete;
+
+ ~StatelessRejector();
+
+ // Called when |chlo| is received for |connection_id|.
+ void OnChlo(QuicTransportVersion version,
+ QuicConnectionId connection_id,
+ QuicConnectionId server_designated_connection_id,
+ const CryptoHandshakeMessage& chlo);
+
+ class ProcessDoneCallback {
+ public:
+ virtual ~ProcessDoneCallback() = default;
+ virtual void Run(std::unique_ptr<StatelessRejector> rejector) = 0;
+ };
+
+ // Perform processing to determine whether the CHLO received in OnChlo should
+ // be statelessly rejected, and invoke the callback once a decision has been
+ // made.
+ static void Process(std::unique_ptr<StatelessRejector> rejector,
+ std::unique_ptr<ProcessDoneCallback> done_cb);
+
+ // Return the version of the CHLO.
+ ParsedQuicVersion version() const { return version_; }
+
+ // Returns the state of the rejector after OnChlo() has been called.
+ State state() const { return state_; }
+
+ // Returns the error code when state() returns FAILED.
+ QuicErrorCode error() const { return error_; }
+
+ // Returns the error details when state() returns FAILED.
+ std::string error_details() const { return error_details_; }
+
+ // Returns the connection ID.
+ QuicConnectionId connection_id() const { return connection_id_; }
+
+ // Returns the SREJ message when state() returns REJECTED.
+ const CryptoHandshakeMessage& reply() const { return *reply_; }
+
+ private:
+ // Helper class which is passed in to
+ // QuicCryptoServerConfig::ValidateClientHello.
+ class ValidateCallback;
+ friend class ValidateCallback;
+
+ class ProcessClientHelloCallback;
+ friend class ProcessClientHelloCallback;
+
+ void ProcessClientHello(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result,
+ std::unique_ptr<StatelessRejector> rejector,
+ std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb);
+
+ void ProcessClientHelloDone(
+ QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<StatelessRejector> rejector,
+ std::unique_ptr<StatelessRejector::ProcessDoneCallback> done_cb);
+
+ State state_;
+ QuicErrorCode error_;
+ std::string error_details_;
+ ParsedQuicVersion version_;
+ ParsedQuicVersionVector versions_;
+ QuicConnectionId connection_id_;
+ QuicConnectionId server_designated_connection_id_;
+ QuicByteCount chlo_packet_size_;
+ QuicSocketAddress client_address_;
+ QuicSocketAddress server_address_;
+ const QuicClock* clock_;
+ QuicRandom* random_;
+ const QuicCryptoServerConfig* crypto_config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+ CryptoHandshakeMessage chlo_;
+ std::unique_ptr<CryptoHandshakeMessage> reply_;
+ CryptoFramer crypto_framer_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_STATELESS_REJECTOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/stateless_rejector_test.cc b/chromium/net/third_party/quiche/src/quic/core/stateless_rejector_test.cc
new file mode 100644
index 00000000000..f8757e10b11
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/stateless_rejector_test.cc
@@ -0,0 +1,292 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/stateless_rejector.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+QuicConnectionId TestServerDesignatedConnectionId() {
+ return TestConnectionId(24);
+}
+
+// All four combinations of the two flags involved.
+enum FlagsMode { ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED };
+
+const char* FlagsModeToString(FlagsMode mode) {
+ switch (mode) {
+ case ENABLED:
+ return "ENABLED";
+ case STATELESS_DISABLED:
+ return "STATELESS_DISABLED";
+ case CHEAP_DISABLED:
+ return "CHEAP_DISABLED";
+ case BOTH_DISABLED:
+ return "BOTH_DISABLED";
+ default:
+ QUIC_DLOG(FATAL) << "Unexpected FlagsMode";
+ return nullptr;
+ }
+}
+
+// Test various combinations of QUIC version and flag state.
+struct TestParams {
+ ParsedQuicVersion version = UnsupportedQuicVersion();
+ FlagsMode flags;
+};
+
+std::string TestParamToString(
+ const testing::TestParamInfo<TestParams>& params) {
+ return QuicStrCat("v", ParsedQuicVersionToString(params.param.version), "_",
+ FlagsModeToString(params.param.flags));
+}
+
+std::vector<TestParams> GetTestParams() {
+ std::vector<TestParams> params;
+ for (FlagsMode flags :
+ {ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED}) {
+ for (ParsedQuicVersion version : AllSupportedVersions()) {
+ TestParams param;
+ param.version = version;
+ param.flags = flags;
+ params.push_back(param);
+ }
+ }
+ return params;
+}
+
+class StatelessRejectorTest : public QuicTestWithParam<TestParams> {
+ public:
+ StatelessRejectorTest()
+ : proof_source_(crypto_test_utils::ProofSourceForTesting()),
+ config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ config_peer_(&config_),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ rejector_(QuicMakeUnique<StatelessRejector>(
+ GetParam().version,
+ AllSupportedVersions(),
+ &config_,
+ &compressed_certs_cache_,
+ &clock_,
+ QuicRandom::GetInstance(),
+ kDefaultMaxPacketSize,
+ QuicSocketAddress(QuicIpAddress::Loopback4(), 12345),
+ QuicSocketAddress(QuicIpAddress::Loopback4(), 443))) {
+ SetQuicReloadableFlag(
+ enable_quic_stateless_reject_support,
+ GetParam().flags == ENABLED || GetParam().flags == CHEAP_DISABLED);
+ SetQuicReloadableFlag(
+ quic_use_cheap_stateless_rejects,
+ GetParam().flags == ENABLED || GetParam().flags == STATELESS_DISABLED);
+
+ // Add a new primary config.
+ std::unique_ptr<CryptoHandshakeMessage> msg(config_.AddDefaultConfig(
+ QuicRandom::GetInstance(), &clock_, config_options_));
+
+ // Save the server config.
+ scid_hex_ =
+ "#" + QuicTextUtils::HexEncode(config_peer_.GetPrimaryConfig()->id);
+
+ // Encode the QUIC version.
+ ver_hex_ = ParsedQuicVersionToString(GetParam().version);
+
+ // Generate a public value.
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+ pubs_hex_ =
+ "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
+
+ // Generate a client nonce.
+ std::string nonce;
+ CryptoUtils::GenerateNonce(
+ clock_.WallNow(), QuicRandom::GetInstance(),
+ QuicStringPiece(
+ reinterpret_cast<char*>(config_peer_.GetPrimaryConfig()->orbit),
+ kOrbitSize),
+ &nonce);
+ nonc_hex_ = "#" + QuicTextUtils::HexEncode(nonce);
+
+ // Generate a source address token.
+ SourceAddressTokens previous_tokens;
+ QuicIpAddress ip = QuicIpAddress::Loopback4();
+ MockRandom rand;
+ std::string stk = config_peer_.NewSourceAddressToken(
+ config_peer_.GetPrimaryConfig()->id, previous_tokens, ip, &rand,
+ clock_.WallNow(), nullptr);
+ stk_hex_ = "#" + QuicTextUtils::HexEncode(stk);
+ }
+
+ protected:
+ class ProcessDoneCallback : public StatelessRejector::ProcessDoneCallback {
+ public:
+ explicit ProcessDoneCallback(StatelessRejectorTest* test) : test_(test) {}
+ void Run(std::unique_ptr<StatelessRejector> rejector) override {
+ test_->rejector_ = std::move(rejector);
+ }
+
+ private:
+ StatelessRejectorTest* test_;
+ };
+
+ std::unique_ptr<ProofSource> proof_source_;
+ MockClock clock_;
+ QuicCryptoServerConfig config_;
+ QuicCryptoServerConfigPeer config_peer_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicCryptoServerConfig::ConfigOptions config_options_;
+ std::unique_ptr<StatelessRejector> rejector_;
+
+ // Values used in CHLO messages
+ std::string scid_hex_;
+ std::string nonc_hex_;
+ std::string pubs_hex_;
+ std::string ver_hex_;
+ std::string stk_hex_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Flags,
+ StatelessRejectorTest,
+ ::testing::ValuesIn(GetTestParams()),
+ TestParamToString);
+
+TEST_P(StatelessRejectorTest, InvalidChlo) {
+ // clang-format off
+ const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"COPT", "SREJ"}});
+ // clang-format on
+ rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
+ TestServerDesignatedConnectionId(), client_hello);
+
+ if (GetParam().flags != ENABLED) {
+ EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
+ return;
+ }
+
+ // The StatelessRejector is undecided - proceed with async processing
+ ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
+ StatelessRejector::Process(std::move(rejector_),
+ QuicMakeUnique<ProcessDoneCallback>(this));
+
+ EXPECT_EQ(StatelessRejector::FAILED, rejector_->state());
+ EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, rejector_->error());
+}
+
+TEST_P(StatelessRejectorTest, ValidChloWithoutSrejSupport) {
+ // clang-format off
+ const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", pubs_hex_},
+ {"NONC", nonc_hex_},
+ {"VER\0", ver_hex_}},
+ kClientHelloMinimumSize);
+ // clang-format on
+
+ rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
+ TestServerDesignatedConnectionId(), client_hello);
+ EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
+}
+
+TEST_P(StatelessRejectorTest, RejectChlo) {
+ // clang-format off
+ const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"COPT", "SREJ"},
+ {"SCID", scid_hex_},
+ {"PUBS", pubs_hex_},
+ {"NONC", nonc_hex_},
+ {"#004b5453", stk_hex_},
+ {"VER\0", ver_hex_}},
+ kClientHelloMinimumSize);
+ // clang-format on
+
+ rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
+ TestServerDesignatedConnectionId(), client_hello);
+ if (GetParam().flags != ENABLED) {
+ EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
+ return;
+ }
+
+ // The StatelessRejector is undecided - proceed with async processing
+ ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
+ StatelessRejector::Process(std::move(rejector_),
+ QuicMakeUnique<ProcessDoneCallback>(this));
+
+ ASSERT_EQ(StatelessRejector::REJECTED, rejector_->state());
+ const CryptoHandshakeMessage& reply = rejector_->reply();
+ EXPECT_EQ(kSREJ, reply.tag());
+ QuicTagVector reject_reasons;
+ EXPECT_EQ(QUIC_NO_ERROR, reply.GetTaglist(kRREJ, &reject_reasons));
+ EXPECT_EQ(1u, reject_reasons.size());
+ EXPECT_EQ(INVALID_EXPECTED_LEAF_CERTIFICATE,
+ static_cast<HandshakeFailureReason>(reject_reasons[0]));
+}
+
+TEST_P(StatelessRejectorTest, AcceptChlo) {
+ const uint64_t xlct = crypto_test_utils::LeafCertHashForTesting();
+ const std::string xlct_hex =
+ "#" + QuicTextUtils::HexEncode(reinterpret_cast<const char*>(&xlct),
+ sizeof(xlct));
+ // clang-format off
+ const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"COPT", "SREJ"},
+ {"SCID", scid_hex_},
+ {"PUBS", pubs_hex_},
+ {"NONC", nonc_hex_},
+ {"#004b5453", stk_hex_},
+ {"VER\0", ver_hex_},
+ {"XLCT", xlct_hex}},
+ kClientHelloMinimumSize);
+ // clang-format on
+
+ rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
+ TestServerDesignatedConnectionId(), client_hello);
+ if (GetParam().flags != ENABLED) {
+ EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
+ return;
+ }
+
+ // The StatelessRejector is undecided - proceed with async processing
+ ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
+ StatelessRejector::Process(std::move(rejector_),
+ QuicMakeUnique<ProcessDoneCallback>(this));
+
+ EXPECT_EQ(StatelessRejector::ACCEPTED, rejector_->state());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..932d537b16d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.cc
@@ -0,0 +1,357 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+
+#include <cstring>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+TlsClientHandshaker::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
+ TlsClientHandshaker* parent)
+ : parent_(parent) {}
+
+TlsClientHandshaker::ProofVerifierCallbackImpl::~ProofVerifierCallbackImpl() {}
+
+void TlsClientHandshaker::ProofVerifierCallbackImpl::Run(
+ bool ok,
+ const std::string& error_details,
+ std::unique_ptr<ProofVerifyDetails>* details) {
+ if (parent_ == nullptr) {
+ return;
+ }
+
+ parent_->verify_details_ = std::move(*details);
+ parent_->verify_result_ = ok ? ssl_verify_ok : ssl_verify_invalid;
+ parent_->state_ = STATE_HANDSHAKE_RUNNING;
+ parent_->proof_verify_callback_ = nullptr;
+ parent_->AdvanceHandshake();
+}
+
+void TlsClientHandshaker::ProofVerifierCallbackImpl::Cancel() {
+ parent_ = nullptr;
+}
+
+TlsClientHandshaker::TlsClientHandshaker(
+ QuicCryptoStream* stream,
+ QuicSession* session,
+ const QuicServerId& server_id,
+ ProofVerifier* proof_verifier,
+ SSL_CTX* ssl_ctx,
+ std::unique_ptr<ProofVerifyContext> verify_context,
+ const std::string& user_agent_id)
+ : TlsHandshaker(stream, session, ssl_ctx),
+ server_id_(server_id),
+ proof_verifier_(proof_verifier),
+ verify_context_(std::move(verify_context)),
+ user_agent_id_(user_agent_id),
+ crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {}
+
+TlsClientHandshaker::~TlsClientHandshaker() {
+ if (proof_verify_callback_) {
+ proof_verify_callback_->Cancel();
+ }
+}
+
+// static
+bssl::UniquePtr<SSL_CTX> TlsClientHandshaker::CreateSslCtx() {
+ return TlsHandshaker::CreateSslCtx();
+}
+
+bool TlsClientHandshaker::CryptoConnect() {
+ CrypterPair crypters;
+ CryptoUtils::CreateTlsInitialCrypters(
+ Perspective::IS_CLIENT, session()->connection()->transport_version(),
+ session()->connection_id(), &crypters);
+ session()->connection()->SetEncrypter(ENCRYPTION_INITIAL,
+ std::move(crypters.encrypter));
+ session()->connection()->InstallDecrypter(ENCRYPTION_INITIAL,
+ std::move(crypters.decrypter));
+ state_ = STATE_HANDSHAKE_RUNNING;
+ // Configure certificate verification.
+ // TODO(nharper): This only verifies certs on initial connection, not on
+ // resumption. Chromium has this callback be a no-op and verifies the
+ // certificate after the connection is complete. We need to re-verify on
+ // resumption in case of expiration or revocation/distrust.
+ SSL_set_custom_verify(ssl(), SSL_VERIFY_PEER, &VerifyCallback);
+
+ // Configure the SSL to be a client.
+ SSL_set_connect_state(ssl());
+ if (SSL_set_tlsext_host_name(ssl(), server_id_.host().c_str()) != 1) {
+ return false;
+ }
+
+ std::string alpn_string =
+ AlpnForVersion(session()->supported_versions().front());
+ if (alpn_string.length() > std::numeric_limits<uint8_t>::max()) {
+ QUIC_BUG << "ALPN too long: '" << alpn_string << "'";
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "ALPN too long");
+ return false;
+ }
+ const uint8_t alpn_length = alpn_string.length();
+ // SSL_set_alpn_protos expects a sequence of one-byte-length-prefixed strings
+ // so we copy alpn_string to a new buffer that has the length in alpn[0].
+ uint8_t alpn[std::numeric_limits<uint8_t>::max() + 1];
+ alpn[0] = alpn_length;
+ memcpy(reinterpret_cast<char*>(alpn + 1), alpn_string.data(), alpn_length);
+ if (SSL_set_alpn_protos(ssl(), alpn,
+ static_cast<unsigned>(alpn_length) + 1) != 0) {
+ QUIC_BUG << "Failed to set ALPN: '" << alpn_string << "'";
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "Failed to set ALPN");
+ return false;
+ }
+ QUIC_DLOG(INFO) << "Client using ALPN: '" << alpn_string << "'";
+
+ // Set the Transport Parameters to send in the ClientHello
+ if (!SetTransportParameters()) {
+ CloseConnection(QUIC_HANDSHAKE_FAILED,
+ "Failed to set Transport Parameters");
+ return false;
+ }
+
+ // Start the handshake.
+ AdvanceHandshake();
+ return session()->connection()->connected();
+}
+
+bool TlsClientHandshaker::SetTransportParameters() {
+ TransportParameters params;
+ params.perspective = Perspective::IS_CLIENT;
+ params.version =
+ CreateQuicVersionLabel(session()->supported_versions().front());
+
+ if (!session()->config()->FillTransportParameters(&params)) {
+ return false;
+ }
+ params.google_quic_params->SetStringPiece(kUAID, user_agent_id_);
+
+ std::vector<uint8_t> param_bytes;
+ return SerializeTransportParameters(params, &param_bytes) &&
+ SSL_set_quic_transport_params(ssl(), param_bytes.data(),
+ param_bytes.size()) == 1;
+}
+
+bool TlsClientHandshaker::ProcessTransportParameters(
+ std::string* error_details) {
+ TransportParameters params;
+ const uint8_t* param_bytes;
+ size_t param_bytes_len;
+ SSL_get_peer_quic_transport_params(ssl(), &param_bytes, &param_bytes_len);
+ if (param_bytes_len == 0 ||
+ !ParseTransportParameters(param_bytes, param_bytes_len,
+ Perspective::IS_SERVER, &params)) {
+ *error_details = "Unable to parse Transport Parameters";
+ return false;
+ }
+
+ if (params.version !=
+ CreateQuicVersionLabel(session()->connection()->version())) {
+ *error_details = "Version mismatch detected";
+ return false;
+ }
+ if (CryptoUtils::ValidateServerHelloVersions(
+ params.supported_versions,
+ session()->connection()->server_supported_versions(),
+ error_details) != QUIC_NO_ERROR ||
+ session()->config()->ProcessTransportParameters(
+ params, SERVER, error_details) != QUIC_NO_ERROR) {
+ DCHECK(!error_details->empty());
+ return false;
+ }
+
+ session()->OnConfigNegotiated();
+ return true;
+}
+
+int TlsClientHandshaker::num_sent_client_hellos() const {
+ // TODO(nharper): Return a sensible value here.
+ return 0;
+}
+
+int TlsClientHandshaker::num_scup_messages_received() const {
+ // SCUP messages aren't sent or received when using the TLS handshake.
+ return 0;
+}
+
+bool TlsClientHandshaker::WasChannelIDSent() const {
+ // Channel ID is not used with TLS in QUIC.
+ return false;
+}
+
+bool TlsClientHandshaker::WasChannelIDSourceCallbackRun() const {
+ // Channel ID is not used with TLS in QUIC.
+ return false;
+}
+
+std::string TlsClientHandshaker::chlo_hash() const {
+ return "";
+}
+
+bool TlsClientHandshaker::encryption_established() const {
+ return encryption_established_;
+}
+
+bool TlsClientHandshaker::handshake_confirmed() const {
+ return handshake_confirmed_;
+}
+
+const QuicCryptoNegotiatedParameters&
+TlsClientHandshaker::crypto_negotiated_params() const {
+ return *crypto_negotiated_params_;
+}
+
+CryptoMessageParser* TlsClientHandshaker::crypto_message_parser() {
+ return TlsHandshaker::crypto_message_parser();
+}
+
+void TlsClientHandshaker::AdvanceHandshake() {
+ if (state_ == STATE_CONNECTION_CLOSED) {
+ QUIC_LOG(INFO)
+ << "TlsClientHandshaker received message after connection closed";
+ return;
+ }
+ if (state_ == STATE_IDLE) {
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed");
+ return;
+ }
+ if (state_ == STATE_HANDSHAKE_COMPLETE) {
+ // TODO(nharper): Handle post-handshake messages.
+ return;
+ }
+
+ QUIC_LOG(INFO) << "TlsClientHandshaker: continuing handshake";
+ int rv = SSL_do_handshake(ssl());
+ if (rv == 1) {
+ FinishHandshake();
+ return;
+ }
+ int ssl_error = SSL_get_error(ssl(), rv);
+ bool should_close = true;
+ switch (state_) {
+ case STATE_HANDSHAKE_RUNNING:
+ should_close = ssl_error != SSL_ERROR_WANT_READ;
+ break;
+ case STATE_CERT_VERIFY_PENDING:
+ should_close = ssl_error != SSL_ERROR_WANT_CERTIFICATE_VERIFY;
+ break;
+ default:
+ should_close = true;
+ }
+ if (should_close && state_ != STATE_CONNECTION_CLOSED) {
+ // TODO(nharper): Surface error details from the error queue when ssl_error
+ // is SSL_ERROR_SSL.
+ QUIC_LOG(WARNING) << "SSL_do_handshake failed; closing connection";
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed");
+ }
+}
+
+void TlsClientHandshaker::CloseConnection(QuicErrorCode error,
+ const std::string& reason_phrase) {
+ DCHECK(!reason_phrase.empty());
+ state_ = STATE_CONNECTION_CLOSED;
+ stream()->CloseConnectionWithDetails(error, reason_phrase);
+}
+
+void TlsClientHandshaker::FinishHandshake() {
+ QUIC_LOG(INFO) << "Client: handshake finished";
+ state_ = STATE_HANDSHAKE_COMPLETE;
+
+ std::string error_details;
+ if (!ProcessTransportParameters(&error_details)) {
+ DCHECK(!error_details.empty());
+ CloseConnection(QUIC_HANDSHAKE_FAILED, error_details);
+ return;
+ }
+
+ const uint8_t* alpn_data = nullptr;
+ unsigned alpn_length = 0;
+ SSL_get0_alpn_selected(ssl(), &alpn_data, &alpn_length);
+ // TODO(b/130164908) Act on ALPN.
+ if (alpn_length != 0) {
+ std::string received_alpn_string(reinterpret_cast<const char*>(alpn_data),
+ alpn_length);
+ std::string sent_alpn_string =
+ AlpnForVersion(session()->supported_versions().front());
+ if (received_alpn_string != sent_alpn_string) {
+ QUIC_LOG(ERROR) << "Client: received mismatched ALPN '"
+ << received_alpn_string << "', expected '"
+ << sent_alpn_string << "'";
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "Mismatched ALPN");
+ return;
+ }
+ QUIC_DLOG(INFO) << "Client: server selected ALPN: '" << received_alpn_string
+ << "'";
+ } else {
+ QUIC_DLOG(INFO) << "Client: server did not select ALPN";
+ }
+
+ session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ session()->NeuterUnencryptedData();
+ encryption_established_ = true;
+ handshake_confirmed_ = true;
+}
+
+// static
+TlsClientHandshaker* TlsClientHandshaker::HandshakerFromSsl(SSL* ssl) {
+ return static_cast<TlsClientHandshaker*>(
+ TlsHandshaker::HandshakerFromSsl(ssl));
+}
+
+// static
+enum ssl_verify_result_t TlsClientHandshaker::VerifyCallback(
+ SSL* ssl,
+ uint8_t* out_alert) {
+ return HandshakerFromSsl(ssl)->VerifyCert(out_alert);
+}
+
+enum ssl_verify_result_t TlsClientHandshaker::VerifyCert(uint8_t* out_alert) {
+ if (verify_result_ != ssl_verify_retry ||
+ state_ == STATE_CERT_VERIFY_PENDING) {
+ enum ssl_verify_result_t result = verify_result_;
+ verify_result_ = ssl_verify_retry;
+ return result;
+ }
+ const STACK_OF(CRYPTO_BUFFER)* cert_chain = SSL_get0_peer_certificates(ssl());
+ if (cert_chain == nullptr) {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ return ssl_verify_invalid;
+ }
+ // TODO(nharper): Pass the CRYPTO_BUFFERs into the QUIC stack to avoid copies.
+ std::vector<std::string> certs;
+ for (CRYPTO_BUFFER* cert : cert_chain) {
+ certs.push_back(
+ std::string(reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert)),
+ CRYPTO_BUFFER_len(cert)));
+ }
+
+ ProofVerifierCallbackImpl* proof_verify_callback =
+ new ProofVerifierCallbackImpl(this);
+
+ QuicAsyncStatus verify_result = proof_verifier_->VerifyCertChain(
+ server_id_.host(), certs, verify_context_.get(),
+ &cert_verify_error_details_, &verify_details_,
+ std::unique_ptr<ProofVerifierCallback>(proof_verify_callback));
+ switch (verify_result) {
+ case QUIC_SUCCESS:
+ return ssl_verify_ok;
+ case QUIC_PENDING:
+ proof_verify_callback_ = proof_verify_callback;
+ state_ = STATE_CERT_VERIFY_PENDING;
+ return ssl_verify_retry;
+ case QUIC_FAILURE:
+ default:
+ QUIC_LOG(INFO) << "Cert chain verification failed: "
+ << cert_verify_error_details_;
+ return ssl_verify_invalid;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h
new file mode 100644
index 00000000000..c2243b3efb5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_client_handshaker.h
@@ -0,0 +1,128 @@
+// 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_CORE_TLS_CLIENT_HANDSHAKER_H_
+#define QUICHE_QUIC_CORE_TLS_CLIENT_HANDSHAKER_H_
+
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/tls_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An implementation of QuicCryptoClientStream::HandshakerDelegate which uses
+// TLS 1.3 for the crypto handshake protocol.
+class QUIC_EXPORT_PRIVATE TlsClientHandshaker
+ : public QuicCryptoClientStream::HandshakerDelegate,
+ public TlsHandshaker {
+ public:
+ TlsClientHandshaker(QuicCryptoStream* stream,
+ QuicSession* session,
+ const QuicServerId& server_id,
+ ProofVerifier* proof_verifier,
+ SSL_CTX* ssl_ctx,
+ std::unique_ptr<ProofVerifyContext> verify_context,
+ const std::string& user_agent_id);
+ TlsClientHandshaker(const TlsClientHandshaker&) = delete;
+ TlsClientHandshaker& operator=(const TlsClientHandshaker&) = delete;
+
+ ~TlsClientHandshaker() override;
+
+ // Creates and configures an SSL_CTX to be used with a TlsClientHandshaker.
+ // The caller is responsible for ownership of the newly created struct.
+ static bssl::UniquePtr<SSL_CTX> CreateSslCtx();
+
+ // From QuicCryptoClientStream::HandshakerDelegate
+ bool CryptoConnect() override;
+ int num_sent_client_hellos() const override;
+ int num_scup_messages_received() const override;
+ bool WasChannelIDSent() const override;
+ bool WasChannelIDSourceCallbackRun() const override;
+ std::string chlo_hash() const override;
+
+ // From QuicCryptoClientStream::HandshakerDelegate and TlsHandshaker
+ bool encryption_established() const override;
+ bool handshake_confirmed() const override;
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override;
+ CryptoMessageParser* crypto_message_parser() override;
+
+ private:
+ // ProofVerifierCallbackImpl handles the result of an asynchronous certificate
+ // verification operation.
+ class ProofVerifierCallbackImpl : public ProofVerifierCallback {
+ public:
+ explicit ProofVerifierCallbackImpl(TlsClientHandshaker* parent);
+ ~ProofVerifierCallbackImpl() override;
+
+ // ProofVerifierCallback interface.
+ void Run(bool ok,
+ const std::string& error_details,
+ std::unique_ptr<ProofVerifyDetails>* details) override;
+
+ // If called, Cancel causes the pending callback to be a no-op.
+ void Cancel();
+
+ private:
+ TlsClientHandshaker* parent_;
+ };
+
+ enum State {
+ STATE_IDLE,
+ STATE_HANDSHAKE_RUNNING,
+ STATE_CERT_VERIFY_PENDING,
+ STATE_HANDSHAKE_COMPLETE,
+ STATE_CONNECTION_CLOSED,
+ } state_ = STATE_IDLE;
+
+ bool SetTransportParameters();
+ bool ProcessTransportParameters(std::string* error_details);
+ void FinishHandshake();
+
+ void AdvanceHandshake() override;
+ void CloseConnection(QuicErrorCode error,
+ const std::string& reason_phrase) override;
+
+ // Certificate verification functions:
+
+ enum ssl_verify_result_t VerifyCert(uint8_t* out_alert);
+ // Static method to supply to SSL_set_custom_verify.
+ static enum ssl_verify_result_t VerifyCallback(SSL* ssl, uint8_t* out_alert);
+
+ // Takes an SSL* |ssl| and returns a pointer to the TlsClientHandshaker that
+ // it belongs to. This is a specialization of
+ // TlsHandshaker::HandshakerFromSsl.
+ static TlsClientHandshaker* HandshakerFromSsl(SSL* ssl);
+
+ QuicServerId server_id_;
+
+ // Objects used for verifying the server's certificate chain.
+ // |proof_verifier_| is owned by the caller of TlsClientHandshaker's
+ // constructor.
+ ProofVerifier* proof_verifier_;
+ std::unique_ptr<ProofVerifyContext> verify_context_;
+
+ std::string user_agent_id_;
+
+ // ProofVerifierCallback used for async certificate verification. This object
+ // is owned by |proof_verifier_|.
+ ProofVerifierCallbackImpl* proof_verify_callback_ = nullptr;
+ std::unique_ptr<ProofVerifyDetails> verify_details_;
+ enum ssl_verify_result_t verify_result_ = ssl_verify_retry;
+ std::string cert_verify_error_details_;
+
+ bool encryption_established_ = false;
+ bool handshake_confirmed_ = false;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ crypto_negotiated_params_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_TLS_CLIENT_HANDSHAKER_H_
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
new file mode 100644
index 00000000000..3a45f36379f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.cc
@@ -0,0 +1,230 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/tls_handshaker.h"
+
+#include "third_party/boringssl/src/include/openssl/crypto.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+namespace {
+
+class SslIndexSingleton {
+ public:
+ static SslIndexSingleton* GetInstance() {
+ static SslIndexSingleton* instance = new SslIndexSingleton();
+ return instance;
+ }
+
+ int HandshakerIndex() const { return ssl_ex_data_index_handshaker_; }
+
+ private:
+ SslIndexSingleton() {
+ CRYPTO_library_init();
+ ssl_ex_data_index_handshaker_ =
+ SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
+ CHECK_LE(0, ssl_ex_data_index_handshaker_);
+ }
+
+ SslIndexSingleton(const SslIndexSingleton&) = delete;
+ SslIndexSingleton& operator=(const SslIndexSingleton&) = delete;
+
+ int ssl_ex_data_index_handshaker_;
+};
+
+} // namespace
+
+TlsHandshaker::TlsHandshaker(QuicCryptoStream* stream,
+ QuicSession* session,
+ SSL_CTX* ssl_ctx)
+ : stream_(stream), session_(session) {
+ ssl_.reset(SSL_new(ssl_ctx));
+ SSL_set_ex_data(ssl(), SslIndexSingleton::GetInstance()->HandshakerIndex(),
+ this);
+}
+
+TlsHandshaker::~TlsHandshaker() {}
+
+bool TlsHandshaker::ProcessInput(QuicStringPiece input, EncryptionLevel level) {
+ if (parser_error_ != QUIC_NO_ERROR) {
+ return false;
+ }
+ // TODO(nharper): Call SSL_quic_read_level(ssl()) and check whether the
+ // encryption level BoringSSL expects matches the encryption level that we
+ // just received input at. If they mismatch, should ProcessInput return true
+ // or false? If data is for a future encryption level, it should be queued for
+ // later?
+ if (SSL_provide_quic_data(ssl(), BoringEncryptionLevel(level),
+ reinterpret_cast<const uint8_t*>(input.data()),
+ input.size()) != 1) {
+ // SSL_provide_quic_data can fail for 3 reasons:
+ // - API misuse (calling it before SSL_set_custom_quic_method, which we
+ // call in the TlsHandshaker c'tor)
+ // - Memory exhaustion when appending data to its buffer
+ // - Data provided at the wrong encryption level
+ //
+ // Of these, the only sensible error to handle is data provided at the wrong
+ // encryption level.
+ //
+ // Note: the error provided below has a good-sounding enum value, although
+ // it doesn't match the description as it's a QUIC Crypto specific error.
+ parser_error_ = QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ parser_error_detail_ = "TLS stack failed to receive data";
+ return false;
+ }
+ AdvanceHandshake();
+ return true;
+}
+
+// static
+bssl::UniquePtr<SSL_CTX> TlsHandshaker::CreateSslCtx() {
+ CRYPTO_library_init();
+ bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_with_buffers_method()));
+ SSL_CTX_set_min_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
+ SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
+ SSL_CTX_set_quic_method(ssl_ctx.get(), &kSslQuicMethod);
+ return ssl_ctx;
+}
+
+// static
+TlsHandshaker* TlsHandshaker::HandshakerFromSsl(const SSL* ssl) {
+ return reinterpret_cast<TlsHandshaker*>(SSL_get_ex_data(
+ ssl, SslIndexSingleton::GetInstance()->HandshakerIndex()));
+}
+
+// static
+EncryptionLevel TlsHandshaker::QuicEncryptionLevel(
+ enum ssl_encryption_level_t level) {
+ switch (level) {
+ case ssl_encryption_initial:
+ return ENCRYPTION_INITIAL;
+ case ssl_encryption_early_data:
+ case ssl_encryption_handshake:
+ return ENCRYPTION_ZERO_RTT;
+ case ssl_encryption_application:
+ return ENCRYPTION_FORWARD_SECURE;
+ }
+}
+
+// static
+enum ssl_encryption_level_t TlsHandshaker::BoringEncryptionLevel(
+ EncryptionLevel level) {
+ switch (level) {
+ case ENCRYPTION_INITIAL:
+ return ssl_encryption_initial;
+ case ENCRYPTION_HANDSHAKE:
+ case ENCRYPTION_ZERO_RTT:
+ return ssl_encryption_handshake;
+ case ENCRYPTION_FORWARD_SECURE:
+ return ssl_encryption_application;
+ default:
+ QUIC_BUG << "Invalid encryption level " << level;
+ return ssl_encryption_initial;
+ }
+}
+
+const EVP_MD* TlsHandshaker::Prf() {
+ return EVP_get_digestbynid(
+ SSL_CIPHER_get_prf_nid(SSL_get_pending_cipher(ssl())));
+}
+
+std::unique_ptr<QuicEncrypter> TlsHandshaker::CreateEncrypter(
+ const std::vector<uint8_t>& pp_secret) {
+ std::unique_ptr<QuicEncrypter> encrypter =
+ QuicEncrypter::CreateFromCipherSuite(
+ SSL_CIPHER_get_id(SSL_get_pending_cipher(ssl())));
+ CryptoUtils::SetKeyAndIV(Prf(), pp_secret, encrypter.get());
+ return encrypter;
+}
+
+std::unique_ptr<QuicDecrypter> TlsHandshaker::CreateDecrypter(
+ const std::vector<uint8_t>& pp_secret) {
+ std::unique_ptr<QuicDecrypter> decrypter =
+ QuicDecrypter::CreateFromCipherSuite(
+ SSL_CIPHER_get_id(SSL_get_pending_cipher(ssl())));
+ CryptoUtils::SetKeyAndIV(Prf(), pp_secret, decrypter.get());
+ return decrypter;
+}
+
+const SSL_QUIC_METHOD TlsHandshaker::kSslQuicMethod{
+ TlsHandshaker::SetEncryptionSecretCallback,
+ TlsHandshaker::WriteMessageCallback, TlsHandshaker::FlushFlightCallback,
+ TlsHandshaker::SendAlertCallback};
+
+// static
+int TlsHandshaker::SetEncryptionSecretCallback(
+ SSL* ssl,
+ enum ssl_encryption_level_t level,
+ const uint8_t* read_key,
+ const uint8_t* write_key,
+ size_t secret_len) {
+ // TODO(nharper): replace these vectors and memcpys with spans (which
+ // unfortunately doesn't yet exist in quic/platform/api).
+ std::vector<uint8_t> read_secret(secret_len), write_secret(secret_len);
+ memcpy(read_secret.data(), read_key, secret_len);
+ memcpy(write_secret.data(), write_key, secret_len);
+ HandshakerFromSsl(ssl)->SetEncryptionSecret(QuicEncryptionLevel(level),
+ read_secret, write_secret);
+ return 1;
+}
+
+// static
+int TlsHandshaker::WriteMessageCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ const uint8_t* data,
+ size_t len) {
+ HandshakerFromSsl(ssl)->WriteMessage(
+ QuicEncryptionLevel(level),
+ QuicStringPiece(reinterpret_cast<const char*>(data), len));
+ return 1;
+}
+
+// static
+int TlsHandshaker::FlushFlightCallback(SSL* ssl) {
+ HandshakerFromSsl(ssl)->FlushFlight();
+ return 1;
+}
+
+// static
+int TlsHandshaker::SendAlertCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ uint8_t desc) {
+ HandshakerFromSsl(ssl)->SendAlert(QuicEncryptionLevel(level), desc);
+ return 1;
+}
+
+void TlsHandshaker::SetEncryptionSecret(
+ EncryptionLevel level,
+ const std::vector<uint8_t>& read_secret,
+ const std::vector<uint8_t>& write_secret) {
+ std::unique_ptr<QuicEncrypter> encrypter = CreateEncrypter(write_secret);
+ session()->connection()->SetEncrypter(level, std::move(encrypter));
+ std::unique_ptr<QuicDecrypter> decrypter = CreateDecrypter(read_secret);
+ session()->connection()->InstallDecrypter(level, std::move(decrypter));
+}
+
+void TlsHandshaker::WriteMessage(EncryptionLevel level, QuicStringPiece data) {
+ stream_->WriteCryptoData(level, data);
+}
+
+void TlsHandshaker::FlushFlight() {}
+
+void TlsHandshaker::SendAlert(EncryptionLevel level, uint8_t desc) {
+ // TODO(nharper): Alerts should be sent on the wire as a 16-bit QUIC error
+ // code computed to be 0x100 | desc (draft-ietf-quic-tls-14, section 4.8).
+ // This puts it in the range reserved for CRYPTO_ERROR
+ // (draft-ietf-quic-transport-14, section 11.3). However, according to
+ // quic_error_codes.h, this QUIC implementation only sends 1-byte error codes
+ // right now.
+ QUIC_DLOG(INFO) << "TLS failing handshake due to alert "
+ << static_cast<int>(desc);
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failure");
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..96589237c92
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker.h
@@ -0,0 +1,145 @@
+// 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_CORE_TLS_HANDSHAKER_H_
+#define QUICHE_QUIC_CORE_TLS_HANDSHAKER_H_
+
+#include "third_party/boringssl/src/include/openssl/base.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_message_parser.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+class QuicCryptoStream;
+
+// Base class for TlsClientHandshaker and TlsServerHandshaker. TlsHandshaker
+// provides functionality common to both the client and server, such as moving
+// messages between the TLS stack and the QUIC crypto stream, and handling
+// derivation of secrets.
+class QUIC_EXPORT_PRIVATE TlsHandshaker : public CryptoMessageParser {
+ public:
+ // TlsHandshaker does not take ownership of any of its arguments; they must
+ // outlive the TlsHandshaker.
+ TlsHandshaker(QuicCryptoStream* stream,
+ QuicSession* session,
+ SSL_CTX* ssl_ctx);
+ TlsHandshaker(const TlsHandshaker&) = delete;
+ TlsHandshaker& operator=(const TlsHandshaker&) = delete;
+
+ ~TlsHandshaker() override;
+
+ // From CryptoMessageParser
+ bool ProcessInput(QuicStringPiece input, EncryptionLevel level) override;
+ size_t InputBytesRemaining() const override { return 0; }
+ QuicErrorCode error() const override { return parser_error_; }
+ const std::string& error_detail() const override {
+ return parser_error_detail_;
+ }
+
+ // From QuicCryptoStream
+ virtual bool encryption_established() const = 0;
+ virtual bool handshake_confirmed() const = 0;
+ virtual const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const = 0;
+ virtual CryptoMessageParser* crypto_message_parser() { return this; }
+
+ protected:
+ virtual void AdvanceHandshake() = 0;
+
+ virtual void CloseConnection(QuicErrorCode error,
+ const std::string& reason_phrase) = 0;
+
+ // 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
+ // newly created struct.
+ static bssl::UniquePtr<SSL_CTX> CreateSslCtx();
+
+ // From a given SSL* |ssl|, returns a pointer to the TlsHandshaker that it
+ // belongs to. This is a helper method for implementing callbacks set on an
+ // SSL, as it allows the callback function to find the TlsHandshaker instance
+ // and call an instance method.
+ static TlsHandshaker* HandshakerFromSsl(const SSL* ssl);
+
+ static EncryptionLevel QuicEncryptionLevel(enum ssl_encryption_level_t level);
+ static enum ssl_encryption_level_t BoringEncryptionLevel(
+ EncryptionLevel level);
+
+ // Returns the PRF used by the cipher suite negotiated in the TLS handshake.
+ const EVP_MD* Prf();
+
+ std::unique_ptr<QuicEncrypter> CreateEncrypter(
+ const std::vector<uint8_t>& pp_secret);
+ std::unique_ptr<QuicDecrypter> CreateDecrypter(
+ const std::vector<uint8_t>& pp_secret);
+
+ SSL* ssl() { return ssl_.get(); }
+ QuicCryptoStream* stream() { return stream_; }
+ QuicSession* session() { return session_; }
+
+ private:
+ // TlsHandshaker implements SSL_QUIC_METHOD, which provides the interface
+ // between BoringSSL's TLS stack and a QUIC implementation.
+ static const SSL_QUIC_METHOD kSslQuicMethod;
+
+ // Pointers to the following 4 functions form |kSslQuicMethod|. Each one
+ // is a wrapper around the corresponding instance method below. The |ssl|
+ // argument is used to look up correct TlsHandshaker instance on which to call
+ // the method. According to the BoringSSL documentation, these functions
+ // return 0 on error and 1 otherwise; here they never error and thus always
+ // return 1.
+ static int SetEncryptionSecretCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ const uint8_t* read_key,
+ const uint8_t* write_key,
+ size_t secret_len);
+ static int WriteMessageCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ const uint8_t* data,
+ size_t len);
+ static int FlushFlightCallback(SSL* ssl);
+ static int SendAlertCallback(SSL* ssl,
+ enum ssl_encryption_level_t level,
+ uint8_t alert);
+
+ // SetEncryptionSecret provides the encryption secret to use at a particular
+ // encryption level. The secrets provided here are the ones from the TLS 1.3
+ // key schedule (RFC 8446 section 7.1), in particular the handshake traffic
+ // secrets and application traffic secrets. For a given secret |secret|,
+ // |level| indicates which EncryptionLevel it is to be used at, and |is_write|
+ // indicates whether it is used for encryption or decryption.
+ void SetEncryptionSecret(EncryptionLevel level,
+ const std::vector<uint8_t>& read_secret,
+ const std::vector<uint8_t>& write_secret);
+
+ // WriteMessage is called when there is |data| from the TLS stack ready for
+ // the QUIC stack to write in a crypto frame. The data must be transmitted at
+ // encryption level |level|.
+ void WriteMessage(EncryptionLevel level, QuicStringPiece data);
+
+ // FlushFlight is called to signal that the current flight of
+ // messages have all been written (via calls to WriteMessage) and can be
+ // flushed to the underlying transport.
+ void FlushFlight();
+
+ // SendAlert causes this TlsHandshaker to close the QUIC connection with an
+ // error code corresponding to the TLS alert description |desc|.
+ void SendAlert(EncryptionLevel level, uint8_t desc);
+
+ QuicCryptoStream* stream_;
+ QuicSession* session_;
+
+ QuicErrorCode parser_error_ = QUIC_NO_ERROR;
+ std::string parser_error_detail_;
+
+ bssl::UniquePtr<SSL> ssl_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_TLS_HANDSHAKER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc
new file mode 100644
index 00000000000..a7b2aa8b410
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_handshaker_test.cc
@@ -0,0 +1,434 @@
+// 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.
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using ::testing::_;
+
+class FakeProofVerifier : public ProofVerifier {
+ public:
+ FakeProofVerifier()
+ : verifier_(crypto_test_utils::ProofVerifierForTesting()) {}
+
+ QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const uint16_t port,
+ const std::string& server_config,
+ QuicTransportVersion quic_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override {
+ return verifier_->VerifyProof(
+ hostname, port, server_config, quic_version, chlo_hash, certs, cert_sct,
+ signature, context, error_details, details, std::move(callback));
+ }
+
+ QuicAsyncStatus VerifyCertChain(
+ const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override {
+ if (!active_) {
+ return verifier_->VerifyCertChain(hostname, certs, context, error_details,
+ details, std::move(callback));
+ }
+ pending_ops_.push_back(QuicMakeUnique<VerifyChainPendingOp>(
+ hostname, certs, context, error_details, details, std::move(callback),
+ verifier_.get()));
+ return QUIC_PENDING;
+ }
+
+ std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override {
+ return nullptr;
+ }
+
+ void Activate() { active_ = true; }
+
+ size_t NumPendingCallbacks() const { return pending_ops_.size(); }
+
+ void InvokePendingCallback(size_t n) {
+ CHECK(NumPendingCallbacks() > n);
+ pending_ops_[n]->Run();
+ auto it = pending_ops_.begin() + n;
+ pending_ops_.erase(it);
+ }
+
+ private:
+ // Implementation of ProofVerifierCallback that fails if the callback is ever
+ // run.
+ class FailingProofVerifierCallback : public ProofVerifierCallback {
+ public:
+ void Run(bool ok,
+ const std::string& error_details,
+ std::unique_ptr<ProofVerifyDetails>* details) override {
+ FAIL();
+ }
+ };
+
+ class VerifyChainPendingOp {
+ public:
+ VerifyChainPendingOp(const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback,
+ ProofVerifier* delegate)
+ : hostname_(hostname),
+ certs_(certs),
+ context_(context),
+ error_details_(error_details),
+ details_(details),
+ callback_(std::move(callback)),
+ delegate_(delegate) {}
+
+ void Run() {
+ // FakeProofVerifier depends on crypto_test_utils::ProofVerifierForTesting
+ // running synchronously. It passes a FailingProofVerifierCallback and
+ // runs the original callback after asserting that the verification ran
+ // synchronously.
+ QuicAsyncStatus status = delegate_->VerifyCertChain(
+ hostname_, certs_, context_, error_details_, details_,
+ QuicMakeUnique<FailingProofVerifierCallback>());
+ ASSERT_NE(status, QUIC_PENDING);
+ callback_->Run(status == QUIC_SUCCESS, *error_details_, details_);
+ }
+
+ private:
+ std::string hostname_;
+ std::vector<std::string> certs_;
+ const ProofVerifyContext* context_;
+ std::string* error_details_;
+ std::unique_ptr<ProofVerifyDetails>* details_;
+ std::unique_ptr<ProofVerifierCallback> callback_;
+ ProofVerifier* delegate_;
+ };
+
+ std::unique_ptr<ProofVerifier> verifier_;
+ bool active_ = false;
+ std::vector<std::unique_ptr<VerifyChainPendingOp>> pending_ops_;
+};
+
+class TestQuicCryptoStream : public QuicCryptoStream {
+ public:
+ explicit TestQuicCryptoStream(QuicSession* session)
+ : QuicCryptoStream(session) {}
+
+ ~TestQuicCryptoStream() override = default;
+
+ virtual TlsHandshaker* handshaker() const = 0;
+
+ bool encryption_established() const override {
+ return handshaker()->encryption_established();
+ }
+
+ bool handshake_confirmed() const override {
+ return handshaker()->handshake_confirmed();
+ }
+
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override {
+ return handshaker()->crypto_negotiated_params();
+ }
+
+ CryptoMessageParser* crypto_message_parser() override {
+ return handshaker()->crypto_message_parser();
+ }
+
+ void WriteCryptoData(EncryptionLevel level, QuicStringPiece data) override {
+ pending_writes_.push_back(std::make_pair(std::string(data), level));
+ }
+
+ const std::vector<std::pair<std::string, EncryptionLevel>>& pending_writes() {
+ return pending_writes_;
+ }
+
+ // Sends the pending frames to |stream| and clears the array of pending
+ // writes.
+ void SendCryptoMessagesToPeer(QuicCryptoStream* stream) {
+ QUIC_LOG(INFO) << "Sending " << pending_writes_.size() << " frames";
+ // This is a minimal re-implementation of QuicCryptoStream::OnDataAvailable.
+ // It doesn't work to call QuicStream::OnStreamFrame because
+ // QuicCryptoStream::OnDataAvailable currently (as an implementation detail)
+ // relies on the QuicConnection to know the EncryptionLevel to pass into
+ // CryptoMessageParser::ProcessInput. Since the crypto messages in this test
+ // never reach the framer or connection and never get encrypted/decrypted,
+ // QuicCryptoStream::OnDataAvailable isn't able to call ProcessInput with
+ // the correct EncryptionLevel. Instead, that can be short-circuited by
+ // directly calling ProcessInput here.
+ for (size_t i = 0; i < pending_writes_.size(); ++i) {
+ if (!stream->crypto_message_parser()->ProcessInput(
+ pending_writes_[i].first, pending_writes_[i].second)) {
+ CloseConnectionWithDetails(
+ stream->crypto_message_parser()->error(),
+ stream->crypto_message_parser()->error_detail());
+ break;
+ }
+ }
+ pending_writes_.clear();
+ }
+
+ private:
+ std::vector<std::pair<std::string, EncryptionLevel>> pending_writes_;
+};
+
+class TestQuicCryptoClientStream : public TestQuicCryptoStream {
+ public:
+ explicit TestQuicCryptoClientStream(QuicSession* session)
+ : TestQuicCryptoStream(session),
+ proof_verifier_(new FakeProofVerifier),
+ ssl_ctx_(TlsClientHandshaker::CreateSslCtx()),
+ handshaker_(new TlsClientHandshaker(
+ this,
+ session,
+ QuicServerId("test.example.com", 443, false),
+ proof_verifier_.get(),
+ ssl_ctx_.get(),
+ crypto_test_utils::ProofVerifyContextForTesting(),
+ "quic-tester")) {}
+
+ ~TestQuicCryptoClientStream() override = default;
+
+ TlsHandshaker* handshaker() const override { return handshaker_.get(); }
+
+ bool CryptoConnect() { return handshaker_->CryptoConnect(); }
+
+ FakeProofVerifier* GetFakeProofVerifier() const {
+ return proof_verifier_.get();
+ }
+
+ private:
+ std::unique_ptr<FakeProofVerifier> proof_verifier_;
+ bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+ std::unique_ptr<TlsClientHandshaker> handshaker_;
+};
+
+class TestQuicCryptoServerStream : public TestQuicCryptoStream {
+ public:
+ TestQuicCryptoServerStream(QuicSession* session,
+ FakeProofSource* proof_source)
+ : TestQuicCryptoStream(session),
+ proof_source_(proof_source),
+ ssl_ctx_(TlsServerHandshaker::CreateSslCtx()),
+ handshaker_(new TlsServerHandshaker(this,
+ session,
+ ssl_ctx_.get(),
+ proof_source_)) {}
+
+ ~TestQuicCryptoServerStream() override = default;
+
+ void CancelOutstandingCallbacks() {
+ handshaker_->CancelOutstandingCallbacks();
+ }
+
+ TlsHandshaker* handshaker() const override { return handshaker_.get(); }
+
+ FakeProofSource* GetFakeProofSource() const { return proof_source_; }
+
+ private:
+ FakeProofSource* proof_source_;
+ bssl::UniquePtr<SSL_CTX> ssl_ctx_;
+ std::unique_ptr<TlsServerHandshaker> handshaker_;
+};
+
+void ExchangeHandshakeMessages(TestQuicCryptoStream* client,
+ TestQuicCryptoStream* server) {
+ while (!client->pending_writes().empty() ||
+ !server->pending_writes().empty()) {
+ client->SendCryptoMessagesToPeer(server);
+ server->SendCryptoMessagesToPeer(client);
+ }
+}
+
+ParsedQuicVersionVector AllTlsSupportedVersions() {
+ SetQuicReloadableFlag(quic_enable_version_99, true);
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ ParsedQuicVersionVector supported_versions;
+ for (QuicTransportVersion version : kSupportedTransportVersions) {
+ if (!QuicVersionUsesCryptoFrames(version)) {
+ // The TLS handshake is only deployable if CRYPTO frames are also used.
+ continue;
+ }
+ supported_versions.push_back(ParsedQuicVersion(PROTOCOL_TLS1_3, version));
+ }
+ return supported_versions;
+}
+
+class TlsHandshakerTest : public QuicTest {
+ public:
+ TlsHandshakerTest()
+ : client_conn_(new MockQuicConnection(&conn_helper_,
+ &alarm_factory_,
+ Perspective::IS_CLIENT,
+ AllTlsSupportedVersions())),
+ server_conn_(new MockQuicConnection(&conn_helper_,
+ &alarm_factory_,
+ Perspective::IS_SERVER,
+ AllTlsSupportedVersions())),
+ client_session_(client_conn_, /*create_mock_crypto_stream=*/false),
+ server_session_(server_conn_, /*create_mock_crypto_stream=*/false) {
+ client_stream_ = new TestQuicCryptoClientStream(&client_session_);
+ client_session_.SetCryptoStream(client_stream_);
+ server_stream_ =
+ new TestQuicCryptoServerStream(&server_session_, &proof_source_);
+ server_session_.SetCryptoStream(server_stream_);
+ client_session_.Initialize();
+ server_session_.Initialize();
+ EXPECT_FALSE(client_stream_->encryption_established());
+ EXPECT_FALSE(client_stream_->handshake_confirmed());
+ EXPECT_FALSE(server_stream_->encryption_established());
+ EXPECT_FALSE(server_stream_->handshake_confirmed());
+ }
+
+ MockQuicConnectionHelper conn_helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnection* client_conn_;
+ MockQuicConnection* server_conn_;
+ MockQuicSession client_session_;
+ MockQuicSession server_session_;
+
+ FakeProofSource proof_source_;
+ TestQuicCryptoClientStream* client_stream_;
+ TestQuicCryptoServerStream* server_stream_;
+};
+
+TEST_F(TlsHandshakerTest, CryptoHandshake) {
+ EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0);
+ client_stream_->CryptoConnect();
+ ExchangeHandshakeMessages(client_stream_, server_stream_);
+
+ EXPECT_TRUE(client_stream_->handshake_confirmed());
+ EXPECT_TRUE(client_stream_->encryption_established());
+ EXPECT_TRUE(server_stream_->handshake_confirmed());
+ EXPECT_TRUE(server_stream_->encryption_established());
+}
+
+TEST_F(TlsHandshakerTest, HandshakeWithAsyncProofSource) {
+ EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0);
+ // Enable FakeProofSource to capture call to ComputeTlsSignature and run it
+ // asynchronously.
+ FakeProofSource* proof_source = server_stream_->GetFakeProofSource();
+ proof_source->Activate();
+
+ // Start handshake.
+ client_stream_->CryptoConnect();
+ ExchangeHandshakeMessages(client_stream_, server_stream_);
+
+ ASSERT_EQ(proof_source->NumPendingCallbacks(), 1);
+ proof_source->InvokePendingCallback(0);
+
+ ExchangeHandshakeMessages(client_stream_, server_stream_);
+
+ EXPECT_TRUE(client_stream_->handshake_confirmed());
+ EXPECT_TRUE(client_stream_->encryption_established());
+ EXPECT_TRUE(server_stream_->handshake_confirmed());
+ EXPECT_TRUE(server_stream_->encryption_established());
+}
+
+TEST_F(TlsHandshakerTest, CancelPendingProofSource) {
+ EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0);
+ // Enable FakeProofSource to capture call to ComputeTlsSignature and run it
+ // asynchronously.
+ FakeProofSource* proof_source = server_stream_->GetFakeProofSource();
+ proof_source->Activate();
+
+ // Start handshake.
+ client_stream_->CryptoConnect();
+ ExchangeHandshakeMessages(client_stream_, server_stream_);
+
+ ASSERT_EQ(proof_source->NumPendingCallbacks(), 1);
+ server_stream_ = nullptr;
+
+ proof_source->InvokePendingCallback(0);
+}
+
+TEST_F(TlsHandshakerTest, HandshakeWithAsyncProofVerifier) {
+ EXPECT_CALL(*client_conn_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_CALL(*server_conn_, CloseConnection(_, _, _)).Times(0);
+ // Enable FakeProofVerifier to capture call to VerifyCertChain and run it
+ // asynchronously.
+ FakeProofVerifier* proof_verifier = client_stream_->GetFakeProofVerifier();
+ proof_verifier->Activate();
+
+ // Start handshake.
+ client_stream_->CryptoConnect();
+ ExchangeHandshakeMessages(client_stream_, server_stream_);
+
+ ASSERT_EQ(proof_verifier->NumPendingCallbacks(), 1u);
+ proof_verifier->InvokePendingCallback(0);
+
+ ExchangeHandshakeMessages(client_stream_, server_stream_);
+
+ EXPECT_TRUE(client_stream_->handshake_confirmed());
+ EXPECT_TRUE(client_stream_->encryption_established());
+ EXPECT_TRUE(server_stream_->handshake_confirmed());
+ EXPECT_TRUE(server_stream_->encryption_established());
+}
+
+TEST_F(TlsHandshakerTest, ClientConnectionClosedOnTlsError) {
+ // Have client send ClientHello.
+ client_stream_->CryptoConnect();
+ EXPECT_CALL(*client_conn_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
+
+ // Send a zero-length ServerHello from server to client.
+ char bogus_handshake_message[] = {
+ // Handshake struct (RFC 8446 appendix B.3)
+ 2, // HandshakeType server_hello
+ 0, 0, 0, // uint24 length
+ };
+ server_stream_->WriteCryptoData(
+ ENCRYPTION_INITIAL,
+ QuicStringPiece(bogus_handshake_message,
+ QUIC_ARRAYSIZE(bogus_handshake_message)));
+ server_stream_->SendCryptoMessagesToPeer(client_stream_);
+
+ EXPECT_FALSE(client_stream_->handshake_confirmed());
+}
+
+TEST_F(TlsHandshakerTest, ServerConnectionClosedOnTlsError) {
+ EXPECT_CALL(*server_conn_, CloseConnection(QUIC_HANDSHAKE_FAILED, _, _));
+
+ // Send a zero-length ClientHello from client to server.
+ char bogus_handshake_message[] = {
+ // Handshake struct (RFC 8446 appendix B.3)
+ 1, // HandshakeType client_hello
+ 0, 0, 0, // uint24 length
+ };
+ client_stream_->WriteCryptoData(
+ ENCRYPTION_INITIAL,
+ QuicStringPiece(bogus_handshake_message,
+ QUIC_ARRAYSIZE(bogus_handshake_message)));
+ client_stream_->SendCryptoMessagesToPeer(server_stream_);
+
+ EXPECT_FALSE(server_stream_->handshake_confirmed());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..ec254b53550
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.cc
@@ -0,0 +1,407 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+
+#include <memory>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/pool.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+TlsServerHandshaker::SignatureCallback::SignatureCallback(
+ TlsServerHandshaker* handshaker)
+ : handshaker_(handshaker) {}
+
+void TlsServerHandshaker::SignatureCallback::Run(bool ok,
+ std::string signature) {
+ if (handshaker_ == nullptr) {
+ return;
+ }
+ if (ok) {
+ handshaker_->cert_verify_sig_ = std::move(signature);
+ }
+ State last_state = handshaker_->state_;
+ handshaker_->state_ = STATE_SIGNATURE_COMPLETE;
+ handshaker_->signature_callback_ = nullptr;
+ if (last_state == STATE_SIGNATURE_PENDING) {
+ handshaker_->AdvanceHandshake();
+ }
+}
+
+void TlsServerHandshaker::SignatureCallback::Cancel() {
+ handshaker_ = nullptr;
+}
+
+const SSL_PRIVATE_KEY_METHOD TlsServerHandshaker::kPrivateKeyMethod{
+ &TlsServerHandshaker::PrivateKeySign,
+ nullptr, // decrypt
+ &TlsServerHandshaker::PrivateKeyComplete,
+};
+
+// static
+bssl::UniquePtr<SSL_CTX> TlsServerHandshaker::CreateSslCtx() {
+ bssl::UniquePtr<SSL_CTX> ssl_ctx = TlsHandshaker::CreateSslCtx();
+ SSL_CTX_set_tlsext_servername_callback(
+ ssl_ctx.get(), TlsServerHandshaker::SelectCertificateCallback);
+ SSL_CTX_set_alpn_select_cb(ssl_ctx.get(),
+ TlsServerHandshaker::SelectAlpnCallback, nullptr);
+ return ssl_ctx;
+}
+
+TlsServerHandshaker::TlsServerHandshaker(QuicCryptoStream* stream,
+ QuicSession* session,
+ SSL_CTX* ssl_ctx,
+ ProofSource* proof_source)
+ : TlsHandshaker(stream, session, ssl_ctx),
+ proof_source_(proof_source),
+ crypto_negotiated_params_(new QuicCryptoNegotiatedParameters) {
+ DCHECK_EQ(PROTOCOL_TLS1_3,
+ session->connection()->version().handshake_protocol);
+ CrypterPair crypters;
+ CryptoUtils::CreateTlsInitialCrypters(
+ Perspective::IS_SERVER, session->connection()->transport_version(),
+ session->connection_id(), &crypters);
+ session->connection()->SetEncrypter(ENCRYPTION_INITIAL,
+ std::move(crypters.encrypter));
+ session->connection()->InstallDecrypter(ENCRYPTION_INITIAL,
+ std::move(crypters.decrypter));
+
+ // Configure the SSL to be a server.
+ SSL_set_accept_state(ssl());
+
+ if (!SetTransportParameters()) {
+ CloseConnection(QUIC_HANDSHAKE_FAILED,
+ "Failed to set Transport Parameters");
+ }
+}
+
+TlsServerHandshaker::~TlsServerHandshaker() {
+ CancelOutstandingCallbacks();
+}
+
+void TlsServerHandshaker::CancelOutstandingCallbacks() {
+ if (signature_callback_) {
+ signature_callback_->Cancel();
+ signature_callback_ = nullptr;
+ }
+}
+
+bool TlsServerHandshaker::GetBase64SHA256ClientChannelID(
+ std::string* output) const {
+ // Channel ID is not supported when TLS is used in QUIC.
+ return false;
+}
+
+void TlsServerHandshaker::SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) {
+ // SCUP messages aren't supported when using the TLS handshake.
+}
+
+uint8_t TlsServerHandshaker::NumHandshakeMessages() const {
+ // TODO(nharper): Return a sensible value here.
+ return 0;
+}
+
+uint8_t TlsServerHandshaker::NumHandshakeMessagesWithServerNonces() const {
+ // TODO(nharper): Return a sensible value here.
+ return 0;
+}
+
+int TlsServerHandshaker::NumServerConfigUpdateMessagesSent() const {
+ // SCUP messages aren't supported when using the TLS handshake.
+ return 0;
+}
+
+const CachedNetworkParameters*
+TlsServerHandshaker::PreviousCachedNetworkParams() const {
+ return nullptr;
+}
+
+bool TlsServerHandshaker::ZeroRttAttempted() const {
+ // TODO(nharper): Support 0-RTT with TLS 1.3 in QUIC.
+ return false;
+}
+
+void TlsServerHandshaker::SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) {}
+
+bool TlsServerHandshaker::ShouldSendExpectCTHeader() const {
+ return false;
+}
+
+bool TlsServerHandshaker::encryption_established() const {
+ return encryption_established_;
+}
+
+bool TlsServerHandshaker::handshake_confirmed() const {
+ return handshake_confirmed_;
+}
+
+const QuicCryptoNegotiatedParameters&
+TlsServerHandshaker::crypto_negotiated_params() const {
+ return *crypto_negotiated_params_;
+}
+
+CryptoMessageParser* TlsServerHandshaker::crypto_message_parser() {
+ return TlsHandshaker::crypto_message_parser();
+}
+
+void TlsServerHandshaker::AdvanceHandshake() {
+ if (state_ == STATE_CONNECTION_CLOSED) {
+ QUIC_LOG(INFO) << "TlsServerHandshaker received handshake message after "
+ "connection was closed";
+ return;
+ }
+ if (state_ == STATE_HANDSHAKE_COMPLETE) {
+ // TODO(nharper): Handle post-handshake messages.
+ return;
+ }
+
+ int rv = SSL_do_handshake(ssl());
+ if (rv == 1) {
+ FinishHandshake();
+ return;
+ }
+
+ int ssl_error = SSL_get_error(ssl(), rv);
+ bool should_close = true;
+ switch (state_) {
+ case STATE_LISTENING:
+ case STATE_SIGNATURE_COMPLETE:
+ should_close = ssl_error != SSL_ERROR_WANT_READ;
+ break;
+ case STATE_SIGNATURE_PENDING:
+ should_close = ssl_error != SSL_ERROR_WANT_PRIVATE_KEY_OPERATION;
+ break;
+ default:
+ should_close = true;
+ }
+ if (should_close && state_ != STATE_CONNECTION_CLOSED) {
+ QUIC_LOG(WARNING) << "SSL_do_handshake failed; SSL_get_error returns "
+ << ssl_error << ", state_ = " << state_;
+ ERR_print_errors_fp(stderr);
+ CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed");
+ }
+}
+
+void TlsServerHandshaker::CloseConnection(QuicErrorCode error,
+ const std::string& reason_phrase) {
+ state_ = STATE_CONNECTION_CLOSED;
+ stream()->CloseConnectionWithDetails(error, reason_phrase);
+}
+
+bool TlsServerHandshaker::ProcessTransportParameters(
+ std::string* error_details) {
+ TransportParameters client_params;
+ const uint8_t* client_params_bytes;
+ size_t params_bytes_len;
+ SSL_get_peer_quic_transport_params(ssl(), &client_params_bytes,
+ &params_bytes_len);
+ if (params_bytes_len == 0 ||
+ !ParseTransportParameters(client_params_bytes, params_bytes_len,
+ Perspective::IS_CLIENT, &client_params)) {
+ *error_details = "Unable to parse Transport Parameters";
+ return false;
+ }
+ if (CryptoUtils::ValidateClientHelloVersion(
+ client_params.version, session()->connection()->version(),
+ session()->supported_versions(), error_details) != QUIC_NO_ERROR ||
+ session()->config()->ProcessTransportParameters(
+ client_params, CLIENT, error_details) != QUIC_NO_ERROR) {
+ return false;
+ }
+
+ session()->OnConfigNegotiated();
+ return true;
+}
+
+bool TlsServerHandshaker::SetTransportParameters() {
+ TransportParameters server_params;
+ server_params.perspective = Perspective::IS_SERVER;
+ server_params.supported_versions =
+ CreateQuicVersionLabelVector(session()->supported_versions());
+ server_params.version =
+ CreateQuicVersionLabel(session()->connection()->version());
+
+ if (!session()->config()->FillTransportParameters(&server_params)) {
+ return false;
+ }
+
+ // TODO(nharper): Provide an actual value for the stateless reset token.
+ server_params.stateless_reset_token.resize(16);
+ std::vector<uint8_t> server_params_bytes;
+ if (!SerializeTransportParameters(server_params, &server_params_bytes) ||
+ SSL_set_quic_transport_params(ssl(), server_params_bytes.data(),
+ server_params_bytes.size()) != 1) {
+ return false;
+ }
+ return true;
+}
+
+void TlsServerHandshaker::FinishHandshake() {
+ QUIC_LOG(INFO) << "Server: handshake finished";
+ state_ = STATE_HANDSHAKE_COMPLETE;
+
+ session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ session()->NeuterUnencryptedData();
+ encryption_established_ = true;
+ handshake_confirmed_ = true;
+}
+
+// static
+TlsServerHandshaker* TlsServerHandshaker::HandshakerFromSsl(SSL* ssl) {
+ return static_cast<TlsServerHandshaker*>(
+ TlsHandshaker::HandshakerFromSsl(ssl));
+}
+
+// static
+ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign(SSL* ssl,
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out,
+ uint16_t sig_alg,
+ const uint8_t* in,
+ size_t in_len) {
+ return HandshakerFromSsl(ssl)->PrivateKeySign(
+ out, out_len, max_out, sig_alg,
+ QuicStringPiece(reinterpret_cast<const char*>(in), in_len));
+}
+
+ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign(
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out,
+ uint16_t sig_alg,
+ QuicStringPiece in) {
+ signature_callback_ = new SignatureCallback(this);
+ proof_source_->ComputeTlsSignature(
+ session()->connection()->self_address(), hostname_, sig_alg, in,
+ std::unique_ptr<SignatureCallback>(signature_callback_));
+ if (state_ == STATE_SIGNATURE_COMPLETE) {
+ return PrivateKeyComplete(out, out_len, max_out);
+ }
+ state_ = STATE_SIGNATURE_PENDING;
+ return ssl_private_key_retry;
+}
+
+// static
+ssl_private_key_result_t TlsServerHandshaker::PrivateKeyComplete(
+ SSL* ssl,
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out) {
+ return HandshakerFromSsl(ssl)->PrivateKeyComplete(out, out_len, max_out);
+}
+
+ssl_private_key_result_t TlsServerHandshaker::PrivateKeyComplete(
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out) {
+ if (state_ == STATE_SIGNATURE_PENDING) {
+ return ssl_private_key_retry;
+ }
+ if (cert_verify_sig_.size() > max_out || cert_verify_sig_.empty()) {
+ return ssl_private_key_failure;
+ }
+ *out_len = cert_verify_sig_.size();
+ memcpy(out, cert_verify_sig_.data(), *out_len);
+ cert_verify_sig_.clear();
+ cert_verify_sig_.shrink_to_fit();
+ return ssl_private_key_success;
+}
+
+// static
+int TlsServerHandshaker::SelectCertificateCallback(SSL* ssl,
+ int* out_alert,
+ void* arg) {
+ return HandshakerFromSsl(ssl)->SelectCertificate(out_alert);
+}
+
+int TlsServerHandshaker::SelectCertificate(int* out_alert) {
+ const char* hostname = SSL_get_servername(ssl(), TLSEXT_NAMETYPE_host_name);
+ if (hostname) {
+ hostname_ = hostname;
+ } else {
+ QUIC_LOG(INFO) << "No hostname indicated in SNI";
+ }
+
+ QuicReferenceCountedPointer<ProofSource::Chain> chain =
+ proof_source_->GetCertChain(session()->connection()->self_address(),
+ hostname_);
+ if (chain->certs.empty()) {
+ QUIC_LOG(ERROR) << "No certs provided for host '" << hostname_ << "'";
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ std::vector<CRYPTO_BUFFER*> certs;
+ certs.resize(chain->certs.size());
+ for (size_t i = 0; i < certs.size(); i++) {
+ certs[i] = CRYPTO_BUFFER_new(
+ reinterpret_cast<const uint8_t*>(chain->certs[i].data()),
+ chain->certs[i].length(), nullptr);
+ }
+
+ SSL_set_chain_and_key(ssl(), certs.data(), certs.size(), nullptr,
+ &kPrivateKeyMethod);
+
+ for (size_t i = 0; i < certs.size(); i++) {
+ CRYPTO_BUFFER_free(certs[i]);
+ }
+
+ std::string error_details;
+ if (!ProcessTransportParameters(&error_details)) {
+ CloseConnection(QUIC_HANDSHAKE_FAILED, error_details);
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ QUIC_LOG(INFO) << "Set " << chain->certs.size() << " certs for server";
+ return SSL_TLSEXT_ERR_OK;
+}
+
+// static
+int TlsServerHandshaker::SelectAlpnCallback(SSL* ssl,
+ const uint8_t** out,
+ uint8_t* out_len,
+ const uint8_t* in,
+ unsigned in_len,
+ void* arg) {
+ return HandshakerFromSsl(ssl)->SelectAlpn(out, out_len, in, in_len);
+}
+
+int TlsServerHandshaker::SelectAlpn(const uint8_t** out,
+ uint8_t* out_len,
+ const uint8_t* in,
+ unsigned in_len) {
+ // |in| contains a sequence of 1-byte-length-prefixed values.
+ // We currently simply return the first provided ALPN value.
+ // TODO(b/130164908) Act on ALPN.
+ if (in_len == 0) {
+ *out_len = 0;
+ *out = nullptr;
+ QUIC_DLOG(INFO) << "No ALPN provided";
+ return SSL_TLSEXT_ERR_OK;
+ }
+ const uint8_t first_alpn_length = in[0];
+ if (static_cast<unsigned>(first_alpn_length) > in_len - 1) {
+ QUIC_LOG(ERROR) << "Failed to parse ALPN";
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ *out_len = first_alpn_length;
+ *out = in + 1;
+ QUIC_DLOG(INFO) << "Server selecting ALPN '"
+ << QuicStringPiece(reinterpret_cast<const char*>(*out),
+ *out_len)
+ << "'";
+ return SSL_TLSEXT_ERR_OK;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..e27d8967314
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/tls_server_handshaker.h
@@ -0,0 +1,183 @@
+// 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_CORE_TLS_SERVER_HANDSHAKER_H_
+#define QUICHE_QUIC_CORE_TLS_SERVER_HANDSHAKER_H_
+
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/pool.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/tls_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// An implementation of QuicCryptoServerStream::HandshakerDelegate which uses
+// TLS 1.3 for the crypto handshake protocol.
+class QUIC_EXPORT_PRIVATE TlsServerHandshaker
+ : public QuicCryptoServerStream::HandshakerDelegate,
+ public TlsHandshaker {
+ public:
+ TlsServerHandshaker(QuicCryptoStream* stream,
+ QuicSession* session,
+ SSL_CTX* ssl_ctx,
+ ProofSource* proof_source);
+ TlsServerHandshaker(const TlsServerHandshaker&) = delete;
+ TlsServerHandshaker& operator=(const TlsServerHandshaker&) = delete;
+
+ ~TlsServerHandshaker() override;
+
+ // Creates and configures an SSL_CTX to be used with a TlsServerHandshaker.
+ // The caller is responsible for ownership of the newly created struct.
+ static bssl::UniquePtr<SSL_CTX> CreateSslCtx();
+
+ // From QuicCryptoServerStream::HandshakerDelegate
+ void CancelOutstandingCallbacks() override;
+ bool GetBase64SHA256ClientChannelID(std::string* output) const override;
+ void SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) override;
+ uint8_t NumHandshakeMessages() const override;
+ uint8_t NumHandshakeMessagesWithServerNonces() const override;
+ int NumServerConfigUpdateMessagesSent() const override;
+ const CachedNetworkParameters* PreviousCachedNetworkParams() const override;
+ bool ZeroRttAttempted() const override;
+ void SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) override;
+ bool ShouldSendExpectCTHeader() const override;
+
+ // From QuicCryptoServerStream::HandshakerDelegate and TlsHandshaker
+ bool encryption_established() const override;
+ bool handshake_confirmed() const override;
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override;
+ CryptoMessageParser* crypto_message_parser() override;
+
+ // Calls SelectCertificate after looking up the TlsServerHandshaker from
+ // |ssl|.
+ static int SelectCertificateCallback(SSL* ssl, int* out_alert, void* arg);
+
+ // Calls SelectAlpn after looking up the TlsServerHandshaker from |ssl|.
+ static int SelectAlpnCallback(SSL* ssl,
+ const uint8_t** out,
+ uint8_t* out_len,
+ const uint8_t* in,
+ unsigned in_len,
+ void* arg);
+
+ private:
+ class SignatureCallback : public ProofSource::SignatureCallback {
+ public:
+ explicit SignatureCallback(TlsServerHandshaker* handshaker);
+ void Run(bool ok, std::string signature) override;
+
+ // If called, Cancel causes the pending callback to be a no-op.
+ void Cancel();
+
+ private:
+ TlsServerHandshaker* handshaker_;
+ };
+
+ enum State {
+ STATE_LISTENING,
+ STATE_SIGNATURE_PENDING,
+ STATE_SIGNATURE_COMPLETE,
+ STATE_HANDSHAKE_COMPLETE,
+ STATE_CONNECTION_CLOSED,
+ };
+
+ // |kPrivateKeyMethod| is a vtable pointing to PrivateKeySign and
+ // PrivateKeyComplete used by the TLS stack to compute the signature for the
+ // CertificateVerify message (using the server's private key).
+ static const SSL_PRIVATE_KEY_METHOD kPrivateKeyMethod;
+
+ // Called when a new message is received on the crypto stream and is available
+ // for the TLS stack to read.
+ void AdvanceHandshake() override;
+ void CloseConnection(QuicErrorCode error,
+ const std::string& reason_phrase) override;
+
+ // Called when the TLS handshake is complete.
+ void FinishHandshake();
+
+ void CloseConnection(const std::string& reason_phrase);
+
+ bool SetTransportParameters();
+ bool ProcessTransportParameters(std::string* error_details);
+
+ // Calls the instance method PrivateKeySign after looking up the
+ // TlsServerHandshaker from |ssl|.
+ static ssl_private_key_result_t PrivateKeySign(SSL* ssl,
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out,
+ uint16_t sig_alg,
+ const uint8_t* in,
+ size_t in_len);
+
+ // Signs |in| using the signature algorithm specified by |sig_alg| (an
+ // SSL_SIGN_* value). If the signing operation cannot be completed
+ // synchronously, ssl_private_key_retry is returned. If there is an error
+ // signing, or if the signature is longer than |max_out|, then
+ // ssl_private_key_failure is returned. Otherwise, ssl_private_key_success is
+ // returned with the signature put in |*out| and the length in |*out_len|.
+ ssl_private_key_result_t PrivateKeySign(uint8_t* out,
+ size_t* out_len,
+ size_t max_out,
+ uint16_t sig_alg,
+ QuicStringPiece in);
+
+ // Calls the instance method PrivateKeyComplete after looking up the
+ // TlsServerHandshaker from |ssl|.
+ static ssl_private_key_result_t PrivateKeyComplete(SSL* ssl,
+ uint8_t* out,
+ size_t* out_len,
+ size_t max_out);
+
+ // When PrivateKeySign returns ssl_private_key_retry, PrivateKeyComplete will
+ // be called after the async sign operation has completed. PrivateKeyComplete
+ // puts the resulting signature in |*out| and length in |*out_len|. If the
+ // length is greater than |max_out| or if there was an error in signing, then
+ // ssl_private_key_failure is returned. Otherwise, ssl_private_key_success is
+ // returned.
+ ssl_private_key_result_t PrivateKeyComplete(uint8_t* out,
+ size_t* out_len,
+ size_t max_out);
+
+ // Configures the certificate to use on |ssl_| based on the SNI sent by the
+ // client. Returns an SSL_TLSEXT_ERR_* value (see
+ // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_tlsext_servername_callback).
+ //
+ // If SelectCertificate returns SSL_TLSEXT_ERR_ALERT_FATAL, then it puts in
+ // |*out_alert| the TLS alert value that the server will send.
+ int SelectCertificate(int* out_alert);
+
+ // Selects which ALPN to use based on the list sent by the client.
+ int SelectAlpn(const uint8_t** out,
+ uint8_t* out_len,
+ const uint8_t* in,
+ unsigned in_len);
+
+ static TlsServerHandshaker* HandshakerFromSsl(SSL* ssl);
+
+ State state_ = STATE_LISTENING;
+
+ ProofSource* proof_source_;
+ SignatureCallback* signature_callback_ = nullptr;
+
+ std::string hostname_;
+ std::string cert_verify_sig_;
+
+ bool encryption_established_ = false;
+ bool handshake_confirmed_ = false;
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters>
+ crypto_negotiated_params_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_TLS_SERVER_HANDSHAKER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc
new file mode 100644
index 00000000000..51ea994fe95
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.cc
@@ -0,0 +1,210 @@
+// 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 "net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+namespace quic {
+namespace {
+
+Perspective Reverse(Perspective perspective) {
+ return perspective == Perspective::IS_SERVER ? Perspective::IS_CLIENT
+ : Perspective::IS_SERVER;
+}
+
+} // namespace
+
+UberQuicStreamIdManager::UberQuicStreamIdManager(
+ QuicSession* session,
+ size_t max_open_outgoing_streams,
+ size_t max_open_incoming_streams)
+ : bidirectional_stream_id_manager_(
+ session,
+ QuicUtils::GetFirstBidirectionalStreamId(
+ session->connection()->transport_version(),
+ session->perspective()),
+ session->perspective() == Perspective::IS_SERVER
+ ? QuicUtils::GetCryptoStreamId(
+ session->connection()->transport_version())
+ : QuicUtils::GetInvalidStreamId(
+ session->connection()->transport_version()),
+ QuicUtils::GetFirstBidirectionalStreamId(
+ session->connection()->transport_version(),
+ Reverse(session->perspective())),
+ max_open_outgoing_streams,
+ max_open_incoming_streams),
+ unidirectional_stream_id_manager_(
+ session,
+ QuicUtils::GetFirstUnidirectionalStreamId(
+ session->connection()->transport_version(),
+ session->perspective()),
+ QuicUtils::GetInvalidStreamId(
+ session->connection()->transport_version()),
+ QuicUtils::GetFirstUnidirectionalStreamId(
+ session->connection()->transport_version(),
+ Reverse(session->perspective())),
+ max_open_outgoing_streams,
+ max_open_incoming_streams) {}
+
+void UberQuicStreamIdManager::RegisterStaticStream(QuicStreamId id) {
+ if (QuicUtils::IsBidirectionalStreamId(id)) {
+ bidirectional_stream_id_manager_.RegisterStaticStream(id);
+ return;
+ }
+ unidirectional_stream_id_manager_.RegisterStaticStream(id);
+}
+
+void UberQuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_streams) {
+ bidirectional_stream_id_manager_.SetMaxOpenOutgoingStreams(max_streams);
+ unidirectional_stream_id_manager_.SetMaxOpenOutgoingStreams(max_streams);
+}
+
+void UberQuicStreamIdManager::SetMaxOpenIncomingStreams(size_t max_streams) {
+ bidirectional_stream_id_manager_.SetMaxOpenIncomingStreams(max_streams);
+ unidirectional_stream_id_manager_.SetMaxOpenIncomingStreams(max_streams);
+}
+
+bool UberQuicStreamIdManager::CanOpenNextOutgoingBidirectionalStream() {
+ return bidirectional_stream_id_manager_.CanOpenNextOutgoingStream();
+}
+
+bool UberQuicStreamIdManager::CanOpenNextOutgoingUnidirectionalStream() {
+ return unidirectional_stream_id_manager_.CanOpenNextOutgoingStream();
+}
+
+QuicStreamId UberQuicStreamIdManager::GetNextOutgoingBidirectionalStreamId() {
+ return bidirectional_stream_id_manager_.GetNextOutgoingStreamId();
+}
+
+QuicStreamId UberQuicStreamIdManager::GetNextOutgoingUnidirectionalStreamId() {
+ return unidirectional_stream_id_manager_.GetNextOutgoingStreamId();
+}
+
+bool UberQuicStreamIdManager::MaybeIncreaseLargestPeerStreamId(
+ QuicStreamId id) {
+ if (QuicUtils::IsBidirectionalStreamId(id)) {
+ return bidirectional_stream_id_manager_.MaybeIncreaseLargestPeerStreamId(
+ id);
+ }
+ return unidirectional_stream_id_manager_.MaybeIncreaseLargestPeerStreamId(id);
+}
+
+void UberQuicStreamIdManager::OnStreamClosed(QuicStreamId id) {
+ if (QuicUtils::IsBidirectionalStreamId(id)) {
+ bidirectional_stream_id_manager_.OnStreamClosed(id);
+ return;
+ }
+ unidirectional_stream_id_manager_.OnStreamClosed(id);
+}
+
+bool UberQuicStreamIdManager::OnMaxStreamIdFrame(
+ const QuicMaxStreamIdFrame& frame) {
+ if (QuicUtils::IsBidirectionalStreamId(frame.max_stream_id)) {
+ return bidirectional_stream_id_manager_.OnMaxStreamIdFrame(frame);
+ }
+ return unidirectional_stream_id_manager_.OnMaxStreamIdFrame(frame);
+}
+
+bool UberQuicStreamIdManager::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ if (QuicUtils::IsBidirectionalStreamId(frame.stream_id)) {
+ return bidirectional_stream_id_manager_.OnStreamIdBlockedFrame(frame);
+ }
+ return unidirectional_stream_id_manager_.OnStreamIdBlockedFrame(frame);
+}
+
+bool UberQuicStreamIdManager::IsIncomingStream(QuicStreamId id) const {
+ if (QuicUtils::IsBidirectionalStreamId(id)) {
+ return bidirectional_stream_id_manager_.IsIncomingStream(id);
+ }
+ return unidirectional_stream_id_manager_.IsIncomingStream(id);
+}
+
+bool UberQuicStreamIdManager::IsAvailableStream(QuicStreamId id) const {
+ if (QuicUtils::IsBidirectionalStreamId(id)) {
+ return bidirectional_stream_id_manager_.IsAvailableStream(id);
+ }
+ return unidirectional_stream_id_manager_.IsAvailableStream(id);
+}
+
+size_t UberQuicStreamIdManager::GetMaxAllowdIncomingBidirectionalStreams()
+ const {
+ return bidirectional_stream_id_manager_.max_allowed_incoming_streams();
+}
+
+size_t UberQuicStreamIdManager::GetMaxAllowdIncomingUnidirectionalStreams()
+ const {
+ return unidirectional_stream_id_manager_.max_allowed_incoming_streams();
+}
+
+void UberQuicStreamIdManager::SetLargestPeerCreatedStreamId(
+ QuicStreamId largest_peer_created_stream_id) {
+ if (QuicUtils::IsBidirectionalStreamId(largest_peer_created_stream_id)) {
+ bidirectional_stream_id_manager_.set_largest_peer_created_stream_id(
+ largest_peer_created_stream_id);
+ return;
+ }
+ unidirectional_stream_id_manager_.set_largest_peer_created_stream_id(
+ largest_peer_created_stream_id);
+}
+
+QuicStreamId UberQuicStreamIdManager::next_outgoing_bidirectional_stream_id()
+ const {
+ return bidirectional_stream_id_manager_.next_outgoing_stream_id();
+}
+
+QuicStreamId UberQuicStreamIdManager::next_outgoing_unidirectional_stream_id()
+ const {
+ return unidirectional_stream_id_manager_.next_outgoing_stream_id();
+}
+
+QuicStreamId
+UberQuicStreamIdManager::max_allowed_outgoing_bidirectional_stream_id() const {
+ return bidirectional_stream_id_manager_.max_allowed_outgoing_stream_id();
+}
+
+QuicStreamId
+UberQuicStreamIdManager::max_allowed_outgoing_unidirectional_stream_id() const {
+ return unidirectional_stream_id_manager_.max_allowed_outgoing_stream_id();
+}
+
+size_t UberQuicStreamIdManager::max_allowed_outgoing_bidirectional_streams()
+ const {
+ return bidirectional_stream_id_manager_.max_allowed_outgoing_streams();
+}
+
+size_t UberQuicStreamIdManager::max_allowed_outgoing_unidirectional_streams()
+ const {
+ return unidirectional_stream_id_manager_.max_allowed_outgoing_streams();
+}
+
+QuicStreamId
+UberQuicStreamIdManager::actual_max_allowed_incoming_bidirectional_stream_id()
+ const {
+ return bidirectional_stream_id_manager_
+ .actual_max_allowed_incoming_stream_id();
+}
+
+QuicStreamId
+UberQuicStreamIdManager::actual_max_allowed_incoming_unidirectional_stream_id()
+ const {
+ return unidirectional_stream_id_manager_
+ .actual_max_allowed_incoming_stream_id();
+}
+
+QuicStreamId UberQuicStreamIdManager::
+ advertised_max_allowed_incoming_bidirectional_stream_id() const {
+ return bidirectional_stream_id_manager_
+ .advertised_max_allowed_incoming_stream_id();
+}
+
+QuicStreamId UberQuicStreamIdManager::
+ advertised_max_allowed_incoming_unidirectional_stream_id() const {
+ return unidirectional_stream_id_manager_
+ .advertised_max_allowed_incoming_stream_id();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h
new file mode 100644
index 00000000000..bf5a5882ba4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h
@@ -0,0 +1,100 @@
+// 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_CORE_UBER_QUIC_STREAM_ID_MANAGER_H_
+#define QUICHE_QUIC_CORE_UBER_QUIC_STREAM_ID_MANAGER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h"
+
+namespace quic {
+
+namespace test {
+class QuicSessionPeer;
+} // namespace test
+
+class QuicSession;
+
+// This class comprises two QuicStreamIdManagers, which manage bidirectional and
+// unidirectional stream IDs, respectively.
+class QUIC_EXPORT_PRIVATE UberQuicStreamIdManager {
+ public:
+ UberQuicStreamIdManager(QuicSession* session,
+ size_t max_open_outgoing_streams,
+ size_t max_open_incoming_streams);
+
+ // Called when a stream with |stream_id| is registered as a static stream.
+ void RegisterStaticStream(QuicStreamId id);
+
+ // Initialize the maximum allowed outgoing stream id, number of streams, and
+ // MAX_STREAM_ID advertisement window.
+ void SetMaxOpenOutgoingStreams(size_t max_streams);
+
+ // Initialize the maximum allowed incoming stream id and number of streams.
+ void SetMaxOpenIncomingStreams(size_t max_streams);
+
+ // Returns true if next outgoing bidirectional stream ID can be allocated.
+ bool CanOpenNextOutgoingBidirectionalStream();
+
+ // Returns true if next outgoing unidirectional stream ID can be allocated.
+ bool CanOpenNextOutgoingUnidirectionalStream();
+
+ // Returns the next outgoing bidirectional stream id.
+ QuicStreamId GetNextOutgoingBidirectionalStreamId();
+
+ // Returns the next outgoing unidirectional stream id.
+ QuicStreamId GetNextOutgoingUnidirectionalStreamId();
+
+ // Returns true if allow to open the incoming |id|.
+ bool MaybeIncreaseLargestPeerStreamId(QuicStreamId id);
+
+ // Called when |id| is released.
+ void OnStreamClosed(QuicStreamId id);
+
+ // Called when a MAX_STREAM_ID frame is received.
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame);
+
+ // Called when a STREAM_ID_BLOCKED frame is received.
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame);
+
+ // Return true if |id| is peer initiated.
+ bool IsIncomingStream(QuicStreamId id) const;
+
+ // Returns true if |id| is still available.
+ bool IsAvailableStream(QuicStreamId id) const;
+
+ size_t GetMaxAllowdIncomingBidirectionalStreams() const;
+
+ size_t GetMaxAllowdIncomingUnidirectionalStreams() const;
+
+ void SetLargestPeerCreatedStreamId(
+ QuicStreamId largest_peer_created_stream_id);
+
+ QuicStreamId next_outgoing_bidirectional_stream_id() const;
+ QuicStreamId next_outgoing_unidirectional_stream_id() const;
+
+ QuicStreamId max_allowed_outgoing_bidirectional_stream_id() const;
+ QuicStreamId max_allowed_outgoing_unidirectional_stream_id() const;
+
+ size_t max_allowed_outgoing_bidirectional_streams() const;
+ size_t max_allowed_outgoing_unidirectional_streams() const;
+
+ QuicStreamId actual_max_allowed_incoming_bidirectional_stream_id() const;
+ QuicStreamId actual_max_allowed_incoming_unidirectional_stream_id() const;
+
+ QuicStreamId advertised_max_allowed_incoming_bidirectional_stream_id() const;
+ QuicStreamId advertised_max_allowed_incoming_unidirectional_stream_id() const;
+
+ private:
+ friend class test::QuicSessionPeer;
+
+ // Manages stream IDs of bidirectional streams.
+ QuicStreamIdManager bidirectional_stream_id_manager_;
+
+ // Manages stream IDs of unidirectional streams.
+ QuicStreamIdManager unidirectional_stream_id_manager_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_UBER_QUIC_STREAM_ID_MANAGER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc
new file mode 100644
index 00000000000..929d0a1cc8f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc
@@ -0,0 +1,323 @@
+// 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 "net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class UberQuicStreamIdManagerTest : public QuicTestWithParam<Perspective> {
+ public:
+ bool SaveControlFrame(const QuicFrame& frame) {
+ frame_ = frame;
+ return true;
+ }
+
+ protected:
+ UberQuicStreamIdManagerTest()
+ : connection_(new MockQuicConnection(
+ &helper_,
+ &alarm_factory_,
+ GetParam(),
+ ParsedQuicVersionVector(
+ {{PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_99}}))) {
+ session_ = QuicMakeUnique<StrictMock<MockQuicSession>>(connection_);
+ manager_ = QuicSessionPeer::v99_streamid_manager(session_.get());
+ }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT) +
+ kV99StreamIdIncrement * n;
+ }
+
+ QuicStreamId GetNthClientInitiatedUnidirectionalId(int n) {
+ return QuicUtils::GetFirstUnidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT) +
+ kV99StreamIdIncrement * n;
+ }
+
+ QuicStreamId GetNthServerInitiatedBidirectionalId(int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_SERVER) +
+ kV99StreamIdIncrement * n;
+ }
+
+ QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
+ return QuicUtils::GetFirstUnidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_SERVER) +
+ kV99StreamIdIncrement * n;
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnection* connection_;
+ std::unique_ptr<StrictMock<MockQuicSession>> session_;
+ UberQuicStreamIdManager* manager_;
+ QuicFrame frame_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ UberQuicStreamIdManagerTest,
+ ::testing::ValuesIn({Perspective::IS_CLIENT,
+ Perspective::IS_SERVER}));
+
+TEST_P(UberQuicStreamIdManagerTest, Initialization) {
+ if (GetParam() == Perspective::IS_SERVER) {
+ EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0),
+ manager_->next_outgoing_bidirectional_stream_id());
+ EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(0),
+ manager_->next_outgoing_unidirectional_stream_id());
+ } else {
+ EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0),
+ manager_->next_outgoing_bidirectional_stream_id());
+ EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(0),
+ manager_->next_outgoing_unidirectional_stream_id());
+ }
+}
+
+TEST_P(UberQuicStreamIdManagerTest, RegisterStaticStream) {
+ QuicStreamId first_incoming_bidirectional_stream_id =
+ GetParam() == Perspective::IS_SERVER
+ ? GetNthClientInitiatedBidirectionalId(0)
+ : GetNthServerInitiatedBidirectionalId(0);
+ QuicStreamId first_incoming_unidirectional_stream_id =
+ GetParam() == Perspective::IS_SERVER
+ ? GetNthClientInitiatedUnidirectionalId(0)
+ : GetNthServerInitiatedUnidirectionalId(0);
+
+ QuicStreamId actual_max_allowed_incoming_bidirectional_stream_id =
+ manager_->actual_max_allowed_incoming_bidirectional_stream_id();
+ QuicStreamId actual_max_allowed_incoming_unidirectional_stream_id =
+ manager_->actual_max_allowed_incoming_unidirectional_stream_id();
+ manager_->RegisterStaticStream(first_incoming_bidirectional_stream_id);
+ // Verify actual_max_allowed_incoming_bidirectional_stream_id increases.
+ EXPECT_EQ(actual_max_allowed_incoming_bidirectional_stream_id +
+ kV99StreamIdIncrement,
+ manager_->actual_max_allowed_incoming_bidirectional_stream_id());
+ // Verify actual_max_allowed_incoming_unidirectional_stream_id does not
+ // change.
+ EXPECT_EQ(actual_max_allowed_incoming_unidirectional_stream_id,
+ manager_->actual_max_allowed_incoming_unidirectional_stream_id());
+
+ manager_->RegisterStaticStream(first_incoming_unidirectional_stream_id);
+ EXPECT_EQ(actual_max_allowed_incoming_bidirectional_stream_id +
+ kV99StreamIdIncrement,
+ manager_->actual_max_allowed_incoming_bidirectional_stream_id());
+ EXPECT_EQ(actual_max_allowed_incoming_unidirectional_stream_id +
+ kV99StreamIdIncrement,
+ manager_->actual_max_allowed_incoming_unidirectional_stream_id());
+}
+
+TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreams) {
+ const size_t kNumMaxOutgoingStream = 123;
+ manager_->SetMaxOpenOutgoingStreams(kNumMaxOutgoingStream);
+ EXPECT_EQ(kNumMaxOutgoingStream,
+ manager_->max_allowed_outgoing_bidirectional_streams());
+ EXPECT_EQ(kNumMaxOutgoingStream,
+ manager_->max_allowed_outgoing_unidirectional_streams());
+}
+
+TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenIncomingStreams) {
+ const size_t kNumMaxIncomingStreams = 456;
+ manager_->SetMaxOpenIncomingStreams(kNumMaxIncomingStreams);
+ EXPECT_EQ(kNumMaxIncomingStreams,
+ manager_->GetMaxAllowdIncomingBidirectionalStreams());
+ EXPECT_EQ(kNumMaxIncomingStreams,
+ manager_->GetMaxAllowdIncomingUnidirectionalStreams());
+ EXPECT_EQ(
+ manager_->actual_max_allowed_incoming_bidirectional_stream_id(),
+ manager_->advertised_max_allowed_incoming_bidirectional_stream_id());
+ EXPECT_EQ(
+ manager_->actual_max_allowed_incoming_unidirectional_stream_id(),
+ manager_->advertised_max_allowed_incoming_unidirectional_stream_id());
+
+ QuicStreamId first_incoming_bidirectional_stream_id =
+ GetParam() == Perspective::IS_SERVER
+ ? GetNthClientInitiatedBidirectionalId(0)
+ : GetNthServerInitiatedBidirectionalId(0);
+ QuicStreamId first_incoming_unidirectional_stream_id =
+ GetParam() == Perspective::IS_SERVER
+ ? GetNthClientInitiatedUnidirectionalId(0)
+ : GetNthServerInitiatedUnidirectionalId(0);
+ EXPECT_EQ(first_incoming_bidirectional_stream_id +
+ (kNumMaxIncomingStreams - 1) * kV99StreamIdIncrement,
+ manager_->actual_max_allowed_incoming_bidirectional_stream_id());
+ EXPECT_EQ(first_incoming_unidirectional_stream_id +
+ (kNumMaxIncomingStreams - 1) * kV99StreamIdIncrement,
+ manager_->actual_max_allowed_incoming_unidirectional_stream_id());
+}
+
+TEST_P(UberQuicStreamIdManagerTest, GetNextOutgoingStreamId) {
+ if (GetParam() == Perspective::IS_SERVER) {
+ EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0),
+ manager_->GetNextOutgoingBidirectionalStreamId());
+ EXPECT_EQ(GetNthServerInitiatedBidirectionalId(1),
+ manager_->GetNextOutgoingBidirectionalStreamId());
+ EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(0),
+ manager_->GetNextOutgoingUnidirectionalStreamId());
+ EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(1),
+ manager_->GetNextOutgoingUnidirectionalStreamId());
+ } else {
+ EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0),
+ manager_->GetNextOutgoingBidirectionalStreamId());
+ EXPECT_EQ(GetNthClientInitiatedBidirectionalId(1),
+ manager_->GetNextOutgoingBidirectionalStreamId());
+ EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(0),
+ manager_->GetNextOutgoingUnidirectionalStreamId());
+ EXPECT_EQ(GetNthClientInitiatedUnidirectionalId(1),
+ manager_->GetNextOutgoingUnidirectionalStreamId());
+ }
+}
+
+TEST_P(UberQuicStreamIdManagerTest, AvailableStreams) {
+ if (GetParam() == Perspective::IS_SERVER) {
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+ GetNthClientInitiatedBidirectionalId(3)));
+ EXPECT_TRUE(
+ manager_->IsAvailableStream(GetNthClientInitiatedBidirectionalId(1)));
+ EXPECT_TRUE(
+ manager_->IsAvailableStream(GetNthClientInitiatedBidirectionalId(2)));
+
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+ GetNthClientInitiatedUnidirectionalId(3)));
+ EXPECT_TRUE(
+ manager_->IsAvailableStream(GetNthClientInitiatedUnidirectionalId(1)));
+ EXPECT_TRUE(
+ manager_->IsAvailableStream(GetNthClientInitiatedUnidirectionalId(2)));
+ } else {
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+ GetNthServerInitiatedBidirectionalId(3)));
+ EXPECT_TRUE(
+ manager_->IsAvailableStream(GetNthServerInitiatedBidirectionalId(1)));
+ EXPECT_TRUE(
+ manager_->IsAvailableStream(GetNthServerInitiatedBidirectionalId(2)));
+
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+ GetNthServerInitiatedUnidirectionalId(3)));
+ EXPECT_TRUE(
+ manager_->IsAvailableStream(GetNthServerInitiatedUnidirectionalId(1)));
+ EXPECT_TRUE(
+ manager_->IsAvailableStream(GetNthServerInitiatedUnidirectionalId(2)));
+ }
+}
+
+TEST_P(UberQuicStreamIdManagerTest, MaybeIncreaseLargestPeerStreamId) {
+ EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+ manager_->actual_max_allowed_incoming_bidirectional_stream_id()));
+ EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+ manager_->actual_max_allowed_incoming_unidirectional_stream_id()));
+
+ std::string error_details = GetParam() == Perspective::IS_SERVER
+ ? "Stream id 404 above 400"
+ : "Stream id 401 above 397";
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _));
+ EXPECT_FALSE(manager_->MaybeIncreaseLargestPeerStreamId(
+ manager_->actual_max_allowed_incoming_bidirectional_stream_id() +
+ kV99StreamIdIncrement));
+ error_details = GetParam() == Perspective::IS_SERVER
+ ? "Stream id 402 above 398"
+ : "Stream id 403 above 399";
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID, error_details, _));
+ EXPECT_FALSE(manager_->MaybeIncreaseLargestPeerStreamId(
+ manager_->actual_max_allowed_incoming_unidirectional_stream_id() +
+ kV99StreamIdIncrement));
+}
+
+TEST_P(UberQuicStreamIdManagerTest, OnMaxStreamIdFrame) {
+ QuicStreamId max_allowed_outgoing_bidirectional_stream_id =
+ manager_->max_allowed_outgoing_bidirectional_stream_id();
+ QuicStreamId max_allowed_outgoing_unidirectional_stream_id =
+ manager_->max_allowed_outgoing_unidirectional_stream_id();
+
+ QuicMaxStreamIdFrame frame(kInvalidControlFrameId,
+ max_allowed_outgoing_bidirectional_stream_id);
+ EXPECT_TRUE(manager_->OnMaxStreamIdFrame(frame));
+ EXPECT_EQ(max_allowed_outgoing_bidirectional_stream_id,
+ manager_->max_allowed_outgoing_bidirectional_stream_id());
+ frame.max_stream_id = max_allowed_outgoing_unidirectional_stream_id;
+ EXPECT_TRUE(manager_->OnMaxStreamIdFrame(frame));
+ EXPECT_EQ(max_allowed_outgoing_unidirectional_stream_id,
+ manager_->max_allowed_outgoing_unidirectional_stream_id());
+
+ frame.max_stream_id =
+ max_allowed_outgoing_bidirectional_stream_id + kV99StreamIdIncrement;
+ EXPECT_TRUE(manager_->OnMaxStreamIdFrame(frame));
+ EXPECT_EQ(
+ max_allowed_outgoing_bidirectional_stream_id + kV99StreamIdIncrement,
+ manager_->max_allowed_outgoing_bidirectional_stream_id());
+ EXPECT_EQ(max_allowed_outgoing_unidirectional_stream_id,
+ manager_->max_allowed_outgoing_unidirectional_stream_id());
+
+ frame.max_stream_id =
+ max_allowed_outgoing_unidirectional_stream_id + kV99StreamIdIncrement;
+ EXPECT_TRUE(manager_->OnMaxStreamIdFrame(frame));
+ EXPECT_EQ(
+ max_allowed_outgoing_bidirectional_stream_id + kV99StreamIdIncrement,
+ manager_->max_allowed_outgoing_bidirectional_stream_id());
+ EXPECT_EQ(
+ max_allowed_outgoing_unidirectional_stream_id + kV99StreamIdIncrement,
+ manager_->max_allowed_outgoing_unidirectional_stream_id());
+}
+
+TEST_P(UberQuicStreamIdManagerTest, OnStreamIdBlockedFrame) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(
+ Invoke(this, &UberQuicStreamIdManagerTest::SaveControlFrame));
+
+ QuicStreamId stream_id =
+ manager_->advertised_max_allowed_incoming_bidirectional_stream_id() -
+ kV99StreamIdIncrement;
+ QuicStreamIdBlockedFrame frame(kInvalidControlFrameId, stream_id);
+ session_->OnStreamIdBlockedFrame(frame);
+ EXPECT_EQ(MAX_STREAM_ID_FRAME, frame_.type);
+ EXPECT_EQ(manager_->actual_max_allowed_incoming_bidirectional_stream_id(),
+ frame_.max_stream_id_frame.max_stream_id);
+
+ frame.stream_id =
+ manager_->advertised_max_allowed_incoming_unidirectional_stream_id() -
+ kV99StreamIdIncrement;
+ session_->OnStreamIdBlockedFrame(frame);
+ EXPECT_EQ(MAX_STREAM_ID_FRAME, frame_.type);
+ EXPECT_EQ(manager_->actual_max_allowed_incoming_unidirectional_stream_id(),
+ frame_.max_stream_id_frame.max_stream_id);
+}
+
+TEST_P(UberQuicStreamIdManagerTest, IsIncomingStream) {
+ if (GetParam() == Perspective::IS_SERVER) {
+ EXPECT_TRUE(
+ manager_->IsIncomingStream(GetNthClientInitiatedBidirectionalId(0)));
+ EXPECT_TRUE(
+ manager_->IsIncomingStream(GetNthClientInitiatedUnidirectionalId(0)));
+ EXPECT_FALSE(
+ manager_->IsIncomingStream(GetNthServerInitiatedBidirectionalId(0)));
+ EXPECT_FALSE(
+ manager_->IsIncomingStream(GetNthServerInitiatedUnidirectionalId(0)));
+ } else {
+ EXPECT_FALSE(
+ manager_->IsIncomingStream(GetNthClientInitiatedBidirectionalId(0)));
+ EXPECT_FALSE(
+ manager_->IsIncomingStream(GetNthClientInitiatedUnidirectionalId(0)));
+ EXPECT_TRUE(
+ manager_->IsIncomingStream(GetNthServerInitiatedBidirectionalId(0)));
+ EXPECT_TRUE(
+ manager_->IsIncomingStream(GetNthServerInitiatedUnidirectionalId(0)));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc
new file mode 100644
index 00000000000..e8876369c8b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.cc
@@ -0,0 +1,210 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/uber_received_packet_manager.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+UberReceivedPacketManager::UberReceivedPacketManager(QuicConnectionStats* stats)
+ : supports_multiple_packet_number_spaces_(false) {
+ for (auto& received_packet_manager : received_packet_managers_) {
+ received_packet_manager.set_connection_stats(stats);
+ }
+}
+
+UberReceivedPacketManager::~UberReceivedPacketManager() {}
+
+void UberReceivedPacketManager::SetFromConfig(const QuicConfig& config,
+ Perspective perspective) {
+ for (auto& received_packet_manager : received_packet_managers_) {
+ received_packet_manager.SetFromConfig(config, perspective);
+ }
+}
+
+bool UberReceivedPacketManager::IsAwaitingPacket(
+ EncryptionLevel decrypted_packet_level,
+ QuicPacketNumber packet_number) const {
+ if (!supports_multiple_packet_number_spaces_) {
+ return received_packet_managers_[0].IsAwaitingPacket(packet_number);
+ }
+ return received_packet_managers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_packet_level)]
+ .IsAwaitingPacket(packet_number);
+}
+
+const QuicFrame UberReceivedPacketManager::GetUpdatedAckFrame(
+ PacketNumberSpace packet_number_space,
+ QuicTime approximate_now) {
+ if (!supports_multiple_packet_number_spaces_) {
+ return received_packet_managers_[0].GetUpdatedAckFrame(approximate_now);
+ }
+ return received_packet_managers_[packet_number_space].GetUpdatedAckFrame(
+ approximate_now);
+}
+
+void UberReceivedPacketManager::RecordPacketReceived(
+ EncryptionLevel decrypted_packet_level,
+ const QuicPacketHeader& header,
+ QuicTime receipt_time) {
+ if (!supports_multiple_packet_number_spaces_) {
+ received_packet_managers_[0].RecordPacketReceived(header, receipt_time);
+ return;
+ }
+ received_packet_managers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_packet_level)]
+ .RecordPacketReceived(header, receipt_time);
+}
+
+void UberReceivedPacketManager::DontWaitForPacketsBefore(
+ EncryptionLevel decrypted_packet_level,
+ QuicPacketNumber least_unacked) {
+ if (!supports_multiple_packet_number_spaces_) {
+ received_packet_managers_[0].DontWaitForPacketsBefore(least_unacked);
+ return;
+ }
+ received_packet_managers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_packet_level)]
+ .DontWaitForPacketsBefore(least_unacked);
+}
+
+void UberReceivedPacketManager::MaybeUpdateAckTimeout(
+ bool should_last_packet_instigate_acks,
+ EncryptionLevel decrypted_packet_level,
+ QuicPacketNumber last_received_packet_number,
+ QuicTime time_of_last_received_packet,
+ QuicTime now,
+ const RttStats* rtt_stats,
+ QuicTime::Delta delayed_ack_time) {
+ if (!supports_multiple_packet_number_spaces_) {
+ received_packet_managers_[0].MaybeUpdateAckTimeout(
+ should_last_packet_instigate_acks, last_received_packet_number,
+ time_of_last_received_packet, now, rtt_stats, delayed_ack_time);
+ return;
+ }
+ received_packet_managers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_packet_level)]
+ .MaybeUpdateAckTimeout(
+ should_last_packet_instigate_acks, last_received_packet_number,
+ time_of_last_received_packet, now, rtt_stats, delayed_ack_time);
+}
+
+void UberReceivedPacketManager::ResetAckStates(
+ EncryptionLevel encryption_level) {
+ if (!supports_multiple_packet_number_spaces_) {
+ received_packet_managers_[0].ResetAckStates();
+ return;
+ }
+ received_packet_managers_[QuicUtils::GetPacketNumberSpace(encryption_level)]
+ .ResetAckStates();
+}
+
+void UberReceivedPacketManager::EnableMultiplePacketNumberSpacesSupport() {
+ if (supports_multiple_packet_number_spaces_) {
+ QUIC_BUG << "Multiple packet number spaces has already been enabled";
+ return;
+ }
+ if (received_packet_managers_[0].GetLargestObserved().IsInitialized()) {
+ QUIC_BUG << "Try to enable multiple packet number spaces support after any "
+ "packet has been received.";
+ return;
+ }
+
+ supports_multiple_packet_number_spaces_ = true;
+}
+
+bool UberReceivedPacketManager::IsAckFrameUpdated() const {
+ if (!supports_multiple_packet_number_spaces_) {
+ return received_packet_managers_[0].ack_frame_updated();
+ }
+ for (const auto& received_packet_manager : received_packet_managers_) {
+ if (received_packet_manager.ack_frame_updated()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicPacketNumber UberReceivedPacketManager::GetLargestObserved(
+ EncryptionLevel decrypted_packet_level) const {
+ if (!supports_multiple_packet_number_spaces_) {
+ return received_packet_managers_[0].GetLargestObserved();
+ }
+ return received_packet_managers_[QuicUtils::GetPacketNumberSpace(
+ decrypted_packet_level)]
+ .GetLargestObserved();
+}
+
+QuicTime UberReceivedPacketManager::GetAckTimeout(
+ PacketNumberSpace packet_number_space) const {
+ if (!supports_multiple_packet_number_spaces_) {
+ return received_packet_managers_[0].ack_timeout();
+ }
+ return received_packet_managers_[packet_number_space].ack_timeout();
+}
+
+QuicTime UberReceivedPacketManager::GetEarliestAckTimeout() const {
+ QuicTime ack_timeout = QuicTime::Zero();
+ // Returns the earliest non-zero ack timeout.
+ for (const auto& received_packet_manager : received_packet_managers_) {
+ const QuicTime timeout = received_packet_manager.ack_timeout();
+ if (!ack_timeout.IsInitialized()) {
+ ack_timeout = timeout;
+ continue;
+ }
+ if (timeout.IsInitialized()) {
+ ack_timeout = std::min(ack_timeout, timeout);
+ }
+ }
+ return ack_timeout;
+}
+
+QuicPacketNumber UberReceivedPacketManager::peer_least_packet_awaiting_ack()
+ const {
+ DCHECK(!supports_multiple_packet_number_spaces_);
+ return received_packet_managers_[0].peer_least_packet_awaiting_ack();
+}
+
+size_t UberReceivedPacketManager::min_received_before_ack_decimation() const {
+ return received_packet_managers_[0].min_received_before_ack_decimation();
+}
+
+void UberReceivedPacketManager::set_min_received_before_ack_decimation(
+ size_t new_value) {
+ for (auto& received_packet_manager : received_packet_managers_) {
+ received_packet_manager.set_min_received_before_ack_decimation(new_value);
+ }
+}
+
+size_t UberReceivedPacketManager::ack_frequency_before_ack_decimation() const {
+ return received_packet_managers_[0].ack_frequency_before_ack_decimation();
+}
+
+void UberReceivedPacketManager::set_ack_frequency_before_ack_decimation(
+ size_t new_value) {
+ for (auto& received_packet_manager : received_packet_managers_) {
+ received_packet_manager.set_ack_frequency_before_ack_decimation(new_value);
+ }
+}
+
+const QuicAckFrame& UberReceivedPacketManager::ack_frame() const {
+ DCHECK(!supports_multiple_packet_number_spaces_);
+ return received_packet_managers_[0].ack_frame();
+}
+
+void UberReceivedPacketManager::set_max_ack_ranges(size_t max_ack_ranges) {
+ for (auto& received_packet_manager : received_packet_managers_) {
+ received_packet_manager.set_max_ack_ranges(max_ack_ranges);
+ }
+}
+
+void UberReceivedPacketManager::set_save_timestamps(bool save_timestamps) {
+ for (auto& received_packet_manager : received_packet_managers_) {
+ received_packet_manager.set_save_timestamps(save_timestamps);
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.h b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.h
new file mode 100644
index 00000000000..5c813e21f6d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager.h
@@ -0,0 +1,107 @@
+// Copyright 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.
+
+#ifndef QUICHE_QUIC_CORE_UBER_RECEIVED_PACKET_MANAGER_H_
+#define QUICHE_QUIC_CORE_UBER_RECEIVED_PACKET_MANAGER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_received_packet_manager.h"
+
+namespace quic {
+
+// This class comprises multiple received packet managers, one per packet number
+// space. Please note, if multiple packet number spaces is not supported, only
+// one received packet manager will be used.
+class QUIC_EXPORT_PRIVATE UberReceivedPacketManager {
+ public:
+ explicit UberReceivedPacketManager(QuicConnectionStats* stats);
+ UberReceivedPacketManager(const UberReceivedPacketManager&) = delete;
+ UberReceivedPacketManager& operator=(const UberReceivedPacketManager&) =
+ delete;
+ virtual ~UberReceivedPacketManager();
+
+ void SetFromConfig(const QuicConfig& config, Perspective perspective);
+
+ // Checks if we are still waiting for the packet with |packet_number|.
+ bool IsAwaitingPacket(EncryptionLevel decrypted_packet_level,
+ QuicPacketNumber packet_number) const;
+
+ // Called after a packet has been successfully decrypted and its header has
+ // been parsed.
+ void RecordPacketReceived(EncryptionLevel decrypted_packet_level,
+ const QuicPacketHeader& header,
+ QuicTime receipt_time);
+
+ // Retrieves a frame containing a QuicAckFrame. The ack frame must be
+ // serialized before another packet is received, or it will change.
+ const QuicFrame GetUpdatedAckFrame(PacketNumberSpace packet_number_space,
+ QuicTime approximate_now);
+
+ // Stop ACKing packets before |least_unacked|.
+ void DontWaitForPacketsBefore(EncryptionLevel decrypted_packet_level,
+ QuicPacketNumber least_unacked);
+
+ // Called after header of last received packet has been successfully processed
+ // to update ACK timeout.
+ void MaybeUpdateAckTimeout(bool should_last_packet_instigate_acks,
+ EncryptionLevel decrypted_packet_level,
+ QuicPacketNumber last_received_packet_number,
+ QuicTime time_of_last_received_packet,
+ QuicTime now,
+ const RttStats* rtt_stats,
+ QuicTime::Delta delayed_ack_time);
+
+ // Resets ACK related states, called after an ACK is successfully sent.
+ void ResetAckStates(EncryptionLevel encryption_level);
+
+ // Called to enable multiple packet number support.
+ void EnableMultiplePacketNumberSpacesSupport();
+
+ // Returns true if ACK frame has been updated since GetUpdatedAckFrame was
+ // last called.
+ bool IsAckFrameUpdated() const;
+
+ // Returns the largest received packet number.
+ QuicPacketNumber GetLargestObserved(
+ EncryptionLevel decrypted_packet_level) const;
+
+ // Returns ACK timeout of |packet_number_space|.
+ QuicTime GetAckTimeout(PacketNumberSpace packet_number_space) const;
+
+ // Get the earliest ack_timeout of all packet number spaces.
+ QuicTime GetEarliestAckTimeout() const;
+
+ QuicPacketNumber peer_least_packet_awaiting_ack() const;
+
+ size_t min_received_before_ack_decimation() const;
+ void set_min_received_before_ack_decimation(size_t new_value);
+
+ size_t ack_frequency_before_ack_decimation() const;
+ void set_ack_frequency_before_ack_decimation(size_t new_value);
+
+ bool supports_multiple_packet_number_spaces() const {
+ return supports_multiple_packet_number_spaces_;
+ }
+
+ // For logging purposes.
+ const QuicAckFrame& ack_frame() const;
+
+ void set_max_ack_ranges(size_t max_ack_ranges);
+
+ void set_save_timestamps(bool save_timestamps);
+
+ private:
+ friend class test::QuicConnectionPeer;
+ friend class test::UberReceivedPacketManagerPeer;
+
+ // One received packet manager per packet number space. If
+ // supports_multiple_packet_number_spaces_ is false, only the first (0 index)
+ // received_packet_manager is used.
+ QuicReceivedPacketManager received_packet_managers_[NUM_PACKET_NUMBER_SPACES];
+
+ bool supports_multiple_packet_number_spaces_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_UBER_RECEIVED_PACKET_MANAGER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc
new file mode 100644
index 00000000000..b717178248a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc
@@ -0,0 +1,799 @@
+// Copyright 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 "net/third_party/quiche/src/quic/core/uber_received_packet_manager.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+namespace test {
+
+class UberReceivedPacketManagerPeer {
+ public:
+ static void SetAckMode(UberReceivedPacketManager* manager, AckMode ack_mode) {
+ for (auto& received_packet_manager : manager->received_packet_managers_) {
+ received_packet_manager.ack_mode_ = ack_mode;
+ }
+ }
+
+ static void SetFastAckAfterQuiescence(UberReceivedPacketManager* manager,
+ bool fast_ack_after_quiescence) {
+ for (auto& received_packet_manager : manager->received_packet_managers_) {
+ received_packet_manager.fast_ack_after_quiescence_ =
+ fast_ack_after_quiescence;
+ }
+ }
+
+ static void SetAckDecimationDelay(UberReceivedPacketManager* manager,
+ float ack_decimation_delay) {
+ for (auto& received_packet_manager : manager->received_packet_managers_) {
+ received_packet_manager.ack_decimation_delay_ = ack_decimation_delay;
+ }
+ }
+};
+
+namespace {
+
+const bool kInstigateAck = true;
+const QuicTime::Delta kMinRttMs = QuicTime::Delta::FromMilliseconds(40);
+const QuicTime::Delta kDelayedAckTime =
+ QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+
+class UberReceivedPacketManagerTest : public QuicTest {
+ protected:
+ UberReceivedPacketManagerTest() {
+ SetQuicReloadableFlag(quic_deprecate_ack_bundling_mode, true);
+ SetQuicReloadableFlag(quic_rpm_decides_when_to_send_acks, true);
+ manager_ = QuicMakeUnique<UberReceivedPacketManager>(&stats_);
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ rtt_stats_.UpdateRtt(kMinRttMs, QuicTime::Delta::Zero(), QuicTime::Zero());
+ manager_->set_save_timestamps(true);
+ }
+
+ void RecordPacketReceipt(uint64_t packet_number) {
+ RecordPacketReceipt(ENCRYPTION_FORWARD_SECURE, packet_number);
+ }
+
+ void RecordPacketReceipt(uint64_t packet_number, QuicTime receipt_time) {
+ RecordPacketReceipt(ENCRYPTION_FORWARD_SECURE, packet_number, receipt_time);
+ }
+
+ void RecordPacketReceipt(EncryptionLevel decrypted_packet_level,
+ uint64_t packet_number) {
+ RecordPacketReceipt(decrypted_packet_level, packet_number,
+ QuicTime::Zero());
+ }
+
+ void RecordPacketReceipt(EncryptionLevel decrypted_packet_level,
+ uint64_t packet_number,
+ QuicTime receipt_time) {
+ QuicPacketHeader header;
+ header.packet_number = QuicPacketNumber(packet_number);
+ manager_->RecordPacketReceived(decrypted_packet_level, header,
+ receipt_time);
+ }
+
+ bool HasPendingAck() {
+ if (!manager_->supports_multiple_packet_number_spaces()) {
+ return manager_->GetAckTimeout(APPLICATION_DATA).IsInitialized();
+ }
+ return manager_->GetEarliestAckTimeout().IsInitialized();
+ }
+
+ void MaybeUpdateAckTimeout(bool should_last_packet_instigate_acks,
+ uint64_t last_received_packet_number) {
+ MaybeUpdateAckTimeout(should_last_packet_instigate_acks,
+ ENCRYPTION_FORWARD_SECURE,
+ last_received_packet_number);
+ }
+
+ void MaybeUpdateAckTimeout(bool should_last_packet_instigate_acks,
+ EncryptionLevel decrypted_packet_level,
+ uint64_t last_received_packet_number) {
+ manager_->MaybeUpdateAckTimeout(
+ should_last_packet_instigate_acks, decrypted_packet_level,
+ QuicPacketNumber(last_received_packet_number), clock_.ApproximateNow(),
+ clock_.ApproximateNow(), &rtt_stats_, kDelayedAckTime);
+ }
+
+ void CheckAckTimeout(QuicTime time) {
+ DCHECK(HasPendingAck());
+ if (!manager_->supports_multiple_packet_number_spaces()) {
+ DCHECK(manager_->GetAckTimeout(APPLICATION_DATA) == time);
+ if (time <= clock_.ApproximateNow()) {
+ // ACK timeout expires, send an ACK.
+ manager_->ResetAckStates(ENCRYPTION_FORWARD_SECURE);
+ DCHECK(!HasPendingAck());
+ }
+ return;
+ }
+ DCHECK(manager_->GetEarliestAckTimeout() == time);
+ // Send all expired ACKs.
+ for (int8_t i = INITIAL_DATA; i < NUM_PACKET_NUMBER_SPACES; ++i) {
+ const QuicTime ack_timeout =
+ manager_->GetAckTimeout(static_cast<PacketNumberSpace>(i));
+ if (!ack_timeout.IsInitialized() ||
+ ack_timeout > clock_.ApproximateNow()) {
+ continue;
+ }
+ manager_->ResetAckStates(
+ QuicUtils::GetEncryptionLevel(static_cast<PacketNumberSpace>(i)));
+ }
+ }
+
+ MockClock clock_;
+ RttStats rtt_stats_;
+ QuicConnectionStats stats_;
+ std::unique_ptr<UberReceivedPacketManager> manager_;
+};
+
+TEST_F(UberReceivedPacketManagerTest, DontWaitForPacketsBefore) {
+ RecordPacketReceipt(2);
+ RecordPacketReceipt(7);
+ EXPECT_TRUE(manager_->IsAwaitingPacket(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(3u)));
+ EXPECT_TRUE(manager_->IsAwaitingPacket(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(6u)));
+ manager_->DontWaitForPacketsBefore(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(4));
+ EXPECT_FALSE(manager_->IsAwaitingPacket(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(3u)));
+ EXPECT_TRUE(manager_->IsAwaitingPacket(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(6u)));
+}
+
+TEST_F(UberReceivedPacketManagerTest, GetUpdatedAckFrame) {
+ QuicTime two_ms = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2);
+ EXPECT_FALSE(manager_->IsAckFrameUpdated());
+ RecordPacketReceipt(2, two_ms);
+ EXPECT_TRUE(manager_->IsAckFrameUpdated());
+
+ QuicFrame ack =
+ manager_->GetUpdatedAckFrame(APPLICATION_DATA, QuicTime::Zero());
+ manager_->ResetAckStates(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_FALSE(manager_->IsAckFrameUpdated());
+ // When UpdateReceivedPacketInfo with a time earlier than the time of the
+ // largest observed packet, make sure that the delta is 0, not negative.
+ EXPECT_EQ(QuicTime::Delta::Zero(), ack.ack_frame->ack_delay_time);
+ EXPECT_EQ(1u, ack.ack_frame->received_packet_times.size());
+
+ QuicTime four_ms = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(4);
+ ack = manager_->GetUpdatedAckFrame(APPLICATION_DATA, four_ms);
+ manager_->ResetAckStates(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_FALSE(manager_->IsAckFrameUpdated());
+ // When UpdateReceivedPacketInfo after not having received a new packet,
+ // the delta should still be accurate.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2),
+ ack.ack_frame->ack_delay_time);
+ // And received packet times won't have change.
+ EXPECT_EQ(1u, ack.ack_frame->received_packet_times.size());
+
+ RecordPacketReceipt(999, two_ms);
+ RecordPacketReceipt(4, two_ms);
+ RecordPacketReceipt(1000, two_ms);
+ EXPECT_TRUE(manager_->IsAckFrameUpdated());
+ ack = manager_->GetUpdatedAckFrame(APPLICATION_DATA, two_ms);
+ manager_->ResetAckStates(ENCRYPTION_FORWARD_SECURE);
+ EXPECT_FALSE(manager_->IsAckFrameUpdated());
+ // UpdateReceivedPacketInfo should discard any times which can't be
+ // expressed on the wire.
+ EXPECT_EQ(2u, ack.ack_frame->received_packet_times.size());
+}
+
+TEST_F(UberReceivedPacketManagerTest, UpdateReceivedConnectionStats) {
+ EXPECT_FALSE(manager_->IsAckFrameUpdated());
+ RecordPacketReceipt(1);
+ EXPECT_TRUE(manager_->IsAckFrameUpdated());
+ RecordPacketReceipt(6);
+ RecordPacketReceipt(2,
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1));
+
+ EXPECT_EQ(4u, stats_.max_sequence_reordering);
+ EXPECT_EQ(1000, stats_.max_time_reordering_us);
+ EXPECT_EQ(1u, stats_.packets_reordered);
+}
+
+TEST_F(UberReceivedPacketManagerTest, LimitAckRanges) {
+ manager_->set_max_ack_ranges(10);
+ EXPECT_FALSE(manager_->IsAckFrameUpdated());
+ for (int i = 0; i < 100; ++i) {
+ RecordPacketReceipt(1 + 2 * i);
+ EXPECT_TRUE(manager_->IsAckFrameUpdated());
+ manager_->GetUpdatedAckFrame(APPLICATION_DATA, QuicTime::Zero());
+ EXPECT_GE(10u, manager_->ack_frame().packets.NumIntervals());
+ EXPECT_EQ(QuicPacketNumber(1u + 2 * i),
+ manager_->ack_frame().packets.Max());
+ for (int j = 0; j < std::min(10, i + 1); ++j) {
+ ASSERT_GE(i, j);
+ EXPECT_TRUE(manager_->ack_frame().packets.Contains(
+ QuicPacketNumber(1 + (i - j) * 2)));
+ if (i > j) {
+ EXPECT_FALSE(manager_->ack_frame().packets.Contains(
+ QuicPacketNumber((i - j) * 2)));
+ }
+ }
+ }
+}
+
+TEST_F(UberReceivedPacketManagerTest, IgnoreOutOfOrderTimestamps) {
+ EXPECT_FALSE(manager_->IsAckFrameUpdated());
+ RecordPacketReceipt(1, QuicTime::Zero());
+ EXPECT_TRUE(manager_->IsAckFrameUpdated());
+ EXPECT_EQ(1u, manager_->ack_frame().received_packet_times.size());
+ RecordPacketReceipt(2,
+ QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(1));
+ EXPECT_EQ(2u, manager_->ack_frame().received_packet_times.size());
+ RecordPacketReceipt(3, QuicTime::Zero());
+ EXPECT_EQ(2u, manager_->ack_frame().received_packet_times.size());
+}
+
+TEST_F(UberReceivedPacketManagerTest, OutOfOrderReceiptCausesAckSent) {
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(3, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 3);
+ if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+ // Delayed ack is scheduled.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ } else {
+ // Should ack immediately since we have missing packets.
+ CheckAckTimeout(clock_.ApproximateNow());
+ }
+
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 2);
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 1);
+ // Should ack immediately, since this fills the last hole.
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ RecordPacketReceipt(4, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 4);
+ // Delayed ack is scheduled.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+}
+
+TEST_F(UberReceivedPacketManagerTest, OutOfOrderAckReceiptCausesNoAck) {
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 2);
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 1);
+ EXPECT_FALSE(HasPendingAck());
+}
+
+TEST_F(UberReceivedPacketManagerTest, AckReceiptCausesAckSend) {
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 1);
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 2);
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(3, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 3);
+ // Delayed ack is scheduled.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ clock_.AdvanceTime(kDelayedAckTime);
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ RecordPacketReceipt(4, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 4);
+ EXPECT_FALSE(HasPendingAck());
+
+ RecordPacketReceipt(5, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(!kInstigateAck, 5);
+ EXPECT_FALSE(HasPendingAck());
+}
+
+TEST_F(UberReceivedPacketManagerTest, AckSentEveryNthPacket) {
+ EXPECT_FALSE(HasPendingAck());
+ manager_->set_ack_frequency_before_ack_decimation(3);
+
+ // Receives packets 1 - 39.
+ for (size_t i = 1; i <= 39; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 3 == 0) {
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+}
+
+TEST_F(UberReceivedPacketManagerTest, AckDecimationReducesAcks) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetAckMode(manager_.get(),
+ ACK_DECIMATION_WITH_REORDERING);
+
+ // Start ack decimation from 10th packet.
+ manager_->set_min_received_before_ack_decimation(10);
+
+ // Receives packets 1 - 29.
+ for (size_t i = 1; i <= 29; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i <= 10) {
+ // For packets 1-10, ack every 2 packets.
+ if (i % 2 == 0) {
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ continue;
+ }
+ // ack at 20.
+ if (i == 20) {
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kMinRttMs * 0.25);
+ }
+ }
+
+ // We now receive the 30th packet, and so we send an ack.
+ RecordPacketReceipt(30, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 30);
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_F(UberReceivedPacketManagerTest, SendDelayedAfterQuiescence) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetFastAckAfterQuiescence(manager_.get(),
+ true);
+ // The beginning of the connection counts as quiescence.
+ QuicTime ack_time =
+ clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 1);
+ CheckAckTimeout(ack_time);
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Process another packet immediately after sending the ack and expect the
+ // ack timeout to be set delayed ack time in the future.
+ ack_time = clock_.ApproximateNow() + kDelayedAckTime;
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 2);
+ CheckAckTimeout(ack_time);
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(kDelayedAckTime);
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Wait 1 second and enesure the ack timeout is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ RecordPacketReceipt(3, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 3);
+ CheckAckTimeout(ack_time);
+}
+
+TEST_F(UberReceivedPacketManagerTest, SendDelayedAckDecimation) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetAckMode(manager_.get(), ACK_DECIMATION);
+ // The ack time should be based on min_rtt * 1/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (uint64_t i = 1; i < 10; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_F(UberReceivedPacketManagerTest,
+ SendDelayedAckAckDecimationAfterQuiescence) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetAckMode(manager_.get(), ACK_DECIMATION);
+ UberReceivedPacketManagerPeer::SetFastAckAfterQuiescence(manager_.get(),
+ true);
+ // The beginning of the connection counts as quiescence.
+ QuicTime ack_time =
+ clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ RecordPacketReceipt(1, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 1);
+ CheckAckTimeout(ack_time);
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Process another packet immedately after sending the ack and expect the
+ // ack timeout to be set delayed ack time in the future.
+ ack_time = clock_.ApproximateNow() + kDelayedAckTime;
+ RecordPacketReceipt(2, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 2);
+ CheckAckTimeout(ack_time);
+ // Simulate delayed ack alarm firing.
+ clock_.AdvanceTime(kDelayedAckTime);
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Wait 1 second and enesure the ack timeout is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ RecordPacketReceipt(3, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, 3);
+ CheckAckTimeout(ack_time);
+ // Process enough packets to get into ack decimation behavior.
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 4; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+ EXPECT_FALSE(HasPendingAck());
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (uint64_t i = 1; i < 10; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // Wait 1 second and enesure the ack timeout is set to 1ms in the future.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(1);
+ RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10);
+ CheckAckTimeout(ack_time);
+}
+
+TEST_F(UberReceivedPacketManagerTest,
+ SendDelayedAckDecimationUnlimitedAggregation) {
+ EXPECT_FALSE(HasPendingAck());
+ QuicConfig config;
+ QuicTagVector connection_options;
+ connection_options.push_back(kACKD);
+ // No limit on the number of packets received before sending an ack.
+ connection_options.push_back(kAKDU);
+ config.SetConnectionOptionsToSend(connection_options);
+ manager_->SetFromConfig(config, Perspective::IS_CLIENT);
+
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+
+ // Process all the initial packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // 18 packets will not cause an ack to be sent. 19 will because when
+ // stop waiting frames are in use, we ack every 20 packets no matter what.
+ for (int i = 1; i <= 18; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(ack_time);
+}
+
+TEST_F(UberReceivedPacketManagerTest, SendDelayedAckDecimationEighthRtt) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetAckMode(manager_.get(), ACK_DECIMATION);
+ UberReceivedPacketManagerPeer::SetAckDecimationDelay(manager_.get(), 0.125);
+
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125;
+
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (uint64_t i = 1; i < 10; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_F(UberReceivedPacketManagerTest, SendDelayedAckDecimationWithReordering) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetAckMode(manager_.get(),
+ ACK_DECIMATION_WITH_REORDERING);
+
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ // Receive one packet out of order and then the rest in order.
+ // The loop leaves a one packet gap between acks sent to simulate some loss.
+ for (int j = 0; j < 3; ++j) {
+ // Process packet 10 first and ensure the timeout is one eighth min_rtt.
+ RecordPacketReceipt(kFirstDecimatedPacket + 9 + (j * 11),
+ clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 9 + (j * 11));
+ ack_time = clock_.ApproximateNow() + QuicTime::Delta::FromMilliseconds(5);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 0; i < 9; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i + (j * 11),
+ clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck,
+ kFirstDecimatedPacket + i + (j * 11));
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+ }
+}
+
+TEST_F(UberReceivedPacketManagerTest,
+ SendDelayedAckDecimationWithLargeReordering) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetAckMode(manager_.get(),
+ ACK_DECIMATION_WITH_REORDERING);
+ // The ack time should be based on min_rtt/4, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.25;
+
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ RecordPacketReceipt(kFirstDecimatedPacket + 19, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 19);
+ ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125;
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 1; i < 9; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // The next packet received in order will cause an immediate ack, because it
+ // fills a hole.
+ RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10);
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_F(UberReceivedPacketManagerTest,
+ SendDelayedAckDecimationWithReorderingEighthRtt) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetAckMode(manager_.get(),
+ ACK_DECIMATION_WITH_REORDERING);
+ UberReceivedPacketManagerPeer::SetAckDecimationDelay(manager_.get(), 0.125);
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125;
+
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ // Process packet 10 first and ensure the timeout is one eighth min_rtt.
+ RecordPacketReceipt(kFirstDecimatedPacket + 9, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 9);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 1; i < 9; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck + i, kFirstDecimatedPacket);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_F(UberReceivedPacketManagerTest,
+ SendDelayedAckDecimationWithLargeReorderingEighthRtt) {
+ EXPECT_FALSE(HasPendingAck());
+ UberReceivedPacketManagerPeer::SetAckMode(manager_.get(),
+ ACK_DECIMATION_WITH_REORDERING);
+ UberReceivedPacketManagerPeer::SetAckDecimationDelay(manager_.get(), 0.125);
+
+ // The ack time should be based on min_rtt/8, since it's less than the
+ // default delayed ack time.
+ QuicTime ack_time = clock_.ApproximateNow() + kMinRttMs * 0.125;
+ // Process all the packets in order so there aren't missing packets.
+ uint64_t kFirstDecimatedPacket = 101;
+ for (uint64_t i = 1; i < kFirstDecimatedPacket; ++i) {
+ RecordPacketReceipt(i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, i);
+ if (i % 2 == 0) {
+ // Ack every 2 packets by default.
+ CheckAckTimeout(clock_.ApproximateNow());
+ } else {
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+ }
+ }
+
+ RecordPacketReceipt(kFirstDecimatedPacket, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket);
+ CheckAckTimeout(ack_time);
+
+ RecordPacketReceipt(kFirstDecimatedPacket + 19, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 19);
+ CheckAckTimeout(ack_time);
+
+ // The 10th received packet causes an ack to be sent.
+ for (int i = 1; i < 9; ++i) {
+ RecordPacketReceipt(kFirstDecimatedPacket + i, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + i);
+ }
+ CheckAckTimeout(clock_.ApproximateNow());
+
+ // The next packet received in order will cause an immediate ack, because it
+ // fills a hole.
+ RecordPacketReceipt(kFirstDecimatedPacket + 10, clock_.ApproximateNow());
+ MaybeUpdateAckTimeout(kInstigateAck, kFirstDecimatedPacket + 10);
+ CheckAckTimeout(clock_.ApproximateNow());
+}
+
+TEST_F(UberReceivedPacketManagerTest,
+ DontWaitForPacketsBeforeMultiplePacketNumberSpaces) {
+ manager_->EnableMultiplePacketNumberSpacesSupport();
+ EXPECT_FALSE(
+ manager_->GetLargestObserved(ENCRYPTION_HANDSHAKE).IsInitialized());
+ EXPECT_FALSE(
+ manager_->GetLargestObserved(ENCRYPTION_FORWARD_SECURE).IsInitialized());
+ RecordPacketReceipt(ENCRYPTION_HANDSHAKE, 2);
+ RecordPacketReceipt(ENCRYPTION_HANDSHAKE, 4);
+ RecordPacketReceipt(ENCRYPTION_FORWARD_SECURE, 3);
+ RecordPacketReceipt(ENCRYPTION_FORWARD_SECURE, 7);
+ EXPECT_EQ(QuicPacketNumber(4),
+ manager_->GetLargestObserved(ENCRYPTION_HANDSHAKE));
+ EXPECT_EQ(QuicPacketNumber(7),
+ manager_->GetLargestObserved(ENCRYPTION_FORWARD_SECURE));
+
+ EXPECT_TRUE(
+ manager_->IsAwaitingPacket(ENCRYPTION_HANDSHAKE, QuicPacketNumber(3)));
+ EXPECT_FALSE(manager_->IsAwaitingPacket(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(3)));
+ EXPECT_TRUE(manager_->IsAwaitingPacket(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(4)));
+
+ manager_->DontWaitForPacketsBefore(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(5));
+ EXPECT_TRUE(
+ manager_->IsAwaitingPacket(ENCRYPTION_HANDSHAKE, QuicPacketNumber(3)));
+ EXPECT_FALSE(manager_->IsAwaitingPacket(ENCRYPTION_FORWARD_SECURE,
+ QuicPacketNumber(4)));
+}
+
+TEST_F(UberReceivedPacketManagerTest, AckSendingDifferentPacketNumberSpaces) {
+ manager_->EnableMultiplePacketNumberSpacesSupport();
+ SetQuicRestartFlag(quic_enable_accept_random_ipn, true);
+ EXPECT_FALSE(HasPendingAck());
+ EXPECT_FALSE(manager_->IsAckFrameUpdated());
+
+ RecordPacketReceipt(ENCRYPTION_HANDSHAKE, 3);
+ EXPECT_TRUE(manager_->IsAckFrameUpdated());
+ MaybeUpdateAckTimeout(kInstigateAck, ENCRYPTION_HANDSHAKE, 3);
+ EXPECT_TRUE(HasPendingAck());
+ // Delayed ack is scheduled.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+
+ RecordPacketReceipt(ENCRYPTION_FORWARD_SECURE, 3);
+ MaybeUpdateAckTimeout(kInstigateAck, ENCRYPTION_FORWARD_SECURE, 3);
+ EXPECT_TRUE(HasPendingAck());
+ // Delayed ack is scheduled.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+
+ RecordPacketReceipt(ENCRYPTION_FORWARD_SECURE, 2);
+ MaybeUpdateAckTimeout(kInstigateAck, ENCRYPTION_FORWARD_SECURE, 2);
+ // Application data ACK should be sent immediately.
+ CheckAckTimeout(clock_.ApproximateNow());
+ // Delayed ACK of handshake data is pending.
+ CheckAckTimeout(clock_.ApproximateNow() + kDelayedAckTime);
+
+ // Send delayed handshake data ACK.
+ clock_.AdvanceTime(kDelayedAckTime);
+ CheckAckTimeout(clock_.ApproximateNow());
+ EXPECT_FALSE(HasPendingAck());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_aligned.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_aligned.h
new file mode 100644
index 00000000000..ecfa649f31c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_aligned.h
@@ -0,0 +1,15 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_ALIGNED_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_ALIGNED_H_
+
+#include "net/quic/platform/impl/quic_aligned_impl.h"
+
+#define QUIC_ALIGN_OF QUIC_ALIGN_OF_IMPL
+#define QUIC_ALIGNED(X) QUIC_ALIGNED_IMPL(X)
+#define QUIC_CACHELINE_ALIGNED QUIC_CACHELINE_ALIGNED_IMPL
+#define QUIC_CACHELINE_SIZE QUIC_CACHELINE_SIZE_IMPL
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_ALIGNED_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_arraysize.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_arraysize.h
new file mode 100644
index 00000000000..eaa2a92a32b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_arraysize.h
@@ -0,0 +1,12 @@
+// 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_ARRAYSIZE_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_ARRAYSIZE_H_
+
+#include "net/quic/platform/impl/quic_arraysize_impl.h"
+
+#define QUIC_ARRAYSIZE(array) QUIC_ARRAYSIZE_IMPL(array)
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_ARRAYSIZE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h
new file mode 100644
index 00000000000..23bcb7b14ac
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h
@@ -0,0 +1,15 @@
+// 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 QUICHE_QUIC_PLATFORM_API_QUIC_BUG_TRACKER_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_BUG_TRACKER_H_
+
+#include "net/quic/platform/impl/quic_bug_tracker_impl.h"
+
+#define QUIC_BUG QUIC_BUG_IMPL
+#define QUIC_BUG_IF QUIC_BUG_IF_IMPL
+#define QUIC_PEER_BUG QUIC_PEER_BUG_IMPL
+#define QUIC_PEER_BUG_IF QUIC_PEER_BUG_IF_IMPL
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_BUG_TRACKER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_cert_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_cert_utils.h
new file mode 100644
index 00000000000..1c660b5a92c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_cert_utils.h
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file._
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_CERT_UTILS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_CERT_UTILS_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/quic/platform/impl/quic_cert_utils_impl.h"
+
+namespace quic {
+
+class QuicCertUtils {
+ public:
+ static bool ExtractSubjectNameFromDERCert(QuicStringPiece cert,
+ QuicStringPiece* subject_out) {
+ return QuicCertUtilsImpl::ExtractSubjectNameFromDERCert(cert, subject_out);
+ }
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_CERT_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_client_stats.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_client_stats.h
new file mode 100644
index 00000000000..b780ba695c8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_client_stats.h
@@ -0,0 +1,87 @@
+// Copyright 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_CLIENT_STATS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_CLIENT_STATS_H_
+
+#include <string>
+#include "net/quic/platform/impl/quic_client_stats_impl.h"
+
+namespace quic {
+
+//------------------------------------------------------------------------------
+// Enumeration histograms.
+//
+// Sample usage:
+// // In Chrome, these values are persisted to logs. Entries should not be
+// // renumbered and numeric values should never be reused.
+// enum class MyEnum {
+// FIRST_VALUE = 0,
+// SECOND_VALUE = 1,
+// ...
+// FINAL_VALUE = N,
+// COUNT
+// };
+// QUIC_CLIENT_HISTOGRAM_ENUM("My.Enumeration", MyEnum::SOME_VALUE,
+// MyEnum::COUNT, "Number of time $foo equals to some enum value");
+//
+// Note: The value in |sample| must be strictly less than |enum_size|.
+
+#define QUIC_CLIENT_HISTOGRAM_ENUM(name, sample, enum_size, docstring) \
+ QUIC_CLIENT_HISTOGRAM_ENUM_IMPL(name, sample, enum_size, docstring)
+
+//------------------------------------------------------------------------------
+// Histogram for boolean values.
+
+// Sample usage:
+// QUIC_CLIENT_HISTOGRAM_BOOL("My.Boolean", bool,
+// "Number of times $foo is true or false");
+#define QUIC_CLIENT_HISTOGRAM_BOOL(name, sample, docstring) \
+ QUIC_CLIENT_HISTOGRAM_BOOL_IMPL(name, sample, docstring)
+
+//------------------------------------------------------------------------------
+// Timing histograms. These are used for collecting timing data (generally
+// latencies).
+
+// These macros create exponentially sized histograms (lengths of the bucket
+// ranges exponentially increase as the sample range increases). The units for
+// sample and max are unspecified, but they must be the same for one histogram.
+
+// Sample usage:
+// QUIC_CLIENT_HISTOGRAM_TIMES("Very.Long.Timing.Histogram", time_delta,
+// QuicTime::Delta::FromSeconds(1), QuicTime::Delta::FromSecond(3600 *
+// 24), 100, "Time spent in doing operation.");
+#define QUIC_CLIENT_HISTOGRAM_TIMES(name, sample, min, max, bucket_count, \
+ docstring) \
+ QUIC_CLIENT_HISTOGRAM_TIMES_IMPL(name, sample, min, max, bucket_count, \
+ docstring)
+
+//------------------------------------------------------------------------------
+// Count histograms. These are used for collecting numeric data.
+
+// These macros default to exponential histograms - i.e. the lengths of the
+// bucket ranges exponentially increase as the sample range increases.
+
+// All of these macros must be called with |name| as a runtime constant.
+
+// Any data outside the range here will be put in underflow and overflow
+// buckets. Min values should be >=1 as emitted 0s will still go into the
+// underflow bucket.
+
+// Sample usage:
+// UMA_CLIENT_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", 1, 100000000, 100,
+// "Counters of hitting certian code.");
+
+#define QUIC_CLIENT_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count, \
+ docstring) \
+ QUIC_CLIENT_HISTOGRAM_COUNTS_IMPL(name, sample, min, max, bucket_count, \
+ docstring)
+
+inline void QuicClientSparseHistogram(const std::string& name, int sample) {
+ QuicClientSparseHistogramImpl(name, sample);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_CLIENT_STATS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_clock.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_clock.cc
new file mode 100644
index 00000000000..03851fa5809
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_clock.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicClock::QuicClock()
+ : is_calibrated_(false), calibration_offset_(QuicTime::Delta::Zero()) {}
+
+QuicClock::~QuicClock() {}
+
+QuicTime::Delta QuicClock::ComputeCalibrationOffset() const {
+ // In the ideal world, all we need to do is to return the difference of
+ // WallNow() and Now(). In the real world, things like context switch may
+ // happen between the calls to WallNow() and Now(), causing their difference
+ // to be arbitrarily large, so we repeat the calculation many times and use
+ // the one with the minimum difference as the true offset.
+ int64_t min_offset_us = std::numeric_limits<int64_t>::max();
+
+ for (int i = 0; i < 128; ++i) {
+ int64_t now_in_us = (Now() - QuicTime::Zero()).ToMicroseconds();
+ int64_t wallnow_in_us =
+ static_cast<int64_t>(WallNow().ToUNIXMicroseconds());
+
+ int64_t offset_us = wallnow_in_us - now_in_us;
+ if (offset_us < min_offset_us) {
+ min_offset_us = offset_us;
+ }
+ }
+
+ return QuicTime::Delta::FromMicroseconds(min_offset_us);
+}
+
+void QuicClock::SetCalibrationOffset(QuicTime::Delta offset) {
+ DCHECK(!is_calibrated_) << "A clock should only be calibrated once";
+ calibration_offset_ = offset;
+ is_calibrated_ = true;
+}
+
+QuicTime QuicClock::ConvertWallTimeToQuicTime(
+ const QuicWallTime& walltime) const {
+ if (is_calibrated_) {
+ int64_t time_in_us = static_cast<int64_t>(walltime.ToUNIXMicroseconds()) -
+ calibration_offset_.ToMicroseconds();
+ return QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(time_in_us);
+ }
+
+ // ..........................
+ // | | |
+ // unix epoch |walltime| WallNow()
+ // ..........................
+ // | | |
+ // clock epoch | Now()
+ // result
+ //
+ // result = Now() - (WallNow() - walltime)
+ return Now() - QuicTime::Delta::FromMicroseconds(
+ WallNow()
+ .Subtract(QuicTime::Delta::FromMicroseconds(
+ walltime.ToUNIXMicroseconds()))
+ .ToUNIXMicroseconds());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_clock.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_clock.h
new file mode 100644
index 00000000000..afd82dc5906
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_clock.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_CLOCK_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_CLOCK_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Interface for retrieving the current time.
+class QUIC_EXPORT_PRIVATE QuicClock {
+ public:
+ QuicClock();
+ virtual ~QuicClock();
+
+ QuicClock(const QuicClock&) = delete;
+ QuicClock& operator=(const QuicClock&) = delete;
+
+ // Compute the offset between this clock with the Unix Epoch clock.
+ // Return the calibrated offset between WallNow() and Now(), in the form of
+ // (wallnow_in_us - now_in_us).
+ // The return value can be used by SetCalibrationOffset() to actually
+ // calibrate the clock, or all instances of this clock type.
+ QuicTime::Delta ComputeCalibrationOffset() const;
+
+ // Calibrate this clock. A calibrated clock guarantees that the
+ // ConvertWallTimeToQuicTime() function always return the same result for the
+ // same walltime.
+ // Should not be called more than once for each QuicClock.
+ void SetCalibrationOffset(QuicTime::Delta offset);
+
+ // Returns the approximate current time as a QuicTime object.
+ virtual QuicTime ApproximateNow() const = 0;
+
+ // Returns the current time as a QuicTime object.
+ // Note: this use significant resources please use only if needed.
+ virtual QuicTime Now() const = 0;
+
+ // WallNow returns the current wall-time - a time that is consistent across
+ // different clocks.
+ virtual QuicWallTime WallNow() const = 0;
+
+ // Converts |walltime| to a QuicTime relative to this clock's epoch.
+ virtual QuicTime ConvertWallTimeToQuicTime(
+ const QuicWallTime& walltime) const;
+
+ protected:
+ // Creates a new QuicTime using |time_us| as the internal value.
+ QuicTime CreateTimeFromMicroseconds(uint64_t time_us) const {
+ return QuicTime(time_us);
+ }
+
+ private:
+ // True if |calibration_offset_| is valid.
+ bool is_calibrated_;
+ // If |is_calibrated_|, |calibration_offset_| is the (fixed) offset between
+ // the Unix Epoch clock and this clock.
+ // In other words, the offset between WallNow() and Now().
+ QuicTime::Delta calibration_offset_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_CLOCK_H_
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
new file mode 100644
index 00000000000..7cfb3117865
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers.h
@@ -0,0 +1,56 @@
+// 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_CONTAINERS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_CONTAINERS_H_
+
+#include "net/quic/platform/impl/quic_containers_impl.h"
+
+namespace quic {
+
+// The default hasher used by hash tables.
+template <typename Key>
+using QuicDefaultHasher = QuicDefaultHasherImpl<Key>;
+
+// A general-purpose unordered map.
+template <typename Key, typename Value, typename Hash = QuicDefaultHasher<Key>>
+using QuicUnorderedMap = QuicUnorderedMapImpl<Key, Value, Hash>;
+
+// A general-purpose unordered set.
+template <typename Key, typename Hash = QuicDefaultHasher<Key>>
+using QuicUnorderedSet = QuicUnorderedSetImpl<Key, Hash>;
+
+// A map which offers insertion-ordered iteration.
+template <typename Key, typename Value, typename Hash = QuicDefaultHasher<Key>>
+using QuicLinkedHashMap = QuicLinkedHashMapImpl<Key, Value, Hash>;
+
+// Used for maps that are typically small, then it is faster than (for example)
+// hash_map which is optimized for large data sets. QuicSmallMap upgrades itself
+// automatically to a QuicSmallMapImpl-specified map when it runs out of space.
+//
+// DOES NOT GUARANTEE POINTER OR ITERATOR STABILITY!
+template <typename Key, typename Value, int Size>
+using QuicSmallMap = QuicSmallMapImpl<Key, Value, Size>;
+
+// Represents a simple queue which may be backed by a list or
+// a flat circular buffer.
+//
+// DOES NOT GUARANTEE POINTER OR ITERATOR STABILITY!
+template <typename T>
+using QuicQueue = QuicQueueImpl<T>;
+
+// 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 QuicDeque = QuicDequeImpl<T>;
+
+// 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>;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_CONTAINERS_H_
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
new file mode 100644
index 00000000000..b272b09efca
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_containers_test.cc
@@ -0,0 +1,63 @@
+// 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 "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+#include "net/third_party/quiche/src/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_default_proof_providers.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h
new file mode 100644
index 00000000000..6f3b636372f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_DEFAULT_PROOF_PROVIDERS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_DEFAULT_PROOF_PROVIDERS_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/quic/platform/impl/quic_default_proof_providers_impl.h"
+
+namespace quic {
+
+// Provides a default proof verifier. The verifier has to do a good faith
+// attempt at verifying the certificate against a reasonable root store, and not
+// just always return success.
+inline std::unique_ptr<ProofVerifier> CreateDefaultProofVerifier() {
+ return CreateDefaultProofVerifierImpl();
+}
+
+// Provides a default proof source for CLI-based tools. The actual certificates
+// used in the proof source should be confifgurable via command-line flags.
+inline std::unique_ptr<ProofSource> CreateDefaultProofSource() {
+ return CreateDefaultProofSourceImpl();
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_DEFAULT_PROOF_PROVIDERS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_endian.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_endian.h
new file mode 100644
index 00000000000..65edd51690f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_endian.h
@@ -0,0 +1,54 @@
+// 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_ENDIAN_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_ENDIAN_H_
+
+#include "net/quic/platform/impl/quic_endian_impl.h"
+
+namespace quic {
+
+enum Endianness {
+ NETWORK_BYTE_ORDER, // big endian
+ HOST_BYTE_ORDER // little endian
+};
+
+// Provide utility functions that convert from/to network order (big endian)
+// to/from host order (can be either little or big endian depending on the
+// platform).
+class QuicEndian {
+ public:
+ // Convert |x| from host order (can be either little or big endian depending
+ // on the platform) to network order (big endian).
+ static uint16_t HostToNet16(uint16_t x) {
+ return QuicEndianImpl::HostToNet16(x);
+ }
+ static uint32_t HostToNet32(uint32_t x) {
+ return QuicEndianImpl::HostToNet32(x);
+ }
+ static uint64_t HostToNet64(uint64_t x) {
+ return QuicEndianImpl::HostToNet64(x);
+ }
+
+ // Convert |x| from network order (big endian) to host order (can be either
+ // little or big endian depending on the platform).
+ static uint16_t NetToHost16(uint16_t x) {
+ return QuicEndianImpl::NetToHost16(x);
+ }
+ static uint32_t NetToHost32(uint32_t x) {
+ return QuicEndianImpl::NetToHost32(x);
+ }
+ static uint64_t NetToHost64(uint64_t x) {
+ return QuicEndianImpl::NetToHost64(x);
+ }
+
+ // Returns true if current host order is little endian.
+ static bool HostIsLittleEndian() {
+ return QuicEndianImpl::HostIsLittleEndian();
+ }
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_ENDIAN_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_endian_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_endian_test.cc
new file mode 100644
index 00000000000..d054d963021
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_endian_test.cc
@@ -0,0 +1,51 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const uint16_t k16BitTestData = 0xaabb;
+const uint16_t k16BitSwappedTestData = 0xbbaa;
+const uint32_t k32BitTestData = 0xaabbccdd;
+const uint32_t k32BitSwappedTestData = 0xddccbbaa;
+const uint64_t k64BitTestData = 0xaabbccdd44332211;
+const uint64_t k64BitSwappedTestData = 0x11223344ddccbbaa;
+
+class QuicEndianTest : public QuicTest {};
+
+TEST_F(QuicEndianTest, HostToNet) {
+ if (QuicEndian::HostIsLittleEndian()) {
+ EXPECT_EQ(k16BitSwappedTestData, QuicEndian::HostToNet16(k16BitTestData));
+ EXPECT_EQ(k32BitSwappedTestData, QuicEndian::HostToNet32(k32BitTestData));
+ EXPECT_EQ(k64BitSwappedTestData, QuicEndian::HostToNet64(k64BitTestData));
+ } else {
+ EXPECT_EQ(k16BitTestData, QuicEndian::HostToNet16(k16BitTestData));
+ EXPECT_EQ(k32BitTestData, QuicEndian::HostToNet32(k32BitTestData));
+ EXPECT_EQ(k64BitTestData, QuicEndian::HostToNet64(k64BitTestData));
+ }
+}
+
+TEST_F(QuicEndianTest, NetToHost) {
+ if (QuicEndian::HostIsLittleEndian()) {
+ EXPECT_EQ(k16BitTestData, QuicEndian::NetToHost16(k16BitSwappedTestData));
+ EXPECT_EQ(k32BitTestData, QuicEndian::NetToHost32(k32BitSwappedTestData));
+ EXPECT_EQ(k64BitTestData, QuicEndian::NetToHost64(k64BitSwappedTestData));
+ } else {
+ EXPECT_EQ(k16BitSwappedTestData,
+ QuicEndian::NetToHost16(k16BitSwappedTestData));
+ EXPECT_EQ(k32BitSwappedTestData,
+ QuicEndian::NetToHost32(k32BitSwappedTestData));
+ EXPECT_EQ(k64BitSwappedTestData,
+ QuicEndian::NetToHost64(k64BitSwappedTestData));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_epoll.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_epoll.h
new file mode 100644
index 00000000000..e429ef8b234
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_epoll.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_EPOLL_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_EPOLL_H_
+
+#include "net/quic/platform/impl/quic_epoll_impl.h"
+
+namespace quic {
+
+using QuicEpollServer = QuicEpollServerImpl;
+using QuicEpollEvent = QuicEpollEventImpl;
+using QuicEpollAlarmBase = QuicEpollAlarmBaseImpl;
+using QuicEpollCallbackInterface = QuicEpollCallbackInterfaceImpl;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_EPOLL_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_epoll_test_tools.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_epoll_test_tools.h
new file mode 100644
index 00000000000..a7c2ce3f287
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_epoll_test_tools.h
@@ -0,0 +1,12 @@
+// 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_EPOLL_TEST_TOOLS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_EPOLL_TEST_TOOLS_H_
+
+#include "net/quic/platform/impl/quic_epoll_test_tools_impl.h"
+
+using QuicFakeEpollServer = QuicFakeEpollServerImpl;
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_EPOLL_TEST_TOOLS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h
new file mode 100644
index 00000000000..733572ef653
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_error_code_wrappers.h
@@ -0,0 +1,14 @@
+// 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_ERROR_CODE_WRAPPERS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_ERROR_CODE_WRAPPERS_H_
+
+#include "net/quic/platform/impl/quic_error_code_wrappers_impl.h"
+
+// TODO(vasilvv): ensure WRITE_STATUS_MSG_TOO_BIG works everywhere and remove
+// this.
+#define QUIC_EMSGSIZE QUIC_EMSGSIZE_IMPL
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_ERROR_CODE_WRAPPERS_H_
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
new file mode 100644
index 00000000000..dd844856bc3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_estimate_memory_usage.h
@@ -0,0 +1,21 @@
+// 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 "net/quic/platform/impl/quic_estimate_memory_usage_impl.h"
+
+namespace quic {
+
+template <class T>
+size_t QuicEstimateMemoryUsage(const T& object) {
+ return QuicEstimateMemoryUsageImpl(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_expect_bug.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h
new file mode 100644
index 00000000000..936b11f59b6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h
@@ -0,0 +1,14 @@
+// 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_EXPECT_BUG_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_EXPECT_BUG_H_
+
+#include "net/quic/platform/impl/quic_expect_bug_impl.h"
+
+#define EXPECT_QUIC_BUG EXPECT_QUIC_BUG_IMPL
+#define EXPECT_QUIC_PEER_BUG(statement, regex) \
+ EXPECT_QUIC_PEER_BUG_IMPL(statement, regex)
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_EXPECT_BUG_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_export.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_export.h
new file mode 100644
index 00000000000..8ffd6765259
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_export.h
@@ -0,0 +1,10 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_EXPORT_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_EXPORT_H_
+
+#include "net/quic/platform/impl/quic_export_impl.h"
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_EXPORT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h
new file mode 100644
index 00000000000..28c82ec8ce0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h
@@ -0,0 +1,96 @@
+// Copyright 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_EXPORTED_STATS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_EXPORTED_STATS_H_
+
+#include "net/quic/platform/impl/quic_client_stats_impl.h"
+#include "net/quic/platform/impl/quic_server_stats_impl.h"
+
+namespace quic {
+
+// TODO(wub): Add support for counters. Only histograms are supported for now.
+
+//------------------------------------------------------------------------------
+// Enumeration histograms.
+//
+// Sample usage:
+// // In Chrome, these values are persisted to logs. Entries should not be
+// // renumbered and numeric values should never be reused.
+// enum class MyEnum {
+// FIRST_VALUE = 0,
+// SECOND_VALUE = 1,
+// ...
+// FINAL_VALUE = N,
+// COUNT
+// };
+// QUIC_HISTOGRAM_ENUM("My.Enumeration", MyEnum::SOME_VALUE, MyEnum::COUNT,
+// "Number of time $foo equals to some enum value");
+//
+// Note: The value in |sample| must be strictly less than |enum_size|.
+
+#define QUIC_HISTOGRAM_ENUM(name, sample, enum_size, docstring) \
+ do { \
+ QUIC_CLIENT_HISTOGRAM_ENUM_IMPL(name, sample, enum_size, docstring); \
+ QUIC_SERVER_HISTOGRAM_ENUM_IMPL(name, sample, enum_size, docstring); \
+ } while (0)
+
+//------------------------------------------------------------------------------
+// Histogram for boolean values.
+
+// Sample usage:
+// QUIC_HISTOGRAM_BOOL("My.Boolean", bool,
+// "Number of times $foo is true or false");
+#define QUIC_HISTOGRAM_BOOL(name, sample, docstring) \
+ do { \
+ QUIC_CLIENT_HISTOGRAM_BOOL_IMPL(name, sample, docstring); \
+ QUIC_SERVER_HISTOGRAM_BOOL_IMPL(name, sample, docstring); \
+ } while (0)
+
+//------------------------------------------------------------------------------
+// Timing histograms. These are used for collecting timing data (generally
+// latencies).
+
+// These macros create exponentially sized histograms (lengths of the bucket
+// ranges exponentially increase as the sample range increases). The units for
+// sample and max are unspecified, but they must be the same for one histogram.
+
+// Sample usage:
+// QUIC_HISTOGRAM_TIMES("My.Timing.Histogram.InMs", time_delta,
+// QuicTime::Delta::FromSeconds(1), QuicTime::Delta::FromSecond(3600 *
+// 24), 100, "Time spent in doing operation.");
+
+#define QUIC_HISTOGRAM_TIMES(name, sample, min, max, bucket_count, docstring) \
+ do { \
+ QUIC_CLIENT_HISTOGRAM_TIMES_IMPL(name, sample, min, max, bucket_count, \
+ docstring); \
+ QUIC_SERVER_HISTOGRAM_TIMES_IMPL(name, sample, min, max, bucket_count, \
+ docstring); \
+ } while (0)
+
+//------------------------------------------------------------------------------
+// Count histograms. These are used for collecting numeric data.
+
+// These macros default to exponential histograms - i.e. the lengths of the
+// bucket ranges exponentially increase as the sample range increases.
+
+// All of these macros must be called with |name| as a runtime constant.
+
+// Sample usage:
+// QUIC_HISTOGRAM_COUNTS("My.Histogram",
+// sample, // Number of something in this event.
+// 1000, // Record up to 1K of something.
+// "Number of something.");
+
+#define QUIC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count, docstring) \
+ do { \
+ QUIC_CLIENT_HISTOGRAM_COUNTS_IMPL(name, sample, min, max, bucket_count, \
+ docstring); \
+ QUIC_SERVER_HISTOGRAM_COUNTS_IMPL(name, sample, min, max, bucket_count, \
+ docstring); \
+ } while (0)
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_EXPORTED_STATS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h
new file mode 100644
index 00000000000..499b61abf74
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h
@@ -0,0 +1,12 @@
+// 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_FALLTHROUGH_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_FALLTHROUGH_H_
+
+#include "net/quic/platform/impl/quic_fallthrough_impl.h"
+
+#define QUIC_FALLTHROUGH_INTENDED QUIC_FALLTHROUGH_INTENDED_IMPL
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_FALLTHROUGH_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
new file mode 100644
index 00000000000..29ec416b83a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.cc
@@ -0,0 +1,22 @@
+// 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 "net/third_party/quiche/src/quic/platform/api/quic_file_utils.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(QuicStringPiece 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
new file mode 100644
index 00000000000..7558786f83a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_file_utils.h
@@ -0,0 +1,26 @@
+// 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 "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.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(QuicStringPiece 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_flag_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h
new file mode 100644
index 00000000000..bec9b39c653
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h
@@ -0,0 +1,19 @@
+// 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_FLAG_UTILS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_FLAG_UTILS_H_
+
+#include "net/quic/platform/impl/quic_flag_utils_impl.h"
+
+#define QUIC_RELOADABLE_FLAG_COUNT QUIC_RELOADABLE_FLAG_COUNT_IMPL
+#define QUIC_RELOADABLE_FLAG_COUNT_N QUIC_RELOADABLE_FLAG_COUNT_N_IMPL
+
+#define QUIC_RESTART_FLAG_COUNT QUIC_RESTART_FLAG_COUNT_IMPL
+#define QUIC_RESTART_FLAG_COUNT_N QUIC_RESTART_FLAG_COUNT_N_IMPL
+
+#define QUIC_CODE_COUNT QUIC_CODE_COUNT_IMPL
+#define QUIC_CODE_COUNT_N QUIC_CODE_COUNT_N_IMPL
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_FLAG_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_flags.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_flags.h
new file mode 100644
index 00000000000..302036fd3d8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_flags.h
@@ -0,0 +1,48 @@
+// 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_FLAGS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_FLAGS_H_
+
+#include <string>
+#include <vector>
+
+#include "net/quic/platform/impl/quic_flags_impl.h"
+
+// Define a command-line flag that can be automatically set via
+// QuicParseCommandLineFlags().
+#define DEFINE_QUIC_COMMAND_LINE_FLAG(type, name, default_value, help) \
+ DEFINE_QUIC_COMMAND_LINE_FLAG_IMPL(type, name, default_value, help)
+
+#define GetQuicReloadableFlag(flag) GetQuicReloadableFlagImpl(flag)
+#define SetQuicReloadableFlag(flag, value) \
+ SetQuicReloadableFlagImpl(flag, value)
+#define GetQuicRestartFlag(flag) GetQuicRestartFlagImpl(flag)
+#define SetQuicRestartFlag(flag, value) SetQuicRestartFlagImpl(flag, value)
+#define GetQuicFlag(flag) GetQuicFlagImpl(flag)
+#define SetQuicFlag(flag, value) SetQuicFlagImpl(flag, value)
+
+namespace quic {
+
+// Parses command-line flags, setting flag variables defined using
+// DEFINE_QUIC_COMMAND_LINE_FLAG if they appear in the command line, and
+// returning a list of any non-flag arguments specified in the command line. If
+// the command line specifies '-h' or '--help', prints a usage message with flag
+// descriptions to stdout and exits with status 0. If a flag has an unparsable
+// value, writes an error message to stderr and exits with status 1.
+inline std::vector<std::string> QuicParseCommandLineFlags(
+ const char* usage,
+ int argc,
+ const char* const* argv) {
+ return QuicParseCommandLineFlagsImpl(usage, argc, argv);
+}
+
+// Prints a usage message with flag descriptions to stdout.
+inline void QuicPrintCommandLineFlagHelp(const char* usage) {
+ QuicPrintCommandLineFlagHelpImpl(usage);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_FLAGS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h
new file mode 100644
index 00000000000..ef565e2395f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h
@@ -0,0 +1,16 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_FUZZED_DATA_PROVIDER_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_FUZZED_DATA_PROVIDER_H_
+
+#include "net/quic/platform/impl/quic_fuzzed_data_provider_impl.h"
+
+namespace quic {
+
+using QuicFuzzedDataProvider = QuicFuzzedDataProviderImpl;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_FUZZED_DATA_PROVIDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.cc
new file mode 100644
index 00000000000..b7e4642854a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.cc
@@ -0,0 +1,19 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h"
+
+namespace quic {
+
+// static
+bool QuicHostnameUtils::IsValidSNI(QuicStringPiece sni) {
+ return QuicHostnameUtilsImpl::IsValidSNI(sni);
+}
+
+// static
+std::string QuicHostnameUtils::NormalizeHostname(QuicStringPiece hostname) {
+ return QuicHostnameUtilsImpl::NormalizeHostname(hostname);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h
new file mode 100644
index 00000000000..672847c0733
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h
@@ -0,0 +1,34 @@
+// 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_HOSTNAME_UTILS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_HOSTNAME_UTILS_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/quic/platform/impl/quic_hostname_utils_impl.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicHostnameUtils {
+ public:
+ QuicHostnameUtils() = delete;
+
+ // Returns true if the sni is valid, false otherwise.
+ // (1) disallow IP addresses;
+ // (2) check that the hostname contains valid characters only; and
+ // (3) contains at least one dot.
+ static bool IsValidSNI(QuicStringPiece sni);
+
+ // Canonicalizes the specified hostname. This involves a wide variety of
+ // transformations, including lowercasing, removing trailing dots and IDNA
+ // conversion.
+ static std::string NormalizeHostname(QuicStringPiece hostname);
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_HOSTNAME_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
new file mode 100644
index 00000000000..2c6573b1933
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc
@@ -0,0 +1,89 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicHostnameUtilsTest : public QuicTest {};
+
+TEST_F(QuicHostnameUtilsTest, IsValidSNI) {
+ // IP as SNI.
+ EXPECT_FALSE(QuicHostnameUtils::IsValidSNI("192.168.0.1"));
+ // SNI without any dot.
+ 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
+ // it.
+ EXPECT_FALSE(QuicHostnameUtils::IsValidSNI(""));
+
+ // Valid SNI
+ EXPECT_TRUE(QuicHostnameUtils::IsValidSNI("test.google.com"));
+}
+
+TEST_F(QuicHostnameUtilsTest, NormalizeHostname) {
+ // clang-format off
+ struct {
+ const char *input, *expected;
+ } tests[] = {
+ {
+ "www.google.com",
+ "www.google.com",
+ },
+ {
+ "WWW.GOOGLE.COM",
+ "www.google.com",
+ },
+ {
+ "www.google.com.",
+ "www.google.com",
+ },
+ {
+ "www.google.COM.",
+ "www.google.com",
+ },
+ {
+ "www.google.com..",
+ "www.google.com",
+ },
+ {
+ "www.google.com........",
+ "www.google.com",
+ },
+ {
+ "",
+ "",
+ },
+ {
+ ".",
+ "",
+ },
+ {
+ "........",
+ "",
+ },
+ {
+ "\xe5\x85\x89.google.com",
+ "xn--54q.google.com",
+ },
+ };
+ // clang-format on
+
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(tests); ++i) {
+ EXPECT_EQ(std::string(tests[i].expected),
+ QuicHostnameUtils::NormalizeHostname(tests[i].input));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_iovec.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_iovec.h
new file mode 100644
index 00000000000..97202cd0afd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_iovec.h
@@ -0,0 +1,27 @@
+// 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_IOVEC_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_IOVEC_H_
+
+#include <cstddef>
+#include <type_traits>
+
+#include "net/quic/platform/impl/quic_iovec_impl.h"
+
+namespace quic {
+
+// The impl header has to export struct iovec, or a POSIX-compatible polyfill.
+// Below, we mostly assert that what we have is appropriate.
+static_assert(std::is_standard_layout<struct iovec>::value,
+ "iovec has to be a standard-layout struct");
+
+static_assert(offsetof(struct iovec, iov_base) < sizeof(struct iovec),
+ "iovec has to have iov_base");
+static_assert(offsetof(struct iovec, iov_len) < sizeof(struct iovec),
+ "iovec has to have iov_len");
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_IOVEC_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.cc
new file mode 100644
index 00000000000..2b86cadc3fa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h"
+
+#include <string>
+
+namespace quic {
+
+QuicIpAddress QuicIpAddress::Loopback4() {
+ return QuicIpAddress(QuicIpAddressImpl::Loopback4());
+}
+
+QuicIpAddress QuicIpAddress::Loopback6() {
+ return QuicIpAddress(QuicIpAddressImpl::Loopback6());
+}
+
+QuicIpAddress QuicIpAddress::Any4() {
+ return QuicIpAddress(QuicIpAddressImpl::Any4());
+}
+
+QuicIpAddress QuicIpAddress::Any6() {
+ return QuicIpAddress(QuicIpAddressImpl::Any6());
+}
+
+QuicIpAddress::QuicIpAddress(const QuicIpAddressImpl& impl) : impl_(impl) {}
+
+QuicIpAddress::QuicIpAddress(const in_addr& ipv4_address)
+ : impl_(ipv4_address) {}
+QuicIpAddress::QuicIpAddress(const in6_addr& ipv6_address)
+ : impl_(ipv6_address) {}
+
+bool operator==(QuicIpAddress lhs, QuicIpAddress rhs) {
+ return lhs.impl_ == rhs.impl_;
+}
+
+bool operator!=(QuicIpAddress lhs, QuicIpAddress rhs) {
+ return !(lhs == rhs);
+}
+
+bool QuicIpAddress::IsInitialized() const {
+ return impl_.IsInitialized();
+}
+
+IpAddressFamily QuicIpAddress::address_family() const {
+ return impl_.address_family();
+}
+
+int QuicIpAddress::AddressFamilyToInt() const {
+ return impl_.AddressFamilyToInt();
+}
+
+std::string QuicIpAddress::ToPackedString() const {
+ return impl_.ToPackedString();
+}
+
+std::string QuicIpAddress::ToString() const {
+ return impl_.ToString();
+}
+
+QuicIpAddress QuicIpAddress::Normalized() const {
+ return QuicIpAddress(impl_.Normalized());
+}
+
+QuicIpAddress QuicIpAddress::DualStacked() const {
+ return QuicIpAddress(impl_.DualStacked());
+}
+
+bool QuicIpAddress::FromPackedString(const char* data, size_t length) {
+ return impl_.FromPackedString(data, length);
+}
+
+bool QuicIpAddress::FromString(std::string str) {
+ return impl_.FromString(str);
+}
+
+bool QuicIpAddress::IsIPv4() const {
+ return impl_.IsIPv4();
+}
+
+bool QuicIpAddress::IsIPv6() const {
+ return impl_.IsIPv6();
+}
+
+bool QuicIpAddress::InSameSubnet(const QuicIpAddress& other,
+ int subnet_length) {
+ return impl_.InSameSubnet(other.impl(), subnet_length);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.h
new file mode 100644
index 00000000000..f6013af4d4b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address.h
@@ -0,0 +1,84 @@
+// 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 QUICHE_QUIC_PLATFORM_API_QUIC_IP_ADDRESS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_IP_ADDRESS_H_
+
+#if defined(_WIN32)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#endif
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/quic/platform/impl/quic_ip_address_impl.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicIpAddress {
+ // A class representing an IPv4 or IPv6 address in QUIC. The actual
+ // implementation (platform dependent) of an IP address is in
+ // QuicIpAddressImpl.
+ public:
+ enum : size_t {
+ kIPv4AddressSize = QuicIpAddressImpl::kIPv4AddressSize,
+ kIPv6AddressSize = QuicIpAddressImpl::kIPv6AddressSize
+ };
+
+ // TODO(fayang): Remove Loopback*() and use TestLoopback*() in tests.
+ static QuicIpAddress Loopback4();
+ static QuicIpAddress Loopback6();
+ static QuicIpAddress Any4();
+ static QuicIpAddress Any6();
+
+ QuicIpAddress() = default;
+ QuicIpAddress(const QuicIpAddress& other) = default;
+ explicit QuicIpAddress(const QuicIpAddressImpl& impl);
+ explicit QuicIpAddress(const in_addr& ipv4_address);
+ explicit QuicIpAddress(const in6_addr& ipv6_address);
+ QuicIpAddress& operator=(const QuicIpAddress& other) = default;
+ QuicIpAddress& operator=(QuicIpAddress&& other) = default;
+ QUIC_EXPORT_PRIVATE friend bool operator==(QuicIpAddress lhs,
+ QuicIpAddress rhs);
+ QUIC_EXPORT_PRIVATE friend bool operator!=(QuicIpAddress lhs,
+ QuicIpAddress rhs);
+
+ bool IsInitialized() const;
+ IpAddressFamily address_family() const;
+ int AddressFamilyToInt() const;
+ // Returns the address as a sequence of bytes in network-byte-order. IPv4 will
+ // be 4 bytes. IPv6 will be 16 bytes.
+ std::string ToPackedString() const;
+ // Returns string representation of the address.
+ std::string ToString() const;
+ // Normalizes the address representation with respect to IPv4 addresses, i.e,
+ // mapped IPv4 addresses ("::ffff:X.Y.Z.Q") are converted to pure IPv4
+ // addresses. All other IPv4, IPv6, and empty values are left unchanged.
+ QuicIpAddress Normalized() const;
+ // Returns an address suitable for use in IPv6-aware contexts. This is the
+ // opposite of NormalizeIPAddress() above. IPv4 addresses are converted into
+ // their IPv4-mapped address equivalents (e.g. 192.0.2.1 becomes
+ // ::ffff:192.0.2.1). IPv6 addresses are a noop (they are returned
+ // unchanged).
+ QuicIpAddress DualStacked() const;
+ bool FromPackedString(const char* data, size_t length);
+ bool FromString(std::string str);
+ bool IsIPv4() const;
+ bool IsIPv6() const;
+ bool InSameSubnet(const QuicIpAddress& other, int subnet_length);
+
+ const QuicIpAddressImpl& impl() const { return impl_; }
+
+ private:
+ QuicIpAddressImpl impl_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_IP_ADDRESS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address_family.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address_family.h
new file mode 100644
index 00000000000..dad2cb971b7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ip_address_family.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 QUICHE_QUIC_PLATFORM_API_QUIC_IP_ADDRESS_FAMILY_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_IP_ADDRESS_FAMILY_H_
+
+namespace quic {
+
+// IP address family type used in QUIC. This hides platform dependant IP address
+// family types.
+enum class IpAddressFamily {
+ IP_V4,
+ IP_V6,
+ IP_UNSPEC,
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_IP_ADDRESS_FAMILY_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_logging.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_logging.h
new file mode 100644
index 00000000000..ba48dc12b76
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_logging.h
@@ -0,0 +1,39 @@
+// 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_LOGGING_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_LOGGING_H_
+
+#include "net/quic/platform/impl/quic_logging_impl.h"
+
+// Please note following QUIC_LOG are platform dependent:
+// INFO severity can be degraded (to VLOG(1) or DVLOG(1)).
+// Some platforms may not support QUIC_LOG_FIRST_N or QUIC_LOG_EVERY_N_SEC, and
+// they would simply be translated to LOG.
+
+#define QUIC_DVLOG(verbose_level) QUIC_DVLOG_IMPL(verbose_level)
+#define QUIC_DVLOG_IF(verbose_level, condition) \
+ QUIC_DVLOG_IF_IMPL(verbose_level, condition)
+#define QUIC_DLOG(severity) QUIC_DLOG_IMPL(severity)
+#define QUIC_DLOG_IF(severity, condition) QUIC_DLOG_IF_IMPL(severity, condition)
+#define QUIC_VLOG(verbose_level) QUIC_VLOG_IMPL(verbose_level)
+#define QUIC_LOG(severity) QUIC_LOG_IMPL(severity)
+#define QUIC_LOG_FIRST_N(severity, n) QUIC_LOG_FIRST_N_IMPL(severity, n)
+#define QUIC_LOG_EVERY_N_SEC(severity, seconds) \
+ QUIC_LOG_EVERY_N_SEC_IMPL(severity, seconds)
+#define QUIC_LOG_IF(severity, condition) QUIC_LOG_IF_IMPL(severity, condition)
+
+#define QUIC_PREDICT_FALSE(x) QUIC_PREDICT_FALSE_IMPL(x)
+
+// This is a noop in release build.
+#define QUIC_NOTREACHED() QUIC_NOTREACHED_IMPL()
+
+#define QUIC_PLOG(severity) QUIC_PLOG_IMPL(severity)
+
+#define QUIC_DLOG_INFO_IS_ON() QUIC_DLOG_INFO_IS_ON_IMPL()
+#define QUIC_LOG_INFO_IS_ON() QUIC_LOG_INFO_IS_ON_IMPL()
+#define QUIC_LOG_WARNING_IS_ON() QUIC_LOG_WARNING_IS_ON_IMPL()
+#define QUIC_LOG_ERROR_IS_ON() QUIC_LOG_ERROR_IS_ON_IMPL()
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_LOGGING_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_macros.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_macros.h
new file mode 100644
index 00000000000..9dee2e987c8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_macros.h
@@ -0,0 +1,13 @@
+// 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_MACROS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_MACROS_H_
+
+#include "net/quic/platform/impl/quic_macros_impl.h"
+
+#define QUIC_MUST_USE_RESULT QUIC_MUST_USE_RESULT_IMPL
+#define QUIC_UNUSED QUIC_UNUSED_IMPL
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_MACROS_H_
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
new file mode 100644
index 00000000000..95daec82c30
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_map_util.h
@@ -0,0 +1,24 @@
+// 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
new file mode 100644
index 00000000000..a4318ad8bcc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h
@@ -0,0 +1,62 @@
+// 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_MEM_SLICE_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/quic/platform/impl/quic_mem_slice_impl.h"
+
+namespace quic {
+
+// QuicMemSlice is an internally reference counted data buffer used as the
+// source buffers for write operations. QuicMemSlice implicitly maintains a
+// reference count and will free the underlying data buffer when the reference
+// count reaches zero.
+class QUIC_EXPORT_PRIVATE QuicMemSlice {
+ public:
+ // Constructs a empty QuicMemSlice with no underlying data and 0 reference
+ // count.
+ QuicMemSlice() = default;
+ // Let |allocator| allocate a data buffer of |length|, then construct
+ // QuicMemSlice with reference count 1 from the allocated data buffer.
+ // Once all of the references to the allocated data buffer are released,
+ // |allocator| is responsible to free the memory. |allocator| must
+ // not be null, and |length| must not be 0. To construct an empty
+ // QuicMemSlice, use the zero-argument constructor instead.
+ QuicMemSlice(QuicBufferAllocator* allocator, size_t length)
+ : impl_(allocator, length) {}
+
+ // Constructs QuicMemSlice from |impl|. It takes the reference away from
+ // |impl|.
+ explicit QuicMemSlice(QuicMemSliceImpl impl) : impl_(std::move(impl)) {}
+
+ QuicMemSlice(const QuicMemSlice& other) = delete;
+ QuicMemSlice& operator=(const QuicMemSlice& other) = delete;
+
+ // Move constructors. |other| will not hold a reference to the data buffer
+ // after this call completes.
+ QuicMemSlice(QuicMemSlice&& other) = default;
+ QuicMemSlice& operator=(QuicMemSlice&& other) = default;
+
+ ~QuicMemSlice() = default;
+
+ // Release the underlying reference. Further access the memory will result in
+ // undefined behavior.
+ void Reset() { impl_.Reset(); }
+
+ // Returns a const char pointer to underlying data buffer.
+ const char* data() const { return impl_.data(); }
+ // Returns the length of underlying data buffer.
+ size_t length() const { return impl_.length(); }
+
+ bool empty() const { return impl_.empty(); }
+
+ private:
+ QuicMemSliceImpl impl_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h
new file mode 100644
index 00000000000..4fdf059434b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h
@@ -0,0 +1,54 @@
+// 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_MEM_SLICE_SPAN_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_SPAN_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/quic/platform/impl/quic_mem_slice_span_impl.h"
+
+namespace quic {
+
+// QuicMemSliceSpan is effectively wrapper around an array of data structures
+// used as QuicMemSlice. So it could implemented with:
+// QuicMemSlice* slices_;
+// size_t num_slices_;
+// But for efficiency reasons, the actual implementation is an array of
+// platform-specific objects. This could avoid the translation from
+// platform-specific object to QuicMemSlice.
+// QuicMemSliceSpan does not own the underling data buffers.
+class QUIC_EXPORT_PRIVATE QuicMemSliceSpan {
+ public:
+ explicit QuicMemSliceSpan(QuicMemSliceSpanImpl impl) : impl_(impl) {}
+
+ QuicMemSliceSpan(const QuicMemSliceSpan& other) = default;
+ QuicMemSliceSpan& operator=(const QuicMemSliceSpan& other) = default;
+ QuicMemSliceSpan(QuicMemSliceSpan&& other) = default;
+ QuicMemSliceSpan& operator=(QuicMemSliceSpan&& other) = default;
+
+ ~QuicMemSliceSpan() = default;
+
+ template <typename ConsumeFunction>
+ QuicByteCount ConsumeAll(ConsumeFunction consume) {
+ return impl_.ConsumeAll(consume);
+ }
+
+ // Return data of the span at |index| by the form of a QuicStringPiece.
+ QuicStringPiece GetData(int index) { return impl_.GetData(index); }
+
+ // Return the total length of the data inside the span.
+ QuicByteCount total_length() { return impl_.total_length(); }
+
+ // Return total number of slices in the span.
+ size_t NumSlices() { return impl_.NumSlices(); }
+
+ bool empty() const { return impl_.empty(); }
+
+ private:
+ QuicMemSliceSpanImpl impl_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_SPAN_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span_test.cc
new file mode 100644
index 00000000000..8230a1ba482
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span_test.cc
@@ -0,0 +1,47 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicMemSliceSpanImplTest : public QuicTest {
+ public:
+ QuicMemSliceSpanImplTest() {
+ for (size_t i = 0; i < 10; ++i) {
+ buffers_.push_back(std::make_pair(data_, 1024));
+ }
+ }
+
+ char data_[1024];
+ std::vector<std::pair<char*, size_t>> buffers_;
+};
+
+TEST_F(QuicMemSliceSpanImplTest, ConsumeAll) {
+ SimpleBufferAllocator allocator;
+ QuicTestMemSliceVector vector(buffers_);
+
+ int num_slices = 0;
+ QuicByteCount bytes_consumed =
+ vector.span().ConsumeAll([&](QuicMemSlice slice) {
+ EXPECT_EQ(data_, slice.data());
+ EXPECT_EQ(1024u, slice.length());
+ ++num_slices;
+ });
+
+ EXPECT_EQ(10 * 1024u, bytes_consumed);
+ EXPECT_EQ(10, num_slices);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h
new file mode 100644
index 00000000000..dc48a43d177
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h
@@ -0,0 +1,39 @@
+// 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_MEM_SLICE_STORAGE_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_STORAGE_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/quic/platform/impl/quic_mem_slice_storage_impl.h"
+
+namespace quic {
+
+// QuicMemSliceStorage is a container class that store QuicMemSlices for further
+// use cases such as turning into QuicMemSliceSpan.
+class QUIC_EXPORT_PRIVATE QuicMemSliceStorage {
+ public:
+ QuicMemSliceStorage(const struct iovec* iov,
+ int iov_count,
+ QuicBufferAllocator* allocator,
+ const QuicByteCount max_slice_len)
+ : impl_(iov, iov_count, allocator, max_slice_len) {}
+
+ QuicMemSliceStorage(const QuicMemSliceStorage& other) = default;
+ QuicMemSliceStorage& operator=(const QuicMemSliceStorage& other) = default;
+ QuicMemSliceStorage(QuicMemSliceStorage&& other) = default;
+ QuicMemSliceStorage& operator=(QuicMemSliceStorage&& other) = default;
+
+ ~QuicMemSliceStorage() = default;
+
+ // Return a QuicMemSliceSpan form of the storage.
+ QuicMemSliceSpan ToSpan() { return impl_.ToSpan(); }
+
+ private:
+ QuicMemSliceStorageImpl impl_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_MEM_SLICE_STORAGE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc
new file mode 100644
index 00000000000..d0c25885e9f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc
@@ -0,0 +1,60 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicMemSliceStorageImplTest : public QuicTest {
+ public:
+ QuicMemSliceStorageImplTest() = default;
+};
+
+TEST_F(QuicMemSliceStorageImplTest, EmptyIov) {
+ QuicMemSliceStorage storage(nullptr, 0, nullptr, 1024);
+ EXPECT_TRUE(storage.ToSpan().empty());
+}
+
+TEST_F(QuicMemSliceStorageImplTest, SingleIov) {
+ SimpleBufferAllocator allocator;
+ std::string body(3, 'c');
+ struct iovec iov = {const_cast<char*>(body.data()), body.length()};
+ QuicMemSliceStorage storage(&iov, 1, &allocator, 1024);
+ auto span = storage.ToSpan();
+ EXPECT_EQ("ccc", span.GetData(0));
+ EXPECT_NE(static_cast<const void*>(span.GetData(0).data()), body.data());
+}
+
+TEST_F(QuicMemSliceStorageImplTest, MultipleIovInSingleSlice) {
+ SimpleBufferAllocator allocator;
+ std::string body1(3, 'a');
+ std::string body2(4, 'b');
+ struct iovec iov[] = {{const_cast<char*>(body1.data()), body1.length()},
+ {const_cast<char*>(body2.data()), body2.length()}};
+
+ QuicMemSliceStorage storage(iov, 2, &allocator, 1024);
+ auto span = storage.ToSpan();
+ EXPECT_EQ("aaabbbb", span.GetData(0));
+}
+
+TEST_F(QuicMemSliceStorageImplTest, MultipleIovInMultipleSlice) {
+ SimpleBufferAllocator allocator;
+ std::string body1(4, 'a');
+ std::string body2(4, 'b');
+ struct iovec iov[] = {{const_cast<char*>(body1.data()), body1.length()},
+ {const_cast<char*>(body2.data()), body2.length()}};
+
+ QuicMemSliceStorage storage(iov, 2, &allocator, 4);
+ auto span = storage.ToSpan();
+ EXPECT_EQ("aaaa", span.GetData(0));
+ EXPECT_EQ("bbbb", span.GetData(1));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..185f536271d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc
@@ -0,0 +1,50 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicMemSliceTest : public QuicTest {
+ public:
+ QuicMemSliceTest() {
+ size_t length = 1024;
+ slice_ = QuicMemSlice(&allocator_, length);
+ orig_data_ = slice_.data();
+ orig_length_ = slice_.length();
+ }
+
+ SimpleBufferAllocator allocator_;
+ QuicMemSlice slice_;
+ const char* orig_data_;
+ size_t orig_length_;
+};
+
+TEST_F(QuicMemSliceTest, MoveConstruct) {
+ QuicMemSlice moved(std::move(slice_));
+ EXPECT_EQ(moved.data(), orig_data_);
+ EXPECT_EQ(moved.length(), orig_length_);
+ EXPECT_EQ(nullptr, slice_.data());
+ EXPECT_EQ(0u, slice_.length());
+ EXPECT_TRUE(slice_.empty());
+}
+
+TEST_F(QuicMemSliceTest, MoveAssign) {
+ QuicMemSlice moved;
+ moved = std::move(slice_);
+ EXPECT_EQ(moved.data(), orig_data_);
+ EXPECT_EQ(moved.length(), orig_length_);
+ EXPECT_EQ(nullptr, slice_.data());
+ EXPECT_EQ(0u, slice_.length());
+ EXPECT_TRUE(slice_.empty());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mock_log.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mock_log.h
new file mode 100644
index 00000000000..d1c26931d68
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mock_log.h
@@ -0,0 +1,18 @@
+// 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_MOCK_LOG_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_MOCK_LOG_H_
+
+#include "net/quic/platform/impl/quic_mock_log_impl.h"
+
+using QuicMockLog = QuicMockLogImpl;
+#define CREATE_QUIC_MOCK_LOG(log) CREATE_QUIC_MOCK_LOG_IMPL(log)
+
+#define EXPECT_QUIC_LOG_CALL(log) EXPECT_QUIC_LOG_CALL_IMPL(log)
+
+#define EXPECT_QUIC_LOG_CALL_CONTAINS(log, level, content) \
+ EXPECT_QUIC_LOG_CALL_CONTAINS_IMPL(log, level, content)
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_MOCK_LOG_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.cc
new file mode 100644
index 00000000000..fc0c9b562e9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
+
+namespace quic {
+
+void QuicMutex::WriterLock() {
+ impl_.WriterLock();
+}
+
+void QuicMutex::WriterUnlock() {
+ impl_.WriterUnlock();
+}
+
+void QuicMutex::ReaderLock() {
+ impl_.ReaderLock();
+}
+
+void QuicMutex::ReaderUnlock() {
+ impl_.ReaderUnlock();
+}
+
+void QuicMutex::AssertReaderHeld() const {
+ impl_.AssertReaderHeld();
+}
+
+QuicReaderMutexLock::QuicReaderMutexLock(QuicMutex* lock) : lock_(lock) {
+ lock->ReaderLock();
+}
+
+QuicReaderMutexLock::~QuicReaderMutexLock() {
+ lock_->ReaderUnlock();
+}
+
+QuicWriterMutexLock::QuicWriterMutexLock(QuicMutex* lock) : lock_(lock) {
+ lock->WriterLock();
+}
+
+QuicWriterMutexLock::~QuicWriterMutexLock() {
+ lock_->WriterUnlock();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.h
new file mode 100644
index 00000000000..162863a06b0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_mutex.h
@@ -0,0 +1,88 @@
+// 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 QUICHE_QUIC_PLATFORM_API_QUIC_MUTEX_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_MUTEX_H_
+
+#include "net/quic/platform/impl/quic_mutex_impl.h"
+
+namespace quic {
+
+// A class representing a non-reentrant mutex in QUIC.
+class LOCKABLE QUIC_EXPORT_PRIVATE QuicMutex {
+ public:
+ QuicMutex() = default;
+ QuicMutex(const QuicMutex&) = delete;
+ QuicMutex& operator=(const QuicMutex&) = delete;
+
+ // Block until this Mutex is free, then acquire it exclusively.
+ void WriterLock() EXCLUSIVE_LOCK_FUNCTION();
+
+ // Release this Mutex. Caller must hold it exclusively.
+ void WriterUnlock() UNLOCK_FUNCTION();
+
+ // Block until this Mutex is free or shared, then acquire a share of it.
+ void ReaderLock() SHARED_LOCK_FUNCTION();
+
+ // Release this Mutex. Caller could hold it in shared mode.
+ void ReaderUnlock() UNLOCK_FUNCTION();
+
+ // Returns immediately if current thread holds the Mutex in at least shared
+ // mode. Otherwise, may report an error (typically by crashing with a
+ // diagnostic), or may return immediately.
+ void AssertReaderHeld() const ASSERT_SHARED_LOCK();
+
+ private:
+ QuicLockImpl impl_;
+};
+
+// A helper class that acquires the given QuicMutex shared lock while the
+// QuicReaderMutexLock is in scope.
+class SCOPED_LOCKABLE QUIC_EXPORT_PRIVATE QuicReaderMutexLock {
+ public:
+ explicit QuicReaderMutexLock(QuicMutex* lock) SHARED_LOCK_FUNCTION(lock);
+ QuicReaderMutexLock(const QuicReaderMutexLock&) = delete;
+ QuicReaderMutexLock& operator=(const QuicReaderMutexLock&) = delete;
+
+ ~QuicReaderMutexLock() UNLOCK_FUNCTION();
+
+ private:
+ QuicMutex* const lock_;
+};
+
+// A helper class that acquires the given QuicMutex exclusive lock while the
+// QuicWriterMutexLock is in scope.
+class SCOPED_LOCKABLE QUIC_EXPORT_PRIVATE QuicWriterMutexLock {
+ public:
+ explicit QuicWriterMutexLock(QuicMutex* lock) EXCLUSIVE_LOCK_FUNCTION(lock);
+ QuicWriterMutexLock(const QuicWriterMutexLock&) = delete;
+ QuicWriterMutexLock& operator=(const QuicWriterMutexLock&) = delete;
+
+ ~QuicWriterMutexLock() UNLOCK_FUNCTION();
+
+ private:
+ QuicMutex* const lock_;
+};
+
+// A Notification allows threads to receive notification of a single occurrence
+// of a single event.
+class QUIC_EXPORT_PRIVATE QuicNotification {
+ public:
+ QuicNotification() = default;
+ QuicNotification(const QuicNotification&) = delete;
+ QuicNotification& operator=(const QuicNotification&) = delete;
+
+ bool HasBeenNotified() { return impl_.HasBeenNotified(); }
+
+ void Notify() { impl_.Notify(); }
+
+ void WaitForNotification() { impl_.WaitForNotification(); }
+
+ private:
+ QuicNotificationImpl impl_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_MUTEX_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h
new file mode 100644
index 00000000000..add66827181
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_pcc_sender.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_PCC_SENDER_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_PCC_SENDER_H_
+
+#include "net/quic/platform/impl/quic_pcc_sender_impl.h"
+
+namespace quic {
+
+class SendAlgorithmInterface;
+// Interface for creating a PCC SendAlgorithmInterface
+SendAlgorithmInterface* CreatePccSender(
+ const QuicClock* clock,
+ const RttStats* rtt_stats,
+ const QuicUnackedPacketMap* unacked_packets,
+ QuicRandom* random,
+ QuicConnectionStats* stats,
+ QuicPacketCount initial_congestion_window,
+ QuicPacketCount max_congestion_window) {
+ return CreatePccSenderImpl(clock, rtt_stats, unacked_packets, random, stats,
+ initial_congestion_window, max_congestion_window);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_PCC_SENDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_port_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_port_utils.h
new file mode 100644
index 00000000000..aa85fc5a765
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_port_utils.h
@@ -0,0 +1,26 @@
+// 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_PORT_UTILS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_PORT_UTILS_H_
+
+#include "net/quic/platform/impl/quic_port_utils_impl.h"
+
+namespace quic {
+
+// Returns a UDP port that is currently unused. Check-fails if none are
+// available.
+inline int QuicPickUnusedPortOrDie() {
+ return QuicPickUnusedPortOrDieImpl();
+}
+
+// Indicates that a specified port previously returned by
+// QuicPickUnusedPortOrDie is no longer used.
+inline void QuicRecyclePort(int port) {
+ return QuicRecyclePortImpl(port);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_PORT_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_prefetch.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_prefetch.h
new file mode 100644
index 00000000000..49dbe4871e8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_prefetch.h
@@ -0,0 +1,39 @@
+// 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_PREFETCH_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_PREFETCH_H_
+
+#include "net/quic/platform/impl/quic_prefetch_impl.h"
+
+namespace quic {
+
+// Move data into the cache before it is read, or "prefetch" it.
+//
+// The value of `addr` is the address of the memory to prefetch. If
+// the target and compiler support it, data prefetch instructions are
+// generated. If the prefetch is done some time before the memory is
+// read, it may be in the cache by the time the read occurs.
+//
+// The function names specify the temporal locality heuristic applied,
+// using the names of Intel prefetch instructions:
+//
+// T0 - high degree of temporal locality; data should be left in as
+// many levels of the cache possible
+// T1 - moderate degree of temporal locality
+// T2 - low degree of temporal locality
+// Nta - no temporal locality, data need not be left in the cache
+// after the read
+//
+// Incorrect or gratuitous use of these functions can degrade
+// performance, so use them only when representative benchmarks show
+// an improvement.
+
+inline void QuicPrefetchT0(const void* addr) {
+ return QuicPrefetchT0Impl(addr);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_PREFETCH_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h
new file mode 100644
index 00000000000..d0ed4606cbb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h
@@ -0,0 +1,27 @@
+// 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_PTR_UTIL_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_PTR_UTIL_H_
+
+#include <memory>
+#include <utility>
+
+#include "net/quic/platform/impl/quic_ptr_util_impl.h"
+
+namespace quic {
+
+template <typename T, typename... Args>
+std::unique_ptr<T> QuicMakeUnique(Args&&... args) {
+ return QuicMakeUniqueImpl<T>(std::forward<Args>(args)...);
+}
+
+template <typename T>
+std::unique_ptr<T> QuicWrapUnique(T* ptr) {
+ return QuicWrapUniqueImpl<T>(ptr);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_PTR_UTIL_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h
new file mode 100644
index 00000000000..6ffc237db36
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h
@@ -0,0 +1,162 @@
+// 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 QUICHE_QUIC_PLATFORM_API_QUIC_REFERENCE_COUNTED_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_REFERENCE_COUNTED_H_
+
+#include "net/quic/platform/impl/quic_reference_counted_impl.h"
+
+namespace quic {
+
+// Base class for explicitly reference-counted objects in QUIC.
+class QUIC_EXPORT_PRIVATE QuicReferenceCounted
+ : public QuicReferenceCountedImpl {
+ public:
+ QuicReferenceCounted() {}
+
+ protected:
+ ~QuicReferenceCounted() override {}
+};
+
+// A class representing a reference counted pointer in QUIC.
+//
+// Construct or initialize QuicReferenceCountedPointer from raw pointer. Here
+// raw pointer MUST be a newly created object. Reference count of a newly
+// created object is undefined, but that will be 1 after being added to
+// QuicReferenceCountedPointer.
+// QuicReferenceCountedPointer is used as a local variable.
+// QuicReferenceCountedPointer<T> r_ptr(new T());
+// or, equivalently:
+// QuicReferenceCountedPointer<T> r_ptr;
+// T* p = new T();
+// r_ptr = T;
+//
+// QuicReferenceCountedPointer is used as a member variable:
+// MyClass::MyClass() : r_ptr(new T()) {}
+//
+// This is WRONG, since *p is not guaranteed to be newly created:
+// MyClass::MyClass(T* p) : r_ptr(p) {}
+//
+// Given an existing QuicReferenceCountedPointer, create a duplicate that has
+// its own reference on the object:
+// QuicReferenceCountedPointer<T> r_ptr_b(r_ptr_a);
+// or, equivalently:
+// QuicReferenceCountedPointer<T> r_ptr_b = r_ptr_a;
+//
+// Given an existing QuicReferenceCountedPointer, create a
+// QuicReferenceCountedPointer that adopts the reference:
+// QuicReferenceCountedPointer<T> r_ptr_b(std::move(r_ptr_a));
+// or, equivalently:
+// QuicReferenceCountedPointer<T> r_ptr_b = std::move(r_ptr_a);
+
+template <class T>
+class QuicReferenceCountedPointer {
+ public:
+ QuicReferenceCountedPointer() = default;
+
+ // Constructor from raw pointer |p|. This guarantees that the reference count
+ // of *p is 1. This should be only called when a new object is created.
+ // Calling this on an already existent object does not increase its reference
+ // count.
+ explicit QuicReferenceCountedPointer(T* p) : impl_(p) {}
+
+ // Allows implicit conversion from nullptr.
+ QuicReferenceCountedPointer(std::nullptr_t) : impl_(nullptr) {} // NOLINT
+
+ // Copy and copy conversion constructors. It does not take the reference away
+ // from |other| and they each end up with their own reference.
+ template <typename U>
+ QuicReferenceCountedPointer( // NOLINT
+ const QuicReferenceCountedPointer<U>& other)
+ : impl_(other.impl()) {}
+ QuicReferenceCountedPointer(const QuicReferenceCountedPointer& other)
+ : impl_(other.impl()) {}
+
+ // Move constructors. After move, it adopts the reference from |other|.
+ template <typename U>
+ QuicReferenceCountedPointer(QuicReferenceCountedPointer<U>&& other) // NOLINT
+ : impl_(std::move(other.impl())) {}
+ QuicReferenceCountedPointer(QuicReferenceCountedPointer&& other)
+ : impl_(std::move(other.impl())) {}
+
+ ~QuicReferenceCountedPointer() = default;
+
+ // Copy assignments.
+ QuicReferenceCountedPointer& operator=(
+ const QuicReferenceCountedPointer& other) {
+ impl_ = other.impl();
+ return *this;
+ }
+ template <typename U>
+ QuicReferenceCountedPointer<T>& operator=(
+ const QuicReferenceCountedPointer<U>& other) {
+ impl_ = other.impl();
+ return *this;
+ }
+
+ // Move assignments.
+ QuicReferenceCountedPointer& operator=(QuicReferenceCountedPointer&& other) {
+ impl_ = std::move(other.impl());
+ return *this;
+ }
+ template <typename U>
+ QuicReferenceCountedPointer<T>& operator=(
+ QuicReferenceCountedPointer<U>&& other) {
+ impl_ = std::move(other.impl());
+ return *this;
+ }
+
+ // Accessors for the referenced object.
+ // operator*() and operator->() will assert() if there is no current object.
+ T& operator*() const { return *impl_; }
+ T* operator->() const { return impl_.get(); }
+
+ explicit operator bool() const { return static_cast<bool>(impl_); }
+
+ // Assignment operator on raw pointer. Drops a reference to current pointee,
+ // if any, and replaces it with |p|. This guarantees that the reference count
+ // of *p is 1. This should only be used when a new object is created. Calling
+ // this on an already existent object is undefined behavior.
+ QuicReferenceCountedPointer<T>& operator=(T* p) {
+ impl_ = p;
+ return *this;
+ }
+
+ // Returns the raw pointer with no change in reference count.
+ T* get() const { return impl_.get(); }
+
+ QuicReferenceCountedPointerImpl<T>& impl() { return impl_; }
+ const QuicReferenceCountedPointerImpl<T>& impl() const { return impl_; }
+
+ // Comparisons against same type.
+ friend bool operator==(const QuicReferenceCountedPointer& a,
+ const QuicReferenceCountedPointer& b) {
+ return a.get() == b.get();
+ }
+ friend bool operator!=(const QuicReferenceCountedPointer& a,
+ const QuicReferenceCountedPointer& b) {
+ return a.get() != b.get();
+ }
+
+ // Comparisons against nullptr.
+ friend bool operator==(const QuicReferenceCountedPointer& a, std::nullptr_t) {
+ return a.get() == nullptr;
+ }
+ friend bool operator==(std::nullptr_t, const QuicReferenceCountedPointer& b) {
+ return nullptr == b.get();
+ }
+ friend bool operator!=(const QuicReferenceCountedPointer& a, std::nullptr_t) {
+ return a.get() != nullptr;
+ }
+ friend bool operator!=(std::nullptr_t, const QuicReferenceCountedPointer& b) {
+ return nullptr != b.get();
+ }
+
+ private:
+ QuicReferenceCountedPointerImpl<T> impl_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_REFERENCE_COUNTED_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_reference_counted_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_reference_counted_test.cc
new file mode 100644
index 00000000000..a1932ed55d2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_reference_counted_test.cc
@@ -0,0 +1,173 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class Base : public QuicReferenceCounted {
+ public:
+ explicit Base(bool* destroyed) : destroyed_(destroyed) {
+ *destroyed_ = false;
+ }
+
+ protected:
+ ~Base() override { *destroyed_ = true; }
+
+ private:
+ bool* destroyed_;
+};
+
+class Derived : public Base {
+ public:
+ explicit Derived(bool* destroyed) : Base(destroyed) {}
+
+ private:
+ ~Derived() override {}
+};
+
+class QuicReferenceCountedTest : public QuicTest {};
+
+TEST_F(QuicReferenceCountedTest, DefaultConstructor) {
+ QuicReferenceCountedPointer<Base> a;
+ EXPECT_EQ(nullptr, a);
+ EXPECT_EQ(nullptr, a.get());
+ EXPECT_FALSE(a);
+}
+
+TEST_F(QuicReferenceCountedTest, ConstructFromRawPointer) {
+ bool destroyed = false;
+ {
+ QuicReferenceCountedPointer<Base> a(new Base(&destroyed));
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, RawPointerAssignment) {
+ bool destroyed = false;
+ {
+ QuicReferenceCountedPointer<Base> a;
+ Base* rct = new Base(&destroyed);
+ a = rct;
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, PointerCopy) {
+ bool destroyed = false;
+ {
+ QuicReferenceCountedPointer<Base> a(new Base(&destroyed));
+ {
+ QuicReferenceCountedPointer<Base> b(a);
+ EXPECT_EQ(a, b);
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, PointerCopyAssignment) {
+ bool destroyed = false;
+ {
+ QuicReferenceCountedPointer<Base> a(new Base(&destroyed));
+ {
+ QuicReferenceCountedPointer<Base> b = a;
+ EXPECT_EQ(a, b);
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, PointerCopyFromOtherType) {
+ bool destroyed = false;
+ {
+ QuicReferenceCountedPointer<Derived> a(new Derived(&destroyed));
+ {
+ QuicReferenceCountedPointer<Base> b(a);
+ EXPECT_EQ(a.get(), b.get());
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, PointerCopyAssignmentFromOtherType) {
+ bool destroyed = false;
+ {
+ QuicReferenceCountedPointer<Derived> a(new Derived(&destroyed));
+ {
+ QuicReferenceCountedPointer<Base> b = a;
+ EXPECT_EQ(a.get(), b.get());
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, PointerMove) {
+ bool destroyed = false;
+ QuicReferenceCountedPointer<Base> a(new Derived(&destroyed));
+ EXPECT_FALSE(destroyed);
+ QuicReferenceCountedPointer<Base> b(std::move(a));
+ EXPECT_FALSE(destroyed);
+ EXPECT_NE(nullptr, b);
+ EXPECT_EQ(nullptr, a); // NOLINT
+
+ b = nullptr;
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, PointerMoveAssignment) {
+ bool destroyed = false;
+ QuicReferenceCountedPointer<Base> a(new Derived(&destroyed));
+ EXPECT_FALSE(destroyed);
+ QuicReferenceCountedPointer<Base> b = std::move(a);
+ EXPECT_FALSE(destroyed);
+ EXPECT_NE(nullptr, b);
+ EXPECT_EQ(nullptr, a); // NOLINT
+
+ b = nullptr;
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, PointerMoveFromOtherType) {
+ bool destroyed = false;
+ QuicReferenceCountedPointer<Derived> a(new Derived(&destroyed));
+ EXPECT_FALSE(destroyed);
+ QuicReferenceCountedPointer<Base> b(std::move(a));
+ EXPECT_FALSE(destroyed);
+ EXPECT_NE(nullptr, b);
+ EXPECT_EQ(nullptr, a); // NOLINT
+
+ b = nullptr;
+ EXPECT_TRUE(destroyed);
+}
+
+TEST_F(QuicReferenceCountedTest, PointerMoveAssignmentFromOtherType) {
+ bool destroyed = false;
+ QuicReferenceCountedPointer<Derived> a(new Derived(&destroyed));
+ EXPECT_FALSE(destroyed);
+ QuicReferenceCountedPointer<Base> b = std::move(a);
+ EXPECT_FALSE(destroyed);
+ EXPECT_NE(nullptr, b);
+ EXPECT_EQ(nullptr, a); // NOLINT
+
+ b = nullptr;
+ EXPECT_TRUE(destroyed);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_server_stats.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_server_stats.h
new file mode 100644
index 00000000000..3c4223e4cf8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_server_stats.h
@@ -0,0 +1,82 @@
+// Copyright 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_SERVER_STATS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_SERVER_STATS_H_
+
+#include "net/quic/platform/impl/quic_server_stats_impl.h"
+
+namespace quic {
+
+//------------------------------------------------------------------------------
+// Enumeration histograms.
+//
+// Sample usage:
+// // In Chrome, these values are persisted to logs. Entries should not be
+// // renumbered and numeric values should never be reused.
+// enum class MyEnum {
+// FIRST_VALUE = 0,
+// SECOND_VALUE = 1,
+// ...
+// FINAL_VALUE = N,
+// COUNT
+// };
+// QUIC_SERVER_HISTOGRAM_ENUM("My.Enumeration", MyEnum::SOME_VALUE,
+// MyEnum::COUNT, "Number of time $foo equals to some enum value");
+//
+// Note: The value in |sample| must be strictly less than |enum_size|.
+
+#define QUIC_SERVER_HISTOGRAM_ENUM(name, sample, enum_size, docstring) \
+ QUIC_SERVER_HISTOGRAM_ENUM_IMPL(name, sample, enum_size, docstring)
+
+//------------------------------------------------------------------------------
+// Histogram for boolean values.
+
+// Sample usage:
+// QUIC_SERVER_HISTOGRAM_BOOL("My.Boolean", bool,
+// "Number of times $foo is true or false");
+#define QUIC_SERVER_HISTOGRAM_BOOL(name, sample, docstring) \
+ QUIC_SERVER_HISTOGRAM_BOOL_IMPL(name, sample, docstring)
+
+//------------------------------------------------------------------------------
+// Timing histograms. These are used for collecting timing data (generally
+// latencies).
+
+// These macros create exponentially sized histograms (lengths of the bucket
+// ranges exponentially increase as the sample range increases). The units for
+// sample and max are unspecified, but they must be the same for one histogram.
+
+// Sample usage:
+// QUIC_SERVER_HISTOGRAM_TIMES("Very.Long.Timing.Histogram", time_delta,
+// QuicTime::Delta::FromSeconds(1), QuicTime::Delta::FromSecond(3600 *
+// 24), 100, "Time spent in doing operation.");
+#define QUIC_SERVER_HISTOGRAM_TIMES(name, sample, min, max, bucket_count, \
+ docstring) \
+ QUIC_SERVER_HISTOGRAM_TIMES_IMPL(name, sample, min, max, bucket_count, \
+ docstring)
+
+//------------------------------------------------------------------------------
+// Count histograms. These are used for collecting numeric data.
+
+// These macros default to exponential histograms - i.e. the lengths of the
+// bucket ranges exponentially increase as the sample range increases.
+
+// All of these macros must be called with |name| as a runtime constant.
+
+// Any data outside the range here will be put in underflow and overflow
+// buckets. Min values should be >=1 as emitted 0s will still go into the
+// underflow bucket.
+
+// Sample usage:
+// QUIC_SERVER_SERVER_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", 1, 100000000,
+// 100, "Counters of hitting certian code.");
+
+#define QUIC_SERVER_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count, \
+ docstring) \
+ QUIC_SERVER_HISTOGRAM_COUNTS_IMPL(name, sample, min, max, bucket_count, \
+ docstring)
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_SERVER_STATS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_sleep.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_sleep.h
new file mode 100644
index 00000000000..12c20df536c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_sleep.h
@@ -0,0 +1,19 @@
+// Copyright 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_SLEEP_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_SLEEP_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/quic/platform/impl/quic_sleep_impl.h"
+
+namespace quic {
+
+inline void QuicSleep(QuicTime::Delta duration) {
+ QuicSleepImpl(duration);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_SLEEP_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.cc
new file mode 100644
index 00000000000..dacd6ac3505
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+
+namespace quic {
+
+QuicSocketAddress::QuicSocketAddress(QuicIpAddress address, uint16_t port)
+ : impl_(address.impl(), port) {}
+
+QuicSocketAddress::QuicSocketAddress(const struct sockaddr_storage& saddr)
+ : impl_(saddr) {}
+
+QuicSocketAddress::QuicSocketAddress(const struct sockaddr& saddr)
+ : impl_(saddr) {}
+
+QuicSocketAddress::QuicSocketAddress(const QuicSocketAddressImpl& impl)
+ : impl_(impl) {}
+
+bool operator==(const QuicSocketAddress& lhs, const QuicSocketAddress& rhs) {
+ return lhs.impl_ == rhs.impl_;
+}
+
+bool operator!=(const QuicSocketAddress& lhs, const QuicSocketAddress& rhs) {
+ return lhs.impl_ != rhs.impl_;
+}
+
+bool QuicSocketAddress::IsInitialized() const {
+ return impl_.IsInitialized();
+}
+
+std::string QuicSocketAddress::ToString() const {
+ return impl_.ToString();
+}
+
+int QuicSocketAddress::FromSocket(int fd) {
+ return impl_.FromSocket(fd);
+}
+
+QuicSocketAddress QuicSocketAddress::Normalized() const {
+ return QuicSocketAddress(impl_.Normalized());
+}
+
+QuicIpAddress QuicSocketAddress::host() const {
+ return QuicIpAddress(impl_.host());
+}
+
+uint16_t QuicSocketAddress::port() const {
+ return impl_.port();
+}
+
+sockaddr_storage QuicSocketAddress::generic_address() const {
+ return impl_.generic_address();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.h
new file mode 100644
index 00000000000..57689a77828
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_socket_address.h
@@ -0,0 +1,50 @@
+// 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 QUICHE_QUIC_PLATFORM_API_QUIC_SOCKET_ADDRESS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_SOCKET_ADDRESS_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h"
+#include "net/quic/platform/impl/quic_socket_address_impl.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE QuicSocketAddress {
+ // A class representing a socket endpoint address (i.e., IP address plus a
+ // port) in QUIC. The actual implementation (platform dependent) of a socket
+ // address is in QuicSocketAddressImpl.
+ public:
+ QuicSocketAddress() = default;
+ QuicSocketAddress(QuicIpAddress address, uint16_t port);
+ explicit QuicSocketAddress(const struct sockaddr_storage& saddr);
+ explicit QuicSocketAddress(const struct sockaddr& saddr);
+ explicit QuicSocketAddress(const QuicSocketAddressImpl& impl);
+ QuicSocketAddress(const QuicSocketAddress& other) = default;
+ QuicSocketAddress& operator=(const QuicSocketAddress& other) = default;
+ QuicSocketAddress& operator=(QuicSocketAddress&& other) = default;
+ QUIC_EXPORT_PRIVATE friend bool operator==(const QuicSocketAddress& lhs,
+ const QuicSocketAddress& rhs);
+ QUIC_EXPORT_PRIVATE friend bool operator!=(const QuicSocketAddress& lhs,
+ const QuicSocketAddress& rhs);
+
+ bool IsInitialized() const;
+ std::string ToString() const;
+ int FromSocket(int fd);
+ QuicSocketAddress Normalized() const;
+
+ QuicIpAddress host() const;
+ uint16_t port() const;
+ sockaddr_storage generic_address() const;
+ const QuicSocketAddressImpl& impl() const { return impl_; }
+
+ private:
+ QuicSocketAddressImpl impl_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_SOCKET_ADDRESS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h
new file mode 100644
index 00000000000..ff6c887de6b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h
@@ -0,0 +1,20 @@
+// 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_STACK_TRACE_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_STACK_TRACE_H_
+
+#include <string>
+
+#include "net/quic/platform/impl/quic_stack_trace_impl.h"
+
+namespace quic {
+
+inline std::string QuicStackTrace() {
+ return QuicStackTraceImpl();
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_STACK_TRACE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_str_cat.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_str_cat.h
new file mode 100644
index 00000000000..534e31e49bc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_str_cat.h
@@ -0,0 +1,28 @@
+// 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 QUICHE_QUIC_PLATFORM_API_QUIC_STR_CAT_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_STR_CAT_H_
+
+#include <string>
+#include <utility>
+
+#include "net/quic/platform/impl/quic_str_cat_impl.h"
+
+namespace quic {
+
+// Merges given strings or numbers with no delimiter.
+template <typename... Args>
+inline std::string QuicStrCat(const Args&... args) {
+ return QuicStrCatImpl(std::forward<const Args&>(args)...);
+}
+
+template <typename... Args>
+inline std::string QuicStringPrintf(const Args&... args) {
+ return QuicStringPrintfImpl(std::forward<const Args&>(args)...);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_STR_CAT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_str_cat_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_str_cat_test.cc
new file mode 100644
index 00000000000..06941bb5b4a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_str_cat_test.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicStrCatTest : public QuicTest {};
+
+TEST_F(QuicStrCatTest, Ints) {
+ const int16_t s = -1;
+ const uint16_t us = 2;
+ const int i = -3;
+ const uint32_t ui = 4;
+ const int64_t l = -5;
+ const uint64_t ul = 6;
+ const ptrdiff_t ptrdiff = -7;
+ const size_t size = 8;
+ const intptr_t intptr = -9;
+ const uintptr_t uintptr = 10;
+ std::string answer;
+ answer = QuicStrCat(s, us);
+ EXPECT_EQ(answer, "-12");
+ answer = QuicStrCat(i, ui);
+ EXPECT_EQ(answer, "-34");
+ answer = QuicStrCat(l, ul);
+ EXPECT_EQ(answer, "-56");
+ answer = QuicStrCat(ptrdiff, size);
+ EXPECT_EQ(answer, "-78");
+ answer = QuicStrCat(size, intptr);
+ EXPECT_EQ(answer, "8-9");
+ answer = QuicStrCat(uintptr, 0);
+ EXPECT_EQ(answer, "100");
+}
+
+TEST_F(QuicStrCatTest, Basics) {
+ std::string result;
+
+ std::string strs[] = {"Hello", "Cruel", "World"};
+
+ QuicStringPiece pieces[] = {"Hello", "Cruel", "World"};
+
+ const char* c_strs[] = {"Hello", "Cruel", "World"};
+
+ int32_t i32s[] = {'H', 'C', 'W'};
+ uint64_t ui64s[] = {12345678910LL, 10987654321LL};
+
+ result = QuicStrCat(false, true, 2, 3);
+ EXPECT_EQ(result, "0123");
+
+ result = QuicStrCat(-1);
+ EXPECT_EQ(result, "-1");
+
+ result = QuicStrCat(0.5);
+ EXPECT_EQ(result, "0.5");
+
+ result = QuicStrCat(strs[1], pieces[2]);
+ EXPECT_EQ(result, "CruelWorld");
+
+ result = QuicStrCat(strs[0], ", ", pieces[2]);
+ EXPECT_EQ(result, "Hello, World");
+
+ result = QuicStrCat(strs[0], ", ", strs[1], " ", strs[2], "!");
+ EXPECT_EQ(result, "Hello, Cruel World!");
+
+ result = QuicStrCat(pieces[0], ", ", pieces[1], " ", pieces[2]);
+ EXPECT_EQ(result, "Hello, Cruel World");
+
+ result = QuicStrCat(c_strs[0], ", ", c_strs[1], " ", c_strs[2]);
+ EXPECT_EQ(result, "Hello, Cruel World");
+
+ result = QuicStrCat("ASCII ", i32s[0], ", ", i32s[1], " ", i32s[2], "!");
+ EXPECT_EQ(result, "ASCII 72, 67 87!");
+
+ result = QuicStrCat(ui64s[0], ", ", ui64s[1], "!");
+ EXPECT_EQ(result, "12345678910, 10987654321!");
+
+ std::string one = "1";
+ result = QuicStrCat("And a ", one.size(), " and a ", &result[2] - &result[0],
+ " and a ", one, " 2 3 4", "!");
+ EXPECT_EQ(result, "And a 1 and a 2 and a 1 2 3 4!");
+
+ result =
+ QuicStrCat("To output a char by ASCII/numeric value, use +: ", '!' + 0);
+ EXPECT_EQ(result, "To output a char by ASCII/numeric value, use +: 33");
+
+ float f = 10000.5;
+ result = QuicStrCat("Ten K and a half is ", f);
+ EXPECT_EQ(result, "Ten K and a half is 10000.5");
+
+ double d = 99999.9;
+ result = QuicStrCat("This double number is ", d);
+ EXPECT_EQ(result, "This double number is 99999.9");
+
+ result =
+ QuicStrCat(1, 22, 333, 4444, 55555, 666666, 7777777, 88888888, 999999999);
+ EXPECT_EQ(result, "122333444455555666666777777788888888999999999");
+}
+
+TEST_F(QuicStrCatTest, MaxArgs) {
+ std::string result;
+ // Test 10 up to 26 arguments, the current maximum
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a");
+ EXPECT_EQ(result, "123456789a");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b");
+ EXPECT_EQ(result, "123456789ab");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c");
+ EXPECT_EQ(result, "123456789abc");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d");
+ EXPECT_EQ(result, "123456789abcd");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e");
+ EXPECT_EQ(result, "123456789abcde");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f");
+ EXPECT_EQ(result, "123456789abcdef");
+ result =
+ QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f", "g");
+ EXPECT_EQ(result, "123456789abcdefg");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h");
+ EXPECT_EQ(result, "123456789abcdefgh");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i");
+ EXPECT_EQ(result, "123456789abcdefghi");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j");
+ EXPECT_EQ(result, "123456789abcdefghij");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k");
+ EXPECT_EQ(result, "123456789abcdefghijk");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k", "l");
+ EXPECT_EQ(result, "123456789abcdefghijkl");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k", "l", "m");
+ EXPECT_EQ(result, "123456789abcdefghijklm");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k", "l", "m", "n");
+ EXPECT_EQ(result, "123456789abcdefghijklmn");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k", "l", "m", "n", "o");
+ EXPECT_EQ(result, "123456789abcdefghijklmno");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k", "l", "m", "n", "o", "p");
+ EXPECT_EQ(result, "123456789abcdefghijklmnop");
+ result = QuicStrCat(1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q");
+ EXPECT_EQ(result, "123456789abcdefghijklmnopq");
+ // No limit thanks to C++11's variadic templates
+ result = QuicStrCat(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "a", "b", "c", "d", "e", "f", "g", "h",
+ "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w",
+ "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L",
+ "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");
+ EXPECT_EQ(result,
+ "12345678910abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_stream_buffer_allocator.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_stream_buffer_allocator.h
new file mode 100644
index 00000000000..ea1cc9fd5ec
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_stream_buffer_allocator.h
@@ -0,0 +1,16 @@
+// 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_STREAM_BUFFER_ALLOCATOR_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_STREAM_BUFFER_ALLOCATOR_H_
+
+#include "net/quic/platform/impl/quic_stream_buffer_allocator_impl.h"
+
+namespace quic {
+
+using QuicStreamBufferAllocator = QuicStreamBufferAllocatorImpl;
+
+}
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_STREAM_BUFFER_ALLOCATOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_piece.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_piece.h
new file mode 100644
index 00000000000..91cb1831181
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_piece.h
@@ -0,0 +1,16 @@
+// 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_STRING_PIECE_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_STRING_PIECE_H_
+
+#include "net/quic/platform/impl/quic_string_piece_impl.h"
+
+namespace quic {
+
+using QuicStringPiece = QuicStringPieceImpl;
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_STRING_PIECE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_utils.h
new file mode 100644
index 00000000000..a30833e69be
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_utils.h
@@ -0,0 +1,23 @@
+// 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_STRING_UTILS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_STRING_UTILS_H_
+
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/quic/platform/impl/quic_string_utils_impl.h"
+
+namespace quic {
+
+template <typename... Args>
+inline void QuicStrAppend(std::string* output, const Args&... args) {
+ QuicStrAppendImpl(output, std::forward<const Args&>(args)...);
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_STRING_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_utils_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_utils_test.cc
new file mode 100644
index 00000000000..e2f13005846
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_string_utils_test.cc
@@ -0,0 +1,178 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h"
+
+#include <cstdint>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+TEST(QuicStringUtilsTest, QuicStrCat) {
+ // No arguments.
+ EXPECT_EQ("", QuicStrCat());
+
+ // Single string-like argument.
+ const char kFoo[] = "foo";
+ const std::string string_foo(kFoo);
+ const QuicStringPiece stringpiece_foo(string_foo);
+ EXPECT_EQ("foo", QuicStrCat(kFoo));
+ EXPECT_EQ("foo", QuicStrCat(string_foo));
+ EXPECT_EQ("foo", QuicStrCat(stringpiece_foo));
+
+ // Two string-like arguments.
+ const char kBar[] = "bar";
+ const QuicStringPiece stringpiece_bar(kBar);
+ const std::string string_bar(kBar);
+ EXPECT_EQ("foobar", QuicStrCat(kFoo, kBar));
+ EXPECT_EQ("foobar", QuicStrCat(kFoo, string_bar));
+ EXPECT_EQ("foobar", QuicStrCat(kFoo, stringpiece_bar));
+ EXPECT_EQ("foobar", QuicStrCat(string_foo, kBar));
+ EXPECT_EQ("foobar", QuicStrCat(string_foo, string_bar));
+ EXPECT_EQ("foobar", QuicStrCat(string_foo, stringpiece_bar));
+ EXPECT_EQ("foobar", QuicStrCat(stringpiece_foo, kBar));
+ EXPECT_EQ("foobar", QuicStrCat(stringpiece_foo, string_bar));
+ EXPECT_EQ("foobar", QuicStrCat(stringpiece_foo, stringpiece_bar));
+
+ // Many-many arguments.
+ EXPECT_EQ(
+ "foobarbazquxquuxquuzcorgegraultgarplywaldofredplughxyzzythud",
+ QuicStrCat("foo", "bar", "baz", "qux", "quux", "quuz", "corge", "grault",
+ "garply", "waldo", "fred", "plugh", "xyzzy", "thud"));
+
+ // Numerical arguments.
+ const int16_t i = 1;
+ const uint64_t u = 8;
+ const double d = 3.1415;
+
+ EXPECT_EQ("1 8", QuicStrCat(i, " ", u));
+ EXPECT_EQ("3.14151181", QuicStrCat(d, i, i, u, i));
+ EXPECT_EQ("i: 1, u: 8, d: 3.1415",
+ QuicStrCat("i: ", i, ", u: ", u, ", d: ", d));
+
+ // Boolean arguments.
+ const bool t = true;
+ const bool f = false;
+
+ EXPECT_EQ("1", QuicStrCat(t));
+ EXPECT_EQ("0", QuicStrCat(f));
+ EXPECT_EQ("0110", QuicStrCat(f, t, t, f));
+
+ // Mixed string-like, numerical, and Boolean arguments.
+ EXPECT_EQ("foo1foo081bar3.14151",
+ QuicStrCat(kFoo, i, string_foo, f, u, t, stringpiece_bar, d, t));
+ EXPECT_EQ("3.141511bar18bar13.14150",
+ QuicStrCat(d, t, t, string_bar, i, u, kBar, t, d, f));
+}
+
+TEST(QuicStringUtilsTest, QuicStrAppend) {
+ // No arguments on empty string.
+ std::string output;
+ QuicStrAppend(&output);
+ EXPECT_TRUE(output.empty());
+
+ // Single string-like argument.
+ const char kFoo[] = "foo";
+ const std::string string_foo(kFoo);
+ const QuicStringPiece stringpiece_foo(string_foo);
+ QuicStrAppend(&output, kFoo);
+ EXPECT_EQ("foo", output);
+ QuicStrAppend(&output, string_foo);
+ EXPECT_EQ("foofoo", output);
+ QuicStrAppend(&output, stringpiece_foo);
+ EXPECT_EQ("foofoofoo", output);
+
+ // No arguments on non-empty string.
+ QuicStrAppend(&output);
+ EXPECT_EQ("foofoofoo", output);
+
+ output.clear();
+
+ // Two string-like arguments.
+ const char kBar[] = "bar";
+ const QuicStringPiece stringpiece_bar(kBar);
+ const std::string string_bar(kBar);
+ QuicStrAppend(&output, kFoo, kBar);
+ EXPECT_EQ("foobar", output);
+ QuicStrAppend(&output, kFoo, string_bar);
+ EXPECT_EQ("foobarfoobar", output);
+ QuicStrAppend(&output, kFoo, stringpiece_bar);
+ EXPECT_EQ("foobarfoobarfoobar", output);
+ QuicStrAppend(&output, string_foo, kBar);
+ EXPECT_EQ("foobarfoobarfoobarfoobar", output);
+
+ output.clear();
+
+ QuicStrAppend(&output, string_foo, string_bar);
+ EXPECT_EQ("foobar", output);
+ QuicStrAppend(&output, string_foo, stringpiece_bar);
+ EXPECT_EQ("foobarfoobar", output);
+ QuicStrAppend(&output, stringpiece_foo, kBar);
+ EXPECT_EQ("foobarfoobarfoobar", output);
+ QuicStrAppend(&output, stringpiece_foo, string_bar);
+ EXPECT_EQ("foobarfoobarfoobarfoobar", output);
+
+ output.clear();
+
+ QuicStrAppend(&output, stringpiece_foo, stringpiece_bar);
+ EXPECT_EQ("foobar", output);
+
+ // Many-many arguments.
+ QuicStrAppend(&output, "foo", "bar", "baz", "qux", "quux", "quuz", "corge",
+ "grault", "garply", "waldo", "fred", "plugh", "xyzzy", "thud");
+ EXPECT_EQ(
+ "foobarfoobarbazquxquuxquuzcorgegraultgarplywaldofredplughxyzzythud",
+ output);
+
+ output.clear();
+
+ // Numerical arguments.
+ const int16_t i = 1;
+ const uint64_t u = 8;
+ const double d = 3.1415;
+
+ QuicStrAppend(&output, i, " ", u);
+ EXPECT_EQ("1 8", output);
+ QuicStrAppend(&output, d, i, i, u, i);
+ EXPECT_EQ("1 83.14151181", output);
+ QuicStrAppend(&output, "i: ", i, ", u: ", u, ", d: ", d);
+ EXPECT_EQ("1 83.14151181i: 1, u: 8, d: 3.1415", output);
+
+ output.clear();
+
+ // Boolean arguments.
+ const bool t = true;
+ const bool f = false;
+
+ QuicStrAppend(&output, t);
+ EXPECT_EQ("1", output);
+ QuicStrAppend(&output, f);
+ EXPECT_EQ("10", output);
+ QuicStrAppend(&output, f, t, t, f);
+ EXPECT_EQ("100110", output);
+
+ output.clear();
+
+ // Mixed string-like, numerical, and Boolean arguments.
+ QuicStrAppend(&output, kFoo, i, string_foo, f, u, t, stringpiece_bar, d, t);
+ EXPECT_EQ("foo1foo081bar3.14151", output);
+ QuicStrAppend(&output, d, t, t, string_bar, i, u, kBar, t, d, f);
+ EXPECT_EQ("foo1foo081bar3.141513.141511bar18bar13.14150", output);
+}
+
+TEST(QuicStringUtilsTest, QuicStringPrintf) {
+ EXPECT_EQ("", QuicStringPrintf("%s", ""));
+ EXPECT_EQ("foobar", QuicStringPrintf("%sbar", "foo"));
+ EXPECT_EQ("foobar", QuicStringPrintf("%s%s", "foo", "bar"));
+ EXPECT_EQ("foo: 1, bar: 2.0", QuicStringPrintf("foo: %d, bar: %.1f", 1, 2.0));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h
new file mode 100644
index 00000000000..3103d4e0e8c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h
@@ -0,0 +1,16 @@
+// 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.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_SYSTEM_EVENT_LOOP_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_SYSTEM_EVENT_LOOP_H_
+
+#include "net/quic/platform/impl/quic_system_event_loop_impl.h"
+
+inline void QuicRunSystemEventLoopIteration() {
+ QuicRunSystemEventLoopIterationImpl();
+}
+
+using QuicSystemEventLoop = QuicSystemEventLoopImpl;
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_SYSTEM_EVENT_LOOP_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test.h
new file mode 100644
index 00000000000..f4d255ab0b8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test.h
@@ -0,0 +1,27 @@
+// 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_TEST_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_TEST_H_
+
+#include "net/quic/platform/impl/quic_test_impl.h"
+
+using QuicFlagSaver = QuicFlagSaverImpl;
+
+// Defines the base classes to be used in QUIC tests.
+using QuicTest = QuicTestImpl;
+template <class T>
+using QuicTestWithParam = QuicTestWithParamImpl<T>;
+
+// Class which needs to be instantiated in tests which use threads.
+using ScopedEnvironmentForThreads = ScopedEnvironmentForThreadsImpl;
+
+#define QUIC_TEST_DISABLED_IN_CHROME(name) \
+ QUIC_TEST_DISABLED_IN_CHROME_IMPL(name)
+
+inline std::string QuicGetTestMemoryCachePath() {
+ return QuicGetTestMemoryCachePathImpl();
+}
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_TEST_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_loopback.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_loopback.cc
new file mode 100644
index 00000000000..febcf3731de
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_loopback.cc
@@ -0,0 +1,29 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
+
+namespace quic {
+
+IpAddressFamily AddressFamilyUnderTest() {
+ return AddressFamilyUnderTestImpl();
+}
+
+QuicIpAddress TestLoopback4() {
+ return TestLoopback4Impl();
+}
+
+QuicIpAddress TestLoopback6() {
+ return TestLoopback6Impl();
+}
+
+QuicIpAddress TestLoopback() {
+ return TestLoopbackImpl();
+}
+
+QuicIpAddress TestLoopback(int index) {
+ return TestLoopbackImpl(index);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h
new file mode 100644
index 00000000000..708e766c1e6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h
@@ -0,0 +1,32 @@
+// 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_TEST_LOOPBACK_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_TEST_LOOPBACK_H_
+
+#include "net/quic/platform/impl/quic_test_loopback_impl.h"
+
+namespace quic {
+
+// Returns the address family (IPv4 or IPv6) used to run test under.
+IpAddressFamily AddressFamilyUnderTest();
+
+// Returns an IPv4 loopback address.
+QuicIpAddress TestLoopback4();
+
+// Returns the only IPv6 loopback address.
+QuicIpAddress TestLoopback6();
+
+// Returns an appropriate IPv4/Ipv6 loopback address based upon whether the
+// test's environment.
+QuicIpAddress TestLoopback();
+
+// If address family under test is IPv4, returns an indexed IPv4 loopback
+// address. If address family under test is IPv6, the address returned is
+// platform-dependent.
+QuicIpAddress TestLoopback(int index);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_TEST_LOOPBACK_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h
new file mode 100644
index 00000000000..06be8f7a501
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h
@@ -0,0 +1,35 @@
+// 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_TEST_MEM_SLICE_VECTOR_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_TEST_MEM_SLICE_VECTOR_H_
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+#include "net/quic/platform/impl/quic_test_mem_slice_vector_impl.h"
+
+namespace quic {
+namespace test {
+// QuicTestMemSliceVector is a test only class which creates a vector of
+// platform-specific data structure (used as QuicMemSlice) from an array of data
+// buffers. QuicTestMemSliceVector does not own the underlying data buffer.
+// Tests using QuicTestMemSliceVector need to make sure the actual data buffers
+// outlive QuicTestMemSliceVector, and QuicTestMemSliceVector outlive the
+// returned QuicMemSliceSpan.
+class QuicTestMemSliceVector {
+ public:
+ explicit QuicTestMemSliceVector(std::vector<std::pair<char*, size_t>> buffers)
+ : impl_(std::move(buffers)) {}
+
+ QuicMemSliceSpan span() { return QuicMemSliceSpan(impl_.span()); }
+
+ private:
+ QuicTestMemSliceVectorImpl impl_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_TEST_MEM_SLICE_VECTOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_output.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_output.h
new file mode 100644
index 00000000000..3cb13933e51
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_test_output.h
@@ -0,0 +1,25 @@
+// 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_TEST_OUTPUT_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_TEST_OUTPUT_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/quic/platform/impl/quic_test_output_impl.h"
+
+namespace quic {
+
+// Records a QUIC test output file into a directory specified by QUIC_TRACE_DIR
+// environment variable. Assumes that it's called from a unit test.
+//
+// The |identifier| is a human-readable identifier that will be combined with
+// the name of the unit test and a timestamp. |data| is the test output data
+// that is being recorded into the file.
+inline void QuicRecordTestOutput(QuicStringPiece identifier,
+ QuicStringPiece data) {
+ QuicRecordTestOutputImpl(identifier, data);
+}
+
+} // namespace quic
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_TEST_OUTPUT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils.h
new file mode 100644
index 00000000000..8db44cef75e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils.h
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_TEXT_UTILS_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_TEXT_UTILS_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/quic/platform/impl/quic_text_utils_impl.h"
+
+namespace quic {
+
+// Various utilities for manipulating text.
+class QuicTextUtils {
+ public:
+ // Returns true if |data| starts with |prefix|, case sensitively.
+ static bool StartsWith(QuicStringPiece data, QuicStringPiece prefix) {
+ return QuicTextUtilsImpl::StartsWith(data, prefix);
+ }
+
+ // Returns true if |data| ends with |suffix|, case insensitively.
+ static bool EndsWithIgnoreCase(QuicStringPiece data, QuicStringPiece suffix) {
+ return QuicTextUtilsImpl::EndsWithIgnoreCase(data, suffix);
+ }
+
+ // Returns a new string in which |data| has been converted to lower case.
+ static std::string ToLower(QuicStringPiece data) {
+ return QuicTextUtilsImpl::ToLower(data);
+ }
+
+ // Removes leading and trailing whitespace from |data|.
+ static void RemoveLeadingAndTrailingWhitespace(QuicStringPiece* data) {
+ QuicTextUtilsImpl::RemoveLeadingAndTrailingWhitespace(data);
+ }
+
+ // Returns true if |in| represents a valid uint64, and stores that value in
+ // |out|.
+ static bool StringToUint64(QuicStringPiece in, uint64_t* out) {
+ return QuicTextUtilsImpl::StringToUint64(in, out);
+ }
+
+ // Returns true if |in| represents a valid int, and stores that value in
+ // |out|.
+ static bool StringToInt(QuicStringPiece in, int* out) {
+ return QuicTextUtilsImpl::StringToInt(in, out);
+ }
+
+ // Returns true if |in| represents a valid uint32, and stores that value in
+ // |out|.
+ static bool StringToUint32(QuicStringPiece in, uint32_t* out) {
+ return QuicTextUtilsImpl::StringToUint32(in, out);
+ }
+
+ // Returns true if |in| represents a valid size_t, and stores that value in
+ // |out|.
+ static bool StringToSizeT(QuicStringPiece in, size_t* out) {
+ return QuicTextUtilsImpl::StringToSizeT(in, out);
+ }
+
+ // Returns a new string representing |in|.
+ static std::string Uint64ToString(uint64_t in) {
+ return QuicTextUtilsImpl::Uint64ToString(in);
+ }
+
+ // This converts |length| bytes of binary to a 2*|length|-character
+ // hexadecimal representation.
+ // Return value: 2*|length| characters of ASCII string.
+ static std::string HexEncode(const char* data, size_t length) {
+ return HexEncode(QuicStringPiece(data, length));
+ }
+
+ // This converts |data.length()| bytes of binary to a
+ // 2*|data.length()|-character hexadecimal representation.
+ // Return value: 2*|data.length()| characters of ASCII string.
+ static std::string HexEncode(QuicStringPiece data) {
+ return QuicTextUtilsImpl::HexEncode(data);
+ }
+
+ // This converts a uint32 into an 8-character hexidecimal
+ // representation. Return value: 8 characters of ASCII string.
+ static std::string Hex(uint32_t v) { return QuicTextUtilsImpl::Hex(v); }
+
+ // Converts |data| from a hexadecimal ASCII string to a binary string
+ // that is |data.length()/2| bytes long.
+ static std::string HexDecode(QuicStringPiece data) {
+ return QuicTextUtilsImpl::HexDecode(data);
+ }
+
+ // Base64 encodes with no padding |data_len| bytes of |data| into |output|.
+ static void Base64Encode(const uint8_t* data,
+ size_t data_len,
+ std::string* output) {
+ return QuicTextUtilsImpl::Base64Encode(data, data_len, output);
+ }
+
+ // Returns a string containing hex and ASCII representations of |binary|,
+ // side-by-side in the style of hexdump. Non-printable characters will be
+ // printed as '.' in the ASCII output.
+ // For example, given the input "Hello, QUIC!\01\02\03\04", returns:
+ // "0x0000: 4865 6c6c 6f2c 2051 5549 4321 0102 0304 Hello,.QUIC!...."
+ static std::string HexDump(QuicStringPiece binary_data) {
+ return QuicTextUtilsImpl::HexDump(binary_data);
+ }
+
+ // Returns true if |data| contains any uppercase characters.
+ static bool ContainsUpperCase(QuicStringPiece data) {
+ return QuicTextUtilsImpl::ContainsUpperCase(data);
+ }
+
+ // Splits |data| into a vector of pieces delimited by |delim|.
+ static std::vector<QuicStringPiece> Split(QuicStringPiece data, char delim) {
+ return QuicTextUtilsImpl::Split(data, delim);
+ }
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_TEXT_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc b/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc
new file mode 100644
index 00000000000..9b156d05ddf
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc
@@ -0,0 +1,207 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class QuicTextUtilsTest : public QuicTest {};
+
+TEST_F(QuicTextUtilsTest, StartsWith) {
+ EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", "hello"));
+ EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", "hello world"));
+ EXPECT_TRUE(QuicTextUtils::StartsWith("hello world", ""));
+ EXPECT_FALSE(QuicTextUtils::StartsWith("hello world", "Hello"));
+ EXPECT_FALSE(QuicTextUtils::StartsWith("hello world", "world"));
+ EXPECT_FALSE(QuicTextUtils::StartsWith("hello world", "bar"));
+}
+
+TEST_F(QuicTextUtilsTest, EndsWithIgnoreCase) {
+ EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", "world"));
+ EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", "hello world"));
+ EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", ""));
+ EXPECT_TRUE(QuicTextUtils::EndsWithIgnoreCase("hello world", "WORLD"));
+ EXPECT_FALSE(QuicTextUtils::EndsWithIgnoreCase("hello world", "hello"));
+}
+
+TEST_F(QuicTextUtilsTest, ToLower) {
+ EXPECT_EQ("lower", QuicTextUtils::ToLower("LOWER"));
+ EXPECT_EQ("lower", QuicTextUtils::ToLower("lower"));
+ EXPECT_EQ("lower", QuicTextUtils::ToLower("lOwEr"));
+ EXPECT_EQ("123", QuicTextUtils::ToLower("123"));
+ EXPECT_EQ("", QuicTextUtils::ToLower(""));
+}
+
+TEST_F(QuicTextUtilsTest, RemoveLeadingAndTrailingWhitespace) {
+ std::string input;
+
+ for (auto* input : {"text", " text", " text", "text ", "text ", " text ",
+ " text ", "\r\n\ttext", "text\n\r\t"}) {
+ QuicStringPiece piece(input);
+ QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&piece);
+ EXPECT_EQ("text", piece);
+ }
+}
+
+TEST_F(QuicTextUtilsTest, StringToNumbers) {
+ const std::string kMaxInt32Plus1 = "2147483648";
+ const std::string kMinInt32Minus1 = "-2147483649";
+ const std::string kMaxUint32Plus1 = "4294967296";
+
+ {
+ // StringToUint64
+ uint64_t uint64_val = 0;
+ EXPECT_TRUE(QuicTextUtils::StringToUint64("123", &uint64_val));
+ EXPECT_EQ(123u, uint64_val);
+ EXPECT_TRUE(QuicTextUtils::StringToUint64("1234", &uint64_val));
+ EXPECT_EQ(1234u, uint64_val);
+ EXPECT_FALSE(QuicTextUtils::StringToUint64("", &uint64_val));
+ EXPECT_FALSE(QuicTextUtils::StringToUint64("-123", &uint64_val));
+ EXPECT_FALSE(QuicTextUtils::StringToUint64("-123.0", &uint64_val));
+ EXPECT_TRUE(QuicTextUtils::StringToUint64(kMaxUint32Plus1, &uint64_val));
+ EXPECT_EQ(4294967296u, uint64_val);
+ }
+
+ {
+ // StringToint
+ int int_val = 0;
+ EXPECT_TRUE(QuicTextUtils::StringToInt("123", &int_val));
+ EXPECT_EQ(123, int_val);
+ EXPECT_TRUE(QuicTextUtils::StringToInt("1234", &int_val));
+ EXPECT_EQ(1234, int_val);
+ EXPECT_FALSE(QuicTextUtils::StringToInt("", &int_val));
+ EXPECT_TRUE(QuicTextUtils::StringToInt("-123", &int_val));
+ EXPECT_EQ(-123, int_val);
+ EXPECT_FALSE(QuicTextUtils::StringToInt("-123.0", &int_val));
+ if (sizeof(int) > 4) {
+ EXPECT_TRUE(QuicTextUtils::StringToInt(kMinInt32Minus1, &int_val));
+ EXPECT_EQ(-2147483649ll, int_val);
+ EXPECT_TRUE(QuicTextUtils::StringToInt(kMaxInt32Plus1, &int_val));
+ EXPECT_EQ(2147483648ll, int_val);
+ } else {
+ EXPECT_FALSE(QuicTextUtils::StringToInt(kMinInt32Minus1, &int_val));
+ EXPECT_FALSE(QuicTextUtils::StringToInt(kMaxInt32Plus1, &int_val));
+ }
+ }
+
+ {
+ // StringToUint32
+ uint32_t uint32_val = 0;
+ EXPECT_TRUE(QuicTextUtils::StringToUint32("123", &uint32_val));
+ EXPECT_EQ(123u, uint32_val);
+ EXPECT_TRUE(QuicTextUtils::StringToUint32("1234", &uint32_val));
+ EXPECT_EQ(1234u, uint32_val);
+ EXPECT_FALSE(QuicTextUtils::StringToUint32("", &uint32_val));
+ EXPECT_FALSE(QuicTextUtils::StringToUint32("-123", &uint32_val));
+ EXPECT_FALSE(QuicTextUtils::StringToUint32("-123.0", &uint32_val));
+ EXPECT_FALSE(QuicTextUtils::StringToUint32(kMaxUint32Plus1, &uint32_val));
+ }
+
+ {
+ // StringToSizeT
+ size_t size_t_val = 0;
+ EXPECT_TRUE(QuicTextUtils::StringToSizeT("123", &size_t_val));
+ EXPECT_EQ(123u, size_t_val);
+ EXPECT_TRUE(QuicTextUtils::StringToSizeT("1234", &size_t_val));
+ EXPECT_EQ(1234u, size_t_val);
+ EXPECT_FALSE(QuicTextUtils::StringToSizeT("", &size_t_val));
+ EXPECT_FALSE(QuicTextUtils::StringToSizeT("-123", &size_t_val));
+ EXPECT_FALSE(QuicTextUtils::StringToSizeT("-123.0", &size_t_val));
+ if (sizeof(size_t) > 4) {
+ EXPECT_TRUE(QuicTextUtils::StringToSizeT(kMaxUint32Plus1, &size_t_val));
+ EXPECT_EQ(4294967296ull, size_t_val);
+ } else {
+ EXPECT_FALSE(QuicTextUtils::StringToSizeT(kMaxUint32Plus1, &size_t_val));
+ }
+ }
+}
+
+TEST_F(QuicTextUtilsTest, Uint64ToString) {
+ EXPECT_EQ("123", QuicTextUtils::Uint64ToString(123));
+ EXPECT_EQ("1234", QuicTextUtils::Uint64ToString(1234));
+}
+
+TEST_F(QuicTextUtilsTest, HexEncode) {
+ EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello", 5));
+ EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello World", 5));
+ EXPECT_EQ("48656c6c6f", QuicTextUtils::HexEncode("Hello"));
+ EXPECT_EQ("0102779cfa", QuicTextUtils::HexEncode("\x01\x02\x77\x9c\xfa"));
+}
+
+TEST_F(QuicTextUtilsTest, HexDecode) {
+ EXPECT_EQ("Hello", QuicTextUtils::HexDecode("48656c6c6f"));
+ EXPECT_EQ("", QuicTextUtils::HexDecode(""));
+ EXPECT_EQ("\x01\x02\x77\x9c\xfa", QuicTextUtils::HexDecode("0102779cfa"));
+}
+
+TEST_F(QuicTextUtilsTest, HexDump) {
+ // Verify output of the HexDump method is as expected.
+ char packet[] = {
+ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x51, 0x55, 0x49, 0x43, 0x21,
+ 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
+ 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x6c,
+ 0x6f, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74,
+ 0x6f, 0x20, 0x73, 0x70, 0x61, 0x6e, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69,
+ 0x70, 0x6c, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x01, 0x02, 0x03, 0x00,
+ };
+ EXPECT_EQ(
+ QuicTextUtils::HexDump(packet),
+ "0x0000: 4865 6c6c 6f2c 2051 5549 4321 2054 6869 Hello,.QUIC!.Thi\n"
+ "0x0010: 7320 7374 7269 6e67 2073 686f 756c 6420 s.string.should.\n"
+ "0x0020: 6265 206c 6f6e 6720 656e 6f75 6768 2074 be.long.enough.t\n"
+ "0x0030: 6f20 7370 616e 206d 756c 7469 706c 6520 o.span.multiple.\n"
+ "0x0040: 6c69 6e65 7320 6f66 206f 7574 7075 742e lines.of.output.\n"
+ "0x0050: 0102 03 ...\n");
+ // Verify that 0x21 and 0x7e are printable, 0x20 and 0x7f are not.
+ EXPECT_EQ("0x0000: 2021 7e7f .!~.\n",
+ QuicTextUtils::HexDump(QuicTextUtils::HexDecode("20217e7f")));
+ // Verify that values above numeric_limits<unsigned char>::max() are formatted
+ // properly on platforms where char is unsigned.
+ EXPECT_EQ("0x0000: 90aa ff ...\n",
+ QuicTextUtils::HexDump(QuicTextUtils::HexDecode("90aaff")));
+}
+
+TEST_F(QuicTextUtilsTest, Base64Encode) {
+ std::string output;
+ std::string input = "Hello";
+ QuicTextUtils::Base64Encode(reinterpret_cast<const uint8_t*>(input.data()),
+ input.length(), &output);
+ EXPECT_EQ("SGVsbG8", output);
+
+ input =
+ "Hello, QUIC! This string should be long enough to span"
+ "multiple lines of output\n";
+ QuicTextUtils::Base64Encode(reinterpret_cast<const uint8_t*>(input.data()),
+ input.length(), &output);
+ EXPECT_EQ(
+ "SGVsbG8sIFFVSUMhIFRoaXMgc3RyaW5nIHNob3VsZCBiZSBsb25n"
+ "IGVub3VnaCB0byBzcGFubXVsdGlwbGUgbGluZXMgb2Ygb3V0cHV0Cg",
+ output);
+}
+
+TEST_F(QuicTextUtilsTest, ContainsUpperCase) {
+ EXPECT_FALSE(QuicTextUtils::ContainsUpperCase("abc"));
+ EXPECT_FALSE(QuicTextUtils::ContainsUpperCase(""));
+ EXPECT_FALSE(QuicTextUtils::ContainsUpperCase("123"));
+ EXPECT_TRUE(QuicTextUtils::ContainsUpperCase("ABC"));
+ EXPECT_TRUE(QuicTextUtils::ContainsUpperCase("aBc"));
+}
+
+TEST_F(QuicTextUtilsTest, Split) {
+ EXPECT_EQ(std::vector<QuicStringPiece>({"a", "b", "c"}),
+ QuicTextUtils::Split("a,b,c", ','));
+ EXPECT_EQ(std::vector<QuicStringPiece>({"a", "b", "c"}),
+ QuicTextUtils::Split("a:b:c", ':'));
+ EXPECT_EQ(std::vector<QuicStringPiece>({"a:b:c"}),
+ QuicTextUtils::Split("a:b:c", ','));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_thread.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_thread.h
new file mode 100644
index 00000000000..7032dc425af
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_thread.h
@@ -0,0 +1,27 @@
+// 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_THREAD_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_THREAD_H_
+
+#include <string>
+
+#include "net/quic/platform/impl/quic_thread_impl.h"
+
+namespace quic {
+
+// A class representing a thread of execution in QUIC.
+class QuicThread : public QuicThreadImpl {
+ public:
+ QuicThread(const std::string& string) : QuicThreadImpl(string) {}
+ QuicThread(const QuicThread&) = delete;
+ QuicThread& operator=(const QuicThread&) = delete;
+
+ // Impl defines a virtual void Run() method which subclasses
+ // must implement.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_THREAD_H_
diff --git a/chromium/net/third_party/quiche/src/quic/platform/api/quic_uint128.h b/chromium/net/third_party/quiche/src/quic/platform/api/quic_uint128.h
new file mode 100644
index 00000000000..4f333ae131a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/platform/api/quic_uint128.h
@@ -0,0 +1,19 @@
+// Copyright 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_UINT128_H_
+#define QUICHE_QUIC_PLATFORM_API_QUIC_UINT128_H_
+
+#include "net/quic/platform/impl/quic_uint128_impl.h"
+
+namespace quic {
+
+using QuicUint128 = QuicUint128Impl;
+#define MakeQuicUint128(hi, lo) MakeQuicUint128Impl(hi, lo)
+#define QuicUint128Low64(x) QuicUint128Low64Impl(x)
+#define QuicUint128High64(x) QuicUint128High64Impl(x)
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_PLATFORM_API_QUIC_UINT128_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h b/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h
new file mode 100644
index 00000000000..32feffc7497
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/counting_packet_filter.h
@@ -0,0 +1,43 @@
+// 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_QUARTC_COUNTING_PACKET_FILTER_H_
+#define QUICHE_QUIC_QUARTC_COUNTING_PACKET_FILTER_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+// Simple packet filter which drops the first N packets it observes.
+class CountingPacketFilter : public simulator::PacketFilter {
+ public:
+ CountingPacketFilter(simulator::Simulator* simulator,
+ const std::string& name,
+ simulator::Endpoint* endpoint)
+ : PacketFilter(simulator, name, endpoint) {}
+
+ void set_packets_to_drop(int count) { packets_to_drop_ = count; }
+
+ protected:
+ bool FilterPacket(const simulator::Packet& packet) override {
+ if (packets_to_drop_ > 0) {
+ --packets_to_drop_;
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ int packets_to_drop_ = 0;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_COUNTING_PACKET_FILTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc
new file mode 100644
index 00000000000..6c6ee97c29f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.cc
@@ -0,0 +1,24 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+
+namespace quic {
+
+QuartcConnectionHelper::QuartcConnectionHelper(const QuicClock* clock)
+ : clock_(clock) {}
+
+const QuicClock* QuartcConnectionHelper::GetClock() const {
+ return clock_;
+}
+
+QuicRandom* QuartcConnectionHelper::GetRandomGenerator() {
+ return QuicRandom::GetInstance();
+}
+
+QuicBufferAllocator* QuartcConnectionHelper::GetStreamSendBufferAllocator() {
+ return &buffer_allocator_;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h
new file mode 100644
index 00000000000..2b1d62f9a53
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h
@@ -0,0 +1,33 @@
+// 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_QUARTC_QUARTC_CONNECTION_HELPER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+namespace quic {
+
+// Simple implementation of QuicConnectionHelperInterface for Quartc.
+class QuartcConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+ explicit QuartcConnectionHelper(const QuicClock* clock);
+
+ // QuicConnectionHelperInterface overrides.
+ const QuicClock* GetClock() const override;
+ QuicRandom* GetRandomGenerator() override;
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+
+ private:
+ const QuicClock* clock_;
+ SimpleBufferAllocator buffer_allocator_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_CONNECTION_HELPER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc
new file mode 100644
index 00000000000..93023e72d3f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.cc
@@ -0,0 +1,162 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+
+namespace quic {
+
+void DummyProofSource::GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) {
+ QuicReferenceCountedPointer<ProofSource::Chain> chain =
+ GetCertChain(server_address, hostname);
+ QuicCryptoProof proof;
+ proof.signature = "Dummy signature";
+ proof.leaf_cert_scts = "Dummy timestamp";
+ callback->Run(true, chain, proof, nullptr /* details */);
+}
+
+QuicReferenceCountedPointer<DummyProofSource::Chain>
+DummyProofSource::GetCertChain(const QuicSocketAddress& server_address,
+ const std::string& hostname) {
+ std::vector<std::string> certs;
+ certs.push_back(kDummyCertName);
+ return QuicReferenceCountedPointer<ProofSource::Chain>(
+ new ProofSource::Chain(certs));
+}
+
+void DummyProofSource::ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) {
+ callback->Run(true, "Dummy signature");
+}
+
+QuicAsyncStatus InsecureProofVerifier::VerifyProof(
+ const std::string& hostname,
+ const uint16_t port,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* verify_details,
+ std::unique_ptr<ProofVerifierCallback> callback) {
+ return QUIC_SUCCESS;
+}
+
+QuicAsyncStatus InsecureProofVerifier::VerifyCertChain(
+ const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) {
+ return QUIC_SUCCESS;
+}
+
+std::unique_ptr<ProofVerifyContext>
+InsecureProofVerifier::CreateDefaultContext() {
+ return nullptr;
+}
+
+QuicConnectionId QuartcCryptoServerStreamHelper::GenerateConnectionIdForReject(
+ QuicTransportVersion version,
+ QuicConnectionId connection_id) const {
+ // TODO(b/124399417): Request a zero-length connection id here when the QUIC
+ // server perspective supports it. Right now, the stateless rejector requires
+ // a connection id that is not the same as the client-chosen connection id.
+ return QuicUtils::CreateRandomConnectionId();
+}
+
+bool QuartcCryptoServerStreamHelper::CanAcceptClientHello(
+ const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const {
+ return true;
+}
+
+std::unique_ptr<QuicCryptoClientConfig> CreateCryptoClientConfig(
+ QuicStringPiece pre_shared_key) {
+ auto config = QuicMakeUnique<QuicCryptoClientConfig>(
+ QuicMakeUnique<InsecureProofVerifier>(),
+ TlsClientHandshaker::CreateSslCtx());
+ config->set_pad_inchoate_hello(false);
+ config->set_pad_full_hello(false);
+ if (!pre_shared_key.empty()) {
+ config->set_pre_shared_key(pre_shared_key);
+ }
+ return config;
+}
+
+CryptoServerConfig CreateCryptoServerConfig(QuicRandom* random,
+ const QuicClock* clock,
+ QuicStringPiece pre_shared_key) {
+ CryptoServerConfig crypto_server_config;
+
+ // Generate a random source address token secret. For long-running servers
+ // it's better to not regenerate it for each connection to enable zero-RTT
+ // handshakes, but for transient clients it does not matter.
+ char source_address_token_secret[kInputKeyingMaterialLength];
+ random->RandBytes(source_address_token_secret, kInputKeyingMaterialLength);
+ auto config = QuicMakeUnique<QuicCryptoServerConfig>(
+ std::string(source_address_token_secret, kInputKeyingMaterialLength),
+ random, QuicMakeUnique<DummyProofSource>(), KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+
+ // We run QUIC over ICE, and ICE is verifying remote side with STUN pings.
+ // We disable source address token validation in order to allow for 0-rtt
+ // setup (plus source ip addresses are changing even during the connection
+ // when ICE is used).
+ config->set_validate_source_address_token(false);
+
+ // Effectively disables the anti-amplification measures (we don't need
+ // them because we use ICE, and we need to disable them because we disable
+ // padding of crypto packets).
+ // This multiplier must be large enough so that the crypto handshake packet
+ // (approx. 300 bytes) multiplied by this multiplier is larger than a fully
+ // sized packet (currently 1200 bytes).
+ // 1500 is a bit extreme: if you can imagine sending a 1 byte packet, and
+ // your largest MTU would be below 1500 bytes, 1500*1 >=
+ // any_packet_that_you_can_imagine_sending.
+ // (again, we hardcode packet size to 1200, so we are not dealing with jumbo
+ // frames).
+ config->set_chlo_multiplier(1500);
+
+ // We are sending small client hello, we must not validate its size.
+ config->set_validate_chlo_size(false);
+
+ // Provide server with serialized config string to prove ownership.
+ QuicCryptoServerConfig::ConfigOptions options;
+ // The |message| is used to handle the return value of AddDefaultConfig
+ // which is raw pointer of the CryptoHandshakeMessage.
+ std::unique_ptr<CryptoHandshakeMessage> message(
+ config->AddDefaultConfig(random, clock, options));
+ config->set_pad_rej(false);
+ config->set_pad_shlo(false);
+ if (!pre_shared_key.empty()) {
+ config->set_pre_shared_key(pre_shared_key);
+ }
+ crypto_server_config.config = std::move(config);
+ const QuicData& data = message->GetSerialized();
+
+ crypto_server_config.serialized_crypto_config =
+ std::string(data.data(), data.length());
+ return crypto_server_config;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h
new file mode 100644
index 00000000000..2dba7ac7d06
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h
@@ -0,0 +1,122 @@
+// 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_QUARTC_QUARTC_CRYPTO_HELPERS_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Never, ever, change this certificate name. You will break 0-rtt handshake if
+// you do.
+static constexpr char kDummyCertName[] = "Dummy cert";
+
+struct CryptoServerConfig {
+ std::unique_ptr<QuicCryptoServerConfig> config;
+ std::string serialized_crypto_config;
+};
+
+// Length of HKDF input keying material, equal to its number of bytes.
+// https://tools.ietf.org/html/rfc5869#section-2.2.
+// TODO(zhihuang): Verify that input keying material length is correct.
+constexpr size_t kInputKeyingMaterialLength = 32;
+
+// Used by QuicCryptoServerConfig to provide dummy proof credentials.
+// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible.
+class DummyProofSource : public ProofSource {
+ public:
+ DummyProofSource() {}
+ ~DummyProofSource() override {}
+
+ // ProofSource overrides.
+ void GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) override;
+
+ QuicReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname) override;
+
+ void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) override;
+};
+
+// Used by QuicCryptoClientConfig to ignore the peer's credentials
+// and establish an insecure QUIC connection.
+// TODO(zhihuang): Remove when secure P2P QUIC handshake is possible.
+class InsecureProofVerifier : public ProofVerifier {
+ public:
+ InsecureProofVerifier() {}
+ ~InsecureProofVerifier() override {}
+
+ // ProofVerifier overrides.
+ QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const uint16_t port,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* verify_details,
+ std::unique_ptr<ProofVerifierCallback> callback) override;
+
+ QuicAsyncStatus VerifyCertChain(
+ const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override;
+
+ std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override;
+};
+
+// Implementation of the server-side crypto stream helper.
+class QuartcCryptoServerStreamHelper : public QuicCryptoServerStream::Helper {
+ public:
+ QuicConnectionId GenerateConnectionIdForReject(
+ QuicTransportVersion version,
+ QuicConnectionId connection_id) const override;
+
+ bool CanAcceptClientHello(const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const override;
+};
+
+std::unique_ptr<QuicCryptoClientConfig> CreateCryptoClientConfig(
+ QuicStringPiece pre_shared_key);
+
+CryptoServerConfig CreateCryptoServerConfig(QuicRandom* random,
+ const QuicClock* clock,
+ QuicStringPiece pre_shared_key);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_CRYPTO_HELPERS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc
new file mode 100644
index 00000000000..fd4f289f3a6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.cc
@@ -0,0 +1,89 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+
+namespace quic {
+
+QuartcDispatcher::QuartcDispatcher(
+ std::unique_ptr<QuicConfig> config,
+ std::unique_ptr<QuicCryptoServerConfig> crypto_config,
+ QuicStringPiece crypto_config_serialized,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ std::unique_ptr<QuartcPacketWriter> packet_writer,
+ Delegate* delegate)
+ : QuicDispatcher(
+ config.get(),
+ crypto_config.get(),
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ QuicUtils::CreateZeroConnectionId(
+ version_manager->GetSupportedVersions()[0].transport_version)
+ .length()),
+ owned_quic_config_(std::move(config)),
+ owned_crypto_config_(std::move(crypto_config)),
+ crypto_config_(crypto_config_serialized),
+ delegate_(delegate),
+ packet_writer_(packet_writer.get()) {
+ // Allow incoming packets to set our expected connection ID length.
+ SetShouldUpdateExpectedConnectionIdLength(true);
+ // Allow incoming packets with connection ID lengths shorter than allowed.
+ SetAllowShortInitialConnectionIds(true);
+ // QuicDispatcher takes ownership of the writer.
+ QuicDispatcher::InitializeWithWriter(packet_writer.release());
+ // NB: This must happen *after* InitializeWithWriter. It can call us back
+ // with OnTransportCanWrite() immediately, and the dispatcher needs to be
+ // fully initialized to handle that.
+ packet_writer_->SetPacketTransportDelegate(this);
+}
+
+QuartcDispatcher::~QuartcDispatcher() {
+ packet_writer_->SetPacketTransportDelegate(nullptr);
+}
+
+QuartcSession* QuartcDispatcher::CreateQuicSession(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) {
+ // Make our expected connection ID non-mutable since we have a connection.
+ SetShouldUpdateExpectedConnectionIdLength(false);
+ std::unique_ptr<QuicConnection> connection = CreateQuicConnection(
+ connection_id, client_address, helper(), alarm_factory(), writer(),
+ Perspective::IS_SERVER, ParsedQuicVersionVector{version});
+ QuartcSession* session = new QuartcServerSession(
+ std::move(connection), /*visitor=*/this, config(), GetSupportedVersions(),
+ helper()->GetClock(), crypto_config(), compressed_certs_cache(),
+ session_helper());
+ delegate_->OnSessionCreated(session);
+ return session;
+}
+
+void QuartcDispatcher::OnTransportCanWrite() {
+ OnCanWrite();
+}
+
+void QuartcDispatcher::OnTransportReceived(const char* data, size_t data_len) {
+ // QuartcPacketTransport does not surface real peer addresses, so the
+ // dispatcher uses a dummy address when processing incoming packets. Note that
+ // the dispatcher refuses to process anything with port 0.
+ static const QuicSocketAddress* dummy_address =
+ new QuicSocketAddress(QuicIpAddress::Any4(), /*port=*/1);
+
+ QuicReceivedPacket packet(data, data_len, helper()->GetClock()->Now());
+ ProcessPacket(/*self_address=*/*dummy_address,
+ /*peer_address=*/*dummy_address, packet);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h
new file mode 100644
index 00000000000..26b52ac4f0c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h
@@ -0,0 +1,73 @@
+// 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_QUARTC_QUARTC_DISPATCHER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+namespace quic {
+
+class QuartcDispatcher : public QuicDispatcher,
+ QuartcPacketTransport::Delegate {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+ virtual void OnSessionCreated(QuartcSession* session) = 0;
+ };
+
+ QuartcDispatcher(
+ std::unique_ptr<QuicConfig> config,
+ std::unique_ptr<QuicCryptoServerConfig> crypto_config,
+ QuicStringPiece crypto_config_serialized,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ std::unique_ptr<QuartcPacketWriter> packet_writer,
+ Delegate* delegate);
+ ~QuartcDispatcher() override;
+
+ QuartcSession* CreateQuicSession(QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) override;
+
+ // QuartcPacketTransport::Delegate overrides.
+ void OnTransportCanWrite() override;
+ void OnTransportReceived(const char* data, size_t data_len) override;
+
+ // A serialized server config in quic wire format.
+ QuicStringPiece server_crypto_config() const { return crypto_config_; }
+
+ private:
+ // Members owned by QuartcDispatcher but not QuicDispatcher.
+ std::unique_ptr<QuicConfig> owned_quic_config_;
+ std::unique_ptr<QuicCryptoServerConfig> owned_crypto_config_;
+ std::string crypto_config_;
+
+ // Delegate invoked when the dispatcher creates a new session.
+ Delegate* delegate_;
+
+ // The packet writer used by this dispatcher. Owned by the base class, but
+ // the base class upcasts it to QuicPacketWriter (which prevents detaching the
+ // transport delegate without a downcast).
+ QuartcPacketWriter* packet_writer_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_DISPATCHER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc
new file mode 100644
index 00000000000..459ceba0618
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.cc
@@ -0,0 +1,117 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h"
+
+namespace quic {
+
+namespace {
+
+// Wrapper around a QuicAlarmFactory which delegates to the wrapped factory.
+// Usee to convert an unowned pointer into an owned pointer, so that the new
+// "owner" does not delete the underlying factory. Note that this is only valid
+// when the unowned pointer is already guaranteed to outlive the new "owner".
+class QuartcAlarmFactoryWrapper : public QuicAlarmFactory {
+ public:
+ explicit QuartcAlarmFactoryWrapper(QuicAlarmFactory* impl) : impl_(impl) {}
+
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override;
+
+ private:
+ QuicAlarmFactory* impl_;
+};
+
+QuicAlarm* QuartcAlarmFactoryWrapper::CreateAlarm(
+ QuicAlarm::Delegate* delegate) {
+ return impl_->CreateAlarm(delegate);
+}
+
+QuicArenaScopedPtr<QuicAlarm> QuartcAlarmFactoryWrapper::CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) {
+ return impl_->CreateAlarm(std::move(delegate), arena);
+}
+
+} // namespace
+
+QuartcClientEndpoint::QuartcClientEndpoint(
+ QuicAlarmFactory* alarm_factory,
+ const QuicClock* clock,
+ QuartcEndpoint::Delegate* delegate,
+ const QuartcSessionConfig& config,
+ QuicStringPiece serialized_server_config,
+ std::unique_ptr<QuicVersionManager> version_manager)
+ : alarm_factory_(alarm_factory),
+ clock_(clock),
+ delegate_(delegate),
+ serialized_server_config_(serialized_server_config),
+ version_manager_(version_manager ? std::move(version_manager)
+ : QuicMakeUnique<QuicVersionManager>(
+ AllSupportedVersions())),
+ create_session_alarm_(QuicWrapUnique(
+ alarm_factory_->CreateAlarm(new CreateSessionDelegate(this)))),
+ connection_helper_(QuicMakeUnique<QuartcConnectionHelper>(clock)),
+ config_(config) {}
+
+void QuartcClientEndpoint::Connect(QuartcPacketTransport* packet_transport) {
+ packet_transport_ = packet_transport;
+ create_session_alarm_->Set(clock_->Now());
+}
+
+void QuartcClientEndpoint::OnCreateSessionAlarm() {
+ session_ = CreateQuartcClientSession(
+ config_, clock_, alarm_factory_, connection_helper_.get(),
+ version_manager_->GetSupportedVersions(), serialized_server_config_,
+ packet_transport_);
+ delegate_->OnSessionCreated(session_.get());
+}
+
+QuartcServerEndpoint::QuartcServerEndpoint(
+ QuicAlarmFactory* alarm_factory,
+ const QuicClock* clock,
+ QuartcEndpoint::Delegate* delegate,
+ const QuartcSessionConfig& config,
+ std::unique_ptr<QuicVersionManager> version_manager)
+ : alarm_factory_(alarm_factory),
+ delegate_(delegate),
+ config_(config),
+ version_manager_(version_manager ? std::move(version_manager)
+ : QuicMakeUnique<QuicVersionManager>(
+ AllSupportedVersions())),
+ pre_connection_helper_(QuicMakeUnique<QuartcConnectionHelper>(clock)),
+ crypto_config_(
+ CreateCryptoServerConfig(pre_connection_helper_->GetRandomGenerator(),
+ clock,
+ config.pre_shared_key)) {}
+
+void QuartcServerEndpoint::Connect(QuartcPacketTransport* packet_transport) {
+ DCHECK(pre_connection_helper_ != nullptr);
+ dispatcher_ = QuicMakeUnique<QuartcDispatcher>(
+ QuicMakeUnique<QuicConfig>(CreateQuicConfig(config_)),
+ std::move(crypto_config_.config), crypto_config_.serialized_crypto_config,
+ version_manager_.get(), std::move(pre_connection_helper_),
+ QuicMakeUnique<QuartcCryptoServerStreamHelper>(),
+ QuicMakeUnique<QuartcAlarmFactoryWrapper>(alarm_factory_),
+ QuicMakeUnique<QuartcPacketWriter>(packet_transport,
+ config_.max_packet_size),
+ this);
+ // The dispatcher requires at least one call to |ProcessBufferedChlos| to
+ // set the number of connections it is allowed to create.
+ dispatcher_->ProcessBufferedChlos(/*max_connections_to_create=*/1);
+}
+
+void QuartcServerEndpoint::OnSessionCreated(QuartcSession* session) {
+ delegate_->OnSessionCreated(session);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h
new file mode 100644
index 00000000000..97f30609ec0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint.h
@@ -0,0 +1,181 @@
+// 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_QUARTC_QUARTC_ENDPOINT_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_dispatcher.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+
+namespace quic {
+
+// Private implementation of QuartcEndpoint. Enables different implementations
+// for client and server endpoints.
+class QuartcEndpointImpl {
+ public:
+ virtual ~QuartcEndpointImpl() = default;
+
+ virtual QuicStringPiece server_crypto_config() const = 0;
+};
+
+// Endpoint (client or server) in a peer-to-peer Quartc connection.
+class QuartcEndpoint {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Called when an endpoint creates a new session, before any packets are
+ // processed or sent. The callee should perform any additional
+ // configuration required, such as setting a session delegate, before
+ // returning. |session| is owned by the endpoint, but remains safe to use
+ // until another call to |OnSessionCreated| occurs, at which point previous
+ // session is destroyed.
+ virtual void OnSessionCreated(QuartcSession* session) = 0;
+
+ // Called if the endpoint fails to establish a session after a call to
+ // Connect. (The most likely cause is a network idle timeout.)
+ virtual void OnConnectError(QuicErrorCode error,
+ const std::string& error_details) = 0;
+ };
+
+ virtual ~QuartcEndpoint() = default;
+
+ // Connects the endpoint using the given session config. After |Connect| is
+ // called, the endpoint will asynchronously create a session, then call
+ // |Delegate::OnSessionCreated|.
+ virtual void Connect(QuartcPacketTransport* packet_transport) = 0;
+};
+
+// Implementation of QuartcEndpoint which immediately (but asynchronously)
+// creates a session by scheduling a QuicAlarm. Only suitable for use with the
+// client perspective.
+class QuartcClientEndpoint : public QuartcEndpoint {
+ public:
+ // |alarm_factory|, |clock|, and |delegate| are owned by the caller and must
+ // outlive the endpoint.
+ QuartcClientEndpoint(
+ QuicAlarmFactory* alarm_factory,
+ const QuicClock* clock,
+ Delegate* delegate,
+ const QuartcSessionConfig& config,
+ QuicStringPiece serialized_server_config,
+ std::unique_ptr<QuicVersionManager> version_manager = nullptr);
+
+ void Connect(QuartcPacketTransport* packet_transport) override;
+
+ private:
+ friend class CreateSessionDelegate;
+ class CreateSessionDelegate : public QuicAlarm::Delegate {
+ public:
+ CreateSessionDelegate(QuartcClientEndpoint* endpoint)
+ : endpoint_(endpoint) {}
+
+ void OnAlarm() override { endpoint_->OnCreateSessionAlarm(); }
+
+ private:
+ QuartcClientEndpoint* endpoint_;
+ };
+
+ // Callback which occurs when |create_session_alarm_| fires.
+ void OnCreateSessionAlarm();
+
+ // Implementation of QuicAlarmFactory used by this endpoint. Unowned.
+ QuicAlarmFactory* alarm_factory_;
+
+ // Implementation of QuicClock used by this endpoint. Unowned.
+ const QuicClock* clock_;
+
+ // Delegate which receives callbacks for newly created sessions.
+ QuartcEndpoint::Delegate* delegate_;
+
+ // Server config. If valid, used to perform a 0-RTT connection.
+ const std::string serialized_server_config_;
+
+ // Version manager. May be injected to control version negotiation in tests.
+ std::unique_ptr<QuicVersionManager> version_manager_;
+
+ // Alarm for creating sessions asynchronously. The alarm is set when
+ // Connect() is called. When it fires, the endpoint creates a session and
+ // calls the delegate.
+ std::unique_ptr<QuicAlarm> create_session_alarm_;
+
+ // Helper used by QuicConnection.
+ std::unique_ptr<QuicConnectionHelperInterface> connection_helper_;
+
+ // Config to be used for new sessions.
+ QuartcSessionConfig config_;
+
+ // The currently-active session. Nullptr until |Connect| and
+ // |Delegate::OnSessionCreated| are called.
+ std::unique_ptr<QuartcSession> session_;
+
+ QuartcPacketTransport* packet_transport_;
+};
+
+// Implementation of QuartcEndpoint which uses a QuartcDispatcher to listen for
+// an incoming CHLO and create a session when one arrives. Only suitable for
+// use with the server perspective.
+class QuartcServerEndpoint : public QuartcEndpoint,
+ public QuartcDispatcher::Delegate {
+ public:
+ QuartcServerEndpoint(
+ QuicAlarmFactory* alarm_factory,
+ const QuicClock* clock,
+ QuartcEndpoint::Delegate* delegate,
+ const QuartcSessionConfig& config,
+ std::unique_ptr<QuicVersionManager> version_manager = nullptr);
+
+ // Implements QuartcEndpoint.
+ void Connect(QuartcPacketTransport* packet_transport) override;
+
+ // Implements QuartcDispatcher::Delegate.
+ void OnSessionCreated(QuartcSession* session) override;
+
+ // Accessor to retrieve the server crypto config. May only be called after
+ // Connect().
+ QuicStringPiece server_crypto_config() const {
+ return crypto_config_.serialized_crypto_config;
+ }
+
+ const std::vector<ParsedQuicVersion> GetSupportedQuicVersions() const {
+ return version_manager_->GetSupportedVersions();
+ }
+
+ private:
+ // Implementation of QuicAlarmFactory used by this endpoint. Unowned.
+ QuicAlarmFactory* alarm_factory_;
+
+ // Delegate which receives callbacks for newly created sessions.
+ QuartcEndpoint::Delegate* delegate_;
+
+ // Config to be used for new sessions.
+ QuartcSessionConfig config_;
+
+ // Version manager. May be injected to control version negotiation in tests.
+ std::unique_ptr<QuicVersionManager> version_manager_;
+
+ // QuartcDispatcher waits for an incoming CHLO, then either rejects it or
+ // creates a session to respond to it. The dispatcher owns all sessions it
+ // creates.
+ std::unique_ptr<QuartcDispatcher> dispatcher_;
+
+ // This field is only available before connection was started.
+ std::unique_ptr<QuartcConnectionHelper> pre_connection_helper_;
+
+ // A configuration, containing public key, that may need to be passed to the
+ // client to enable 0rtt.
+ CryptoServerConfig crypto_config_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_ENDPOINT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc
new file mode 100644
index 00000000000..3d7d85e8ed8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_endpoint_test.cc
@@ -0,0 +1,214 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h"
+#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace {
+
+class QuartcEndpointTest : public QuicTest {
+ protected:
+ QuartcEndpointTest()
+ : client_transport_(&simulator_,
+ "client_transport",
+ "server_transport",
+ 10 * kDefaultMaxPacketSize),
+ server_transport_(&simulator_,
+ "server_transport",
+ "client_transport",
+ 10 * kDefaultMaxPacketSize),
+ client_server_link_(&client_transport_,
+ &server_transport_,
+ QuicBandwidth::FromKBitsPerSecond(10000),
+ QuicTime::Delta::FromMilliseconds(1)),
+ server_session_delegate_(&server_stream_delegate_,
+ simulator_.GetClock()),
+ server_endpoint_delegate_(&server_session_delegate_),
+ server_endpoint_(
+ QuicMakeUnique<QuartcServerEndpoint>(simulator_.GetAlarmFactory(),
+ simulator_.GetClock(),
+ &server_endpoint_delegate_,
+ QuartcSessionConfig())),
+ client_session_delegate_(&client_stream_delegate_,
+ simulator_.GetClock()),
+ client_endpoint_delegate_(&client_session_delegate_),
+ client_endpoint_(QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(),
+ simulator_.GetClock(),
+ &client_endpoint_delegate_,
+ QuartcSessionConfig(),
+ /*serialized_server_config=*/"")) {}
+
+ simulator::Simulator simulator_;
+
+ simulator::SimulatedQuartcPacketTransport client_transport_;
+ simulator::SimulatedQuartcPacketTransport server_transport_;
+ simulator::SymmetricLink client_server_link_;
+
+ FakeQuartcStreamDelegate server_stream_delegate_;
+ FakeQuartcSessionDelegate server_session_delegate_;
+ FakeQuartcEndpointDelegate server_endpoint_delegate_;
+
+ std::unique_ptr<QuartcServerEndpoint> server_endpoint_;
+
+ FakeQuartcStreamDelegate client_stream_delegate_;
+ FakeQuartcSessionDelegate client_session_delegate_;
+ FakeQuartcEndpointDelegate client_endpoint_delegate_;
+
+ std::unique_ptr<QuartcClientEndpoint> client_endpoint_;
+};
+
+// After calling Connect, the client endpoint must wait for an async callback.
+// The callback occurs after a finite amount of time and produces a session.
+TEST_F(QuartcEndpointTest, ClientCreatesSessionAsynchronously) {
+ client_endpoint_->Connect(&client_transport_);
+
+ EXPECT_EQ(client_endpoint_delegate_.session(), nullptr);
+
+ EXPECT_TRUE(simulator_.RunUntil(
+ [this] { return client_endpoint_delegate_.session() != nullptr; }));
+}
+
+// Tests that the server can negotiate for an older QUIC version if the client
+// attempts to connect using a newer version.
+TEST_F(QuartcEndpointTest,
+ QUIC_TEST_DISABLED_IN_CHROME(ServerNegotiatesForOldVersion)) {
+ // Note: for this test, we need support for two versions. Which two shouldn't
+ // matter, but they must be enabled so that the version manager doesn't filter
+ // them out.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+
+ // Reset the client endpoint to prefer version 46 but also be capable of
+ // speaking version 43.
+ ParsedQuicVersionVector client_versions;
+ client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+ client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &client_endpoint_delegate_, QuartcSessionConfig(),
+ /*serialized_server_config=*/"",
+ QuicMakeUnique<QuicVersionManager>(client_versions));
+
+ // Reset the server endpoint to only speak version 43.
+ ParsedQuicVersionVector server_versions;
+ server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &server_endpoint_delegate_, QuartcSessionConfig(),
+ QuicMakeUnique<QuicVersionManager>(server_versions));
+
+ // The endpoints should be able to establish a connection using version 46.
+ server_endpoint_->Connect(&server_transport_);
+ client_endpoint_->Connect(&client_transport_);
+
+ ASSERT_TRUE(simulator_.RunUntil([this] {
+ return client_endpoint_delegate_.session() != nullptr &&
+ client_endpoint_delegate_.session()->IsEncryptionEstablished() &&
+ server_endpoint_delegate_.session() != nullptr &&
+ server_endpoint_delegate_.session()->IsEncryptionEstablished();
+ }));
+ EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(),
+ server_versions[0]);
+ EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(),
+ server_versions[0]);
+}
+
+// Tests that the server can accept connections from clients that use older
+// QUIC versions.
+TEST_F(QuartcEndpointTest,
+ QUIC_TEST_DISABLED_IN_CHROME(ServerAcceptsOldVersion)) {
+ // Note: for this test, we need support for two versions. Which two shouldn't
+ // matter, but they must be enabled so that the version manager doesn't filter
+ // them out.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+
+ // Reset the client endpoint to only speak version 43.
+ ParsedQuicVersionVector client_versions;
+ client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &client_endpoint_delegate_, QuartcSessionConfig(),
+ /*serialized_server_config=*/"",
+ QuicMakeUnique<QuicVersionManager>(client_versions));
+
+ // Reset the server endpoint to prefer version 46 but also be capable of
+ // speaking version 43.
+ ParsedQuicVersionVector server_versions;
+ server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+ server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &server_endpoint_delegate_, QuartcSessionConfig(),
+ QuicMakeUnique<QuicVersionManager>(server_versions));
+
+ // The endpoints should be able to establish a connection using version 46.
+ server_endpoint_->Connect(&server_transport_);
+ client_endpoint_->Connect(&client_transport_);
+
+ ASSERT_TRUE(simulator_.RunUntil([this] {
+ return client_endpoint_delegate_.session() != nullptr &&
+ client_endpoint_delegate_.session()->IsEncryptionEstablished() &&
+ server_endpoint_delegate_.session() != nullptr &&
+ server_endpoint_delegate_.session()->IsEncryptionEstablished();
+ }));
+ EXPECT_EQ(client_endpoint_delegate_.session()->connection()->version(),
+ client_versions[0]);
+ EXPECT_EQ(server_endpoint_delegate_.session()->connection()->version(),
+ client_versions[0]);
+}
+
+// Tests that version negotiation fails when the client and server support
+// completely disjoint sets of versions.
+TEST_F(QuartcEndpointTest,
+ QUIC_TEST_DISABLED_IN_CHROME(VersionNegotiationWithDisjointVersions)) {
+ // Note: for this test, we need support for two versions. Which two shouldn't
+ // matter, but they must be enabled so that the version manager doesn't filter
+ // them out.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+ SetQuicReloadableFlag(quic_enable_version_43, true);
+
+ // Reset the client endpoint to only speak version 43.
+ ParsedQuicVersionVector client_versions;
+ client_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43});
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &client_endpoint_delegate_, QuartcSessionConfig(),
+ /*serialized_server_config=*/"",
+ QuicMakeUnique<QuicVersionManager>(client_versions));
+
+ // Reset the server endpoint to only speak version 46.
+ ParsedQuicVersionVector server_versions;
+ server_versions.push_back({PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46});
+ server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ &server_endpoint_delegate_, QuartcSessionConfig(),
+ QuicMakeUnique<QuicVersionManager>(server_versions));
+
+ // The endpoints should be unable to establish a connection.
+ server_endpoint_->Connect(&server_transport_);
+ client_endpoint_->Connect(&client_transport_);
+
+ // Note that the error is reported from the client and *not* the server. The
+ // server sees an invalid version, sends a version negotiation packet, and
+ // never gets a response, because the client stops sending when it can't find
+ // a mutually supported versions.
+ ASSERT_TRUE(simulator_.RunUntil([this] {
+ return client_endpoint_delegate_.session() != nullptr &&
+ client_endpoint_delegate_.session()->error() != QUIC_NO_ERROR;
+ }));
+ EXPECT_EQ(client_endpoint_delegate_.session()->error(), QUIC_INVALID_VERSION);
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc
new file mode 100644
index 00000000000..436212fd7a1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.cc
@@ -0,0 +1,242 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_connection_helper.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+namespace quic {
+
+std::unique_ptr<QuartcSession> CreateQuartcClientSession(
+ const QuartcSessionConfig& quartc_session_config,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* connection_helper,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicStringPiece server_crypto_config,
+ QuartcPacketTransport* packet_transport) {
+ DCHECK(packet_transport);
+
+ // QuartcSession will eventually own both |writer| and |quic_connection|.
+ auto writer = QuicMakeUnique<QuartcPacketWriter>(
+ packet_transport, quartc_session_config.max_packet_size);
+
+ // While the QuicConfig is not directly used by the connection, creating it
+ // also sets flag values which must be set before creating the connection.
+ QuicConfig quic_config = CreateQuicConfig(quartc_session_config);
+
+ // |dummy_id| and |dummy_address| are used because Quartc network layer will
+ // not use these two.
+ QuicConnectionId dummy_id = QuicUtils::CreateZeroConnectionId(
+ supported_versions[0].transport_version);
+ QuicSocketAddress dummy_address(QuicIpAddress::Any4(), /*port=*/0);
+ std::unique_ptr<QuicConnection> quic_connection = CreateQuicConnection(
+ dummy_id, dummy_address, connection_helper, alarm_factory, writer.get(),
+ Perspective::IS_CLIENT, supported_versions);
+
+ return QuicMakeUnique<QuartcClientSession>(
+ std::move(quic_connection), quic_config, supported_versions, clock,
+ std::move(writer),
+ CreateCryptoClientConfig(quartc_session_config.pre_shared_key),
+ server_crypto_config);
+}
+
+void ConfigureGlobalQuicSettings() {
+ // Fixes behavior of StopReading() with level-triggered stream sequencers.
+ SetQuicReloadableFlag(quic_stop_reading_when_level_triggered, true);
+
+ // Fix b/110259444.
+ SetQuicReloadableFlag(quic_fix_spurious_ack_alarm, true);
+
+ // Enable version 46 to enable SendMessage API and 'quic bit' per draft 17.
+ SetQuicReloadableFlag(quic_enable_version_46, true);
+
+ // Enable version 47 to enable variable-length connection ids.
+ SetQuicReloadableFlag(quic_enable_version_47, true);
+
+ // Fix for inconsistent reporting of crypto handshake.
+ SetQuicReloadableFlag(quic_fix_has_pending_crypto_data, true);
+
+ // Ensure that we don't drop data because QUIC streams refuse to buffer it.
+ // TODO(b/120099046): Replace this with correct handling of WriteMemSlices().
+ SetQuicFlag(&FLAGS_quic_buffered_data_threshold,
+ std::numeric_limits<int>::max());
+
+ // TODO(b/117157454): Perform version negotiation for Quartc outside of
+ // QuicSession/QuicConnection. Currently default of
+ // quic_restart_flag_quic_no_server_conn_ver_negotiation2 is false,
+ // but we fail blueprint test that sets all QUIC flags to true.
+ //
+ // Forcing flag to false to pass blueprint tests, but eventually we'll have
+ // to implement negotiation outside of QuicConnection.
+ SetQuicRestartFlag(quic_no_server_conn_ver_negotiation2, false);
+ SetQuicReloadableFlag(quic_no_client_conn_ver_negotiation, false);
+
+ // Enable and request QUIC to include receive timestamps in ACK frames.
+ SetQuicReloadableFlag(quic_send_timestamps, true);
+
+ // Enable ACK_DECIMATION_WITH_REORDERING. It requires ack_decimation to be
+ // false.
+ SetQuicReloadableFlag(quic_enable_ack_decimation, false);
+
+ // Note: flag settings have no effect for Exoblaze builds since
+ // SetQuicReloadableFlag() gets stubbed out.
+ SetQuicReloadableFlag(quic_bbr_less_probe_rtt, true); // Enable BBR6,7,8.
+ SetQuicReloadableFlag(quic_unified_iw_options, true); // Enable IWXX opts.
+ SetQuicReloadableFlag(quic_bbr_slower_startup3, true); // Enable BBQX opts.
+ SetQuicReloadableFlag(quic_bbr_flexible_app_limited, true); // Enable BBR9.
+}
+
+QuicConfig CreateQuicConfig(const QuartcSessionConfig& quartc_session_config) {
+ // TODO(b/124398962): Figure out a better way to initialize QUIC flags.
+ // Creating a config shouldn't have global side-effects on flags. However,
+ // this has the advantage of ensuring that flag values stay in sync with the
+ // options requested by configs, so simply splitting the config and flag
+ // settings doesn't seem preferable.
+ ConfigureGlobalQuicSettings();
+
+ // In exoblaze this may return false. DCHECK to avoid problems caused by
+ // incorrect flags configuration.
+ DCHECK(GetQuicReloadableFlag(quic_enable_version_46))
+ << "Your build does not support quic reloadable flags and shouldn't "
+ "place Quartc calls";
+
+ QuicTagVector copt;
+ copt.push_back(kNSTP);
+
+ // Enable and request QUIC to include receive timestamps in ACK frames.
+ copt.push_back(kSTMP);
+
+ // Enable ACK_DECIMATION_WITH_REORDERING. It requires ack_decimation to be
+ // false.
+ copt.push_back(kAKD2);
+
+ // Use unlimited decimation in order to reduce number of unbundled ACKs.
+ copt.push_back(kAKDU);
+
+ // Enable time-based loss detection.
+ copt.push_back(kTIME);
+
+ copt.push_back(kBBR3); // Stay in low-gain until in-flight < BDP.
+ copt.push_back(kBBR5); // 40 RTT ack aggregation.
+ copt.push_back(kBBR6); // Use a 0.75 * BDP cwnd during PROBE_RTT.
+ copt.push_back(kBBR8); // Skip PROBE_RTT if app-limited.
+ copt.push_back(kBBR9); // Ignore app-limited if enough data is in flight.
+ copt.push_back(kBBQ1); // 2.773 pacing gain in STARTUP.
+ copt.push_back(kBBQ2); // 2.0 CWND gain in STARTUP.
+ copt.push_back(kBBQ4); // 0.75 pacing gain in DRAIN.
+ copt.push_back(k1RTT); // Exit STARTUP after 1 RTT with no gains.
+ copt.push_back(kIW10); // 10-packet (14600 byte) initial cwnd.
+
+ if (!quartc_session_config.enable_tail_loss_probe) {
+ copt.push_back(kNTLP);
+ }
+
+ // TODO(b/112192153): Test and possible enable slower startup when pipe
+ // filling is ready to use. Slower startup is kBBRS.
+
+ QuicConfig quic_config;
+
+ // Use the limits for the session & stream flow control. The default 16KB
+ // limit leads to significantly undersending (not reaching BWE on the outgoing
+ // bitrate) due to blocked frames, and it leads to high latency (and one-way
+ // delay). Setting it to its limits is not going to cause issues (our streams
+ // are small generally, and if we were to buffer 24MB it wouldn't be the end
+ // of the world). We can consider setting different limits in future (e.g. 1MB
+ // stream, 1.5MB session). It's worth noting that on 1mbps bitrate, limit of
+ // 24MB can capture approx 4 minutes of the call, and the default increase in
+ // size of the window (half of the window size) is approximately 2 minutes of
+ // the call.
+ quic_config.SetInitialSessionFlowControlWindowToSend(
+ kSessionReceiveWindowLimit);
+ quic_config.SetInitialStreamFlowControlWindowToSend(
+ kStreamReceiveWindowLimit);
+ quic_config.SetConnectionOptionsToSend(copt);
+ quic_config.SetClientConnectionOptions(copt);
+ if (quartc_session_config.max_time_before_crypto_handshake >
+ QuicTime::Delta::Zero()) {
+ quic_config.set_max_time_before_crypto_handshake(
+ quartc_session_config.max_time_before_crypto_handshake);
+ }
+ if (quartc_session_config.max_idle_time_before_crypto_handshake >
+ QuicTime::Delta::Zero()) {
+ quic_config.set_max_idle_time_before_crypto_handshake(
+ quartc_session_config.max_idle_time_before_crypto_handshake);
+ }
+ if (quartc_session_config.idle_network_timeout > QuicTime::Delta::Zero()) {
+ quic_config.SetIdleNetworkTimeout(
+ quartc_session_config.idle_network_timeout,
+ quartc_session_config.idle_network_timeout);
+ }
+
+ // The ICE transport provides a unique 5-tuple for each connection. Save
+ // overhead by omitting the connection id.
+ quic_config.SetBytesForConnectionIdToSend(0);
+
+ // Allow up to 1000 incoming streams at once. Quartc streams typically contain
+ // one audio or video frame and close immediately. However, when a video frame
+ // becomes larger than one packet, there is some delay between the start and
+ // end of each stream. The default maximum of 100 only leaves about 1 second
+ // of headroom (Quartc sends ~30 video frames per second) before QUIC starts
+ // to refuse incoming streams. Back-pressure should clear backlogs of
+ // incomplete streams, but targets 1 second for recovery. Increasing the
+ // number of open streams gives sufficient headroom to recover before QUIC
+ // refuses new streams.
+ quic_config.SetMaxIncomingDynamicStreamsToSend(1000);
+
+ return quic_config;
+}
+
+std::unique_ptr<QuicConnection> CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionHelperInterface* connection_helper,
+ QuicAlarmFactory* alarm_factory,
+ QuicPacketWriter* packet_writer,
+ Perspective perspective,
+ ParsedQuicVersionVector supported_versions) {
+ auto quic_connection = QuicMakeUnique<QuicConnection>(
+ connection_id, peer_address, connection_helper, alarm_factory,
+ packet_writer,
+ /*owns_writer=*/false, perspective, supported_versions);
+ quic_connection->SetMaxPacketLength(
+ packet_writer->GetMaxPacketSize(peer_address));
+
+ QuicSentPacketManager& sent_packet_manager =
+ quic_connection->sent_packet_manager();
+
+ // Default delayed ack time is 25ms.
+ // If data packets are sent less often (e.g. because p-time was modified),
+ // we would force acks to be sent every 25ms regardless, increasing
+ // overhead. Since generally we guarantee a packet every 20ms, changing
+ // this value should have miniscule effect on quality on good connections,
+ // but on poor connections, changing this number significantly reduced the
+ // number of ack-only packets.
+ // The p-time can go up to as high as 120ms, and when it does, it's
+ // when the low overhead is the most important thing. Ideally it should be
+ // above 120ms, but it cannot be higher than 0.5*RTO, which equals to 100ms.
+ sent_packet_manager.set_delayed_ack_time(
+ QuicTime::Delta::FromMilliseconds(100));
+
+ quic_connection->set_fill_up_link_during_probing(true);
+
+ // We start ack decimation after 15 packets. Typically, we would see
+ // 1-2 crypto handshake packets, one media packet, and 10 probing packets.
+ // We want to get acks for the probing packets as soon as possible,
+ // but we can start using ack decimation right after first probing completes.
+ // The default was to not start ack decimation for the first 100 packets.
+ quic_connection->set_min_received_before_ack_decimation(15);
+
+ return quic_connection;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h
new file mode 100644
index 00000000000..df55cd3a3ba
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_factory.h
@@ -0,0 +1,67 @@
+// 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_QUARTC_QUARTC_FACTORY_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_FACTORY_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+namespace quic {
+
+struct QuartcSessionConfig {
+ // If a pre-shared cryptographic key is available for this session, specify it
+ // here. This value will only be used if non-empty.
+ std::string pre_shared_key;
+
+ // The maximum size of the packet can be written with the packet writer.
+ // 1200 bytes by default.
+ QuicPacketLength max_packet_size = 1200;
+
+ // Timeouts for the crypto handshake. Set them to higher values to
+ // prevent closing the session before it started on a slow network.
+ // Zero entries are ignored and QUIC defaults are used in that case.
+ QuicTime::Delta max_idle_time_before_crypto_handshake =
+ QuicTime::Delta::Zero();
+ QuicTime::Delta max_time_before_crypto_handshake = QuicTime::Delta::Zero();
+ QuicTime::Delta idle_network_timeout = QuicTime::Delta::Zero();
+
+ // Tail loss probes (TLP) are enabled by default, but it may be useful to
+ // disable them in tests. We can also consider disabling them in production
+ // if we discover that tail loss probes add overhead in low bitrate audio.
+ bool enable_tail_loss_probe = true;
+};
+
+// Creates a new QuartcClientSession using the given configuration.
+std::unique_ptr<QuartcSession> CreateQuartcClientSession(
+ const QuartcSessionConfig& quartc_session_config,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* connection_helper,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicStringPiece server_crypto_config,
+ QuartcPacketTransport* packet_transport);
+
+// Configures global settings, such as supported quic versions.
+// Must execute on QUIC thread.
+void ConfigureGlobalQuicSettings();
+
+// Must execute on QUIC thread.
+QuicConfig CreateQuicConfig(const QuartcSessionConfig& quartc_session_config);
+
+std::unique_ptr<QuicConnection> CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionHelperInterface* connection_helper,
+ QuicAlarmFactory* alarm_factory,
+ QuicPacketWriter* packet_writer,
+ Perspective perspective,
+ ParsedQuicVersionVector supported_versions);
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_FACTORY_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h
new file mode 100644
index 00000000000..17e3874051c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_fakes.h
@@ -0,0 +1,139 @@
+// 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_QUARTC_QUARTC_FAKES_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_FAKES_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h"
+
+namespace quic {
+
+class FakeQuartcEndpointDelegate : public QuartcEndpoint::Delegate {
+ public:
+ explicit FakeQuartcEndpointDelegate(QuartcSession::Delegate* session_delegate)
+ : session_delegate_(session_delegate) {}
+
+ void OnSessionCreated(QuartcSession* session) override {
+ CHECK_EQ(session_, nullptr);
+ CHECK_NE(session, nullptr);
+ session_ = session;
+ session_->SetDelegate(session_delegate_);
+ session_->StartCryptoHandshake();
+ }
+
+ void OnConnectError(QuicErrorCode error,
+ const std::string& error_details) override {
+ LOG(FATAL) << "Unexpected error during QuartcEndpoint::Connect(); error="
+ << error << ", error_details=" << error_details;
+ }
+
+ QuartcSession* session() { return session_; }
+
+ private:
+ QuartcSession::Delegate* session_delegate_;
+ QuartcSession* session_ = nullptr;
+};
+
+class FakeQuartcSessionDelegate : public QuartcSession::Delegate {
+ public:
+ explicit FakeQuartcSessionDelegate(QuartcStream::Delegate* stream_delegate,
+ const QuicClock* clock)
+ : stream_delegate_(stream_delegate), clock_(clock) {}
+
+ void OnConnectionWritable() override {
+ LOG(INFO) << "Connection writable!";
+ if (!writable_time_.IsInitialized()) {
+ writable_time_ = clock_->Now();
+ }
+ }
+
+ // Called when peers have established forward-secure encryption
+ void OnCryptoHandshakeComplete() override {
+ LOG(INFO) << "Crypto handshake complete!";
+ crypto_handshake_time_ = clock_->Now();
+ }
+
+ // Called when connection closes locally, or remotely by peer.
+ void OnConnectionClosed(QuicErrorCode error_code,
+ const std::string& error_details,
+ ConnectionCloseSource source) override {
+ connected_ = false;
+ }
+
+ // Called when an incoming QUIC stream is created.
+ void OnIncomingStream(QuartcStream* quartc_stream) override {
+ last_incoming_stream_ = quartc_stream;
+ last_incoming_stream_->SetDelegate(stream_delegate_);
+ }
+
+ void OnMessageReceived(QuicStringPiece message) override {
+ incoming_messages_.emplace_back(message);
+ }
+
+ void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
+ QuicBandwidth pacing_rate,
+ QuicTime::Delta latest_rtt) override {}
+
+ QuartcStream* last_incoming_stream() { return last_incoming_stream_; }
+
+ // Returns all received messages.
+ const std::vector<std::string>& incoming_messages() {
+ return incoming_messages_;
+ }
+
+ bool connected() { return connected_; }
+ QuicTime writable_time() const { return writable_time_; }
+ QuicTime crypto_handshake_time() const { return crypto_handshake_time_; }
+
+ private:
+ QuartcStream* last_incoming_stream_;
+ std::vector<std::string> incoming_messages_;
+ bool connected_ = true;
+ QuartcStream::Delegate* stream_delegate_;
+ QuicTime writable_time_ = QuicTime::Zero();
+ QuicTime crypto_handshake_time_ = QuicTime::Zero();
+ const QuicClock* clock_;
+};
+
+class FakeQuartcStreamDelegate : public QuartcStream::Delegate {
+ public:
+ size_t OnReceived(QuartcStream* stream,
+ iovec* iov,
+ size_t iov_length,
+ bool fin) override {
+ size_t bytes_consumed = 0;
+ for (size_t i = 0; i < iov_length; ++i) {
+ received_data_[stream->id()] += std::string(
+ static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
+ bytes_consumed += iov[i].iov_len;
+ }
+ return bytes_consumed;
+ }
+
+ void OnClose(QuartcStream* stream) override {
+ errors_[stream->id()] = stream->stream_error();
+ }
+
+ void OnBufferChanged(QuartcStream* stream) override {}
+
+ bool has_data() { return !received_data_.empty(); }
+ std::map<QuicStreamId, std::string> data() { return received_data_; }
+
+ QuicRstStreamErrorCode stream_error(QuicStreamId id) { return errors_[id]; }
+
+ private:
+ std::map<QuicStreamId, std::string> received_data_;
+ std::map<QuicStreamId, QuicRstStreamErrorCode> errors_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_FAKES_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h
new file mode 100644
index 00000000000..fe3b083c695
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h
@@ -0,0 +1,122 @@
+// 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_QUARTC_QUARTC_INTERVAL_COUNTER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_INTERVAL_COUNTER_H_
+
+#include <stddef.h>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+
+namespace quic {
+
+// QuartcIntervalCounter counts the number of times each value appears within
+// a set of potentially overlapping intervals.
+//
+// QuartcIntervalCounter is not intended for widespread use. Consider replacing
+// it with a full interval-map if more use cases arise.
+//
+// QuartcIntervalCounter is only suitable for cases where the maximum count is
+// expected to remain low. (For example, counting the number of times the same
+// portions of stream data are lost.) It is inefficient when the maximum count
+// becomes high.
+template <typename T>
+class QuartcIntervalCounter {
+ public:
+ // Adds |interval| to the counter. The count associated with each value in
+ // |interval| is incremented by one. |interval| may overlap with previous
+ // intervals added to the counter.
+ //
+ // For each possible value:
+ // - If the value is present in both |interval| and the counter, the count
+ // associated with that value is incremented by one.
+ // - If the value is present in |interval| but not counter, the count
+ // associated with that value is set to one (incremented from zero).
+ // - If the value is absent from |interval|, the count is unchanged.
+ //
+ // Time complexity is O(|MaxCount| * the complexity of adding an interval to a
+ // QuicIntervalSet).
+ void AddInterval(QuicInterval<T> interval);
+
+ // Removes an interval from the counter. This method may be called to prune
+ // irrelevant intervals from the counter. This is useful to prevent unbounded
+ // growth.
+ //
+ // Time complexity is O(|MaxCount| * the complexity of removing an interval
+ // from a QuicIntervalSet).
+ void RemoveInterval(QuicInterval<T> interval);
+
+ // Returns the maximum number of times any single value has appeared in
+ // intervals added to the counter.
+ //
+ // Time complexity is constant.
+ size_t MaxCount() const { return intervals_by_count_.size(); }
+
+ // Returns the maximum number of times a particular value has appeared in
+ // intervals added to the counter.
+ //
+ // Time complexity is O(|MaxCount| * log(number of non-contiguous intervals)).
+ size_t Count(const T& value) const;
+
+ private:
+ // Each entry in this vector represents the intervals of values counted at
+ // least i + 1 times, where i is the index of the entry.
+ //
+ // Whenever an interval is added to the counter, each value in the interval is
+ // added to the first entry which does not already contain that value. If
+ // part of an interval is already present in the last entry, a new entry is
+ // added containing that part.
+ //
+ // Note that this means each value present in one of the interval sets will be
+ // present in all previous sets.
+ std::vector<QuicIntervalSet<T>> intervals_by_count_;
+};
+
+template <typename T>
+void QuartcIntervalCounter<T>::AddInterval(QuicInterval<T> interval) {
+ // After the Nth iteration, |leftover| contains the parts of |interval| that
+ // are already present in the first N entries. These parts of |interval| have
+ // been added to the counter more than N times.
+ QuicIntervalSet<T> leftover(interval);
+ for (auto& intervals : intervals_by_count_) {
+ QuicIntervalSet<T> tmp = leftover;
+ leftover.Intersection(intervals);
+ intervals.Union(tmp);
+ }
+
+ // Whatever ranges are still in |leftover| are already in all the entries
+ // Add a new entry containing |leftover|.
+ if (!leftover.Empty()) {
+ intervals_by_count_.push_back(leftover);
+ }
+}
+
+template <typename T>
+void QuartcIntervalCounter<T>::RemoveInterval(QuicInterval<T> interval) {
+ // Remove the interval from each entry in the vector, popping any entries that
+ // become empty.
+ for (size_t i = intervals_by_count_.size(); i > 0; --i) {
+ intervals_by_count_[i - 1].Difference(interval);
+ if (intervals_by_count_[i - 1].Empty()) {
+ intervals_by_count_.pop_back();
+ }
+ }
+}
+
+template <typename T>
+size_t QuartcIntervalCounter<T>::Count(const T& value) const {
+ // The index of the last entry containing |value| gives its count.
+ for (size_t i = intervals_by_count_.size(); i > 0; --i) {
+ if (intervals_by_count_[i - 1].Contains(value)) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_INTERVAL_COUNTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc
new file mode 100644
index 00000000000..9bc9af5e55b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_interval_counter_test.cc
@@ -0,0 +1,110 @@
+// 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 "net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace {
+
+class QuartcIntervalCounterTest : public QuicTest {
+ protected:
+ QuartcIntervalCounter<int> counter_;
+};
+
+void ExpectCount(const QuartcIntervalCounter<int>& counter,
+ QuicInterval<int> interval,
+ size_t count) {
+ for (int i = interval.min(); i < interval.max(); ++i) {
+ EXPECT_EQ(counter.Count(i), count) << "i=" << i;
+ }
+}
+
+TEST_F(QuartcIntervalCounterTest, InitiallyEmpty) {
+ EXPECT_EQ(counter_.MaxCount(), 0u);
+}
+
+TEST_F(QuartcIntervalCounterTest, SameInterval) {
+ counter_.AddInterval(QuicInterval<int>(0, 6));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 6), 1);
+
+ counter_.AddInterval(QuicInterval<int>(0, 6));
+ EXPECT_EQ(counter_.MaxCount(), 2u);
+ ExpectCount(counter_, QuicInterval<int>(0, 6), 2);
+}
+
+TEST_F(QuartcIntervalCounterTest, DisjointIntervals) {
+ counter_.AddInterval(QuicInterval<int>(0, 5));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 1);
+ ExpectCount(counter_, QuicInterval<int>(5, 10), 0);
+
+ counter_.AddInterval(QuicInterval<int>(5, 10));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 1);
+ ExpectCount(counter_, QuicInterval<int>(5, 10), 1);
+}
+
+TEST_F(QuartcIntervalCounterTest, OverlappingIntervals) {
+ counter_.AddInterval(QuicInterval<int>(0, 6));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 6), 1);
+ ExpectCount(counter_, QuicInterval<int>(6, 10), 0);
+
+ counter_.AddInterval(QuicInterval<int>(5, 10));
+ EXPECT_EQ(counter_.MaxCount(), 2u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 1);
+ EXPECT_EQ(counter_.Count(5), 2u);
+ ExpectCount(counter_, QuicInterval<int>(6, 10), 1);
+}
+
+TEST_F(QuartcIntervalCounterTest, IntervalsWithGapThenOverlap) {
+ counter_.AddInterval(QuicInterval<int>(0, 4));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 4), 1);
+ ExpectCount(counter_, QuicInterval<int>(4, 10), 0);
+
+ counter_.AddInterval(QuicInterval<int>(7, 10));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 4), 1);
+ ExpectCount(counter_, QuicInterval<int>(4, 7), 0);
+ ExpectCount(counter_, QuicInterval<int>(7, 10), 1);
+
+ counter_.AddInterval(QuicInterval<int>(3, 8));
+ EXPECT_EQ(counter_.MaxCount(), 2u);
+ ExpectCount(counter_, QuicInterval<int>(0, 3), 1);
+ EXPECT_EQ(counter_.Count(3), 2u);
+ ExpectCount(counter_, QuicInterval<int>(4, 7), 1);
+ EXPECT_EQ(counter_.Count(7), 2u);
+ ExpectCount(counter_, QuicInterval<int>(8, 10), 1);
+}
+
+TEST_F(QuartcIntervalCounterTest, RemoveIntervals) {
+ counter_.AddInterval(QuicInterval<int>(0, 5));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 1);
+
+ counter_.AddInterval(QuicInterval<int>(4, 10));
+ EXPECT_EQ(counter_.MaxCount(), 2u);
+ ExpectCount(counter_, QuicInterval<int>(0, 4), 1);
+ EXPECT_EQ(counter_.Count(4), 2u);
+ ExpectCount(counter_, QuicInterval<int>(5, 10), 1);
+
+ counter_.RemoveInterval(QuicInterval<int>(0, 5));
+ EXPECT_EQ(counter_.MaxCount(), 1u);
+ ExpectCount(counter_, QuicInterval<int>(0, 5), 0);
+ ExpectCount(counter_, QuicInterval<int>(5, 10), 1);
+
+ counter_.RemoveInterval(QuicInterval<int>(5, 10));
+ EXPECT_EQ(counter_.MaxCount(), 0u);
+ ExpectCount(counter_, QuicInterval<int>(0, 10), 0);
+}
+
+} // namespace
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc
new file mode 100644
index 00000000000..64a72a89410
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.cc
@@ -0,0 +1,76 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+
+namespace quic {
+
+std::unique_ptr<PerPacketOptions> QuartcPerPacketOptions::Clone() const {
+ return QuicMakeUnique<QuartcPerPacketOptions>(*this);
+}
+
+QuartcPacketWriter::QuartcPacketWriter(QuartcPacketTransport* packet_transport,
+ QuicByteCount max_packet_size)
+ : packet_transport_(packet_transport), max_packet_size_(max_packet_size) {}
+
+WriteResult QuartcPacketWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ DCHECK(packet_transport_);
+
+ QuartcPacketTransport::PacketInfo info;
+ QuartcPerPacketOptions* quartc_options =
+ static_cast<QuartcPerPacketOptions*>(options);
+ if (quartc_options && quartc_options->connection) {
+ info.packet_number =
+ quartc_options->connection->packet_generator().packet_number();
+ }
+ int bytes_written = packet_transport_->Write(buffer, buf_len, info);
+ if (bytes_written <= 0) {
+ writable_ = false;
+ return WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK);
+ }
+ return WriteResult(WRITE_STATUS_OK, bytes_written);
+}
+
+bool QuartcPacketWriter::IsWriteBlocked() const {
+ return !writable_;
+}
+
+QuicByteCount QuartcPacketWriter::GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const {
+ return max_packet_size_;
+}
+
+void QuartcPacketWriter::SetWritable() {
+ writable_ = true;
+}
+
+bool QuartcPacketWriter::SupportsReleaseTime() const {
+ return false;
+}
+
+bool QuartcPacketWriter::IsBatchMode() const {
+ return false;
+}
+
+char* QuartcPacketWriter::GetNextWriteLocation(
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ return nullptr;
+}
+
+WriteResult QuartcPacketWriter::Flush() {
+ return WriteResult(WRITE_STATUS_OK, 0);
+}
+
+void QuartcPacketWriter::SetPacketTransportDelegate(
+ QuartcPacketTransport::Delegate* delegate) {
+ packet_transport_->SetDelegate(delegate);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h
new file mode 100644
index 00000000000..f3eb885fd53
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h
@@ -0,0 +1,113 @@
+// 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_QUARTC_QUARTC_PACKET_WRITER_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_PACKET_WRITER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+
+// Send and receive packets, like a virtual UDP socket. For example, this
+// could be implemented by WebRTC's IceTransport.
+class QuartcPacketTransport {
+ public:
+ // Additional metadata provided for each packet written.
+ struct PacketInfo {
+ QuicPacketNumber packet_number;
+ };
+
+ // Delegate for packet transport callbacks. Note that the delegate is not
+ // thread-safe. Packet transport implementations must ensure that callbacks
+ // are synchronized with all other work done by QUIC.
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Called whenever the transport can write.
+ virtual void OnTransportCanWrite() = 0;
+
+ // Called when the transport receives a packet.
+ virtual void OnTransportReceived(const char* data, size_t data_len) = 0;
+ };
+
+ virtual ~QuartcPacketTransport() {}
+
+ // Called by the QuartcPacketWriter when writing packets to the network.
+ // Return the number of written bytes. Return 0 if the write is blocked.
+ virtual int Write(const char* buffer,
+ size_t buf_len,
+ const PacketInfo& info) = 0;
+
+ // Sets the delegate which must be called when the transport can write or
+ // a packet is received. QUIC sets |delegate| to a nonnull pointer when it
+ // is ready to process incoming packets and sets |delegate| to nullptr before
+ // QUIC is deleted. Implementations may assume |delegate| remains valid until
+ // it is set to nullptr.
+ virtual void SetDelegate(Delegate* delegate) = 0;
+};
+
+struct QuartcPerPacketOptions : public PerPacketOptions {
+ std::unique_ptr<PerPacketOptions> Clone() const override;
+
+ // The connection which is sending this packet.
+ QuicConnection* connection = nullptr;
+};
+
+// Implements a QuicPacketWriter using a QuartcPacketTransport, which allows a
+// QuicConnection to use (for example), a WebRTC IceTransport.
+class QuartcPacketWriter : public QuicPacketWriter {
+ public:
+ QuartcPacketWriter(QuartcPacketTransport* packet_transport,
+ QuicByteCount max_packet_size);
+ ~QuartcPacketWriter() override {}
+
+ // The QuicConnection calls WritePacket and the QuicPacketWriter writes them
+ // to the QuartcSession::PacketTransport.
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ // Whether the underneath |transport_| is blocked. If this returns true,
+ // outgoing QUIC packets are queued by QuicConnection until SetWritable() is
+ // called.
+ bool IsWriteBlocked() const override;
+
+ // Maximum size of the QUIC packet which can be written. Users such as WebRTC
+ // can set the value through the QuartcFactoryConfig without updating the QUIC
+ // code.
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const override;
+
+ // Sets the packet writer to a writable (non-blocked) state.
+ void SetWritable() override;
+
+ bool SupportsReleaseTime() const override;
+
+ bool IsBatchMode() const override;
+
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override;
+
+ WriteResult Flush() override;
+
+ void SetPacketTransportDelegate(QuartcPacketTransport::Delegate* delegate);
+
+ private:
+ // QuartcPacketWriter will not own the transport.
+ QuartcPacketTransport* packet_transport_;
+ // The maximum size of the packet can be written by this writer.
+ QuicByteCount max_packet_size_;
+
+ // Whether packets can be written.
+ bool writable_ = false;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_PACKET_WRITER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc
new file mode 100644
index 00000000000..80031dd0f2c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.cc
@@ -0,0 +1,397 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_crypto_helpers.h"
+
+namespace quic {
+namespace {
+
+// Arbitrary server port number for net::QuicCryptoClientConfig.
+const int kQuicServerPort = 0;
+
+} // namespace
+
+QuartcSession::QuartcSession(std::unique_ptr<QuicConnection> connection,
+ Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock)
+ : QuicSession(connection.get(), visitor, config, supported_versions),
+ connection_(std::move(connection)),
+ clock_(clock),
+ per_packet_options_(QuicMakeUnique<QuartcPerPacketOptions>()) {
+ per_packet_options_->connection = connection_.get();
+ connection_->set_per_packet_options(per_packet_options_.get());
+}
+
+QuartcSession::~QuartcSession() {}
+
+QuartcStream* QuartcSession::CreateOutgoingBidirectionalStream() {
+ // Use default priority for incoming QUIC streams.
+ // TODO(zhihuang): Determine if this value is correct.
+ return ActivateDataStream(CreateDataStream(
+ GetNextOutgoingBidirectionalStreamId(), QuicStream::kDefaultPriority));
+}
+
+bool QuartcSession::SendOrQueueMessage(std::string message) {
+ if (!CanSendMessage()) {
+ QUIC_LOG(ERROR) << "Quic session does not support SendMessage";
+ return false;
+ }
+
+ if (message.size() > GetCurrentLargestMessagePayload()) {
+ QUIC_LOG(ERROR) << "Message is too big, message_size=" << message.size()
+ << ", GetCurrentLargestMessagePayload="
+ << GetCurrentLargestMessagePayload();
+ return false;
+ }
+
+ // There may be other messages in send queue, so we have to add message
+ // to the queue and call queue processing helper.
+ send_message_queue_.emplace_back(std::move(message));
+
+ ProcessSendMessageQueue();
+
+ return true;
+}
+
+void QuartcSession::ProcessSendMessageQueue() {
+ while (!send_message_queue_.empty()) {
+ struct iovec iov = {const_cast<char*>(send_message_queue_.front().data()),
+ send_message_queue_.front().length()};
+ QuicMemSliceStorage storage(
+ &iov, 1, connection()->helper()->GetStreamSendBufferAllocator(),
+ send_message_queue_.front().length());
+ MessageResult result = SendMessage(storage.ToSpan());
+
+ const size_t message_size = send_message_queue_.front().size();
+
+ // Handle errors.
+ switch (result.status) {
+ case MESSAGE_STATUS_SUCCESS:
+ QUIC_VLOG(1) << "Quartc message sent, message_id=" << result.message_id
+ << ", message_size=" << message_size;
+ break;
+
+ // If connection is congestion controlled or not writable yet, stop
+ // send loop and we'll retry again when we get OnCanWrite notification.
+ case MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED:
+ case MESSAGE_STATUS_BLOCKED:
+ QUIC_VLOG(1) << "Quartc message not sent because connection is blocked"
+ << ", message will be retried later, status="
+ << result.status << ", message_size=" << message_size;
+
+ return;
+
+ // Other errors are unexpected. We do not propagate error to Quartc,
+ // because writes can be delayed.
+ case MESSAGE_STATUS_UNSUPPORTED:
+ case MESSAGE_STATUS_TOO_LARGE:
+ case MESSAGE_STATUS_INTERNAL_ERROR:
+ QUIC_DLOG(DFATAL)
+ << "Failed to send quartc message due to unexpected error"
+ << ", message will not be retried, status=" << result.status
+ << ", message_size=" << message_size;
+ break;
+ }
+
+ send_message_queue_.pop_front();
+ }
+}
+
+void QuartcSession::OnCanWrite() {
+ // TODO(b/119640244): Since we currently use messages for audio and streams
+ // for video, it makes sense to process queued messages first, then call quic
+ // core OnCanWrite, which will resend queued streams. Long term we may need
+ // better solution especially if quic connection is used for both data and
+ // media.
+
+ // Process quartc messages that were previously blocked.
+ ProcessSendMessageQueue();
+
+ QuicSession::OnCanWrite();
+}
+
+void QuartcSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+ switch (event) {
+ case ENCRYPTION_FIRST_ESTABLISHED:
+ case ENCRYPTION_REESTABLISHED:
+ // 1-rtt setup triggers 'ENCRYPTION_REESTABLISHED' (after REJ, when the
+ // CHLO is sent).
+ DCHECK(IsEncryptionEstablished());
+ DCHECK(session_delegate_);
+ session_delegate_->OnConnectionWritable();
+ break;
+ case HANDSHAKE_CONFIRMED:
+ // On the server, handshake confirmed is the first time when you can start
+ // writing packets.
+ DCHECK(IsEncryptionEstablished());
+ DCHECK(IsCryptoHandshakeConfirmed());
+
+ DCHECK(session_delegate_);
+ session_delegate_->OnConnectionWritable();
+ session_delegate_->OnCryptoHandshakeComplete();
+ break;
+ }
+}
+
+void QuartcSession::CancelStream(QuicStreamId stream_id) {
+ ResetStream(stream_id, QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
+}
+
+void QuartcSession::ResetStream(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error) {
+ if (!IsOpenStream(stream_id)) {
+ return;
+ }
+ QuicStream* stream = QuicSession::GetOrCreateStream(stream_id);
+ if (stream) {
+ stream->Reset(error);
+ }
+}
+
+void QuartcSession::OnCongestionWindowChange(QuicTime now) {
+ DCHECK(session_delegate_);
+ const RttStats* rtt_stats = connection_->sent_packet_manager().GetRttStats();
+
+ QuicBandwidth bandwidth_estimate =
+ connection_->sent_packet_manager().BandwidthEstimate();
+
+ QuicByteCount in_flight =
+ connection_->sent_packet_manager().GetBytesInFlight();
+ QuicBandwidth pacing_rate =
+ connection_->sent_packet_manager().GetSendAlgorithm()->PacingRate(
+ in_flight);
+
+ session_delegate_->OnCongestionControlChange(bandwidth_estimate, pacing_rate,
+ rtt_stats->latest_rtt());
+}
+
+bool QuartcSession::ShouldKeepConnectionAlive() const {
+ // TODO(mellem): Quartc may want different keepalive logic than HTTP.
+ return GetNumOpenDynamicStreams() > 0;
+}
+
+void QuartcSession::OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {
+ QuicSession::OnConnectionClosed(error, error_details, source);
+ DCHECK(session_delegate_);
+ session_delegate_->OnConnectionClosed(error, error_details, source);
+}
+
+void QuartcSession::CloseConnection(const std::string& details) {
+ connection_->CloseConnection(
+ QuicErrorCode::QUIC_CONNECTION_CANCELLED, details,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET_WITH_NO_ACK);
+}
+
+void QuartcSession::SetDelegate(Delegate* session_delegate) {
+ if (session_delegate_) {
+ LOG(WARNING) << "The delegate for the session has already been set.";
+ }
+ session_delegate_ = session_delegate;
+ DCHECK(session_delegate_);
+}
+
+void QuartcSession::OnTransportCanWrite() {
+ connection()->writer()->SetWritable();
+ if (HasDataToWrite()) {
+ connection()->OnCanWrite();
+ }
+}
+
+void QuartcSession::OnTransportReceived(const char* data, size_t data_len) {
+ QuicReceivedPacket packet(data, data_len, clock_->Now());
+ ProcessUdpPacket(connection()->self_address(), connection()->peer_address(),
+ packet);
+}
+
+void QuartcSession::OnMessageReceived(QuicStringPiece message) {
+ session_delegate_->OnMessageReceived(message);
+}
+
+QuicStream* QuartcSession::CreateIncomingStream(QuicStreamId id) {
+ return ActivateDataStream(CreateDataStream(id, QuicStream::kDefaultPriority));
+}
+
+QuicStream* QuartcSession::CreateIncomingStream(PendingStream pending) {
+ return ActivateDataStream(
+ CreateDataStream(std::move(pending), QuicStream::kDefaultPriority));
+}
+
+std::unique_ptr<QuartcStream> QuartcSession::CreateDataStream(
+ QuicStreamId id,
+ spdy::SpdyPriority priority) {
+ if (GetCryptoStream() == nullptr ||
+ !GetCryptoStream()->encryption_established()) {
+ // Encryption not active so no stream created
+ return nullptr;
+ }
+ return InitializeDataStream(QuicMakeUnique<QuartcStream>(id, this), priority);
+}
+
+std::unique_ptr<QuartcStream> QuartcSession::CreateDataStream(
+ PendingStream pending,
+ spdy::SpdyPriority priority) {
+ return InitializeDataStream(QuicMakeUnique<QuartcStream>(std::move(pending)),
+ priority);
+}
+
+std::unique_ptr<QuartcStream> QuartcSession::InitializeDataStream(
+ std::unique_ptr<QuartcStream> stream,
+ spdy::SpdyPriority priority) {
+ // Register the stream to the QuicWriteBlockedList. |priority| is clamped
+ // between 0 and 7, with 0 being the highest priority and 7 the lowest
+ // priority.
+ write_blocked_streams()->UpdateStreamPriority(stream->id(), priority);
+
+ if (IsIncomingStream(stream->id())) {
+ DCHECK(session_delegate_);
+ // Incoming streams need to be registered with the session_delegate_.
+ session_delegate_->OnIncomingStream(stream.get());
+ }
+ return stream;
+}
+
+QuartcStream* QuartcSession::ActivateDataStream(
+ std::unique_ptr<QuartcStream> stream) {
+ // Transfer ownership of the data stream to the session via ActivateStream().
+ QuartcStream* raw = stream.release();
+ if (raw) {
+ // Make QuicSession take ownership of the stream.
+ ActivateStream(std::unique_ptr<QuicStream>(raw));
+ }
+ return raw;
+}
+
+QuartcClientSession::QuartcClientSession(
+ std::unique_ptr<QuicConnection> connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ std::unique_ptr<QuartcPacketWriter> packet_writer,
+ std::unique_ptr<QuicCryptoClientConfig> client_crypto_config,
+ QuicStringPiece server_crypto_config)
+ : QuartcSession(std::move(connection),
+ /*visitor=*/nullptr,
+ config,
+ supported_versions,
+ clock),
+ packet_writer_(std::move(packet_writer)),
+ client_crypto_config_(std::move(client_crypto_config)),
+ server_config_(server_crypto_config) {
+ DCHECK_EQ(QuartcSession::connection()->perspective(), Perspective::IS_CLIENT);
+}
+
+QuartcClientSession::~QuartcClientSession() {
+ // The client session is the packet transport delegate, so it must be unset
+ // before the session is deleted.
+ packet_writer_->SetPacketTransportDelegate(nullptr);
+}
+
+void QuartcClientSession::Initialize() {
+ DCHECK(crypto_stream_) << "Do not call QuartcSession::Initialize(), call "
+ "StartCryptoHandshake() instead.";
+ QuartcSession::Initialize();
+
+ // QUIC is ready to process incoming packets after Initialize().
+ // Set the packet transport delegate to begin receiving packets.
+ packet_writer_->SetPacketTransportDelegate(this);
+}
+
+const QuicCryptoStream* QuartcClientSession::GetCryptoStream() const {
+ return crypto_stream_.get();
+}
+
+QuicCryptoStream* QuartcClientSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+void QuartcClientSession::StartCryptoHandshake() {
+ QuicServerId server_id(/*host=*/"", kQuicServerPort,
+ /*privacy_mode_enabled=*/false);
+
+ if (!server_config_.empty()) {
+ QuicCryptoServerConfig::ConfigOptions options;
+
+ std::string error;
+ QuicWallTime now = clock()->WallNow();
+ QuicCryptoClientConfig::CachedState::ServerConfigState result =
+ client_crypto_config_->LookupOrCreate(server_id)->SetServerConfig(
+ server_config_, now,
+ /*expiry_time=*/now.Add(QuicTime::Delta::Infinite()), &error);
+
+ if (result == QuicCryptoClientConfig::CachedState::SERVER_CONFIG_VALID) {
+ DCHECK_EQ(error, "");
+ client_crypto_config_->LookupOrCreate(server_id)->SetProof(
+ std::vector<std::string>{kDummyCertName}, /*cert_sct=*/"",
+ /*chlo_hash=*/"", /*signature=*/"anything");
+ } else {
+ LOG(DFATAL) << "Unable to set server config, error=" << error;
+ }
+ }
+
+ crypto_stream_ = QuicMakeUnique<QuicCryptoClientStream>(
+ server_id, this,
+ client_crypto_config_->proof_verifier()->CreateDefaultContext(),
+ client_crypto_config_.get(), this);
+ Initialize();
+ crypto_stream_->CryptoConnect();
+}
+
+void QuartcClientSession::OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) {
+ // TODO(zhihuang): Handle the proof verification.
+}
+
+void QuartcClientSession::OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) {
+ // TODO(zhihuang): Handle the proof verification.
+}
+
+QuartcServerSession::QuartcServerSession(
+ std::unique_ptr<QuicConnection> connection,
+ Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ const QuicCryptoServerConfig* server_crypto_config,
+ QuicCompressedCertsCache* const compressed_certs_cache,
+ QuicCryptoServerStream::Helper* const stream_helper)
+ : QuartcSession(std::move(connection),
+ visitor,
+ config,
+ supported_versions,
+ clock),
+ server_crypto_config_(server_crypto_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ stream_helper_(stream_helper) {
+ DCHECK_EQ(QuartcSession::connection()->perspective(), Perspective::IS_SERVER);
+}
+
+const QuicCryptoStream* QuartcServerSession::GetCryptoStream() const {
+ return crypto_stream_.get();
+}
+
+QuicCryptoStream* QuartcServerSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+void QuartcServerSession::StartCryptoHandshake() {
+ crypto_stream_ = QuicMakeUnique<QuicCryptoServerStream>(
+ server_crypto_config_, compressed_certs_cache_,
+ /*use_stateless_rejects_if_peer_supported=*/false, this, stream_helper_);
+ Initialize();
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h
new file mode 100644
index 00000000000..29e63066efe
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session.h
@@ -0,0 +1,284 @@
+// 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_QUARTC_QUARTC_SESSION_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_SESSION_H_
+
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h"
+
+namespace quic {
+
+// QuartcSession owns and manages a QUIC connection.
+class QuartcSession : public QuicSession,
+ public QuartcPacketTransport::Delegate {
+ public:
+ QuartcSession(std::unique_ptr<QuicConnection> connection,
+ Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock);
+ QuartcSession(const QuartcSession&) = delete;
+ QuartcSession& operator=(const QuartcSession&) = delete;
+ ~QuartcSession() override;
+
+ // QuicSession overrides.
+ QuartcStream* CreateOutgoingBidirectionalStream();
+
+ // Sends short unreliable message using quic message frame (message must fit
+ // in one quic packet). If connection is blocked by congestion control,
+ // message will be queued and resent later after receiving an OnCanWrite
+ // notification.
+ //
+ // Message size must be <= GetLargestMessagePayload().
+ //
+ // Supported in quic version 45 or later.
+ //
+ // Returns false and logs error if message is too long or session does not
+ // support SendMessage API. Other unexpected errors during send will not be
+ // returned, because messages can be sent later if connection is congestion
+ // controlled.
+ bool SendOrQueueMessage(std::string message);
+
+ // Returns largest message payload acceptable in SendQuartcMessage.
+ QuicPacketLength GetCurrentLargestMessagePayload() const {
+ return connection()->GetCurrentLargestMessagePayload();
+ }
+
+ // Return true if transport support message frame.
+ bool CanSendMessage() const {
+ return connection()->transport_version() > QUIC_VERSION_44;
+ }
+
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
+ // QuicConnectionVisitorInterface overrides.
+ void OnCongestionWindowChange(QuicTime now) override;
+ bool ShouldKeepConnectionAlive() const override;
+
+ void OnCanWrite() override;
+
+ void OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) override;
+
+ // QuartcSession methods.
+ virtual void StartCryptoHandshake() = 0;
+
+ // Closes the connection with the given human-readable error details.
+ // The connection closes with the QUIC_CONNECTION_CANCELLED error code to
+ // indicate the application closed it.
+ //
+ // Informs the peer that the connection has been closed. This prevents the
+ // peer from waiting until the connection times out.
+ //
+ // Cleans up the underlying QuicConnection's state. Closing the connection
+ // makes it safe to delete the QuartcSession.
+ void CloseConnection(const std::string& details);
+
+ // If the given stream is still open, sends a reset frame to cancel it.
+ // Note: This method cancels a stream by QuicStreamId rather than by pointer
+ // (or by a method on QuartcStream) because QuartcSession (and not
+ // the caller) owns the streams. Streams may finish and be deleted before the
+ // caller tries to cancel them, rendering the caller's pointers invalid.
+ void CancelStream(QuicStreamId stream_id);
+
+ // Callbacks called by the QuartcSession to notify the user of the
+ // QuartcSession of certain events.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called when the crypto handshake is complete. Crypto handshake on the
+ // client is only completed _after_ SHLO is received, but we can actually
+ // start sending media data right after CHLO is sent.
+ virtual void OnCryptoHandshakeComplete() = 0;
+
+ // Connection can be writable even before crypto handshake is complete.
+ // In particular, on the client, we can start sending data after sending
+ // full CHLO, without waiting for SHLO. This reduces a send delay by 1-rtt.
+ //
+ // This may be called multiple times.
+ virtual void OnConnectionWritable() = 0;
+
+ // Called when a new stream is received from the remote endpoint.
+ virtual void OnIncomingStream(QuartcStream* stream) = 0;
+
+ // Called when network parameters change in response to an ack frame.
+ virtual void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
+ QuicBandwidth pacing_rate,
+ QuicTime::Delta latest_rtt) = 0;
+
+ // Called when the connection is closed. This means all of the streams will
+ // be closed and no new streams can be created.
+ virtual void OnConnectionClosed(QuicErrorCode error_code,
+ const std::string& error_details,
+ ConnectionCloseSource source) = 0;
+
+ // Called when message (sent as SendMessage) is received.
+ virtual void OnMessageReceived(QuicStringPiece message) = 0;
+
+ // TODO(zhihuang): Add proof verification.
+ };
+
+ // The |delegate| is not owned by QuartcSession.
+ void SetDelegate(Delegate* session_delegate);
+
+ // Called when CanWrite() changes from false to true.
+ void OnTransportCanWrite() override;
+
+ // Called when a packet has been received and should be handled by the
+ // QuicConnection.
+ void OnTransportReceived(const char* data, size_t data_len) override;
+
+ void OnMessageReceived(QuicStringPiece message) override;
+
+ // Returns number of queued (not sent) messages submitted by
+ // SendOrQueueMessage. Messages are queued if connection is congestion
+ // controlled.
+ size_t send_message_queue_size() const { return send_message_queue_.size(); }
+
+ protected:
+ // QuicSession override.
+ QuicStream* CreateIncomingStream(QuicStreamId id) override;
+ QuicStream* CreateIncomingStream(PendingStream pending) override;
+
+ std::unique_ptr<QuartcStream> CreateDataStream(QuicStreamId id,
+ spdy::SpdyPriority priority);
+ std::unique_ptr<QuartcStream> CreateDataStream(PendingStream pending,
+ spdy::SpdyPriority priority);
+ // Activates a QuartcStream. The session takes ownership of the stream, but
+ // returns an unowned pointer to the stream for convenience.
+ QuartcStream* ActivateDataStream(std::unique_ptr<QuartcStream> stream);
+
+ void ResetStream(QuicStreamId stream_id, QuicRstStreamErrorCode error);
+
+ const QuicClock* clock() { return clock_; }
+
+ private:
+ std::unique_ptr<QuartcStream> InitializeDataStream(
+ std::unique_ptr<QuartcStream> stream,
+ spdy::SpdyPriority priority);
+
+ void ProcessSendMessageQueue();
+
+ // Take ownership of the QuicConnection. Note: if |connection_| changes,
+ // the new value of |connection_| must be given to |packet_writer_| before any
+ // packets are written. Otherwise, |packet_writer_| will crash.
+ std::unique_ptr<QuicConnection> connection_;
+
+ // For recording packet receipt time
+ const QuicClock* clock_;
+
+ // Not owned by QuartcSession.
+ Delegate* session_delegate_ = nullptr;
+
+ // Options passed to the packet writer for each packet.
+ std::unique_ptr<QuartcPerPacketOptions> per_packet_options_;
+
+ // Queue of pending messages sent by SendQuartcMessage that were not sent
+ // yet or blocked by congestion control. Messages are queued in the order
+ // of sent by SendOrQueueMessage().
+ QuicDeque<std::string> send_message_queue_;
+};
+
+class QuartcClientSession : public QuartcSession,
+ public QuicCryptoClientStream::ProofHandler {
+ public:
+ QuartcClientSession(
+ std::unique_ptr<QuicConnection> connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ std::unique_ptr<QuartcPacketWriter> packet_writer,
+ std::unique_ptr<QuicCryptoClientConfig> client_crypto_config,
+ QuicStringPiece server_crypto_config);
+ QuartcClientSession(const QuartcClientSession&) = delete;
+ QuartcClientSession& operator=(const QuartcClientSession&) = delete;
+
+ ~QuartcClientSession() override;
+
+ // Initialize should not be called on a QuartcSession. Instead, call
+ // StartCryptoHandshake().
+ // TODO(mellem): Move creation of the crypto stream into Initialize() and
+ // remove StartCryptoHandshake() to bring QuartcSession in line with other
+ // implementations of QuicSession, which can be started by calling
+ // Initialize().
+ void Initialize() override;
+
+ // Accessors for the client crypto stream.
+ QuicCryptoStream* GetMutableCryptoStream() override;
+ const QuicCryptoStream* GetCryptoStream() const override;
+
+ // Initializes the session and sends a handshake.
+ void StartCryptoHandshake() override;
+
+ // ProofHandler overrides.
+ void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
+
+ // Called by the client crypto handshake when proof verification details
+ // become available, either because proof verification is complete, or when
+ // cached details are used.
+ void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) override;
+
+ private:
+ // Packet writer used by |connection_|.
+ std::unique_ptr<QuartcPacketWriter> packet_writer_;
+
+ // Config for QUIC crypto stream.
+ std::unique_ptr<QuicCryptoClientConfig> client_crypto_config_;
+
+ // Client perspective crypto stream.
+ std::unique_ptr<QuicCryptoClientStream> crypto_stream_;
+
+ const std::string server_config_;
+};
+
+class QuartcServerSession : public QuartcSession {
+ public:
+ QuartcServerSession(std::unique_ptr<QuicConnection> connection,
+ Visitor* visitor,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ const QuicCryptoServerConfig* server_crypto_config,
+ QuicCompressedCertsCache* const compressed_certs_cache,
+ QuicCryptoServerStream::Helper* const stream_helper);
+ QuartcServerSession(const QuartcServerSession&) = delete;
+ QuartcServerSession& operator=(const QuartcServerSession&) = delete;
+
+ // Accessors for the server crypto stream.
+ QuicCryptoStream* GetMutableCryptoStream() override;
+ const QuicCryptoStream* GetCryptoStream() const override;
+
+ // Initializes the session and prepares to receive a handshake.
+ void StartCryptoHandshake() override;
+
+ private:
+ // Config for QUIC crypto stream.
+ const QuicCryptoServerConfig* server_crypto_config_;
+
+ // Used by QUIC crypto server stream to track most recently compressed certs.
+ QuicCompressedCertsCache* const compressed_certs_cache_;
+
+ // This helper is needed to create QuicCryptoServerStream.
+ QuicCryptoServerStream::Helper* const stream_helper_;
+
+ // Server perspective crypto stream.
+ std::unique_ptr<QuicCryptoServerStream> crypto_stream_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_SESSION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc
new file mode 100644
index 00000000000..31abd57231e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_session_test.cc
@@ -0,0 +1,559 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_session.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/quartc/counting_packet_filter.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+
+namespace {
+
+constexpr QuicTime::Delta kPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(10);
+// Propagation delay and a bit, but no more than full RTT.
+constexpr QuicTime::Delta kPropagationDelayAndABit =
+ QuicTime::Delta::FromMilliseconds(12);
+
+static QuicByteCount kDefaultMaxPacketSize = 1200;
+
+class QuartcSessionTest : public QuicTest {
+ public:
+ ~QuartcSessionTest() override {}
+
+ void Init(bool create_client_endpoint = true) {
+ client_transport_ =
+ QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
+ &simulator_, "client_transport", "server_transport",
+ 10 * kDefaultMaxPacketSize);
+ server_transport_ =
+ QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
+ &simulator_, "server_transport", "client_transport",
+ 10 * kDefaultMaxPacketSize);
+
+ client_filter_ = QuicMakeUnique<simulator::CountingPacketFilter>(
+ &simulator_, "client_filter", client_transport_.get());
+
+ client_server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
+ client_filter_.get(), server_transport_.get(),
+ QuicBandwidth::FromKBitsPerSecond(10 * 1000), kPropagationDelay);
+
+ client_stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>();
+ client_session_delegate_ = QuicMakeUnique<FakeQuartcSessionDelegate>(
+ client_stream_delegate_.get(), simulator_.GetClock());
+ client_endpoint_delegate_ = QuicMakeUnique<FakeQuartcEndpointDelegate>(
+ client_session_delegate_.get());
+
+ server_stream_delegate_ = QuicMakeUnique<FakeQuartcStreamDelegate>();
+ server_session_delegate_ = QuicMakeUnique<FakeQuartcSessionDelegate>(
+ server_stream_delegate_.get(), simulator_.GetClock());
+ server_endpoint_delegate_ = QuicMakeUnique<FakeQuartcEndpointDelegate>(
+ server_session_delegate_.get());
+
+ // No 0-rtt setup, because server config is empty.
+ // CannotCreateDataStreamBeforeHandshake depends on 1-rtt setup.
+ if (create_client_endpoint) {
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ client_endpoint_delegate_.get(), quic::QuartcSessionConfig(),
+ /*serialized_server_config=*/"");
+ }
+ server_endpoint_ = QuicMakeUnique<QuartcServerEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ server_endpoint_delegate_.get(), quic::QuartcSessionConfig());
+ }
+
+ // Note that input session config will apply to both server and client.
+ // Perspective and packet_transport will be overwritten.
+ void CreateClientAndServerSessions(const QuartcSessionConfig& session_config,
+ bool init = true) {
+ if (init) {
+ Init();
+ }
+
+ server_endpoint_->Connect(server_transport_.get());
+ client_endpoint_->Connect(client_transport_.get());
+
+ CHECK(simulator_.RunUntil([this] {
+ return client_endpoint_delegate_->session() != nullptr &&
+ server_endpoint_delegate_->session() != nullptr;
+ }));
+
+ client_peer_ = client_endpoint_delegate_->session();
+ server_peer_ = server_endpoint_delegate_->session();
+ }
+
+ // Runs all tasks scheduled in the next 200 ms.
+ void RunTasks() { simulator_.RunFor(QuicTime::Delta::FromMilliseconds(200)); }
+
+ void AwaitHandshake() {
+ simulator_.RunUntil([this] {
+ return client_peer_->IsCryptoHandshakeConfirmed() &&
+ server_peer_->IsCryptoHandshakeConfirmed();
+ });
+ }
+
+ // Test handshake establishment and sending/receiving of data for two
+ // directions.
+ void TestSendReceiveStreams() {
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsEncryptionEstablished());
+ ASSERT_TRUE(client_peer_->IsEncryptionEstablished());
+
+ // Now we can establish encrypted outgoing stream.
+ QuartcStream* outgoing_stream =
+ server_peer_->CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = outgoing_stream->id();
+ ASSERT_NE(nullptr, outgoing_stream);
+ EXPECT_TRUE(server_peer_->ShouldKeepConnectionAlive());
+
+ outgoing_stream->SetDelegate(server_stream_delegate_.get());
+
+ // Send a test message from peer 1 to peer 2.
+ char kTestMessage[] = "Hello";
+ test::QuicTestMemSliceVector data(
+ {std::make_pair(kTestMessage, strlen(kTestMessage))});
+ outgoing_stream->WriteMemSlices(data.span(), /*fin=*/false);
+ RunTasks();
+
+ // Wait for peer 2 to receive messages.
+ ASSERT_TRUE(client_stream_delegate_->has_data());
+
+ QuartcStream* incoming = client_session_delegate_->last_incoming_stream();
+ ASSERT_TRUE(incoming);
+ EXPECT_EQ(incoming->id(), stream_id);
+ EXPECT_TRUE(client_peer_->ShouldKeepConnectionAlive());
+
+ EXPECT_EQ(client_stream_delegate_->data()[stream_id], kTestMessage);
+ // Send a test message from peer 2 to peer 1.
+ char kTestResponse[] = "Response";
+ test::QuicTestMemSliceVector response(
+ {std::make_pair(kTestResponse, strlen(kTestResponse))});
+ incoming->WriteMemSlices(response.span(), /*fin=*/false);
+ RunTasks();
+ // Wait for peer 1 to receive messages.
+ ASSERT_TRUE(server_stream_delegate_->has_data());
+
+ EXPECT_EQ(server_stream_delegate_->data()[stream_id], kTestResponse);
+ }
+
+ // Test sending/receiving of messages for two directions.
+ void TestSendReceiveMessage() {
+ ASSERT_TRUE(server_peer_->CanSendMessage());
+ ASSERT_TRUE(client_peer_->CanSendMessage());
+
+ // Send message from peer 1 to peer 2.
+ ASSERT_TRUE(server_peer_->SendOrQueueMessage("Message from server"));
+
+ // First message in each direction should not be queued.
+ EXPECT_EQ(server_peer_->send_message_queue_size(), 0u);
+
+ // Wait for peer 2 to receive message.
+ RunTasks();
+
+ EXPECT_THAT(client_session_delegate_->incoming_messages(),
+ testing::ElementsAre("Message from server"));
+
+ // Send message from peer 2 to peer 1.
+ ASSERT_TRUE(client_peer_->SendOrQueueMessage("Message from client"));
+
+ // First message in each direction should not be queued.
+ EXPECT_EQ(client_peer_->send_message_queue_size(), 0u);
+
+ // Wait for peer 1 to receive message.
+ RunTasks();
+
+ EXPECT_THAT(server_session_delegate_->incoming_messages(),
+ testing::ElementsAre("Message from client"));
+ }
+
+ // Test for sending multiple messages that also result in queueing.
+ // This is one-way test, which is run in given direction.
+ void TestSendReceiveQueuedMessages(bool direction_from_server) {
+ // Send until queue_size number of messages are queued.
+ constexpr size_t queue_size = 10;
+
+ ASSERT_TRUE(server_peer_->CanSendMessage());
+ ASSERT_TRUE(client_peer_->CanSendMessage());
+
+ QuartcSession* const peer_sending =
+ direction_from_server ? server_peer_ : client_peer_;
+
+ FakeQuartcSessionDelegate* const delegate_receiving =
+ direction_from_server ? client_session_delegate_.get()
+ : server_session_delegate_.get();
+
+ // There should be no messages in the queue before we start sending.
+ EXPECT_EQ(peer_sending->send_message_queue_size(), 0u);
+
+ // Send messages from peer 1 to peer 2 until required number of messages
+ // are queued in unsent message queue.
+ std::vector<std::string> sent_messages;
+ while (peer_sending->send_message_queue_size() < queue_size) {
+ sent_messages.push_back(
+ QuicStrCat("Sending message, index=", sent_messages.size()));
+ ASSERT_TRUE(peer_sending->SendOrQueueMessage(sent_messages.back()));
+ }
+
+ // Wait for peer 2 to receive all messages.
+ RunTasks();
+
+ EXPECT_EQ(delegate_receiving->incoming_messages(), sent_messages);
+ }
+
+ // Test sending long messages:
+ // - message of maximum allowed length should succeed
+ // - message of > maximum allowed length should fail.
+ void TestSendLongMessage() {
+ ASSERT_TRUE(server_peer_->CanSendMessage());
+ ASSERT_TRUE(client_peer_->CanSendMessage());
+
+ // Send message of maximum allowed length.
+ std::string message_max_long =
+ std::string(server_peer_->GetCurrentLargestMessagePayload(), 'A');
+ ASSERT_TRUE(server_peer_->SendOrQueueMessage(message_max_long));
+
+ // Send long message which should fail.
+ std::string message_too_long =
+ std::string(server_peer_->GetCurrentLargestMessagePayload() + 1, 'B');
+ ASSERT_FALSE(server_peer_->SendOrQueueMessage(message_too_long));
+
+ // Wait for peer 2 to receive message.
+ RunTasks();
+
+ // Client should only receive one message of allowed length.
+ EXPECT_THAT(client_session_delegate_->incoming_messages(),
+ testing::ElementsAre(message_max_long));
+ }
+
+ // Test that client and server are not connected after handshake failure.
+ void TestDisconnectAfterFailedHandshake() {
+ EXPECT_TRUE(!client_session_delegate_->connected());
+ EXPECT_TRUE(!server_session_delegate_->connected());
+
+ EXPECT_FALSE(client_peer_->IsEncryptionEstablished());
+ EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed());
+
+ EXPECT_FALSE(server_peer_->IsEncryptionEstablished());
+ EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed());
+ }
+
+ protected:
+ simulator::Simulator simulator_;
+
+ std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
+ std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
+ std::unique_ptr<simulator::CountingPacketFilter> client_filter_;
+ std::unique_ptr<simulator::SymmetricLink> client_server_link_;
+
+ std::unique_ptr<FakeQuartcStreamDelegate> client_stream_delegate_;
+ std::unique_ptr<FakeQuartcSessionDelegate> client_session_delegate_;
+ std::unique_ptr<FakeQuartcEndpointDelegate> client_endpoint_delegate_;
+ std::unique_ptr<FakeQuartcStreamDelegate> server_stream_delegate_;
+ std::unique_ptr<FakeQuartcSessionDelegate> server_session_delegate_;
+ std::unique_ptr<FakeQuartcEndpointDelegate> server_endpoint_delegate_;
+
+ std::unique_ptr<QuartcClientEndpoint> client_endpoint_;
+ std::unique_ptr<QuartcServerEndpoint> server_endpoint_;
+
+ QuartcSession* client_peer_ = nullptr;
+ QuartcSession* server_peer_ = nullptr;
+};
+
+TEST_F(QuartcSessionTest, SendReceiveStreams) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ TestSendReceiveStreams();
+}
+
+TEST_F(QuartcSessionTest, SendReceiveMessages) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ TestSendReceiveMessage();
+}
+
+TEST_F(QuartcSessionTest, SendReceiveQueuedMessages) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ TestSendReceiveQueuedMessages(/*direction_from_server=*/true);
+ TestSendReceiveQueuedMessages(/*direction_from_server=*/false);
+}
+
+TEST_F(QuartcSessionTest, SendMessageFails) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ TestSendLongMessage();
+}
+
+TEST_F(QuartcSessionTest, TestCryptoHandshakeCanWriteTriggers) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+
+ AwaitHandshake();
+
+ RunTasks();
+
+ ASSERT_TRUE(client_session_delegate_->writable_time().IsInitialized());
+ ASSERT_TRUE(
+ client_session_delegate_->crypto_handshake_time().IsInitialized());
+ // On client, we are writable 1-rtt before crypto handshake is complete.
+ ASSERT_LT(client_session_delegate_->writable_time(),
+ client_session_delegate_->crypto_handshake_time());
+
+ ASSERT_TRUE(server_session_delegate_->writable_time().IsInitialized());
+ ASSERT_TRUE(
+ server_session_delegate_->crypto_handshake_time().IsInitialized());
+ // On server, the writable time and crypto handshake are the same. (when SHLO
+ // is sent).
+ ASSERT_EQ(server_session_delegate_->writable_time(),
+ server_session_delegate_->crypto_handshake_time());
+}
+
+TEST_F(QuartcSessionTest, PreSharedKeyHandshake) {
+ QuartcSessionConfig config;
+ config.pre_shared_key = "foo";
+ CreateClientAndServerSessions(config);
+ AwaitHandshake();
+ TestSendReceiveStreams();
+ TestSendReceiveMessage();
+}
+
+// Test that data streams are not created before handshake.
+TEST_F(QuartcSessionTest, CannotCreateDataStreamBeforeHandshake) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ EXPECT_EQ(nullptr, server_peer_->CreateOutgoingBidirectionalStream());
+ EXPECT_EQ(nullptr, client_peer_->CreateOutgoingBidirectionalStream());
+}
+
+TEST_F(QuartcSessionTest, CancelQuartcStream) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ ASSERT_NE(nullptr, stream);
+
+ uint32_t id = stream->id();
+ EXPECT_FALSE(client_peer_->IsClosedStream(id));
+ stream->SetDelegate(client_stream_delegate_.get());
+ client_peer_->CancelStream(id);
+ EXPECT_EQ(stream->stream_error(),
+ QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED);
+ EXPECT_TRUE(client_peer_->IsClosedStream(id));
+}
+
+// TODO(b/112561077): This is the wrong layer for this test. We should write a
+// test specifically for QuartcPacketWriter with a stubbed-out
+// QuartcPacketTransport and remove
+// SimulatedQuartcPacketTransport::last_packet_number().
+TEST_F(QuartcSessionTest, WriterGivesPacketNumberToTransport) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ stream->SetDelegate(client_stream_delegate_.get());
+
+ char kClientMessage[] = "Hello";
+ test::QuicTestMemSliceVector stream_data(
+ {std::make_pair(kClientMessage, strlen(kClientMessage))});
+ stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
+ RunTasks();
+
+ // The transport should see the latest packet number sent by QUIC.
+ EXPECT_EQ(
+ client_transport_->last_packet_number(),
+ client_peer_->connection()->sent_packet_manager().GetLargestSentPacket());
+}
+
+TEST_F(QuartcSessionTest, CloseConnection) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ client_peer_->CloseConnection("Connection closed by client");
+ EXPECT_FALSE(client_session_delegate_->connected());
+ RunTasks();
+ EXPECT_FALSE(server_session_delegate_->connected());
+}
+
+TEST_F(QuartcSessionTest, StreamRetransmissionEnabled) {
+ CreateClientAndServerSessions(QuartcSessionConfig());
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = stream->id();
+ stream->SetDelegate(client_stream_delegate_.get());
+ stream->set_cancel_on_loss(false);
+
+ client_filter_->set_packets_to_drop(1);
+
+ char kClientMessage[] = "Hello";
+ test::QuicTestMemSliceVector stream_data(
+ {std::make_pair(kClientMessage, strlen(kClientMessage))});
+ stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
+ RunTasks();
+
+ // Stream data should make it despite packet loss.
+ ASSERT_TRUE(server_stream_delegate_->has_data());
+ EXPECT_EQ(server_stream_delegate_->data()[stream_id], kClientMessage);
+}
+
+TEST_F(QuartcSessionTest, StreamRetransmissionDisabled) {
+ // Disable tail loss probe, otherwise test maybe flaky because dropped
+ // message will be retransmitted to detect tail loss.
+ QuartcSessionConfig session_config;
+ session_config.enable_tail_loss_probe = false;
+ CreateClientAndServerSessions(session_config);
+
+ // Disable probing retransmissions, otherwise test maybe flaky because dropped
+ // message will be retransmitted to to probe for more bandwidth.
+ client_peer_->connection()->set_fill_up_link_during_probing(false);
+
+ AwaitHandshake();
+ ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
+ ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
+
+ // The client sends an ACK for the crypto handshake next. This must be
+ // flushed before we set the filter to drop the next packet, in order to
+ // ensure that the filter drops a data-bearing packet instead of just an ack.
+ RunTasks();
+
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ QuicStreamId stream_id = stream->id();
+ stream->SetDelegate(client_stream_delegate_.get());
+ stream->set_cancel_on_loss(true);
+
+ client_filter_->set_packets_to_drop(1);
+
+ char kMessage[] = "Hello";
+ test::QuicTestMemSliceVector stream_data(
+ {std::make_pair(kMessage, strlen(kMessage))});
+ stream->WriteMemSlices(stream_data.span(), /*fin=*/false);
+ simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1));
+
+ // Send another packet to trigger loss detection.
+ QuartcStream* stream_1 = client_peer_->CreateOutgoingBidirectionalStream();
+ stream_1->SetDelegate(client_stream_delegate_.get());
+
+ char kMessage1[] = "Second message";
+ test::QuicTestMemSliceVector stream_data_1(
+ {std::make_pair(kMessage1, strlen(kMessage1))});
+ stream_1->WriteMemSlices(stream_data_1.span(), /*fin=*/false);
+ RunTasks();
+
+ // QUIC should try to retransmit the first stream by loss detection. Instead,
+ // it will cancel itself.
+ EXPECT_THAT(server_stream_delegate_->data()[stream_id], testing::IsEmpty());
+
+ EXPECT_TRUE(client_peer_->IsClosedStream(stream_id));
+ EXPECT_TRUE(server_peer_->IsClosedStream(stream_id));
+ EXPECT_EQ(client_stream_delegate_->stream_error(stream_id),
+ QUIC_STREAM_CANCELLED);
+ EXPECT_EQ(server_stream_delegate_->stream_error(stream_id),
+ QUIC_STREAM_CANCELLED);
+}
+
+TEST_F(QuartcSessionTest, ServerRegistersAsWriteBlocked) {
+ // Initialize client and server session, but with the server write-blocked.
+ Init();
+ server_transport_->SetWritable(false);
+ CreateClientAndServerSessions(QuartcSessionConfig(), /*init=*/false);
+
+ // Let the client send a few copies of the CHLO. The server can't respond, as
+ // it's still write-blocked.
+ RunTasks();
+
+ // Making the server's transport writable should trigger a callback that
+ // reaches the server session, allowing it to write packets.
+ server_transport_->SetWritable(true);
+
+ // Now the server should respond with the SHLO, encryption should be
+ // established, and data should flow normally.
+ // Note that if the server is *not* correctly registered as write-blocked,
+ // it will crash here (see b/124527328 for details).
+ AwaitHandshake();
+ TestSendReceiveStreams();
+}
+
+TEST_F(QuartcSessionTest, PreSharedKeyHandshakeIs0RTT) {
+ QuartcSessionConfig session_config;
+ session_config.pre_shared_key = "foo";
+
+ // Client endpoint is created below. Destructing client endpoint
+ // causes issues with the simulator.
+ Init(/*create_client_endpoint=*/false);
+
+ server_endpoint_->Connect(server_transport_.get());
+
+ client_endpoint_ = QuicMakeUnique<QuartcClientEndpoint>(
+ simulator_.GetAlarmFactory(), simulator_.GetClock(),
+ client_endpoint_delegate_.get(), QuartcSessionConfig(),
+ // This is the key line here. It passes through the server config
+ // from the server to the client.
+ server_endpoint_->server_crypto_config());
+
+ client_endpoint_->Connect(client_transport_.get());
+
+ // Running for 1ms. This is shorter than the RTT, so the
+ // client session should be created, but server won't be created yet.
+ simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1));
+
+ client_peer_ = client_endpoint_delegate_->session();
+ server_peer_ = server_endpoint_delegate_->session();
+
+ ASSERT_NE(client_peer_, nullptr);
+ ASSERT_EQ(server_peer_, nullptr);
+
+ // Write data to the client before running tasks. This should be sent by the
+ // client and received by the server if the handshake is 0RTT.
+ // If this test fails, add 'RunTasks()' above, and see what error is sent
+ // by the server in the rejection message.
+ QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream();
+ ASSERT_NE(stream, nullptr);
+ QuicStreamId stream_id = stream->id();
+ stream->SetDelegate(client_stream_delegate_.get());
+
+ char message[] = "Hello in 0RTTs!";
+ test::QuicTestMemSliceVector data({std::make_pair(message, strlen(message))});
+ stream->WriteMemSlices(data.span(), /*fin=*/false);
+
+ // This will now run the rest of the connection. But the
+ // Server peer will receive the CHLO and message after 1 delay.
+ simulator_.RunFor(kPropagationDelayAndABit);
+
+ // If we can decrypt the data, it means that 0 rtt was successful.
+ // This is because we waited only a propagation delay. So if the decryption
+ // failed, we would send sREJ instead of SHLO, but it wouldn't be delivered to
+ // the client yet.
+ ASSERT_TRUE(server_stream_delegate_->has_data());
+ EXPECT_EQ(server_stream_delegate_->data()[stream_id], message);
+}
+
+} // namespace
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc
new file mode 100644
index 00000000000..49ade235765
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.cc
@@ -0,0 +1,171 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h"
+
+#include <memory>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QuartcStream::QuartcStream(QuicStreamId id, QuicSession* session)
+ : QuicStream(id, session, /*is_static=*/false, BIDIRECTIONAL) {
+ sequencer()->set_level_triggered(true);
+}
+
+QuartcStream::QuartcStream(PendingStream pending)
+ : QuicStream(std::move(pending), BIDIRECTIONAL) {
+ sequencer()->set_level_triggered(true);
+}
+
+QuartcStream::~QuartcStream() {}
+
+void QuartcStream::OnDataAvailable() {
+ bool fin = sequencer()->ReadableBytes() + sequencer()->NumBytesConsumed() ==
+ sequencer()->close_offset();
+
+ // Upper bound on number of readable regions. Each complete block's worth of
+ // data crosses at most one region boundary. The remainder may cross one more
+ // boundary. Number of regions is one more than the number of region
+ // boundaries crossed.
+ size_t iov_length = sequencer()->ReadableBytes() /
+ QuicStreamSequencerBuffer::kBlockSizeBytes +
+ 2;
+ std::unique_ptr<iovec[]> iovecs = QuicMakeUnique<iovec[]>(iov_length);
+ iov_length = sequencer()->GetReadableRegions(iovecs.get(), iov_length);
+
+ sequencer()->MarkConsumed(
+ delegate_->OnReceived(this, iovecs.get(), iov_length, fin));
+ if (sequencer()->IsClosed()) {
+ OnFinRead();
+ }
+}
+
+void QuartcStream::OnClose() {
+ QuicStream::OnClose();
+ DCHECK(delegate_);
+ delegate_->OnClose(this);
+}
+
+void QuartcStream::OnStreamDataConsumed(size_t bytes_consumed) {
+ QuicStream::OnStreamDataConsumed(bytes_consumed);
+
+ DCHECK(delegate_);
+ delegate_->OnBufferChanged(this);
+}
+
+void QuartcStream::OnDataBuffered(
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) {
+ DCHECK(delegate_);
+ delegate_->OnBufferChanged(this);
+}
+
+bool QuartcStream::OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) {
+ // Previous losses of acked data are no longer relevant to the retransmission
+ // count. Once data is acked, it will never be retransmitted.
+ lost_frame_counter_.RemoveInterval(
+ QuicInterval<QuicStreamOffset>(offset, offset + data_length));
+
+ return QuicStream::OnStreamFrameAcked(offset, data_length, fin_acked,
+ ack_delay_time, newly_acked_length);
+}
+
+void QuartcStream::OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted) {
+ QuicStream::OnStreamFrameRetransmitted(offset, data_length,
+ fin_retransmitted);
+
+ DCHECK(delegate_);
+ delegate_->OnBufferChanged(this);
+}
+
+void QuartcStream::OnStreamFrameLost(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_lost) {
+ QuicStream::OnStreamFrameLost(offset, data_length, fin_lost);
+
+ lost_frame_counter_.AddInterval(
+ QuicInterval<QuicStreamOffset>(offset, offset + data_length));
+
+ DCHECK(delegate_);
+ delegate_->OnBufferChanged(this);
+}
+
+void QuartcStream::OnCanWrite() {
+ if (lost_frame_counter_.MaxCount() >
+ static_cast<size_t>(max_retransmission_count_) &&
+ HasPendingRetransmission()) {
+ Reset(QUIC_STREAM_CANCELLED);
+ return;
+ }
+ QuicStream::OnCanWrite();
+}
+
+bool QuartcStream::cancel_on_loss() {
+ return max_retransmission_count_ == 0;
+}
+
+void QuartcStream::set_cancel_on_loss(bool cancel_on_loss) {
+ if (cancel_on_loss) {
+ max_retransmission_count_ = 0;
+ } else {
+ max_retransmission_count_ = std::numeric_limits<int>::max();
+ }
+}
+
+int QuartcStream::max_retransmission_count() const {
+ return max_retransmission_count_;
+}
+
+void QuartcStream::set_max_retransmission_count(int max_retransmission_count) {
+ max_retransmission_count_ = max_retransmission_count;
+}
+
+QuicByteCount QuartcStream::BytesPendingRetransmission() {
+ if (lost_frame_counter_.MaxCount() >
+ static_cast<size_t>(max_retransmission_count_)) {
+ return 0; // Lost bytes will never be retransmitted.
+ }
+ QuicByteCount bytes = 0;
+ for (const auto& interval : send_buffer().pending_retransmissions()) {
+ bytes += interval.Length();
+ }
+ return bytes;
+}
+
+QuicStreamOffset QuartcStream::ReadOffset() {
+ return sequencer()->NumBytesConsumed();
+}
+
+void QuartcStream::FinishWriting() {
+ WriteOrBufferData(QuicStringPiece(nullptr, 0), true, nullptr);
+}
+
+void QuartcStream::SetDelegate(Delegate* delegate) {
+ if (delegate_) {
+ LOG(WARNING) << "The delegate for Stream " << id()
+ << " has already been set.";
+ }
+ delegate_ = delegate;
+ DCHECK(delegate_);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h
new file mode 100644
index 00000000000..bf15a684732
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream.h
@@ -0,0 +1,142 @@
+// 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_QUARTC_QUARTC_STREAM_H_
+#define QUICHE_QUIC_QUARTC_QUARTC_STREAM_H_
+
+#include <stddef.h>
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_span.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_reference_counted.h"
+#include "net/quic/platform/impl/quic_export_impl.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_interval_counter.h"
+
+namespace quic {
+
+// Sends and receives data with a particular QUIC stream ID, reliably and
+// in-order. To send/receive data out of order, use separate streams. To
+// send/receive unreliably, close a stream after reliability is no longer
+// needed.
+class QuartcStream : public QuicStream {
+ public:
+ QuartcStream(QuicStreamId id, QuicSession* session);
+ explicit QuartcStream(PendingStream pending);
+
+ ~QuartcStream() override;
+
+ // QuicStream overrides.
+ void OnDataAvailable() override;
+
+ void OnClose() override;
+
+ void OnStreamDataConsumed(size_t bytes_consumed) override;
+
+ void OnDataBuffered(
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener)
+ override;
+
+ bool OnStreamFrameAcked(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_acked,
+ QuicTime::Delta ack_delay_time,
+ QuicByteCount* newly_acked_length) override;
+
+ void OnStreamFrameRetransmitted(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_retransmitted) override;
+
+ void OnStreamFrameLost(QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin_lost) override;
+
+ void OnCanWrite() override;
+
+ // QuartcStream interface methods.
+
+ // Whether the stream should be cancelled instead of retransmitted on loss.
+ // If set to true, the stream will reset itself instead of retransmitting lost
+ // stream frames. Defaults to false. Setting it to true is equivalent to
+ // setting |max_retransmission_count| to zero.
+ bool cancel_on_loss();
+ void set_cancel_on_loss(bool cancel_on_loss);
+
+ // Maximum number of times this stream's data may be retransmitted. Each byte
+ // of stream data may be retransmitted this many times. If any byte (or range
+ // of bytes) is lost and would be retransmitted more than this number of
+ // times, the stream resets itself instead of retransmitting the data again.
+ // Setting this value to zero disables retransmissions.
+ //
+ // Note that this limit applies only to stream data, not to the FIN bit. If
+ // only the FIN bit needs to be retransmitted, there is no benefit to
+ // cancelling the stream and sending a reset frame instead.
+ int max_retransmission_count() const;
+ void set_max_retransmission_count(int max_retransmission_count);
+
+ QuicByteCount BytesPendingRetransmission();
+
+ // Returns the current read offset for this stream. During a call to
+ // Delegate::OnReceived, this value is the offset of the first byte read.
+ QuicStreamOffset ReadOffset();
+
+ // Marks this stream as finished writing. Asynchronously sends a FIN and
+ // closes the write-side. It is not necessary to call FinishWriting() if the
+ // last call to Write() sends a FIN.
+ void FinishWriting();
+
+ // Implemented by the user of the QuartcStream to receive incoming
+ // data and be notified of state changes.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called when the stream receives data. |iov| is a pointer to the first of
+ // |iov_length| readable regions. |iov| points to readable data within
+ // |stream|'s sequencer buffer. QUIC may modify or delete this data after
+ // the application consumes it. |fin| indicates the end of stream data.
+ // Returns the number of bytes consumed. May return 0 if the delegate is
+ // unable to consume any bytes at this time.
+ virtual size_t OnReceived(QuartcStream* stream,
+ iovec* iov,
+ size_t iov_length,
+ bool fin) = 0;
+
+ // Called when the stream is closed, either locally or by the remote
+ // endpoint. Streams close when (a) fin bits are both sent and received,
+ // (b) Close() is called, or (c) the stream is reset.
+ // TODO(zhihuang) Creates a map from the integer error_code to WebRTC native
+ // error code.
+ virtual void OnClose(QuartcStream* stream) = 0;
+
+ // Called when the contents of the stream's buffer changes.
+ virtual void OnBufferChanged(QuartcStream* stream) = 0;
+ };
+
+ // The |delegate| is not owned by QuartcStream.
+ void SetDelegate(Delegate* delegate);
+
+ private:
+ Delegate* delegate_ = nullptr;
+
+ // Maximum number of times this stream's data may be retransmitted.
+ int max_retransmission_count_ = std::numeric_limits<int>::max();
+
+ // Counter which tracks the number of times each frame has been lost
+ // (accounting for the possibility of overlapping frames).
+ //
+ // If the maximum count of any lost frame exceeds |max_retransmission_count_|,
+ // the stream will cancel itself on the next attempt to retransmit data (the
+ // next call to |OnCanWrite|).
+ QuartcIntervalCounter<QuicStreamOffset> lost_frame_counter_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_QUARTC_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc
new file mode 100644
index 00000000000..3b5458c7870
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/quartc_stream_test.cc
@@ -0,0 +1,640 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_stream.h"
+
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_factory.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+namespace quic {
+
+namespace {
+
+static const QuicStreamId kStreamId = 5;
+
+// MockQuicSession that does not create streams and writes data from
+// QuicStream to a string.
+class MockQuicSession : public QuicSession {
+ public:
+ MockQuicSession(QuicConnection* connection,
+ const QuicConfig& config,
+ std::string* write_buffer)
+ : QuicSession(connection,
+ nullptr /*visitor*/,
+ config,
+ CurrentSupportedVersions()),
+ write_buffer_(write_buffer) {}
+
+ ~MockQuicSession() override {}
+
+ // Writes outgoing data from QuicStream to a string.
+ QuicConsumedData WritevData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) override {
+ if (!writable_) {
+ return QuicConsumedData(0, false);
+ }
+
+ // WritevData does not pass down a iovec, data is saved in stream before
+ // data is consumed. Retrieve data from stream.
+ char* buf = new char[write_length];
+ QuicDataWriter writer(write_length, buf, NETWORK_BYTE_ORDER);
+ if (write_length > 0) {
+ stream->WriteStreamData(offset, write_length, &writer);
+ }
+ write_buffer_->append(buf, write_length);
+ delete[] buf;
+ return QuicConsumedData(write_length, state != StreamSendingState::NO_FIN);
+ }
+
+ QuartcStream* CreateIncomingStream(QuicStreamId id) override {
+ return nullptr;
+ }
+
+ QuartcStream* CreateIncomingStream(PendingStream pending) override {
+ return nullptr;
+ }
+
+ const QuicCryptoStream* GetCryptoStream() const override { return nullptr; }
+ QuicCryptoStream* GetMutableCryptoStream() override { return nullptr; }
+ bool ShouldKeepConnectionAlive() const override {
+ return GetNumOpenDynamicStreams() > 0;
+ }
+
+ // Called by QuicStream when they want to close stream.
+ void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) override {}
+
+ // Sets whether data is written to buffer, or else if this is write blocked.
+ void set_writable(bool writable) { writable_ = writable; }
+
+ // Tracks whether the stream is write blocked and its priority.
+ void RegisterReliableStream(QuicStreamId stream_id,
+ spdy::SpdyPriority priority) {
+ write_blocked_streams()->RegisterStream(stream_id,
+ /*is_static_stream=*/false,
+ priority);
+ }
+
+ // The session take ownership of the stream.
+ void ActivateReliableStream(std::unique_ptr<QuicStream> stream) {
+ ActivateStream(std::move(stream));
+ }
+
+ private:
+ // Stores written data from ReliableQuicStreamAdapter.
+ std::string* write_buffer_;
+ // Whether data is written to write_buffer_.
+ bool writable_ = true;
+};
+
+// Packet writer that does nothing. This is required for QuicConnection but
+// isn't used for writing data.
+class DummyPacketWriter : public QuicPacketWriter {
+ public:
+ DummyPacketWriter() {}
+
+ // QuicPacketWriter overrides.
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override {
+ return WriteResult(WRITE_STATUS_ERROR, 0);
+ }
+
+ bool IsWriteBlocked() const override { return false; }
+
+ void SetWritable() override {}
+
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const override {
+ return 0;
+ }
+
+ bool SupportsReleaseTime() const override { return false; }
+
+ bool IsBatchMode() const override { return false; }
+
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override {
+ return nullptr;
+ }
+
+ WriteResult Flush() override { return WriteResult(WRITE_STATUS_OK, 0); }
+};
+
+class MockQuartcStreamDelegate : public QuartcStream::Delegate {
+ public:
+ MockQuartcStreamDelegate(QuicStreamId id, std::string* read_buffer)
+ : id_(id), read_buffer_(read_buffer) {}
+
+ void OnBufferChanged(QuartcStream* stream) override {
+ last_bytes_buffered_ = stream->BufferedDataBytes();
+ last_bytes_pending_retransmission_ = stream->BytesPendingRetransmission();
+ }
+
+ size_t OnReceived(QuartcStream* stream,
+ iovec* iov,
+ size_t iov_length,
+ bool fin) override {
+ EXPECT_EQ(id_, stream->id());
+ EXPECT_EQ(stream->ReadOffset(), read_buffer_->size());
+ size_t bytes_consumed = 0;
+ for (size_t i = 0; i < iov_length; ++i) {
+ read_buffer_->append(static_cast<const char*>(iov[i].iov_base),
+ iov[i].iov_len);
+ bytes_consumed += iov[i].iov_len;
+ }
+ return bytes_consumed;
+ }
+
+ void OnClose(QuartcStream* stream) override { closed_ = true; }
+
+ bool closed() { return closed_; }
+
+ QuicByteCount last_bytes_buffered() { return last_bytes_buffered_; }
+ QuicByteCount last_bytes_pending_retransmission() {
+ return last_bytes_pending_retransmission_;
+ }
+
+ protected:
+ QuicStreamId id_;
+ // Data read by the QuicStream.
+ std::string* read_buffer_;
+ // Whether the QuicStream is closed.
+ bool closed_ = false;
+
+ // Last amount of data observed as buffered.
+ QuicByteCount last_bytes_buffered_ = 0;
+ QuicByteCount last_bytes_pending_retransmission_ = 0;
+};
+
+class QuartcStreamTest : public QuicTest, public QuicConnectionHelperInterface {
+ public:
+ QuartcStreamTest() {
+ // Required to correctly handle StopReading().
+ SetQuicReloadableFlag(quic_stop_reading_when_level_triggered, true);
+ }
+
+ ~QuartcStreamTest() override = default;
+
+ void CreateReliableQuicStream() {
+ // Arbitrary values for QuicConnection.
+ Perspective perspective = Perspective::IS_SERVER;
+ QuicIpAddress ip;
+ ip.FromString("0.0.0.0");
+ bool owns_writer = true;
+
+ alarm_factory_ = QuicMakeUnique<test::MockAlarmFactory>();
+
+ connection_ = QuicMakeUnique<QuicConnection>(
+ QuicUtils::CreateZeroConnectionId(
+ CurrentSupportedVersions()[0].transport_version),
+ QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/,
+ alarm_factory_.get(), new DummyPacketWriter(), owns_writer, perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0));
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ session_ = QuicMakeUnique<MockQuicSession>(connection_.get(), QuicConfig(),
+ &write_buffer_);
+ mock_stream_delegate_ =
+ QuicMakeUnique<MockQuartcStreamDelegate>(kStreamId, &read_buffer_);
+ stream_ = new QuartcStream(kStreamId, session_.get());
+ stream_->SetDelegate(mock_stream_delegate_.get());
+ session_->ActivateReliableStream(std::unique_ptr<QuartcStream>(stream_));
+ }
+
+ const QuicClock* GetClock() const override { return &clock_; }
+
+ QuicRandom* GetRandomGenerator() override {
+ return QuicRandom::GetInstance();
+ }
+
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override {
+ return &buffer_allocator_;
+ }
+
+ protected:
+ // The QuicSession will take the ownership.
+ QuartcStream* stream_;
+ std::unique_ptr<MockQuartcStreamDelegate> mock_stream_delegate_;
+ std::unique_ptr<MockQuicSession> session_;
+ // Data written by the ReliableQuicStreamAdapterTest.
+ std::string write_buffer_;
+ // Data read by the ReliableQuicStreamAdapterTest.
+ std::string read_buffer_;
+ std::unique_ptr<QuicAlarmFactory> alarm_factory_;
+ std::unique_ptr<QuicConnection> connection_;
+ // Used to implement the QuicConnectionHelperInterface.
+ SimpleBufferAllocator buffer_allocator_;
+ MockClock clock_;
+};
+
+// Write an entire string.
+TEST_F(QuartcStreamTest, WriteDataWhole) {
+ CreateReliableQuicStream();
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+ EXPECT_EQ("Foo bar", write_buffer_);
+}
+
+// Write part of a string.
+TEST_F(QuartcStreamTest, WriteDataPartial) {
+ CreateReliableQuicStream();
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 5)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+ EXPECT_EQ("Foo b", write_buffer_);
+}
+
+// Test that a QuartcStream buffers writes correctly.
+TEST_F(QuartcStreamTest, StreamBuffersData) {
+ CreateReliableQuicStream();
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+
+ // The stream is not yet writable, so data will be buffered.
+ session_->set_writable(false);
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ // Check that data is buffered.
+ EXPECT_TRUE(stream_->HasBufferedData());
+ EXPECT_EQ(7u, stream_->BufferedDataBytes());
+
+ // Check that the stream told its delegate about the buffer change.
+ EXPECT_EQ(7u, mock_stream_delegate_->last_bytes_buffered());
+
+ // Check that none of the data was written yet.
+ // Note that |write_buffer_| actually holds data written by the QuicSession
+ // (not data buffered by the stream).
+ EXPECT_EQ(0ul, write_buffer_.size());
+
+ char message1[] = "xyzzy";
+ test::QuicTestMemSliceVector data1({std::make_pair(message1, 5)});
+
+ // More writes go into the buffer.
+ stream_->WriteMemSlices(data1.span(), /*fin=*/false);
+
+ EXPECT_TRUE(stream_->HasBufferedData());
+ EXPECT_EQ(12u, stream_->BufferedDataBytes());
+ EXPECT_EQ(12u, mock_stream_delegate_->last_bytes_buffered());
+ EXPECT_EQ(0ul, write_buffer_.size());
+
+ // The stream becomes writable, so it sends the buffered data.
+ session_->set_writable(true);
+ stream_->OnCanWrite();
+
+ EXPECT_FALSE(stream_->HasBufferedData());
+ EXPECT_EQ(0u, stream_->BufferedDataBytes());
+ EXPECT_EQ(0u, mock_stream_delegate_->last_bytes_buffered());
+ EXPECT_EQ("Foo barxyzzy", write_buffer_);
+}
+
+// Finish writing to a stream.
+// It delivers the fin bit and closes the write-side as soon as possible.
+TEST_F(QuartcStreamTest, FinishWriting) {
+ CreateReliableQuicStream();
+
+ session_->set_writable(false);
+ stream_->FinishWriting();
+ EXPECT_FALSE(stream_->fin_sent());
+
+ // Fin is sent as soon as the stream becomes writable.
+ session_->set_writable(true);
+ stream_->OnCanWrite();
+ EXPECT_TRUE(stream_->fin_sent());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+// Read an entire string.
+TEST_F(QuartcStreamTest, ReadDataWhole) {
+ CreateReliableQuicStream();
+ QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_EQ("Hello, World!", read_buffer_);
+}
+
+// Read part of a string.
+TEST_F(QuartcStreamTest, ReadDataPartial) {
+ CreateReliableQuicStream();
+ QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
+ frame.data_length = 5;
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_EQ("Hello", read_buffer_);
+}
+
+// Streams do not call OnReceived() after StopReading().
+// Note: this is tested here because Quartc relies on this behavior.
+TEST_F(QuartcStreamTest, StopReading) {
+ CreateReliableQuicStream();
+ stream_->StopReading();
+
+ QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_EQ(0ul, read_buffer_.size());
+
+ QuicStreamFrame frame2(kStreamId, true, 0, "Hello, World!");
+ stream_->OnStreamFrame(frame2);
+
+ EXPECT_EQ(0ul, read_buffer_.size());
+ EXPECT_TRUE(stream_->fin_received());
+}
+
+// Test that closing the stream results in a callback.
+TEST_F(QuartcStreamTest, CloseStream) {
+ CreateReliableQuicStream();
+ EXPECT_FALSE(mock_stream_delegate_->closed());
+ stream_->OnClose();
+ EXPECT_TRUE(mock_stream_delegate_->closed());
+}
+
+// Both sending and receiving fin automatically closes a stream.
+TEST_F(QuartcStreamTest, CloseOnFins) {
+ CreateReliableQuicStream();
+ QuicStreamFrame frame(kStreamId, true, 0, 0);
+ stream_->OnStreamFrame(frame);
+
+ test::QuicTestMemSliceVector data({});
+ stream_->WriteMemSlices(data.span(), /*fin=*/true);
+
+ // Check that the OnClose() callback occurred.
+ EXPECT_TRUE(mock_stream_delegate_->closed());
+}
+
+TEST_F(QuartcStreamTest, TestCancelOnLossDisabled) {
+ CreateReliableQuicStream();
+
+ // This should be the default state.
+ EXPECT_FALSE(stream_->cancel_on_loss());
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, TestCancelOnLossEnabled) {
+ CreateReliableQuicStream();
+ stream_->set_cancel_on_loss(true);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsAbsent) {
+ CreateReliableQuicStream();
+
+ // This should be the default state.
+ EXPECT_EQ(stream_->max_retransmission_count(),
+ std::numeric_limits<int>::max());
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsSet) {
+ CreateReliableQuicStream();
+ stream_->set_max_retransmission_count(2);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsDisjointFrames) {
+ CreateReliableQuicStream();
+ stream_->set_max_retransmission_count(2);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ // Retransmit bytes [0, 3].
+ stream_->OnStreamFrameLost(0, 4, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo ", write_buffer_);
+
+ // Retransmit bytes [4, 6]. Everything has been retransmitted once.
+ stream_->OnStreamFrameLost(4, 3, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ // Retransmit bytes [0, 6]. Everything can be retransmitted a second time.
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsOverlappingFrames) {
+ CreateReliableQuicStream();
+ stream_->set_max_retransmission_count(2);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ // Retransmit bytes 0 to 3.
+ stream_->OnStreamFrameLost(0, 4, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo ", write_buffer_);
+
+ // Retransmit bytes 3 to 6. Byte 3 has been retransmitted twice.
+ stream_->OnStreamFrameLost(3, 4, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ // Retransmit byte 3 a third time. This should cause cancellation.
+ stream_->OnStreamFrameLost(3, 1, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
+TEST_F(QuartcStreamTest, MaxRetransmissionsWithAckedFrame) {
+ CreateReliableQuicStream();
+ stream_->set_max_retransmission_count(1);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ // Retransmit bytes [0, 7).
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ // Ack bytes [0, 7). These bytes should be pruned from the data tracked by
+ // the stream.
+ QuicByteCount newly_acked_length = 0;
+ stream_->OnStreamFrameAcked(0, 7, false, QuicTime::Delta::FromMilliseconds(1),
+ &newly_acked_length);
+ EXPECT_EQ(7u, newly_acked_length);
+ stream_->OnCanWrite();
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+ // Retransmit bytes [0, 7) again.
+ // QUIC will never mark frames as lost after they've been acked, but this lets
+ // us test that QuartcStream stopped tracking these bytes after the acked.
+ stream_->OnStreamFrameLost(0, 7, false);
+ stream_->OnCanWrite();
+
+ // QuartcStream should be cancelled, but it stopped tracking the lost bytes
+ // after they were acked, so it's not.
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, TestBytesPendingRetransmission) {
+ CreateReliableQuicStream();
+ stream_->set_cancel_on_loss(false);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 4, false);
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 4u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 4u);
+
+ stream_->OnStreamFrameLost(4, 3, false);
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 7u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 7u);
+
+ stream_->OnCanWrite();
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u);
+
+ EXPECT_EQ("Foo barFoo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, TestBytesPendingRetransmissionWithCancelOnLoss) {
+ CreateReliableQuicStream();
+ stream_->set_cancel_on_loss(true);
+
+ char message[] = "Foo bar";
+ test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+ stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+
+ stream_->OnStreamFrameLost(0, 4, false);
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u);
+
+ stream_->OnStreamFrameLost(4, 3, false);
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u);
+
+ stream_->OnCanWrite();
+ EXPECT_EQ(stream_->BytesPendingRetransmission(), 0u);
+ EXPECT_EQ(mock_stream_delegate_->last_bytes_pending_retransmission(), 0u);
+
+ EXPECT_EQ("Foo bar", write_buffer_);
+ EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
+} // namespace
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc
new file mode 100644
index 00000000000..8e82d98b62d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.cc
@@ -0,0 +1,95 @@
+// 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 "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+namespace simulator {
+
+SimulatedQuartcPacketTransport::SimulatedQuartcPacketTransport(
+ Simulator* simulator,
+ const std::string& name,
+ const std::string& peer_name,
+ QuicByteCount queue_capacity)
+ : Endpoint(simulator, name),
+ peer_name_(peer_name),
+ egress_queue_(simulator,
+ QuicStringPrintf("%s (TX Queue)", name.c_str()),
+ queue_capacity) {
+ egress_queue_.set_listener_interface(this);
+}
+
+int SimulatedQuartcPacketTransport::Write(const char* buffer,
+ size_t buf_len,
+ const PacketInfo& info) {
+ if (!writable_) {
+ return 0;
+ }
+ if (egress_queue_.bytes_queued() + buf_len > egress_queue_.capacity()) {
+ return 0;
+ }
+
+ last_packet_number_ = info.packet_number;
+
+ auto packet = QuicMakeUnique<Packet>();
+ packet->contents = std::string(buffer, buf_len);
+ packet->size = buf_len;
+ packet->tx_timestamp = clock_->Now();
+ packet->source = name();
+ packet->destination = peer_name_;
+
+ egress_queue_.AcceptPacket(std::move(packet));
+ return buf_len;
+}
+
+void SimulatedQuartcPacketTransport::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ Schedule(clock_->Now());
+}
+
+UnconstrainedPortInterface* SimulatedQuartcPacketTransport::GetRxPort() {
+ return this;
+}
+
+void SimulatedQuartcPacketTransport::SetTxPort(ConstrainedPortInterface* port) {
+ egress_queue_.set_tx_port(port);
+ Schedule(clock_->Now());
+}
+
+void SimulatedQuartcPacketTransport::AcceptPacket(
+ std::unique_ptr<Packet> packet) {
+ // Simulated switches broadcast packets to all ports if the cannot determine
+ // the recipient, so we need to drop packets that aren't intended for us.
+ if (packet->destination != name()) {
+ return;
+ }
+
+ if (delegate_) {
+ delegate_->OnTransportReceived(packet->contents.data(), packet->size);
+ }
+}
+
+void SimulatedQuartcPacketTransport::OnPacketDequeued() {
+ if (delegate_ && writable_) {
+ delegate_->OnTransportCanWrite();
+ }
+}
+
+void SimulatedQuartcPacketTransport::Act() {
+ if (delegate_ && writable_) {
+ delegate_->OnTransportCanWrite();
+ }
+}
+
+void SimulatedQuartcPacketTransport::SetWritable(bool writable) {
+ writable_ = writable;
+ if (writable_) {
+ // May need to call |Delegate::OnTransportCanWrite|.
+ Schedule(clock_->Now());
+ }
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h
new file mode 100644
index 00000000000..185668b528a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h
@@ -0,0 +1,84 @@
+// 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_QUARTC_SIMULATED_PACKET_TRANSPORT_H_
+#define QUICHE_QUIC_QUARTC_SIMULATED_PACKET_TRANSPORT_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+
+namespace quic {
+namespace simulator {
+
+// Simulated implementation of QuartcPacketTransport. This packet transport
+// implementation connects Quartc to a QUIC simulator's network fabric.
+// Assumes that its caller and delegate run on the same thread as the network
+// simulation and therefore require no additional synchronization.
+class SimulatedQuartcPacketTransport : public Endpoint,
+ public QuartcPacketTransport,
+ public UnconstrainedPortInterface,
+ public Queue::ListenerInterface {
+ public:
+ SimulatedQuartcPacketTransport(Simulator* simulator,
+ const std::string& name,
+ const std::string& peer_name,
+ QuicByteCount queue_capacity);
+
+ // QuartcPacketTransport methods.
+ int Write(const char* buffer,
+ size_t buf_len,
+ const PacketInfo& info) override;
+ void SetDelegate(Delegate* delegate) override;
+
+ // Simulation methods below. These are implementation details.
+
+ // Endpoint methods. Called by the simulation to connect the transport.
+ UnconstrainedPortInterface* GetRxPort() override;
+ void SetTxPort(ConstrainedPortInterface* port) override;
+
+ // UnconstrainedPortInterface method. Called by the simulation to deliver a
+ // packet to this transport.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+
+ // Queue::ListenerInterface method. Called when the internal egress queue has
+ // dispatched a packet and may have room for more.
+ void OnPacketDequeued() override;
+
+ // Actor method. The transport schedules this to run when the delegate is set
+ // in order to trigger an initial call to |Delegate::OnTransportCanWrite()|.
+ // (The Quartc packet writer starts in a blocked state and needs an initial
+ // callback to unblock it.)
+ void Act() override;
+
+ // Changes whether the transport is writable. If |writable| is false, the
+ // transport will reject calls to |Write| and will not call
+ // |Delegate::OnTransportCanWrite|. If |writable| is true, the transport will
+ // allow calls to |Write| and will call |Delegate::OnTransportCanWrite|
+ // whenever it is able to write another packet.
+ void SetWritable(bool writable);
+
+ // Last packet number sent over this simulated transport.
+ // TODO(b/112561077): Reorganize tests so that this method can be deleted.
+ // This exists purely for use by quartc_session_test.cc, to test that the
+ // packet writer passes packet numbers to the transport.
+ QuicPacketNumber last_packet_number() { return last_packet_number_; }
+
+ private:
+ std::string peer_name_;
+ Delegate* delegate_ = nullptr;
+ Queue egress_queue_;
+ QuicPacketNumber last_packet_number_;
+
+ // Controls whether the transport is considered to be writable. Used to
+ // simulate behavior that arises when the transport is blocked.
+ bool writable_ = true;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUARTC_SIMULATED_PACKET_TRANSPORT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc
new file mode 100644
index 00000000000..cae966f4a3e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc
@@ -0,0 +1,165 @@
+// 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 "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+
+namespace quic {
+namespace simulator {
+namespace {
+
+using ::testing::ElementsAre;
+
+const QuicBandwidth kDefaultBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(10 * 1000);
+const QuicTime::Delta kDefaultPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(20);
+const QuicByteCount kDefaultBdp = kDefaultBandwidth * kDefaultPropagationDelay;
+const QuicByteCount kDefaultPacketSize = 1200;
+const QuicPacketCount kDefaultQueueLength = 10;
+
+class FakeDelegate : public QuartcPacketTransport::Delegate {
+ public:
+ explicit FakeDelegate(QuartcPacketTransport* transport)
+ : transport_(transport) {
+ transport_->SetDelegate(this);
+ }
+
+ ~FakeDelegate() { transport_->SetDelegate(nullptr); }
+
+ void OnTransportCanWrite() override {
+ while (!packets_to_send_.empty()) {
+ const std::string& packet = packets_to_send_.front();
+ if (transport_->Write(packet.data(), packet.size(),
+ QuartcPacketTransport::PacketInfo()) <
+ static_cast<int>(packet.size())) {
+ ++write_blocked_count_;
+ return;
+ }
+ packets_to_send_.pop();
+ }
+ }
+
+ void OnTransportReceived(const char* data, size_t data_len) override {
+ packets_received_.emplace_back(data, data_len);
+ }
+
+ void AddPacketToSend(const std::string& packet) {
+ packets_to_send_.push(packet);
+ }
+
+ size_t packets_to_send() { return packets_to_send_.size(); }
+ const std::vector<std::string>& packets_received() {
+ return packets_received_;
+ }
+ int write_blocked_count() { return write_blocked_count_; }
+
+ private:
+ QuartcPacketTransport* const transport_ = nullptr;
+ std::queue<std::string> packets_to_send_;
+ std::vector<std::string> packets_received_;
+ int write_blocked_count_ = 0;
+};
+
+class SimulatedPacketTransportTest : public QuicTest {
+ protected:
+ SimulatedPacketTransportTest()
+ : switch_(&simulator_, "Switch", /*port_count=*/8, 2 * kDefaultBdp),
+ client_(&simulator_,
+ "sender",
+ "receiver",
+ kDefaultQueueLength * kDefaultPacketSize),
+ server_(&simulator_,
+ "receiver",
+ "sender",
+ kDefaultQueueLength * kDefaultPacketSize),
+ client_link_(&client_,
+ switch_.port(1),
+ kDefaultBandwidth,
+ kDefaultPropagationDelay),
+ server_link_(&server_,
+ switch_.port(2),
+ kDefaultBandwidth,
+ kDefaultPropagationDelay),
+ client_delegate_(&client_),
+ server_delegate_(&server_) {}
+
+ Simulator simulator_;
+ Switch switch_;
+
+ SimulatedQuartcPacketTransport client_;
+ SimulatedQuartcPacketTransport server_;
+
+ SymmetricLink client_link_;
+ SymmetricLink server_link_;
+
+ FakeDelegate client_delegate_;
+ FakeDelegate server_delegate_;
+};
+
+TEST_F(SimulatedPacketTransportTest, OneWayTransmission) {
+ std::string packet_1(kDefaultPacketSize, 'a');
+ std::string packet_2(kDefaultPacketSize, 'b');
+ client_delegate_.AddPacketToSend(packet_1);
+ client_delegate_.AddPacketToSend(packet_2);
+
+ simulator_.RunUntil(
+ [this] { return client_delegate_.packets_to_send() == 0; });
+ simulator_.RunFor(3 * kDefaultPropagationDelay);
+
+ EXPECT_THAT(server_delegate_.packets_received(),
+ ElementsAre(packet_1, packet_2));
+ EXPECT_THAT(client_delegate_.packets_received(), ElementsAre());
+}
+
+TEST_F(SimulatedPacketTransportTest, TwoWayTransmission) {
+ std::string packet_1(kDefaultPacketSize, 'a');
+ std::string packet_2(kDefaultPacketSize, 'b');
+ std::string packet_3(kDefaultPacketSize, 'c');
+ std::string packet_4(kDefaultPacketSize, 'd');
+
+ client_delegate_.AddPacketToSend(packet_1);
+ client_delegate_.AddPacketToSend(packet_2);
+ server_delegate_.AddPacketToSend(packet_3);
+ server_delegate_.AddPacketToSend(packet_4);
+
+ simulator_.RunUntil(
+ [this] { return client_delegate_.packets_to_send() == 0; });
+ simulator_.RunUntil(
+ [this] { return server_delegate_.packets_to_send() == 0; });
+ simulator_.RunFor(3 * kDefaultPropagationDelay);
+
+ EXPECT_THAT(server_delegate_.packets_received(),
+ ElementsAre(packet_1, packet_2));
+ EXPECT_THAT(client_delegate_.packets_received(),
+ ElementsAre(packet_3, packet_4));
+}
+
+TEST_F(SimulatedPacketTransportTest, TestWriteBlocked) {
+ // Add 10 packets beyond what fits in the egress queue.
+ std::vector<std::string> packets;
+ for (unsigned int i = 0; i < kDefaultQueueLength + 10; ++i) {
+ packets.push_back(std::string(kDefaultPacketSize, 'a' + i));
+ client_delegate_.AddPacketToSend(packets.back());
+ }
+
+ simulator_.RunUntil(
+ [this] { return client_delegate_.packets_to_send() == 0; });
+ simulator_.RunFor(3 * kDefaultPropagationDelay);
+
+ // Each of the 10 packets in excess of the sender's egress queue length will
+ // block the first time |client_delegate_| tries to write them.
+ EXPECT_EQ(client_delegate_.write_blocked_count(), 10);
+ EXPECT_EQ(server_delegate_.packets_received(), packets);
+}
+
+} // namespace
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/bad_packet_writer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/bad_packet_writer.cc
new file mode 100644
index 00000000000..3d00f8eeb11
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/bad_packet_writer.cc
@@ -0,0 +1,36 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h"
+
+namespace quic {
+namespace test {
+
+BadPacketWriter::BadPacketWriter(size_t packet_causing_write_error,
+ int error_code)
+ : packet_causing_write_error_(packet_causing_write_error),
+ error_code_(error_code) {}
+
+BadPacketWriter::~BadPacketWriter() {}
+
+WriteResult BadPacketWriter::WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ if (error_code_ == 0 || packet_causing_write_error_ > 0) {
+ if (packet_causing_write_error_ > 0) {
+ --packet_causing_write_error_;
+ }
+ return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+ peer_address, options);
+ }
+ // It's time to cause write error.
+ int error_code = error_code_;
+ error_code_ = 0;
+ return WriteResult(WRITE_STATUS_ERROR, error_code);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h b/chromium/net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h
new file mode 100644
index 00000000000..35bf6019486
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/bad_packet_writer.h
@@ -0,0 +1,36 @@
+// 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_TEST_TOOLS_BAD_PACKET_WRITER_H_
+#define QUICHE_QUIC_TEST_TOOLS_BAD_PACKET_WRITER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+
+namespace quic {
+
+namespace test {
+// This packet writer allows causing packet write error with specified error
+// code when writing a particular packet.
+class BadPacketWriter : public QuicPacketWriterWrapper {
+ public:
+ BadPacketWriter(size_t packet_causing_write_error, int error_code);
+
+ ~BadPacketWriter() override;
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ private:
+ size_t packet_causing_write_error_;
+ int error_code_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_BAD_PACKET_WRITER_H_
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
new file mode 100644
index 00000000000..63154b9b54c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.cc
@@ -0,0 +1,1026 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+#include <memory>
+#include <string>
+
+#include "third_party/boringssl/src/include/openssl/bn.h"
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ecdsa.h"
+#include "third_party/boringssl/src/include/openssl/nid.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "net/third_party/quiche/src/quic/core/crypto/channel_id.h"
+#include "net/third_party/quiche/src/quic/core/crypto/common_cert_set.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+
+namespace quic {
+namespace test {
+
+TestChannelIDKey::TestChannelIDKey(EVP_PKEY* ecdsa_key)
+ : ecdsa_key_(ecdsa_key) {}
+TestChannelIDKey::~TestChannelIDKey() {}
+
+bool TestChannelIDKey::Sign(QuicStringPiece signed_data,
+ std::string* out_signature) const {
+ bssl::ScopedEVP_MD_CTX md_ctx;
+ if (EVP_DigestSignInit(md_ctx.get(), nullptr, EVP_sha256(), nullptr,
+ ecdsa_key_.get()) != 1) {
+ return false;
+ }
+
+ EVP_DigestUpdate(md_ctx.get(), ChannelIDVerifier::kContextStr,
+ strlen(ChannelIDVerifier::kContextStr) + 1);
+ EVP_DigestUpdate(md_ctx.get(), ChannelIDVerifier::kClientToServerStr,
+ strlen(ChannelIDVerifier::kClientToServerStr) + 1);
+ EVP_DigestUpdate(md_ctx.get(), signed_data.data(), signed_data.size());
+
+ size_t sig_len;
+ if (!EVP_DigestSignFinal(md_ctx.get(), nullptr, &sig_len)) {
+ return false;
+ }
+
+ std::unique_ptr<uint8_t[]> der_sig(new uint8_t[sig_len]);
+ if (!EVP_DigestSignFinal(md_ctx.get(), der_sig.get(), &sig_len)) {
+ return false;
+ }
+
+ uint8_t* derp = der_sig.get();
+ bssl::UniquePtr<ECDSA_SIG> sig(
+ d2i_ECDSA_SIG(nullptr, const_cast<const uint8_t**>(&derp), sig_len));
+ if (sig.get() == nullptr) {
+ return false;
+ }
+
+ // The signature consists of a pair of 32-byte numbers.
+ static const size_t kSignatureLength = 32 * 2;
+ std::unique_ptr<uint8_t[]> signature(new uint8_t[kSignatureLength]);
+ if (!BN_bn2bin_padded(&signature[0], 32, sig->r) ||
+ !BN_bn2bin_padded(&signature[32], 32, sig->s)) {
+ return false;
+ }
+
+ *out_signature =
+ std::string(reinterpret_cast<char*>(signature.get()), kSignatureLength);
+
+ return true;
+}
+
+std::string TestChannelIDKey::SerializeKey() const {
+ // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
+ // key, is 0x04 (meaning uncompressed) followed by the x and y field
+ // elements as 32-byte, big-endian numbers.
+ static const int kExpectedKeyLength = 65;
+
+ int len = i2d_PublicKey(ecdsa_key_.get(), nullptr);
+ if (len != kExpectedKeyLength) {
+ return "";
+ }
+
+ uint8_t buf[kExpectedKeyLength];
+ uint8_t* derp = buf;
+ i2d_PublicKey(ecdsa_key_.get(), &derp);
+
+ return std::string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1);
+}
+
+TestChannelIDSource::~TestChannelIDSource() {}
+
+QuicAsyncStatus TestChannelIDSource::GetChannelIDKey(
+ const std::string& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) {
+ *channel_id_key = QuicMakeUnique<TestChannelIDKey>(HostnameToKey(hostname));
+ return QUIC_SUCCESS;
+}
+
+// static
+EVP_PKEY* TestChannelIDSource::HostnameToKey(const std::string& hostname) {
+ // In order to generate a deterministic key for a given hostname the
+ // hostname is hashed with SHA-256 and the resulting digest is treated as a
+ // big-endian number. The most-significant bit is cleared to ensure that
+ // the resulting value is less than the order of the group and then it's
+ // taken as a private key. Given the private key, the public key is
+ // calculated with a group multiplication.
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ SHA256_Update(&sha256, hostname.data(), hostname.size());
+
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_Final(digest, &sha256);
+
+ // Ensure that the digest is less than the order of the P-256 group by
+ // clearing the most-significant bit.
+ digest[0] &= 0x7f;
+
+ bssl::UniquePtr<BIGNUM> k(BN_new());
+ CHECK(BN_bin2bn(digest, sizeof(digest), k.get()) != nullptr);
+
+ bssl::UniquePtr<EC_GROUP> p256(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ CHECK(p256);
+
+ bssl::UniquePtr<EC_KEY> ecdsa_key(EC_KEY_new());
+ CHECK(ecdsa_key && EC_KEY_set_group(ecdsa_key.get(), p256.get()));
+
+ bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
+ CHECK(EC_POINT_mul(p256.get(), point.get(), k.get(), nullptr, nullptr,
+ nullptr));
+
+ EC_KEY_set_private_key(ecdsa_key.get(), k.get());
+ EC_KEY_set_public_key(ecdsa_key.get(), point.get());
+
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+ // EVP_PKEY_set1_EC_KEY takes a reference so no |release| here.
+ EVP_PKEY_set1_EC_KEY(pkey.get(), ecdsa_key.get());
+
+ return pkey.release();
+}
+
+namespace crypto_test_utils {
+
+namespace {
+
+// CryptoFramerVisitor is a framer visitor that records handshake messages.
+class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
+ public:
+ CryptoFramerVisitor() : error_(false) {}
+
+ void OnError(CryptoFramer* framer) override { error_ = true; }
+
+ void OnHandshakeMessage(const CryptoHandshakeMessage& message) override {
+ messages_.push_back(message);
+ }
+
+ bool error() const { return error_; }
+
+ const std::vector<CryptoHandshakeMessage>& messages() const {
+ return messages_;
+ }
+
+ private:
+ bool error_;
+ std::vector<CryptoHandshakeMessage> messages_;
+};
+
+// HexChar parses |c| as a hex character. If valid, it sets |*value| to the
+// value of the hex character and returns true. Otherwise it returns false.
+bool HexChar(char c, uint8_t* value) {
+ if (c >= '0' && c <= '9') {
+ *value = c - '0';
+ return true;
+ }
+ if (c >= 'a' && c <= 'f') {
+ *value = c - 'a' + 10;
+ return true;
+ }
+ if (c >= 'A' && c <= 'F') {
+ *value = c - 'A' + 10;
+ return true;
+ }
+ return false;
+}
+
+// A ChannelIDSource that works in asynchronous mode unless the |callback|
+// argument to GetChannelIDKey is nullptr.
+class AsyncTestChannelIDSource : public ChannelIDSource, public CallbackSource {
+ public:
+ // |sync_source| is a synchronous ChannelIDSource.
+ explicit AsyncTestChannelIDSource(
+ std::unique_ptr<ChannelIDSource> sync_source)
+ : sync_source_(std::move(sync_source)) {}
+ ~AsyncTestChannelIDSource() override {}
+
+ // ChannelIDSource implementation.
+ QuicAsyncStatus GetChannelIDKey(const std::string& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) override {
+ // Synchronous mode.
+ if (!callback) {
+ return sync_source_->GetChannelIDKey(hostname, channel_id_key, nullptr);
+ }
+
+ // Asynchronous mode.
+ QuicAsyncStatus status =
+ sync_source_->GetChannelIDKey(hostname, &channel_id_key_, nullptr);
+ if (status != QUIC_SUCCESS) {
+ return QUIC_FAILURE;
+ }
+ callback_.reset(callback);
+ return QUIC_PENDING;
+ }
+
+ // CallbackSource implementation.
+ void RunPendingCallbacks() override {
+ if (callback_) {
+ callback_->Run(&channel_id_key_);
+ callback_.reset();
+ }
+ }
+
+ private:
+ std::unique_ptr<ChannelIDSource> sync_source_;
+ std::unique_ptr<ChannelIDSourceCallback> callback_;
+ std::unique_ptr<ChannelIDKey> channel_id_key_;
+};
+
+} // anonymous namespace
+
+FakeServerOptions::FakeServerOptions() {}
+
+FakeServerOptions::~FakeServerOptions() {}
+
+FakeClientOptions::FakeClientOptions()
+ : channel_id_enabled(false), channel_id_source_async(false) {}
+
+FakeClientOptions::~FakeClientOptions() {}
+
+namespace {
+// This class is used by GenerateFullCHLO() to extract SCID and STK from
+// REJ/SREJ and to construct a full CHLO with these fields and given inchoate
+// CHLO.
+class FullChloGenerator {
+ public:
+ FullChloGenerator(
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ CryptoHandshakeMessage* out)
+ : crypto_config_(crypto_config),
+ server_addr_(server_addr),
+ client_addr_(client_addr),
+ clock_(clock),
+ signed_config_(signed_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ out_(out),
+ params_(new QuicCryptoNegotiatedParameters) {}
+
+ class ValidateClientHelloCallback : public ValidateClientHelloResultCallback {
+ public:
+ explicit ValidateClientHelloCallback(FullChloGenerator* generator)
+ : generator_(generator) {}
+ void Run(QuicReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result> result,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ generator_->ValidateClientHelloDone(std::move(result));
+ }
+
+ private:
+ FullChloGenerator* generator_;
+ };
+
+ std::unique_ptr<ValidateClientHelloCallback>
+ GetValidateClientHelloCallback() {
+ return QuicMakeUnique<ValidateClientHelloCallback>(this);
+ }
+
+ private:
+ void ValidateClientHelloDone(
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result) {
+ result_ = result;
+ crypto_config_->ProcessClientHello(
+ result_, /*reject_only=*/false, TestConnectionId(1), server_addr_,
+ client_addr_, AllSupportedVersions().front(), AllSupportedVersions(),
+ /*use_stateless_rejects=*/true,
+ /*server_designated_connection_id=*/TestConnectionId(2), clock_,
+ QuicRandom::GetInstance(), compressed_certs_cache_, params_,
+ signed_config_, /*total_framing_overhead=*/50, kDefaultMaxPacketSize,
+ GetProcessClientHelloCallback());
+ }
+
+ class ProcessClientHelloCallback : public ProcessClientHelloResultCallback {
+ public:
+ explicit ProcessClientHelloCallback(FullChloGenerator* generator)
+ : generator_(generator) {}
+ void Run(
+ QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) override {
+ generator_->ProcessClientHelloDone(std::move(message));
+ }
+
+ private:
+ FullChloGenerator* generator_;
+ };
+
+ std::unique_ptr<ProcessClientHelloCallback> GetProcessClientHelloCallback() {
+ return QuicMakeUnique<ProcessClientHelloCallback>(this);
+ }
+
+ void ProcessClientHelloDone(std::unique_ptr<CryptoHandshakeMessage> rej) {
+ // Verify output is a REJ or SREJ.
+ EXPECT_THAT(rej->tag(),
+ testing::AnyOf(testing::Eq(kSREJ), testing::Eq(kREJ)));
+
+ VLOG(1) << "Extract valid STK and SCID from\n" << rej->DebugString();
+ QuicStringPiece srct;
+ ASSERT_TRUE(rej->GetStringPiece(kSourceAddressTokenTag, &srct));
+
+ QuicStringPiece scfg;
+ ASSERT_TRUE(rej->GetStringPiece(kSCFG, &scfg));
+ std::unique_ptr<CryptoHandshakeMessage> server_config(
+ CryptoFramer::ParseMessage(scfg));
+
+ QuicStringPiece scid;
+ ASSERT_TRUE(server_config->GetStringPiece(kSCID, &scid));
+
+ *out_ = result_->client_hello;
+ out_->SetStringPiece(kSCID, scid);
+ out_->SetStringPiece(kSourceAddressTokenTag, srct);
+ uint64_t xlct = LeafCertHashForTesting();
+ out_->SetValue(kXLCT, xlct);
+ }
+
+ protected:
+ QuicCryptoServerConfig* crypto_config_;
+ QuicSocketAddress server_addr_;
+ QuicSocketAddress client_addr_;
+ const QuicClock* clock_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+ CryptoHandshakeMessage* out_;
+
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result_;
+};
+
+} // namespace
+
+int HandshakeWithFakeServer(QuicConfig* server_quic_config,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* client_conn,
+ QuicCryptoClientStream* client,
+ const FakeServerOptions& options) {
+ PacketSavingConnection* server_conn = new PacketSavingConnection(
+ helper, alarm_factory, Perspective::IS_SERVER,
+ ParsedVersionOfIndex(client_conn->supported_versions(), 0));
+
+ QuicCryptoServerConfig crypto_config(
+ QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
+ ProofSourceForTesting(), KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+ SetupCryptoServerConfigForTest(server_conn->clock(),
+ server_conn->random_generator(),
+ &crypto_config, options);
+
+ TestQuicSpdyServerSession server_session(
+ server_conn, *server_quic_config, client_conn->supported_versions(),
+ &crypto_config, &compressed_certs_cache);
+ server_session.OnSuccessfulVersionNegotiation(
+ client_conn->supported_versions().front());
+ EXPECT_CALL(*server_session.helper(),
+ CanAcceptClientHello(testing::_, testing::_, testing::_,
+ testing::_, testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_session.helper(),
+ GenerateConnectionIdForReject(testing::_, testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*server_conn, OnCanWrite()).Times(testing::AnyNumber());
+ EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+
+ // The client's handshake must have been started already.
+ CHECK_NE(0u, client_conn->encrypted_packets_.size());
+
+ CommunicateHandshakeMessages(client_conn, client, server_conn,
+ server_session.GetMutableCryptoStream());
+ CompareClientAndServerKeys(client, server_session.GetMutableCryptoStream());
+
+ return client->num_sent_client_hellos();
+}
+
+int HandshakeWithFakeClient(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* server_conn,
+ QuicCryptoServerStream* server,
+ const QuicServerId& server_id,
+ const FakeClientOptions& options) {
+ ParsedQuicVersionVector supported_versions = AllSupportedVersions();
+ if (options.only_tls_versions) {
+ supported_versions.clear();
+ for (QuicTransportVersion transport_version :
+ AllSupportedTransportVersions()) {
+ supported_versions.push_back(
+ ParsedQuicVersion(PROTOCOL_TLS1_3, transport_version));
+ }
+ }
+ PacketSavingConnection* client_conn = new PacketSavingConnection(
+ helper, alarm_factory, Perspective::IS_CLIENT, supported_versions);
+ // Advance the time, because timers do not like uninitialized times.
+ client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+
+ QuicCryptoClientConfig crypto_config(ProofVerifierForTesting(),
+ TlsClientHandshaker::CreateSslCtx());
+ AsyncTestChannelIDSource* async_channel_id_source = nullptr;
+ if (options.channel_id_enabled) {
+ std::unique_ptr<ChannelIDSource> source = ChannelIDSourceForTesting();
+ if (options.channel_id_source_async) {
+ auto temp = QuicMakeUnique<AsyncTestChannelIDSource>(std::move(source));
+ async_channel_id_source = temp.get();
+ source = std::move(temp);
+ }
+ crypto_config.SetChannelIDSource(std::move(source));
+ }
+ if (!options.token_binding_params.empty()) {
+ crypto_config.tb_key_params = options.token_binding_params;
+ }
+ TestQuicSpdyClientSession client_session(client_conn, DefaultQuicConfig(),
+ supported_versions, server_id,
+ &crypto_config);
+
+ EXPECT_CALL(client_session, OnProofValid(testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(client_session, OnProofVerifyDetailsAvailable(testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+ client_session.GetMutableCryptoStream()->CryptoConnect();
+ CHECK_EQ(1u, client_conn->encrypted_packets_.size());
+
+ CommunicateHandshakeMessagesAndRunCallbacks(
+ client_conn, client_session.GetMutableCryptoStream(), server_conn, server,
+ async_channel_id_source);
+
+ if (server->handshake_confirmed() && server->encryption_established()) {
+ CompareClientAndServerKeys(client_session.GetMutableCryptoStream(), server);
+
+ if (options.channel_id_enabled) {
+ std::unique_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ crypto_config.channel_id_source()->GetChannelIDKey(
+ server_id.host(), &channel_id_key, nullptr);
+ EXPECT_EQ(QUIC_SUCCESS, status);
+ EXPECT_EQ(channel_id_key->SerializeKey(),
+ server->crypto_negotiated_params().channel_id);
+ EXPECT_EQ(
+ options.channel_id_source_async,
+ client_session.GetCryptoStream()->WasChannelIDSourceCallbackRun());
+ }
+ }
+
+ return client_session.GetCryptoStream()->num_sent_client_hellos();
+}
+
+void SetupCryptoServerConfigForTest(const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCryptoServerConfig* crypto_config,
+ const FakeServerOptions& fake_options) {
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.channel_id_enabled = true;
+ options.token_binding_params = fake_options.token_binding_params;
+ std::unique_ptr<CryptoHandshakeMessage> scfg =
+ crypto_config->AddDefaultConfig(rand, clock, options);
+}
+
+void SendHandshakeMessageToStream(QuicCryptoStream* stream,
+ const CryptoHandshakeMessage& message,
+ Perspective perspective) {
+ const QuicData& data = message.GetSerialized();
+ QuicSession* session = QuicStreamPeer::session(stream);
+ if (!QuicVersionUsesCryptoFrames(
+ session->connection()->transport_version())) {
+ QuicStreamFrame frame(QuicUtils::GetCryptoStreamId(
+ session->connection()->transport_version()),
+ false, stream->crypto_bytes_read(),
+ data.AsStringPiece());
+ stream->OnStreamFrame(frame);
+ } else {
+ EncryptionLevel level = session->connection()->last_decrypted_level();
+ QuicCryptoFrame frame(level, stream->BytesReadOnLevel(level),
+ data.AsStringPiece());
+ stream->OnCryptoFrame(frame);
+ }
+}
+
+void CommunicateHandshakeMessages(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server) {
+ CommunicateHandshakeMessagesAndRunCallbacks(client_conn, client, server_conn,
+ server, nullptr);
+}
+
+void CommunicateHandshakeMessagesAndRunCallbacks(
+ PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ CallbackSource* callback_source) {
+ size_t client_i = 0, server_i = 0;
+ while (!client->handshake_confirmed() || !server->handshake_confirmed()) {
+ ASSERT_GT(client_conn->encrypted_packets_.size(), client_i);
+ QUIC_LOG(INFO) << "Processing "
+ << client_conn->encrypted_packets_.size() - client_i
+ << " packets client->server";
+ MovePackets(client_conn, &client_i, server, server_conn,
+ Perspective::IS_SERVER);
+ if (callback_source) {
+ callback_source->RunPendingCallbacks();
+ }
+
+ if (client->handshake_confirmed() && server->handshake_confirmed()) {
+ break;
+ }
+ ASSERT_GT(server_conn->encrypted_packets_.size(), server_i);
+ QUIC_LOG(INFO) << "Processing "
+ << server_conn->encrypted_packets_.size() - server_i
+ << " packets server->client";
+ MovePackets(server_conn, &server_i, client, client_conn,
+ Perspective::IS_CLIENT);
+ if (callback_source) {
+ callback_source->RunPendingCallbacks();
+ }
+ }
+}
+
+std::pair<size_t, size_t> AdvanceHandshake(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ size_t client_i,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ size_t server_i) {
+ QUIC_LOG(INFO) << "Processing "
+ << client_conn->encrypted_packets_.size() - client_i
+ << " packets client->server";
+ MovePackets(client_conn, &client_i, server, server_conn,
+ Perspective::IS_SERVER);
+
+ QUIC_LOG(INFO) << "Processing "
+ << server_conn->encrypted_packets_.size() - server_i
+ << " packets server->client";
+ if (server_conn->encrypted_packets_.size() - server_i == 2) {
+ QUIC_LOG(INFO) << "here";
+ }
+ MovePackets(server_conn, &server_i, client, client_conn,
+ Perspective::IS_CLIENT);
+
+ return std::make_pair(client_i, server_i);
+}
+
+std::string GetValueForTag(const CryptoHandshakeMessage& message, QuicTag tag) {
+ auto it = message.tag_value_map().find(tag);
+ if (it == message.tag_value_map().end()) {
+ return std::string();
+ }
+ return it->second;
+}
+
+uint64_t LeafCertHashForTesting() {
+ QuicReferenceCountedPointer<ProofSource::Chain> chain;
+ QuicSocketAddress server_address(QuicIpAddress::Any4(), 42);
+ QuicCryptoProof proof;
+ std::unique_ptr<ProofSource> proof_source(ProofSourceForTesting());
+
+ class Callback : public ProofSource::Callback {
+ public:
+ Callback(bool* ok, QuicReferenceCountedPointer<ProofSource::Chain>* chain)
+ : ok_(ok), chain_(chain) {}
+
+ void Run(bool ok,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const QuicCryptoProof& /* proof */,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ *ok_ = ok;
+ *chain_ = chain;
+ }
+
+ private:
+ bool* ok_;
+ QuicReferenceCountedPointer<ProofSource::Chain>* chain_;
+ };
+
+ // Note: relies on the callback being invoked synchronously
+ bool ok = false;
+ proof_source->GetProof(
+ server_address, "", "", AllSupportedTransportVersions().front(), "",
+ std::unique_ptr<ProofSource::Callback>(new Callback(&ok, &chain)));
+ if (!ok || chain->certs.empty()) {
+ DCHECK(false) << "Proof generation failed";
+ return 0;
+ }
+
+ return QuicUtils::FNV1a_64_Hash(chain->certs.at(0));
+}
+
+class MockCommonCertSets : public CommonCertSets {
+ public:
+ MockCommonCertSets(QuicStringPiece cert, uint64_t hash, uint32_t index)
+ : cert_(cert), hash_(hash), index_(index) {}
+
+ QuicStringPiece GetCommonHashes() const override {
+ QUIC_BUG << "not implemented";
+ return QuicStringPiece();
+ }
+
+ QuicStringPiece GetCert(uint64_t hash, uint32_t index) const override {
+ if (hash == hash_ && index == index_) {
+ return cert_;
+ }
+ return QuicStringPiece();
+ }
+
+ bool MatchCert(QuicStringPiece cert,
+ QuicStringPiece common_set_hashes,
+ uint64_t* out_hash,
+ uint32_t* out_index) const override {
+ if (cert != cert_) {
+ return false;
+ }
+
+ if (common_set_hashes.size() % sizeof(uint64_t) != 0) {
+ return false;
+ }
+ bool client_has_set = false;
+ for (size_t i = 0; i < common_set_hashes.size(); i += sizeof(uint64_t)) {
+ uint64_t hash;
+ memcpy(&hash, common_set_hashes.data() + i, sizeof(hash));
+ if (hash == hash_) {
+ client_has_set = true;
+ break;
+ }
+ }
+
+ if (!client_has_set) {
+ return false;
+ }
+
+ *out_hash = hash_;
+ *out_index = index_;
+ return true;
+ }
+
+ private:
+ const std::string cert_;
+ const uint64_t hash_;
+ const uint32_t index_;
+};
+
+CommonCertSets* MockCommonCertSets(QuicStringPiece cert,
+ uint64_t hash,
+ uint32_t index) {
+ return new class MockCommonCertSets(cert, hash, index);
+}
+
+void FillInDummyReject(CryptoHandshakeMessage* rej, bool reject_is_stateless) {
+ if (reject_is_stateless) {
+ rej->set_tag(kSREJ);
+ } else {
+ rej->set_tag(kREJ);
+ }
+
+ // Minimum SCFG that passes config validation checks.
+ // clang-format off
+ unsigned char scfg[] = {
+ // SCFG
+ 0x53, 0x43, 0x46, 0x47,
+ // num entries
+ 0x01, 0x00,
+ // padding
+ 0x00, 0x00,
+ // EXPY
+ 0x45, 0x58, 0x50, 0x59,
+ // EXPY end offset
+ 0x08, 0x00, 0x00, 0x00,
+ // Value
+ '1', '2', '3', '4',
+ '5', '6', '7', '8'
+ };
+ // clang-format on
+ rej->SetValue(kSCFG, scfg);
+ rej->SetStringPiece(kServerNonceTag, "SERVER_NONCE");
+ int64_t ttl = 2 * 24 * 60 * 60;
+ rej->SetValue(kSTTL, ttl);
+ std::vector<QuicTag> reject_reasons;
+ reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE);
+ rej->SetVector(kRREJ, reject_reasons);
+}
+
+namespace {
+
+#define RETURN_STRING_LITERAL(x) \
+ case x: \
+ return #x
+
+std::string EncryptionLevelString(EncryptionLevel level) {
+ switch (level) {
+ RETURN_STRING_LITERAL(ENCRYPTION_INITIAL);
+ RETURN_STRING_LITERAL(ENCRYPTION_HANDSHAKE);
+ RETURN_STRING_LITERAL(ENCRYPTION_ZERO_RTT);
+ RETURN_STRING_LITERAL(ENCRYPTION_FORWARD_SECURE);
+ default:
+ return "";
+ }
+}
+
+void CompareCrypters(const QuicEncrypter* encrypter,
+ const QuicDecrypter* decrypter,
+ std::string label) {
+ if (encrypter == nullptr || decrypter == nullptr) {
+ ADD_FAILURE() << "Expected non-null crypters; have " << encrypter << " and "
+ << decrypter;
+ return;
+ }
+ QuicStringPiece encrypter_key = encrypter->GetKey();
+ QuicStringPiece encrypter_iv = encrypter->GetNoncePrefix();
+ QuicStringPiece decrypter_key = decrypter->GetKey();
+ QuicStringPiece decrypter_iv = decrypter->GetNoncePrefix();
+ CompareCharArraysWithHexError(label + " key", encrypter_key.data(),
+ encrypter_key.length(), decrypter_key.data(),
+ decrypter_key.length());
+ CompareCharArraysWithHexError(label + " iv", encrypter_iv.data(),
+ encrypter_iv.length(), decrypter_iv.data(),
+ decrypter_iv.length());
+}
+
+} // namespace
+
+void CompareClientAndServerKeys(QuicCryptoClientStream* client,
+ QuicCryptoServerStream* server) {
+ QuicFramer* client_framer = QuicConnectionPeer::GetFramer(
+ QuicStreamPeer::session(client)->connection());
+ QuicFramer* server_framer = QuicConnectionPeer::GetFramer(
+ QuicStreamPeer::session(server)->connection());
+ for (EncryptionLevel level :
+ {ENCRYPTION_HANDSHAKE, ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+ SCOPED_TRACE(EncryptionLevelString(level));
+ const QuicEncrypter* client_encrypter(
+ QuicFramerPeer::GetEncrypter(client_framer, level));
+ const QuicDecrypter* server_decrypter(
+ QuicFramerPeer::GetDecrypter(server_framer, level));
+ if (level == ENCRYPTION_FORWARD_SECURE ||
+ !(client_encrypter == nullptr && server_decrypter == nullptr)) {
+ CompareCrypters(client_encrypter, server_decrypter,
+ "client " + EncryptionLevelString(level) + " write");
+ }
+ const QuicEncrypter* server_encrypter(
+ QuicFramerPeer::GetEncrypter(server_framer, level));
+ const QuicDecrypter* client_decrypter(
+ QuicFramerPeer::GetDecrypter(client_framer, level));
+ if (level == ENCRYPTION_FORWARD_SECURE ||
+ !(server_encrypter == nullptr && client_decrypter == nullptr)) {
+ CompareCrypters(server_encrypter, client_decrypter,
+ "server " + EncryptionLevelString(level) + " write");
+ }
+ }
+
+ QuicStringPiece client_subkey_secret =
+ client->crypto_negotiated_params().subkey_secret;
+ QuicStringPiece server_subkey_secret =
+ server->crypto_negotiated_params().subkey_secret;
+ CompareCharArraysWithHexError("subkey secret", client_subkey_secret.data(),
+ client_subkey_secret.length(),
+ server_subkey_secret.data(),
+ server_subkey_secret.length());
+
+ const char kSampleLabel[] = "label";
+ const char kSampleContext[] = "context";
+ const size_t kSampleOutputLength = 32;
+ std::string client_key_extraction;
+ std::string server_key_extraction;
+ EXPECT_TRUE(client->ExportKeyingMaterial(kSampleLabel, kSampleContext,
+ kSampleOutputLength,
+ &client_key_extraction));
+ EXPECT_TRUE(server->ExportKeyingMaterial(kSampleLabel, kSampleContext,
+ kSampleOutputLength,
+ &server_key_extraction));
+ CompareCharArraysWithHexError(
+ "sample key extraction", client_key_extraction.data(),
+ client_key_extraction.length(), server_key_extraction.data(),
+ server_key_extraction.length());
+}
+
+QuicTag ParseTag(const char* tagstr) {
+ const size_t len = strlen(tagstr);
+ CHECK_NE(0u, len);
+
+ QuicTag tag = 0;
+
+ if (tagstr[0] == '#') {
+ CHECK_EQ(static_cast<size_t>(1 + 2 * 4), len);
+ tagstr++;
+
+ for (size_t i = 0; i < 8; i++) {
+ tag <<= 4;
+
+ uint8_t v = 0;
+ CHECK(HexChar(tagstr[i], &v));
+ tag |= v;
+ }
+
+ return tag;
+ }
+
+ CHECK_LE(len, 4u);
+ for (size_t i = 0; i < 4; i++) {
+ tag >>= 8;
+ if (i < len) {
+ tag |= static_cast<uint32_t>(tagstr[i]) << 24;
+ }
+ }
+
+ return tag;
+}
+
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<std::string, std::string>> tags_and_values) {
+ return CreateCHLO(tags_and_values, -1);
+}
+
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<std::string, std::string>> tags_and_values,
+ int minimum_size_bytes) {
+ CryptoHandshakeMessage msg;
+ msg.set_tag(MakeQuicTag('C', 'H', 'L', 'O'));
+
+ if (minimum_size_bytes > 0) {
+ msg.set_minimum_size(minimum_size_bytes);
+ }
+
+ for (const auto& tag_and_value : tags_and_values) {
+ const std::string& tag = tag_and_value.first;
+ const std::string& value = tag_and_value.second;
+
+ const QuicTag quic_tag = ParseTag(tag.c_str());
+
+ size_t value_len = value.length();
+ if (value_len > 0 && value[0] == '#') {
+ // This is ascii encoded hex.
+ std::string hex_value =
+ QuicTextUtils::HexDecode(QuicStringPiece(&value[1]));
+ msg.SetStringPiece(quic_tag, hex_value);
+ continue;
+ }
+ msg.SetStringPiece(quic_tag, value);
+ }
+
+ // The CryptoHandshakeMessage needs to be serialized and parsed to ensure
+ // that any padding is included.
+ std::unique_ptr<QuicData> bytes =
+ CryptoFramer::ConstructHandshakeMessage(msg);
+ std::unique_ptr<CryptoHandshakeMessage> parsed(
+ CryptoFramer::ParseMessage(bytes->AsStringPiece()));
+ CHECK(parsed);
+
+ return *parsed;
+}
+
+std::unique_ptr<ChannelIDSource> ChannelIDSourceForTesting() {
+ return QuicMakeUnique<TestChannelIDSource>();
+}
+
+void MovePackets(PacketSavingConnection* source_conn,
+ size_t* inout_packet_index,
+ QuicCryptoStream* dest_stream,
+ PacketSavingConnection* dest_conn,
+ Perspective dest_perspective) {
+ SimpleQuicFramer framer(source_conn->supported_versions(), dest_perspective);
+
+ SimpleQuicFramer null_encryption_framer(source_conn->supported_versions(),
+ dest_perspective);
+
+ size_t index = *inout_packet_index;
+ for (; index < source_conn->encrypted_packets_.size(); index++) {
+ // 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
+ // them into |framer|, perform the decryption with them, and then swap ther
+ // back.
+ QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+ if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) {
+ // The framer will be unable to decrypt forward-secure packets sent after
+ // the handshake is complete. Don't treat them as handshake packets.
+ break;
+ }
+ QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+ dest_conn->OnDecryptedPacket(framer.last_decrypted_level());
+
+ if (dest_stream->handshake_protocol() == PROTOCOL_TLS1_3) {
+ // Try to process the packet with a framer that only has the NullDecrypter
+ // for decryption. If ProcessPacket succeeds, that means the packet was
+ // encrypted with the NullEncrypter. With the TLS handshaker in use, no
+ // packets should ever be encrypted with the NullEncrypter, instead
+ // they're encrypted with an obfuscation cipher based on QUIC version and
+ // connection ID.
+ ASSERT_FALSE(null_encryption_framer.ProcessPacket(
+ *source_conn->encrypted_packets_[index]))
+ << "No TLS packets should be encrypted with the NullEncrypter";
+ }
+
+ // Since we're using QuicFramers separate from the connections to move
+ // packets, the QuicConnection never gets notified about what level the last
+ // packet was decrypted at. This is needed by TLS to know what encryption
+ // level was used for the data it's receiving, so we plumb this information
+ // from the SimpleQuicFramer back into the connection.
+ dest_conn->OnDecryptedPacket(framer.last_decrypted_level());
+
+ QuicConnectionPeer::SetCurrentPacket(
+ dest_conn, source_conn->encrypted_packets_[index]->AsStringPiece());
+ for (const auto& stream_frame : framer.stream_frames()) {
+ dest_stream->OnStreamFrame(*stream_frame);
+ }
+ for (const auto& crypto_frame : framer.crypto_frames()) {
+ dest_stream->OnCryptoFrame(*crypto_frame);
+ }
+ }
+ *inout_packet_index = index;
+
+ QuicConnectionPeer::SetCurrentPacket(dest_conn, QuicStringPiece(nullptr, 0));
+}
+
+CryptoHandshakeMessage GenerateDefaultInchoateCHLO(
+ const QuicClock* clock,
+ QuicTransportVersion version,
+ QuicCryptoServerConfig* crypto_config) {
+ // clang-format off
+ return CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"PUBS", GenerateClientPublicValuesHex().c_str()},
+ {"NONC", GenerateClientNonceHex(clock, crypto_config).c_str()},
+ {"VER\0", QuicVersionLabelToString(
+ QuicVersionToQuicVersionLabel(version)).c_str()}},
+ kClientHelloMinimumSize);
+ // clang-format on
+}
+
+std::string GenerateClientNonceHex(const QuicClock* clock,
+ QuicCryptoServerConfig* crypto_config) {
+ QuicCryptoServerConfig::ConfigOptions old_config_options;
+ QuicCryptoServerConfig::ConfigOptions new_config_options;
+ old_config_options.id = "old-config-id";
+ crypto_config->AddDefaultConfig(QuicRandom::GetInstance(), clock,
+ old_config_options);
+ QuicServerConfigProtobuf primary_config = crypto_config->GenerateConfig(
+ QuicRandom::GetInstance(), clock, new_config_options);
+ primary_config.set_primary_time(clock->WallNow().ToUNIXSeconds());
+ std::unique_ptr<CryptoHandshakeMessage> msg =
+ crypto_config->AddConfig(std::move(primary_config), clock->WallNow());
+ QuicStringPiece orbit;
+ CHECK(msg->GetStringPiece(kORBT, &orbit));
+ std::string nonce;
+ CryptoUtils::GenerateNonce(clock->WallNow(), QuicRandom::GetInstance(), orbit,
+ &nonce);
+ return ("#" + QuicTextUtils::HexEncode(nonce));
+}
+
+std::string GenerateClientPublicValuesHex() {
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+ return ("#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value)));
+}
+
+void GenerateFullCHLO(const CryptoHandshakeMessage& inchoate_chlo,
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ QuicTransportVersion version,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> proof,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ CryptoHandshakeMessage* out) {
+ // Pass a inchoate CHLO.
+ FullChloGenerator generator(crypto_config, server_addr, client_addr, clock,
+ proof, compressed_certs_cache, out);
+ crypto_config->ValidateClientHello(
+ inchoate_chlo, client_addr.host(), server_addr, version, clock, proof,
+ generator.GetValidateClientHelloCallback());
+}
+
+} // namespace crypto_test_utils
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h
new file mode 100644
index 00000000000..5bb4b4e42a3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h
@@ -0,0 +1,276 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
+#define QUICHE_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
+
+#include <cstdarg>
+#include <cstddef>
+#include <cstdint>
+#include <utility>
+#include <vector>
+
+#include "third_party/boringssl/src/include/openssl/evp.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+class ChannelIDSource;
+class CommonCertSets;
+class ProofSource;
+class ProofVerifier;
+class ProofVerifyContext;
+class QuicClock;
+class QuicConfig;
+class QuicCryptoClientStream;
+class QuicCryptoServerConfig;
+class QuicCryptoServerStream;
+class QuicCryptoStream;
+class QuicRandom;
+class QuicServerId;
+
+namespace test {
+
+class PacketSavingConnection;
+
+class TestChannelIDKey : public ChannelIDKey {
+ public:
+ explicit TestChannelIDKey(EVP_PKEY* ecdsa_key);
+ ~TestChannelIDKey() override;
+
+ // ChannelIDKey implementation.
+
+ bool Sign(QuicStringPiece signed_data,
+ std::string* out_signature) const override;
+
+ std::string SerializeKey() const override;
+
+ private:
+ bssl::UniquePtr<EVP_PKEY> ecdsa_key_;
+};
+
+class TestChannelIDSource : public ChannelIDSource {
+ public:
+ ~TestChannelIDSource() override;
+
+ // ChannelIDSource implementation.
+
+ QuicAsyncStatus GetChannelIDKey(
+ const std::string& hostname,
+ std::unique_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) override;
+
+ private:
+ static EVP_PKEY* HostnameToKey(const std::string& hostname);
+};
+
+namespace crypto_test_utils {
+
+// An interface for a source of callbacks. This is used for invoking
+// callbacks asynchronously.
+//
+// Call the RunPendingCallbacks method regularly to run the callbacks from
+// this source.
+class CallbackSource {
+ public:
+ virtual ~CallbackSource() {}
+
+ // Runs pending callbacks from this source. If there is no pending
+ // callback, does nothing.
+ virtual void RunPendingCallbacks() = 0;
+};
+
+// FakeServerOptions bundles together a number of options for configuring the
+// server in HandshakeWithFakeServer.
+struct FakeServerOptions {
+ FakeServerOptions();
+ ~FakeServerOptions();
+
+ // The Token Binding params that the server supports and will negotiate.
+ QuicTagVector token_binding_params;
+};
+
+// FakeClientOptions bundles together a number of options for configuring
+// HandshakeWithFakeClient.
+struct FakeClientOptions {
+ FakeClientOptions();
+ ~FakeClientOptions();
+
+ // If channel_id_enabled is true then the client will attempt to send a
+ // ChannelID.
+ bool channel_id_enabled;
+
+ // If channel_id_source_async is true then the client will use an async
+ // ChannelIDSource for testing. Ignored if channel_id_enabled is false.
+ bool channel_id_source_async;
+
+ // The Token Binding params that the client supports and will negotiate.
+ QuicTagVector token_binding_params;
+
+ // If only_tls_versions is set, then the client will only use TLS for the
+ // crypto handshake.
+ bool only_tls_versions = false;
+};
+
+// returns: the number of client hellos that the client sent.
+int HandshakeWithFakeServer(QuicConfig* server_quic_config,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* client_conn,
+ QuicCryptoClientStream* client,
+ const FakeServerOptions& options);
+
+// returns: the number of client hellos that the client sent.
+int HandshakeWithFakeClient(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ PacketSavingConnection* server_conn,
+ QuicCryptoServerStream* server,
+ const QuicServerId& server_id,
+ const FakeClientOptions& options);
+
+// SetupCryptoServerConfigForTest configures |crypto_config|
+// with sensible defaults for testing.
+void SetupCryptoServerConfigForTest(const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCryptoServerConfig* crypto_config,
+ const FakeServerOptions& options);
+
+// Sends the handshake message |message| to stream |stream| with the perspective
+// that the message is coming from |perspective|.
+void SendHandshakeMessageToStream(QuicCryptoStream* stream,
+ const CryptoHandshakeMessage& message,
+ Perspective perspective);
+
+// CommunicateHandshakeMessages moves messages from |client| to |server| and
+// back until |clients|'s handshake has completed.
+void CommunicateHandshakeMessages(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server);
+
+// CommunicateHandshakeMessagesAndRunCallbacks moves messages from |client|
+// to |server| and back until |client|'s handshake has completed. If
+// |callback_source| is not nullptr,
+// CommunicateHandshakeMessagesAndRunCallbacks also runs callbacks from
+// |callback_source| between processing messages.
+void CommunicateHandshakeMessagesAndRunCallbacks(
+ PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ CallbackSource* callback_source);
+
+// AdvanceHandshake attempts to moves messages from |client| to |server| and
+// |server| to |client|. Returns the number of messages moved.
+std::pair<size_t, size_t> AdvanceHandshake(PacketSavingConnection* client_conn,
+ QuicCryptoStream* client,
+ size_t client_i,
+ PacketSavingConnection* server_conn,
+ QuicCryptoStream* server,
+ size_t server_i);
+
+// Returns the value for the tag |tag| in the tag value map of |message|.
+std::string GetValueForTag(const CryptoHandshakeMessage& message, QuicTag tag);
+
+// Returns a new |ProofSource| that serves up test certificates.
+std::unique_ptr<ProofSource> ProofSourceForTesting();
+
+// Returns a new |ProofVerifier| that uses the QUIC testing root CA.
+std::unique_ptr<ProofVerifier> ProofVerifierForTesting();
+
+// Returns a hash of the leaf test certificate.
+uint64_t LeafCertHashForTesting();
+
+// Returns a |ProofVerifyContext| that must be used with the verifier
+// returned by |ProofVerifierForTesting|.
+std::unique_ptr<ProofVerifyContext> ProofVerifyContextForTesting();
+
+// MockCommonCertSets returns a CommonCertSets that contains a single set with
+// hash |hash|, consisting of the certificate |cert| at index |index|.
+CommonCertSets* MockCommonCertSets(QuicStringPiece cert,
+ uint64_t hash,
+ uint32_t index);
+
+// Creates a minimal dummy reject message that will pass the client-config
+// validation tests. This will include a server config, but no certs, proof
+// source address token, or server nonce.
+void FillInDummyReject(CryptoHandshakeMessage* rej, bool reject_is_stateless);
+
+// ParseTag returns a QuicTag from parsing |tagstr|. |tagstr| may either be
+// in the format "EXMP" (i.e. ASCII format), or "#11223344" (an explicit hex
+// format). It CHECK fails if there's a parse error.
+QuicTag ParseTag(const char* tagstr);
+
+// Message constructs a CHLO message from a provided vector of tag/value pairs.
+// The first of each pair is the tag of a tag/value and is given as an argument
+// to |ParseTag|. The second is the value of the tag/value pair and is either a
+// hex dump, preceeded by a '#', or a raw value. If minimum_size_bytes is
+// provided then the message will be padded to this minimum size.
+//
+// CreateCHLO(
+// {{"NOCE", "#11223344"},
+// {"SNI", "www.example.com"}},
+// optional_minimum_size_bytes);
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<std::string, std::string>> tags_and_values);
+CryptoHandshakeMessage CreateCHLO(
+ std::vector<std::pair<std::string, std::string>> tags_and_values,
+ int minimum_size_bytes);
+
+// ChannelIDSourceForTesting returns a ChannelIDSource that generates keys
+// deterministically based on the hostname given in the GetChannelIDKey call.
+// This ChannelIDSource works in synchronous mode, i.e., its GetChannelIDKey
+// method never returns QUIC_PENDING.
+std::unique_ptr<ChannelIDSource> ChannelIDSourceForTesting();
+
+// MovePackets parses crypto handshake messages from packet number
+// |*inout_packet_index| through to the last packet (or until a packet fails
+// to decrypt) and has |dest_stream| process them. |*inout_packet_index| is
+// updated with an index one greater than the last packet processed.
+void MovePackets(PacketSavingConnection* source_conn,
+ size_t* inout_packet_index,
+ QuicCryptoStream* dest_stream,
+ PacketSavingConnection* dest_conn,
+ Perspective dest_perspective);
+
+// Return an inchoate CHLO with some basic tag value pairs.
+CryptoHandshakeMessage GenerateDefaultInchoateCHLO(
+ const QuicClock* clock,
+ QuicTransportVersion version,
+ QuicCryptoServerConfig* crypto_config);
+
+// Takes a inchoate CHLO, returns a full CHLO in |out| which can pass
+// |crypto_config|'s validation.
+void GenerateFullCHLO(
+ const CryptoHandshakeMessage& inchoate_chlo,
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ QuicTransportVersion version,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ CryptoHandshakeMessage* out);
+
+void CompareClientAndServerKeys(QuicCryptoClientStream* client,
+ QuicCryptoServerStream* server);
+
+// Return a CHLO nonce in hexadecimal.
+std::string GenerateClientNonceHex(const QuicClock* clock,
+ QuicCryptoServerConfig* crypto_config);
+
+// Return a CHLO PUBS in hexadecimal.
+std::string GenerateClientPublicValuesHex();
+
+} // namespace crypto_test_utils
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils_test.cc b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils_test.cc
new file mode 100644
index 00000000000..a62cb969a79
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/crypto_test_utils_test.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+#include "net/third_party/quiche/src/quic/core/proto/crypto_server_config.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+namespace test {
+
+class ShloVerifier {
+ public:
+ ShloVerifier(
+ QuicCryptoServerConfig* crypto_config,
+ QuicSocketAddress server_addr,
+ QuicSocketAddress client_addr,
+ const QuicClock* clock,
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config,
+ QuicCompressedCertsCache* compressed_certs_cache)
+ : crypto_config_(crypto_config),
+ server_addr_(server_addr),
+ client_addr_(client_addr),
+ clock_(clock),
+ signed_config_(signed_config),
+ compressed_certs_cache_(compressed_certs_cache),
+ params_(new QuicCryptoNegotiatedParameters) {}
+
+ class ValidateClientHelloCallback : public ValidateClientHelloResultCallback {
+ public:
+ explicit ValidateClientHelloCallback(ShloVerifier* shlo_verifier)
+ : shlo_verifier_(shlo_verifier) {}
+ void Run(QuicReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result> result,
+ std::unique_ptr<ProofSource::Details> /* details */) override {
+ shlo_verifier_->ValidateClientHelloDone(result);
+ }
+
+ private:
+ ShloVerifier* shlo_verifier_;
+ };
+
+ std::unique_ptr<ValidateClientHelloCallback>
+ GetValidateClientHelloCallback() {
+ return QuicMakeUnique<ValidateClientHelloCallback>(this);
+ }
+
+ private:
+ void ValidateClientHelloDone(
+ const QuicReferenceCountedPointer<
+ ValidateClientHelloResultCallback::Result>& result) {
+ result_ = result;
+ crypto_config_->ProcessClientHello(
+ result_, /*reject_only=*/false,
+ /*connection_id=*/TestConnectionId(1), server_addr_, client_addr_,
+ AllSupportedVersions().front(), AllSupportedVersions(),
+ /*use_stateless_rejects=*/true,
+ /*server_designated_connection_id=*/TestConnectionId(2), clock_,
+ QuicRandom::GetInstance(), compressed_certs_cache_, params_,
+ signed_config_, /*total_framing_overhead=*/50, kDefaultMaxPacketSize,
+ GetProcessClientHelloCallback());
+ }
+
+ class ProcessClientHelloCallback : public ProcessClientHelloResultCallback {
+ public:
+ explicit ProcessClientHelloCallback(ShloVerifier* shlo_verifier)
+ : shlo_verifier_(shlo_verifier) {}
+ void Run(
+ QuicErrorCode error,
+ const std::string& error_details,
+ std::unique_ptr<CryptoHandshakeMessage> message,
+ std::unique_ptr<DiversificationNonce> diversification_nonce,
+ std::unique_ptr<ProofSource::Details> proof_source_details) override {
+ shlo_verifier_->ProcessClientHelloDone(std::move(message));
+ }
+
+ private:
+ ShloVerifier* shlo_verifier_;
+ };
+
+ std::unique_ptr<ProcessClientHelloCallback> GetProcessClientHelloCallback() {
+ return QuicMakeUnique<ProcessClientHelloCallback>(this);
+ }
+
+ void ProcessClientHelloDone(std::unique_ptr<CryptoHandshakeMessage> message) {
+ // Verify output is a SHLO.
+ EXPECT_EQ(message->tag(), kSHLO)
+ << "Fail to pass validation. Get " << message->DebugString();
+ }
+
+ QuicCryptoServerConfig* crypto_config_;
+ QuicSocketAddress server_addr_;
+ QuicSocketAddress client_addr_;
+ const QuicClock* clock_;
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config_;
+ QuicCompressedCertsCache* compressed_certs_cache_;
+
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ QuicReferenceCountedPointer<ValidateClientHelloResultCallback::Result>
+ result_;
+};
+
+class CryptoTestUtilsTest : public QuicTest {};
+
+TEST_F(CryptoTestUtilsTest, TestGenerateFullCHLO) {
+ MockClock clock;
+ QuicCryptoServerConfig crypto_config(
+ QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(), KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx());
+ QuicSocketAddress server_addr(QuicIpAddress::Any4(), 5);
+ QuicSocketAddress client_addr(QuicIpAddress::Loopback4(), 1);
+ QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config(
+ new QuicSignedServerConfig);
+ QuicCompressedCertsCache compressed_certs_cache(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize);
+ CryptoHandshakeMessage full_chlo;
+
+ QuicCryptoServerConfig::ConfigOptions old_config_options;
+ old_config_options.id = "old-config-id";
+ crypto_config.AddDefaultConfig(QuicRandom::GetInstance(), &clock,
+ old_config_options);
+ QuicCryptoServerConfig::ConfigOptions new_config_options;
+ QuicServerConfigProtobuf primary_config = crypto_config.GenerateConfig(
+ QuicRandom::GetInstance(), &clock, new_config_options);
+ primary_config.set_primary_time(clock.WallNow().ToUNIXSeconds());
+ std::unique_ptr<CryptoHandshakeMessage> msg =
+ crypto_config.AddConfig(std::move(primary_config), clock.WallNow());
+ QuicStringPiece orbit;
+ ASSERT_TRUE(msg->GetStringPiece(kORBT, &orbit));
+ std::string nonce;
+ CryptoUtils::GenerateNonce(clock.WallNow(), QuicRandom::GetInstance(), orbit,
+ &nonce);
+ std::string nonce_hex = "#" + QuicTextUtils::HexEncode(nonce);
+
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+ std::string pub_hex =
+ "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
+
+ QuicTransportVersion version(AllSupportedTransportVersions().front());
+ CryptoHandshakeMessage inchoate_chlo = crypto_test_utils::CreateCHLO(
+ {{"PDMD", "X509"},
+ {"AEAD", "AESG"},
+ {"KEXS", "C255"},
+ {"COPT", "SREJ"},
+ {"PUBS", pub_hex},
+ {"NONC", nonce_hex},
+ {"VER\0",
+ QuicVersionLabelToString(QuicVersionToQuicVersionLabel(version))}},
+ kClientHelloMinimumSize);
+
+ crypto_test_utils::GenerateFullCHLO(
+ inchoate_chlo, &crypto_config, server_addr, client_addr, version, &clock,
+ signed_config, &compressed_certs_cache, &full_chlo);
+ // Verify that full_chlo can pass crypto_config's verification.
+ ShloVerifier shlo_verifier(&crypto_config, server_addr, client_addr, &clock,
+ signed_config, &compressed_certs_cache);
+ crypto_config.ValidateClientHello(
+ full_chlo, client_addr.host(), server_addr, version, &clock,
+ signed_config, shlo_verifier.GetValidateClientHelloCallback());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.cc b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.cc
new file mode 100644
index 00000000000..339b793eae8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.cc
@@ -0,0 +1,35 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/test_tools/failing_proof_source.h"
+
+namespace quic {
+namespace test {
+
+void FailingProofSource::GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) {
+ callback->Run(false, nullptr, QuicCryptoProof(), nullptr);
+}
+
+QuicReferenceCountedPointer<ProofSource::Chain>
+FailingProofSource::GetCertChain(const QuicSocketAddress& server_address,
+ const std::string& hostname) {
+ return QuicReferenceCountedPointer<Chain>();
+}
+
+void FailingProofSource::ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) {
+ callback->Run(false, "");
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..10f9971c4ff
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/failing_proof_source.h
@@ -0,0 +1,38 @@
+// 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_TEST_TOOLS_FAILING_PROOF_SOURCE_H_
+#define QUICHE_QUIC_TEST_TOOLS_FAILING_PROOF_SOURCE_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+
+class FailingProofSource : public ProofSource {
+ public:
+ void GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<Callback> callback) override;
+
+ QuicReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname) override;
+
+ void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<SignatureCallback> callback) override;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_FAILING_PROOF_SOURCE_H_
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
new file mode 100644
index 00000000000..8f1cd376833
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/fake_proof_source.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+
+namespace quic {
+namespace test {
+
+FakeProofSource::FakeProofSource()
+ : delegate_(crypto_test_utils::ProofSourceForTesting()) {}
+
+FakeProofSource::~FakeProofSource() {}
+
+FakeProofSource::PendingOp::~PendingOp() = default;
+
+FakeProofSource::GetProofOp::GetProofOp(
+ const QuicSocketAddress& server_addr,
+ std::string hostname,
+ std::string server_config,
+ QuicTransportVersion transport_version,
+ std::string chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback,
+ ProofSource* delegate)
+ : server_address_(server_addr),
+ hostname_(std::move(hostname)),
+ server_config_(std::move(server_config)),
+ transport_version_(transport_version),
+ chlo_hash_(std::move(chlo_hash)),
+ callback_(std::move(callback)),
+ delegate_(delegate) {}
+
+FakeProofSource::GetProofOp::~GetProofOp() = default;
+
+void FakeProofSource::GetProofOp::Run() {
+ // Note: relies on the callback being invoked synchronously
+ delegate_->GetProof(server_address_, hostname_, server_config_,
+ transport_version_, chlo_hash_, std::move(callback_));
+}
+
+FakeProofSource::ComputeSignatureOp::ComputeSignatureOp(
+ const QuicSocketAddress& server_address,
+ std::string hostname,
+ uint16_t sig_alg,
+ QuicStringPiece in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback,
+ ProofSource* delegate)
+ : server_address_(server_address),
+ hostname_(std::move(hostname)),
+ sig_alg_(sig_alg),
+ in_(in),
+ callback_(std::move(callback)),
+ delegate_(delegate) {}
+
+FakeProofSource::ComputeSignatureOp::~ComputeSignatureOp() = default;
+
+void FakeProofSource::ComputeSignatureOp::Run() {
+ delegate_->ComputeTlsSignature(server_address_, hostname_, sig_alg_, in_,
+ std::move(callback_));
+}
+
+void FakeProofSource::Activate() {
+ active_ = true;
+}
+
+void FakeProofSource::GetProof(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback) {
+ if (!active_) {
+ delegate_->GetProof(server_address, hostname, server_config,
+ transport_version, chlo_hash, std::move(callback));
+ return;
+ }
+
+ pending_ops_.push_back(QuicMakeUnique<GetProofOp>(
+ server_address, hostname, server_config, transport_version,
+ std::string(chlo_hash), std::move(callback), delegate_.get()));
+}
+
+QuicReferenceCountedPointer<ProofSource::Chain> FakeProofSource::GetCertChain(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname) {
+ return delegate_->GetCertChain(server_address, hostname);
+}
+
+void FakeProofSource::ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback) {
+ QUIC_LOG(INFO) << "FakeProofSource::ComputeTlsSignature";
+ if (!active_) {
+ QUIC_LOG(INFO) << "Not active - directly calling delegate";
+ delegate_->ComputeTlsSignature(
+ server_address, hostname, signature_algorithm, in, std::move(callback));
+ return;
+ }
+
+ QUIC_LOG(INFO) << "Adding pending op";
+ pending_ops_.push_back(QuicMakeUnique<ComputeSignatureOp>(
+ server_address, hostname, signature_algorithm, in, std::move(callback),
+ delegate_.get()));
+}
+
+int FakeProofSource::NumPendingCallbacks() const {
+ return pending_ops_.size();
+}
+
+void FakeProofSource::InvokePendingCallback(int n) {
+ CHECK(NumPendingCallbacks() > n);
+
+ pending_ops_[n]->Run();
+
+ auto it = pending_ops_.begin() + n;
+ pending_ops_.erase(it);
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..62c3b29de9e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/fake_proof_source.h
@@ -0,0 +1,117 @@
+// 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 QUICHE_QUIC_TEST_TOOLS_FAKE_PROOF_SOURCE_H_
+#define QUICHE_QUIC_TEST_TOOLS_FAKE_PROOF_SOURCE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+
+// Implementation of ProofSource which delegates to a ProofSourceForTesting,
+// except that when the async GetProof is called, it captures the call and
+// allows tests to see that a call is pending, which they can then cause to
+// complete at a time of their choosing.
+class FakeProofSource : public ProofSource {
+ public:
+ FakeProofSource();
+ ~FakeProofSource() override;
+
+ // Before this object is "active", all calls to GetProof will be delegated
+ // immediately. Once "active", the async ones will be intercepted. This
+ // distinction is necessary to ensure that GetProof can be called without
+ // interference during test case setup.
+ void Activate();
+
+ // ProofSource interface
+ void GetProof(const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback) override;
+ QuicReferenceCountedPointer<Chain> GetCertChain(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname) override;
+ void ComputeTlsSignature(
+ const QuicSocketAddress& server_address,
+ const std::string& hostname,
+ uint16_t signature_algorithm,
+ QuicStringPiece in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback) override;
+
+ // Get the number of callbacks which are pending
+ int NumPendingCallbacks() const;
+
+ // Invoke a pending callback. The index refers to the position in
+ // pending_ops_ of the callback to be completed.
+ void InvokePendingCallback(int n);
+
+ private:
+ std::unique_ptr<ProofSource> delegate_;
+ bool active_ = false;
+
+ class PendingOp {
+ public:
+ virtual ~PendingOp();
+ virtual void Run() = 0;
+ };
+
+ class GetProofOp : public PendingOp {
+ public:
+ GetProofOp(const QuicSocketAddress& server_addr,
+ std::string hostname,
+ std::string server_config,
+ QuicTransportVersion transport_version,
+ std::string chlo_hash,
+ std::unique_ptr<ProofSource::Callback> callback,
+ ProofSource* delegate);
+ ~GetProofOp() override;
+
+ void Run() override;
+
+ private:
+ QuicSocketAddress server_address_;
+ std::string hostname_;
+ std::string server_config_;
+ QuicTransportVersion transport_version_;
+ std::string chlo_hash_;
+ std::unique_ptr<ProofSource::Callback> callback_;
+ ProofSource* delegate_;
+ };
+
+ class ComputeSignatureOp : public PendingOp {
+ public:
+ ComputeSignatureOp(const QuicSocketAddress& server_address,
+ std::string hostname,
+ uint16_t sig_alg,
+ QuicStringPiece in,
+ std::unique_ptr<ProofSource::SignatureCallback> callback,
+ ProofSource* delegate);
+ ~ComputeSignatureOp() override;
+
+ void Run() override;
+
+ private:
+ QuicSocketAddress server_address_;
+ std::string hostname_;
+ uint16_t sig_alg_;
+ std::string in_;
+ std::unique_ptr<ProofSource::SignatureCallback> callback_;
+ ProofSource* delegate_;
+ };
+
+ std::vector<std::unique_ptr<PendingOp>> pending_ops_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_FAKE_PROOF_SOURCE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.cc
new file mode 100644
index 00000000000..bc71c7cbcf5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.h"
+
+namespace quic {
+namespace test {
+
+LimitedMtuTestWriter::LimitedMtuTestWriter(QuicByteCount mtu) : mtu_(mtu) {}
+
+LimitedMtuTestWriter::~LimitedMtuTestWriter() = default;
+
+WriteResult LimitedMtuTestWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ if (buf_len > mtu_) {
+ // Drop the packet.
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+
+ return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+ peer_address, options);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.h b/chromium/net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.h
new file mode 100644
index 00000000000..ff3c5e64a7f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/limited_mtu_test_writer.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_LIMITED_MTU_TEST_WRITER_H_
+#define QUICHE_QUIC_TEST_TOOLS_LIMITED_MTU_TEST_WRITER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+namespace test {
+
+// Simulates a connection over a link with fixed MTU. Drops packets which
+// exceed the MTU and passes the rest of them as-is.
+class LimitedMtuTestWriter : public QuicPacketWriterWrapper {
+ public:
+ explicit LimitedMtuTestWriter(QuicByteCount mtu);
+ LimitedMtuTestWriter(const LimitedMtuTestWriter&) = delete;
+ LimitedMtuTestWriter& operator=(const LimitedMtuTestWriter&) = delete;
+ ~LimitedMtuTestWriter() override;
+
+ // Inherited from QuicPacketWriterWrapper.
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ private:
+ QuicByteCount mtu_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_LIMITED_MTU_TEST_WRITER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.cc
new file mode 100644
index 00000000000..1761dd9af32
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+
+namespace quic {
+
+MockClock::MockClock() : now_(QuicTime::Zero()) {}
+
+MockClock::~MockClock() {}
+
+void MockClock::AdvanceTime(QuicTime::Delta delta) {
+ now_ = now_ + delta;
+}
+
+QuicTime MockClock::Now() const {
+ return now_;
+}
+
+QuicTime MockClock::ApproximateNow() const {
+ return now_;
+}
+
+QuicWallTime MockClock::WallNow() const {
+ return QuicWallTime::FromUNIXSeconds((now_ - QuicTime::Zero()).ToSeconds());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.h
new file mode 100644
index 00000000000..dbd57d3b740
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_clock.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+namespace quic {
+
+class MockClock : public QuicClock {
+ public:
+ MockClock();
+ MockClock(const MockClock&) = delete;
+ MockClock& operator=(const MockClock&) = delete;
+ ~MockClock() override;
+
+ // QuicClock implementation:
+ QuicTime Now() const override;
+ QuicTime ApproximateNow() const override;
+ QuicWallTime WallNow() const override;
+
+ // Advances the current time by |delta|, which may be negative.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ private:
+ QuicTime now_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.cc
new file mode 100644
index 00000000000..5b781046d4d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.cc
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h"
+
+namespace quic {
+namespace test {
+
+MockQuicClientPromisedInfo::MockQuicClientPromisedInfo(
+ QuicSpdyClientSessionBase* session,
+ QuicStreamId id,
+ std::string url)
+ : QuicClientPromisedInfo(session, id, url) {}
+
+MockQuicClientPromisedInfo::~MockQuicClientPromisedInfo() {}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h
new file mode 100644
index 00000000000..2fbdfc38948
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_client_promised_info.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_CLIENT_PROMISED_INFO_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_CLIENT_PROMISED_INFO_H_
+
+#include <string>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+namespace test {
+
+class MockQuicClientPromisedInfo : public QuicClientPromisedInfo {
+ public:
+ MockQuicClientPromisedInfo(QuicSpdyClientSessionBase* session,
+ QuicStreamId id,
+ std::string url);
+ ~MockQuicClientPromisedInfo() override;
+
+ MOCK_METHOD2(HandleClientRequest,
+ QuicAsyncStatus(const spdy::SpdyHeaderBlock& headers,
+ QuicClientPushPromiseIndex::Delegate* delegate));
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_CLIENT_PROMISED_INFO_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.cc
new file mode 100644
index 00000000000..3b3ca4691b1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.h"
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+MockQuicDispatcher::MockQuicDispatcher(
+ const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleDispatcher(config,
+ crypto_config,
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ quic_simple_server_backend,
+ kQuicDefaultConnectionIdLength) {}
+
+MockQuicDispatcher::~MockQuicDispatcher() {}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.h
new file mode 100644
index 00000000000..0eaddc5fb35
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+
+namespace quic {
+namespace test {
+
+class MockQuicDispatcher : public QuicSimpleDispatcher {
+ public:
+ MockQuicDispatcher(
+ const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ MockQuicDispatcher(const MockQuicDispatcher&) = delete;
+ MockQuicDispatcher& operator=(const MockQuicDispatcher&) = delete;
+
+ ~MockQuicDispatcher() override;
+
+ MOCK_METHOD3(ProcessPacket,
+ void(const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ const QuicReceivedPacket& packet));
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.cc
new file mode 100644
index 00000000000..fea84140e88
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+
+namespace quic {
+namespace test {
+
+MockQuicSessionVisitor::MockQuicSessionVisitor() = default;
+
+MockQuicSessionVisitor::~MockQuicSessionVisitor() = default;
+
+MockQuicCryptoServerStreamHelper::MockQuicCryptoServerStreamHelper() = default;
+
+MockQuicCryptoServerStreamHelper::~MockQuicCryptoServerStreamHelper() = default;
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h
new file mode 100644
index 00000000000..d429cd42c0b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h
@@ -0,0 +1,56 @@
+// 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 QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SESSION_VISITOR_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SESSION_VISITOR_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+
+namespace quic {
+namespace test {
+
+class MockQuicSessionVisitor : public QuicTimeWaitListManager::Visitor {
+ public:
+ MockQuicSessionVisitor();
+ MockQuicSessionVisitor(const MockQuicSessionVisitor&) = delete;
+ MockQuicSessionVisitor& operator=(const MockQuicSessionVisitor&) = delete;
+ ~MockQuicSessionVisitor() override;
+ MOCK_METHOD4(OnConnectionClosed,
+ void(QuicConnectionId connection_id,
+ QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(OnWriteBlocked,
+ void(QuicBlockedWriterInterface* blocked_writer));
+ MOCK_METHOD1(OnRstStreamReceived, void(const QuicRstStreamFrame& frame));
+ MOCK_METHOD1(OnStopSendingReceived, void(const QuicStopSendingFrame& frame));
+ MOCK_METHOD1(OnConnectionAddedToTimeWaitList,
+ void(QuicConnectionId connection_id));
+};
+
+class MockQuicCryptoServerStreamHelper : public QuicCryptoServerStream::Helper {
+ public:
+ MockQuicCryptoServerStreamHelper();
+ MockQuicCryptoServerStreamHelper(const MockQuicCryptoServerStreamHelper&) =
+ delete;
+ MockQuicCryptoServerStreamHelper& operator=(
+ const MockQuicCryptoServerStreamHelper&) = delete;
+ ~MockQuicCryptoServerStreamHelper() override;
+ MOCK_CONST_METHOD2(GenerateConnectionIdForReject,
+ QuicConnectionId(QuicTransportVersion version,
+ QuicConnectionId connection_id));
+ MOCK_CONST_METHOD5(CanAcceptClientHello,
+ bool(const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details));
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SESSION_VISITOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.cc
new file mode 100644
index 00000000000..1d6e2f9b2bc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.h"
+
+namespace quic {
+namespace test {
+
+MockQuicSpdyClientStream::MockQuicSpdyClientStream(
+ QuicStreamId id,
+ QuicSpdyClientSession* session,
+ StreamType type)
+ : QuicSpdyClientStream(id, session, type) {}
+
+MockQuicSpdyClientStream::~MockQuicSpdyClientStream() {}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.h
new file mode 100644
index 00000000000..e41df5284b7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_spdy_client_stream.h
@@ -0,0 +1,34 @@
+// 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 QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SPDY_CLIENT_STREAM_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SPDY_CLIENT_STREAM_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+namespace test {
+
+class MockQuicSpdyClientStream : public QuicSpdyClientStream {
+ public:
+ MockQuicSpdyClientStream(QuicStreamId id,
+ QuicSpdyClientSession* session,
+ StreamType type);
+ ~MockQuicSpdyClientStream() override;
+
+ MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame& frame));
+ MOCK_METHOD3(OnPromiseHeaderList,
+ void(QuicStreamId promised_stream_id,
+ size_t frame_len,
+ const QuicHeaderList& list));
+ MOCK_METHOD0(OnDataAvailable, void());
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_SPDY_CLIENT_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc
new file mode 100644
index 00000000000..b1890f0fbfc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace quic {
+namespace test {
+
+MockTimeWaitListManager::MockTimeWaitListManager(
+ QuicPacketWriter* writer,
+ Visitor* visitor,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory)
+ : QuicTimeWaitListManager(writer, visitor, clock, alarm_factory) {
+ // Though AddConnectionIdToTimeWait is mocked, we want to retain its
+ // functionality.
+ EXPECT_CALL(*this, AddConnectionIdToTimeWait(_, _, _, _, _))
+ .Times(testing::AnyNumber());
+ ON_CALL(*this, AddConnectionIdToTimeWait(_, _, _, _, _))
+ .WillByDefault(
+ Invoke(this, &MockTimeWaitListManager::
+ QuicTimeWaitListManager_AddConnectionIdToTimeWait));
+}
+
+MockTimeWaitListManager::~MockTimeWaitListManager() = default;
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h
new file mode 100644
index 00000000000..742fef2ae89
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_TIME_WAIT_LIST_MANAGER_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_TIME_WAIT_LIST_MANAGER_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+
+namespace quic {
+namespace test {
+
+class MockTimeWaitListManager : public QuicTimeWaitListManager {
+ public:
+ MockTimeWaitListManager(QuicPacketWriter* writer,
+ Visitor* visitor,
+ const QuicClock* clock,
+ QuicAlarmFactory* alarm_factory);
+ ~MockTimeWaitListManager() override;
+
+ MOCK_METHOD5(AddConnectionIdToTimeWait,
+ void(QuicConnectionId connection_id,
+ bool ietf_quic,
+ QuicTimeWaitListManager::TimeWaitAction action,
+ EncryptionLevel encryption_level,
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>*
+ termination_packets));
+
+ void QuicTimeWaitListManager_AddConnectionIdToTimeWait(
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ QuicTimeWaitListManager::TimeWaitAction action,
+ EncryptionLevel encryption_level,
+ std::vector<std::unique_ptr<QuicEncryptedPacket>>* termination_packets) {
+ QuicTimeWaitListManager::AddConnectionIdToTimeWait(connection_id, ietf_quic,
+ action, encryption_level,
+ termination_packets);
+ }
+
+ MOCK_METHOD5(ProcessPacket,
+ void(const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ QuicConnectionId connection_id,
+ PacketHeaderFormat header_format,
+ std::unique_ptr<QuicPerPacketContext> packet_context));
+
+ MOCK_METHOD6(SendVersionNegotiationPacket,
+ void(QuicConnectionId connection_id,
+ bool ietf_quic,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ std::unique_ptr<QuicPerPacketContext> packet_context));
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_QUIC_TIME_WAIT_LIST_MANAGER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_random.cc b/chromium/net/third_party/quiche/src/quic/test_tools/mock_random.cc
new file mode 100644
index 00000000000..a01a5a7cbfb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_random.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+
+#include <string.h>
+
+namespace quic {
+namespace test {
+
+MockRandom::MockRandom() : base_(0xDEADBEEF), increment_(0) {}
+
+MockRandom::MockRandom(uint32_t base) : base_(base), increment_(0) {}
+
+void MockRandom::RandBytes(void* data, size_t len) {
+ memset(data, increment_ + static_cast<uint8_t>('r'), len);
+}
+
+uint64_t MockRandom::RandUint64() {
+ return base_ + increment_;
+}
+
+void MockRandom::ChangeValue() {
+ increment_++;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/mock_random.h b/chromium/net/third_party/quiche/src/quic/test_tools/mock_random.h
new file mode 100644
index 00000000000..d5de2267ee1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/mock_random.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
+#define QUICHE_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+
+namespace quic {
+namespace test {
+
+class MockRandom : public QuicRandom {
+ public:
+ // Initializes base_ to 0xDEADBEEF.
+ MockRandom();
+ explicit MockRandom(uint32_t base);
+ MockRandom(const MockRandom&) = delete;
+ MockRandom& operator=(const MockRandom&) = delete;
+
+ // QuicRandom:
+ // Fills the |data| buffer with a repeating byte, initially 'r'.
+ void RandBytes(void* data, size_t len) override;
+ // Returns base + the current increment.
+ uint64_t RandUint64() override;
+
+ // ChangeValue increments |increment_|. This causes the value returned by
+ // |RandUint64| and the byte that |RandBytes| fills with, to change.
+ void ChangeValue();
+
+ private:
+ uint32_t base_;
+ uint8_t increment_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
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
new file mode 100644
index 00000000000..87935d5c013
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.cc
@@ -0,0 +1,252 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+namespace quic {
+namespace test {
+
+const int32_t kMaxConsecutivePacketLoss = 3;
+
+// An alarm that is scheduled if a blocked socket is simulated to indicate
+// it's writable again.
+class WriteUnblockedAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
+ : writer_(writer) {}
+
+ void OnAlarm() override {
+ QUIC_DLOG(INFO) << "Unblocking socket.";
+ writer_->OnCanWrite();
+ }
+
+ private:
+ PacketDroppingTestWriter* writer_;
+};
+
+// An alarm that is scheduled every time a new packet is to be written at a
+// later point.
+class DelayAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {}
+
+ void OnAlarm() override {
+ QuicTime new_deadline = writer_->ReleaseOldPackets();
+ if (new_deadline.IsInitialized()) {
+ writer_->SetDelayAlarm(new_deadline);
+ }
+ }
+
+ private:
+ PacketDroppingTestWriter* writer_;
+};
+
+PacketDroppingTestWriter::PacketDroppingTestWriter()
+ : clock_(nullptr),
+ cur_buffer_size_(0),
+ num_calls_to_write_(0),
+ fake_packet_loss_percentage_(0),
+ fake_drop_first_n_packets_(0),
+ fake_blocked_socket_percentage_(0),
+ fake_packet_reorder_percentage_(0),
+ fake_packet_delay_(QuicTime::Delta::Zero()),
+ fake_bandwidth_(QuicBandwidth::Zero()),
+ buffer_size_(0),
+ num_consecutive_packet_lost_(0) {
+ uint64_t seed = QuicRandom::GetInstance()->RandUint64();
+ QUIC_LOG(INFO) << "Seeding packet loss with " << seed;
+ simple_random_.set_seed(seed);
+}
+
+PacketDroppingTestWriter::~PacketDroppingTestWriter() = default;
+
+void PacketDroppingTestWriter::Initialize(
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<Delegate> on_can_write) {
+ clock_ = helper->GetClock();
+ write_unblocked_alarm_.reset(
+ alarm_factory->CreateAlarm(new WriteUnblockedAlarm(this)));
+ delay_alarm_.reset(alarm_factory->CreateAlarm(new DelayAlarm(this)));
+ on_can_write_ = std::move(on_can_write);
+}
+
+WriteResult PacketDroppingTestWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ ++num_calls_to_write_;
+ ReleaseOldPackets();
+
+ QuicWriterMutexLock lock(&config_mutex_);
+ if (fake_drop_first_n_packets_ > 0 &&
+ num_calls_to_write_ <=
+ static_cast<uint64_t>(fake_drop_first_n_packets_)) {
+ QUIC_DVLOG(1) << "Dropping first " << fake_drop_first_n_packets_
+ << " packets (packet number " << num_calls_to_write_ << ")";
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+ const int32_t kMaxPacketLossPercentage =
+ kMaxConsecutivePacketLoss * 100.0 / (kMaxConsecutivePacketLoss + 1);
+ if (fake_packet_loss_percentage_ > 0 &&
+ // Do not allow too many consecutive packet drops to avoid test flakiness.
+ (num_consecutive_packet_lost_ <= kMaxConsecutivePacketLoss ||
+ // Allow as many consecutive packet drops as possbile if
+ // |fake_packet_lost_percentage_| is large enough. Without this exception
+ // it is hard to simulate high loss rate, like 100%.
+ fake_packet_loss_percentage_ > kMaxPacketLossPercentage) &&
+ (simple_random_.RandUint64() % 100 <
+ static_cast<uint64_t>(fake_packet_loss_percentage_))) {
+ QUIC_DVLOG(1) << "Dropping packet.";
+ ++num_consecutive_packet_lost_;
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ } else {
+ num_consecutive_packet_lost_ = 0;
+ }
+ if (fake_blocked_socket_percentage_ > 0 &&
+ simple_random_.RandUint64() % 100 <
+ static_cast<uint64_t>(fake_blocked_socket_percentage_)) {
+ CHECK(on_can_write_ != nullptr);
+ QUIC_DVLOG(1) << "Blocking socket.";
+ if (!write_unblocked_alarm_->IsSet()) {
+ // Set the alarm to fire immediately.
+ write_unblocked_alarm_->Set(clock_->ApproximateNow());
+ }
+ return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
+ }
+
+ if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
+ if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
+ // Drop packets which do not fit into the buffer.
+ QUIC_DVLOG(1) << "Dropping packet because the buffer is full.";
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+
+ // Queue it to be sent.
+ QuicTime send_time = clock_->ApproximateNow() + fake_packet_delay_;
+ if (!fake_bandwidth_.IsZero()) {
+ // Calculate a time the bandwidth limit would impose.
+ QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
+ (buf_len * kNumMicrosPerSecond) / fake_bandwidth_.ToBytesPerSecond());
+ send_time = delayed_packets_.empty()
+ ? send_time + bandwidth_delay
+ : delayed_packets_.back().send_time + bandwidth_delay;
+ }
+ std::unique_ptr<PerPacketOptions> delayed_options;
+ if (options != nullptr) {
+ delayed_options = options->Clone();
+ }
+ delayed_packets_.push_back(
+ DelayedWrite(buffer, buf_len, self_address, peer_address,
+ std::move(delayed_options), send_time));
+ cur_buffer_size_ += buf_len;
+
+ // Set the alarm if it's not yet set.
+ if (!delay_alarm_->IsSet()) {
+ delay_alarm_->Set(send_time);
+ }
+
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+ }
+
+ return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
+ peer_address, options);
+}
+
+bool PacketDroppingTestWriter::IsWriteBlocked() const {
+ if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
+ return true;
+ }
+ return QuicPacketWriterWrapper::IsWriteBlocked();
+}
+
+void PacketDroppingTestWriter::SetWritable() {
+ if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
+ write_unblocked_alarm_->Cancel();
+ }
+ QuicPacketWriterWrapper::SetWritable();
+}
+
+QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
+ if (delayed_packets_.empty()) {
+ return QuicTime::Zero();
+ }
+ QuicReaderMutexLock lock(&config_mutex_);
+ auto iter = delayed_packets_.begin();
+ // Determine if we should re-order.
+ if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
+ simple_random_.RandUint64() % 100 <
+ static_cast<uint64_t>(fake_packet_reorder_percentage_)) {
+ QUIC_DLOG(INFO) << "Reordering packets.";
+ ++iter;
+ // Swap the send times when re-ordering packets.
+ delayed_packets_.begin()->send_time = iter->send_time;
+ }
+
+ QUIC_DVLOG(1) << "Releasing packet. " << (delayed_packets_.size() - 1)
+ << " remaining.";
+ // Grab the next one off the queue and send it.
+ QuicPacketWriterWrapper::WritePacket(
+ iter->buffer.data(), iter->buffer.length(), iter->self_address,
+ iter->peer_address, iter->options.get());
+ DCHECK_GE(cur_buffer_size_, iter->buffer.length());
+ cur_buffer_size_ -= iter->buffer.length();
+ delayed_packets_.erase(iter);
+
+ // If there are others, find the time for the next to be sent.
+ if (delayed_packets_.empty()) {
+ return QuicTime::Zero();
+ }
+ return delayed_packets_.begin()->send_time;
+}
+
+QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
+ while (!delayed_packets_.empty()) {
+ QuicTime next_send_time = delayed_packets_.front().send_time;
+ if (next_send_time > clock_->Now()) {
+ return next_send_time;
+ }
+ ReleaseNextPacket();
+ }
+ return QuicTime::Zero();
+}
+
+void PacketDroppingTestWriter::SetDelayAlarm(QuicTime new_deadline) {
+ delay_alarm_->Set(new_deadline);
+}
+
+void PacketDroppingTestWriter::OnCanWrite() {
+ on_can_write_->OnCanWrite();
+}
+
+void PacketDroppingTestWriter::set_fake_packet_loss_percentage(
+ int32_t fake_packet_loss_percentage) {
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_packet_loss_percentage_ = fake_packet_loss_percentage;
+ num_consecutive_packet_lost_ = 0;
+}
+
+PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ std::unique_ptr<PerPacketOptions> options,
+ QuicTime send_time)
+ : buffer(buffer, buf_len),
+ self_address(self_address),
+ peer_address(peer_address),
+ options(std::move(options)),
+ send_time(send_time) {}
+
+PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() = default;
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h
new file mode 100644
index 00000000000..eb4ad1cf717
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/packet_dropping_test_writer.h
@@ -0,0 +1,176 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_
+#define QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_
+
+#include <cstdint>
+#include <list>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_client.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+// Simulates a connection that drops packets a configured percentage of the time
+// and has a blocked socket a configured percentage of the time. Also provides
+// the options to delay packets and reorder packets if delay is enabled.
+class PacketDroppingTestWriter : public QuicPacketWriterWrapper {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+ virtual void OnCanWrite() = 0;
+ };
+
+ PacketDroppingTestWriter();
+ PacketDroppingTestWriter(const PacketDroppingTestWriter&) = delete;
+ PacketDroppingTestWriter& operator=(const PacketDroppingTestWriter&) = delete;
+
+ ~PacketDroppingTestWriter() override;
+
+ // Must be called before blocking, reordering or delaying (loss is OK). May be
+ // called after connecting if the helper is not available before.
+ // |on_can_write| will be triggered when fake-unblocking.
+ void Initialize(QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<Delegate> on_can_write);
+
+ // QuicPacketWriter methods:
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ bool IsWriteBlocked() const override;
+
+ void SetWritable() override;
+
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override {
+ // If the wrapped writer supports zero-copy, disable it, because it is not
+ // compatible with delayed writes in this class.
+ return nullptr;
+ }
+
+ // Writes out any packet which should have been sent by now
+ // to the contained writer and returns the time
+ // for the next delayed packet to be written.
+ QuicTime ReleaseOldPackets();
+
+ // Sets |delay_alarm_| to fire at |new_deadline|.
+ void SetDelayAlarm(QuicTime new_deadline);
+
+ void OnCanWrite();
+
+ // The percent of time a packet is simulated as being lost.
+ void set_fake_packet_loss_percentage(int32_t fake_packet_loss_percentage);
+
+ // Simulate dropping the first n packets unconditionally.
+ // Subsequent packets will be lost at fake_packet_loss_percentage_ if set.
+ void set_fake_drop_first_n_packets(int32_t fake_drop_first_n_packets) {
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_drop_first_n_packets_ = fake_drop_first_n_packets;
+ }
+
+ // The percent of time WritePacket will block and set WriteResult's status
+ // to WRITE_STATUS_BLOCKED.
+ void set_fake_blocked_socket_percentage(
+ int32_t fake_blocked_socket_percentage) {
+ DCHECK(clock_);
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_blocked_socket_percentage_ = fake_blocked_socket_percentage;
+ }
+
+ // The percent of time a packet is simulated as being reordered.
+ void set_fake_reorder_percentage(int32_t fake_packet_reorder_percentage) {
+ DCHECK(clock_);
+ QuicWriterMutexLock lock(&config_mutex_);
+ DCHECK(!fake_packet_delay_.IsZero());
+ fake_packet_reorder_percentage_ = fake_packet_reorder_percentage;
+ }
+
+ // The delay before writing this packet.
+ void set_fake_packet_delay(QuicTime::Delta fake_packet_delay) {
+ DCHECK(clock_);
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_packet_delay_ = fake_packet_delay;
+ }
+
+ // The maximum bandwidth and buffer size of the connection. When these are
+ // set, packets will be delayed until a connection with that bandwidth would
+ // transmit it. Once the |buffer_size| is reached, all new packets are
+ // dropped.
+ void set_max_bandwidth_and_buffer_size(QuicBandwidth fake_bandwidth,
+ QuicByteCount buffer_size) {
+ DCHECK(clock_);
+ QuicWriterMutexLock lock(&config_mutex_);
+ fake_bandwidth_ = fake_bandwidth;
+ buffer_size_ = buffer_size;
+ }
+
+ // Useful for reproducing very flaky issues.
+ QUIC_UNUSED void set_seed(uint64_t seed) { simple_random_.set_seed(seed); }
+
+ private:
+ // Writes out the next packet to the contained writer and returns the time
+ // for the next delayed packet to be written.
+ QuicTime ReleaseNextPacket();
+
+ // A single packet which will be sent at the supplied send_time.
+ struct DelayedWrite {
+ public:
+ DelayedWrite(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ std::unique_ptr<PerPacketOptions> options,
+ QuicTime send_time);
+ DelayedWrite(const DelayedWrite&) = delete;
+ DelayedWrite(DelayedWrite&&) = default;
+ DelayedWrite& operator=(const DelayedWrite&) = delete;
+ DelayedWrite& operator=(DelayedWrite&&) = default;
+ ~DelayedWrite();
+
+ std::string buffer;
+ QuicIpAddress self_address;
+ QuicSocketAddress peer_address;
+ std::unique_ptr<PerPacketOptions> options;
+ QuicTime send_time;
+ };
+
+ typedef std::list<DelayedWrite> DelayedPacketList;
+
+ const QuicClock* clock_;
+ std::unique_ptr<QuicAlarm> write_unblocked_alarm_;
+ std::unique_ptr<QuicAlarm> delay_alarm_;
+ std::unique_ptr<Delegate> on_can_write_;
+ SimpleRandom simple_random_;
+ // Stored packets delayed by fake packet delay or bandwidth restrictions.
+ DelayedPacketList delayed_packets_;
+ QuicByteCount cur_buffer_size_;
+ uint64_t num_calls_to_write_;
+
+ QuicMutex config_mutex_;
+ int32_t fake_packet_loss_percentage_ GUARDED_BY(config_mutex_);
+ int32_t fake_drop_first_n_packets_ GUARDED_BY(config_mutex_);
+ int32_t fake_blocked_socket_percentage_ GUARDED_BY(config_mutex_);
+ int32_t fake_packet_reorder_percentage_ GUARDED_BY(config_mutex_);
+ QuicTime::Delta fake_packet_delay_ GUARDED_BY(config_mutex_);
+ QuicBandwidth fake_bandwidth_ GUARDED_BY(config_mutex_);
+ QuicByteCount buffer_size_ GUARDED_BY(config_mutex_);
+ int32_t num_consecutive_packet_lost_ GUARDED_BY(config_mutex_);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_PACKET_DROPPING_TEST_WRITER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.cc
new file mode 100644
index 00000000000..5fa5659e596
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.cc
@@ -0,0 +1,53 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h"
+
+namespace quic {
+namespace test {
+
+PacketReorderingWriter::PacketReorderingWriter() = default;
+
+PacketReorderingWriter::~PacketReorderingWriter() = default;
+
+WriteResult PacketReorderingWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ if (!delay_next_) {
+ VLOG(2) << "Writing a non-delayed packet";
+ WriteResult wr = QuicPacketWriterWrapper::WritePacket(
+ buffer, buf_len, self_address, peer_address, options);
+ --num_packets_to_wait_;
+ if (num_packets_to_wait_ == 0) {
+ VLOG(2) << "Writing a delayed packet";
+ // It's time to write the delayed packet.
+ QuicPacketWriterWrapper::WritePacket(
+ delayed_data_.data(), delayed_data_.length(), delayed_self_address_,
+ delayed_peer_address_, delayed_options_.get());
+ }
+ return wr;
+ }
+ // Still have packet to wait.
+ DCHECK_LT(0u, num_packets_to_wait_) << "Only allow one packet to be delayed";
+ delayed_data_ = std::string(buffer, buf_len);
+ delayed_self_address_ = self_address;
+ delayed_peer_address_ = peer_address;
+ if (options != nullptr) {
+ delayed_options_ = options->Clone();
+ }
+ delay_next_ = false;
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+}
+
+void PacketReorderingWriter::SetDelay(size_t num_packets_to_wait) {
+ DCHECK_GT(num_packets_to_wait, 0u);
+ num_packets_to_wait_ = num_packets_to_wait;
+ delay_next_ = true;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h b/chromium/net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h
new file mode 100644
index 00000000000..31cbd80700b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/packet_reordering_writer.h
@@ -0,0 +1,45 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_
+#define QUICHE_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+
+namespace quic {
+
+namespace test {
+
+// This packet writer allows delaying writing the next packet after
+// SetDelay(num_packets_to_wait)
+// is called and buffer this packet and write it after it writes next
+// |num_packets_to_wait| packets. It doesn't support delaying a packet while
+// there is already a packet delayed.
+class PacketReorderingWriter : public QuicPacketWriterWrapper {
+ public:
+ PacketReorderingWriter();
+
+ ~PacketReorderingWriter() override;
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+
+ void SetDelay(size_t num_packets_to_wait);
+
+ private:
+ bool delay_next_ = false;
+ size_t num_packets_to_wait_ = 0;
+ std::string delayed_data_;
+ QuicIpAddress delayed_self_address_;
+ QuicSocketAddress delayed_peer_address_;
+ std::unique_ptr<PerPacketOptions> delayed_options_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_PACKET_REORDERING_WRITER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.cc
new file mode 100644
index 00000000000..73b9e987eef
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.cc
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_buffered_packet_store.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicAlarm* QuicBufferedPacketStorePeer::expiration_alarm(
+ QuicBufferedPacketStore* store) {
+ return store->expiration_alarm_.get();
+}
+
+// static
+void QuicBufferedPacketStorePeer::set_clock(QuicBufferedPacketStore* store,
+ const QuicClock* clock) {
+ store->clock_ = clock;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h
new file mode 100644
index 00000000000..e857dfbe786
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_buffered_packet_store_peer.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_BUFFERED_PACKET_STORE_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_BUFFERED_PACKET_STORE_PEER_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+namespace quic {
+
+class QuicBufferedPacketStore;
+
+namespace test {
+
+class QuicBufferedPacketStorePeer {
+ public:
+ QuicBufferedPacketStorePeer() = delete;
+
+ static QuicAlarm* expiration_alarm(QuicBufferedPacketStore* store);
+
+ static void set_clock(QuicBufferedPacketStore* store, const QuicClock* clock);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_BUFFERED_PACKET_STORE_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_peer.cc
new file mode 100644
index 00000000000..c8bfa6ab022
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_peer.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
+
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+
+namespace quic {
+namespace test {
+
+// static
+bool QuicClientPeer::CreateUDPSocketAndBind(QuicClient* client) {
+ return client->network_helper()->CreateUDPSocketAndBind(
+ client->server_address(), client->bind_to_address(),
+ client->local_port());
+}
+
+// static
+void QuicClientPeer::CleanUpUDPSocket(QuicClient* client, int fd) {
+ client->epoll_network_helper()->CleanUpUDPSocket(fd);
+}
+
+// static
+void QuicClientPeer::SetClientPort(QuicClient* client, int port) {
+ client->epoll_network_helper()->SetClientPort(port);
+}
+
+// static
+void QuicClientPeer::SetWriter(QuicClient* client, QuicPacketWriter* writer) {
+ client->set_writer(writer);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_peer.h
new file mode 100644
index 00000000000..7070779f47c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_peer.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+
+namespace quic {
+
+class QuicClient;
+class QuicPacketWriter;
+
+namespace test {
+
+class QuicClientPeer {
+ public:
+ QuicClientPeer() = delete;
+
+ static bool CreateUDPSocketAndBind(QuicClient* client);
+ static void CleanUpUDPSocket(QuicClient* client, int fd);
+ static void SetClientPort(QuicClient* client, int port);
+ static void SetWriter(QuicClient* client, QuicPacketWriter* writer);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.cc
new file mode 100644
index 00000000000..91fec1dfb25
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.cc
@@ -0,0 +1,17 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicAlarm* QuicClientPromisedInfoPeer::GetAlarm(
+ QuicClientPromisedInfo* promised_stream) {
+ return promised_stream->cleanup_alarm_.get();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.h
new file mode 100644
index 00000000000..4b2e2070d0a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_client_promised_info_peer.h
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PROMISED_INFO_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PROMISED_INFO_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_promised_info.h"
+
+namespace quic {
+namespace test {
+
+class QuicClientPromisedInfoPeer {
+ public:
+ QuicClientPromisedInfoPeer() = delete;
+
+ static QuicAlarm* GetAlarm(QuicClientPromisedInfo* promised_stream);
+};
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CLIENT_PROMISED_INFO_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc
new file mode 100644
index 00000000000..f5a5b315b3f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.cc
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ QuicConfig* config,
+ uint32_t window_bytes) {
+ config->initial_stream_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
+ QuicConfig* config,
+ uint32_t window_bytes) {
+ config->initial_session_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedConnectionOptions(
+ QuicConfig* config,
+ const QuicTagVector& options) {
+ config->connection_options_.SetReceivedValues(options);
+}
+
+// static
+void QuicConfigPeer::SetReceivedBytesForConnectionId(QuicConfig* config,
+ uint32_t bytes) {
+ DCHECK(bytes == 0 || bytes == 8);
+ config->bytes_for_connection_id_.SetReceivedValue(bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedDisableConnectionMigration(QuicConfig* config) {
+ config->connection_migration_disabled_.SetReceivedValue(1);
+}
+
+// static
+void QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(
+ QuicConfig* config,
+ uint32_t max_streams) {
+ config->max_incoming_dynamic_streams_.SetReceivedValue(max_streams);
+}
+
+// static
+void QuicConfigPeer::SetConnectionOptionsToSend(QuicConfig* config,
+ const QuicTagVector& options) {
+ config->SetConnectionOptionsToSend(options);
+}
+
+// static
+void QuicConfigPeer::SetReceivedStatelessResetToken(QuicConfig* config,
+ QuicUint128 token) {
+ config->stateless_reset_token_.SetReceivedValue(token);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h
new file mode 100644
index 00000000000..8869d270875
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_config_peer.h
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+class QuicConfig;
+
+namespace test {
+
+class QuicConfigPeer {
+ public:
+ QuicConfigPeer() = delete;
+
+ static void SetReceivedInitialStreamFlowControlWindow(QuicConfig* config,
+ uint32_t window_bytes);
+
+ static void SetReceivedInitialSessionFlowControlWindow(QuicConfig* config,
+ uint32_t window_bytes);
+
+ static void SetReceivedConnectionOptions(QuicConfig* config,
+ const QuicTagVector& options);
+
+ static void SetReceivedBytesForConnectionId(QuicConfig* config,
+ uint32_t bytes);
+
+ static void SetReceivedDisableConnectionMigration(QuicConfig* config);
+
+ static void SetReceivedMaxIncomingDynamicStreams(QuicConfig* config,
+ uint32_t max_streams);
+
+ static void SetConnectionOptionsToSend(QuicConfig* config,
+ const QuicTagVector& options);
+
+ static void SetReceivedStatelessResetToken(QuicConfig* config,
+ QuicUint128 token);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
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
new file mode 100644
index 00000000000..4e344a85864
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.cc
@@ -0,0 +1,393 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_received_packet_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicConnectionPeer::SendAck(QuicConnection* connection) {
+ connection->SendAck();
+}
+
+// static
+void QuicConnectionPeer::SetSendAlgorithm(
+ QuicConnection* connection,
+ SendAlgorithmInterface* send_algorithm) {
+ GetSentPacketManager(connection)->SetSendAlgorithm(send_algorithm);
+}
+
+// static
+void QuicConnectionPeer::SetLossAlgorithm(
+ QuicConnection* connection,
+ LossDetectionInterface* loss_algorithm) {
+ GetSentPacketManager(connection)->loss_algorithm_ = loss_algorithm;
+}
+
+// static
+const QuicFrame QuicConnectionPeer::GetUpdatedAckFrame(
+ QuicConnection* connection) {
+ const bool ack_frame_updated = connection->ack_frame_updated();
+ const QuicFrame ack_frame = connection->GetUpdatedAckFrame();
+ if (connection->use_uber_received_packet_manager_) {
+ DCHECK(!connection->uber_received_packet_manager_
+ .supports_multiple_packet_number_spaces());
+ connection->uber_received_packet_manager_.received_packet_managers_[0]
+ .ack_frame_updated_ = ack_frame_updated;
+ } else {
+ connection->received_packet_manager_.ack_frame_updated_ = ack_frame_updated;
+ }
+ return ack_frame;
+}
+
+// static
+void QuicConnectionPeer::PopulateStopWaitingFrame(
+ QuicConnection* connection,
+ QuicStopWaitingFrame* stop_waiting) {
+ connection->PopulateStopWaitingFrame(stop_waiting);
+}
+
+// static
+QuicConnectionVisitorInterface* QuicConnectionPeer::GetVisitor(
+ QuicConnection* connection) {
+ return connection->visitor_;
+}
+
+// static
+QuicPacketCreator* QuicConnectionPeer::GetPacketCreator(
+ QuicConnection* connection) {
+ return QuicPacketGeneratorPeer::GetPacketCreator(
+ &connection->packet_generator_);
+}
+
+// static
+QuicPacketGenerator* QuicConnectionPeer::GetPacketGenerator(
+ QuicConnection* connection) {
+ return &connection->packet_generator_;
+}
+
+// static
+QuicSentPacketManager* QuicConnectionPeer::GetSentPacketManager(
+ QuicConnection* connection) {
+ return &connection->sent_packet_manager_;
+}
+
+// static
+QuicTime::Delta QuicConnectionPeer::GetNetworkTimeout(
+ QuicConnection* connection) {
+ return connection->idle_network_timeout_;
+}
+
+// static
+void QuicConnectionPeer::SetPerspective(QuicConnection* connection,
+ Perspective perspective) {
+ connection->perspective_ = perspective;
+ QuicFramerPeer::SetPerspective(&connection->framer_, perspective);
+}
+
+// static
+void QuicConnectionPeer::SetSelfAddress(QuicConnection* connection,
+ const QuicSocketAddress& self_address) {
+ connection->self_address_ = self_address;
+}
+
+// static
+void QuicConnectionPeer::SetPeerAddress(QuicConnection* connection,
+ const QuicSocketAddress& peer_address) {
+ connection->peer_address_ = peer_address;
+}
+
+// static
+void QuicConnectionPeer::SetDirectPeerAddress(
+ QuicConnection* connection,
+ const QuicSocketAddress& direct_peer_address) {
+ connection->direct_peer_address_ = direct_peer_address;
+}
+
+// static
+void QuicConnectionPeer::SetEffectivePeerAddress(
+ QuicConnection* connection,
+ const QuicSocketAddress& effective_peer_address) {
+ connection->effective_peer_address_ = effective_peer_address;
+}
+
+// static
+bool QuicConnectionPeer::IsSilentCloseEnabled(QuicConnection* connection) {
+ return connection->idle_timeout_connection_close_behavior_ ==
+ ConnectionCloseBehavior::SILENT_CLOSE;
+}
+
+// static
+void QuicConnectionPeer::SwapCrypters(QuicConnection* connection,
+ QuicFramer* framer) {
+ QuicFramerPeer::SwapCrypters(framer, &connection->framer_);
+}
+
+// static
+void QuicConnectionPeer::SetCurrentPacket(QuicConnection* connection,
+ QuicStringPiece current_packet) {
+ connection->current_packet_data_ = current_packet.data();
+ connection->last_size_ = current_packet.size();
+}
+
+// static
+QuicConnectionHelperInterface* QuicConnectionPeer::GetHelper(
+ QuicConnection* connection) {
+ return connection->helper_;
+}
+
+// static
+QuicAlarmFactory* QuicConnectionPeer::GetAlarmFactory(
+ QuicConnection* connection) {
+ return connection->alarm_factory_;
+}
+
+// static
+QuicFramer* QuicConnectionPeer::GetFramer(QuicConnection* connection) {
+ return &connection->framer_;
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetAckAlarm(QuicConnection* connection) {
+ return connection->ack_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) {
+ return connection->ping_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetRetransmissionAlarm(
+ QuicConnection* connection) {
+ return connection->retransmission_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetSendAlarm(QuicConnection* connection) {
+ return connection->send_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetTimeoutAlarm(QuicConnection* connection) {
+ return connection->timeout_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetMtuDiscoveryAlarm(
+ QuicConnection* connection) {
+ return connection->mtu_discovery_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetPathDegradingAlarm(
+ QuicConnection* connection) {
+ return connection->path_degrading_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(
+ QuicConnection* connection) {
+ return connection->process_undecryptable_packets_alarm_.get();
+}
+
+// static
+QuicPacketWriter* QuicConnectionPeer::GetWriter(QuicConnection* connection) {
+ return connection->writer_;
+}
+
+// static
+void QuicConnectionPeer::SetWriter(QuicConnection* connection,
+ QuicPacketWriter* writer,
+ bool owns_writer) {
+ if (connection->owns_writer_) {
+ delete connection->writer_;
+ }
+ connection->writer_ = writer;
+ connection->owns_writer_ = owns_writer;
+}
+
+// static
+void QuicConnectionPeer::TearDownLocalConnectionState(
+ QuicConnection* connection) {
+ connection->connected_ = false;
+}
+
+// static
+QuicEncryptedPacket* QuicConnectionPeer::GetConnectionClosePacket(
+ QuicConnection* connection) {
+ if (connection->termination_packets_ == nullptr ||
+ connection->termination_packets_->empty()) {
+ return nullptr;
+ }
+ return (*connection->termination_packets_)[0].get();
+}
+
+// static
+QuicPacketHeader* QuicConnectionPeer::GetLastHeader(
+ QuicConnection* connection) {
+ return &connection->last_header_;
+}
+
+// static
+QuicConnectionStats* QuicConnectionPeer::GetStats(QuicConnection* connection) {
+ return &connection->stats_;
+}
+
+// static
+QuicPacketCount QuicConnectionPeer::GetPacketsBetweenMtuProbes(
+ QuicConnection* connection) {
+ return connection->packets_between_mtu_probes_;
+}
+
+// static
+void QuicConnectionPeer::SetPacketsBetweenMtuProbes(QuicConnection* connection,
+ QuicPacketCount packets) {
+ connection->packets_between_mtu_probes_ = packets;
+}
+
+// static
+void QuicConnectionPeer::SetNextMtuProbeAt(QuicConnection* connection,
+ QuicPacketNumber number) {
+ connection->next_mtu_probe_at_ = number;
+}
+
+// static
+void QuicConnectionPeer::SetAckMode(QuicConnection* connection,
+ AckMode ack_mode) {
+ if (connection->received_packet_manager_.decide_when_to_send_acks()) {
+ if (connection->use_uber_received_packet_manager_) {
+ for (auto& received_packet_manager :
+ connection->uber_received_packet_manager_
+ .received_packet_managers_) {
+ received_packet_manager.ack_mode_ = ack_mode;
+ }
+ } else {
+ connection->received_packet_manager_.ack_mode_ = ack_mode;
+ }
+ } else {
+ connection->ack_mode_ = ack_mode;
+ }
+}
+
+// static
+void QuicConnectionPeer::SetFastAckAfterQuiescence(
+ QuicConnection* connection,
+ bool fast_ack_after_quiescence) {
+ if (connection->received_packet_manager_.decide_when_to_send_acks()) {
+ if (connection->use_uber_received_packet_manager_) {
+ for (auto& received_packet_manager :
+ connection->uber_received_packet_manager_
+ .received_packet_managers_) {
+ received_packet_manager.fast_ack_after_quiescence_ =
+ fast_ack_after_quiescence;
+ }
+ } else {
+ connection->received_packet_manager_.fast_ack_after_quiescence_ =
+ fast_ack_after_quiescence;
+ }
+ } else {
+ connection->fast_ack_after_quiescence_ = fast_ack_after_quiescence;
+ }
+}
+
+// static
+void QuicConnectionPeer::SetAckDecimationDelay(QuicConnection* connection,
+ float ack_decimation_delay) {
+ if (connection->received_packet_manager_.decide_when_to_send_acks()) {
+ if (connection->use_uber_received_packet_manager_) {
+ for (auto& received_packet_manager :
+ connection->uber_received_packet_manager_
+ .received_packet_managers_) {
+ received_packet_manager.ack_decimation_delay_ = ack_decimation_delay;
+ }
+ } else {
+ connection->received_packet_manager_.ack_decimation_delay_ =
+ ack_decimation_delay;
+ }
+ } else {
+ connection->ack_decimation_delay_ = ack_decimation_delay;
+ }
+}
+
+// static
+bool QuicConnectionPeer::HasRetransmittableFrames(QuicConnection* connection,
+ uint64_t packet_number) {
+ return QuicSentPacketManagerPeer::HasRetransmittableFrames(
+ GetSentPacketManager(connection), packet_number);
+}
+
+// static
+bool QuicConnectionPeer::GetNoStopWaitingFrames(QuicConnection* connection) {
+ return connection->no_stop_waiting_frames_;
+}
+
+// static
+void QuicConnectionPeer::SetNoStopWaitingFrames(QuicConnection* connection,
+ bool no_stop_waiting_frames) {
+ connection->no_stop_waiting_frames_ = no_stop_waiting_frames;
+}
+
+// static
+void QuicConnectionPeer::SetMaxTrackedPackets(
+ QuicConnection* connection,
+ QuicPacketCount max_tracked_packets) {
+ connection->max_tracked_packets_ = max_tracked_packets;
+}
+
+// static
+void QuicConnectionPeer::SetSessionDecidesWhatToWrite(
+ QuicConnection* connection) {
+ connection->sent_packet_manager_.SetSessionDecideWhatToWrite(true);
+ connection->packet_generator_.SetCanSetTransmissionType(true);
+}
+
+// static
+void QuicConnectionPeer::SetNegotiatedVersion(QuicConnection* connection) {
+ connection->version_negotiation_state_ = QuicConnection::NEGOTIATED_VERSION;
+}
+
+// static
+void QuicConnectionPeer::SetMaxConsecutiveNumPacketsWithNoRetransmittableFrames(
+ QuicConnection* connection,
+ size_t new_value) {
+ connection->max_consecutive_num_packets_with_no_retransmittable_frames_ =
+ new_value;
+}
+
+// static
+void QuicConnectionPeer::SetNoVersionNegotiation(QuicConnection* connection,
+ bool no_version_negotiation) {
+ *const_cast<bool*>(&connection->no_version_negotiation_) =
+ no_version_negotiation;
+}
+
+// static
+bool QuicConnectionPeer::SupportsReleaseTime(QuicConnection* connection) {
+ return connection->supports_release_time_;
+}
+
+// static
+QuicConnection::PacketContent QuicConnectionPeer::GetCurrentPacketContent(
+ QuicConnection* connection) {
+ return connection->current_packet_content_;
+}
+
+// static
+void QuicConnectionPeer::SetLastHeaderFormat(QuicConnection* connection,
+ PacketHeaderFormat format) {
+ connection->last_header_.form = format;
+}
+
+} // 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
new file mode 100644
index 00000000000..5df9cd59438
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h
@@ -0,0 +1,148 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+struct QuicPacketHeader;
+class QuicAlarm;
+class QuicConnectionHelperInterface;
+class QuicConnectionVisitorInterface;
+class QuicEncryptedPacket;
+class QuicFramer;
+class QuicPacketCreator;
+class QuicPacketGenerator;
+class QuicPacketWriter;
+class QuicSentPacketManager;
+class SendAlgorithmInterface;
+
+namespace test {
+
+// Peer to make public a number of otherwise private QuicConnection methods.
+class QuicConnectionPeer {
+ public:
+ QuicConnectionPeer() = delete;
+
+ static void SendAck(QuicConnection* connection);
+
+ static void SetSendAlgorithm(QuicConnection* connection,
+ SendAlgorithmInterface* send_algorithm);
+
+ static void SetLossAlgorithm(QuicConnection* connection,
+ LossDetectionInterface* loss_algorithm);
+
+ static const QuicFrame GetUpdatedAckFrame(QuicConnection* connection);
+
+ static void PopulateStopWaitingFrame(QuicConnection* connection,
+ QuicStopWaitingFrame* stop_waiting);
+
+ static QuicConnectionVisitorInterface* GetVisitor(QuicConnection* connection);
+
+ static QuicPacketCreator* GetPacketCreator(QuicConnection* connection);
+
+ static QuicPacketGenerator* GetPacketGenerator(QuicConnection* connection);
+
+ static QuicSentPacketManager* GetSentPacketManager(
+ QuicConnection* connection);
+
+ static QuicTime::Delta GetNetworkTimeout(QuicConnection* connection);
+
+ static void SetPerspective(QuicConnection* connection,
+ Perspective perspective);
+
+ static void SetSelfAddress(QuicConnection* connection,
+ const QuicSocketAddress& self_address);
+
+ static void SetPeerAddress(QuicConnection* connection,
+ const QuicSocketAddress& peer_address);
+
+ static void SetDirectPeerAddress(
+ QuicConnection* connection,
+ const QuicSocketAddress& direct_peer_address);
+
+ static void SetEffectivePeerAddress(
+ QuicConnection* connection,
+ const QuicSocketAddress& effective_peer_address);
+
+ static bool IsSilentCloseEnabled(QuicConnection* connection);
+
+ static void SwapCrypters(QuicConnection* connection, QuicFramer* framer);
+
+ static void SetCurrentPacket(QuicConnection* connection,
+ QuicStringPiece current_packet);
+
+ static QuicConnectionHelperInterface* GetHelper(QuicConnection* connection);
+
+ static QuicAlarmFactory* GetAlarmFactory(QuicConnection* connection);
+
+ static QuicFramer* GetFramer(QuicConnection* connection);
+
+ static QuicAlarm* GetAckAlarm(QuicConnection* connection);
+ static QuicAlarm* GetPingAlarm(QuicConnection* connection);
+ static QuicAlarm* GetRetransmissionAlarm(QuicConnection* connection);
+ static QuicAlarm* GetSendAlarm(QuicConnection* connection);
+ static QuicAlarm* GetTimeoutAlarm(QuicConnection* connection);
+ static QuicAlarm* GetMtuDiscoveryAlarm(QuicConnection* connection);
+ static QuicAlarm* GetPathDegradingAlarm(QuicConnection* connection);
+ static QuicAlarm* GetProcessUndecryptablePacketsAlarm(
+ QuicConnection* connection);
+
+ static QuicPacketWriter* GetWriter(QuicConnection* connection);
+ // If |owns_writer| is true, takes ownership of |writer|.
+ static void SetWriter(QuicConnection* connection,
+ QuicPacketWriter* writer,
+ bool owns_writer);
+ static void TearDownLocalConnectionState(QuicConnection* connection);
+ static QuicEncryptedPacket* GetConnectionClosePacket(
+ QuicConnection* connection);
+
+ static QuicPacketHeader* GetLastHeader(QuicConnection* connection);
+
+ static QuicConnectionStats* GetStats(QuicConnection* connection);
+
+ static QuicPacketCount GetPacketsBetweenMtuProbes(QuicConnection* connection);
+
+ static void SetPacketsBetweenMtuProbes(QuicConnection* connection,
+ QuicPacketCount packets);
+ static void SetNextMtuProbeAt(QuicConnection* connection,
+ QuicPacketNumber number);
+ static void SetAckMode(QuicConnection* connection, AckMode ack_mode);
+ static void SetFastAckAfterQuiescence(QuicConnection* connection,
+ bool fast_ack_after_quiescence);
+ static void SetAckDecimationDelay(QuicConnection* connection,
+ float ack_decimation_delay);
+ static bool HasRetransmittableFrames(QuicConnection* connection,
+ uint64_t packet_number);
+ static bool GetNoStopWaitingFrames(QuicConnection* connection);
+ static void SetNoStopWaitingFrames(QuicConnection* connection,
+ bool no_stop_waiting_frames);
+ static void SetMaxTrackedPackets(QuicConnection* connection,
+ QuicPacketCount max_tracked_packets);
+ static void SetSessionDecidesWhatToWrite(QuicConnection* connection);
+ static void SetNegotiatedVersion(QuicConnection* connection);
+ static void SetMaxConsecutiveNumPacketsWithNoRetransmittableFrames(
+ QuicConnection* connection,
+ size_t new_value);
+ static void SetNoVersionNegotiation(QuicConnection* connection,
+ bool no_version_negotiation);
+ static bool SupportsReleaseTime(QuicConnection* connection);
+ static QuicConnection::PacketContent GetCurrentPacketContent(
+ QuicConnection* connection);
+ static void SetLastHeaderFormat(QuicConnection* connection,
+ PacketHeaderFormat format);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.cc
new file mode 100644
index 00000000000..be2d3386bb8
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.cc
@@ -0,0 +1,162 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
+
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+
+QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfigPeer::GetPrimaryConfig() {
+ QuicReaderMutexLock locked(&server_config_->configs_lock_);
+ return QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>(
+ server_config_->primary_config_);
+}
+
+QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfigPeer::GetConfig(std::string config_id) {
+ QuicReaderMutexLock locked(&server_config_->configs_lock_);
+ if (config_id == "<primary>") {
+ return QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>(
+ server_config_->primary_config_);
+ } else {
+ return server_config_->GetConfigWithScid(config_id);
+ }
+}
+
+ProofSource* QuicCryptoServerConfigPeer::GetProofSource() const {
+ return server_config_->proof_source_.get();
+}
+
+void QuicCryptoServerConfigPeer::ResetProofSource(
+ std::unique_ptr<ProofSource> proof_source) {
+ server_config_->proof_source_ = std::move(proof_source);
+}
+
+std::string QuicCryptoServerConfigPeer::NewSourceAddressToken(
+ std::string config_id,
+ SourceAddressTokens previous_tokens,
+ const QuicIpAddress& ip,
+ QuicRandom* rand,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params) {
+ return server_config_->NewSourceAddressToken(*GetConfig(config_id),
+ previous_tokens, ip, rand, now,
+ cached_network_params);
+}
+
+HandshakeFailureReason QuicCryptoServerConfigPeer::ValidateSourceAddressTokens(
+ std::string config_id,
+ QuicStringPiece srct,
+ const QuicIpAddress& ip,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params) {
+ SourceAddressTokens tokens;
+ HandshakeFailureReason reason = server_config_->ParseSourceAddressToken(
+ *GetConfig(config_id), srct, &tokens);
+ if (reason != HANDSHAKE_OK) {
+ return reason;
+ }
+
+ return server_config_->ValidateSourceAddressTokens(tokens, ip, now,
+ cached_network_params);
+}
+
+HandshakeFailureReason
+QuicCryptoServerConfigPeer::ValidateSingleSourceAddressToken(
+ QuicStringPiece token,
+ const QuicIpAddress& ip,
+ QuicWallTime now) {
+ SourceAddressTokens tokens;
+ HandshakeFailureReason parse_status = server_config_->ParseSourceAddressToken(
+ *GetPrimaryConfig(), token, &tokens);
+ if (HANDSHAKE_OK != parse_status) {
+ return parse_status;
+ }
+ EXPECT_EQ(1, tokens.tokens_size());
+ return server_config_->ValidateSingleSourceAddressToken(tokens.tokens(0), ip,
+ now);
+}
+
+void QuicCryptoServerConfigPeer::CheckConfigs(
+ std::vector<std::pair<std::string, bool>> expected_ids_and_status) {
+ QuicReaderMutexLock locked(&server_config_->configs_lock_);
+
+ ASSERT_EQ(expected_ids_and_status.size(), server_config_->configs_.size())
+ << ConfigsDebug();
+
+ for (const std::pair<
+ const ServerConfigID,
+ QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>>& i :
+ server_config_->configs_) {
+ bool found = false;
+ for (std::pair<ServerConfigID, bool>& j : expected_ids_and_status) {
+ if (i.first == j.first && i.second->is_primary == j.second) {
+ found = true;
+ j.first.clear();
+ break;
+ }
+ }
+
+ ASSERT_TRUE(found) << "Failed to find match for " << i.first
+ << " in configs:\n"
+ << ConfigsDebug();
+ }
+}
+
+// ConfigsDebug returns a std::string that contains debugging information about
+// the set of Configs loaded in |server_config_| and their status.
+std::string QuicCryptoServerConfigPeer::ConfigsDebug() {
+ if (server_config_->configs_.empty()) {
+ return "No Configs in QuicCryptoServerConfig";
+ }
+
+ std::string s;
+
+ for (const auto& i : server_config_->configs_) {
+ const QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> config =
+ i.second;
+ if (config->is_primary) {
+ s += "(primary) ";
+ } else {
+ s += " ";
+ }
+ s += config->id;
+ s += "\n";
+ }
+
+ return s;
+}
+
+void QuicCryptoServerConfigPeer::SelectNewPrimaryConfig(int seconds) {
+ QuicWriterMutexLock locked(&server_config_->configs_lock_);
+ server_config_->SelectNewPrimaryConfig(
+ QuicWallTime::FromUNIXSeconds(seconds));
+}
+
+std::string QuicCryptoServerConfigPeer::CompressChain(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes,
+ const CommonCertSets* common_sets) {
+ return QuicCryptoServerConfig::CompressChain(
+ compressed_certs_cache, chain, client_common_set_hashes,
+ client_cached_cert_hashes, common_sets);
+}
+
+uint32_t QuicCryptoServerConfigPeer::source_address_token_future_secs() {
+ return server_config_->source_address_token_future_secs_;
+}
+
+uint32_t QuicCryptoServerConfigPeer::source_address_token_lifetime_secs() {
+ return server_config_->source_address_token_lifetime_secs_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h
new file mode 100644
index 00000000000..3f5a9840a33
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h
@@ -0,0 +1,98 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_CRYPTO_SERVER_CONFIG_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_CRYPTO_SERVER_CONFIG_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+
+// Peer for accessing otherwise private members of a QuicCryptoServerConfig.
+class QuicCryptoServerConfigPeer {
+ public:
+ explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
+ : server_config_(server_config) {}
+
+ // Returns the primary config.
+ QuicReferenceCountedPointer<QuicCryptoServerConfig::Config>
+ GetPrimaryConfig();
+
+ // Returns the config associated with |config_id|.
+ QuicReferenceCountedPointer<QuicCryptoServerConfig::Config> GetConfig(
+ std::string config_id);
+
+ // Returns a pointer to the ProofSource object.
+ ProofSource* GetProofSource() const;
+
+ // Reset the proof_source_ member.
+ void ResetProofSource(std::unique_ptr<ProofSource> proof_source);
+
+ // Generates a new valid source address token.
+ std::string NewSourceAddressToken(
+ std::string config_id,
+ SourceAddressTokens previous_tokens,
+ const QuicIpAddress& ip,
+ QuicRandom* rand,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params);
+
+ // Attempts to validate the tokens in |tokens|.
+ HandshakeFailureReason ValidateSourceAddressTokens(
+ std::string config_id,
+ QuicStringPiece tokens,
+ const QuicIpAddress& ip,
+ QuicWallTime now,
+ CachedNetworkParameters* cached_network_params);
+
+ // Attempts to validate the single source address token in |token|.
+ HandshakeFailureReason ValidateSingleSourceAddressToken(
+ QuicStringPiece token,
+ const QuicIpAddress& ip,
+ QuicWallTime now);
+
+ // CheckConfigs compares the state of the Configs in |server_config_| to the
+ // description given as arguments.
+ // The first of each pair is the server config ID of a Config. The second is a
+ // boolean describing whether the config is the primary. For example:
+ // CheckConfigs(std::vector<std::pair<ServerConfigID, bool>>()); // checks
+ // that no Configs are loaded.
+ //
+ // // Checks that exactly three Configs are loaded with the given IDs and
+ // // status.
+ // CheckConfigs(
+ // {{"id1", false},
+ // {"id2", true},
+ // {"id3", false}});
+ void CheckConfigs(
+ std::vector<std::pair<ServerConfigID, bool>> expected_ids_and_status);
+
+ // ConfigsDebug returns a std::string that contains debugging information
+ // about the set of Configs loaded in |server_config_| and their status.
+ std::string ConfigsDebug()
+ SHARED_LOCKS_REQUIRED(server_config_->configs_lock_);
+
+ void SelectNewPrimaryConfig(int seconds);
+
+ static std::string CompressChain(
+ QuicCompressedCertsCache* compressed_certs_cache,
+ const QuicReferenceCountedPointer<ProofSource::Chain>& chain,
+ const std::string& client_common_set_hashes,
+ const std::string& client_cached_cert_hashes,
+ const CommonCertSets* common_sets);
+
+ uint32_t source_address_token_future_secs();
+
+ uint32_t source_address_token_lifetime_secs();
+
+ private:
+ QuicCryptoServerConfig* server_config_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_CRYPTO_SERVER_CONFIG_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.cc
new file mode 100644
index 00000000000..2590834891c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicDispatcherPeer::SetTimeWaitListManager(
+ QuicDispatcher* dispatcher,
+ QuicTimeWaitListManager* time_wait_list_manager) {
+ dispatcher->time_wait_list_manager_.reset(time_wait_list_manager);
+}
+
+// static
+void QuicDispatcherPeer::UseWriter(QuicDispatcher* dispatcher,
+ QuicPacketWriterWrapper* writer) {
+ writer->set_writer(dispatcher->writer_.release());
+ dispatcher->writer_.reset(writer);
+}
+
+// static
+QuicPacketWriter* QuicDispatcherPeer::GetWriter(QuicDispatcher* dispatcher) {
+ return dispatcher->writer_.get();
+}
+
+// static
+QuicCompressedCertsCache* QuicDispatcherPeer::GetCache(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->compressed_certs_cache();
+}
+
+// static
+QuicConnectionHelperInterface* QuicDispatcherPeer::GetHelper(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->helper_.get();
+}
+
+// static
+QuicAlarmFactory* QuicDispatcherPeer::GetAlarmFactory(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->alarm_factory_.get();
+}
+
+// static
+QuicDispatcher::WriteBlockedList* QuicDispatcherPeer::GetWriteBlockedList(
+ QuicDispatcher* dispatcher) {
+ return &dispatcher->write_blocked_list_;
+}
+
+// static
+QuicErrorCode QuicDispatcherPeer::GetAndClearLastError(
+ QuicDispatcher* dispatcher) {
+ QuicErrorCode ret = dispatcher->last_error_;
+ dispatcher->last_error_ = QUIC_NO_ERROR;
+ return ret;
+}
+
+// static
+QuicBufferedPacketStore* QuicDispatcherPeer::GetBufferedPackets(
+ QuicDispatcher* dispatcher) {
+ return &(dispatcher->buffered_packets_);
+}
+
+// static
+const QuicDispatcher::SessionMap& QuicDispatcherPeer::session_map(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->session_map();
+}
+
+// static
+void QuicDispatcherPeer::set_new_sessions_allowed_per_event_loop(
+ QuicDispatcher* dispatcher,
+ size_t num_session_allowed) {
+ return dispatcher->set_new_sessions_allowed_per_event_loop(
+ num_session_allowed);
+}
+
+// static
+void QuicDispatcherPeer::SendPublicReset(
+ QuicDispatcher* dispatcher,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ std::unique_ptr<QuicPerPacketContext> packet_context) {
+ dispatcher->time_wait_list_manager()->SendPublicReset(
+ self_address, peer_address, connection_id, ietf_quic,
+ std::move(packet_context));
+}
+
+// static
+std::unique_ptr<QuicPerPacketContext> QuicDispatcherPeer::GetPerPacketContext(
+ QuicDispatcher* dispatcher) {
+ return dispatcher->GetPerPacketContext();
+}
+
+// static
+void QuicDispatcherPeer::RestorePerPacketContext(
+ QuicDispatcher* dispatcher,
+ std::unique_ptr<QuicPerPacketContext> context) {
+ dispatcher->RestorePerPacketContext(std::move(context));
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h
new file mode 100644
index 00000000000..a888b4e0967
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_dispatcher_peer.h
@@ -0,0 +1,71 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+
+namespace quic {
+
+class QuicPacketWriterWrapper;
+
+namespace test {
+
+class QuicDispatcherPeer {
+ public:
+ QuicDispatcherPeer() = delete;
+
+ static void SetTimeWaitListManager(
+ QuicDispatcher* dispatcher,
+ QuicTimeWaitListManager* time_wait_list_manager);
+
+ // Injects |writer| into |dispatcher| as the shared writer.
+ static void UseWriter(QuicDispatcher* dispatcher,
+ QuicPacketWriterWrapper* writer);
+
+ static QuicPacketWriter* GetWriter(QuicDispatcher* dispatcher);
+
+ static QuicCompressedCertsCache* GetCache(QuicDispatcher* dispatcher);
+
+ static QuicConnectionHelperInterface* GetHelper(QuicDispatcher* dispatcher);
+
+ static QuicAlarmFactory* GetAlarmFactory(QuicDispatcher* dispatcher);
+
+ static QuicDispatcher::WriteBlockedList* GetWriteBlockedList(
+ QuicDispatcher* dispatcher);
+
+ // Get the dispatcher's record of the last error reported to its framer
+ // visitor's OnError() method. Then set that record to QUIC_NO_ERROR.
+ static QuicErrorCode GetAndClearLastError(QuicDispatcher* dispatcher);
+
+ static QuicBufferedPacketStore* GetBufferedPackets(
+ QuicDispatcher* dispatcher);
+
+ static const QuicDispatcher::SessionMap& session_map(
+ QuicDispatcher* dispatcher);
+
+ static void set_new_sessions_allowed_per_event_loop(
+ QuicDispatcher* dispatcher,
+ size_t num_session_allowed);
+
+ static void SendPublicReset(
+ QuicDispatcher* dispatcher,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId connection_id,
+ bool ietf_quic,
+ std::unique_ptr<QuicPerPacketContext> packet_context);
+
+ static std::unique_ptr<QuicPerPacketContext> GetPerPacketContext(
+ QuicDispatcher* dispatcher);
+
+ static void RestorePerPacketContext(QuicDispatcher* dispatcher,
+ std::unique_ptr<QuicPerPacketContext>);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_DISPATCHER_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.cc
new file mode 100644
index 00000000000..32efe459379
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h"
+
+#include <list>
+
+#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicFlowControllerPeer::SetSendWindowOffset(
+ QuicFlowController* flow_controller,
+ QuicStreamOffset offset) {
+ flow_controller->send_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetReceiveWindowOffset(
+ QuicFlowController* flow_controller,
+ QuicStreamOffset offset) {
+ flow_controller->receive_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetMaxReceiveWindow(
+ QuicFlowController* flow_controller,
+ QuicByteCount window_size) {
+ flow_controller->receive_window_size_ = window_size;
+}
+
+// static
+QuicStreamOffset QuicFlowControllerPeer::SendWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->send_window_offset_;
+}
+
+// static
+QuicByteCount QuicFlowControllerPeer::SendWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->SendWindowSize();
+}
+
+// static
+QuicStreamOffset QuicFlowControllerPeer::ReceiveWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_;
+}
+
+// static
+QuicByteCount QuicFlowControllerPeer::ReceiveWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_ -
+ flow_controller->highest_received_byte_offset_;
+}
+
+// static
+QuicByteCount QuicFlowControllerPeer::WindowUpdateThreshold(
+ QuicFlowController* flow_controller) {
+ return flow_controller->WindowUpdateThreshold();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h
new file mode 100644
index 00000000000..631871ade25
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_flow_controller_peer.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicFlowController;
+
+namespace test {
+
+class QuicFlowControllerPeer {
+ public:
+ QuicFlowControllerPeer() = delete;
+
+ static void SetSendWindowOffset(QuicFlowController* flow_controller,
+ QuicStreamOffset offset);
+
+ static void SetReceiveWindowOffset(QuicFlowController* flow_controller,
+ QuicStreamOffset offset);
+
+ static void SetMaxReceiveWindow(QuicFlowController* flow_controller,
+ QuicByteCount window_size);
+
+ static QuicStreamOffset SendWindowOffset(QuicFlowController* flow_controller);
+
+ static QuicByteCount SendWindowSize(QuicFlowController* flow_controller);
+
+ static QuicStreamOffset ReceiveWindowOffset(
+ QuicFlowController* flow_controller);
+
+ static QuicByteCount ReceiveWindowSize(QuicFlowController* flow_controller);
+
+ static QuicByteCount WindowUpdateThreshold(
+ QuicFlowController* flow_controller);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
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
new file mode 100644
index 00000000000..13b315000ea
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.cc
@@ -0,0 +1,355 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+
+namespace quic {
+namespace test {
+
+// static
+uint64_t QuicFramerPeer::CalculatePacketNumberFromWire(
+ QuicFramer* framer,
+ QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber last_packet_number,
+ uint64_t packet_number) {
+ return framer->CalculatePacketNumberFromWire(
+ packet_number_length, last_packet_number, packet_number);
+}
+
+// static
+void QuicFramerPeer::SetLastSerializedConnectionId(
+ QuicFramer* framer,
+ QuicConnectionId connection_id) {
+ framer->last_serialized_connection_id_ = connection_id;
+}
+
+// static
+void QuicFramerPeer::SetLargestPacketNumber(QuicFramer* framer,
+ QuicPacketNumber packet_number) {
+ framer->largest_packet_number_ = packet_number;
+}
+
+// static
+void QuicFramerPeer::SetPerspective(QuicFramer* framer,
+ Perspective perspective) {
+ framer->perspective_ = perspective;
+ framer->infer_packet_header_type_from_version_ =
+ perspective == Perspective::IS_CLIENT;
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfStreamFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ uint8_t frame_type,
+ QuicStreamFrame* frame) {
+ return framer->ProcessIetfStreamFrame(reader, frame_type, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfStreamFrame(QuicFramer* framer,
+ const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfStreamFrame(frame, last_frame_in_packet, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessCryptoFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicCryptoFrame* frame) {
+ return framer->ProcessCryptoFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendCryptoFrame(QuicFramer* framer,
+ const QuicCryptoFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendCryptoFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfAckFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ uint64_t frame_type,
+ QuicAckFrame* ack_frame) {
+ return framer->ProcessIetfAckFrame(reader, frame_type, ack_frame);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfAckFrameAndTypeByte(QuicFramer* framer,
+ const QuicAckFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfAckFrameAndTypeByte(frame, writer);
+}
+// static
+size_t QuicFramerPeer::GetIetfAckFrameSize(QuicFramer* framer,
+ const QuicAckFrame& frame) {
+ return framer->GetIetfAckFrameSize(frame);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfConnectionCloseFrame(
+ QuicFramer* framer,
+ const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfConnectionCloseFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfConnectionCloseFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicConnectionCloseType type,
+ QuicConnectionCloseFrame* frame) {
+ return framer->ProcessIetfConnectionCloseFrame(reader, type, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessPathChallengeFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicPathChallengeFrame* frame) {
+ return framer->ProcessPathChallengeFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessPathResponseFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicPathResponseFrame* frame) {
+ return framer->ProcessPathResponseFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendPathChallengeFrame(
+ QuicFramer* framer,
+ const QuicPathChallengeFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendPathChallengeFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendPathResponseFrame(QuicFramer* framer,
+ const QuicPathResponseFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendPathResponseFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfResetStreamFrame(QuicFramer* framer,
+ const QuicRstStreamFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfResetStreamFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfResetStreamFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicRstStreamFrame* frame) {
+ return framer->ProcessIetfResetStreamFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessStopSendingFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicStopSendingFrame* stop_sending_frame) {
+ return framer->ProcessStopSendingFrame(reader, stop_sending_frame);
+}
+
+// static
+bool QuicFramerPeer::AppendStopSendingFrame(
+ QuicFramer* framer,
+ const QuicStopSendingFrame& stop_sending_frame,
+ QuicDataWriter* writer) {
+ return framer->AppendStopSendingFrame(stop_sending_frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendMaxDataFrame(QuicFramer* framer,
+ const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendMaxDataFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::AppendMaxStreamDataFrame(
+ QuicFramer* framer,
+ const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendMaxStreamDataFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessMaxDataFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame) {
+ return framer->ProcessMaxDataFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::ProcessMaxStreamDataFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame) {
+ return framer->ProcessMaxStreamDataFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendMaxStreamsFrame(QuicFramer* framer,
+ const QuicMaxStreamIdFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendMaxStreamsFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessMaxStreamsFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicMaxStreamIdFrame* frame,
+ uint64_t frame_type) {
+ return framer->ProcessMaxStreamsFrame(reader, frame, frame_type);
+}
+
+// static
+bool QuicFramerPeer::AppendIetfBlockedFrame(QuicFramer* framer,
+ const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendIetfBlockedFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessIetfBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicBlockedFrame* frame) {
+ return framer->ProcessIetfBlockedFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendStreamBlockedFrame(QuicFramer* framer,
+ const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendStreamBlockedFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessStreamBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicBlockedFrame* frame) {
+ return framer->ProcessStreamBlockedFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendStreamsBlockedFrame(
+ QuicFramer* framer,
+ const QuicStreamIdBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendStreamsBlockedFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessStreamsBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicStreamIdBlockedFrame* frame,
+ uint64_t frame_type) {
+ return framer->ProcessStreamsBlockedFrame(reader, frame, frame_type);
+}
+
+// static
+bool QuicFramerPeer::AppendNewConnectionIdFrame(
+ QuicFramer* framer,
+ const QuicNewConnectionIdFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendNewConnectionIdFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessNewConnectionIdFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicNewConnectionIdFrame* frame) {
+ return framer->ProcessNewConnectionIdFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendRetireConnectionIdFrame(
+ QuicFramer* framer,
+ const QuicRetireConnectionIdFrame& frame,
+ QuicDataWriter* writer) {
+ return framer->AppendRetireConnectionIdFrame(frame, writer);
+}
+
+// static
+bool QuicFramerPeer::ProcessRetireConnectionIdFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicRetireConnectionIdFrame* frame) {
+ return framer->ProcessRetireConnectionIdFrame(reader, frame);
+}
+
+// static
+void QuicFramerPeer::SwapCrypters(QuicFramer* framer1, QuicFramer* framer2) {
+ for (int i = ENCRYPTION_INITIAL; i < NUM_ENCRYPTION_LEVELS; i++) {
+ framer1->encrypter_[i].swap(framer2->encrypter_[i]);
+ framer1->decrypter_[i].swap(framer2->decrypter_[i]);
+ }
+
+ EncryptionLevel framer2_level = framer2->decrypter_level_;
+ framer2->decrypter_level_ = framer1->decrypter_level_;
+ framer1->decrypter_level_ = framer2_level;
+ framer2_level = framer2->alternative_decrypter_level_;
+ framer2->alternative_decrypter_level_ = framer1->alternative_decrypter_level_;
+ framer1->alternative_decrypter_level_ = framer2_level;
+
+ const bool framer2_latch = framer2->alternative_decrypter_latch_;
+ framer2->alternative_decrypter_latch_ = framer1->alternative_decrypter_latch_;
+ framer1->alternative_decrypter_latch_ = framer2_latch;
+}
+
+// static
+QuicEncrypter* QuicFramerPeer::GetEncrypter(QuicFramer* framer,
+ EncryptionLevel level) {
+ return framer->encrypter_[level].get();
+}
+
+// static
+QuicDecrypter* QuicFramerPeer::GetDecrypter(QuicFramer* framer,
+ EncryptionLevel level) {
+ return framer->decrypter_[level].get();
+}
+
+// static
+size_t QuicFramerPeer::ComputeFrameLength(
+ QuicFramer* framer,
+ const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicPacketNumberLength packet_number_length) {
+ return framer->ComputeFrameLength(frame, last_frame_in_packet,
+ packet_number_length);
+}
+
+// static
+void QuicFramerPeer::SetFirstSendingPacketNumber(QuicFramer* framer,
+ uint64_t packet_number) {
+ *const_cast<QuicPacketNumber*>(&framer->first_sending_packet_number_) =
+ QuicPacketNumber(packet_number);
+}
+
+// static
+void QuicFramerPeer::SetExpectedConnectionIDLength(
+ QuicFramer* framer,
+ uint8_t expected_connection_id_length) {
+ *const_cast<uint8_t*>(&framer->expected_connection_id_length_) =
+ expected_connection_id_length;
+}
+
+// static
+QuicPacketNumber QuicFramerPeer::GetLargestDecryptedPacketNumber(
+ QuicFramer* framer,
+ PacketNumberSpace packet_number_space) {
+ return framer->largest_decrypted_packet_numbers_[packet_number_space];
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h
new file mode 100644
index 00000000000..7b23189af24
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h
@@ -0,0 +1,174 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+namespace test {
+
+class QuicFramerPeer {
+ public:
+ QuicFramerPeer() = delete;
+
+ static uint64_t CalculatePacketNumberFromWire(
+ QuicFramer* framer,
+ QuicPacketNumberLength packet_number_length,
+ QuicPacketNumber last_packet_number,
+ uint64_t packet_number);
+ static void SetLastSerializedConnectionId(QuicFramer* framer,
+ QuicConnectionId connection_id);
+ static void SetLargestPacketNumber(QuicFramer* framer,
+ QuicPacketNumber packet_number);
+ static void SetPerspective(QuicFramer* framer, Perspective perspective);
+
+ // SwapCrypters exchanges the state of the crypters of |framer1| with
+ // |framer2|.
+ static void SwapCrypters(QuicFramer* framer1, QuicFramer* framer2);
+
+ static QuicEncrypter* GetEncrypter(QuicFramer* framer, EncryptionLevel level);
+ static QuicDecrypter* GetDecrypter(QuicFramer* framer, EncryptionLevel level);
+
+ // IETF defined frame append/process methods.
+ static bool ProcessIetfStreamFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ uint8_t frame_type,
+ QuicStreamFrame* frame);
+ static bool AppendIetfStreamFrame(QuicFramer* framer,
+ const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer);
+ static bool ProcessCryptoFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicCryptoFrame* frame);
+ static bool AppendCryptoFrame(QuicFramer* framer,
+ const QuicCryptoFrame& frame,
+ QuicDataWriter* writer);
+
+ static bool AppendIetfConnectionCloseFrame(
+ QuicFramer* framer,
+ const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessIetfConnectionCloseFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicConnectionCloseType type,
+ QuicConnectionCloseFrame* frame);
+ static bool ProcessIetfAckFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ uint64_t frame_type,
+ QuicAckFrame* ack_frame);
+ static bool AppendIetfAckFrameAndTypeByte(QuicFramer* framer,
+ const QuicAckFrame& frame,
+ QuicDataWriter* writer);
+ static size_t GetIetfAckFrameSize(QuicFramer* framer,
+ const QuicAckFrame& frame);
+ static bool AppendIetfResetStreamFrame(QuicFramer* framer,
+ const QuicRstStreamFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessIetfResetStreamFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicRstStreamFrame* frame);
+
+ static bool ProcessPathChallengeFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicPathChallengeFrame* frame);
+ static bool ProcessPathResponseFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicPathResponseFrame* frame);
+
+ static bool AppendPathChallengeFrame(QuicFramer* framer,
+ const QuicPathChallengeFrame& frame,
+ QuicDataWriter* writer);
+ static bool AppendPathResponseFrame(QuicFramer* framer,
+ const QuicPathResponseFrame& frame,
+ QuicDataWriter* writer);
+
+ static bool ProcessStopSendingFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicStopSendingFrame* stop_sending_frame);
+ static bool AppendStopSendingFrame(
+ QuicFramer* framer,
+ const QuicStopSendingFrame& stop_sending_frame,
+ QuicDataWriter* writer);
+
+ // Append/consume IETF-Format MAX_DATA and MAX_STREAM_DATA frames
+ static bool AppendMaxDataFrame(QuicFramer* framer,
+ const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ static bool AppendMaxStreamDataFrame(QuicFramer* framer,
+ const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessMaxDataFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame);
+ static bool ProcessMaxStreamDataFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicWindowUpdateFrame* frame);
+ static bool AppendMaxStreamsFrame(QuicFramer* framer,
+ const QuicMaxStreamIdFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessMaxStreamsFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicMaxStreamIdFrame* frame,
+ uint64_t frame_type);
+ static bool AppendIetfBlockedFrame(QuicFramer* framer,
+ const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessIetfBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicBlockedFrame* frame);
+
+ static bool AppendStreamBlockedFrame(QuicFramer* framer,
+ const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessStreamBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicBlockedFrame* frame);
+
+ static bool AppendStreamsBlockedFrame(QuicFramer* framer,
+ const QuicStreamIdBlockedFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessStreamsBlockedFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicStreamIdBlockedFrame* frame,
+ uint64_t frame_type);
+
+ static bool AppendNewConnectionIdFrame(QuicFramer* framer,
+ const QuicNewConnectionIdFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessNewConnectionIdFrame(QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicNewConnectionIdFrame* frame);
+ static bool AppendRetireConnectionIdFrame(
+ QuicFramer* framer,
+ const QuicRetireConnectionIdFrame& frame,
+ QuicDataWriter* writer);
+ static bool ProcessRetireConnectionIdFrame(
+ QuicFramer* framer,
+ QuicDataReader* reader,
+ QuicRetireConnectionIdFrame* frame);
+ static size_t ComputeFrameLength(QuicFramer* framer,
+ const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicPacketNumberLength packet_number_length);
+ static void SetFirstSendingPacketNumber(QuicFramer* framer,
+ uint64_t packet_number);
+ static void SetExpectedConnectionIDLength(
+ QuicFramer* framer,
+ uint8_t expected_connection_id_length);
+ static QuicPacketNumber GetLargestDecryptedPacketNumber(
+ QuicFramer* framer,
+ PacketNumberSpace packet_number_space);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.cc
new file mode 100644
index 00000000000..c5dd747d0db
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+
+namespace quic {
+namespace test {
+
+// static
+bool QuicPacketCreatorPeer::SendVersionInPacket(QuicPacketCreator* creator) {
+ return creator->IncludeVersionInHeader();
+}
+
+// static
+void QuicPacketCreatorPeer::SetSendVersionInPacket(
+ QuicPacketCreator* creator,
+ bool send_version_in_packet) {
+ if (creator->framer_->transport_version() != QUIC_VERSION_99) {
+ creator->send_version_in_packet_ = send_version_in_packet;
+ return;
+ }
+ if (!send_version_in_packet) {
+ creator->packet_.encryption_level = ENCRYPTION_FORWARD_SECURE;
+ return;
+ }
+ DCHECK(creator->packet_.encryption_level < ENCRYPTION_FORWARD_SECURE);
+}
+
+// static
+void QuicPacketCreatorPeer::SetPacketNumberLength(
+ QuicPacketCreator* creator,
+ QuicPacketNumberLength packet_number_length) {
+ creator->packet_.packet_number_length = packet_number_length;
+}
+
+// static
+QuicPacketNumberLength QuicPacketCreatorPeer::GetPacketNumberLength(
+ QuicPacketCreator* creator) {
+ return creator->GetPacketNumberLength();
+}
+
+// static
+QuicVariableLengthIntegerLength
+QuicPacketCreatorPeer::GetRetryTokenLengthLength(QuicPacketCreator* creator) {
+ return creator->GetRetryTokenLengthLength();
+}
+
+// static
+QuicVariableLengthIntegerLength QuicPacketCreatorPeer::GetLengthLength(
+ QuicPacketCreator* creator) {
+ return creator->GetLengthLength();
+}
+
+void QuicPacketCreatorPeer::SetPacketNumber(QuicPacketCreator* creator,
+ uint64_t s) {
+ DCHECK_NE(0u, s);
+ creator->packet_.packet_number = QuicPacketNumber(s);
+}
+
+// static
+void QuicPacketCreatorPeer::ClearPacketNumber(QuicPacketCreator* creator) {
+ creator->packet_.packet_number.Clear();
+}
+
+// static
+void QuicPacketCreatorPeer::FillPacketHeader(QuicPacketCreator* creator,
+ QuicPacketHeader* header) {
+ creator->FillPacketHeader(header);
+}
+
+// static
+void QuicPacketCreatorPeer::CreateStreamFrame(QuicPacketCreator* creator,
+ QuicStreamId id,
+ size_t data_length,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicFrame* frame) {
+ creator->CreateStreamFrame(id, data_length, offset, fin, frame);
+}
+
+// static
+bool QuicPacketCreatorPeer::CreateCryptoFrame(QuicPacketCreator* creator,
+ EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset,
+ QuicFrame* frame) {
+ return creator->CreateCryptoFrame(level, write_length, offset, frame);
+}
+
+// static
+SerializedPacket QuicPacketCreatorPeer::SerializeAllFrames(
+ QuicPacketCreator* creator,
+ const QuicFrames& frames,
+ char* buffer,
+ size_t buffer_len) {
+ DCHECK(creator->queued_frames_.empty());
+ DCHECK(!frames.empty());
+ for (const QuicFrame& frame : frames) {
+ bool success = creator->AddFrame(frame, false, NOT_RETRANSMISSION);
+ DCHECK(success);
+ }
+ creator->SerializePacket(buffer, buffer_len);
+ SerializedPacket packet = creator->packet_;
+ // The caller takes ownership of the QuicEncryptedPacket.
+ creator->packet_.encrypted_buffer = nullptr;
+ DCHECK(packet.retransmittable_frames.empty());
+ return packet;
+}
+
+// static
+OwningSerializedPacketPointer
+QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
+ QuicPacketCreator* creator) {
+ return creator->SerializeConnectivityProbingPacket();
+}
+
+// static
+OwningSerializedPacketPointer
+QuicPacketCreatorPeer::SerializePathChallengeConnectivityProbingPacket(
+ QuicPacketCreator* creator,
+ QuicPathFrameBuffer* payload) {
+ return creator->SerializePathChallengeConnectivityProbingPacket(payload);
+}
+
+// static
+EncryptionLevel QuicPacketCreatorPeer::GetEncryptionLevel(
+ QuicPacketCreator* creator) {
+ return creator->packet_.encryption_level;
+}
+
+// static
+QuicFramer* QuicPacketCreatorPeer::framer(QuicPacketCreator* creator) {
+ return creator->framer_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h
new file mode 100644
index 00000000000..e040090f6dd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_creator_peer.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+class QuicFramer;
+class QuicPacketCreator;
+
+namespace test {
+
+class QuicPacketCreatorPeer {
+ public:
+ QuicPacketCreatorPeer() = delete;
+
+ static bool SendVersionInPacket(QuicPacketCreator* creator);
+
+ static void SetSendVersionInPacket(QuicPacketCreator* creator,
+ bool send_version_in_packet);
+ static void SetPacketNumberLength(
+ QuicPacketCreator* creator,
+ QuicPacketNumberLength packet_number_length);
+ static QuicPacketNumberLength GetPacketNumberLength(
+ QuicPacketCreator* creator);
+ static QuicVariableLengthIntegerLength GetRetryTokenLengthLength(
+ QuicPacketCreator* creator);
+ static QuicVariableLengthIntegerLength GetLengthLength(
+ QuicPacketCreator* creator);
+ static void SetPacketNumber(QuicPacketCreator* creator, uint64_t s);
+ static void ClearPacketNumber(QuicPacketCreator* creator);
+ static void FillPacketHeader(QuicPacketCreator* creator,
+ QuicPacketHeader* header);
+ static void CreateStreamFrame(QuicPacketCreator* creator,
+ QuicStreamId id,
+ size_t data_length,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicFrame* frame);
+ static bool CreateCryptoFrame(QuicPacketCreator* creator,
+ EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset,
+ QuicFrame* frame);
+ static SerializedPacket SerializeAllFrames(QuicPacketCreator* creator,
+ const QuicFrames& frames,
+ char* buffer,
+ size_t buffer_len);
+ static OwningSerializedPacketPointer SerializeConnectivityProbingPacket(
+ QuicPacketCreator* creator);
+ static OwningSerializedPacketPointer
+ SerializePathChallengeConnectivityProbingPacket(QuicPacketCreator* creator,
+ QuicPathFrameBuffer* payload);
+
+ static EncryptionLevel GetEncryptionLevel(QuicPacketCreator* creator);
+ static QuicFramer* framer(QuicPacketCreator* creator);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.cc
new file mode 100644
index 00000000000..91a875282c2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_generator.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicPacketCreator* QuicPacketGeneratorPeer::GetPacketCreator(
+ QuicPacketGenerator* generator) {
+ return &generator->packet_creator_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h
new file mode 100644
index 00000000000..6941b089d23
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_packet_generator_peer.h
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicPacketCreator;
+class QuicPacketGenerator;
+
+namespace test {
+
+class QuicPacketGeneratorPeer {
+ public:
+ QuicPacketGeneratorPeer() = delete;
+
+ static QuicPacketCreator* GetPacketCreator(QuicPacketGenerator* generator);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.cc
new file mode 100644
index 00000000000..3057e5e189c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -0,0 +1,239 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+size_t QuicSentPacketManagerPeer::GetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->max_tail_loss_probes_;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager,
+ size_t max_tail_loss_probes) {
+ sent_packet_manager->max_tail_loss_probes_ = max_tail_loss_probes;
+}
+
+// static
+bool QuicSentPacketManagerPeer::GetEnableHalfRttTailLossProbe(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->enable_half_rtt_tail_loss_probe_;
+}
+
+// static
+bool QuicSentPacketManagerPeer::GetUseNewRto(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->use_new_rto_;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetPerspective(
+ QuicSentPacketManager* sent_packet_manager,
+ Perspective perspective) {
+ QuicUnackedPacketMapPeer::SetPerspective(
+ &sent_packet_manager->unacked_packets_, perspective);
+}
+
+// static
+SendAlgorithmInterface* QuicSentPacketManagerPeer::GetSendAlgorithm(
+ const QuicSentPacketManager& sent_packet_manager) {
+ return sent_packet_manager.send_algorithm_.get();
+}
+
+// static
+void QuicSentPacketManagerPeer::SetSendAlgorithm(
+ QuicSentPacketManager* sent_packet_manager,
+ SendAlgorithmInterface* send_algorithm) {
+ sent_packet_manager->SetSendAlgorithm(send_algorithm);
+}
+
+// static
+const LossDetectionInterface* QuicSentPacketManagerPeer::GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->loss_algorithm_;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector) {
+ sent_packet_manager->loss_algorithm_ = loss_detector;
+}
+
+// static
+RttStats* QuicSentPacketManagerPeer::GetRttStats(
+ QuicSentPacketManager* sent_packet_manager) {
+ return &sent_packet_manager->rtt_stats_;
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasInFlightPackets();
+}
+
+// static
+bool QuicSentPacketManagerPeer::IsRetransmission(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number) {
+ DCHECK(HasRetransmittableFrames(sent_packet_manager, packet_number));
+ if (!HasRetransmittableFrames(sent_packet_manager, packet_number)) {
+ return false;
+ }
+ if (sent_packet_manager->session_decides_what_to_write()) {
+ return sent_packet_manager->unacked_packets_
+ .GetTransmissionInfo(QuicPacketNumber(packet_number))
+ .transmission_type != NOT_RETRANSMISSION;
+ }
+ for (auto transmission_info : sent_packet_manager->unacked_packets_) {
+ if (transmission_info.retransmission.IsInitialized() &&
+ transmission_info.retransmission == QuicPacketNumber(packet_number)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// static
+void QuicSentPacketManagerPeer::MarkForRetransmission(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number,
+ TransmissionType transmission_type) {
+ sent_packet_manager->MarkForRetransmission(QuicPacketNumber(packet_number),
+ transmission_type);
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager,
+ size_t consecutive_rto_count) {
+ return sent_packet_manager->GetRetransmissionDelay(consecutive_rto_count);
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->GetRetransmissionDelay();
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetTailLossProbeDelay(
+ const QuicSentPacketManager* sent_packet_manager,
+ size_t consecutive_tlp_count) {
+ return sent_packet_manager->GetTailLossProbeDelay(consecutive_tlp_count);
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetTailLossProbeDelay(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->GetTailLossProbeDelay();
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasPendingCryptoPackets();
+}
+
+// static
+size_t QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ size_t num_unacked_packets = 0;
+ for (auto it = sent_packet_manager->unacked_packets_.begin();
+ it != sent_packet_manager->unacked_packets_.end(); ++it) {
+ if (sent_packet_manager->unacked_packets_.HasRetransmittableFrames(*it)) {
+ ++num_unacked_packets;
+ }
+ }
+ return num_unacked_packets;
+}
+
+// static
+QuicByteCount QuicSentPacketManagerPeer::GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.bytes_in_flight();
+}
+
+// static
+void QuicSentPacketManagerPeer::SetConsecutiveRtoCount(
+ QuicSentPacketManager* sent_packet_manager,
+ size_t count) {
+ sent_packet_manager->consecutive_rto_count_ = count;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetConsecutiveTlpCount(
+ QuicSentPacketManager* sent_packet_manager,
+ size_t count) {
+ sent_packet_manager->consecutive_tlp_count_ = count;
+}
+
+// static
+QuicSustainedBandwidthRecorder& QuicSentPacketManagerPeer::GetBandwidthRecorder(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->sustained_bandwidth_recorder_;
+}
+
+// static
+bool QuicSentPacketManagerPeer::UsingPacing(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->using_pacing_;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetUsingPacing(
+ QuicSentPacketManager* sent_packet_manager,
+ bool using_pacing) {
+ sent_packet_manager->using_pacing_ = using_pacing;
+}
+
+// static
+bool QuicSentPacketManagerPeer::IsUnacked(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number) {
+ return sent_packet_manager->unacked_packets_.IsUnacked(
+ QuicPacketNumber(packet_number));
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasRetransmittableFrames(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number) {
+ return sent_packet_manager->unacked_packets_.HasRetransmittableFrames(
+ QuicPacketNumber(packet_number));
+}
+
+// static
+QuicUnackedPacketMap* QuicSentPacketManagerPeer::GetUnackedPacketMap(
+ QuicSentPacketManager* sent_packet_manager) {
+ return &sent_packet_manager->unacked_packets_;
+}
+
+// static
+void QuicSentPacketManagerPeer::DisablePacerBursts(
+ QuicSentPacketManager* sent_packet_manager) {
+ sent_packet_manager->pacing_sender_.burst_tokens_ = 0;
+ sent_packet_manager->pacing_sender_.initial_burst_size_ = 0;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetNextPacedPacketTime(
+ QuicSentPacketManager* sent_packet_manager,
+ QuicTime time) {
+ sent_packet_manager->pacing_sender_.ideal_next_packet_send_time_ = time;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h
new file mode 100644
index 00000000000..23244e5d2b9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -0,0 +1,114 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+
+namespace quic {
+
+class SendAlgorithmInterface;
+
+namespace test {
+
+class QuicSentPacketManagerPeer {
+ public:
+ QuicSentPacketManagerPeer() = delete;
+
+ static size_t GetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void SetMaxTailLossProbes(QuicSentPacketManager* sent_packet_manager,
+ size_t max_tail_loss_probes);
+
+ static bool GetEnableHalfRttTailLossProbe(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static bool GetUseNewRto(QuicSentPacketManager* sent_packet_manager);
+
+ static void SetPerspective(QuicSentPacketManager* sent_packet_manager,
+ Perspective perspective);
+
+ static SendAlgorithmInterface* GetSendAlgorithm(
+ const QuicSentPacketManager& sent_packet_manager);
+
+ static void SetSendAlgorithm(QuicSentPacketManager* sent_packet_manager,
+ SendAlgorithmInterface* send_algorithm);
+
+ static const LossDetectionInterface* GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void SetLossAlgorithm(QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector);
+
+ static RttStats* GetRttStats(QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ // Returns true if |packet_number| is a retransmission of a packet.
+ static bool IsRetransmission(QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number);
+
+ static void MarkForRetransmission(QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number,
+ TransmissionType transmission_type);
+
+ static QuicTime::Delta GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager,
+ size_t consecutive_rto_count);
+ static QuicTime::Delta GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager);
+ static QuicTime::Delta GetTailLossProbeDelay(
+ const QuicSentPacketManager* sent_packet_manager,
+ size_t consecutive_tlp_count);
+ static QuicTime::Delta GetTailLossProbeDelay(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static size_t GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static QuicByteCount GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static void SetConsecutiveRtoCount(QuicSentPacketManager* sent_packet_manager,
+ size_t count);
+
+ static void SetConsecutiveTlpCount(QuicSentPacketManager* sent_packet_manager,
+ size_t count);
+
+ static QuicSustainedBandwidthRecorder& GetBandwidthRecorder(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void SetUsingPacing(QuicSentPacketManager* sent_packet_manager,
+ bool using_pacing);
+
+ static bool UsingPacing(const QuicSentPacketManager* sent_packet_manager);
+
+ static bool IsUnacked(QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number);
+
+ static bool HasRetransmittableFrames(
+ QuicSentPacketManager* sent_packet_manager,
+ uint64_t packet_number);
+
+ static QuicUnackedPacketMap* GetUnackedPacketMap(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static void DisablePacerBursts(QuicSentPacketManager* sent_packet_manager);
+
+ static void SetNextPacedPacketTime(QuicSentPacketManager* sent_packet_manager,
+ QuicTime time);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_peer.cc
new file mode 100644
index 00000000000..9e8d9620d72
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_peer.cc
@@ -0,0 +1,32 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+namespace quic {
+namespace test {
+
+// static
+bool QuicServerPeer::SetSmallSocket(QuicServer* server) {
+ int size = 1024 * 10;
+ return setsockopt(server->fd_, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) !=
+ -1;
+}
+
+// static
+QuicDispatcher* QuicServerPeer::GetDispatcher(QuicServer* server) {
+ return server->dispatcher_.get();
+}
+
+// static
+void QuicServerPeer::SetReader(QuicServer* server, QuicPacketReader* reader) {
+ server->packet_reader_.reset(reader);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_peer.h
new file mode 100644
index 00000000000..29a36d45065
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_peer.h
@@ -0,0 +1,28 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_
+
+namespace quic {
+
+class QuicDispatcher;
+class QuicServer;
+class QuicPacketReader;
+
+namespace test {
+
+class QuicServerPeer {
+ public:
+ QuicServerPeer() = delete;
+
+ static bool SetSmallSocket(QuicServer* server);
+ static QuicDispatcher* GetDispatcher(QuicServer* server);
+ static void SetReader(QuicServer* server, QuicPacketReader* reader);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_session_base_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_session_base_peer.h
new file mode 100644
index 00000000000..30c9b22836d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_server_session_base_peer.h
@@ -0,0 +1,37 @@
+// 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_TEST_TOOLS_QUIC_SERVER_SESSION_BASE_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_SESSION_BASE_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+namespace quic {
+namespace test {
+
+class QuicServerSessionBasePeer {
+ public:
+ static QuicStream* GetOrCreateDynamicStream(QuicServerSessionBase* s,
+ QuicStreamId id) {
+ return s->GetOrCreateDynamicStream(id);
+ }
+ static void SetCryptoStream(QuicServerSessionBase* s,
+ QuicCryptoServerStream* crypto_stream) {
+ s->crypto_stream_.reset(crypto_stream);
+ s->RegisterStaticStream(
+ QuicUtils::GetCryptoStreamId(s->connection()->transport_version()),
+ crypto_stream);
+ }
+ static bool IsBandwidthResumptionEnabled(QuicServerSessionBase* s) {
+ return s->bandwidth_resumption_enabled_;
+ }
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SERVER_SESSION_BASE_PEER_H_
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
new file mode 100644
index 00000000000..37e5013cf92
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicStreamId QuicSessionPeer::GetNextOutgoingBidirectionalStreamId(
+ QuicSession* session) {
+ return session->GetNextOutgoingBidirectionalStreamId();
+}
+
+// static
+QuicStreamId QuicSessionPeer::GetNextOutgoingUnidirectionalStreamId(
+ QuicSession* session) {
+ return session->GetNextOutgoingUnidirectionalStreamId();
+}
+
+// static
+void QuicSessionPeer::SetNextOutgoingBidirectionalStreamId(QuicSession* session,
+ QuicStreamId id) {
+ if (session->connection()->transport_version() == QUIC_VERSION_99) {
+ session->v99_streamid_manager_.bidirectional_stream_id_manager_
+ .next_outgoing_stream_id_ = id;
+ return;
+ }
+ session->stream_id_manager_.next_outgoing_stream_id_ = id;
+}
+
+// static
+void QuicSessionPeer::SetMaxOpenIncomingStreams(QuicSession* session,
+ uint32_t max_streams) {
+ if (session->connection()->transport_version() == QUIC_VERSION_99) {
+ session->v99_streamid_manager_.SetMaxOpenIncomingStreams(max_streams);
+ return;
+ }
+ session->stream_id_manager_.set_max_open_incoming_streams(max_streams);
+}
+
+// static
+void QuicSessionPeer::SetMaxOpenOutgoingStreams(QuicSession* session,
+ uint32_t max_streams) {
+ if (session->connection()->transport_version() == QUIC_VERSION_99) {
+ session->v99_streamid_manager_.SetMaxOpenOutgoingStreams(max_streams);
+ return;
+ }
+ session->stream_id_manager_.set_max_open_outgoing_streams(max_streams);
+}
+
+// static
+QuicCryptoStream* QuicSessionPeer::GetMutableCryptoStream(
+ QuicSession* session) {
+ return session->GetMutableCryptoStream();
+}
+
+// static
+QuicWriteBlockedList* QuicSessionPeer::GetWriteBlockedStreams(
+ QuicSession* session) {
+ return &session->write_blocked_streams_;
+}
+
+// static
+QuicStream* QuicSessionPeer::GetOrCreateDynamicStream(QuicSession* session,
+ QuicStreamId stream_id) {
+ return session->GetOrCreateDynamicStream(stream_id);
+}
+
+// static
+std::map<QuicStreamId, QuicStreamOffset>&
+QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(QuicSession* session) {
+ return session->locally_closed_streams_highest_offset_;
+}
+
+// static
+QuicSession::StaticStreamMap& QuicSessionPeer::static_streams(
+ QuicSession* session) {
+ return session->static_stream_map_;
+}
+
+// static
+QuicSession::DynamicStreamMap& QuicSessionPeer::dynamic_streams(
+ QuicSession* session) {
+ return session->dynamic_streams();
+}
+
+// static
+const QuicSession::ClosedStreams& QuicSessionPeer::closed_streams(
+ QuicSession* session) {
+ return *session->closed_streams();
+}
+
+// static
+QuicSession::ZombieStreamMap& QuicSessionPeer::zombie_streams(
+ QuicSession* session) {
+ return session->zombie_streams_;
+}
+
+// static
+QuicUnorderedSet<QuicStreamId>* QuicSessionPeer::GetDrainingStreams(
+ QuicSession* session) {
+ return &session->draining_streams_;
+}
+
+// static
+void QuicSessionPeer::ActivateStream(QuicSession* session,
+ std::unique_ptr<QuicStream> stream) {
+ return session->ActivateStream(std::move(stream));
+}
+
+// static
+bool QuicSessionPeer::IsStreamClosed(QuicSession* session, QuicStreamId id) {
+ DCHECK_NE(0u, id);
+ return session->IsClosedStream(id);
+}
+
+// static
+bool QuicSessionPeer::IsStreamCreated(QuicSession* session, QuicStreamId id) {
+ DCHECK_NE(0u, id);
+ return QuicContainsKey(session->dynamic_streams(), id);
+}
+
+// static
+bool QuicSessionPeer::IsStreamAvailable(QuicSession* session, QuicStreamId id) {
+ DCHECK_NE(0u, id);
+ if (session->connection()->transport_version() == QUIC_VERSION_99) {
+ if (id % kV99StreamIdIncrement < 2) {
+ return QuicContainsKey(
+ session->v99_streamid_manager_.bidirectional_stream_id_manager_
+ .available_streams_,
+ id);
+ }
+ return QuicContainsKey(
+ session->v99_streamid_manager_.unidirectional_stream_id_manager_
+ .available_streams_,
+ id);
+ }
+ return QuicContainsKey(session->stream_id_manager_.available_streams_, id);
+}
+
+// static
+QuicStream* QuicSessionPeer::GetStream(QuicSession* session, QuicStreamId id) {
+ return session->GetStream(id);
+}
+
+// static
+bool QuicSessionPeer::IsStreamWriteBlocked(QuicSession* session,
+ QuicStreamId id) {
+ return session->write_blocked_streams_.IsStreamBlocked(id);
+}
+
+// static
+QuicAlarm* QuicSessionPeer::GetCleanUpClosedStreamsAlarm(QuicSession* session) {
+ return session->closed_streams_clean_up_alarm_.get();
+}
+
+// static
+LegacyQuicStreamIdManager* QuicSessionPeer::GetStreamIdManager(
+ QuicSession* session) {
+ return &session->stream_id_manager_;
+}
+
+// static
+UberQuicStreamIdManager* QuicSessionPeer::v99_streamid_manager(
+ QuicSession* session) {
+ return &session->v99_streamid_manager_;
+}
+
+// static
+QuicStreamIdManager* QuicSessionPeer::v99_bidirectional_stream_id_manager(
+ QuicSession* session) {
+ return &session->v99_streamid_manager_.bidirectional_stream_id_manager_;
+}
+
+// static
+QuicStreamIdManager* QuicSessionPeer::v99_unidirectional_stream_id_manager(
+ QuicSession* session) {
+ return &session->v99_streamid_manager_.unidirectional_stream_id_manager_;
+}
+
+// static
+void QuicSessionPeer::SendRstStreamInner(QuicSession* session,
+ QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written,
+ bool close_write_side_only) {
+ session->SendRstStreamInner(id, error, bytes_written, close_write_side_only);
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h
new file mode 100644
index 00000000000..30e358f4bc6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_session_peer.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
+
+#include <cstdint>
+#include <map>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+class QuicCryptoStream;
+class QuicSession;
+class QuicStream;
+
+namespace test {
+
+class QuicSessionPeer {
+ public:
+ QuicSessionPeer() = delete;
+
+ static QuicStreamId GetNextOutgoingBidirectionalStreamId(
+ QuicSession* session);
+ static QuicStreamId GetNextOutgoingUnidirectionalStreamId(
+ QuicSession* session);
+ static void SetNextOutgoingBidirectionalStreamId(QuicSession* session,
+ QuicStreamId id);
+ static void SetMaxOpenIncomingStreams(QuicSession* session,
+ uint32_t max_streams);
+ static void SetMaxOpenOutgoingStreams(QuicSession* session,
+ uint32_t max_streams);
+ static QuicCryptoStream* GetMutableCryptoStream(QuicSession* session);
+ static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session);
+ static QuicStream* GetOrCreateDynamicStream(QuicSession* session,
+ QuicStreamId stream_id);
+ static std::map<QuicStreamId, QuicStreamOffset>&
+ GetLocallyClosedStreamsHighestOffset(QuicSession* session);
+ static QuicSession::StaticStreamMap& static_streams(QuicSession* session);
+ static QuicSession::DynamicStreamMap& dynamic_streams(QuicSession* session);
+ static const QuicSession::ClosedStreams& closed_streams(QuicSession* session);
+ static QuicSession::ZombieStreamMap& zombie_streams(QuicSession* session);
+ static QuicUnorderedSet<QuicStreamId>* GetDrainingStreams(
+ QuicSession* session);
+ static void ActivateStream(QuicSession* session,
+ std::unique_ptr<QuicStream> stream);
+
+ // Discern the state of a stream. Exactly one of these should be true at a
+ // time for any stream id > 0 (other than the special streams 1 and 3).
+ static bool IsStreamClosed(QuicSession* session, QuicStreamId id);
+ static bool IsStreamCreated(QuicSession* session, QuicStreamId id);
+ static bool IsStreamAvailable(QuicSession* session, QuicStreamId id);
+
+ static QuicStream* GetStream(QuicSession* session, QuicStreamId id);
+ static bool IsStreamWriteBlocked(QuicSession* session, QuicStreamId id);
+ static QuicAlarm* GetCleanUpClosedStreamsAlarm(QuicSession* session);
+ static LegacyQuicStreamIdManager* GetStreamIdManager(QuicSession* session);
+ static UberQuicStreamIdManager* v99_streamid_manager(QuicSession* session);
+ static QuicStreamIdManager* v99_bidirectional_stream_id_manager(
+ QuicSession* session);
+ static QuicStreamIdManager* v99_unidirectional_stream_id_manager(
+ QuicSession* session);
+ static void SendRstStreamInner(QuicSession* session,
+ QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written,
+ bool close_write_side_only);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
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
new file mode 100644
index 00000000000..dcba12c5fee
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+namespace quic {
+namespace test {
+
+// static
+QuicHeadersStream* QuicSpdySessionPeer::GetHeadersStream(
+ QuicSpdySession* session) {
+ return session->headers_stream_.get();
+}
+
+// static
+void QuicSpdySessionPeer::SetHeadersStream(QuicSpdySession* session,
+ QuicHeadersStream* headers_stream) {
+ session->headers_stream_.reset(headers_stream);
+ if (headers_stream != nullptr) {
+ session->RegisterStaticStream(headers_stream->id(), headers_stream);
+ }
+}
+
+// static
+const spdy::SpdyFramer& QuicSpdySessionPeer::GetSpdyFramer(
+ QuicSpdySession* session) {
+ return session->spdy_framer_;
+}
+
+void QuicSpdySessionPeer::SetHpackEncoderDebugVisitor(
+ QuicSpdySession* session,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor) {
+ session->SetHpackEncoderDebugVisitor(std::move(visitor));
+}
+
+void QuicSpdySessionPeer::SetHpackDecoderDebugVisitor(
+ QuicSpdySession* session,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor) {
+ session->SetHpackDecoderDebugVisitor(std::move(visitor));
+}
+
+void QuicSpdySessionPeer::SetMaxUncompressedHeaderBytes(
+ QuicSpdySession* session,
+ size_t set_max_uncompressed_header_bytes) {
+ session->set_max_uncompressed_header_bytes(set_max_uncompressed_header_bytes);
+}
+
+// static
+size_t QuicSpdySessionPeer::WriteHeadersOnHeadersStream(
+ QuicSpdySession* session,
+ QuicStreamId id,
+ spdy::SpdyHeaderBlock headers,
+ bool fin,
+ spdy::SpdyPriority priority,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ return session->WriteHeadersOnHeadersStream(
+ id, std::move(headers), fin, priority, std::move(ack_listener));
+}
+
+// static
+QuicStreamId QuicSpdySessionPeer::GetNextOutgoingUnidirectionalStreamId(
+ QuicSpdySession* session) {
+ return session->GetNextOutgoingUnidirectionalStreamId();
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..83e8b0caf11
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+class QuicHeadersStream;
+class QuicSpdySession;
+class QuicHpackDebugVisitor;
+
+namespace test {
+
+class QuicSpdySessionPeer {
+ public:
+ QuicSpdySessionPeer() = delete;
+
+ static QuicHeadersStream* GetHeadersStream(QuicSpdySession* session);
+ static void SetHeadersStream(QuicSpdySession* session,
+ QuicHeadersStream* headers_stream);
+ static const spdy::SpdyFramer& GetSpdyFramer(QuicSpdySession* session);
+ static void SetHpackEncoderDebugVisitor(
+ QuicSpdySession* session,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor);
+ static void SetHpackDecoderDebugVisitor(
+ QuicSpdySession* session,
+ std::unique_ptr<QuicHpackDebugVisitor> visitor);
+ static void SetMaxUncompressedHeaderBytes(
+ QuicSpdySession* session,
+ size_t set_max_uncompressed_header_bytes);
+ static size_t WriteHeadersOnHeadersStream(
+ QuicSpdySession* session,
+ QuicStreamId id,
+ spdy::SpdyHeaderBlock headers,
+ bool fin,
+ spdy::SpdyPriority priority,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+ // |session| can't be nullptr.
+ static QuicStreamId GetNextOutgoingUnidirectionalStreamId(
+ QuicSpdySession* session);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_SESSION_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.cc
new file mode 100644
index 00000000000..4d23434e1b7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.cc
@@ -0,0 +1,26 @@
+// 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 "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicSpdyStreamPeer::set_ack_listener(
+ QuicSpdyStream* stream,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ stream->set_ack_listener(std::move(ack_listener));
+}
+
+// static
+const QuicIntervalSet<QuicStreamOffset>&
+QuicSpdyStreamPeer::unacked_frame_headers_offsets(QuicSpdyStream* stream) {
+ return stream->unacked_frame_headers_offsets_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h
new file mode 100644
index 00000000000..7b3fe7c0a68
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_ack_listener_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+class QuicSpdyStream;
+
+namespace test {
+
+class QuicSpdyStreamPeer {
+ public:
+ static void set_ack_listener(
+ QuicSpdyStream* stream,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+ static const QuicIntervalSet<QuicStreamOffset>& unacked_frame_headers_offsets(
+ QuicSpdyStream* stream);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.cc
new file mode 100644
index 00000000000..705ee270b55
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.cc
@@ -0,0 +1,36 @@
+// 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 "net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_id_manager.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicStreamIdManagerPeer::IncrementMaximumAllowedOutgoingStreamId(
+ QuicStreamIdManager* stream_id_manager,
+ int increment) {
+ stream_id_manager->max_allowed_outgoing_stream_id_ +=
+ (increment * kV99StreamIdIncrement);
+}
+
+// static
+void QuicStreamIdManagerPeer::IncrementMaximumAllowedIncomingStreamId(
+ QuicStreamIdManager* stream_id_manager,
+ int increment) {
+ stream_id_manager->actual_max_allowed_incoming_stream_id_ +=
+ (increment * kV99StreamIdIncrement);
+ stream_id_manager->advertised_max_allowed_incoming_stream_id_ +=
+ (increment * kV99StreamIdIncrement);
+}
+
+// static
+void QuicStreamIdManagerPeer::SetMaxOpenIncomingStreams(
+ QuicStreamIdManager* stream_id_manager,
+ size_t max_streams) {
+ stream_id_manager->SetMaxOpenIncomingStreams(max_streams);
+}
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h
new file mode 100644
index 00000000000..2ec07b1e7fd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_id_manager_peer.h
@@ -0,0 +1,32 @@
+// 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_TEST_TOOLS_QUIC_STREAM_ID_MANAGER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_ID_MANAGER_PEER_H_
+
+#include <stddef.h>
+
+namespace quic {
+
+class QuicStreamIdManager;
+
+namespace test {
+
+class QuicStreamIdManagerPeer {
+ public:
+ QuicStreamIdManagerPeer() = delete;
+ static void IncrementMaximumAllowedOutgoingStreamId(
+ QuicStreamIdManager* stream_id_manager,
+ int increment);
+ static void IncrementMaximumAllowedIncomingStreamId(
+ QuicStreamIdManager* stream_id_manager,
+ int increment);
+ static void SetMaxOpenIncomingStreams(QuicStreamIdManager* stream_id_manager,
+ size_t max_streams);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_peer.cc
new file mode 100644
index 00000000000..d501debb7e3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_peer.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+
+#include <list>
+
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicStreamPeer::SetWriteSideClosed(bool value, QuicStream* stream) {
+ stream->write_side_closed_ = value;
+}
+
+// static
+void QuicStreamPeer::SetStreamBytesWritten(
+ QuicStreamOffset stream_bytes_written,
+ QuicStream* stream) {
+ stream->send_buffer_.stream_bytes_written_ = stream_bytes_written;
+ stream->send_buffer_.stream_bytes_outstanding_ = stream_bytes_written;
+ QuicStreamSendBufferPeer::SetStreamOffset(&stream->send_buffer_,
+ stream_bytes_written);
+}
+
+// static
+bool QuicStreamPeer::read_side_closed(QuicStream* stream) {
+ return stream->read_side_closed();
+}
+
+// static
+void QuicStreamPeer::CloseReadSide(QuicStream* stream) {
+ stream->CloseReadSide();
+}
+
+// static
+bool QuicStreamPeer::StreamContributesToConnectionFlowControl(
+ QuicStream* stream) {
+ return stream->stream_contributes_to_connection_flow_control_;
+}
+
+// static
+QuicStreamSequencer* QuicStreamPeer::sequencer(QuicStream* stream) {
+ return &(stream->sequencer_);
+}
+
+// static
+QuicSession* QuicStreamPeer::session(QuicStream* stream) {
+ return stream->session();
+}
+
+// static
+QuicStreamSendBuffer& QuicStreamPeer::SendBuffer(QuicStream* stream) {
+ return stream->send_buffer_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h
new file mode 100644
index 00000000000..9142348b771
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_PEER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QuicStream;
+class QuicSession;
+
+namespace test {
+
+class QuicStreamPeer {
+ public:
+ QuicStreamPeer() = delete;
+
+ static void SetWriteSideClosed(bool value, QuicStream* stream);
+ static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written,
+ QuicStream* stream);
+ static bool read_side_closed(QuicStream* stream);
+ static void CloseReadSide(QuicStream* stream);
+
+ static bool StreamContributesToConnectionFlowControl(QuicStream* stream);
+
+ static QuicStreamSequencer* sequencer(QuicStream* stream);
+ static QuicSession* session(QuicStream* stream);
+
+ static QuicStreamSendBuffer& SendBuffer(QuicStream* stream);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.cc
new file mode 100644
index 00000000000..22e5e566c00
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.cc
@@ -0,0 +1,39 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h"
+
+namespace quic {
+
+namespace test {
+
+// static
+void QuicStreamSendBufferPeer::SetStreamOffset(
+ QuicStreamSendBuffer* send_buffer,
+ QuicStreamOffset stream_offset) {
+ send_buffer->stream_offset_ = stream_offset;
+}
+
+// static
+const BufferedSlice* QuicStreamSendBufferPeer::CurrentWriteSlice(
+ QuicStreamSendBuffer* send_buffer) {
+ if (send_buffer->write_index_ == -1) {
+ return nullptr;
+ }
+ return &send_buffer->buffered_slices_[send_buffer->write_index_];
+}
+
+// static
+QuicByteCount QuicStreamSendBufferPeer::TotalLength(
+ QuicStreamSendBuffer* send_buffer) {
+ QuicByteCount length = 0;
+ for (const auto& slice : send_buffer->buffered_slices_) {
+ length += slice.slice.length();
+ }
+ return length;
+}
+
+} // namespace test
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h
new file mode 100644
index 00000000000..f61cb0049a5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_send_buffer_peer.h
@@ -0,0 +1,29 @@
+// 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_TEST_TOOLS_QUIC_STREAM_SEND_BUFFER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEND_BUFFER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+
+namespace quic {
+
+namespace test {
+
+class QuicStreamSendBufferPeer {
+ public:
+ static void SetStreamOffset(QuicStreamSendBuffer* send_buffer,
+ QuicStreamOffset stream_offset);
+
+ static const BufferedSlice* CurrentWriteSlice(
+ QuicStreamSendBuffer* send_buffer);
+
+ static QuicByteCount TotalLength(QuicStreamSendBuffer* send_buffer);
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEND_BUFFER_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.cc
new file mode 100644
index 00000000000..36d2b04ee60
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.cc
@@ -0,0 +1,155 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+typedef quic::QuicStreamSequencerBuffer::BufferBlock BufferBlock;
+
+static const size_t kBlockSizeBytes =
+ quic::QuicStreamSequencerBuffer::kBlockSizeBytes;
+
+namespace quic {
+namespace test {
+
+QuicStreamSequencerBufferPeer::QuicStreamSequencerBufferPeer(
+ QuicStreamSequencerBuffer* buffer)
+ : buffer_(buffer) {}
+
+// Read from this buffer_ into the given destination buffer_ up to the
+// size of the destination. Returns the number of bytes read. Reading from
+// an empty buffer_->returns 0.
+size_t QuicStreamSequencerBufferPeer::Read(char* dest_buffer, size_t size) {
+ iovec dest;
+ dest.iov_base = dest_buffer, dest.iov_len = size;
+ size_t bytes_read;
+ std::string error_details;
+ EXPECT_EQ(QUIC_NO_ERROR,
+ buffer_->Readv(&dest, 1, &bytes_read, &error_details));
+ return bytes_read;
+}
+
+// If buffer is empty, the blocks_ array must be empty, which means all
+// blocks are deallocated.
+bool QuicStreamSequencerBufferPeer::CheckEmptyInvariants() {
+ return !buffer_->Empty() || IsBlockArrayEmpty();
+}
+
+bool QuicStreamSequencerBufferPeer::IsBlockArrayEmpty() {
+ if (buffer_->blocks_ == nullptr) {
+ return true;
+ }
+
+ size_t count = buffer_->blocks_count_;
+ for (size_t i = 0; i < count; i++) {
+ if (buffer_->blocks_[i] != nullptr) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicStreamSequencerBufferPeer::CheckInitialState() {
+ EXPECT_TRUE(buffer_->Empty() && buffer_->total_bytes_read_ == 0 &&
+ buffer_->num_bytes_buffered_ == 0);
+ return CheckBufferInvariants();
+}
+
+bool QuicStreamSequencerBufferPeer::CheckBufferInvariants() {
+ QuicStreamOffset data_span =
+ buffer_->NextExpectedByte() - buffer_->total_bytes_read_;
+ bool capacity_sane = data_span <= buffer_->max_buffer_capacity_bytes_ &&
+ data_span >= buffer_->num_bytes_buffered_;
+ if (!capacity_sane) {
+ QUIC_LOG(ERROR) << "data span is larger than capacity.";
+ QUIC_LOG(ERROR) << "total read: " << buffer_->total_bytes_read_
+ << " last byte: " << buffer_->NextExpectedByte();
+ }
+ bool total_read_sane =
+ buffer_->FirstMissingByte() >= buffer_->total_bytes_read_;
+ if (!total_read_sane) {
+ QUIC_LOG(ERROR) << "read across 1st gap.";
+ }
+ bool read_offset_sane = buffer_->ReadOffset() < kBlockSizeBytes;
+ if (!capacity_sane) {
+ QUIC_LOG(ERROR) << "read offset go beyond 1st block";
+ }
+ bool block_match_capacity = (buffer_->max_buffer_capacity_bytes_ <=
+ buffer_->blocks_count_ * kBlockSizeBytes) &&
+ (buffer_->max_buffer_capacity_bytes_ >
+ (buffer_->blocks_count_ - 1) * kBlockSizeBytes);
+ if (!capacity_sane) {
+ QUIC_LOG(ERROR) << "block number not match capcaity.";
+ }
+ bool block_retired_when_empty = CheckEmptyInvariants();
+ if (!block_retired_when_empty) {
+ QUIC_LOG(ERROR) << "block is not retired after use.";
+ }
+ return capacity_sane && total_read_sane && read_offset_sane &&
+ block_match_capacity && block_retired_when_empty;
+}
+
+size_t QuicStreamSequencerBufferPeer::GetInBlockOffset(
+ QuicStreamOffset offset) {
+ return buffer_->GetInBlockOffset(offset);
+}
+
+BufferBlock* QuicStreamSequencerBufferPeer::GetBlock(size_t index) {
+ return buffer_->blocks_[index];
+}
+
+int QuicStreamSequencerBufferPeer::IntervalSize() {
+ if (buffer_->bytes_received_.Empty()) {
+ return 1;
+ }
+ int gap_size = buffer_->bytes_received_.Size() + 1;
+ if (buffer_->bytes_received_.Empty()) {
+ return gap_size;
+ }
+ if (buffer_->bytes_received_.begin()->min() == 0) {
+ --gap_size;
+ }
+ if (buffer_->bytes_received_.rbegin()->max() ==
+ std::numeric_limits<uint64_t>::max()) {
+ --gap_size;
+ }
+ return gap_size;
+}
+
+size_t QuicStreamSequencerBufferPeer::max_buffer_capacity() {
+ return buffer_->max_buffer_capacity_bytes_;
+}
+
+size_t QuicStreamSequencerBufferPeer::ReadableBytes() {
+ return buffer_->ReadableBytes();
+}
+
+void QuicStreamSequencerBufferPeer::set_total_bytes_read(
+ QuicStreamOffset total_bytes_read) {
+ buffer_->total_bytes_read_ = total_bytes_read;
+}
+
+void QuicStreamSequencerBufferPeer::AddBytesReceived(QuicStreamOffset offset,
+ QuicByteCount length) {
+ buffer_->bytes_received_.Add(offset, offset + length);
+}
+
+bool QuicStreamSequencerBufferPeer::IsBufferAllocated() {
+ return buffer_->blocks_ != nullptr;
+}
+
+size_t QuicStreamSequencerBufferPeer::block_count() {
+ return buffer_->blocks_count_;
+}
+
+const QuicIntervalSet<QuicStreamOffset>&
+QuicStreamSequencerBufferPeer::bytes_received() {
+ return buffer_->bytes_received_;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h
new file mode 100644
index 00000000000..da2d054bbd3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_BUFFER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_BUFFER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer_buffer.h"
+
+namespace quic {
+
+namespace test {
+
+class QuicStreamSequencerBufferPeer {
+ public:
+ explicit QuicStreamSequencerBufferPeer(QuicStreamSequencerBuffer* buffer);
+ QuicStreamSequencerBufferPeer(const QuicStreamSequencerBufferPeer&) = delete;
+ QuicStreamSequencerBufferPeer& operator=(
+ const QuicStreamSequencerBufferPeer&) = delete;
+
+ // Read from this buffer_ into the given destination buffer_ up to the
+ // size of the destination. Returns the number of bytes read. Reading from
+ // an empty buffer_->returns 0.
+ size_t Read(char* dest_buffer, size_t size);
+
+ // If buffer is empty, the blocks_ array must be empty, which means all
+ // blocks are deallocated.
+ bool CheckEmptyInvariants();
+
+ bool IsBlockArrayEmpty();
+
+ bool CheckInitialState();
+
+ bool CheckBufferInvariants();
+
+ size_t GetInBlockOffset(QuicStreamOffset offset);
+
+ QuicStreamSequencerBuffer::BufferBlock* GetBlock(size_t index);
+
+ int IntervalSize();
+
+ size_t max_buffer_capacity();
+
+ size_t ReadableBytes();
+
+ void set_total_bytes_read(QuicStreamOffset total_bytes_read);
+
+ void AddBytesReceived(QuicStreamOffset offset, QuicByteCount length);
+
+ bool IsBufferAllocated();
+
+ size_t block_count();
+
+ const QuicIntervalSet<QuicStreamOffset>& bytes_received();
+
+ private:
+ QuicStreamSequencerBuffer* buffer_;
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_BUFFER_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.cc
new file mode 100644
index 00000000000..05cf35cdf5d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_buffer_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+size_t QuicStreamSequencerPeer::GetNumBufferedBytes(
+ QuicStreamSequencer* sequencer) {
+ return sequencer->buffered_frames_.BytesBuffered();
+}
+
+// static
+QuicStreamOffset QuicStreamSequencerPeer::GetCloseOffset(
+ QuicStreamSequencer* sequencer) {
+ return sequencer->close_offset_;
+}
+
+// static
+bool QuicStreamSequencerPeer::IsUnderlyingBufferAllocated(
+ QuicStreamSequencer* sequencer) {
+ QuicStreamSequencerBufferPeer buffer_peer(&(sequencer->buffered_frames_));
+ return buffer_peer.IsBufferAllocated();
+}
+
+// static
+void QuicStreamSequencerPeer::SetFrameBufferTotalBytesRead(
+ QuicStreamSequencer* sequencer,
+ QuicStreamOffset total_bytes_read) {
+ QuicStreamSequencerBufferPeer buffer_peer(&(sequencer->buffered_frames_));
+ buffer_peer.set_total_bytes_read(total_bytes_read);
+}
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h
new file mode 100644
index 00000000000..d358995bc0e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_stream_sequencer_peer.h
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicStreamSequencer;
+
+namespace test {
+
+class QuicStreamSequencerPeer {
+ public:
+ QuicStreamSequencerPeer() = delete;
+
+ static size_t GetNumBufferedBytes(QuicStreamSequencer* sequencer);
+
+ static QuicStreamOffset GetCloseOffset(QuicStreamSequencer* sequencer);
+
+ static bool IsUnderlyingBufferAllocated(QuicStreamSequencer* sequencer);
+
+ static void SetFrameBufferTotalBytesRead(QuicStreamSequencer* sequencer,
+ QuicStreamOffset total_bytes_read);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc
new file mode 100644
index 00000000000..9f08faf00e7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_sustained_bandwidth_recorder.h"
+
+namespace quic {
+namespace test {
+
+// static
+void QuicSustainedBandwidthRecorderPeer::SetBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32_t bandwidth_estimate_kbytes_per_second) {
+ bandwidth_recorder->has_estimate_ = true;
+ bandwidth_recorder->bandwidth_estimate_ =
+ QuicBandwidth::FromKBytesPerSecond(bandwidth_estimate_kbytes_per_second);
+}
+
+// static
+void QuicSustainedBandwidthRecorderPeer::SetMaxBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32_t max_bandwidth_estimate_kbytes_per_second,
+ int32_t max_bandwidth_timestamp) {
+ bandwidth_recorder->max_bandwidth_estimate_ =
+ QuicBandwidth::FromKBytesPerSecond(
+ max_bandwidth_estimate_kbytes_per_second);
+ bandwidth_recorder->max_bandwidth_timestamp_ = max_bandwidth_timestamp;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h
new file mode 100644
index 00000000000..24c0df27a8a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium 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_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+class QuicSustainedBandwidthRecorder;
+
+namespace test {
+
+class QuicSustainedBandwidthRecorderPeer {
+ public:
+ QuicSustainedBandwidthRecorderPeer() = delete;
+
+ static void SetBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32_t bandwidth_estimate_kbytes_per_second);
+
+ static void SetMaxBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32_t max_bandwidth_estimate_kbytes_per_second,
+ int32_t max_bandwidth_timestamp);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
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
new file mode 100644
index 00000000000..fb76f7a10f3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.cc
@@ -0,0 +1,909 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_client.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "third_party/boringssl/src/include/openssl/x509.h"
+#include "net/third_party/quiche/src/quic/core/crypto/proof_verifier.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_stack_trace.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+// RecordingProofVerifier accepts any certificate chain and records the common
+// name of the leaf and then delegates the actual verification to an actual
+// verifier. If no optional verifier is provided, then VerifyProof will return
+// success.
+class RecordingProofVerifier : public ProofVerifier {
+ public:
+ explicit RecordingProofVerifier(std::unique_ptr<ProofVerifier> verifier)
+ : verifier_(std::move(verifier)) {}
+
+ // ProofVerifier interface.
+ QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const uint16_t port,
+ const std::string& server_config,
+ QuicTransportVersion transport_version,
+ QuicStringPiece chlo_hash,
+ const std::vector<std::string>& certs,
+ const std::string& cert_sct,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override {
+ common_name_.clear();
+ if (certs.empty()) {
+ return QUIC_FAILURE;
+ }
+
+ const uint8_t* data;
+ data = reinterpret_cast<const uint8_t*>(certs[0].data());
+ bssl::UniquePtr<X509> cert(d2i_X509(nullptr, &data, certs[0].size()));
+ if (!cert.get()) {
+ return QUIC_FAILURE;
+ }
+
+ static const unsigned kMaxCommonNameLength = 256;
+ char buf[kMaxCommonNameLength];
+ X509_NAME* subject_name = X509_get_subject_name(cert.get());
+ if (X509_NAME_get_text_by_NID(subject_name, NID_commonName, buf,
+ sizeof(buf)) <= 0) {
+ return QUIC_FAILURE;
+ }
+
+ common_name_ = buf;
+ cert_sct_ = cert_sct;
+
+ if (!verifier_) {
+ return QUIC_SUCCESS;
+ }
+
+ return verifier_->VerifyProof(hostname, port, server_config,
+ transport_version, chlo_hash, certs, cert_sct,
+ signature, context, error_details, details,
+ std::move(callback));
+ }
+
+ QuicAsyncStatus VerifyCertChain(
+ const std::string& hostname,
+ const std::vector<std::string>& certs,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ std::unique_ptr<ProofVerifyDetails>* details,
+ std::unique_ptr<ProofVerifierCallback> callback) override {
+ return QUIC_SUCCESS;
+ }
+
+ std::unique_ptr<ProofVerifyContext> CreateDefaultContext() override {
+ return verifier_ != nullptr ? verifier_->CreateDefaultContext() : nullptr;
+ }
+
+ const std::string& common_name() const { return common_name_; }
+
+ const std::string& cert_sct() const { return cert_sct_; }
+
+ private:
+ std::unique_ptr<ProofVerifier> verifier_;
+ std::string common_name_;
+ std::string cert_sct_;
+};
+} // namespace
+
+class MockableQuicClientEpollNetworkHelper
+ : public QuicClientEpollNetworkHelper {
+ public:
+ using QuicClientEpollNetworkHelper::QuicClientEpollNetworkHelper;
+ ~MockableQuicClientEpollNetworkHelper() override = default;
+
+ void ProcessPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) override {
+ QuicClientEpollNetworkHelper::ProcessPacket(self_address, peer_address,
+ packet);
+ if (track_last_incoming_packet_) {
+ last_incoming_packet_ = packet.Clone();
+ }
+ }
+
+ QuicPacketWriter* CreateQuicPacketWriter() override {
+ QuicPacketWriter* writer =
+ QuicClientEpollNetworkHelper::CreateQuicPacketWriter();
+ if (!test_writer_) {
+ return writer;
+ }
+ test_writer_->set_writer(writer);
+ return test_writer_;
+ }
+
+ const QuicReceivedPacket* last_incoming_packet() {
+ return last_incoming_packet_.get();
+ }
+
+ void set_track_last_incoming_packet(bool track) {
+ track_last_incoming_packet_ = track;
+ }
+
+ void UseWriter(QuicPacketWriterWrapper* writer) {
+ CHECK(test_writer_ == nullptr);
+ test_writer_ = writer;
+ }
+
+ void set_peer_address(const QuicSocketAddress& address) {
+ CHECK(test_writer_ != nullptr);
+ test_writer_->set_peer_address(address);
+ }
+
+ private:
+ QuicPacketWriterWrapper* test_writer_ = nullptr;
+ // The last incoming packet, iff |track_last_incoming_packet_| is true.
+ std::unique_ptr<QuicReceivedPacket> last_incoming_packet_;
+ // If true, copy each packet from ProcessPacket into |last_incoming_packet_|
+ bool track_last_incoming_packet_ = false;
+};
+
+MockableQuicClient::MockableQuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server)
+ : MockableQuicClient(server_address,
+ server_id,
+ QuicConfig(),
+ supported_versions,
+ epoll_server) {}
+
+MockableQuicClient::MockableQuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server)
+ : MockableQuicClient(server_address,
+ server_id,
+ config,
+ supported_versions,
+ epoll_server,
+ nullptr) {}
+
+MockableQuicClient::MockableQuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicClient(
+ server_address,
+ server_id,
+ supported_versions,
+ config,
+ epoll_server,
+ QuicMakeUnique<MockableQuicClientEpollNetworkHelper>(epoll_server,
+ this),
+ QuicWrapUnique(
+ new RecordingProofVerifier(std::move(proof_verifier)))),
+ override_connection_id_(EmptyQuicConnectionId()),
+ connection_id_overridden_(false) {}
+
+MockableQuicClient::~MockableQuicClient() {
+ if (connected()) {
+ Disconnect();
+ }
+}
+
+MockableQuicClientEpollNetworkHelper*
+MockableQuicClient::mockable_network_helper() {
+ return static_cast<MockableQuicClientEpollNetworkHelper*>(
+ epoll_network_helper());
+}
+
+const MockableQuicClientEpollNetworkHelper*
+MockableQuicClient::mockable_network_helper() const {
+ return static_cast<const MockableQuicClientEpollNetworkHelper*>(
+ epoll_network_helper());
+}
+
+QuicConnectionId MockableQuicClient::GenerateNewConnectionId() {
+ return connection_id_overridden_ ? override_connection_id_
+ : QuicClient::GenerateNewConnectionId();
+}
+
+void MockableQuicClient::UseConnectionId(QuicConnectionId connection_id) {
+ connection_id_overridden_ = true;
+ override_connection_id_ = connection_id;
+}
+
+void MockableQuicClient::UseWriter(QuicPacketWriterWrapper* writer) {
+ mockable_network_helper()->UseWriter(writer);
+}
+
+void MockableQuicClient::set_peer_address(const QuicSocketAddress& address) {
+ mockable_network_helper()->set_peer_address(address);
+}
+
+const QuicReceivedPacket* MockableQuicClient::last_incoming_packet() {
+ return mockable_network_helper()->last_incoming_packet();
+}
+
+void MockableQuicClient::set_track_last_incoming_packet(bool track) {
+ mockable_network_helper()->set_track_last_incoming_packet(track);
+}
+
+QuicTestClient::QuicTestClient(
+ QuicSocketAddress server_address,
+ const std::string& server_hostname,
+ const ParsedQuicVersionVector& supported_versions)
+ : QuicTestClient(server_address,
+ server_hostname,
+ QuicConfig(),
+ supported_versions) {}
+
+QuicTestClient::QuicTestClient(
+ QuicSocketAddress server_address,
+ const std::string& server_hostname,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions)
+ : client_(new MockableQuicClient(
+ server_address,
+ QuicServerId(server_hostname, server_address.port(), false),
+ config,
+ supported_versions,
+ &epoll_server_)) {
+ Initialize();
+}
+
+QuicTestClient::QuicTestClient(
+ QuicSocketAddress server_address,
+ const std::string& server_hostname,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : client_(new MockableQuicClient(
+ server_address,
+ QuicServerId(server_hostname, server_address.port(), false),
+ config,
+ supported_versions,
+ &epoll_server_,
+ std::move(proof_verifier))) {
+ Initialize();
+}
+
+QuicTestClient::QuicTestClient() = default;
+
+QuicTestClient::~QuicTestClient() {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ stream.second->set_visitor(nullptr);
+ }
+}
+
+void QuicTestClient::Initialize() {
+ priority_ = 3;
+ connect_attempted_ = false;
+ auto_reconnect_ = false;
+ buffer_body_ = true;
+ num_requests_ = 0;
+ num_responses_ = 0;
+ ClearPerConnectionState();
+ // As chrome will generally do this, we want it to be the default when it's
+ // not overridden.
+ if (!client_->config()->HasSetBytesForConnectionIdToSend()) {
+ client_->config()->SetBytesForConnectionIdToSend(0);
+ }
+}
+
+void QuicTestClient::SetUserAgentID(const std::string& user_agent_id) {
+ client_->SetUserAgentID(user_agent_id);
+}
+
+ssize_t QuicTestClient::SendRequest(const std::string& uri) {
+ spdy::SpdyHeaderBlock headers;
+ if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
+ return 0;
+ }
+ return SendMessage(headers, "");
+}
+
+ssize_t QuicTestClient::SendRequestAndRstTogether(const std::string& uri) {
+ spdy::SpdyHeaderBlock headers;
+ if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
+ return 0;
+ }
+
+ QuicSpdyClientSession* session = client()->client_session();
+ QuicConnection::ScopedPacketFlusher flusher(
+ session->connection(), QuicConnection::SEND_ACK_IF_PENDING);
+ ssize_t ret = SendMessage(headers, "", /*fin=*/true, /*flush=*/false);
+
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId(
+ session->connection()->transport_version(), 0);
+ session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0);
+ return ret;
+}
+
+void QuicTestClient::SendRequestsAndWaitForResponses(
+ const std::vector<std::string>& url_list) {
+ for (const std::string& url : url_list) {
+ SendRequest(url);
+ }
+ while (client()->WaitForEvents()) {
+ }
+}
+
+ssize_t QuicTestClient::GetOrCreateStreamAndSendRequest(
+ const spdy::SpdyHeaderBlock* headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ if (headers) {
+ QuicClientPushPromiseIndex::TryHandle* handle;
+ QuicAsyncStatus rv =
+ client()->push_promise_index()->Try(*headers, this, &handle);
+ if (rv == QUIC_SUCCESS)
+ return 1;
+ if (rv == QUIC_PENDING) {
+ // May need to retry request if asynchronous rendezvous fails.
+ std::unique_ptr<spdy::SpdyHeaderBlock> new_headers(
+ new spdy::SpdyHeaderBlock(headers->Clone()));
+ push_promise_data_to_resend_ = QuicMakeUnique<TestClientDataToResend>(
+ std::move(new_headers), body, fin, this, std::move(ack_listener));
+ return 1;
+ }
+ }
+
+ // Maybe it's better just to overload this. it's just that we need
+ // for the GetOrCreateStream function to call something else...which
+ // is icky and complicated, but maybe not worse than this.
+ QuicSpdyClientStream* stream = GetOrCreateStream();
+ if (stream == nullptr) {
+ return 0;
+ }
+ QuicSpdyStreamPeer::set_ack_listener(stream, ack_listener);
+
+ ssize_t ret = 0;
+ if (headers != nullptr) {
+ spdy::SpdyHeaderBlock spdy_headers(headers->Clone());
+ if (spdy_headers[":authority"].as_string().empty()) {
+ spdy_headers[":authority"] = client_->server_id().host();
+ }
+ ret = stream->SendRequest(std::move(spdy_headers), body, fin);
+ ++num_requests_;
+ } else {
+ stream->WriteOrBufferBody(std::string(body), fin);
+ ret = body.length();
+ }
+ if (GetQuicReloadableFlag(enable_quic_stateless_reject_support)) {
+ std::unique_ptr<spdy::SpdyHeaderBlock> new_headers;
+ if (headers) {
+ new_headers = QuicMakeUnique<spdy::SpdyHeaderBlock>(headers->Clone());
+ }
+ std::unique_ptr<QuicSpdyClientBase::QuicDataToResend> data_to_resend(
+ new TestClientDataToResend(std::move(new_headers), body, fin, this,
+ ack_listener));
+ client()->MaybeAddQuicDataToResend(std::move(data_to_resend));
+ }
+ return ret;
+}
+
+ssize_t QuicTestClient::SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body) {
+ return SendMessage(headers, body, /*fin=*/true);
+}
+
+ssize_t QuicTestClient::SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ return SendMessage(headers, body, fin, /*flush=*/true);
+}
+
+ssize_t QuicTestClient::SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin,
+ bool flush) {
+ // Always force creation of a stream for SendMessage.
+ latest_created_stream_ = nullptr;
+
+ ssize_t ret = GetOrCreateStreamAndSendRequest(&headers, body, fin, nullptr);
+
+ if (flush) {
+ WaitForWriteToFlush();
+ }
+ return ret;
+}
+
+ssize_t QuicTestClient::SendData(const std::string& data, bool last_data) {
+ return SendData(data, last_data, nullptr);
+}
+
+ssize_t QuicTestClient::SendData(
+ const std::string& data,
+ bool last_data,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+ return GetOrCreateStreamAndSendRequest(nullptr, QuicStringPiece(data),
+ last_data, std::move(ack_listener));
+}
+
+bool QuicTestClient::response_complete() const {
+ return response_complete_;
+}
+
+int64_t QuicTestClient::response_body_size() const {
+ return response_body_size_;
+}
+
+bool QuicTestClient::buffer_body() const {
+ return buffer_body_;
+}
+
+void QuicTestClient::set_buffer_body(bool buffer_body) {
+ buffer_body_ = buffer_body;
+}
+
+const std::string& QuicTestClient::response_body() const {
+ return response_;
+}
+
+std::string QuicTestClient::SendCustomSynchronousRequest(
+ const spdy::SpdyHeaderBlock& headers,
+ const std::string& body) {
+ // Clear connection state here and only track this synchronous request.
+ ClearPerConnectionState();
+ if (SendMessage(headers, body) == 0) {
+ QUIC_DLOG(ERROR) << "Failed the request for: " << headers.DebugString();
+ // Set the response_ explicitly. Otherwise response_ will contain the
+ // response from the previously successful request.
+ response_ = "";
+ } else {
+ WaitForResponse();
+ }
+ return response_;
+}
+
+std::string QuicTestClient::SendSynchronousRequest(const std::string& uri) {
+ spdy::SpdyHeaderBlock headers;
+ if (!PopulateHeaderBlockFromUrl(uri, &headers)) {
+ return "";
+ }
+ return SendCustomSynchronousRequest(headers, "");
+}
+
+void QuicTestClient::SendConnectivityProbing() {
+ QuicConnection* connection = client()->client_session()->connection();
+ connection->SendConnectivityProbingPacket(connection->writer(),
+ connection->peer_address());
+}
+
+void QuicTestClient::SetLatestCreatedStream(QuicSpdyClientStream* stream) {
+ latest_created_stream_ = stream;
+ if (latest_created_stream_ != nullptr) {
+ open_streams_[stream->id()] = stream;
+ stream->set_visitor(this);
+ }
+}
+
+QuicSpdyClientStream* QuicTestClient::GetOrCreateStream() {
+ if (!connect_attempted_ || auto_reconnect_) {
+ if (!connected()) {
+ Connect();
+ }
+ if (!connected()) {
+ return nullptr;
+ }
+ }
+ if (open_streams_.empty()) {
+ ClearPerConnectionState();
+ }
+ if (!latest_created_stream_) {
+ SetLatestCreatedStream(client_->CreateClientStream());
+ if (latest_created_stream_) {
+ latest_created_stream_->SetPriority(priority_);
+ }
+ }
+
+ return latest_created_stream_;
+}
+
+QuicErrorCode QuicTestClient::connection_error() {
+ return client()->connection_error();
+}
+
+MockableQuicClient* QuicTestClient::client() {
+ return client_.get();
+}
+
+const std::string& QuicTestClient::cert_common_name() const {
+ return reinterpret_cast<RecordingProofVerifier*>(client_->proof_verifier())
+ ->common_name();
+}
+
+const std::string& QuicTestClient::cert_sct() const {
+ return reinterpret_cast<RecordingProofVerifier*>(client_->proof_verifier())
+ ->cert_sct();
+}
+
+QuicTagValueMap QuicTestClient::GetServerConfig() const {
+ QuicCryptoClientConfig* config = client_->crypto_config();
+ 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();
+ }
+}
+
+bool QuicTestClient::connected() const {
+ return client_->connected();
+}
+
+void QuicTestClient::Connect() {
+ DCHECK(!connected());
+ if (!connect_attempted_) {
+ client_->Initialize();
+ }
+
+ // If we've been asked to override SNI, set it now
+ if (override_sni_set_) {
+ client_->set_server_id(
+ QuicServerId(override_sni_, address().port(), false));
+ }
+
+ client_->Connect();
+ connect_attempted_ = true;
+}
+
+void QuicTestClient::ResetConnection() {
+ Disconnect();
+ Connect();
+}
+
+void QuicTestClient::Disconnect() {
+ ClearPerConnectionState();
+ client_->Disconnect();
+ connect_attempted_ = false;
+}
+
+QuicSocketAddress QuicTestClient::local_address() const {
+ return client_->network_helper()->GetLatestClientAddress();
+}
+
+void QuicTestClient::ClearPerRequestState() {
+ stream_error_ = QUIC_STREAM_NO_ERROR;
+ response_ = "";
+ response_complete_ = false;
+ response_headers_complete_ = false;
+ preliminary_headers_.clear();
+ response_headers_.clear();
+ response_trailers_.clear();
+ bytes_read_ = 0;
+ bytes_written_ = 0;
+ response_body_size_ = 0;
+}
+
+bool QuicTestClient::HaveActiveStream() {
+ return push_promise_data_to_resend_.get() || !open_streams_.empty();
+}
+
+bool QuicTestClient::WaitUntil(int timeout_ms, std::function<bool()> trigger) {
+ int64_t timeout_us = timeout_ms * kNumMicrosPerMilli;
+ int64_t old_timeout_us = epoll_server()->timeout_in_us_for_test();
+ if (timeout_us > 0) {
+ epoll_server()->set_timeout_in_us(timeout_us);
+ }
+ const QuicClock* clock =
+ QuicConnectionPeer::GetHelper(client()->session()->connection())
+ ->GetClock();
+ QuicTime end_waiting_time =
+ clock->Now() + QuicTime::Delta::FromMicroseconds(timeout_us);
+ while (HaveActiveStream() && !(trigger && trigger()) &&
+ (timeout_us < 0 || clock->Now() < end_waiting_time)) {
+ client_->WaitForEvents();
+ }
+ ReadNextResponse();
+ if (timeout_us > 0) {
+ epoll_server()->set_timeout_in_us(old_timeout_us);
+ }
+ if (trigger && !trigger()) {
+ VLOG(1) << "Client WaitUntil returning with trigger returning false."
+ << QuicStackTrace();
+ return false;
+ }
+ return true;
+}
+
+ssize_t QuicTestClient::Send(const void* buffer, size_t size) {
+ return SendData(std::string(static_cast<const char*>(buffer), size), false);
+}
+
+bool QuicTestClient::response_headers_complete() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ if (stream.second->headers_decompressed()) {
+ return true;
+ }
+ }
+ return response_headers_complete_;
+}
+
+const spdy::SpdyHeaderBlock* QuicTestClient::response_headers() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ size_t bytes_read =
+ stream.second->stream_bytes_read() + stream.second->header_bytes_read();
+ if (bytes_read > 0) {
+ response_headers_ = stream.second->response_headers().Clone();
+ break;
+ }
+ }
+ return &response_headers_;
+}
+
+const spdy::SpdyHeaderBlock* QuicTestClient::preliminary_headers() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ size_t bytes_read =
+ stream.second->stream_bytes_read() + stream.second->header_bytes_read();
+ if (bytes_read > 0) {
+ preliminary_headers_ = stream.second->preliminary_headers().Clone();
+ break;
+ }
+ }
+ return &preliminary_headers_;
+}
+
+const spdy::SpdyHeaderBlock& QuicTestClient::response_trailers() const {
+ return response_trailers_;
+}
+
+int64_t QuicTestClient::response_size() const {
+ return bytes_read();
+}
+
+size_t QuicTestClient::bytes_read() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ size_t bytes_read = stream.second->total_body_bytes_read() +
+ stream.second->header_bytes_read();
+ if (bytes_read > 0) {
+ return bytes_read;
+ }
+ }
+ return bytes_read_;
+}
+
+size_t QuicTestClient::bytes_written() const {
+ for (std::pair<QuicStreamId, QuicSpdyClientStream*> stream : open_streams_) {
+ size_t bytes_written = stream.second->stream_bytes_written() +
+ stream.second->header_bytes_written();
+ if (bytes_written > 0) {
+ return bytes_written;
+ }
+ }
+ return bytes_written_;
+}
+
+void QuicTestClient::OnClose(QuicSpdyStream* stream) {
+ if (stream == nullptr) {
+ return;
+ }
+ // Always close the stream, regardless of whether it was the last stream
+ // written.
+ client()->OnClose(stream);
+ ++num_responses_;
+ if (!QuicContainsKey(open_streams_, stream->id())) {
+ return;
+ }
+ if (latest_created_stream_ == stream) {
+ latest_created_stream_ = nullptr;
+ }
+ QuicSpdyClientStream* client_stream =
+ static_cast<QuicSpdyClientStream*>(stream);
+ QuicStreamId id = client_stream->id();
+ closed_stream_states_.insert(std::make_pair(
+ id,
+ PerStreamState(
+ client_stream->stream_error(), true,
+ client_stream->headers_decompressed(),
+ client_stream->response_headers(),
+ client_stream->preliminary_headers(),
+ (buffer_body() ? client_stream->data() : ""),
+ client_stream->received_trailers(),
+ // Use NumBytesConsumed to avoid counting retransmitted stream frames.
+ client_stream->total_body_bytes_read() +
+ client_stream->header_bytes_read(),
+ client_stream->stream_bytes_written() +
+ client_stream->header_bytes_written(),
+ client_stream->data().size())));
+ open_streams_.erase(id);
+}
+
+bool QuicTestClient::CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) {
+ return true;
+}
+
+void QuicTestClient::OnRendezvousResult(QuicSpdyStream* stream) {
+ std::unique_ptr<TestClientDataToResend> data_to_resend =
+ std::move(push_promise_data_to_resend_);
+ SetLatestCreatedStream(static_cast<QuicSpdyClientStream*>(stream));
+ if (stream) {
+ stream->OnBodyAvailable();
+ } else if (data_to_resend) {
+ data_to_resend->Resend();
+ }
+}
+
+void QuicTestClient::UseWriter(QuicPacketWriterWrapper* writer) {
+ client_->UseWriter(writer);
+}
+
+void QuicTestClient::UseConnectionId(QuicConnectionId connection_id) {
+ DCHECK(!connected());
+ client_->UseConnectionId(connection_id);
+}
+
+bool QuicTestClient::MigrateSocket(const QuicIpAddress& new_host) {
+ return client_->MigrateSocket(new_host);
+}
+
+bool QuicTestClient::MigrateSocketWithSpecifiedPort(
+ const QuicIpAddress& new_host,
+ int port) {
+ client_->set_local_port(port);
+ return client_->MigrateSocket(new_host);
+}
+
+QuicIpAddress QuicTestClient::bind_to_address() const {
+ return client_->bind_to_address();
+}
+
+void QuicTestClient::set_bind_to_address(QuicIpAddress address) {
+ client_->set_bind_to_address(address);
+}
+
+const QuicSocketAddress& QuicTestClient::address() const {
+ return client_->server_address();
+}
+
+void QuicTestClient::WaitForWriteToFlush() {
+ while (connected() && client()->session()->HasDataToWrite()) {
+ client_->WaitForEvents();
+ }
+}
+
+QuicTestClient::TestClientDataToResend::TestClientDataToResend(
+ std::unique_ptr<spdy::SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicTestClient* test_client,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener)
+ : QuicClient::QuicDataToResend(std::move(headers), body, fin),
+ test_client_(test_client),
+ ack_listener_(std::move(ack_listener)) {}
+
+QuicTestClient::TestClientDataToResend::~TestClientDataToResend() = default;
+
+void QuicTestClient::TestClientDataToResend::Resend() {
+ test_client_->GetOrCreateStreamAndSendRequest(headers_.get(), body_, fin_,
+ ack_listener_);
+ headers_.reset();
+}
+
+QuicTestClient::PerStreamState::PerStreamState(const PerStreamState& other)
+ : stream_error(other.stream_error),
+ response_complete(other.response_complete),
+ response_headers_complete(other.response_headers_complete),
+ response_headers(other.response_headers.Clone()),
+ preliminary_headers(other.preliminary_headers.Clone()),
+ response(other.response),
+ response_trailers(other.response_trailers.Clone()),
+ bytes_read(other.bytes_read),
+ bytes_written(other.bytes_written),
+ response_body_size(other.response_body_size) {}
+
+QuicTestClient::PerStreamState::PerStreamState(
+ QuicRstStreamErrorCode stream_error,
+ bool response_complete,
+ bool response_headers_complete,
+ const spdy::SpdyHeaderBlock& response_headers,
+ const spdy::SpdyHeaderBlock& preliminary_headers,
+ const std::string& response,
+ const spdy::SpdyHeaderBlock& response_trailers,
+ uint64_t bytes_read,
+ uint64_t bytes_written,
+ int64_t response_body_size)
+ : stream_error(stream_error),
+ response_complete(response_complete),
+ response_headers_complete(response_headers_complete),
+ response_headers(response_headers.Clone()),
+ preliminary_headers(preliminary_headers.Clone()),
+ response(response),
+ response_trailers(response_trailers.Clone()),
+ bytes_read(bytes_read),
+ bytes_written(bytes_written),
+ response_body_size(response_body_size) {}
+
+QuicTestClient::PerStreamState::~PerStreamState() = default;
+
+bool QuicTestClient::PopulateHeaderBlockFromUrl(
+ const std::string& uri,
+ spdy::SpdyHeaderBlock* headers) {
+ std::string url;
+ if (QuicTextUtils::StartsWith(uri, "https://") ||
+ QuicTextUtils::StartsWith(uri, "http://")) {
+ url = uri;
+ } else if (uri[0] == '/') {
+ url = "https://" + client_->server_id().host() + uri;
+ } else {
+ url = "https://" + uri;
+ }
+ return SpdyUtils::PopulateHeaderBlockFromUrl(url, headers);
+}
+
+void QuicTestClient::ReadNextResponse() {
+ if (closed_stream_states_.empty()) {
+ return;
+ }
+
+ PerStreamState state(closed_stream_states_.front().second);
+
+ stream_error_ = state.stream_error;
+ response_ = state.response;
+ response_complete_ = state.response_complete;
+ response_headers_complete_ = state.response_headers_complete;
+ preliminary_headers_ = state.preliminary_headers.Clone();
+ response_headers_ = state.response_headers.Clone();
+ response_trailers_ = state.response_trailers.Clone();
+ bytes_read_ = state.bytes_read;
+ bytes_written_ = state.bytes_written;
+ response_body_size_ = state.response_body_size;
+
+ closed_stream_states_.pop_front();
+}
+
+void QuicTestClient::ClearPerConnectionState() {
+ ClearPerRequestState();
+ open_streams_.clear();
+ closed_stream_states_.clear();
+ latest_created_stream_ = nullptr;
+}
+
+void QuicTestClient::WaitForDelayedAcks() {
+ // kWaitDuration is a period of time that is long enough for all delayed
+ // acks to be sent and received on the other end.
+ const QuicTime::Delta kWaitDuration =
+ 4 * QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+
+ const QuicClock* clock = client()->client_session()->connection()->clock();
+
+ QuicTime wait_until = clock->ApproximateNow() + kWaitDuration;
+ while (clock->ApproximateNow() < wait_until) {
+ // This waits for up to 50 ms.
+ client()->WaitForEvents();
+ }
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..02a0ed6cf8f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_client.h
@@ -0,0 +1,413 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+
+namespace quic {
+
+class ProofVerifier;
+class QuicPacketWriterWrapper;
+
+namespace test {
+
+class MockableQuicClientEpollNetworkHelper;
+
+// A quic client which allows mocking out reads and writes.
+class MockableQuicClient : public QuicClient {
+ public:
+ MockableQuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server);
+
+ MockableQuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server);
+
+ MockableQuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ MockableQuicClient(const MockableQuicClient&) = delete;
+ MockableQuicClient& operator=(const MockableQuicClient&) = delete;
+
+ ~MockableQuicClient() override;
+
+ QuicConnectionId GenerateNewConnectionId() override;
+ void UseConnectionId(QuicConnectionId connection_id);
+
+ void UseWriter(QuicPacketWriterWrapper* writer);
+ void set_peer_address(const QuicSocketAddress& address);
+ // The last incoming packet, iff |track_last_incoming_packet| is true.
+ const QuicReceivedPacket* last_incoming_packet();
+ // If true, copy each packet from ProcessPacket into |last_incoming_packet|
+ void set_track_last_incoming_packet(bool track);
+
+ // Casts the network helper to a MockableQuicClientEpollNetworkHelper.
+ MockableQuicClientEpollNetworkHelper* mockable_network_helper();
+ const MockableQuicClientEpollNetworkHelper* mockable_network_helper() const;
+
+ private:
+ // ConnectionId to use, if connection_id_overridden_
+ QuicConnectionId override_connection_id_;
+ bool connection_id_overridden_;
+ CachedNetworkParameters cached_network_paramaters_;
+};
+
+// A toy QUIC client used for testing.
+class QuicTestClient : public QuicSpdyStream::Visitor,
+ public QuicClientPushPromiseIndex::Delegate {
+ public:
+ QuicTestClient(QuicSocketAddress server_address,
+ const std::string& server_hostname,
+ const ParsedQuicVersionVector& supported_versions);
+ QuicTestClient(QuicSocketAddress server_address,
+ const std::string& server_hostname,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions);
+ QuicTestClient(QuicSocketAddress server_address,
+ const std::string& server_hostname,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+
+ ~QuicTestClient() override;
+
+ // Sets the |user_agent_id| of the |client_|.
+ void SetUserAgentID(const std::string& user_agent_id);
+
+ // Wraps data in a quic packet and sends it.
+ ssize_t SendData(const std::string& data, bool last_data);
+ // As above, but |delegate| will be notified when |data| is ACKed.
+ ssize_t SendData(
+ const std::string& data,
+ bool last_data,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ // Clears any outstanding state and sends a simple GET of 'uri' to the
+ // server. Returns 0 if the request failed and no bytes were written.
+ ssize_t SendRequest(const std::string& uri);
+ // Send a request R and a RST_FRAME which resets R, in the same packet.
+ ssize_t SendRequestAndRstTogether(const std::string& uri);
+ // Sends requests for all the urls and waits for the responses. To process
+ // the individual responses as they are returned, the caller should use the
+ // set the response_listener on the client().
+ void SendRequestsAndWaitForResponses(
+ const std::vector<std::string>& url_list);
+ // Sends a request containing |headers| and |body| and returns the number of
+ // bytes sent (the size of the serialized request headers and body).
+ ssize_t SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body);
+ // Sends a request containing |headers| and |body| with the fin bit set to
+ // |fin| and returns the number of bytes sent (the size of the serialized
+ // request headers and body).
+ ssize_t SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+ // Sends a request containing |headers| and |body| with the fin bit set to
+ // |fin| and returns the number of bytes sent (the size of the serialized
+ // request headers and body). If |flush| is true, will wait for the message to
+ // be flushed before returning.
+ ssize_t SendMessage(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin,
+ bool flush);
+ // Sends a request containing |headers| and |body|, waits for the response,
+ // and returns the response body.
+ std::string SendCustomSynchronousRequest(const spdy::SpdyHeaderBlock& headers,
+ const std::string& body);
+ // Sends a GET request for |uri|, waits for the response, and returns the
+ // response body.
+ std::string SendSynchronousRequest(const std::string& uri);
+ void SendConnectivityProbing();
+ void Connect();
+ void ResetConnection();
+ void Disconnect();
+ QuicSocketAddress local_address() const;
+ void ClearPerRequestState();
+ bool WaitUntil(int timeout_ms, std::function<bool()> trigger);
+ ssize_t Send(const void* buffer, size_t size);
+ bool connected() const;
+ bool buffer_body() const;
+ void set_buffer_body(bool buffer_body);
+
+ // Getters for stream state. Please note, these getters are divided into two
+ // groups. 1) returns state which only get updated once a complete response
+ // is received. 2) returns state of the oldest active stream which have
+ // received partial response (if any).
+ // Group 1.
+ const spdy::SpdyHeaderBlock& response_trailers() const;
+ bool response_complete() const;
+ int64_t response_body_size() const;
+ const std::string& response_body() const;
+ // Group 2.
+ bool response_headers_complete() const;
+ const spdy::SpdyHeaderBlock* response_headers() const;
+ const spdy::SpdyHeaderBlock* preliminary_headers() const;
+ int64_t response_size() const;
+ size_t bytes_read() const;
+ size_t bytes_written() const;
+
+ // Returns once at least one complete response or a connection close has been
+ // received from the server. If responses are received for multiple (say 2)
+ // streams, next WaitForResponse will return immediately.
+ void WaitForResponse() { WaitForResponseForMs(-1); }
+
+ // Returns once some data is received on any open streams or at least one
+ // complete response is received from the server.
+ void WaitForInitialResponse() { WaitForInitialResponseForMs(-1); }
+
+ // Returns once at least one complete response or a connection close has been
+ // received from the server, or once the timeout expires. -1 means no timeout.
+ // If responses are received for multiple (say 2) streams, next
+ // WaitForResponseForMs will return immediately.
+ void WaitForResponseForMs(int timeout_ms) {
+ WaitUntil(timeout_ms, [this]() { return !closed_stream_states_.empty(); });
+ if (response_complete()) {
+ VLOG(1) << "Client received response:"
+ << response_headers()->DebugString() << response_body();
+ }
+ }
+
+ // Returns once some data is received on any open streams or at least one
+ // complete response is received from the server, or once the timeout
+ // expires. -1 means no timeout.
+ void WaitForInitialResponseForMs(int timeout_ms) {
+ WaitUntil(timeout_ms, [this]() { return response_size() != 0; });
+ }
+
+ // Migrate local address to <|new_host|, a random port>.
+ // Return whether the migration succeeded.
+ bool MigrateSocket(const QuicIpAddress& new_host);
+ // Migrate local address to <|new_host|, |port|>.
+ // Return whether the migration succeeded.
+ bool MigrateSocketWithSpecifiedPort(const QuicIpAddress& new_host, int port);
+ QuicIpAddress bind_to_address() const;
+ void set_bind_to_address(QuicIpAddress address);
+ const QuicSocketAddress& address() const;
+
+ // From QuicSpdyStream::Visitor
+ void OnClose(QuicSpdyStream* stream) override;
+
+ // From QuicClientPushPromiseIndex::Delegate
+ bool CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) override;
+ void OnRendezvousResult(QuicSpdyStream*) override;
+
+ // Configures client_ to take ownership of and use the writer.
+ // Must be called before initial connect.
+ void UseWriter(QuicPacketWriterWrapper* writer);
+ // If the given ConnectionId is nonzero, configures client_ to use a specific
+ // ConnectionId instead of a random one.
+ void UseConnectionId(QuicConnectionId connection_id);
+
+ // Returns nullptr if the maximum number of streams have already been created.
+ QuicSpdyClientStream* GetOrCreateStream();
+
+ // Calls GetOrCreateStream(), sends the request on the stream, and
+ // stores the request in case it needs to be resent. If |headers| is
+ // null, only the body will be sent on the stream.
+ ssize_t GetOrCreateStreamAndSendRequest(
+ const spdy::SpdyHeaderBlock* headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ QuicRstStreamErrorCode stream_error() { return stream_error_; }
+ QuicErrorCode connection_error();
+
+ MockableQuicClient* client();
+
+ // cert_common_name returns the common name value of the server's certificate,
+ // or the empty std::string if no certificate was presented.
+ const std::string& cert_common_name() const;
+
+ // cert_sct returns the signed timestamp of the server's certificate,
+ // 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;
+
+ void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; }
+
+ void set_priority(spdy::SpdyPriority priority) { priority_ = priority; }
+
+ void WaitForWriteToFlush();
+
+ QuicEpollServer* epoll_server() { return &epoll_server_; }
+
+ size_t num_requests() const { return num_requests_; }
+
+ size_t num_responses() const { return num_responses_; }
+
+ void set_server_address(const QuicSocketAddress& server_address) {
+ client_->set_server_address(server_address);
+ }
+
+ void set_peer_address(const QuicSocketAddress& address) {
+ client_->set_peer_address(address);
+ }
+
+ // Explicitly set the SNI value for this client, overriding the default
+ // behavior which extracts the SNI value from the request URL.
+ void OverrideSni(const std::string& sni) {
+ override_sni_set_ = true;
+ override_sni_ = sni;
+ }
+
+ void Initialize();
+
+ void set_client(MockableQuicClient* client) { client_.reset(client); }
+
+ // Given |uri|, populates the fields in |headers| for a simple GET
+ // request. If |uri| is a relative URL, the QuicServerId will be
+ // use to specify the authority.
+ bool PopulateHeaderBlockFromUrl(const std::string& uri,
+ spdy::SpdyHeaderBlock* headers);
+
+ // Waits for a period of time that is long enough to receive all delayed acks
+ // sent by peer.
+ void WaitForDelayedAcks();
+
+ QuicSpdyClientStream* latest_created_stream() {
+ return latest_created_stream_;
+ }
+
+ protected:
+ QuicTestClient();
+ QuicTestClient(const QuicTestClient&) = delete;
+ QuicTestClient& operator=(const QuicTestClient&) = delete;
+
+ private:
+ class TestClientDataToResend : public QuicClient::QuicDataToResend {
+ public:
+ TestClientDataToResend(
+ std::unique_ptr<spdy::SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicTestClient* test_client,
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+
+ ~TestClientDataToResend() override;
+
+ void Resend() override;
+
+ protected:
+ QuicTestClient* test_client_;
+ QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener_;
+ };
+
+ // PerStreamState of a stream is updated when it is closed.
+ struct PerStreamState {
+ PerStreamState(const PerStreamState& other);
+ PerStreamState(QuicRstStreamErrorCode stream_error,
+ bool response_complete,
+ bool response_headers_complete,
+ const spdy::SpdyHeaderBlock& response_headers,
+ const spdy::SpdyHeaderBlock& preliminary_headers,
+ const std::string& response,
+ const spdy::SpdyHeaderBlock& response_trailers,
+ uint64_t bytes_read,
+ uint64_t bytes_written,
+ int64_t response_body_size);
+ ~PerStreamState();
+
+ QuicRstStreamErrorCode stream_error;
+ bool response_complete;
+ bool response_headers_complete;
+ spdy::SpdyHeaderBlock response_headers;
+ spdy::SpdyHeaderBlock preliminary_headers;
+ std::string response;
+ spdy::SpdyHeaderBlock response_trailers;
+ uint64_t bytes_read;
+ uint64_t bytes_written;
+ int64_t response_body_size;
+ };
+
+ bool HaveActiveStream();
+
+ // Read oldest received response and remove it from closed_stream_states_.
+ void ReadNextResponse();
+
+ // Clear open_streams_, closed_stream_states_ and reset
+ // latest_created_stream_.
+ void ClearPerConnectionState();
+
+ // Update latest_created_stream_, add |stream| to open_streams_ and starts
+ // tracking its state.
+ void SetLatestCreatedStream(QuicSpdyClientStream* stream);
+
+ QuicEpollServer epoll_server_;
+ std::unique_ptr<MockableQuicClient> client_; // The actual client
+ QuicSpdyClientStream* latest_created_stream_;
+ std::map<QuicStreamId, QuicSpdyClientStream*> open_streams_;
+ // Received responses of closed streams.
+ QuicLinkedHashMap<QuicStreamId, PerStreamState> closed_stream_states_;
+
+ QuicRstStreamErrorCode stream_error_;
+
+ bool response_complete_;
+ bool response_headers_complete_;
+ mutable spdy::SpdyHeaderBlock preliminary_headers_;
+ mutable spdy::SpdyHeaderBlock response_headers_;
+
+ // Parsed response trailers (if present), copied from the stream in OnClose.
+ spdy::SpdyHeaderBlock response_trailers_;
+
+ spdy::SpdyPriority priority_;
+ std::string response_;
+ // bytes_read_ and bytes_written_ are updated only when stream_ is released;
+ // prefer bytes_read() and bytes_written() member functions.
+ uint64_t bytes_read_;
+ uint64_t bytes_written_;
+ // The number of HTTP body bytes received.
+ int64_t response_body_size_;
+ // True if we tried to connect already since the last call to Disconnect().
+ bool connect_attempted_;
+ // The client will auto-connect exactly once before sending data. If
+ // something causes a connection reset, it will not automatically reconnect
+ // unless auto_reconnect_ is true.
+ bool auto_reconnect_;
+ // Should we buffer the response body? Defaults to true.
+ bool buffer_body_;
+ // For async push promise rendezvous, validation may fail in which
+ // case the request should be retried.
+ std::unique_ptr<TestClientDataToResend> push_promise_data_to_resend_;
+ // Number of requests/responses this client has sent/received.
+ size_t num_requests_;
+ size_t num_responses_;
+
+ // If set, this value is used for the connection SNI, overriding the usual
+ // logic which extracts the SNI from the request URL.
+ bool override_sni_set_ = false;
+ std::string override_sni_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_CLIENT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc
new file mode 100644
index 00000000000..bda2148731c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_server.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+namespace quic {
+
+namespace test {
+
+class CustomStreamSession : public QuicSimpleServerSession {
+ public:
+ CustomStreamSession(
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicTestServer::StreamFactory* stream_factory,
+ QuicTestServer::CryptoStreamFactory* crypto_stream_factory,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerSession(config,
+ supported_versions,
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache,
+ quic_simple_server_backend),
+ stream_factory_(stream_factory),
+ crypto_stream_factory_(crypto_stream_factory) {}
+
+ QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override {
+ if (!ShouldCreateIncomingStream(id)) {
+ return nullptr;
+ }
+ if (stream_factory_) {
+ QuicSpdyStream* stream =
+ stream_factory_->CreateStream(id, this, server_backend());
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+ }
+ return QuicSimpleServerSession::CreateIncomingStream(id);
+ }
+
+ QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) override {
+ if (crypto_stream_factory_) {
+ return crypto_stream_factory_->CreateCryptoStream(crypto_config, this);
+ }
+ return QuicSimpleServerSession::CreateQuicCryptoServerStream(
+ crypto_config, compressed_certs_cache);
+ }
+
+ private:
+ QuicTestServer::StreamFactory* stream_factory_; // Not owned.
+ QuicTestServer::CryptoStreamFactory* crypto_stream_factory_; // Not owned.
+};
+
+class QuicTestDispatcher : public QuicSimpleDispatcher {
+ public:
+ QuicTestDispatcher(
+ const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length)
+ : QuicSimpleDispatcher(config,
+ crypto_config,
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ quic_simple_server_backend,
+ expected_connection_id_length),
+ session_factory_(nullptr),
+ stream_factory_(nullptr),
+ crypto_stream_factory_(nullptr) {}
+
+ QuicServerSessionBase* CreateQuicSession(
+ QuicConnectionId id,
+ const QuicSocketAddress& client,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) override {
+ QuicReaderMutexLock lock(&factory_lock_);
+ if (session_factory_ == nullptr && stream_factory_ == nullptr &&
+ crypto_stream_factory_ == nullptr) {
+ return QuicSimpleDispatcher::CreateQuicSession(id, client, alpn, version);
+ }
+ QuicConnection* connection =
+ new QuicConnection(id, client, helper(), alarm_factory(), writer(),
+ /* owns_writer= */ false, Perspective::IS_SERVER,
+ ParsedQuicVersionVector{version});
+
+ QuicServerSessionBase* session = nullptr;
+ if (stream_factory_ != nullptr || crypto_stream_factory_ != nullptr) {
+ session = new CustomStreamSession(
+ config(), GetSupportedVersions(), connection, this, session_helper(),
+ crypto_config(), compressed_certs_cache(), stream_factory_,
+ crypto_stream_factory_, server_backend());
+ } else {
+ session = session_factory_->CreateSession(
+ config(), connection, this, session_helper(), crypto_config(),
+ compressed_certs_cache(), server_backend());
+ }
+ session->Initialize();
+ return session;
+ }
+
+ void SetSessionFactory(QuicTestServer::SessionFactory* factory) {
+ QuicWriterMutexLock lock(&factory_lock_);
+ DCHECK(session_factory_ == nullptr);
+ DCHECK(stream_factory_ == nullptr);
+ DCHECK(crypto_stream_factory_ == nullptr);
+ session_factory_ = factory;
+ }
+
+ void SetStreamFactory(QuicTestServer::StreamFactory* factory) {
+ QuicWriterMutexLock lock(&factory_lock_);
+ DCHECK(session_factory_ == nullptr);
+ DCHECK(stream_factory_ == nullptr);
+ stream_factory_ = factory;
+ }
+
+ void SetCryptoStreamFactory(QuicTestServer::CryptoStreamFactory* factory) {
+ QuicWriterMutexLock lock(&factory_lock_);
+ DCHECK(session_factory_ == nullptr);
+ DCHECK(crypto_stream_factory_ == nullptr);
+ crypto_stream_factory_ = factory;
+ }
+
+ private:
+ QuicMutex factory_lock_;
+ QuicTestServer::SessionFactory* session_factory_; // Not owned.
+ QuicTestServer::StreamFactory* stream_factory_; // Not owned.
+ QuicTestServer::CryptoStreamFactory* crypto_stream_factory_; // Not owned.
+};
+
+QuicTestServer::QuicTestServer(
+ std::unique_ptr<ProofSource> proof_source,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicServer(std::move(proof_source), quic_simple_server_backend) {}
+
+QuicTestServer::QuicTestServer(
+ std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicTestServer(std::move(proof_source),
+ config,
+ supported_versions,
+ quic_simple_server_backend,
+ kQuicDefaultConnectionIdLength) {}
+
+QuicTestServer::QuicTestServer(
+ std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length)
+ : QuicServer(std::move(proof_source),
+ config,
+ QuicCryptoServerConfig::ConfigOptions(),
+ supported_versions,
+ quic_simple_server_backend,
+ expected_connection_id_length) {}
+
+QuicDispatcher* QuicTestServer::CreateQuicDispatcher() {
+ return new QuicTestDispatcher(
+ &config(), &crypto_config(), version_manager(),
+ QuicMakeUnique<QuicEpollConnectionHelper>(epoll_server(),
+ QuicAllocator::BUFFER_POOL),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())),
+ QuicMakeUnique<QuicEpollAlarmFactory>(epoll_server()), server_backend(),
+ expected_connection_id_length());
+}
+
+void QuicTestServer::SetSessionFactory(SessionFactory* factory) {
+ DCHECK(dispatcher());
+ static_cast<QuicTestDispatcher*>(dispatcher())->SetSessionFactory(factory);
+}
+
+void QuicTestServer::SetSpdyStreamFactory(StreamFactory* factory) {
+ static_cast<QuicTestDispatcher*>(dispatcher())->SetStreamFactory(factory);
+}
+
+void QuicTestServer::SetCryptoStreamFactory(CryptoStreamFactory* factory) {
+ static_cast<QuicTestDispatcher*>(dispatcher())
+ ->SetCryptoStreamFactory(factory);
+}
+
+/////////////////////////// TEST SESSIONS ///////////////////////////////
+
+ImmediateGoAwaySession::ImmediateGoAwaySession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerSession(config,
+ CurrentSupportedVersions(),
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache,
+ quic_simple_server_backend) {}
+
+void ImmediateGoAwaySession::OnStreamFrame(const QuicStreamFrame& frame) {
+ SendGoAway(QUIC_PEER_GOING_AWAY, "");
+ QuicSimpleServerSession::OnStreamFrame(frame);
+}
+
+} // namespace test
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.h
new file mode 100644
index 00000000000..52fb7c531d5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_server.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+namespace quic {
+
+namespace test {
+
+// A test server which enables easy creation of custom QuicServerSessions
+//
+// Eventually this may be extended to allow custom QuicConnections etc.
+class QuicTestServer : public QuicServer {
+ public:
+ // Factory for creating QuicServerSessions.
+ class SessionFactory {
+ public:
+ virtual ~SessionFactory() {}
+
+ // Returns a new session owned by the caller.
+ virtual QuicServerSessionBase* CreateSession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend) = 0;
+ };
+
+ // Factory for creating QuicSimpleServerStreams.
+ class StreamFactory {
+ public:
+ virtual ~StreamFactory() {}
+
+ // Returns a new stream owned by the caller.
+ virtual QuicSimpleServerStream* CreateStream(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ QuicSimpleServerBackend* quic_simple_server_backend) = 0;
+ };
+
+ class CryptoStreamFactory {
+ public:
+ virtual ~CryptoStreamFactory() {}
+
+ // Returns a new QuicCryptoServerStreamBase owned by the caller
+ virtual QuicCryptoServerStreamBase* CreateCryptoStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicServerSessionBase* session) = 0;
+ };
+
+ QuicTestServer(std::unique_ptr<ProofSource> proof_source,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicTestServer(std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicTestServer(std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length);
+
+ // Create a custom dispatcher which creates custom sessions.
+ QuicDispatcher* CreateQuicDispatcher() override;
+
+ // Sets a custom session factory, owned by the caller, for easy custom
+ // session logic. This is incompatible with setting a stream factory or a
+ // crypto stream factory.
+ void SetSessionFactory(SessionFactory* factory);
+
+ // Sets a custom stream factory, owned by the caller, for easy custom
+ // stream logic. This is incompatible with setting a session factory.
+ void SetSpdyStreamFactory(StreamFactory* factory);
+
+ // Sets a custom crypto stream factory, owned by the caller, for easy custom
+ // crypto logic. This is incompatible with setting a session factory.
+ void SetCryptoStreamFactory(CryptoStreamFactory* factory);
+};
+
+// Useful test sessions for the QuicTestServer.
+
+// Test session which sends a GOAWAY immedaitely on creation, before crypto
+// credentials have even been established.
+class ImmediateGoAwaySession : public QuicSimpleServerSession {
+ public:
+ ImmediateGoAwaySession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+
+ // Override to send GoAway.
+ void OnStreamFrame(const QuicStreamFrame& frame) override;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_SERVER_H_
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
new file mode 100644
index 00000000000..870a7b82e66
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.cc
@@ -0,0 +1,1148 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "third_party/boringssl/src/include/openssl/sha.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_creator.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace quic {
+namespace test {
+
+QuicConnectionId TestConnectionId() {
+ // Chosen by fair dice roll.
+ // Guaranteed to be random.
+ return TestConnectionId(42);
+}
+
+QuicConnectionId TestConnectionId(uint64_t connection_number) {
+ const uint64_t connection_id64_net =
+ QuicEndian::HostToNet64(connection_number);
+ return QuicConnectionId(reinterpret_cast<const char*>(&connection_id64_net),
+ sizeof(connection_id64_net));
+}
+
+QuicConnectionId TestConnectionIdNineBytesLong(uint64_t connection_number) {
+ const uint64_t connection_number_net =
+ QuicEndian::HostToNet64(connection_number);
+ char connection_id_bytes[9] = {};
+ static_assert(
+ sizeof(connection_id_bytes) == 1 + sizeof(connection_number_net),
+ "bad lengths");
+ memcpy(connection_id_bytes + 1, &connection_number_net,
+ sizeof(connection_number_net));
+ return QuicConnectionId(connection_id_bytes, sizeof(connection_id_bytes));
+}
+
+uint64_t TestConnectionIdToUInt64(QuicConnectionId connection_id) {
+ DCHECK_EQ(connection_id.length(), kQuicDefaultConnectionIdLength);
+ uint64_t connection_id64_net = 0;
+ memcpy(&connection_id64_net, connection_id.data(),
+ std::min<size_t>(static_cast<size_t>(connection_id.length()),
+ sizeof(connection_id64_net)));
+ return QuicEndian::NetToHost64(connection_id64_net);
+}
+
+QuicAckFrame InitAckFrame(const std::vector<QuicAckBlock>& ack_blocks) {
+ DCHECK_GT(ack_blocks.size(), 0u);
+
+ QuicAckFrame ack;
+ QuicPacketNumber end_of_previous_block(1);
+ for (const QuicAckBlock& block : ack_blocks) {
+ DCHECK_GE(block.start, end_of_previous_block);
+ DCHECK_GT(block.limit, block.start);
+ ack.packets.AddRange(block.start, block.limit);
+ end_of_previous_block = block.limit;
+ }
+
+ ack.largest_acked = ack.packets.Max();
+
+ return ack;
+}
+
+QuicAckFrame InitAckFrame(uint64_t largest_acked) {
+ return InitAckFrame(QuicPacketNumber(largest_acked));
+}
+
+QuicAckFrame InitAckFrame(QuicPacketNumber largest_acked) {
+ return InitAckFrame({{QuicPacketNumber(1), largest_acked + 1}});
+}
+
+QuicAckFrame MakeAckFrameWithAckBlocks(size_t num_ack_blocks,
+ uint64_t least_unacked) {
+ QuicAckFrame ack;
+ ack.largest_acked = QuicPacketNumber(2 * num_ack_blocks + least_unacked);
+ // Add enough received packets to get num_ack_blocks ack blocks.
+ for (QuicPacketNumber i = QuicPacketNumber(2);
+ i < QuicPacketNumber(2 * num_ack_blocks + 1); i += 2) {
+ ack.packets.Add(i + least_unacked);
+ }
+ return ack;
+}
+
+std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
+ QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ const size_t max_plaintext_size =
+ framer->GetMaxPlaintextSize(kMaxOutgoingPacketSize);
+ size_t packet_size = GetPacketHeaderSize(framer->transport_version(), header);
+ for (size_t i = 0; i < frames.size(); ++i) {
+ DCHECK_LE(packet_size, max_plaintext_size);
+ bool first_frame = i == 0;
+ bool last_frame = i == frames.size() - 1;
+ const size_t frame_size = framer->GetSerializedFrameLength(
+ frames[i], max_plaintext_size - packet_size, first_frame, last_frame,
+ header.packet_number_length);
+ DCHECK(frame_size);
+ packet_size += frame_size;
+ }
+ return BuildUnsizedDataPacket(framer, header, frames, packet_size);
+}
+
+std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
+ QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size) {
+ char* buffer = new char[packet_size];
+ size_t length = framer->BuildDataPacket(header, frames, buffer, packet_size,
+ ENCRYPTION_INITIAL);
+ DCHECK_NE(0u, length);
+ // Re-construct the data packet with data ownership.
+ return QuicMakeUnique<QuicPacket>(
+ buffer, length, /* owns_buffer */ true,
+ GetIncludedDestinationConnectionIdLength(header),
+ GetIncludedSourceConnectionIdLength(header), header.version_flag,
+ header.nonce != nullptr, header.packet_number_length,
+ header.retry_token_length_length, header.retry_token.length(),
+ header.length_length);
+}
+
+std::string Sha1Hash(QuicStringPiece data) {
+ char buffer[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
+ reinterpret_cast<uint8_t*>(buffer));
+ return std::string(buffer, QUIC_ARRAYSIZE(buffer));
+}
+
+uint64_t SimpleRandom::RandUint64() {
+ std::string hash =
+ Sha1Hash(QuicStringPiece(reinterpret_cast<char*>(&seed_), sizeof(seed_)));
+ DCHECK_EQ(static_cast<size_t>(SHA_DIGEST_LENGTH), hash.length());
+ memcpy(&seed_, hash.data(), sizeof(seed_));
+ return seed_;
+}
+
+void SimpleRandom::RandBytes(void* data, size_t len) {
+ uint8_t* real_data = static_cast<uint8_t*>(data);
+ for (size_t offset = 0; offset < len; offset++) {
+ real_data[offset] = RandUint64() & 0xff;
+ }
+}
+
+MockFramerVisitor::MockFramerVisitor() {
+ // By default, we want to accept packets.
+ ON_CALL(*this, OnProtocolVersionMismatch(_, _))
+ .WillByDefault(testing::Return(false));
+
+ // By default, we want to accept packets.
+ ON_CALL(*this, OnUnauthenticatedHeader(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnUnauthenticatedPublicHeader(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPacketHeader(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnStreamFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnCryptoFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnStopWaitingFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPaddingFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPingFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnRstStreamFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnConnectionCloseFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnStopSendingFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPathChallengeFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPathResponseFrame(_)).WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnGoAwayFrame(_)).WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnMaxStreamIdFrame(_)).WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnStreamIdBlockedFrame(_))
+ .WillByDefault(testing::Return(true));
+}
+
+MockFramerVisitor::~MockFramerVisitor() {}
+
+bool NoOpFramerVisitor::OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) {
+ return false;
+}
+
+bool NoOpFramerVisitor::OnUnauthenticatedPublicHeader(
+ const QuicPacketHeader& header) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) {
+ return true;
+}
+
+void NoOpFramerVisitor::OnCoalescedPacket(const QuicEncryptedPacket& packet) {}
+
+bool NoOpFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnCryptoFrame(const QuicCryptoFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckRange(QuicPacketNumber start,
+ QuicPacketNumber end) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckFrameEnd(QuicPacketNumber start) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPaddingFrame(const QuicPaddingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPingFrame(const QuicPingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnNewConnectionIdFrame(
+ const QuicNewConnectionIdFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnNewTokenFrame(const QuicNewTokenFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPathChallengeFrame(
+ const QuicPathChallengeFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPathResponseFrame(
+ const QuicPathResponseFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnStreamIdBlockedFrame(
+ const QuicStreamIdBlockedFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnMessageFrame(const QuicMessageFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::IsValidStatelessResetToken(QuicUint128 token) const {
+ return false;
+}
+
+MockQuicConnectionVisitor::MockQuicConnectionVisitor() {}
+
+MockQuicConnectionVisitor::~MockQuicConnectionVisitor() {}
+
+MockQuicConnectionHelper::MockQuicConnectionHelper() {}
+
+MockQuicConnectionHelper::~MockQuicConnectionHelper() {}
+
+const QuicClock* MockQuicConnectionHelper::GetClock() const {
+ return &clock_;
+}
+
+QuicRandom* MockQuicConnectionHelper::GetRandomGenerator() {
+ return &random_generator_;
+}
+
+QuicAlarm* MockAlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) {
+ return new MockAlarmFactory::TestAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
+}
+
+QuicArenaScopedPtr<QuicAlarm> MockAlarmFactory::CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) {
+ if (arena != nullptr) {
+ return arena->New<TestAlarm>(std::move(delegate));
+ } else {
+ return QuicArenaScopedPtr<TestAlarm>(new TestAlarm(std::move(delegate)));
+ }
+}
+
+QuicBufferAllocator* MockQuicConnectionHelper::GetStreamSendBufferAllocator() {
+ return &buffer_allocator_;
+}
+
+void MockQuicConnectionHelper::AdvanceTime(QuicTime::Delta delta) {
+ clock_.AdvanceTime(delta);
+}
+
+MockQuicConnection::MockQuicConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(TestConnectionId(),
+ QuicSocketAddress(TestPeerIPAddress(), kTestPort),
+ helper,
+ alarm_factory,
+ perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
+
+MockQuicConnection::MockQuicConnection(QuicSocketAddress address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(TestConnectionId(),
+ address,
+ helper,
+ alarm_factory,
+ perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
+
+MockQuicConnection::MockQuicConnection(QuicConnectionId connection_id,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(connection_id,
+ QuicSocketAddress(TestPeerIPAddress(), kTestPort),
+ helper,
+ alarm_factory,
+ perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
+
+MockQuicConnection::MockQuicConnection(
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : MockQuicConnection(TestConnectionId(),
+ QuicSocketAddress(TestPeerIPAddress(), kTestPort),
+ helper,
+ alarm_factory,
+ perspective,
+ supported_versions) {}
+
+MockQuicConnection::MockQuicConnection(
+ QuicConnectionId connection_id,
+ QuicSocketAddress address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : QuicConnection(connection_id,
+ address,
+ helper,
+ alarm_factory,
+ new testing::NiceMock<MockPacketWriter>(),
+ /* owns_writer= */ true,
+ perspective,
+ supported_versions) {
+ ON_CALL(*this, OnError(_))
+ .WillByDefault(
+ Invoke(this, &PacketSavingConnection::QuicConnection_OnError));
+ ON_CALL(*this, SendCryptoData(_, _, _))
+ .WillByDefault(
+ Invoke(this, &MockQuicConnection::QuicConnection_SendCryptoData));
+
+ SetSelfAddress(QuicSocketAddress(QuicIpAddress::Any4(), 5));
+}
+
+MockQuicConnection::~MockQuicConnection() {}
+
+void MockQuicConnection::AdvanceTime(QuicTime::Delta delta) {
+ static_cast<MockQuicConnectionHelper*>(helper())->AdvanceTime(delta);
+}
+
+bool MockQuicConnection::OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) {
+ return false;
+}
+
+PacketSavingConnection::PacketSavingConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(helper, alarm_factory, perspective) {}
+
+PacketSavingConnection::PacketSavingConnection(
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : MockQuicConnection(helper,
+ alarm_factory,
+ perspective,
+ supported_versions) {}
+
+PacketSavingConnection::~PacketSavingConnection() {}
+
+void PacketSavingConnection::SendOrQueuePacket(SerializedPacket* packet) {
+ encrypted_packets_.push_back(QuicMakeUnique<QuicEncryptedPacket>(
+ CopyBuffer(*packet), packet->encrypted_length, true));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ // Transfer ownership of the packet to the SentPacketManager and the
+ // ack notifier to the AckNotifierManager.
+ QuicConnectionPeer::GetSentPacketManager(this)->OnPacketSent(
+ packet, QuicPacketNumber(), clock_.ApproximateNow(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+}
+
+MockQuicSession::MockQuicSession(QuicConnection* connection)
+ : MockQuicSession(connection, true) {}
+
+MockQuicSession::MockQuicSession(QuicConnection* connection,
+ bool create_mock_crypto_stream)
+ : QuicSession(connection,
+ nullptr,
+ DefaultQuicConfig(),
+ connection->supported_versions()) {
+ if (create_mock_crypto_stream) {
+ crypto_stream_ = QuicMakeUnique<MockQuicCryptoStream>(this);
+ }
+ ON_CALL(*this, WritevData(_, _, _, _, _))
+ .WillByDefault(testing::Return(QuicConsumedData(0, false)));
+}
+
+MockQuicSession::~MockQuicSession() {
+ delete connection();
+}
+
+QuicCryptoStream* MockQuicSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+const QuicCryptoStream* MockQuicSession::GetCryptoStream() const {
+ return crypto_stream_.get();
+}
+
+void MockQuicSession::SetCryptoStream(QuicCryptoStream* crypto_stream) {
+ crypto_stream_.reset(crypto_stream);
+}
+
+// static
+QuicConsumedData MockQuicSession::ConsumeData(QuicStream* stream,
+ QuicStreamId /*id*/,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ if (write_length > 0) {
+ auto buf = QuicMakeUnique<char[]>(write_length);
+ QuicDataWriter writer(write_length, buf.get(), HOST_BYTE_ORDER);
+ stream->WriteStreamData(offset, write_length, &writer);
+ } else {
+ DCHECK(state != NO_FIN);
+ }
+ return QuicConsumedData(write_length, state != NO_FIN);
+}
+
+MockQuicCryptoStream::MockQuicCryptoStream(QuicSession* session)
+ : QuicCryptoStream(session), params_(new QuicCryptoNegotiatedParameters) {}
+
+MockQuicCryptoStream::~MockQuicCryptoStream() {}
+
+bool MockQuicCryptoStream::encryption_established() const {
+ return false;
+}
+
+bool MockQuicCryptoStream::handshake_confirmed() const {
+ return false;
+}
+
+const QuicCryptoNegotiatedParameters&
+MockQuicCryptoStream::crypto_negotiated_params() const {
+ return *params_;
+}
+
+CryptoMessageParser* MockQuicCryptoStream::crypto_message_parser() {
+ return &crypto_framer_;
+}
+
+MockQuicSpdySession::MockQuicSpdySession(QuicConnection* connection)
+ : MockQuicSpdySession(connection, true) {}
+
+MockQuicSpdySession::MockQuicSpdySession(QuicConnection* connection,
+ bool create_mock_crypto_stream)
+ : QuicSpdySession(connection,
+ nullptr,
+ DefaultQuicConfig(),
+ connection->supported_versions()) {
+ if (create_mock_crypto_stream) {
+ crypto_stream_ = QuicMakeUnique<MockQuicCryptoStream>(this);
+ }
+
+ ON_CALL(*this, WritevData(_, _, _, _, _))
+ .WillByDefault(testing::Return(QuicConsumedData(0, false)));
+}
+
+MockQuicSpdySession::~MockQuicSpdySession() {
+ delete connection();
+}
+
+QuicCryptoStream* MockQuicSpdySession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+const QuicCryptoStream* MockQuicSpdySession::GetCryptoStream() const {
+ return crypto_stream_.get();
+}
+
+void MockQuicSpdySession::SetCryptoStream(QuicCryptoStream* crypto_stream) {
+ crypto_stream_.reset(crypto_stream);
+}
+
+TestQuicSpdyServerSession::TestQuicSpdyServerSession(
+ QuicConnection* connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache)
+ : QuicServerSessionBase(config,
+ supported_versions,
+ connection,
+ &visitor_,
+ &helper_,
+ crypto_config,
+ compressed_certs_cache) {
+ Initialize();
+ ON_CALL(helper_, GenerateConnectionIdForReject(_, _))
+ .WillByDefault(testing::Return(
+ QuicUtils::CreateRandomConnectionId(connection->random_generator())));
+ ON_CALL(helper_, CanAcceptClientHello(_, _, _, _, _))
+ .WillByDefault(testing::Return(true));
+}
+
+TestQuicSpdyServerSession::~TestQuicSpdyServerSession() {
+ delete connection();
+}
+
+QuicCryptoServerStreamBase*
+TestQuicSpdyServerSession::CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) {
+ return new QuicCryptoServerStream(
+ crypto_config, compressed_certs_cache,
+ GetQuicReloadableFlag(enable_quic_stateless_reject_support), this,
+ &helper_);
+}
+
+void TestQuicSpdyServerSession::OnCryptoHandshakeEvent(
+ CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+}
+
+QuicCryptoServerStream* TestQuicSpdyServerSession::GetMutableCryptoStream() {
+ return static_cast<QuicCryptoServerStream*>(
+ QuicServerSessionBase::GetMutableCryptoStream());
+}
+
+const QuicCryptoServerStream* TestQuicSpdyServerSession::GetCryptoStream()
+ const {
+ return static_cast<const QuicCryptoServerStream*>(
+ QuicServerSessionBase::GetCryptoStream());
+}
+
+TestQuicSpdyClientSession::TestQuicSpdyClientSession(
+ QuicConnection* connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config)
+ : QuicSpdyClientSessionBase(connection,
+ &push_promise_index_,
+ config,
+ supported_versions) {
+ crypto_stream_ = QuicMakeUnique<QuicCryptoClientStream>(
+ server_id, this, crypto_test_utils::ProofVerifyContextForTesting(),
+ crypto_config, this);
+ Initialize();
+}
+
+TestQuicSpdyClientSession::~TestQuicSpdyClientSession() {}
+
+bool TestQuicSpdyClientSession::IsAuthorized(const std::string& authority) {
+ return true;
+}
+
+void TestQuicSpdyClientSession::OnCryptoHandshakeEvent(
+ CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+}
+
+QuicCryptoClientStream* TestQuicSpdyClientSession::GetMutableCryptoStream() {
+ return crypto_stream_.get();
+}
+
+const QuicCryptoClientStream* TestQuicSpdyClientSession::GetCryptoStream()
+ const {
+ return crypto_stream_.get();
+}
+
+TestPushPromiseDelegate::TestPushPromiseDelegate(bool match)
+ : match_(match), rendezvous_fired_(false), rendezvous_stream_(nullptr) {}
+
+bool TestPushPromiseDelegate::CheckVary(
+ const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) {
+ QUIC_DVLOG(1) << "match " << match_;
+ return match_;
+}
+
+void TestPushPromiseDelegate::OnRendezvousResult(QuicSpdyStream* stream) {
+ rendezvous_fired_ = true;
+ rendezvous_stream_ = stream;
+}
+
+MockPacketWriter::MockPacketWriter() {
+ ON_CALL(*this, GetMaxPacketSize(_))
+ .WillByDefault(testing::Return(kMaxOutgoingPacketSize));
+ ON_CALL(*this, IsBatchMode()).WillByDefault(testing::Return(false));
+ ON_CALL(*this, GetNextWriteLocation(_, _))
+ .WillByDefault(testing::Return(nullptr));
+ ON_CALL(*this, Flush())
+ .WillByDefault(testing::Return(WriteResult(WRITE_STATUS_OK, 0)));
+}
+
+MockPacketWriter::~MockPacketWriter() {}
+
+MockSendAlgorithm::MockSendAlgorithm() {}
+
+MockSendAlgorithm::~MockSendAlgorithm() {}
+
+MockLossAlgorithm::MockLossAlgorithm() {}
+
+MockLossAlgorithm::~MockLossAlgorithm() {}
+
+MockAckListener::MockAckListener() {}
+
+MockAckListener::~MockAckListener() {}
+
+MockNetworkChangeVisitor::MockNetworkChangeVisitor() {}
+
+MockNetworkChangeVisitor::~MockNetworkChangeVisitor() {}
+
+namespace {
+
+std::string HexDumpWithMarks(const char* data,
+ int length,
+ const bool* marks,
+ int mark_length) {
+ static const char kHexChars[] = "0123456789abcdef";
+ static const int kColumns = 4;
+
+ const int kSizeLimit = 1024;
+ if (length > kSizeLimit || mark_length > kSizeLimit) {
+ QUIC_LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
+ length = std::min(length, kSizeLimit);
+ mark_length = std::min(mark_length, kSizeLimit);
+ }
+
+ std::string hex;
+ for (const char* row = data; length > 0;
+ row += kColumns, length -= kColumns) {
+ for (const char* p = row; p < row + 4; ++p) {
+ if (p < row + length) {
+ const bool mark =
+ (marks && (p - data) < mark_length && marks[p - data]);
+ hex += mark ? '*' : ' ';
+ hex += kHexChars[(*p & 0xf0) >> 4];
+ hex += kHexChars[*p & 0x0f];
+ hex += mark ? '*' : ' ';
+ } else {
+ hex += " ";
+ }
+ }
+ hex = hex + " ";
+
+ for (const char* p = row; p < row + 4 && p < row + length; ++p) {
+ hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.';
+ }
+
+ hex = hex + '\n';
+ }
+ return hex;
+}
+
+} // namespace
+
+QuicIpAddress TestPeerIPAddress() {
+ return QuicIpAddress::Loopback4();
+}
+
+ParsedQuicVersion QuicVersionMax() {
+ return AllSupportedVersions().front();
+}
+
+ParsedQuicVersion QuicVersionMin() {
+ return AllSupportedVersions().back();
+}
+
+QuicTransportVersion QuicTransportVersionMax() {
+ return AllSupportedTransportVersions().front();
+}
+
+QuicTransportVersion QuicTransportVersionMin() {
+ return AllSupportedTransportVersions().back();
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data) {
+ return ConstructEncryptedPacket(
+ destination_connection_id, source_connection_id, version_flag, reset_flag,
+ packet_number, data, CONNECTION_ID_PRESENT, CONNECTION_ID_ABSENT,
+ PACKET_4BYTE_PACKET_NUMBER);
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length) {
+ return ConstructEncryptedPacket(
+ destination_connection_id, source_connection_id, version_flag, reset_flag,
+ packet_number, data, destination_connection_id_included,
+ source_connection_id_included, packet_number_length, nullptr);
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions) {
+ return ConstructEncryptedPacket(
+ destination_connection_id, source_connection_id, version_flag, reset_flag,
+ packet_number, data, destination_connection_id_included,
+ source_connection_id_included, packet_number_length, versions,
+ Perspective::IS_CLIENT);
+}
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions,
+ Perspective perspective) {
+ QuicPacketHeader header;
+ header.destination_connection_id = destination_connection_id;
+ header.destination_connection_id_included =
+ destination_connection_id_included;
+ header.source_connection_id = source_connection_id;
+ header.source_connection_id_included = source_connection_id_included;
+ header.version_flag = version_flag;
+ header.reset_flag = reset_flag;
+ header.packet_number_length = packet_number_length;
+ header.packet_number = QuicPacketNumber(packet_number);
+ ParsedQuicVersionVector supported_versions = CurrentSupportedVersions();
+ if (!versions) {
+ versions = &supported_versions;
+ }
+ if (QuicVersionHasLongHeaderLengths((*versions)[0].transport_version) &&
+ version_flag) {
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+
+ QuicFrames frames;
+ QuicFramer framer(*versions, QuicTime::Zero(), perspective,
+ kQuicDefaultConnectionIdLength);
+ if (!QuicVersionUsesCryptoFrames((*versions)[0].transport_version)) {
+ QuicFrame frame(QuicStreamFrame(
+ QuicUtils::GetCryptoStreamId((*versions)[0].transport_version), false,
+ 0, QuicStringPiece(data)));
+ frames.push_back(frame);
+ } else {
+ QuicFrame frame(new QuicCryptoFrame(ENCRYPTION_INITIAL, 0, data));
+ frames.push_back(frame);
+ }
+
+ std::unique_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames));
+ EXPECT_TRUE(packet != nullptr);
+ char* buffer = new char[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ framer.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(packet_number),
+ *packet, buffer, kMaxOutgoingPacketSize);
+ EXPECT_NE(0u, encrypted_length);
+ DeleteFrames(&frames);
+ return new QuicEncryptedPacket(buffer, encrypted_length, true);
+}
+
+QuicReceivedPacket* ConstructReceivedPacket(
+ const QuicEncryptedPacket& encrypted_packet,
+ QuicTime receipt_time) {
+ char* buffer = new char[encrypted_packet.length()];
+ memcpy(buffer, encrypted_packet.data(), encrypted_packet.length());
+ return new QuicReceivedPacket(buffer, encrypted_packet.length(), receipt_time,
+ true);
+}
+
+QuicEncryptedPacket* ConstructMisFramedEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions,
+ Perspective perspective) {
+ QuicPacketHeader header;
+ header.destination_connection_id = destination_connection_id;
+ header.destination_connection_id_included =
+ destination_connection_id_included;
+ header.source_connection_id = source_connection_id;
+ header.source_connection_id_included = source_connection_id_included;
+ header.version_flag = version_flag;
+ header.reset_flag = reset_flag;
+ header.packet_number_length = packet_number_length;
+ header.packet_number = QuicPacketNumber(packet_number);
+ if (QuicVersionHasLongHeaderLengths((*versions)[0].transport_version) &&
+ version_flag) {
+ header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1;
+ header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2;
+ }
+ QuicFrame frame(QuicStreamFrame(1, false, 0, QuicStringPiece(data)));
+ QuicFrames frames;
+ frames.push_back(frame);
+ QuicFramer framer(versions != nullptr ? *versions : AllSupportedVersions(),
+ QuicTime::Zero(), perspective,
+ kQuicDefaultConnectionIdLength);
+
+ std::unique_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames));
+ EXPECT_TRUE(packet != nullptr);
+
+ // Now set the frame type to 0x1F, which is an invalid frame type.
+ reinterpret_cast<unsigned char*>(
+ packet->mutable_data())[GetStartOfEncryptedData(
+ framer.transport_version(),
+ GetIncludedDestinationConnectionIdLength(header),
+ GetIncludedSourceConnectionIdLength(header), version_flag,
+ false /* no diversification nonce */, packet_number_length,
+ header.retry_token_length_length, 0, header.length_length)] = 0x1F;
+
+ char* buffer = new char[kMaxOutgoingPacketSize];
+ size_t encrypted_length =
+ framer.EncryptPayload(ENCRYPTION_INITIAL, QuicPacketNumber(packet_number),
+ *packet, buffer, kMaxOutgoingPacketSize);
+ EXPECT_NE(0u, encrypted_length);
+ return new QuicEncryptedPacket(buffer, encrypted_length, true);
+}
+
+void CompareCharArraysWithHexError(const std::string& description,
+ const char* actual,
+ const int actual_len,
+ const char* expected,
+ const int expected_len) {
+ EXPECT_EQ(actual_len, expected_len);
+ const int min_len = std::min(actual_len, expected_len);
+ const int max_len = std::max(actual_len, expected_len);
+ std::unique_ptr<bool[]> marks(new bool[max_len]);
+ bool identical = (actual_len == expected_len);
+ for (int i = 0; i < min_len; ++i) {
+ if (actual[i] != expected[i]) {
+ marks[i] = true;
+ identical = false;
+ } else {
+ marks[i] = false;
+ }
+ }
+ for (int i = min_len; i < max_len; ++i) {
+ marks[i] = true;
+ }
+ if (identical)
+ return;
+ ADD_FAILURE() << "Description:\n"
+ << description << "\n\nExpected:\n"
+ << HexDumpWithMarks(expected, expected_len, marks.get(),
+ max_len)
+ << "\nActual:\n"
+ << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
+}
+
+QuicConfig DefaultQuicConfig() {
+ QuicConfig config;
+ config.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(
+ &config, kDefaultMaxStreamsPerConnection);
+ // Default enable NSTP.
+ // This is unnecessary for versions > 44
+ if (!config.HasClientSentConnectionOption(quic::kNSTP,
+ quic::Perspective::IS_CLIENT)) {
+ quic::QuicTagVector connection_options;
+ connection_options.push_back(quic::kNSTP);
+ config.SetConnectionOptionsToSend(connection_options);
+ }
+ return config;
+}
+
+QuicConfig DefaultQuicConfigStatelessRejects() {
+ QuicConfig config = DefaultQuicConfig();
+ QuicTagVector copt;
+ copt.push_back(kSREJ);
+ config.SetConnectionOptionsToSend(copt);
+ return config;
+}
+
+QuicTransportVersionVector SupportedTransportVersions(
+ QuicTransportVersion version) {
+ QuicTransportVersionVector versions;
+ versions.push_back(version);
+ return versions;
+}
+
+ParsedQuicVersionVector SupportedVersions(ParsedQuicVersion version) {
+ ParsedQuicVersionVector versions;
+ versions.push_back(version);
+ return versions;
+}
+
+MockQuicConnectionDebugVisitor::MockQuicConnectionDebugVisitor() {}
+
+MockQuicConnectionDebugVisitor::~MockQuicConnectionDebugVisitor() {}
+
+MockReceivedPacketManager::MockReceivedPacketManager(QuicConnectionStats* stats)
+ : QuicReceivedPacketManager(stats) {}
+
+MockReceivedPacketManager::~MockReceivedPacketManager() {}
+
+MockConnectionCloseDelegate::MockConnectionCloseDelegate() {}
+
+MockConnectionCloseDelegate::~MockConnectionCloseDelegate() {}
+
+MockPacketCreatorDelegate::MockPacketCreatorDelegate() {}
+MockPacketCreatorDelegate::~MockPacketCreatorDelegate() {}
+
+MockSessionNotifier::MockSessionNotifier() {}
+MockSessionNotifier::~MockSessionNotifier() {}
+
+void CreateClientSessionForTest(
+ QuicServerId server_id,
+ bool supports_stateless_rejects,
+ QuicTime::Delta connection_start_time,
+ const ParsedQuicVersionVector& supported_versions,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicCryptoClientConfig* crypto_client_config,
+ PacketSavingConnection** client_connection,
+ TestQuicSpdyClientSession** client_session) {
+ CHECK(crypto_client_config);
+ CHECK(client_connection);
+ CHECK(client_session);
+ CHECK(!connection_start_time.IsZero())
+ << "Connections must start at non-zero times, otherwise the "
+ << "strike-register will be unhappy.";
+
+ QuicConfig config = supports_stateless_rejects
+ ? DefaultQuicConfigStatelessRejects()
+ : DefaultQuicConfig();
+ *client_connection = new PacketSavingConnection(
+ helper, alarm_factory, Perspective::IS_CLIENT, supported_versions);
+ *client_session = new TestQuicSpdyClientSession(*client_connection, config,
+ supported_versions, server_id,
+ crypto_client_config);
+ (*client_connection)->AdvanceTime(connection_start_time);
+}
+
+void CreateServerSessionForTest(
+ QuicServerId server_id,
+ QuicTime::Delta connection_start_time,
+ ParsedQuicVersionVector supported_versions,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicCryptoServerConfig* server_crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ PacketSavingConnection** server_connection,
+ TestQuicSpdyServerSession** server_session) {
+ CHECK(server_crypto_config);
+ CHECK(server_connection);
+ CHECK(server_session);
+ CHECK(!connection_start_time.IsZero())
+ << "Connections must start at non-zero times, otherwise the "
+ << "strike-register will be unhappy.";
+
+ *server_connection =
+ new PacketSavingConnection(helper, alarm_factory, Perspective::IS_SERVER,
+ ParsedVersionOfIndex(supported_versions, 0));
+ *server_session = new TestQuicSpdyServerSession(
+ *server_connection, DefaultQuicConfig(), supported_versions,
+ server_crypto_config, compressed_certs_cache);
+
+ // We advance the clock initially because the default time is zero and the
+ // strike register worries that we've just overflowed a uint32_t time.
+ (*server_connection)->AdvanceTime(connection_start_time);
+}
+
+QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
+ QuicTransportVersion version,
+ int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(version,
+ Perspective::IS_CLIENT) +
+ // + 1 because spdy_session contains headers stream.
+ QuicUtils::StreamIdDelta(version) * (n + 1);
+}
+
+QuicStreamId GetNthServerInitiatedBidirectionalStreamId(
+ QuicTransportVersion version,
+ int n) {
+ return QuicUtils::GetFirstBidirectionalStreamId(version,
+ Perspective::IS_SERVER) +
+ QuicUtils::StreamIdDelta(version) * n;
+}
+
+QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(
+ QuicTransportVersion version,
+ int n) {
+ return QuicUtils::GetFirstUnidirectionalStreamId(version,
+ Perspective::IS_SERVER) +
+ QuicUtils::StreamIdDelta(version) * n;
+}
+
+StreamType DetermineStreamType(QuicStreamId id,
+ QuicTransportVersion version,
+ Perspective perspective,
+ bool is_incoming,
+ StreamType default_type) {
+ return version == QUIC_VERSION_99
+ ? QuicUtils::GetStreamType(id, perspective, is_incoming)
+ : default_type;
+}
+
+QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator,
+ QuicStringPiece message_data,
+ QuicMemSliceStorage* storage) {
+ if (message_data.length() == 0) {
+ *storage =
+ QuicMemSliceStorage(nullptr, 0, allocator, kMaxOutgoingPacketSize);
+ return storage->ToSpan();
+ }
+ struct iovec iov = {const_cast<char*>(message_data.data()),
+ message_data.length()};
+ *storage = QuicMemSliceStorage(&iov, 1, allocator, kMaxOutgoingPacketSize);
+ return storage->ToSpan();
+}
+
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..c888fef9b72
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils.h
@@ -0,0 +1,1191 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Common utilities for Quic tests
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/loss_detection_interface.h"
+#include "net/third_party/quiche/src/quic/core/congestion_control/send_algorithm_interface.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection_close_delegate_interface.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_sent_packet_manager.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_clock.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+
+namespace quic {
+
+namespace test {
+
+// A generic predictable connection ID suited for testing.
+QuicConnectionId TestConnectionId();
+
+// A generic predictable connection ID suited for testing, generated from a
+// given number, such as an index.
+QuicConnectionId TestConnectionId(uint64_t connection_number);
+
+// A generic predictable connection ID suited for testing, generated from a
+// given number, such as an index. Guaranteed to be 9 bytes long.
+QuicConnectionId TestConnectionIdNineBytesLong(uint64_t connection_number);
+
+// Extracts the connection number passed to TestConnectionId().
+uint64_t TestConnectionIdToUInt64(QuicConnectionId connection_id);
+
+static const uint16_t kTestPort = 12345;
+static const uint32_t kInitialStreamFlowControlWindowForTest =
+ 1024 * 1024; // 1 MB
+static const uint32_t kInitialSessionFlowControlWindowForTest =
+ 1536 * 1024; // 1.5 MB
+
+// Returns the test peer IP address.
+QuicIpAddress TestPeerIPAddress();
+
+// Upper limit on versions we support.
+ParsedQuicVersion QuicVersionMax();
+
+// Lower limit on versions we support.
+ParsedQuicVersion QuicVersionMin();
+
+// Upper limit on versions we support.
+// TODO(nharper): Remove this function when it is no longer used.
+QuicTransportVersion QuicTransportVersionMax();
+
+// Lower limit on versions we support.
+// TODO(nharper): Remove this function when it is no longer used.
+QuicTransportVersion QuicTransportVersionMin();
+
+// Create an encrypted packet for testing.
+// If versions == nullptr, uses &AllSupportedVersions().
+// Note that the packet is encrypted with NullEncrypter, so to decrypt the
+// constructed packet, the framer must be set to use NullDecrypter.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions,
+ Perspective perspective);
+
+// Create an encrypted packet for testing.
+// If versions == nullptr, uses &AllSupportedVersions().
+// Note that the packet is encrypted with NullEncrypter, so to decrypt the
+// constructed packet, the framer must be set to use NullDecrypter.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions);
+
+// This form assumes |versions| == nullptr.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length);
+
+// This form assumes |connection_id_length| == PACKET_8BYTE_CONNECTION_ID,
+// |packet_number_length| == PACKET_4BYTE_PACKET_NUMBER and
+// |versions| == nullptr.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data);
+
+// Constructs a received packet for testing. The caller must take ownership of
+// the returned pointer.
+QuicReceivedPacket* ConstructReceivedPacket(
+ const QuicEncryptedPacket& encrypted_packet,
+ QuicTime receipt_time);
+
+// Create an encrypted packet for testing whose data portion erroneous.
+// The specific way the data portion is erroneous is not specified, but
+// it is an error that QuicFramer detects.
+// Note that the packet is encrypted with NullEncrypter, so to decrypt the
+// constructed packet, the framer must be set to use NullDecrypter.
+QuicEncryptedPacket* ConstructMisFramedEncryptedPacket(
+ QuicConnectionId destination_connection_id,
+ QuicConnectionId source_connection_id,
+ bool version_flag,
+ bool reset_flag,
+ uint64_t packet_number,
+ const std::string& data,
+ QuicConnectionIdIncluded destination_connection_id_included,
+ QuicConnectionIdIncluded source_connection_id_included,
+ QuicPacketNumberLength packet_number_length,
+ ParsedQuicVersionVector* versions,
+ Perspective perspective);
+
+void CompareCharArraysWithHexError(const std::string& description,
+ const char* actual,
+ const int actual_len,
+ const char* expected,
+ const int expected_len);
+
+// Returns QuicConfig set to default values.
+QuicConfig DefaultQuicConfig();
+
+// Returns a QuicConfig set to default values that supports stateless rejects.
+QuicConfig DefaultQuicConfigStatelessRejects();
+
+// Returns a version vector consisting of |version|.
+QuicTransportVersionVector SupportedTransportVersions(
+ QuicTransportVersion version);
+
+ParsedQuicVersionVector SupportedVersions(ParsedQuicVersion version);
+
+struct QuicAckBlock {
+ QuicPacketNumber start; // Included
+ QuicPacketNumber limit; // Excluded
+};
+
+// Testing convenience method to construct a QuicAckFrame with arbitrary ack
+// blocks. Each block is given by a (closed-open) range of packet numbers. e.g.:
+// InitAckFrame({{1, 10}})
+// => 1 ack block acking packet numbers 1 to 9.
+//
+// InitAckFrame({{1, 2}, {3, 4}})
+// => 2 ack blocks acking packet 1 and 3. Packet 2 is missing.
+QuicAckFrame InitAckFrame(const std::vector<QuicAckBlock>& ack_blocks);
+
+// Testing convenience method to construct a QuicAckFrame with 1 ack block which
+// covers packet number range [1, |largest_acked| + 1).
+// Equivalent to InitAckFrame({{1, largest_acked + 1}})
+QuicAckFrame InitAckFrame(uint64_t largest_acked);
+QuicAckFrame InitAckFrame(QuicPacketNumber largest_acked);
+
+// Testing convenience method to construct a QuicAckFrame with |num_ack_blocks|
+// ack blocks of width 1 packet, starting from |least_unacked| + 2.
+QuicAckFrame MakeAckFrameWithAckBlocks(size_t num_ack_blocks,
+ uint64_t least_unacked);
+
+// Returns a QuicPacket that is owned by the caller, and
+// is populated with the fields in |header| and |frames|, or is nullptr if the
+// packet could not be created.
+std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
+ QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames);
+// Returns a QuicPacket that is owned by the caller, and of size |packet_size|.
+std::unique_ptr<QuicPacket> BuildUnsizedDataPacket(
+ QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size);
+
+// Compute SHA-1 hash of the supplied std::string.
+std::string Sha1Hash(QuicStringPiece data);
+
+// Simple random number generator used to compute random numbers suitable
+// for pseudo-randomly dropping packets in tests. It works by computing
+// the sha1 hash of the current seed, and using the first 64 bits as
+// the next random number, and the next seed.
+class SimpleRandom : public QuicRandom {
+ public:
+ SimpleRandom() : seed_(0) {}
+ SimpleRandom(const SimpleRandom&) = delete;
+ SimpleRandom& operator=(const SimpleRandom&) = delete;
+ ~SimpleRandom() override {}
+
+ // Returns a random number in the range [0, kuint64max].
+ uint64_t RandUint64() override;
+
+ void RandBytes(void* data, size_t len) override;
+
+ void set_seed(uint64_t seed) { seed_ = seed; }
+
+ private:
+ uint64_t seed_;
+};
+
+class MockFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ MockFramerVisitor();
+ MockFramerVisitor(const MockFramerVisitor&) = delete;
+ MockFramerVisitor& operator=(const MockFramerVisitor&) = delete;
+ ~MockFramerVisitor() override;
+
+ MOCK_METHOD1(OnError, void(QuicFramer* framer));
+ // The constructor sets this up to return false by default.
+ MOCK_METHOD2(OnProtocolVersionMismatch,
+ bool(ParsedQuicVersion version, PacketHeaderFormat form));
+ MOCK_METHOD0(OnPacket, void());
+ MOCK_METHOD1(OnPublicResetPacket, void(const QuicPublicResetPacket& header));
+ MOCK_METHOD1(OnVersionNegotiationPacket,
+ void(const QuicVersionNegotiationPacket& packet));
+ // The constructor sets this up to return true by default.
+ MOCK_METHOD1(OnUnauthenticatedHeader, bool(const QuicPacketHeader& header));
+ // The constructor sets this up to return true by default.
+ MOCK_METHOD1(OnUnauthenticatedPublicHeader,
+ bool(const QuicPacketHeader& header));
+ MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level));
+ MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header));
+ MOCK_METHOD1(OnCoalescedPacket, void(const QuicEncryptedPacket& packet));
+ MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame));
+ MOCK_METHOD1(OnCryptoFrame, bool(const QuicCryptoFrame& frame));
+ MOCK_METHOD2(OnAckFrameStart, bool(QuicPacketNumber, QuicTime::Delta));
+ MOCK_METHOD2(OnAckRange, bool(QuicPacketNumber, QuicPacketNumber));
+ MOCK_METHOD2(OnAckTimestamp, bool(QuicPacketNumber, QuicTime));
+ MOCK_METHOD1(OnAckFrameEnd, bool(QuicPacketNumber));
+ MOCK_METHOD1(OnStopWaitingFrame, bool(const QuicStopWaitingFrame& frame));
+ MOCK_METHOD1(OnPaddingFrame, bool(const QuicPaddingFrame& frame));
+ MOCK_METHOD1(OnPingFrame, bool(const QuicPingFrame& frame));
+ MOCK_METHOD1(OnRstStreamFrame, bool(const QuicRstStreamFrame& frame));
+ MOCK_METHOD1(OnConnectionCloseFrame,
+ bool(const QuicConnectionCloseFrame& frame));
+ MOCK_METHOD1(OnNewConnectionIdFrame,
+ bool(const QuicNewConnectionIdFrame& frame));
+ MOCK_METHOD1(OnRetireConnectionIdFrame,
+ bool(const QuicRetireConnectionIdFrame& frame));
+ MOCK_METHOD1(OnNewTokenFrame, bool(const QuicNewTokenFrame& frame));
+ MOCK_METHOD1(OnStopSendingFrame, bool(const QuicStopSendingFrame& frame));
+ MOCK_METHOD1(OnPathChallengeFrame, bool(const QuicPathChallengeFrame& frame));
+ MOCK_METHOD1(OnPathResponseFrame, bool(const QuicPathResponseFrame& frame));
+ MOCK_METHOD1(OnGoAwayFrame, bool(const QuicGoAwayFrame& frame));
+ MOCK_METHOD1(OnMaxStreamIdFrame, bool(const QuicMaxStreamIdFrame& frame));
+ MOCK_METHOD1(OnStreamIdBlockedFrame,
+ bool(const QuicStreamIdBlockedFrame& frame));
+ MOCK_METHOD1(OnWindowUpdateFrame, bool(const QuicWindowUpdateFrame& frame));
+ MOCK_METHOD1(OnBlockedFrame, bool(const QuicBlockedFrame& frame));
+ MOCK_METHOD1(OnMessageFrame, bool(const QuicMessageFrame& frame));
+ MOCK_METHOD0(OnPacketComplete, void());
+ MOCK_CONST_METHOD1(IsValidStatelessResetToken, bool(QuicUint128));
+ MOCK_METHOD1(OnAuthenticatedIetfStatelessResetPacket,
+ void(const QuicIetfStatelessResetPacket&));
+};
+
+class NoOpFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ NoOpFramerVisitor() {}
+ NoOpFramerVisitor(const NoOpFramerVisitor&) = delete;
+ NoOpFramerVisitor& operator=(const NoOpFramerVisitor&) = delete;
+
+ void OnError(QuicFramer* framer) override {}
+ void OnPacket() override {}
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {}
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {}
+ bool OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) override;
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
+ void OnDecryptedPacket(EncryptionLevel level) override {}
+ bool OnPacketHeader(const QuicPacketHeader& header) override;
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override;
+ bool OnStreamFrame(const QuicStreamFrame& frame) override;
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override;
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override;
+ bool OnAckFrameEnd(QuicPacketNumber start) override;
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
+ bool OnPingFrame(const QuicPingFrame& frame) override;
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override;
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override;
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override;
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override;
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override;
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override;
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override;
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override;
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override;
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override;
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override;
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
+ bool OnMessageFrame(const QuicMessageFrame& frame) override;
+ void OnPacketComplete() override {}
+ bool IsValidStatelessResetToken(QuicUint128 token) const override;
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {}
+};
+
+class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface {
+ public:
+ MockQuicConnectionVisitor();
+ MockQuicConnectionVisitor(const MockQuicConnectionVisitor&) = delete;
+ MockQuicConnectionVisitor& operator=(const MockQuicConnectionVisitor&) =
+ delete;
+ ~MockQuicConnectionVisitor() override;
+
+ MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame& frame));
+ MOCK_METHOD1(OnCryptoFrame, void(const QuicCryptoFrame& frame));
+ MOCK_METHOD1(OnWindowUpdateFrame, void(const QuicWindowUpdateFrame& frame));
+ MOCK_METHOD1(OnBlockedFrame, void(const QuicBlockedFrame& frame));
+ MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
+ MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame));
+ MOCK_METHOD1(OnMessageReceived, void(QuicStringPiece message));
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD0(OnWriteBlocked, void());
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD1(OnCongestionWindowChange, void(QuicTime now));
+ MOCK_METHOD1(OnConnectionMigration, void(AddressChangeType type));
+ MOCK_METHOD0(OnPathDegrading, void());
+ MOCK_CONST_METHOD0(WillingAndAbleToWrite, bool());
+ MOCK_CONST_METHOD0(HasPendingHandshake, bool());
+ MOCK_CONST_METHOD0(ShouldKeepConnectionAlive, bool());
+ MOCK_METHOD1(OnSuccessfulVersionNegotiation,
+ void(const ParsedQuicVersion& version));
+ MOCK_METHOD2(OnConnectivityProbeReceived,
+ void(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address));
+ MOCK_METHOD0(OnConfigNegotiated, void());
+ MOCK_METHOD0(OnAckNeedsRetransmittableFrame, void());
+ MOCK_METHOD0(SendPing, void());
+ MOCK_CONST_METHOD0(AllowSelfAddressChange, bool());
+ MOCK_METHOD0(OnForwardProgressConfirmed, void());
+ MOCK_METHOD1(OnMaxStreamIdFrame, bool(const QuicMaxStreamIdFrame& frame));
+ MOCK_METHOD1(OnStreamIdBlockedFrame,
+ bool(const QuicStreamIdBlockedFrame& frame));
+ MOCK_METHOD1(OnStopSendingFrame, bool(const QuicStopSendingFrame& frame));
+};
+
+class MockQuicConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+ MockQuicConnectionHelper();
+ MockQuicConnectionHelper(const MockQuicConnectionHelper&) = delete;
+ MockQuicConnectionHelper& operator=(const MockQuicConnectionHelper&) = delete;
+ ~MockQuicConnectionHelper() override;
+ const QuicClock* GetClock() const override;
+ QuicRandom* GetRandomGenerator() override;
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+ void AdvanceTime(QuicTime::Delta delta);
+
+ private:
+ MockClock clock_;
+ MockRandom random_generator_;
+ SimpleBufferAllocator buffer_allocator_;
+};
+
+class MockAlarmFactory : public QuicAlarmFactory {
+ public:
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override;
+
+ // No-op alarm implementation
+ class TestAlarm : public QuicAlarm {
+ public:
+ explicit TestAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
+ : QuicAlarm(std::move(delegate)) {}
+
+ void SetImpl() override {}
+ void CancelImpl() override {}
+
+ using QuicAlarm::Fire;
+ };
+
+ void FireAlarm(QuicAlarm* alarm) {
+ reinterpret_cast<TestAlarm*>(alarm)->Fire();
+ }
+};
+
+class MockQuicConnection : public QuicConnection {
+ public:
+ // Uses a ConnectionId of 42 and 127.0.0.1:123.
+ MockQuicConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective);
+
+ // Uses a ConnectionId of 42.
+ MockQuicConnection(QuicSocketAddress address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective);
+
+ // Uses 127.0.0.1:123.
+ MockQuicConnection(QuicConnectionId connection_id,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective);
+
+ // Uses a ConnectionId of 42, and 127.0.0.1:123.
+ MockQuicConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions);
+
+ MockQuicConnection(QuicConnectionId connection_id,
+ QuicSocketAddress address,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions);
+ MockQuicConnection(const MockQuicConnection&) = delete;
+ MockQuicConnection& operator=(const MockQuicConnection&) = delete;
+
+ ~MockQuicConnection() override;
+
+ // If the constructor that uses a MockQuicConnectionHelper has been used then
+ // this method
+ // will advance the time of the MockClock.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ MOCK_METHOD3(ProcessUdpPacket,
+ void(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet));
+ MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error));
+ MOCK_METHOD3(CloseConnection,
+ void(QuicErrorCode error,
+ const std::string& details,
+ ConnectionCloseBehavior connection_close_behavior));
+ MOCK_METHOD3(SendConnectionClosePacket,
+ void(QuicErrorCode error,
+ const std::string& details,
+ AckBundling ack_mode));
+ MOCK_METHOD3(SendRstStream,
+ void(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+ MOCK_METHOD3(SendGoAway,
+ void(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason));
+ MOCK_METHOD1(SendBlocked, void(QuicStreamId id));
+ MOCK_METHOD2(SendWindowUpdate,
+ void(QuicStreamId id, QuicStreamOffset byte_offset));
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD1(SendConnectivityProbingResponsePacket,
+ void(const QuicSocketAddress& peer_address));
+ MOCK_METHOD2(SendConnectivityProbingPacket,
+ bool(QuicPacketWriter* probing_writer,
+ const QuicSocketAddress& peer_address));
+
+ MOCK_METHOD1(OnSendConnectionState, void(const CachedNetworkParameters&));
+ MOCK_METHOD2(ResumeConnectionState,
+ void(const CachedNetworkParameters&, bool));
+ MOCK_METHOD1(SetMaxPacingRate, void(QuicBandwidth));
+
+ MOCK_METHOD2(OnStreamReset, void(QuicStreamId, QuicRstStreamErrorCode));
+ MOCK_METHOD1(SendControlFrame, bool(const QuicFrame& frame));
+ MOCK_METHOD2(SendMessage, MessageStatus(QuicMessageId, QuicMemSliceSpan));
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source));
+
+ MOCK_METHOD1(OnError, void(QuicFramer* framer));
+ void QuicConnection_OnError(QuicFramer* framer) {
+ QuicConnection::OnError(framer);
+ }
+
+ void ReallyCloseConnection(
+ QuicErrorCode error,
+ const std::string& details,
+ ConnectionCloseBehavior connection_close_behavior) {
+ QuicConnection::CloseConnection(error, details, connection_close_behavior);
+ }
+
+ void ReallyProcessUdpPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) {
+ QuicConnection::ProcessUdpPacket(self_address, peer_address, packet);
+ }
+
+ bool OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) override;
+
+ bool ReallySendControlFrame(const QuicFrame& frame) {
+ return QuicConnection::SendControlFrame(frame);
+ }
+
+ bool ReallySendConnectivityProbingPacket(
+ QuicPacketWriter* probing_writer,
+ const QuicSocketAddress& peer_address) {
+ return QuicConnection::SendConnectivityProbingPacket(probing_writer,
+ peer_address);
+ }
+
+ void ReallySendConnectivityProbingResponsePacket(
+ const QuicSocketAddress& peer_address) {
+ QuicConnection::SendConnectivityProbingResponsePacket(peer_address);
+ }
+ MOCK_METHOD1(OnPathResponseFrame, bool(const QuicPathResponseFrame&));
+ MOCK_METHOD1(OnStopSendingFrame, bool(const QuicStopSendingFrame& frame));
+ MOCK_METHOD3(SendCryptoData,
+ size_t(EncryptionLevel, size_t, QuicStreamOffset));
+ size_t QuicConnection_SendCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset) {
+ return QuicConnection::SendCryptoData(level, write_length, offset);
+ }
+};
+
+class PacketSavingConnection : public MockQuicConnection {
+ public:
+ PacketSavingConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective);
+
+ PacketSavingConnection(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions);
+ PacketSavingConnection(const PacketSavingConnection&) = delete;
+ PacketSavingConnection& operator=(const PacketSavingConnection&) = delete;
+
+ ~PacketSavingConnection() override;
+
+ void SendOrQueuePacket(SerializedPacket* packet) override;
+
+ std::vector<std::unique_ptr<QuicEncryptedPacket>> encrypted_packets_;
+ MockClock clock_;
+};
+
+class MockQuicSession : public QuicSession {
+ public:
+ // Takes ownership of |connection|.
+ MockQuicSession(QuicConnection* connection, bool create_mock_crypto_stream);
+
+ // Takes ownership of |connection|.
+ explicit MockQuicSession(QuicConnection* connection);
+ MockQuicSession(const MockQuicSession&) = delete;
+ MockQuicSession& operator=(const MockQuicSession&) = delete;
+ ~MockQuicSession() override;
+
+ QuicCryptoStream* GetMutableCryptoStream() override;
+ const QuicCryptoStream* GetCryptoStream() const override;
+ void SetCryptoStream(QuicCryptoStream* crypto_stream);
+
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(CreateIncomingStream, QuicStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream stream));
+ MOCK_METHOD1(ShouldCreateIncomingStream2, bool(QuicStreamId id));
+ MOCK_METHOD0(ShouldCreateOutgoingBidirectionalStream, bool());
+ MOCK_METHOD0(ShouldCreateOutgoingUnidirectionalStream, bool());
+ MOCK_METHOD5(WritevData,
+ QuicConsumedData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+
+ MOCK_METHOD3(SendRstStream,
+ void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+
+ MOCK_METHOD2(OnStreamHeaders,
+ void(QuicStreamId stream_id, QuicStringPiece headers_data));
+ MOCK_METHOD2(OnStreamHeadersPriority,
+ void(QuicStreamId stream_id, spdy::SpdyPriority priority));
+ MOCK_METHOD3(OnStreamHeadersComplete,
+ void(QuicStreamId stream_id, bool fin, size_t frame_len));
+ MOCK_CONST_METHOD0(IsCryptoHandshakeConfirmed, bool());
+ MOCK_CONST_METHOD0(ShouldKeepConnectionAlive, bool());
+ MOCK_METHOD2(SendStopSending, void(uint16_t code, QuicStreamId stream_id));
+
+ using QuicSession::ActivateStream;
+
+ // Returns a QuicConsumedData that indicates all of |write_length| (and |fin|
+ // if set) has been consumed.
+ static QuicConsumedData ConsumeData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state);
+
+ private:
+ std::unique_ptr<QuicCryptoStream> crypto_stream_;
+};
+
+class MockQuicCryptoStream : public QuicCryptoStream {
+ public:
+ explicit MockQuicCryptoStream(QuicSession* session);
+
+ ~MockQuicCryptoStream() override;
+
+ bool encryption_established() const override;
+ bool handshake_confirmed() const override;
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params()
+ const override;
+ CryptoMessageParser* crypto_message_parser() override;
+
+ private:
+ QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_;
+ CryptoFramer crypto_framer_;
+};
+
+class MockQuicSpdySession : public QuicSpdySession {
+ public:
+ // Takes ownership of |connection|.
+ explicit MockQuicSpdySession(QuicConnection* connection);
+ // Takes ownership of |connection|.
+ MockQuicSpdySession(QuicConnection* connection,
+ bool create_mock_crypto_stream);
+ MockQuicSpdySession(const MockQuicSpdySession&) = delete;
+ MockQuicSpdySession& operator=(const MockQuicSpdySession&) = delete;
+ ~MockQuicSpdySession() override;
+
+ QuicCryptoStream* GetMutableCryptoStream() override;
+ const QuicCryptoStream* GetCryptoStream() const override;
+ void SetCryptoStream(QuicCryptoStream* crypto_stream);
+
+ void ReallyOnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) {
+ QuicSession::OnConnectionClosed(error, error_details, source);
+ }
+
+ // From QuicSession.
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream stream));
+ MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD1(ShouldCreateIncomingStream, bool(QuicStreamId id));
+ MOCK_METHOD0(ShouldCreateOutgoingBidirectionalStream, bool());
+ MOCK_METHOD0(ShouldCreateOutgoingUnidirectionalStream, bool());
+ MOCK_METHOD5(WritevData,
+ QuicConsumedData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+
+ MOCK_METHOD3(SendRstStream,
+ void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+
+ MOCK_METHOD2(OnStreamHeaders,
+ void(QuicStreamId stream_id, QuicStringPiece headers_data));
+ MOCK_METHOD2(OnStreamHeadersPriority,
+ void(QuicStreamId stream_id, spdy::SpdyPriority priority));
+ MOCK_METHOD3(OnStreamHeadersComplete,
+ void(QuicStreamId stream_id, bool fin, size_t frame_len));
+ MOCK_METHOD4(OnStreamHeaderList,
+ void(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list));
+ MOCK_CONST_METHOD0(IsCryptoHandshakeConfirmed, bool());
+ MOCK_METHOD2(OnPromiseHeaders,
+ void(QuicStreamId stream_id, QuicStringPiece headers_data));
+ MOCK_METHOD3(OnPromiseHeadersComplete,
+ void(QuicStreamId stream_id,
+ QuicStreamId promised_stream_id,
+ size_t frame_len));
+ MOCK_METHOD4(OnPromiseHeaderList,
+ void(QuicStreamId stream_id,
+ QuicStreamId promised_stream_id,
+ size_t frame_len,
+ const QuicHeaderList& header_list));
+ MOCK_METHOD2(OnPriorityFrame,
+ void(QuicStreamId id, spdy::SpdyPriority priority));
+
+ MOCK_METHOD1(OnHeadersHeadOfLineBlocking, void(QuicTime::Delta delta));
+ MOCK_METHOD4(
+ OnStreamFrameData,
+ void(QuicStreamId stream_id, const char* data, size_t len, bool fin));
+
+ using QuicSession::ActivateStream;
+
+ private:
+ std::unique_ptr<QuicCryptoStream> crypto_stream_;
+};
+
+class TestQuicSpdyServerSession : public QuicServerSessionBase {
+ public:
+ // Takes ownership of |connection|.
+ TestQuicSpdyServerSession(QuicConnection* connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache);
+ TestQuicSpdyServerSession(const TestQuicSpdyServerSession&) = delete;
+ TestQuicSpdyServerSession& operator=(const TestQuicSpdyServerSession&) =
+ delete;
+ ~TestQuicSpdyServerSession() override;
+
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream stream));
+ MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+ QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) override;
+
+ // Override to not send max header list size.
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
+ QuicCryptoServerStream* GetMutableCryptoStream() override;
+
+ const QuicCryptoServerStream* GetCryptoStream() const override;
+
+ MockQuicCryptoServerStreamHelper* helper() { return &helper_; }
+
+ private:
+ MockQuicSessionVisitor visitor_;
+ MockQuicCryptoServerStreamHelper helper_;
+};
+
+// A test implementation of QuicClientPushPromiseIndex::Delegate.
+class TestPushPromiseDelegate : public QuicClientPushPromiseIndex::Delegate {
+ public:
+ // |match| sets the validation result for checking whether designated header
+ // fields match for promise request and client request.
+ explicit TestPushPromiseDelegate(bool match);
+
+ bool CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) override;
+
+ void OnRendezvousResult(QuicSpdyStream* stream) override;
+
+ QuicSpdyStream* rendezvous_stream() { return rendezvous_stream_; }
+ bool rendezvous_fired() { return rendezvous_fired_; }
+
+ private:
+ bool match_;
+ bool rendezvous_fired_;
+ QuicSpdyStream* rendezvous_stream_;
+};
+
+class TestQuicSpdyClientSession : public QuicSpdyClientSessionBase {
+ public:
+ TestQuicSpdyClientSession(QuicConnection* connection,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config);
+ TestQuicSpdyClientSession(const TestQuicSpdyClientSession&) = delete;
+ TestQuicSpdyClientSession& operator=(const TestQuicSpdyClientSession&) =
+ delete;
+ ~TestQuicSpdyClientSession() override;
+
+ bool IsAuthorized(const std::string& authority) override;
+
+ // QuicSpdyClientSessionBase
+ MOCK_METHOD1(OnProofValid,
+ void(const QuicCryptoClientConfig::CachedState& cached));
+ MOCK_METHOD1(OnProofVerifyDetailsAvailable,
+ void(const ProofVerifyDetails& verify_details));
+
+ // TestQuicSpdyClientSession
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream stream));
+ MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+ MOCK_METHOD1(ShouldCreateIncomingStream, bool(QuicStreamId id));
+ MOCK_METHOD0(ShouldCreateOutgoingBidirectionalStream, bool());
+ MOCK_METHOD0(ShouldCreateOutgoingUnidirectionalStream, bool());
+
+ // Override to not send max header list size.
+ void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+ QuicCryptoClientStream* GetMutableCryptoStream() override;
+ const QuicCryptoClientStream* GetCryptoStream() const override;
+
+ // Override to save sent crypto handshake messages.
+ void OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message) override {
+ sent_crypto_handshake_messages_.push_back(message);
+ }
+
+ const std::vector<CryptoHandshakeMessage>& sent_crypto_handshake_messages()
+ const {
+ return sent_crypto_handshake_messages_;
+ }
+
+ private:
+ std::unique_ptr<QuicCryptoClientStream> crypto_stream_;
+ QuicClientPushPromiseIndex push_promise_index_;
+ std::vector<CryptoHandshakeMessage> sent_crypto_handshake_messages_;
+};
+
+class MockPacketWriter : public QuicPacketWriter {
+ public:
+ MockPacketWriter();
+ MockPacketWriter(const MockPacketWriter&) = delete;
+ MockPacketWriter& operator=(const MockPacketWriter&) = delete;
+ ~MockPacketWriter() override;
+
+ MOCK_METHOD5(WritePacket,
+ WriteResult(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options));
+ MOCK_CONST_METHOD0(IsWriteBlocked, bool());
+ MOCK_METHOD0(SetWritable, void());
+ MOCK_CONST_METHOD1(GetMaxPacketSize,
+ QuicByteCount(const QuicSocketAddress& peer_address));
+ MOCK_CONST_METHOD0(SupportsReleaseTime, bool());
+ MOCK_CONST_METHOD0(IsBatchMode, bool());
+ MOCK_METHOD2(GetNextWriteLocation,
+ char*(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address));
+ MOCK_METHOD0(Flush, WriteResult());
+};
+
+class MockSendAlgorithm : public SendAlgorithmInterface {
+ public:
+ MockSendAlgorithm();
+ MockSendAlgorithm(const MockSendAlgorithm&) = delete;
+ MockSendAlgorithm& operator=(const MockSendAlgorithm&) = delete;
+ ~MockSendAlgorithm() override;
+
+ MOCK_METHOD2(SetFromConfig,
+ void(const QuicConfig& config, Perspective perspective));
+ MOCK_METHOD1(SetNumEmulatedConnections, void(int num_connections));
+ MOCK_METHOD1(SetInitialCongestionWindowInPackets,
+ void(QuicPacketCount packets));
+ MOCK_METHOD1(SetMaxCongestionWindow,
+ void(QuicByteCount max_congestion_window));
+ MOCK_METHOD5(OnCongestionEvent,
+ void(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ QuicTime event_time,
+ const AckedPacketVector& acked_packets,
+ const LostPacketVector& lost_packets));
+ MOCK_METHOD5(OnPacketSent,
+ void(QuicTime,
+ QuicByteCount,
+ QuicPacketNumber,
+ QuicByteCount,
+ HasRetransmittableData));
+ MOCK_METHOD1(OnRetransmissionTimeout, void(bool));
+ MOCK_METHOD0(OnConnectionMigration, void());
+ MOCK_METHOD0(RevertRetransmissionTimeout, void());
+ MOCK_METHOD1(CanSend, bool(QuicByteCount));
+ MOCK_CONST_METHOD1(PacingRate, QuicBandwidth(QuicByteCount));
+ MOCK_CONST_METHOD0(BandwidthEstimate, QuicBandwidth(void));
+ MOCK_CONST_METHOD0(HasReliableBandwidthEstimate, bool());
+ MOCK_METHOD1(OnRttUpdated, void(QuicPacketNumber));
+ MOCK_CONST_METHOD0(GetCongestionWindow, QuicByteCount());
+ MOCK_CONST_METHOD0(GetDebugState, std::string());
+ MOCK_CONST_METHOD0(InSlowStart, bool());
+ MOCK_CONST_METHOD0(InRecovery, bool());
+ MOCK_CONST_METHOD0(ShouldSendProbingPacket, bool());
+ MOCK_CONST_METHOD0(GetSlowStartThreshold, QuicByteCount());
+ MOCK_CONST_METHOD0(GetCongestionControlType, CongestionControlType());
+ MOCK_METHOD2(AdjustNetworkParameters, void(QuicBandwidth, QuicTime::Delta));
+ MOCK_METHOD1(OnApplicationLimited, void(QuicByteCount));
+};
+
+class MockLossAlgorithm : public LossDetectionInterface {
+ public:
+ MockLossAlgorithm();
+ MockLossAlgorithm(const MockLossAlgorithm&) = delete;
+ MockLossAlgorithm& operator=(const MockLossAlgorithm&) = delete;
+ ~MockLossAlgorithm() override;
+
+ MOCK_CONST_METHOD0(GetLossDetectionType, LossDetectionType());
+ MOCK_METHOD6(DetectLosses,
+ void(const QuicUnackedPacketMap& unacked_packets,
+ QuicTime time,
+ const RttStats& rtt_stats,
+ QuicPacketNumber largest_recently_acked,
+ const AckedPacketVector& packets_acked,
+ LostPacketVector* packets_lost));
+ MOCK_CONST_METHOD0(GetLossTimeout, QuicTime());
+ MOCK_METHOD4(SpuriousRetransmitDetected,
+ void(const QuicUnackedPacketMap&,
+ QuicTime,
+ const RttStats&,
+ QuicPacketNumber));
+};
+
+class MockAckListener : public QuicAckListenerInterface {
+ public:
+ MockAckListener();
+ MockAckListener(const MockAckListener&) = delete;
+ MockAckListener& operator=(const MockAckListener&) = delete;
+
+ MOCK_METHOD2(OnPacketAcked,
+ void(int acked_bytes, QuicTime::Delta ack_delay_time));
+
+ MOCK_METHOD1(OnPacketRetransmitted, void(int retransmitted_bytes));
+
+ protected:
+ // Object is ref counted.
+ ~MockAckListener() override;
+};
+
+class MockNetworkChangeVisitor
+ : public QuicSentPacketManager::NetworkChangeVisitor {
+ public:
+ MockNetworkChangeVisitor();
+ MockNetworkChangeVisitor(const MockNetworkChangeVisitor&) = delete;
+ MockNetworkChangeVisitor& operator=(const MockNetworkChangeVisitor&) = delete;
+ ~MockNetworkChangeVisitor() override;
+
+ MOCK_METHOD0(OnCongestionChange, void());
+ MOCK_METHOD1(OnPathMtuIncreased, void(QuicPacketLength));
+};
+
+class MockQuicConnectionDebugVisitor : public QuicConnectionDebugVisitor {
+ public:
+ MockQuicConnectionDebugVisitor();
+ ~MockQuicConnectionDebugVisitor() override;
+
+ MOCK_METHOD1(OnFrameAddedToPacket, void(const QuicFrame&));
+
+ MOCK_METHOD4(OnPacketSent,
+ void(const SerializedPacket&,
+ QuicPacketNumber,
+ TransmissionType,
+ QuicTime));
+
+ MOCK_METHOD0(OnPingSent, void());
+
+ MOCK_METHOD3(OnPacketReceived,
+ void(const QuicSocketAddress&,
+ const QuicSocketAddress&,
+ const QuicEncryptedPacket&));
+
+ MOCK_METHOD1(OnIncorrectConnectionId, void(QuicConnectionId));
+
+ MOCK_METHOD1(OnProtocolVersionMismatch, void(ParsedQuicVersion));
+
+ MOCK_METHOD1(OnPacketHeader, void(const QuicPacketHeader& header));
+
+ MOCK_METHOD1(OnSuccessfulVersionNegotiation, void(const ParsedQuicVersion&));
+
+ MOCK_METHOD1(OnStreamFrame, void(const QuicStreamFrame&));
+
+ MOCK_METHOD1(OnStopWaitingFrame, void(const QuicStopWaitingFrame&));
+
+ MOCK_METHOD1(OnRstStreamFrame, void(const QuicRstStreamFrame&));
+
+ MOCK_METHOD1(OnConnectionCloseFrame, void(const QuicConnectionCloseFrame&));
+
+ MOCK_METHOD1(OnStopSendingFrame, void(const QuicStopSendingFrame&));
+
+ MOCK_METHOD1(OnPathChallengeFrame, void(const QuicPathChallengeFrame&));
+
+ MOCK_METHOD1(OnPathResponseFrame, void(const QuicPathResponseFrame&));
+
+ MOCK_METHOD1(OnPublicResetPacket, void(const QuicPublicResetPacket&));
+
+ MOCK_METHOD1(OnVersionNegotiationPacket,
+ void(const QuicVersionNegotiationPacket&));
+};
+
+class MockReceivedPacketManager : public QuicReceivedPacketManager {
+ public:
+ explicit MockReceivedPacketManager(QuicConnectionStats* stats);
+ ~MockReceivedPacketManager() override;
+
+ MOCK_METHOD2(RecordPacketReceived,
+ void(const QuicPacketHeader& header, QuicTime receipt_time));
+ MOCK_METHOD1(IsMissing, bool(QuicPacketNumber packet_number));
+ MOCK_CONST_METHOD1(IsAwaitingPacket, bool(QuicPacketNumber packet_number));
+ MOCK_METHOD1(UpdatePacketInformationSentByPeer,
+ void(const QuicStopWaitingFrame& stop_waiting));
+ MOCK_CONST_METHOD0(HasNewMissingPackets, bool(void));
+ MOCK_CONST_METHOD0(ack_frame_updated, bool(void));
+};
+
+class MockConnectionCloseDelegate
+ : public QuicConnectionCloseDelegateInterface {
+ public:
+ MockConnectionCloseDelegate();
+ ~MockConnectionCloseDelegate() override;
+
+ MOCK_METHOD3(OnUnrecoverableError,
+ void(QuicErrorCode,
+ const std::string&,
+ ConnectionCloseSource source));
+};
+
+class MockPacketCreatorDelegate : public QuicPacketCreator::DelegateInterface {
+ public:
+ MockPacketCreatorDelegate();
+ MockPacketCreatorDelegate(const MockPacketCreatorDelegate&) = delete;
+ MockPacketCreatorDelegate& operator=(const MockPacketCreatorDelegate&) =
+ delete;
+ ~MockPacketCreatorDelegate() override;
+
+ MOCK_METHOD0(GetPacketBuffer, char*());
+ MOCK_METHOD1(OnSerializedPacket, void(SerializedPacket* packet));
+ MOCK_METHOD3(OnUnrecoverableError,
+ void(QuicErrorCode,
+ const std::string&,
+ ConnectionCloseSource source));
+};
+
+class MockSessionNotifier : public SessionNotifierInterface {
+ public:
+ MockSessionNotifier();
+ ~MockSessionNotifier() override;
+
+ MOCK_METHOD2(OnFrameAcked, bool(const QuicFrame&, QuicTime::Delta));
+ MOCK_METHOD1(OnStreamFrameRetransmitted, void(const QuicStreamFrame&));
+ MOCK_METHOD1(OnFrameLost, void(const QuicFrame&));
+ MOCK_METHOD2(RetransmitFrames,
+ void(const QuicFrames&, TransmissionType type));
+ MOCK_CONST_METHOD1(IsFrameOutstanding, bool(const QuicFrame&));
+ MOCK_CONST_METHOD0(HasUnackedCryptoData, bool());
+};
+
+// Creates a client session for testing.
+//
+// server_id: The server id associated with this stream.
+// supports_stateless_rejects: Does this client support stateless rejects.
+// connection_start_time: The time to set for the connection clock.
+// Needed for strike-register nonce verification. The client
+// connection_start_time should be synchronized witht the server
+// start time, otherwise nonce verification will fail.
+// supported_versions: Set of QUIC versions this client supports.
+// helper: Pointer to the MockQuicConnectionHelper to use for the session.
+// crypto_client_config: Pointer to the crypto client config.
+// client_connection: Pointer reference for newly created
+// connection. This object will be owned by the
+// client_session.
+// client_session: Pointer reference for the newly created client
+// session. The new object will be owned by the caller.
+void CreateClientSessionForTest(
+ QuicServerId server_id,
+ bool supports_stateless_rejects,
+ QuicTime::Delta connection_start_time,
+ const ParsedQuicVersionVector& supported_versions,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicCryptoClientConfig* crypto_client_config,
+ PacketSavingConnection** client_connection,
+ TestQuicSpdyClientSession** client_session);
+
+// Creates a server session for testing.
+//
+// server_id: The server id associated with this stream.
+// connection_start_time: The time to set for the connection clock.
+// Needed for strike-register nonce verification. The server
+// connection_start_time should be synchronized witht the client
+// start time, otherwise nonce verification will fail.
+// supported_versions: Set of QUIC versions this server supports.
+// helper: Pointer to the MockQuicConnectionHelper to use for the session.
+// crypto_server_config: Pointer to the crypto server config.
+// server_connection: Pointer reference for newly created
+// connection. This object will be owned by the
+// server_session.
+// server_session: Pointer reference for the newly created server
+// session. The new object will be owned by the caller.
+void CreateServerSessionForTest(
+ QuicServerId server_id,
+ QuicTime::Delta connection_start_time,
+ ParsedQuicVersionVector supported_versions,
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ QuicCryptoServerConfig* crypto_server_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ PacketSavingConnection** server_connection,
+ TestQuicSpdyServerSession** server_session);
+
+// Verifies that the relative error of |actual| with respect to |expected| is
+// no more than |margin|.
+
+template <typename T>
+void ExpectApproxEq(T expected, T actual, float relative_margin) {
+ // If |relative_margin| > 1 and T is an unsigned type, the comparison will
+ // underflow.
+ ASSERT_LE(relative_margin, 1);
+ ASSERT_GE(relative_margin, 0);
+
+ T absolute_margin = expected * relative_margin;
+
+ EXPECT_GE(expected + absolute_margin, actual);
+ EXPECT_LE(expected - absolute_margin, actual);
+}
+
+template <typename T>
+QuicHeaderList AsHeaderList(const T& container) {
+ QuicHeaderList l;
+ // No need to enforce header list size limits again in this handler.
+ l.set_max_header_list_size(UINT_MAX);
+ l.OnHeaderBlockStart();
+ size_t total_size = 0;
+ for (auto p : container) {
+ total_size += p.first.size() + p.second.size();
+ l.OnHeader(p.first, p.second);
+ }
+ l.OnHeaderBlockEnd(total_size, total_size);
+ return l;
+}
+
+// Utility function that stores |str|'s data in |iov|.
+inline void MakeIOVector(QuicStringPiece str, struct iovec* iov) {
+ iov->iov_base = const_cast<char*>(str.data());
+ iov->iov_len = static_cast<size_t>(str.size());
+}
+
+// Helper functions for stream ids, to allow test logic to abstract over the
+// HTTP stream numbering scheme (i.e. whether one or two QUIC streams are used
+// per HTTP transaction).
+QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
+ QuicTransportVersion version,
+ int n);
+QuicStreamId GetNthServerInitiatedBidirectionalStreamId(
+ QuicTransportVersion version,
+ int n);
+QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(
+ QuicTransportVersion version,
+ int n);
+
+StreamType DetermineStreamType(QuicStreamId id,
+ QuicTransportVersion version,
+ Perspective perspective,
+ bool is_incoming,
+ StreamType default_type);
+
+// Utility function that stores message_data in |storage| and returns a
+// QuicMemSliceSpan.
+QuicMemSliceSpan MakeSpan(QuicBufferAllocator* allocator,
+ QuicStringPiece message_data,
+ QuicMemSliceStorage* storage);
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils_test.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils_test.cc
new file mode 100644
index 00000000000..79d2af8c908
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_test_utils_test.cc
@@ -0,0 +1,61 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+#include "testing/gtest/include/gtest/gtest-spi.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+class QuicTestUtilsTest : public QuicTest {};
+
+TEST_F(QuicTestUtilsTest, ConnectionId) {
+ EXPECT_NE(EmptyQuicConnectionId(), TestConnectionId());
+ EXPECT_NE(EmptyQuicConnectionId(), TestConnectionId(1));
+ EXPECT_EQ(TestConnectionId(), TestConnectionId());
+ EXPECT_EQ(TestConnectionId(33), TestConnectionId(33));
+ EXPECT_NE(TestConnectionId(0xdead), TestConnectionId(0xbeef));
+ EXPECT_EQ(0x1337u, TestConnectionIdToUInt64(TestConnectionId(0x1337)));
+ EXPECT_NE(0xdeadu, TestConnectionIdToUInt64(TestConnectionId(0xbeef)));
+}
+
+TEST_F(QuicTestUtilsTest, BasicApproxEq) {
+ ExpectApproxEq(10, 10, 1e-6f);
+ ExpectApproxEq(1000, 1001, 0.01f);
+ EXPECT_NONFATAL_FAILURE(ExpectApproxEq(1000, 1100, 0.01f), "");
+
+ ExpectApproxEq(64, 31, 0.55f);
+ EXPECT_NONFATAL_FAILURE(ExpectApproxEq(31, 64, 0.55f), "");
+}
+
+TEST_F(QuicTestUtilsTest, QuicTimeDelta) {
+ ExpectApproxEq(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMicroseconds(1003), 0.01f);
+ EXPECT_NONFATAL_FAILURE(
+ ExpectApproxEq(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMicroseconds(1200), 0.01f),
+ "");
+}
+
+TEST_F(QuicTestUtilsTest, QuicBandwidth) {
+ ExpectApproxEq(QuicBandwidth::FromBytesPerSecond(1000),
+ QuicBandwidth::FromBitsPerSecond(8005), 0.01f);
+ EXPECT_NONFATAL_FAILURE(
+ ExpectApproxEq(QuicBandwidth::FromBytesPerSecond(1000),
+ QuicBandwidth::FromBitsPerSecond(9005), 0.01f),
+ "");
+}
+
+// Ensure that SimpleRandom does not change its output for a fixed seed.
+TEST_F(QuicTestUtilsTest, SimpleRandomStability) {
+ SimpleRandom rng;
+ rng.set_seed(UINT64_C(0x1234567800010001));
+ EXPECT_EQ(UINT64_C(14865409841904857791), rng.RandUint64());
+ EXPECT_EQ(UINT64_C(12139094019410129741), rng.RandUint64());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.cc
new file mode 100644
index 00000000000..7c90321883b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h"
+
+namespace quic {
+namespace test {
+
+bool QuicTimeWaitListManagerPeer::ShouldSendResponse(
+ QuicTimeWaitListManager* manager,
+ int received_packet_count) {
+ return manager->ShouldSendResponse(received_packet_count);
+}
+
+QuicTime::Delta QuicTimeWaitListManagerPeer::time_wait_period(
+ QuicTimeWaitListManager* manager) {
+ return manager->time_wait_period_;
+}
+
+QuicAlarm* QuicTimeWaitListManagerPeer::expiration_alarm(
+ QuicTimeWaitListManager* manager) {
+ return manager->connection_id_clean_up_alarm_.get();
+}
+
+void QuicTimeWaitListManagerPeer::set_clock(QuicTimeWaitListManager* manager,
+ const QuicClock* clock) {
+ manager->clock_ = clock;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h
new file mode 100644
index 00000000000..0fdf976fb74
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_time_wait_list_manager_peer.h
@@ -0,0 +1,29 @@
+// 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 QUICHE_QUIC_TEST_TOOLS_QUIC_TIME_WAIT_LIST_MANAGER_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_TIME_WAIT_LIST_MANAGER_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_time_wait_list_manager.h"
+
+namespace quic {
+namespace test {
+
+class QuicTimeWaitListManagerPeer {
+ public:
+ static bool ShouldSendResponse(QuicTimeWaitListManager* manager,
+ int received_packet_count);
+
+ static QuicTime::Delta time_wait_period(QuicTimeWaitListManager* manager);
+
+ static QuicAlarm* expiration_alarm(QuicTimeWaitListManager* manager);
+
+ static void set_clock(QuicTimeWaitListManager* manager,
+ const QuicClock* clock);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_TIME_WAIT_LIST_MANAGER_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.cc
new file mode 100644
index 00000000000..1ecc65f2480
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.cc
@@ -0,0 +1,24 @@
+// 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 "net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+const QuicStreamFrame& QuicUnackedPacketMapPeer::GetAggregatedStreamFrame(
+ const QuicUnackedPacketMap& unacked_packets) {
+ return unacked_packets.aggregated_stream_frame_;
+}
+
+// static
+void QuicUnackedPacketMapPeer::SetPerspective(
+ QuicUnackedPacketMap* unacked_packets,
+ Perspective perspective) {
+ *const_cast<Perspective*>(&unacked_packets->perspective_) = perspective;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h
new file mode 100644
index 00000000000..3bba0b6ced4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/quic_unacked_packet_map_peer.h
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_unacked_packet_map.h"
+
+namespace quic {
+namespace test {
+
+class QuicUnackedPacketMapPeer {
+ public:
+ static const QuicStreamFrame& GetAggregatedStreamFrame(
+ const QuicUnackedPacketMap& unacked_packets);
+
+ static void SetPerspective(QuicUnackedPacketMap* unacked_packets,
+ Perspective perspective);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_QUIC_UNACKED_PACKET_MAP_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.cc
new file mode 100644
index 00000000000..e8c6810fa52
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.h"
+
+namespace quic {
+namespace test {
+
+// static
+void RttStatsPeer::SetSmoothedRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms) {
+ rtt_stats->smoothed_rtt_ = rtt_ms;
+}
+
+// static
+void RttStatsPeer::SetMinRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms) {
+ rtt_stats->min_rtt_ = rtt_ms;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.h b/chromium/net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.h
new file mode 100644
index 00000000000..6243b7a349f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/rtt_stats_peer.h
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
+#define QUICHE_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
+
+#include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+
+namespace quic {
+namespace test {
+
+class RttStatsPeer {
+ public:
+ RttStatsPeer() = delete;
+
+ static void SetSmoothedRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms);
+
+ static void SetMinRtt(RttStats* rtt_stats, QuicTime::Delta rtt_ms);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_RTT_STATS_PEER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.cc b/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.cc
new file mode 100644
index 00000000000..ca159130a87
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.cc
@@ -0,0 +1,121 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/server_thread.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
+
+namespace quic {
+namespace test {
+
+ServerThread::ServerThread(QuicServer* server, const QuicSocketAddress& address)
+ : QuicThread("server_thread"),
+ server_(server),
+ address_(address),
+ port_(0),
+ initialized_(false) {}
+
+ServerThread::~ServerThread() = default;
+
+void ServerThread::Initialize() {
+ if (initialized_) {
+ return;
+ }
+
+ server_->CreateUDPSocketAndListen(address_);
+
+ QuicWriterMutexLock lock(&port_lock_);
+ port_ = server_->port();
+
+ initialized_ = true;
+}
+
+void ServerThread::Run() {
+ if (!initialized_) {
+ Initialize();
+ }
+
+ while (!quit_.HasBeenNotified()) {
+ if (pause_.HasBeenNotified() && !resume_.HasBeenNotified()) {
+ paused_.Notify();
+ resume_.WaitForNotification();
+ }
+ server_->WaitForEvents();
+ ExecuteScheduledActions();
+ MaybeNotifyOfHandshakeConfirmation();
+ }
+
+ server_->Shutdown();
+}
+
+int ServerThread::GetPort() {
+ QuicReaderMutexLock lock(&port_lock_);
+ int rc = port_;
+ return rc;
+}
+
+void ServerThread::Schedule(std::function<void()> action) {
+ DCHECK(!quit_.HasBeenNotified());
+ QuicWriterMutexLock lock(&scheduled_actions_lock_);
+ scheduled_actions_.push_back(std::move(action));
+}
+
+void ServerThread::WaitForCryptoHandshakeConfirmed() {
+ confirmed_.WaitForNotification();
+}
+
+void ServerThread::Pause() {
+ DCHECK(!pause_.HasBeenNotified());
+ pause_.Notify();
+ paused_.WaitForNotification();
+}
+
+void ServerThread::Resume() {
+ DCHECK(!resume_.HasBeenNotified());
+ DCHECK(pause_.HasBeenNotified());
+ resume_.Notify();
+}
+
+void ServerThread::Quit() {
+ if (pause_.HasBeenNotified() && !resume_.HasBeenNotified()) {
+ resume_.Notify();
+ }
+ if (!quit_.HasBeenNotified()) {
+ quit_.Notify();
+ }
+}
+
+void ServerThread::MaybeNotifyOfHandshakeConfirmation() {
+ if (confirmed_.HasBeenNotified()) {
+ // Only notify once.
+ return;
+ }
+ QuicDispatcher* dispatcher = QuicServerPeer::GetDispatcher(server());
+ if (dispatcher->session_map().empty()) {
+ // Wait for a session to be created.
+ return;
+ }
+ QuicSession* session = dispatcher->session_map().begin()->second.get();
+ if (session->IsCryptoHandshakeConfirmed()) {
+ confirmed_.Notify();
+ }
+}
+
+void ServerThread::ExecuteScheduledActions() {
+ QuicDeque<std::function<void()>> actions;
+ {
+ QuicWriterMutexLock lock(&scheduled_actions_lock_);
+ actions.swap(scheduled_actions_);
+ }
+ while (!actions.empty()) {
+ actions.front()();
+ actions.pop_front();
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.h b/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.h
new file mode 100644
index 00000000000..ffbce7f15a9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/server_thread.h
@@ -0,0 +1,88 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SERVER_THREAD_H_
+#define QUICHE_QUIC_TEST_TOOLS_SERVER_THREAD_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_thread.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+namespace quic {
+namespace test {
+
+// Simple wrapper class to run QuicServer in a dedicated thread.
+class ServerThread : public QuicThread {
+ public:
+ ServerThread(QuicServer* server, const QuicSocketAddress& address);
+ ServerThread(const ServerThread&) = delete;
+ ServerThread& operator=(const ServerThread&) = delete;
+
+ ~ServerThread() override;
+
+ // Prepares the server, but does not start accepting connections. Useful for
+ // injecting mocks.
+ void Initialize();
+
+ // Runs the event loop. Will initialize if necessary.
+ void Run() override;
+
+ // Schedules the given action for execution in the event loop.
+ void Schedule(std::function<void()> action);
+
+ // Waits for the handshake to be confirmed for the first session created.
+ void WaitForCryptoHandshakeConfirmed();
+
+ // Pauses execution of the server until Resume() is called. May only be
+ // called once.
+ void Pause();
+
+ // Resumes execution of the server after Pause() has been called. May only
+ // be called once.
+ void Resume();
+
+ // Stops the server from executing and shuts it down, destroying all
+ // server objects.
+ void Quit();
+
+ // Returns the underlying server. Care must be taken to avoid data races
+ // when accessing the server. It is always safe to access the server
+ // after calling Pause() and before calling Resume().
+ QuicServer* server() { return server_.get(); }
+
+ // Returns the port that the server is listening on.
+ int GetPort();
+
+ private:
+ void MaybeNotifyOfHandshakeConfirmation();
+ void ExecuteScheduledActions();
+
+ QuicNotification
+ confirmed_; // Notified when the first handshake is confirmed.
+ QuicNotification pause_; // Notified when the server should pause.
+ QuicNotification paused_; // Notitied when the server has paused
+ QuicNotification resume_; // Notified when the server should resume.
+ QuicNotification quit_; // Notified when the server should quit.
+
+ std::unique_ptr<QuicServer> server_;
+ QuicSocketAddress address_;
+ mutable QuicMutex port_lock_;
+ int port_ GUARDED_BY(port_lock_);
+
+ bool initialized_;
+
+ QuicMutex scheduled_actions_lock_;
+ QuicDeque<std::function<void()>> scheduled_actions_
+ GUARDED_BY(scheduled_actions_lock_);
+};
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SERVER_THREAD_H_
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
new file mode 100644
index 00000000000..89bf5b952e2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.cc
@@ -0,0 +1,71 @@
+// 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.
+
+#include "net/third_party/quiche/src/quic/test_tools/simple_data_producer.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+namespace test {
+
+SimpleDataProducer::SimpleDataProducer() {}
+
+SimpleDataProducer::~SimpleDataProducer() {}
+
+void SimpleDataProducer::SaveStreamData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ size_t iov_offset,
+ QuicByteCount data_length) {
+ if (data_length == 0) {
+ return;
+ }
+ if (!QuicContainsKey(send_buffer_map_, id)) {
+ send_buffer_map_[id] = QuicMakeUnique<QuicStreamSendBuffer>(&allocator_);
+ }
+ send_buffer_map_[id]->SaveStreamData(iov, iov_count, iov_offset, data_length);
+}
+
+void SimpleDataProducer::SaveCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicStringPiece data) {
+ auto key = std::make_pair(level, offset);
+ crypto_buffer_map_[key] = data;
+}
+
+WriteStreamDataResult SimpleDataProducer::WriteStreamData(
+ QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ auto iter = send_buffer_map_.find(id);
+ if (iter == send_buffer_map_.end()) {
+ return STREAM_MISSING;
+ }
+ if (iter->second->WriteStreamData(offset, data_length, writer)) {
+ return WRITE_SUCCESS;
+ }
+ return WRITE_FAILED;
+}
+
+bool SimpleDataProducer::WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ auto it = crypto_buffer_map_.find(std::make_pair(level, offset));
+ if (it == crypto_buffer_map_.end() || it->second.length() < data_length) {
+ return false;
+ }
+ return writer->WriteStringPiece(
+ QuicStringPiece(it->second.data(), data_length));
+}
+
+} // namespace test
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.h b/chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.h
new file mode 100644
index 00000000000..d51ec69be70
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_data_producer.h
@@ -0,0 +1,91 @@
+// 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_TEST_TOOLS_SIMPLE_DATA_PRODUCER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMPLE_DATA_PRODUCER_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_send_buffer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+namespace test {
+
+// A simple data producer which copies stream data into a map from stream
+// id to send buffer.
+class SimpleDataProducer : public QuicStreamFrameDataProducer {
+ public:
+ SimpleDataProducer();
+ ~SimpleDataProducer() override;
+
+ // Saves data to be provided when WriteStreamData is called. Data of length
+ // |data_length| is buffered to be provided for stream |id|. Multiple calls to
+ // SaveStreamData for the same stream ID append to the buffer for that stream.
+ // The data to be buffered is provided in |iov_count| iovec structs, with
+ // |iov| pointing to the first, and |iov_offset| indicating how many bytes
+ // into the iovec structs the data starts.
+ void SaveStreamData(QuicStreamId id,
+ const struct iovec* iov,
+ int iov_count,
+ size_t iov_offset,
+ QuicByteCount data_length);
+
+ void SaveCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicStringPiece data);
+
+ // QuicStreamFrameDataProducer
+ WriteStreamDataResult WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+ bool WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+
+ // TODO(wub): Allow QuicDefaultHasher to accept a pair. Then remove this.
+ class PairHash {
+ public:
+ template <class T1, class T2>
+ size_t operator()(const std::pair<T1, T2>& pair) const {
+ return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
+ }
+ };
+
+ private:
+ using SendBufferMap =
+ QuicUnorderedMap<QuicStreamId, std::unique_ptr<QuicStreamSendBuffer>>;
+
+ using CryptoBufferMap =
+ QuicUnorderedMap<std::pair<EncryptionLevel, QuicStreamOffset>,
+ QuicStringPiece,
+ PairHash>;
+
+ SimpleBufferAllocator allocator_;
+
+ SendBufferMap send_buffer_map_;
+
+ // |crypto_buffer_map_| stores data provided by SaveCryptoData to later write
+ // in WriteCryptoData. The level and data passed into SaveCryptoData are used
+ // as the key to identify the data when WriteCryptoData is called.
+ // WriteCryptoData will only succeed if there is data in the map for the
+ // provided level and offset, and the data in the map matches the data_length
+ // passed into WriteCryptoData.
+ //
+ // Unlike SaveStreamData/WriteStreamData which uses a map of
+ // QuicStreamSendBuffers (for each stream ID), this map provides data for
+ // specific offsets. Using a QuicStreamSendBuffer requires that all data
+ // before an offset exist, whereas this allows providing data that exists at
+ // arbitrary offsets for testing.
+ CryptoBufferMap crypto_buffer_map_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMPLE_DATA_PRODUCER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc
new file mode 100644
index 00000000000..6a90a79a074
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.cc
@@ -0,0 +1,400 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h"
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+namespace test {
+
+class SimpleFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ SimpleFramerVisitor() : error_(QUIC_NO_ERROR) {}
+ SimpleFramerVisitor(const SimpleFramerVisitor&) = delete;
+ SimpleFramerVisitor& operator=(const SimpleFramerVisitor&) = delete;
+
+ ~SimpleFramerVisitor() override {}
+
+ void OnError(QuicFramer* framer) override { error_ = framer->error(); }
+
+ bool OnProtocolVersionMismatch(ParsedQuicVersion version,
+ PacketHeaderFormat form) override {
+ return false;
+ }
+
+ void OnPacket() override {}
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {
+ public_reset_packet_ = QuicMakeUnique<QuicPublicResetPacket>((packet));
+ }
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {
+ version_negotiation_packet_ =
+ QuicMakeUnique<QuicVersionNegotiationPacket>((packet));
+ }
+
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override {
+ return true;
+ }
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override {
+ return true;
+ }
+ void OnDecryptedPacket(EncryptionLevel level) override {
+ last_decrypted_level_ = level;
+ }
+ bool OnPacketHeader(const QuicPacketHeader& header) override {
+ has_header_ = true;
+ header_ = header;
+ return true;
+ }
+
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {}
+
+ bool OnStreamFrame(const QuicStreamFrame& frame) override {
+ // Save a copy of the data so it is valid after the packet is processed.
+ std::string* string_data =
+ new std::string(frame.data_buffer, frame.data_length);
+ stream_data_.push_back(QuicWrapUnique(string_data));
+ // TODO(ianswett): A pointer isn't necessary with emplace_back.
+ stream_frames_.push_back(QuicMakeUnique<QuicStreamFrame>(
+ frame.stream_id, frame.fin, frame.offset,
+ QuicStringPiece(*string_data)));
+ return true;
+ }
+
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+ // Save a copy of the data so it is valid after the packet is processed.
+ std::string* string_data =
+ new std::string(frame.data_buffer, frame.data_length);
+ crypto_data_.push_back(QuicWrapUnique(string_data));
+ crypto_frames_.push_back(QuicMakeUnique<QuicCryptoFrame>(
+ frame.level, frame.offset, QuicStringPiece(*string_data)));
+ return true;
+ }
+
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta ack_delay_time) override {
+ QuicAckFrame ack_frame;
+ ack_frame.largest_acked = largest_acked;
+ ack_frame.ack_delay_time = ack_delay_time;
+ ack_frames_.push_back(ack_frame);
+ return true;
+ }
+
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
+ DCHECK(!ack_frames_.empty());
+ ack_frames_[ack_frames_.size() - 1].packets.AddRange(start, end);
+ return true;
+ }
+
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override {
+ return true;
+ }
+
+ bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; }
+
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
+ stop_waiting_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override {
+ padding_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnPingFrame(const QuicPingFrame& frame) override {
+ ping_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
+ rst_stream_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override {
+ connection_close_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override {
+ new_connection_id_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override {
+ retire_connection_id_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+ new_token_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+ stop_sending_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override {
+ path_challenge_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override {
+ path_response_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override {
+ goaway_frames_.push_back(frame);
+ return true;
+ }
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
+ max_stream_id_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
+ stream_id_blocked_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {
+ window_update_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override {
+ blocked_frames_.push_back(frame);
+ return true;
+ }
+
+ bool OnMessageFrame(const QuicMessageFrame& frame) override {
+ message_frames_.emplace_back(frame.data, frame.message_length);
+ return true;
+ }
+
+ void OnPacketComplete() override {}
+
+ bool IsValidStatelessResetToken(QuicUint128 token) const override {
+ return false;
+ }
+
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {
+ stateless_reset_packet_ =
+ QuicMakeUnique<QuicIetfStatelessResetPacket>(packet);
+ }
+
+ const QuicPacketHeader& header() const { return header_; }
+ const std::vector<QuicAckFrame>& ack_frames() const { return ack_frames_; }
+ const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const {
+ return connection_close_frames_;
+ }
+
+ const std::vector<QuicGoAwayFrame>& goaway_frames() const {
+ return goaway_frames_;
+ }
+ const std::vector<QuicMaxStreamIdFrame>& max_stream_id_frames() const {
+ return max_stream_id_frames_;
+ }
+ const std::vector<QuicStreamIdBlockedFrame>& stream_id_blocked_frames()
+ const {
+ return stream_id_blocked_frames_;
+ }
+ const std::vector<QuicRstStreamFrame>& rst_stream_frames() const {
+ return rst_stream_frames_;
+ }
+ const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const {
+ return stream_frames_;
+ }
+ const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const {
+ return crypto_frames_;
+ }
+ const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return stop_waiting_frames_;
+ }
+ const std::vector<QuicPingFrame>& ping_frames() const { return ping_frames_; }
+ const std::vector<QuicMessageFrame>& message_frames() const {
+ return message_frames_;
+ }
+ const std::vector<QuicWindowUpdateFrame>& window_update_frames() const {
+ return window_update_frames_;
+ }
+ const std::vector<QuicPaddingFrame>& padding_frames() const {
+ return padding_frames_;
+ }
+ const std::vector<QuicPathChallengeFrame>& path_challenge_frames() const {
+ return path_challenge_frames_;
+ }
+ const std::vector<QuicPathResponseFrame>& path_response_frames() const {
+ return path_response_frames_;
+ }
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const {
+ return version_negotiation_packet_.get();
+ }
+ EncryptionLevel last_decrypted_level() const { return last_decrypted_level_; }
+
+ private:
+ QuicErrorCode error_;
+ bool has_header_;
+ QuicPacketHeader header_;
+ std::unique_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
+ std::unique_ptr<QuicPublicResetPacket> public_reset_packet_;
+ std::unique_ptr<QuicIetfStatelessResetPacket> stateless_reset_packet_;
+ std::vector<QuicAckFrame> ack_frames_;
+ std::vector<QuicStopWaitingFrame> stop_waiting_frames_;
+ std::vector<QuicPaddingFrame> padding_frames_;
+ std::vector<QuicPingFrame> ping_frames_;
+ std::vector<std::unique_ptr<QuicStreamFrame>> stream_frames_;
+ std::vector<std::unique_ptr<QuicCryptoFrame>> crypto_frames_;
+ std::vector<QuicRstStreamFrame> rst_stream_frames_;
+ std::vector<QuicGoAwayFrame> goaway_frames_;
+ std::vector<QuicStreamIdBlockedFrame> stream_id_blocked_frames_;
+ std::vector<QuicMaxStreamIdFrame> max_stream_id_frames_;
+ std::vector<QuicConnectionCloseFrame> connection_close_frames_;
+ std::vector<QuicStopSendingFrame> stop_sending_frames_;
+ std::vector<QuicPathChallengeFrame> path_challenge_frames_;
+ std::vector<QuicPathResponseFrame> path_response_frames_;
+ std::vector<QuicWindowUpdateFrame> window_update_frames_;
+ std::vector<QuicBlockedFrame> blocked_frames_;
+ std::vector<QuicNewConnectionIdFrame> new_connection_id_frames_;
+ std::vector<QuicRetireConnectionIdFrame> retire_connection_id_frames_;
+ std::vector<QuicNewTokenFrame> new_token_frames_;
+ std::vector<QuicMessageFrame> message_frames_;
+ std::vector<std::unique_ptr<std::string>> stream_data_;
+ std::vector<std::unique_ptr<std::string>> crypto_data_;
+ EncryptionLevel last_decrypted_level_;
+};
+
+SimpleQuicFramer::SimpleQuicFramer()
+ : framer_(AllSupportedVersions(),
+ QuicTime::Zero(),
+ Perspective::IS_SERVER,
+ kQuicDefaultConnectionIdLength) {}
+
+SimpleQuicFramer::SimpleQuicFramer(
+ const ParsedQuicVersionVector& supported_versions)
+ : framer_(supported_versions,
+ QuicTime::Zero(),
+ Perspective::IS_SERVER,
+ kQuicDefaultConnectionIdLength) {}
+
+SimpleQuicFramer::SimpleQuicFramer(
+ const ParsedQuicVersionVector& supported_versions,
+ Perspective perspective)
+ : framer_(supported_versions,
+ QuicTime::Zero(),
+ perspective,
+ kQuicDefaultConnectionIdLength) {}
+
+SimpleQuicFramer::~SimpleQuicFramer() {}
+
+bool SimpleQuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
+ visitor_ = QuicMakeUnique<SimpleFramerVisitor>();
+ framer_.set_visitor(visitor_.get());
+ return framer_.ProcessPacket(packet);
+}
+
+void SimpleQuicFramer::Reset() {
+ visitor_ = QuicMakeUnique<SimpleFramerVisitor>();
+}
+
+const QuicPacketHeader& SimpleQuicFramer::header() const {
+ return visitor_->header();
+}
+
+const QuicVersionNegotiationPacket*
+SimpleQuicFramer::version_negotiation_packet() const {
+ return visitor_->version_negotiation_packet();
+}
+
+EncryptionLevel SimpleQuicFramer::last_decrypted_level() const {
+ return visitor_->last_decrypted_level();
+}
+
+QuicFramer* SimpleQuicFramer::framer() {
+ return &framer_;
+}
+
+size_t SimpleQuicFramer::num_frames() const {
+ return ack_frames().size() + goaway_frames().size() +
+ rst_stream_frames().size() + stop_waiting_frames().size() +
+ path_challenge_frames().size() + path_response_frames().size() +
+ stream_frames().size() + ping_frames().size() +
+ connection_close_frames().size() + padding_frames().size() +
+ crypto_frames().size();
+}
+
+const std::vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const {
+ return visitor_->ack_frames();
+}
+
+const std::vector<QuicStopWaitingFrame>& SimpleQuicFramer::stop_waiting_frames()
+ const {
+ return visitor_->stop_waiting_frames();
+}
+
+const std::vector<QuicPathChallengeFrame>&
+SimpleQuicFramer::path_challenge_frames() const {
+ return visitor_->path_challenge_frames();
+}
+const std::vector<QuicPathResponseFrame>&
+SimpleQuicFramer::path_response_frames() const {
+ return visitor_->path_response_frames();
+}
+
+const std::vector<QuicPingFrame>& SimpleQuicFramer::ping_frames() const {
+ return visitor_->ping_frames();
+}
+
+const std::vector<QuicMessageFrame>& SimpleQuicFramer::message_frames() const {
+ return visitor_->message_frames();
+}
+
+const std::vector<QuicWindowUpdateFrame>&
+SimpleQuicFramer::window_update_frames() const {
+ return visitor_->window_update_frames();
+}
+
+const std::vector<std::unique_ptr<QuicStreamFrame>>&
+SimpleQuicFramer::stream_frames() const {
+ return visitor_->stream_frames();
+}
+
+const std::vector<std::unique_ptr<QuicCryptoFrame>>&
+SimpleQuicFramer::crypto_frames() const {
+ return visitor_->crypto_frames();
+}
+
+const std::vector<QuicRstStreamFrame>& SimpleQuicFramer::rst_stream_frames()
+ const {
+ return visitor_->rst_stream_frames();
+}
+
+const std::vector<QuicGoAwayFrame>& SimpleQuicFramer::goaway_frames() const {
+ return visitor_->goaway_frames();
+}
+
+const std::vector<QuicConnectionCloseFrame>&
+SimpleQuicFramer::connection_close_frames() const {
+ return visitor_->connection_close_frames();
+}
+
+const std::vector<QuicPaddingFrame>& SimpleQuicFramer::padding_frames() const {
+ return visitor_->padding_frames();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h
new file mode 100644
index 00000000000..a254ce523e9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_quic_framer.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
+
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+
+namespace quic {
+
+struct QuicAckFrame;
+
+namespace test {
+
+class SimpleFramerVisitor;
+
+// Peer to make public a number of otherwise private QuicFramer methods.
+class SimpleQuicFramer {
+ public:
+ SimpleQuicFramer();
+ explicit SimpleQuicFramer(const ParsedQuicVersionVector& supported_versions);
+ SimpleQuicFramer(const ParsedQuicVersionVector& supported_versions,
+ Perspective perspective);
+ SimpleQuicFramer(const SimpleQuicFramer&) = delete;
+ SimpleQuicFramer& operator=(const SimpleQuicFramer&) = delete;
+ ~SimpleQuicFramer();
+
+ bool ProcessPacket(const QuicEncryptedPacket& packet);
+ void Reset();
+
+ const QuicPacketHeader& header() const;
+ size_t num_frames() const;
+ const std::vector<QuicAckFrame>& ack_frames() const;
+ const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const;
+ const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const;
+ const std::vector<QuicPathChallengeFrame>& path_challenge_frames() const;
+ const std::vector<QuicPathResponseFrame>& path_response_frames() const;
+ const std::vector<QuicPingFrame>& ping_frames() const;
+ const std::vector<QuicMessageFrame>& message_frames() const;
+ const std::vector<QuicWindowUpdateFrame>& window_update_frames() const;
+ const std::vector<QuicGoAwayFrame>& goaway_frames() const;
+ const std::vector<QuicRstStreamFrame>& rst_stream_frames() const;
+ const std::vector<std::unique_ptr<QuicStreamFrame>>& stream_frames() const;
+ const std::vector<std::unique_ptr<QuicCryptoFrame>>& crypto_frames() const;
+ const std::vector<QuicPaddingFrame>& padding_frames() const;
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const;
+ EncryptionLevel last_decrypted_level() const;
+
+ QuicFramer* framer();
+
+ void SetSupportedVersions(const ParsedQuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
+ private:
+ QuicFramer framer_;
+ std::unique_ptr<SimpleFramerVisitor> visitor_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
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
new file mode 100644
index 00000000000..643821ab545
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.cc
@@ -0,0 +1,646 @@
+// 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 "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+namespace test {
+
+SimpleSessionNotifier::SimpleSessionNotifier(QuicConnection* connection)
+ : last_control_frame_id_(kInvalidControlFrameId),
+ least_unacked_(1),
+ least_unsent_(1),
+ connection_(connection) {}
+
+SimpleSessionNotifier::~SimpleSessionNotifier() {
+ while (!control_frames_.empty()) {
+ DeleteFrame(&control_frames_.front());
+ control_frames_.pop_front();
+ }
+}
+
+SimpleSessionNotifier::StreamState::StreamState()
+ : bytes_total(0),
+ bytes_sent(0),
+ fin_buffered(false),
+ fin_sent(false),
+ fin_outstanding(false),
+ fin_lost(false) {}
+
+SimpleSessionNotifier::StreamState::~StreamState() {}
+
+QuicConsumedData SimpleSessionNotifier::WriteOrBufferData(
+ QuicStreamId id,
+ QuicByteCount data_length,
+ StreamSendingState state) {
+ if (!QuicContainsKey(stream_map_, id)) {
+ stream_map_[id] = StreamState();
+ }
+ StreamState& stream_state = stream_map_.find(id)->second;
+ const bool had_buffered_data =
+ HasBufferedStreamData() || HasBufferedControlFrames();
+ QuicConsumedData total_consumed(0, false);
+ QuicStreamOffset offset = stream_state.bytes_sent;
+ QUIC_DVLOG(1) << "WriteOrBuffer stream_id: " << id << " [" << offset << ", "
+ << offset + data_length << "), fin: " << (state != NO_FIN);
+ stream_state.bytes_total += data_length;
+ stream_state.fin_buffered = state != NO_FIN;
+ if (had_buffered_data) {
+ QUIC_DLOG(WARNING) << "Connection is write blocked";
+ return {0, false};
+ }
+ const size_t length = stream_state.bytes_total - stream_state.bytes_sent;
+ connection_->SetTransmissionType(NOT_RETRANSMISSION);
+ QuicConsumedData consumed =
+ connection_->SendStreamData(id, length, stream_state.bytes_sent,
+ stream_state.fin_buffered ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << "consumed: " << consumed;
+ OnStreamDataConsumed(id, stream_state.bytes_sent, consumed.bytes_consumed,
+ consumed.fin_consumed);
+ return consumed;
+}
+
+void SimpleSessionNotifier::OnStreamDataConsumed(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin) {
+ StreamState& state = stream_map_.find(id)->second;
+ if (id == QuicUtils::GetCryptoStreamId(connection_->transport_version()) &&
+ data_length > 0) {
+ crypto_bytes_transferred_[connection_->encryption_level()].Add(
+ offset, offset + data_length);
+ }
+ state.bytes_sent += data_length;
+ state.fin_sent = fin;
+ state.fin_outstanding = fin;
+}
+
+size_t SimpleSessionNotifier::WriteCryptoData(EncryptionLevel level,
+ QuicByteCount data_length,
+ QuicStreamOffset offset) {
+ crypto_state_[level].bytes_total += data_length;
+ size_t bytes_written =
+ connection_->SendCryptoData(level, data_length, offset);
+ crypto_state_[level].bytes_sent += bytes_written;
+ crypto_bytes_transferred_[level].Add(offset, offset + bytes_written);
+ return bytes_written;
+}
+
+void SimpleSessionNotifier::WriteOrBufferRstStream(
+ QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ QUIC_DVLOG(1) << "Writing RST_STREAM_FRAME";
+ const bool had_buffered_data =
+ HasBufferedStreamData() || HasBufferedControlFrames();
+ control_frames_.emplace_back((QuicFrame(new QuicRstStreamFrame(
+ ++last_control_frame_id_, id, error, bytes_written))));
+ if (error != QUIC_STREAM_NO_ERROR) {
+ // Delete stream to avoid retransmissions.
+ stream_map_.erase(id);
+ }
+ if (had_buffered_data) {
+ QUIC_DLOG(WARNING) << "Connection is write blocked";
+ return;
+ }
+ WriteBufferedControlFrames();
+}
+
+void SimpleSessionNotifier::WriteOrBufferPing() {
+ QUIC_DVLOG(1) << "Writing PING_FRAME";
+ const bool had_buffered_data =
+ HasBufferedStreamData() || HasBufferedControlFrames();
+ control_frames_.emplace_back(
+ (QuicFrame(QuicPingFrame(++last_control_frame_id_))));
+ if (had_buffered_data) {
+ QUIC_DLOG(WARNING) << "Connection is write blocked";
+ return;
+ }
+ WriteBufferedControlFrames();
+}
+
+void SimpleSessionNotifier::NeuterUnencryptedData() {
+ for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_INITIAL]) {
+ // TODO(nharper): Handle CRYPTO frame case.
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()), false,
+ interval.min(), interval.max() - interval.min());
+ OnFrameAcked(QuicFrame(stream_frame), QuicTime::Delta::Zero());
+ }
+}
+
+void SimpleSessionNotifier::OnCanWrite() {
+ if (!RetransmitLostCryptoData() || !RetransmitLostControlFrames() ||
+ !RetransmitLostStreamData()) {
+ return;
+ }
+ // Write buffered control frames.
+ if (!WriteBufferedControlFrames()) {
+ return;
+ }
+ // Write new data.
+ // TODO(nharper): Write CRYPTO frames.
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ if (!StreamHasBufferedData(pair.first)) {
+ continue;
+ }
+
+ const size_t length = state.bytes_total - state.bytes_sent;
+ const bool can_bundle_fin =
+ state.fin_buffered && (state.bytes_sent + length == state.bytes_total);
+ connection_->SetTransmissionType(NOT_RETRANSMISSION);
+ QuicConsumedData consumed = connection_->SendStreamData(
+ pair.first, length, state.bytes_sent, can_bundle_fin ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << "Tries to write stream_id: " << pair.first << " ["
+ << state.bytes_sent << ", " << state.bytes_sent + length
+ << "), fin: " << can_bundle_fin
+ << ", and consumed: " << consumed;
+ OnStreamDataConsumed(pair.first, state.bytes_sent, consumed.bytes_consumed,
+ consumed.fin_consumed);
+ if (length != consumed.bytes_consumed ||
+ (can_bundle_fin && !consumed.fin_consumed)) {
+ break;
+ }
+ }
+}
+
+bool SimpleSessionNotifier::WillingToWrite() const {
+ QUIC_DVLOG(1) << "has_buffered_control_frames: " << HasBufferedControlFrames()
+ << " as_lost_control_frames: " << !lost_control_frames_.empty()
+ << " has_buffered_stream_data: " << HasBufferedStreamData()
+ << " has_lost_stream_data: " << HasLostStreamData();
+ return HasBufferedControlFrames() || !lost_control_frames_.empty() ||
+ HasBufferedStreamData() || HasLostStreamData();
+}
+
+QuicByteCount SimpleSessionNotifier::StreamBytesSent() const {
+ QuicByteCount bytes_sent = 0;
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ bytes_sent += state.bytes_sent;
+ }
+ return bytes_sent;
+}
+
+QuicByteCount SimpleSessionNotifier::StreamBytesToSend() const {
+ QuicByteCount bytes_to_send = 0;
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ bytes_to_send += (state.bytes_total - state.bytes_sent);
+ }
+ return bytes_to_send;
+}
+
+bool SimpleSessionNotifier::OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta /*ack_delay_time*/) {
+ QUIC_DVLOG(1) << "Acking " << frame;
+ if (frame.type == CRYPTO_FRAME) {
+ StreamState* state = &crypto_state_[frame.crypto_frame->level];
+ QuicStreamOffset offset = frame.crypto_frame->offset;
+ QuicByteCount data_length = frame.crypto_frame->data_length;
+ QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+ newly_acked.Difference(state->bytes_acked);
+ if (newly_acked.Empty()) {
+ return false;
+ }
+ state->bytes_acked.Add(offset, offset + data_length);
+ state->pending_retransmissions.Difference(offset, offset + data_length);
+ return true;
+ }
+ if (frame.type != STREAM_FRAME) {
+ return OnControlFrameAcked(frame);
+ }
+ if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ return false;
+ }
+ auto* state = &stream_map_.find(frame.stream_frame.stream_id)->second;
+ QuicStreamOffset offset = frame.stream_frame.offset;
+ QuicByteCount data_length = frame.stream_frame.data_length;
+ QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length);
+ newly_acked.Difference(state->bytes_acked);
+ const bool fin_newly_acked = frame.stream_frame.fin && state->fin_outstanding;
+ if (newly_acked.Empty() && !fin_newly_acked) {
+ return false;
+ }
+ state->bytes_acked.Add(offset, offset + data_length);
+ if (fin_newly_acked) {
+ state->fin_outstanding = false;
+ state->fin_lost = false;
+ }
+ state->pending_retransmissions.Difference(offset, offset + data_length);
+ return true;
+}
+
+void SimpleSessionNotifier::OnFrameLost(const QuicFrame& frame) {
+ QUIC_DVLOG(1) << "Losting " << frame;
+ if (frame.type == CRYPTO_FRAME) {
+ StreamState* state = &crypto_state_[frame.crypto_frame->level];
+ QuicStreamOffset offset = frame.crypto_frame->offset;
+ QuicByteCount data_length = frame.crypto_frame->data_length;
+ QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length);
+ bytes_lost.Difference(state->bytes_acked);
+ if (bytes_lost.Empty()) {
+ return;
+ }
+ for (const auto& lost : bytes_lost) {
+ state->pending_retransmissions.Add(lost.min(), lost.max());
+ }
+ return;
+ }
+ if (frame.type != STREAM_FRAME) {
+ OnControlFrameLost(frame);
+ return;
+ }
+ if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ return;
+ }
+ auto* state = &stream_map_.find(frame.stream_frame.stream_id)->second;
+ QuicStreamOffset offset = frame.stream_frame.offset;
+ QuicByteCount data_length = frame.stream_frame.data_length;
+ QuicIntervalSet<QuicStreamOffset> bytes_lost(offset, offset + data_length);
+ bytes_lost.Difference(state->bytes_acked);
+ const bool fin_lost = state->fin_outstanding && frame.stream_frame.fin;
+ if (bytes_lost.Empty() && !fin_lost) {
+ return;
+ }
+ for (const auto& lost : bytes_lost) {
+ state->pending_retransmissions.Add(lost.min(), lost.max());
+ }
+ state->fin_lost = fin_lost;
+}
+
+void SimpleSessionNotifier::RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) {
+ QuicConnection::ScopedPacketFlusher retransmission_flusher(
+ connection_, QuicConnection::SEND_ACK_IF_QUEUED);
+ connection_->SetTransmissionType(type);
+ for (const QuicFrame& frame : frames) {
+ if (frame.type == CRYPTO_FRAME) {
+ const StreamState& state = crypto_state_[frame.crypto_frame->level];
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ frame.crypto_frame->offset,
+ frame.crypto_frame->offset + frame.crypto_frame->data_length);
+ retransmission.Difference(state.bytes_acked);
+ for (const auto& interval : retransmission) {
+ QuicStreamOffset offset = interval.min();
+ QuicByteCount length = interval.max() - interval.min();
+ size_t consumed = connection_->SendCryptoData(frame.crypto_frame->level,
+ length, offset);
+ // CRYPTO frames should never be write blocked.
+ DCHECK_EQ(consumed, length);
+ }
+ }
+ if (frame.type != STREAM_FRAME) {
+ if (GetControlFrameId(frame) == kInvalidControlFrameId) {
+ continue;
+ }
+ QuicFrame copy = CopyRetransmittableControlFrame(frame);
+ if (!connection_->SendControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(&copy);
+ return;
+ }
+ continue;
+ }
+ if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ continue;
+ }
+ const auto& state = stream_map_.find(frame.stream_frame.stream_id)->second;
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ frame.stream_frame.offset,
+ frame.stream_frame.offset + frame.stream_frame.data_length);
+ EncryptionLevel retransmission_encryption_level =
+ connection_->encryption_level();
+ EncryptionLevel current_encryption_level = connection_->encryption_level();
+ if (frame.stream_frame.stream_id ==
+ QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ if (retransmission.Intersects(crypto_bytes_transferred_[i])) {
+ retransmission_encryption_level = static_cast<EncryptionLevel>(i);
+ retransmission.Intersection(crypto_bytes_transferred_[i]);
+ break;
+ }
+ }
+ }
+ retransmission.Difference(state.bytes_acked);
+ bool retransmit_fin = frame.stream_frame.fin && state.fin_outstanding;
+ QuicConsumedData consumed(0, false);
+ for (const auto& interval : retransmission) {
+ QuicStreamOffset retransmission_offset = interval.min();
+ QuicByteCount retransmission_length = interval.max() - interval.min();
+ const bool can_bundle_fin =
+ retransmit_fin &&
+ (retransmission_offset + retransmission_length == state.bytes_sent);
+ if (frame.stream_frame.stream_id ==
+ QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+ // Set appropriate encryption level for crypto stream.
+ connection_->SetDefaultEncryptionLevel(retransmission_encryption_level);
+ }
+ consumed = connection_->SendStreamData(
+ frame.stream_frame.stream_id, retransmission_length,
+ retransmission_offset, can_bundle_fin ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << "stream " << frame.stream_frame.stream_id
+ << " is forced to retransmit stream data ["
+ << retransmission_offset << ", "
+ << retransmission_offset + retransmission_length
+ << ") and fin: " << can_bundle_fin
+ << ", consumed: " << consumed;
+ if (can_bundle_fin) {
+ retransmit_fin = !consumed.fin_consumed;
+ }
+ if (frame.stream_frame.stream_id ==
+ QuicUtils::GetCryptoStreamId(connection_->transport_version())) {
+ // Restore encryption level.
+ connection_->SetDefaultEncryptionLevel(current_encryption_level);
+ }
+ if (consumed.bytes_consumed < retransmission_length ||
+ (can_bundle_fin && !consumed.fin_consumed)) {
+ // Connection is write blocked.
+ return;
+ }
+ }
+ if (retransmit_fin) {
+ QUIC_DVLOG(1) << "stream " << frame.stream_frame.stream_id
+ << " retransmits fin only frame.";
+ consumed = connection_->SendStreamData(frame.stream_frame.stream_id, 0,
+ state.bytes_sent, FIN);
+ }
+ }
+}
+
+bool SimpleSessionNotifier::IsFrameOutstanding(const QuicFrame& frame) const {
+ if (frame.type == CRYPTO_FRAME) {
+ QuicStreamOffset offset = frame.crypto_frame->offset;
+ QuicByteCount data_length = frame.crypto_frame->data_length;
+ bool ret = data_length > 0 &&
+ !crypto_state_[frame.crypto_frame->level].bytes_acked.Contains(
+ offset, offset + data_length);
+ return ret;
+ }
+ if (frame.type != STREAM_FRAME) {
+ return IsControlFrameOutstanding(frame);
+ }
+ if (!QuicContainsKey(stream_map_, frame.stream_frame.stream_id)) {
+ return false;
+ }
+ const auto& state = stream_map_.find(frame.stream_frame.stream_id)->second;
+ QuicStreamOffset offset = frame.stream_frame.offset;
+ QuicByteCount data_length = frame.stream_frame.data_length;
+ return (data_length > 0 &&
+ !state.bytes_acked.Contains(offset, offset + data_length)) ||
+ (frame.stream_frame.fin && state.fin_outstanding);
+}
+
+bool SimpleSessionNotifier::HasUnackedCryptoData() const {
+ if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ const StreamState& state = crypto_state_[i];
+ if (state.bytes_total > state.bytes_sent) {
+ return true;
+ }
+ QuicIntervalSet<QuicStreamOffset> bytes_to_ack(0, state.bytes_total);
+ bytes_to_ack.Difference(state.bytes_acked);
+ if (!bytes_to_ack.Empty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
+ connection_->transport_version()))) {
+ return false;
+ }
+ const auto& state =
+ stream_map_
+ .find(QuicUtils::GetCryptoStreamId(connection_->transport_version()))
+ ->second;
+ if (state.bytes_total > state.bytes_sent) {
+ return true;
+ }
+ QuicIntervalSet<QuicStreamOffset> bytes_to_ack(0, state.bytes_total);
+ bytes_to_ack.Difference(state.bytes_acked);
+ return !bytes_to_ack.Empty();
+}
+
+bool SimpleSessionNotifier::OnControlFrameAcked(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ return false;
+ }
+ DCHECK(id < least_unacked_ + control_frames_.size());
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ return false;
+ }
+ SetControlFrameId(kInvalidControlFrameId,
+ &control_frames_.at(id - least_unacked_));
+ lost_control_frames_.erase(id);
+ while (!control_frames_.empty() &&
+ GetControlFrameId(control_frames_.front()) == kInvalidControlFrameId) {
+ DeleteFrame(&control_frames_.front());
+ control_frames_.pop_front();
+ ++least_unacked_;
+ }
+ return true;
+}
+
+void SimpleSessionNotifier::OnControlFrameLost(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ return;
+ }
+ DCHECK(id < least_unacked_ + control_frames_.size());
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ return;
+ }
+ if (!QuicContainsKey(lost_control_frames_, id)) {
+ lost_control_frames_[id] = true;
+ }
+}
+
+bool SimpleSessionNotifier::IsControlFrameOutstanding(
+ const QuicFrame& frame) const {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ return false;
+ }
+ return id < least_unacked_ + control_frames_.size() && id >= least_unacked_ &&
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) !=
+ kInvalidControlFrameId;
+}
+
+bool SimpleSessionNotifier::RetransmitLostControlFrames() {
+ while (!lost_control_frames_.empty()) {
+ QuicFrame pending = control_frames_.at(lost_control_frames_.begin()->first -
+ least_unacked_);
+ QuicFrame copy = CopyRetransmittableControlFrame(pending);
+ connection_->SetTransmissionType(LOSS_RETRANSMISSION);
+ if (!connection_->SendControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(&copy);
+ break;
+ }
+ lost_control_frames_.pop_front();
+ }
+ return lost_control_frames_.empty();
+}
+
+bool SimpleSessionNotifier::RetransmitLostCryptoData() {
+ // TODO(nharper): Handle CRYPTO frame case.
+ if (!QuicContainsKey(stream_map_, QuicUtils::GetCryptoStreamId(
+ connection_->transport_version()))) {
+ return true;
+ }
+ auto& state =
+ stream_map_
+ .find(QuicUtils::GetCryptoStreamId(connection_->transport_version()))
+ ->second;
+ while (!state.pending_retransmissions.Empty()) {
+ connection_->SetTransmissionType(HANDSHAKE_RETRANSMISSION);
+ QuicIntervalSet<QuicStreamOffset> retransmission(
+ state.pending_retransmissions.begin()->min(),
+ state.pending_retransmissions.begin()->max());
+ EncryptionLevel retransmission_encryption_level = ENCRYPTION_INITIAL;
+ for (size_t i = 0; i < NUM_ENCRYPTION_LEVELS; ++i) {
+ if (retransmission.Intersects(crypto_bytes_transferred_[i])) {
+ retransmission_encryption_level = static_cast<EncryptionLevel>(i);
+ retransmission.Intersection(crypto_bytes_transferred_[i]);
+ break;
+ }
+ }
+ QuicStreamOffset retransmission_offset = retransmission.begin()->min();
+ QuicByteCount retransmission_length =
+ retransmission.begin()->max() - retransmission.begin()->min();
+ EncryptionLevel current_encryption_level = connection_->encryption_level();
+ // Set appropriate encryption level.
+ connection_->SetDefaultEncryptionLevel(retransmission_encryption_level);
+ QuicConsumedData consumed = connection_->SendStreamData(
+ QuicUtils::GetCryptoStreamId(connection_->transport_version()),
+ retransmission_length, retransmission_offset, NO_FIN);
+ // Restore encryption level.
+ connection_->SetDefaultEncryptionLevel(current_encryption_level);
+ state.pending_retransmissions.Difference(
+ retransmission_offset, retransmission_offset + consumed.bytes_consumed);
+ if (consumed.bytes_consumed < retransmission_length) {
+ break;
+ }
+ }
+ return state.pending_retransmissions.Empty();
+}
+
+bool SimpleSessionNotifier::RetransmitLostStreamData() {
+ for (auto& pair : stream_map_) {
+ StreamState& state = pair.second;
+ QuicConsumedData consumed(0, false);
+ while (!state.pending_retransmissions.Empty() || state.fin_lost) {
+ connection_->SetTransmissionType(LOSS_RETRANSMISSION);
+ if (state.pending_retransmissions.Empty()) {
+ QUIC_DVLOG(1) << "stream " << pair.first
+ << " retransmits fin only frame.";
+ consumed =
+ connection_->SendStreamData(pair.first, 0, state.bytes_sent, FIN);
+ state.fin_lost = !consumed.fin_consumed;
+ if (state.fin_lost) {
+ DLOG(INFO) << "Connection is write blocked";
+ return false;
+ }
+ } else {
+ QuicStreamOffset offset = state.pending_retransmissions.begin()->min();
+ QuicByteCount length = state.pending_retransmissions.begin()->max() -
+ state.pending_retransmissions.begin()->min();
+ const bool can_bundle_fin =
+ state.fin_lost && (offset + length == state.bytes_sent);
+ consumed = connection_->SendStreamData(pair.first, length, offset,
+ can_bundle_fin ? FIN : NO_FIN);
+ QUIC_DVLOG(1) << "stream " << pair.first
+ << " tries to retransmit stream data [" << offset << ", "
+ << offset + length << ") and fin: " << can_bundle_fin
+ << ", consumed: " << consumed;
+ state.pending_retransmissions.Difference(
+ offset, offset + consumed.bytes_consumed);
+ if (consumed.fin_consumed) {
+ state.fin_lost = false;
+ }
+ if (length > consumed.bytes_consumed ||
+ (can_bundle_fin && !consumed.fin_consumed)) {
+ DVLOG(1) << "Connection is write blocked";
+ break;
+ }
+ }
+ }
+ }
+ return !HasLostStreamData();
+}
+
+bool SimpleSessionNotifier::WriteBufferedControlFrames() {
+ while (HasBufferedControlFrames()) {
+ QuicFrame frame_to_send =
+ control_frames_.at(least_unsent_ - least_unacked_);
+ QuicFrame copy = CopyRetransmittableControlFrame(frame_to_send);
+ connection_->SetTransmissionType(NOT_RETRANSMISSION);
+ if (!connection_->SendControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(&copy);
+ break;
+ }
+ ++least_unsent_;
+ }
+ return !HasBufferedControlFrames();
+}
+
+bool SimpleSessionNotifier::HasBufferedControlFrames() const {
+ return least_unsent_ < least_unacked_ + control_frames_.size();
+}
+
+bool SimpleSessionNotifier::HasBufferedStreamData() const {
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ if (state.bytes_total > state.bytes_sent ||
+ (state.fin_buffered && !state.fin_sent)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SimpleSessionNotifier::StreamIsWaitingForAcks(QuicStreamId id) const {
+ if (!QuicContainsKey(stream_map_, id)) {
+ return false;
+ }
+ const StreamState& state = stream_map_.find(id)->second;
+ return !state.bytes_acked.Contains(0, state.bytes_sent) ||
+ state.fin_outstanding;
+}
+
+bool SimpleSessionNotifier::StreamHasBufferedData(QuicStreamId id) const {
+ if (!QuicContainsKey(stream_map_, id)) {
+ return false;
+ }
+ const StreamState& state = stream_map_.find(id)->second;
+ return state.bytes_total > state.bytes_sent ||
+ (state.fin_buffered && !state.fin_sent);
+}
+
+bool SimpleSessionNotifier::HasLostStreamData() const {
+ for (const auto& pair : stream_map_) {
+ const auto& state = pair.second;
+ if (!state.pending_retransmissions.Empty() || state.fin_lost) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace test
+
+} // namespace quic
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
new file mode 100644
index 00000000000..17616b7693f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h
@@ -0,0 +1,153 @@
+// 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_TEST_TOOLS_SIMPLE_SESSION_NOTIFIER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_NOTIFIER_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/core/session_notifier_interface.h"
+
+namespace quic {
+
+class QuicConnection;
+
+namespace test {
+
+// SimpleSessionNotifier implements the basic functionalities of a session, and
+// it manages stream data and control frames.
+class SimpleSessionNotifier : public SessionNotifierInterface {
+ public:
+ explicit SimpleSessionNotifier(QuicConnection* connection);
+ ~SimpleSessionNotifier() override;
+
+ // Tries to write stream data and returns data consumed.
+ QuicConsumedData WriteOrBufferData(QuicStreamId id,
+ QuicByteCount data_length,
+ StreamSendingState state);
+
+ // Tries to write RST_STREAM_FRAME.
+ void WriteOrBufferRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
+ // Tries to write PING.
+ void WriteOrBufferPing();
+
+ // Tries to write CRYPTO data and returns the number of bytes written.
+ size_t WriteCryptoData(EncryptionLevel level,
+ QuicByteCount data_length,
+ QuicStreamOffset offset);
+
+ // Neuters unencrypted data of crypto stream.
+ void NeuterUnencryptedData();
+
+ // Called when connection_ becomes writable.
+ void OnCanWrite();
+
+ // Returns true if there are 1) unsent control frames and stream data, or 2)
+ // lost control frames and stream data.
+ bool WillingToWrite() const;
+
+ // Number of sent stream bytes. Please note, this does not count
+ // retransmissions.
+ QuicByteCount StreamBytesSent() const;
+
+ // Number of stream bytes waiting to be sent for the first time.
+ QuicByteCount StreamBytesToSend() const;
+
+ // Returns true if there is any stream data waiting to be sent for the first
+ // time.
+ bool HasBufferedStreamData() const;
+
+ // Returns true if stream |id| has any outstanding data.
+ bool StreamIsWaitingForAcks(QuicStreamId id) const;
+
+ // SessionNotifierInterface methods:
+ bool OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) override;
+ void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override {}
+ void OnFrameLost(const QuicFrame& frame) override;
+ void RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) override;
+ bool IsFrameOutstanding(const QuicFrame& frame) const override;
+ bool HasUnackedCryptoData() const override;
+
+ private:
+ struct StreamState {
+ StreamState();
+ ~StreamState();
+
+ // Total number of bytes.
+ QuicByteCount bytes_total;
+ // Number of sent bytes.
+ QuicByteCount bytes_sent;
+ // Record of acked offsets.
+ QuicIntervalSet<QuicStreamOffset> bytes_acked;
+ // Data considered as lost and needs to be retransmitted.
+ QuicIntervalSet<QuicStreamOffset> pending_retransmissions;
+
+ bool fin_buffered;
+ bool fin_sent;
+ bool fin_outstanding;
+ bool fin_lost;
+ };
+
+ friend std::ostream& operator<<(std::ostream& os, const StreamState& s);
+
+ using StreamMap = QuicUnorderedMap<QuicStreamId, StreamState>;
+
+ void OnStreamDataConsumed(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin);
+
+ bool OnControlFrameAcked(const QuicFrame& frame);
+
+ void OnControlFrameLost(const QuicFrame& frame);
+
+ bool RetransmitLostControlFrames();
+
+ bool RetransmitLostCryptoData();
+
+ bool RetransmitLostStreamData();
+
+ bool WriteBufferedControlFrames();
+
+ bool IsControlFrameOutstanding(const QuicFrame& frame) const;
+
+ bool HasBufferedControlFrames() const;
+
+ bool HasLostStreamData() const;
+
+ bool StreamHasBufferedData(QuicStreamId id) const;
+
+ QuicDeque<QuicFrame> control_frames_;
+
+ QuicLinkedHashMap<QuicControlFrameId, bool> lost_control_frames_;
+
+ // Id of latest saved control frame. 0 if no control frame has been saved.
+ QuicControlFrameId last_control_frame_id_;
+
+ // The control frame at the 0th index of control_frames_.
+ QuicControlFrameId least_unacked_;
+
+ // ID of the least unsent control frame.
+ QuicControlFrameId least_unsent_;
+
+ StreamMap stream_map_;
+
+ // Transferred crypto bytes according to encryption levels.
+ QuicIntervalSet<QuicStreamOffset>
+ crypto_bytes_transferred_[NUM_ENCRYPTION_LEVELS];
+
+ StreamState crypto_state_[NUM_ENCRYPTION_LEVELS];
+
+ QuicConnection* connection_;
+};
+
+} // namespace test
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMPLE_SESSION_NOTIFIER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc
new file mode 100644
index 00000000000..53712fd5395
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc
@@ -0,0 +1,271 @@
+// 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 "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+using testing::_;
+using testing::InSequence;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockQuicConnectionWithSendStreamData : public MockQuicConnection {
+ public:
+ MockQuicConnectionWithSendStreamData(MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective)
+ : MockQuicConnection(helper, alarm_factory, perspective) {}
+
+ MOCK_METHOD4(SendStreamData,
+ QuicConsumedData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+};
+
+class SimpleSessionNotifierTest : public QuicTest {
+ public:
+ SimpleSessionNotifierTest()
+ : connection_(&helper_, &alarm_factory_, Perspective::IS_CLIENT),
+ notifier_(&connection_) {
+ connection_.set_visitor(&visitor_);
+ QuicConnectionPeer::SetSessionDecidesWhatToWrite(&connection_);
+ EXPECT_FALSE(notifier_.WillingToWrite());
+ EXPECT_EQ(0u, notifier_.StreamBytesSent());
+ EXPECT_FALSE(notifier_.HasBufferedStreamData());
+ }
+
+ bool ControlFrameConsumed(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnectionVisitor visitor_;
+ StrictMock<MockQuicConnectionWithSendStreamData> connection_;
+ SimpleSessionNotifier notifier_;
+};
+
+TEST_F(SimpleSessionNotifierTest, WriteOrBufferData) {
+ InSequence s;
+ EXPECT_CALL(connection_, SendStreamData(3, 1024, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(3, 1024, NO_FIN);
+ EXPECT_EQ(0u, notifier_.StreamBytesToSend());
+ EXPECT_CALL(connection_, SendStreamData(5, 512, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(512, false)));
+ notifier_.WriteOrBufferData(5, 512, NO_FIN);
+ EXPECT_FALSE(notifier_.WillingToWrite());
+ // Connection is blocked.
+ EXPECT_CALL(connection_, SendStreamData(5, 512, 512, FIN))
+ .WillOnce(Return(QuicConsumedData(256, false)));
+ notifier_.WriteOrBufferData(5, 512, FIN);
+ EXPECT_TRUE(notifier_.WillingToWrite());
+ EXPECT_EQ(1792u, notifier_.StreamBytesSent());
+ EXPECT_EQ(256u, notifier_.StreamBytesToSend());
+ EXPECT_TRUE(notifier_.HasBufferedStreamData());
+
+ // New data cannot be sent as connection is blocked.
+ EXPECT_CALL(connection_, SendStreamData(7, 1024, 0, FIN)).Times(0);
+ notifier_.WriteOrBufferData(7, 1024, FIN);
+ EXPECT_EQ(1792u, notifier_.StreamBytesSent());
+}
+
+TEST_F(SimpleSessionNotifierTest, WriteOrBufferRstStream) {
+ InSequence s;
+ EXPECT_CALL(connection_, SendStreamData(5, 1024, 0, FIN))
+ .WillOnce(Return(QuicConsumedData(1024, true)));
+ notifier_.WriteOrBufferData(5, 1024, FIN);
+
+ // Reset stream 5 with no error.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillRepeatedly(
+ Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ notifier_.WriteOrBufferRstStream(5, QUIC_STREAM_NO_ERROR, 1024);
+ // Verify stream 5 is waiting for acks.
+ EXPECT_TRUE(notifier_.StreamIsWaitingForAcks(5));
+
+ // Reset stream 5 with error.
+ notifier_.WriteOrBufferRstStream(5, QUIC_ERROR_PROCESSING_STREAM, 1024);
+ EXPECT_FALSE(notifier_.StreamIsWaitingForAcks(5));
+}
+
+TEST_F(SimpleSessionNotifierTest, WriteOrBufferPing) {
+ InSequence s;
+ // Write ping when connection is not write blocked.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillRepeatedly(
+ Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ notifier_.WriteOrBufferPing();
+ EXPECT_EQ(0u, notifier_.StreamBytesToSend());
+ EXPECT_FALSE(notifier_.WillingToWrite());
+
+ // Write stream data and cause the connection to be write blocked.
+ EXPECT_CALL(connection_, SendStreamData(3, 1024, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(3, 1024, NO_FIN);
+ EXPECT_EQ(0u, notifier_.StreamBytesToSend());
+ EXPECT_CALL(connection_, SendStreamData(5, 512, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(256, false)));
+ notifier_.WriteOrBufferData(5, 512, NO_FIN);
+ EXPECT_TRUE(notifier_.WillingToWrite());
+
+ // Connection is blocked.
+ EXPECT_CALL(connection_, SendControlFrame(_)).Times(0);
+ notifier_.WriteOrBufferPing();
+}
+
+TEST_F(SimpleSessionNotifierTest, NeuterUnencryptedData) {
+ InSequence s;
+ // Send crypto data [0, 1024) in ENCRYPTION_INITIAL.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 1024, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
+ NO_FIN);
+ // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 1024, 1024, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
+ NO_FIN);
+ // Ack [1024, 2048).
+ QuicStreamFrame stream_frame(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false,
+ 1024, 1024);
+ notifier_.OnFrameAcked(QuicFrame(stream_frame), QuicTime::Delta::Zero());
+ EXPECT_TRUE(notifier_.StreamIsWaitingForAcks(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version())));
+ // Neuters unencrypted data.
+ notifier_.NeuterUnencryptedData();
+ EXPECT_FALSE(notifier_.StreamIsWaitingForAcks(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version())));
+}
+
+TEST_F(SimpleSessionNotifierTest, OnCanWrite) {
+ InSequence s;
+ // Send crypto data [0, 1024) in ENCRYPTION_INITIAL.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 1024, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
+ NO_FIN);
+ // Send crypto data [1024, 2048) in ENCRYPTION_ZERO_RTT.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT);
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 1024, 1024, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(1024, false)));
+ notifier_.WriteOrBufferData(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), 1024,
+ NO_FIN);
+ // Send stream 3 [0, 1024) and connection is blocked.
+ EXPECT_CALL(connection_, SendStreamData(3, 1024, 0, FIN))
+ .WillOnce(Return(QuicConsumedData(512, false)));
+ notifier_.WriteOrBufferData(3, 1024, FIN);
+ // Send stream 5 [0, 1024).
+ EXPECT_CALL(connection_, SendStreamData(5, _, _, _)).Times(0);
+ notifier_.WriteOrBufferData(5, 1024, NO_FIN);
+ // Reset stream 5 with error.
+ EXPECT_CALL(connection_, SendControlFrame(_)).Times(0);
+ notifier_.WriteOrBufferRstStream(5, QUIC_ERROR_PROCESSING_STREAM, 1024);
+
+ // Lost crypto data [500, 1500) and stream 3 [0, 512).
+ QuicStreamFrame frame1(
+ QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, 500,
+ 1000);
+ QuicStreamFrame frame2(3, false, 0, 512);
+ notifier_.OnFrameLost(QuicFrame(frame1));
+ notifier_.OnFrameLost(QuicFrame(frame2));
+
+ // Connection becomes writable.
+ // Lost crypto data gets retransmitted as [500, 1024) and [1024, 1500), as
+ // they are in different encryption levels.
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 524, 500, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(524, false)));
+ EXPECT_CALL(connection_, SendStreamData(QuicUtils::GetCryptoStreamId(
+ connection_.transport_version()),
+ 476, 1024, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(476, false)));
+ // Lost stream 3 data gets retransmitted.
+ EXPECT_CALL(connection_, SendStreamData(3, 512, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(512, false)));
+ // Buffered control frames get sent.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ // Buffered stream 3 data [512, 1024) gets sent.
+ EXPECT_CALL(connection_, SendStreamData(3, 512, 512, FIN))
+ .WillOnce(Return(QuicConsumedData(512, true)));
+ notifier_.OnCanWrite();
+ EXPECT_FALSE(notifier_.WillingToWrite());
+}
+
+TEST_F(SimpleSessionNotifierTest, RetransmitFrames) {
+ InSequence s;
+ // Send stream 3 data [0, 10) and fin.
+ EXPECT_CALL(connection_, SendStreamData(3, 10, 0, FIN))
+ .WillOnce(Return(QuicConsumedData(10, true)));
+ notifier_.WriteOrBufferData(3, 10, FIN);
+ QuicStreamFrame frame1(3, true, 0, 10);
+ // Send stream 5 [0, 10) and fin.
+ EXPECT_CALL(connection_, SendStreamData(5, 10, 0, FIN))
+ .WillOnce(Return(QuicConsumedData(10, true)));
+ notifier_.WriteOrBufferData(5, 10, FIN);
+ QuicStreamFrame frame2(5, true, 0, 10);
+ // Reset stream 5 with no error.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ notifier_.WriteOrBufferRstStream(5, QUIC_STREAM_NO_ERROR, 10);
+
+ // Ack stream 3 [3, 7), and stream 5 [8, 10).
+ QuicStreamFrame ack_frame1(3, false, 3, 4);
+ QuicStreamFrame ack_frame2(5, false, 8, 2);
+ notifier_.OnFrameAcked(QuicFrame(ack_frame1), QuicTime::Delta::Zero());
+ notifier_.OnFrameAcked(QuicFrame(ack_frame2), QuicTime::Delta::Zero());
+ EXPECT_FALSE(notifier_.WillingToWrite());
+
+ // Force to send.
+ QuicRstStreamFrame rst_stream(1, 5, QUIC_STREAM_NO_ERROR, 10);
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame2));
+ frames.push_back(QuicFrame(&rst_stream));
+ frames.push_back(QuicFrame(frame1));
+ // stream 5 data [0, 8), fin only are retransmitted.
+ EXPECT_CALL(connection_, SendStreamData(5, 8, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(8, false)));
+ EXPECT_CALL(connection_, SendStreamData(5, 0, 10, FIN))
+ .WillOnce(Return(QuicConsumedData(0, true)));
+ // rst_stream is retransmitted.
+ EXPECT_CALL(connection_, SendControlFrame(_))
+ .WillOnce(Invoke(this, &SimpleSessionNotifierTest::ControlFrameConsumed));
+ // stream 3 data [0, 3) is retransmitted and connection is blocked.
+ EXPECT_CALL(connection_, SendStreamData(3, 3, 0, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(2, false)));
+ notifier_.RetransmitFrames(frames, RTO_RETRANSMISSION);
+ EXPECT_FALSE(notifier_.WillingToWrite());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/actor.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/actor.cc
new file mode 100644
index 00000000000..578b894aff5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/actor.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+Actor::Actor(Simulator* simulator, std::string name)
+ : simulator_(simulator),
+ clock_(simulator->GetClock()),
+ name_(std::move(name)) {
+ simulator_->AddActor(this);
+}
+
+Actor::~Actor() {
+ simulator_->RemoveActor(this);
+}
+
+void Actor::Schedule(QuicTime next_tick) {
+ simulator_->Schedule(this, next_tick);
+}
+
+void Actor::Unschedule() {
+ simulator_->Unschedule(this);
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/actor.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/actor.h
new file mode 100644
index 00000000000..9e06a7ea0b0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/actor.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ACTOR_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ACTOR_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+
+namespace quic {
+namespace simulator {
+
+class Simulator;
+
+// Actor is the base class for all participants of the simulation which can
+// schedule events to be triggered at the specified time. Every actor has a
+// name assigned to it, which can be used for debugging and addressing purposes.
+//
+// The Actor object is scheduled as follows:
+// 1. Every Actor object appears at most once in the event queue, for one
+// specific time.
+// 2. Actor is scheduled by calling Schedule() method.
+// 3. If Schedule() method is called with multiple different times specified,
+// Act() method will be called at the earliest time specified.
+// 4. Before Act() is called, the Actor is removed from the event queue. Act()
+// will not be called again unless Schedule() is called.
+class Actor {
+ public:
+ Actor(Simulator* simulator, std::string name);
+ virtual ~Actor();
+
+ // Trigger all the events the actor can potentially handle at this point.
+ // Before Act() is called, the actor is removed from the event queue, and has
+ // to schedule the next call manually.
+ virtual void Act() = 0;
+
+ inline std::string name() const { return name_; }
+ inline Simulator* simulator() const { return simulator_; }
+
+ protected:
+ // Calls Schedule() on the associated simulator.
+ void Schedule(QuicTime next_tick);
+
+ // Calls Unschedule() on the associated simulator.
+ void Unschedule();
+
+ Simulator* simulator_;
+ const QuicClock* clock_;
+ std::string name_;
+
+ private:
+ // Since the Actor object registers itself with a simulator using a pointer to
+ // itself, do not allow it to be moved.
+ Actor(Actor&&) = delete;
+ Actor(const Actor&) = delete;
+ Actor& operator=(const Actor&) = delete;
+ Actor& operator=(Actor&&) = delete;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ACTOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.cc
new file mode 100644
index 00000000000..e748da5b0f4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+
+namespace quic {
+namespace simulator {
+
+// Alarm is an implementation of QuicAlarm which can schedule alarms in the
+// simulation timeline.
+class Alarm : public QuicAlarm {
+ public:
+ Alarm(Simulator* simulator,
+ std::string name,
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
+ : QuicAlarm(std::move(delegate)), adapter_(simulator, name, this) {}
+ ~Alarm() override {}
+
+ void SetImpl() override {
+ DCHECK(deadline().IsInitialized());
+ adapter_.Set(deadline());
+ }
+
+ void CancelImpl() override { adapter_.Cancel(); }
+
+ private:
+ // An adapter class triggering a QuicAlarm using a simulation time system.
+ // An adapter is required here because neither Actor nor QuicAlarm are pure
+ // interfaces.
+ class Adapter : public Actor {
+ public:
+ Adapter(Simulator* simulator, std::string name, Alarm* parent)
+ : Actor(simulator, name), parent_(parent) {}
+ ~Adapter() override {}
+
+ void Set(QuicTime time) { Schedule(std::max(time, clock_->Now())); }
+ void Cancel() { Unschedule(); }
+
+ void Act() override {
+ DCHECK(clock_->Now() >= parent_->deadline());
+ parent_->Fire();
+ }
+
+ private:
+ Alarm* parent_;
+ };
+ Adapter adapter_;
+};
+
+AlarmFactory::AlarmFactory(Simulator* simulator, std::string name)
+ : simulator_(simulator), name_(std::move(name)), counter_(0) {}
+
+AlarmFactory::~AlarmFactory() {}
+
+std::string AlarmFactory::GetNewAlarmName() {
+ ++counter_;
+ return QuicStringPrintf("%s (alarm %i)", name_.c_str(), counter_);
+}
+
+QuicAlarm* AlarmFactory::CreateAlarm(QuicAlarm::Delegate* delegate) {
+ return new Alarm(simulator_, GetNewAlarmName(),
+ QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate));
+}
+
+QuicArenaScopedPtr<QuicAlarm> AlarmFactory::CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) {
+ if (arena != nullptr) {
+ return arena->New<Alarm>(simulator_, GetNewAlarmName(),
+ std::move(delegate));
+ }
+ return QuicArenaScopedPtr<QuicAlarm>(
+ new Alarm(simulator_, GetNewAlarmName(), std::move(delegate)));
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h
new file mode 100644
index 00000000000..d765175e18b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ALARM_FACTORY_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ALARM_FACTORY_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+
+namespace quic {
+namespace simulator {
+
+// AlarmFactory allows to schedule QuicAlarms using the simulation event queue.
+class AlarmFactory : public QuicAlarmFactory {
+ public:
+ AlarmFactory(Simulator* simulator, std::string name);
+ AlarmFactory(const AlarmFactory&) = delete;
+ AlarmFactory& operator=(const AlarmFactory&) = delete;
+ ~AlarmFactory() override;
+
+ QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) override;
+ QuicArenaScopedPtr<QuicAlarm> CreateAlarm(
+ QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
+ QuicConnectionArena* arena) override;
+
+ private:
+ // Automatically generate a name for a new alarm.
+ std::string GetNewAlarmName();
+
+ Simulator* simulator_;
+ std::string name_;
+ int counter_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_ALARM_FACTORY_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/link.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/link.cc
new file mode 100644
index 00000000000..a016b89f937
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/link.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+// Parameters for random noise delay.
+const uint64_t kMaxRandomDelayUs = 10;
+
+OneWayLink::OneWayLink(Simulator* simulator,
+ std::string name,
+ UnconstrainedPortInterface* sink,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay)
+ : Actor(simulator, name),
+ sink_(sink),
+ bandwidth_(bandwidth),
+ propagation_delay_(propagation_delay),
+ next_write_at_(QuicTime::Zero()) {}
+
+OneWayLink::~OneWayLink() {}
+
+OneWayLink::QueuedPacket::QueuedPacket(std::unique_ptr<Packet> packet,
+ QuicTime dequeue_time)
+ : packet(std::move(packet)), dequeue_time(dequeue_time) {}
+
+OneWayLink::QueuedPacket::QueuedPacket(QueuedPacket&& other) = default;
+
+OneWayLink::QueuedPacket::~QueuedPacket() {}
+
+void OneWayLink::AcceptPacket(std::unique_ptr<Packet> packet) {
+ DCHECK(TimeUntilAvailable().IsZero());
+ QuicTime::Delta transfer_time = bandwidth_.TransferTime(packet->size);
+ next_write_at_ = clock_->Now() + transfer_time;
+
+ packets_in_transit_.emplace(
+ std::move(packet),
+ next_write_at_ + propagation_delay_ + GetRandomDelay(transfer_time));
+ ScheduleNextPacketDeparture();
+}
+
+QuicTime::Delta OneWayLink::TimeUntilAvailable() {
+ const QuicTime now = clock_->Now();
+ if (next_write_at_ <= now) {
+ return QuicTime::Delta::Zero();
+ }
+
+ return next_write_at_ - now;
+}
+
+void OneWayLink::Act() {
+ DCHECK(!packets_in_transit_.empty());
+ DCHECK(packets_in_transit_.front().dequeue_time >= clock_->Now());
+
+ sink_->AcceptPacket(std::move(packets_in_transit_.front().packet));
+ packets_in_transit_.pop();
+
+ ScheduleNextPacketDeparture();
+}
+
+void OneWayLink::ScheduleNextPacketDeparture() {
+ if (packets_in_transit_.empty()) {
+ return;
+ }
+
+ Schedule(packets_in_transit_.front().dequeue_time);
+}
+
+QuicTime::Delta OneWayLink::GetRandomDelay(QuicTime::Delta transfer_time) {
+ if (!simulator_->enable_random_delays()) {
+ return QuicTime::Delta::Zero();
+ }
+
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(
+ simulator_->GetRandomGenerator()->RandUint64() % (kMaxRandomDelayUs + 1));
+ // Have an upper bound on the delay to ensure packets do not go out of order.
+ delta = std::min(delta, transfer_time * 0.5);
+ return delta;
+}
+
+SymmetricLink::SymmetricLink(Simulator* simulator,
+ std::string name,
+ UnconstrainedPortInterface* sink_a,
+ UnconstrainedPortInterface* sink_b,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay)
+ : a_to_b_link_(simulator,
+ QuicStringPrintf("%s (A-to-B)", name.c_str()),
+ sink_b,
+ bandwidth,
+ propagation_delay),
+ b_to_a_link_(simulator,
+ QuicStringPrintf("%s (B-to-A)", name.c_str()),
+ sink_a,
+ bandwidth,
+ propagation_delay) {}
+
+SymmetricLink::SymmetricLink(Endpoint* endpoint_a,
+ Endpoint* endpoint_b,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay)
+ : SymmetricLink(endpoint_a->simulator(),
+ QuicStringPrintf("Link [%s]<->[%s]",
+ endpoint_a->name().c_str(),
+ endpoint_b->name().c_str()),
+ endpoint_a->GetRxPort(),
+ endpoint_b->GetRxPort(),
+ bandwidth,
+ propagation_delay) {
+ endpoint_a->SetTxPort(&a_to_b_link_);
+ endpoint_b->SetTxPort(&b_to_a_link_);
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/link.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/link.h
new file mode 100644
index 00000000000..103afa0998c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/link.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_LINK_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_LINK_H_
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+
+namespace quic {
+namespace simulator {
+
+// A reliable simplex link between two endpoints with constrained bandwidth. A
+// few microseconds of random delay are added for every packet to avoid
+// synchronization issues.
+class OneWayLink : public Actor, public ConstrainedPortInterface {
+ public:
+ OneWayLink(Simulator* simulator,
+ std::string name,
+ UnconstrainedPortInterface* sink,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay);
+ OneWayLink(const OneWayLink&) = delete;
+ OneWayLink& operator=(const OneWayLink&) = delete;
+ ~OneWayLink() override;
+
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+ QuicTime::Delta TimeUntilAvailable() override;
+ void Act() override;
+
+ inline QuicBandwidth bandwidth() const { return bandwidth_; }
+
+ private:
+ struct QueuedPacket {
+ std::unique_ptr<Packet> packet;
+ QuicTime dequeue_time;
+
+ QueuedPacket(std::unique_ptr<Packet> packet, QuicTime dequeue_time);
+ QueuedPacket(QueuedPacket&& other);
+ ~QueuedPacket();
+ };
+
+ // Schedule the next packet to be egressed out of the link if there are
+ // packets on the link.
+ void ScheduleNextPacketDeparture();
+
+ // Get the value of a random delay imposed on each packet in order to avoid
+ // artifical synchronization artifacts during the simulation.
+ QuicTime::Delta GetRandomDelay(QuicTime::Delta transfer_time);
+
+ UnconstrainedPortInterface* sink_;
+ QuicQueue<QueuedPacket> packets_in_transit_;
+
+ const QuicBandwidth bandwidth_;
+ const QuicTime::Delta propagation_delay_;
+
+ QuicTime next_write_at_;
+};
+
+// A full-duplex link between two endpoints, functionally equivalent to two
+// OneWayLink objects tied together.
+class SymmetricLink {
+ public:
+ SymmetricLink(Simulator* simulator,
+ std::string name,
+ UnconstrainedPortInterface* sink_a,
+ UnconstrainedPortInterface* sink_b,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay);
+ SymmetricLink(Endpoint* endpoint_a,
+ Endpoint* endpoint_b,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta propagation_delay);
+ SymmetricLink(const SymmetricLink&) = delete;
+ SymmetricLink& operator=(const SymmetricLink&) = delete;
+
+ inline QuicBandwidth bandwidth() { return a_to_b_link_.bandwidth(); }
+
+ private:
+ OneWayLink a_to_b_link_;
+ OneWayLink b_to_a_link_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_LINK_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.cc
new file mode 100644
index 00000000000..ad039039165
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+
+namespace quic {
+namespace simulator {
+
+PacketFilter::PacketFilter(Simulator* simulator,
+ std::string name,
+ Endpoint* input)
+ : Endpoint(simulator, name), input_(input) {
+ input_->SetTxPort(this);
+}
+
+PacketFilter::~PacketFilter() {}
+
+void PacketFilter::AcceptPacket(std::unique_ptr<Packet> packet) {
+ if (FilterPacket(*packet)) {
+ output_tx_port_->AcceptPacket(std::move(packet));
+ }
+}
+
+QuicTime::Delta PacketFilter::TimeUntilAvailable() {
+ return output_tx_port_->TimeUntilAvailable();
+}
+
+void PacketFilter::Act() {}
+
+UnconstrainedPortInterface* PacketFilter::GetRxPort() {
+ return input_->GetRxPort();
+}
+
+void PacketFilter::SetTxPort(ConstrainedPortInterface* port) {
+ output_tx_port_ = port;
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h
new file mode 100644
index 00000000000..9873bec960b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h
@@ -0,0 +1,75 @@
+// 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 QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+
+namespace quic {
+namespace simulator {
+
+// Packet filter allows subclasses to filter out the packets that enter the
+// input port and exit the output port. Packets in the other direction are
+// always passed through.
+//
+// The filter wraps around the input endpoint, and exposes the resulting
+// filtered endpoint via the output() method. For example, if initially there
+// are two endpoints, A and B, connected via a symmetric link:
+//
+// QuicEndpoint endpoint_a;
+// QuicEndpoint endpoint_b;
+//
+// [...]
+//
+// SymmetricLink a_b_link(&endpoint_a, &endpoint_b, ...);
+//
+// and the goal is to filter the traffic from A to B, then the new invocation
+// would be as follows:
+//
+// PacketFilter filter(&simulator, "A-to-B packet filter", endpoint_a);
+// SymmetricLink a_b_link(&filter, &endpoint_b, ...);
+//
+// Note that the filter drops the packet instanteneously, without it ever
+// reaching the output wire. This means that in a direct endpoint-to-endpoint
+// scenario, whenever the packet is dropped, the link would become immediately
+// available for the next packet.
+class PacketFilter : public Endpoint, public ConstrainedPortInterface {
+ public:
+ // Initialize the filter by wrapping around |input|. Does not take the
+ // ownership of |input|.
+ PacketFilter(Simulator* simulator, std::string name, Endpoint* input);
+ PacketFilter(const PacketFilter&) = delete;
+ PacketFilter& operator=(const PacketFilter&) = delete;
+ ~PacketFilter() override;
+
+ // Implementation of ConstrainedPortInterface.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+ QuicTime::Delta TimeUntilAvailable() override;
+
+ // Implementation of Endpoint interface methods.
+ UnconstrainedPortInterface* GetRxPort() override;
+ void SetTxPort(ConstrainedPortInterface* port) override;
+
+ // Implementation of Actor interface methods.
+ void Act() override;
+
+ protected:
+ // Returns true if the packet should be passed through, and false if it should
+ // be dropped. The function is called once per packet, in the order that the
+ // packets arrive, so it is safe for the function to alter the internal state
+ // of the filter.
+ virtual bool FilterPacket(const Packet& packet) = 0;
+
+ private:
+ // The port onto which the filtered packets are egressed.
+ ConstrainedPortInterface* output_tx_port_;
+
+ // The original network endpoint wrapped by the class.
+ Endpoint* input_;
+};
+
+} // namespace simulator
+} // namespace quic
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PACKET_FILTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/port.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/port.cc
new file mode 100644
index 00000000000..242ebd489b2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/port.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+
+namespace quic {
+namespace simulator {
+
+Packet::Packet()
+ : source(), destination(), tx_timestamp(QuicTime::Zero()), size(0) {}
+
+Packet::~Packet() {}
+
+Packet::Packet(const Packet& packet) = default;
+
+Endpoint::Endpoint(Simulator* simulator, std::string name)
+ : Actor(simulator, name) {}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/port.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/port.h
new file mode 100644
index 00000000000..15da6384f72
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/port.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PORT_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PORT_H_
+
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+
+namespace quic {
+namespace simulator {
+
+struct Packet {
+ Packet();
+ ~Packet();
+ Packet(const Packet& packet);
+
+ std::string source;
+ std::string destination;
+ QuicTime tx_timestamp;
+
+ std::string contents;
+ QuicByteCount size;
+};
+
+// An interface for anything that accepts packets at arbitrary rate.
+class UnconstrainedPortInterface {
+ public:
+ virtual ~UnconstrainedPortInterface() {}
+ virtual void AcceptPacket(std::unique_ptr<Packet> packet) = 0;
+};
+
+// An interface for any device that accepts packets at a specific rate.
+// Typically one would use a Queue object in order to write into a constrained
+// port.
+class ConstrainedPortInterface {
+ public:
+ virtual ~ConstrainedPortInterface() {}
+
+ // Accept a packet for a port. TimeUntilAvailable() must be zero before this
+ // method is called.
+ virtual void AcceptPacket(std::unique_ptr<Packet> packet) = 0;
+
+ // Time until write for the next port is available. Cannot be infinite.
+ virtual QuicTime::Delta TimeUntilAvailable() = 0;
+};
+
+// A convenience class for any network endpoints, i.e. the objects which can
+// both accept and send packets.
+class Endpoint : public Actor {
+ public:
+ virtual UnconstrainedPortInterface* GetRxPort() = 0;
+ virtual void SetTxPort(ConstrainedPortInterface* port) = 0;
+
+ protected:
+ Endpoint(Simulator* simulator, std::string name);
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_PORT_H_
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
new file mode 100644
index 00000000000..3816fd4b0c0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+Queue::ListenerInterface::~ListenerInterface() {}
+
+Queue::Queue(Simulator* simulator, std::string name, QuicByteCount capacity)
+ : Actor(simulator, name),
+ capacity_(capacity),
+ bytes_queued_(0),
+ aggregation_threshold_(0),
+ aggregation_timeout_(QuicTime::Delta::Infinite()),
+ current_bundle_(0),
+ current_bundle_bytes_(0),
+ listener_(nullptr) {
+ aggregation_timeout_alarm_.reset(simulator_->GetAlarmFactory()->CreateAlarm(
+ new AggregationAlarmDelegate(this)));
+}
+
+Queue::~Queue() {}
+
+void Queue::set_tx_port(ConstrainedPortInterface* port) {
+ tx_port_ = port;
+}
+
+void Queue::AcceptPacket(std::unique_ptr<Packet> packet) {
+ if (packet->size + bytes_queued_ > capacity_) {
+ QUIC_DVLOG(1) << "Queue [" << name() << "] has received a packet from ["
+ << packet->source << "] to [" << packet->destination
+ << "] which is over capacity. Dropping it.";
+ QUIC_DVLOG(1) << "Queue size: " << bytes_queued_ << " out of " << capacity_
+ << ". Packet size: " << packet->size;
+ return;
+ }
+
+ bytes_queued_ += packet->size;
+ queue_.emplace(std::move(packet), current_bundle_);
+
+ if (IsAggregationEnabled()) {
+ current_bundle_bytes_ += queue_.front().packet->size;
+ if (!aggregation_timeout_alarm_->IsSet()) {
+ aggregation_timeout_alarm_->Set(clock_->Now() + aggregation_timeout_);
+ }
+ if (current_bundle_bytes_ >= aggregation_threshold_) {
+ NextBundle();
+ }
+ }
+
+ ScheduleNextPacketDequeue();
+}
+
+void Queue::Act() {
+ DCHECK(!queue_.empty());
+ if (tx_port_->TimeUntilAvailable().IsZero()) {
+ DCHECK(bytes_queued_ >= queue_.front().packet->size);
+ bytes_queued_ -= queue_.front().packet->size;
+
+ tx_port_->AcceptPacket(std::move(queue_.front().packet));
+ queue_.pop();
+ if (listener_ != nullptr) {
+ listener_->OnPacketDequeued();
+ }
+ }
+
+ ScheduleNextPacketDequeue();
+}
+
+void Queue::EnableAggregation(QuicByteCount aggregation_threshold,
+ QuicTime::Delta aggregation_timeout) {
+ DCHECK_EQ(bytes_queued_, 0u);
+ DCHECK_GT(aggregation_threshold, 0u);
+ DCHECK(!aggregation_timeout.IsZero());
+ DCHECK(!aggregation_timeout.IsInfinite());
+
+ aggregation_threshold_ = aggregation_threshold;
+ aggregation_timeout_ = aggregation_timeout;
+}
+
+Queue::AggregationAlarmDelegate::AggregationAlarmDelegate(Queue* queue)
+ : queue_(queue) {}
+
+void Queue::AggregationAlarmDelegate::OnAlarm() {
+ queue_->NextBundle();
+ queue_->ScheduleNextPacketDequeue();
+}
+
+Queue::EnqueuedPacket::EnqueuedPacket(std::unique_ptr<Packet> packet,
+ AggregationBundleNumber bundle)
+ : packet(std::move(packet)), bundle(bundle) {}
+
+Queue::EnqueuedPacket::EnqueuedPacket(EnqueuedPacket&& other) = default;
+
+Queue::EnqueuedPacket::~EnqueuedPacket() = default;
+
+void Queue::NextBundle() {
+ current_bundle_++;
+ current_bundle_bytes_ = 0;
+ aggregation_timeout_alarm_->Cancel();
+}
+
+void Queue::ScheduleNextPacketDequeue() {
+ if (queue_.empty()) {
+ DCHECK_EQ(bytes_queued_, 0u);
+ return;
+ }
+
+ if (IsAggregationEnabled() && queue_.front().bundle == current_bundle_) {
+ return;
+ }
+
+ Schedule(clock_->Now() + tx_port_->TimeUntilAvailable());
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.h
new file mode 100644
index 00000000000..6c3c6b0ef17
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/queue.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUEUE_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUEUE_H_
+
+#include "net/third_party/quiche/src/quic/core/quic_alarm.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+
+namespace quic {
+namespace simulator {
+
+// A finitely sized queue which egresses packets onto a constrained link. The
+// capacity of the queue is measured in bytes as opposed to packets.
+class Queue : public Actor, public UnconstrainedPortInterface {
+ public:
+ class ListenerInterface {
+ public:
+ virtual ~ListenerInterface();
+
+ // Called whenever a packet is removed from the queue.
+ virtual void OnPacketDequeued() = 0;
+ };
+
+ Queue(Simulator* simulator, std::string name, QuicByteCount capacity);
+ Queue(const Queue&) = delete;
+ Queue& operator=(const Queue&) = delete;
+ ~Queue() override;
+
+ void set_tx_port(ConstrainedPortInterface* port);
+
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+
+ void Act() override;
+
+ inline QuicByteCount capacity() const { return capacity_; }
+ inline QuicByteCount bytes_queued() const { return bytes_queued_; }
+ inline QuicPacketCount packets_queued() const { return queue_.size(); }
+
+ inline void set_listener_interface(ListenerInterface* listener) {
+ listener_ = listener;
+ }
+
+ // Enables packet aggregation on the queue. Packet aggregation makes the
+ // queue bundle packets up until they reach certain size. When the
+ // aggregation is enabled, the packets are not dequeued until the total size
+ // of packets in the queue reaches |aggregation_threshold|. The packets are
+ // automatically flushed from the queue if the oldest packet has been in it
+ // for |aggregation_timeout|.
+ //
+ // This method may only be called when the queue is empty. Once enabled,
+ // aggregation cannot be disabled.
+ void EnableAggregation(QuicByteCount aggregation_threshold,
+ QuicTime::Delta aggregation_timeout);
+
+ private:
+ typedef uint64_t AggregationBundleNumber;
+
+ // In order to implement packet aggregation, each packet is tagged with a
+ // bundle number. The queue keeps a bundle counter, and whenever a bundle is
+ // ready, it increments the number of the current bundle. Only the packets
+ // outside of the current bundle are allowed to leave the queue.
+ struct EnqueuedPacket {
+ EnqueuedPacket(std::unique_ptr<Packet> packet,
+ AggregationBundleNumber bundle);
+ EnqueuedPacket(EnqueuedPacket&& other);
+ ~EnqueuedPacket();
+
+ std::unique_ptr<Packet> packet;
+ AggregationBundleNumber bundle;
+ };
+
+ // Alarm handler for aggregation timeout.
+ class AggregationAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit AggregationAlarmDelegate(Queue* queue);
+
+ void OnAlarm() override;
+
+ private:
+ Queue* queue_;
+ };
+
+ inline bool IsAggregationEnabled() const {
+ return aggregation_threshold_ > 0;
+ }
+
+ // Increment the bundle counter and reset the bundle state. This causes all
+ // packets currently in the bundle to be flushed onto the link.
+ void NextBundle();
+
+ void ScheduleNextPacketDequeue();
+
+ const QuicByteCount capacity_;
+ QuicByteCount bytes_queued_;
+
+ QuicByteCount aggregation_threshold_;
+ QuicTime::Delta aggregation_timeout_;
+ // The number of the current aggregation bundle. Monotonically increasing.
+ // All packets in the previous bundles are allowed to leave the queue, and
+ // none of the packets in the current one are.
+ AggregationBundleNumber current_bundle_;
+ // Size of the current bundle. Whenever it exceeds |aggregation_threshold_|,
+ // the next bundle is created.
+ QuicByteCount current_bundle_bytes_;
+ // Alarm responsible for flushing the current bundle upon timeout. Set when
+ // the first packet in the bundle is enqueued.
+ std::unique_ptr<QuicAlarm> aggregation_timeout_alarm_;
+
+ ConstrainedPortInterface* tx_port_;
+ QuicQueue<EnqueuedPacket> queue_;
+
+ ListenerInterface* listener_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUEUE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc
new file mode 100644
index 00000000000..9e5c3fab604
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc
@@ -0,0 +1,421 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_output.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+namespace quic {
+namespace simulator {
+
+const QuicStreamId kDataStream = 3;
+const QuicByteCount kWriteChunkSize = 128 * 1024;
+const char kStreamDataContents = 'Q';
+
+// Takes a SHA-1 hash of the name and converts it into five 32-bit integers.
+static std::vector<uint32_t> HashNameIntoFive32BitIntegers(std::string name) {
+ const std::string hash = test::Sha1Hash(name);
+
+ std::vector<uint32_t> output;
+ uint32_t current_number = 0;
+ for (size_t i = 0; i < hash.size(); i++) {
+ current_number = (current_number << 8) + hash[i];
+ if (i % 4 == 3) {
+ output.push_back(i);
+ current_number = 0;
+ }
+ }
+
+ return output;
+}
+
+QuicSocketAddress GetAddressFromName(std::string name) {
+ const std::vector<uint32_t> hash = HashNameIntoFive32BitIntegers(name);
+
+ // Generate a random port between 1025 and 65535.
+ const uint16_t port = 1025 + hash[0] % (65535 - 1025 + 1);
+
+ // Generate a random 10.x.x.x address, where x is between 1 and 254.
+ std::string ip_address{"\xa\0\0\0", 4};
+ for (size_t i = 1; i < 4; i++) {
+ ip_address[i] = 1 + hash[i] % 254;
+ }
+ QuicIpAddress host;
+ host.FromPackedString(ip_address.c_str(), ip_address.length());
+ return QuicSocketAddress(host, port);
+}
+
+QuicEndpoint::QuicEndpoint(Simulator* simulator,
+ std::string name,
+ std::string peer_name,
+ Perspective perspective,
+ QuicConnectionId connection_id)
+ : Endpoint(simulator, name),
+ peer_name_(peer_name),
+ writer_(this),
+ nic_tx_queue_(simulator,
+ QuicStringPrintf("%s (TX Queue)", name.c_str()),
+ kMaxOutgoingPacketSize * kTxQueueSize),
+ connection_(connection_id,
+ GetAddressFromName(peer_name),
+ simulator,
+ simulator->GetAlarmFactory(),
+ &writer_,
+ false,
+ perspective,
+ ParsedVersionOfIndex(CurrentSupportedVersions(), 0)),
+ bytes_to_transfer_(0),
+ bytes_transferred_(0),
+ write_blocked_count_(0),
+ wrong_data_received_(false),
+ drop_next_packet_(false),
+ notifier_(nullptr) {
+ nic_tx_queue_.set_listener_interface(this);
+
+ connection_.SetSelfAddress(GetAddressFromName(name));
+ connection_.set_visitor(this);
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullEncrypter>(perspective));
+ if (connection_.version().KnowsWhichDecrypterToUse()) {
+ connection_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullDecrypter>(perspective));
+ connection_.RemoveDecrypter(ENCRYPTION_INITIAL);
+ } else {
+ connection_.SetDecrypter(ENCRYPTION_FORWARD_SECURE,
+ QuicMakeUnique<NullDecrypter>(perspective));
+ }
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ if (perspective == Perspective::IS_SERVER) {
+ // Skip version negotiation.
+ test::QuicConnectionPeer::SetNegotiatedVersion(&connection_);
+ }
+ connection_.SetDataProducer(&producer_);
+ connection_.SetSessionNotifier(this);
+ if (connection_.session_decides_what_to_write()) {
+ notifier_ = QuicMakeUnique<test::SimpleSessionNotifier>(&connection_);
+ }
+
+ // Configure the connection as if it received a handshake. This is important
+ // primarily because
+ // - this enables pacing, and
+ // - this sets the non-handshake timeouts.
+ std::string error;
+ CryptoHandshakeMessage peer_hello;
+ peer_hello.SetValue(kICSL,
+ static_cast<uint32_t>(kMaximumIdleTimeoutSecs - 1));
+ peer_hello.SetValue(kMIDS,
+ static_cast<uint32_t>(kDefaultMaxStreamsPerConnection));
+ QuicConfig config;
+ QuicErrorCode error_code = config.ProcessPeerHello(
+ peer_hello, perspective == Perspective::IS_CLIENT ? SERVER : CLIENT,
+ &error);
+ DCHECK_EQ(error_code, QUIC_NO_ERROR) << "Configuration failed: " << error;
+ connection_.SetFromConfig(config);
+}
+
+QuicEndpoint::~QuicEndpoint() {
+ if (trace_visitor_ != nullptr) {
+ const char* perspective_prefix =
+ connection_.perspective() == Perspective::IS_CLIENT ? "C" : "S";
+
+ std::string identifier =
+ QuicStrCat(perspective_prefix, connection_.connection_id().ToString());
+ QuicRecordTestOutput(identifier,
+ trace_visitor_->trace()->SerializeAsString());
+ }
+}
+
+QuicByteCount QuicEndpoint::bytes_received() const {
+ QuicByteCount total = 0;
+ for (auto& interval : offsets_received_) {
+ total += interval.max() - interval.min();
+ }
+ return total;
+}
+
+QuicByteCount QuicEndpoint::bytes_to_transfer() const {
+ if (notifier_ != nullptr) {
+ return notifier_->StreamBytesToSend();
+ }
+ return bytes_to_transfer_;
+}
+
+QuicByteCount QuicEndpoint::bytes_transferred() const {
+ if (notifier_ != nullptr) {
+ return notifier_->StreamBytesSent();
+ }
+ return bytes_transferred_;
+}
+
+void QuicEndpoint::AddBytesToTransfer(QuicByteCount bytes) {
+ if (notifier_ != nullptr) {
+ if (notifier_->HasBufferedStreamData()) {
+ Schedule(clock_->Now());
+ }
+ notifier_->WriteOrBufferData(kDataStream, bytes, NO_FIN);
+ return;
+ }
+
+ if (bytes_to_transfer_ > 0) {
+ Schedule(clock_->Now());
+ }
+
+ bytes_to_transfer_ += bytes;
+ WriteStreamData();
+}
+
+void QuicEndpoint::DropNextIncomingPacket() {
+ drop_next_packet_ = true;
+}
+
+void QuicEndpoint::RecordTrace() {
+ trace_visitor_ = QuicMakeUnique<QuicTraceVisitor>(&connection_);
+ connection_.set_debug_visitor(trace_visitor_.get());
+}
+
+void QuicEndpoint::AcceptPacket(std::unique_ptr<Packet> packet) {
+ if (packet->destination != name_) {
+ return;
+ }
+ if (drop_next_packet_) {
+ drop_next_packet_ = false;
+ return;
+ }
+
+ QuicReceivedPacket received_packet(packet->contents.data(),
+ packet->contents.size(), clock_->Now());
+ connection_.ProcessUdpPacket(connection_.self_address(),
+ connection_.peer_address(), received_packet);
+}
+
+UnconstrainedPortInterface* QuicEndpoint::GetRxPort() {
+ return this;
+}
+
+void QuicEndpoint::SetTxPort(ConstrainedPortInterface* port) {
+ // Any egress done by the endpoint is actually handled by a queue on an NIC.
+ nic_tx_queue_.set_tx_port(port);
+}
+
+void QuicEndpoint::OnPacketDequeued() {
+ if (writer_.IsWriteBlocked() &&
+ (nic_tx_queue_.capacity() - nic_tx_queue_.bytes_queued()) >=
+ kMaxOutgoingPacketSize) {
+ writer_.SetWritable();
+ connection_.OnCanWrite();
+ }
+}
+
+void QuicEndpoint::OnStreamFrame(const QuicStreamFrame& frame) {
+ // Verify that the data received always matches the expected.
+ DCHECK(frame.stream_id == kDataStream);
+ for (size_t i = 0; i < frame.data_length; i++) {
+ if (frame.data_buffer[i] != kStreamDataContents) {
+ wrong_data_received_ = true;
+ }
+ }
+ offsets_received_.Add(frame.offset, frame.offset + frame.data_length);
+ // Sanity check against very pathological connections.
+ DCHECK_LE(offsets_received_.Size(), 1000u);
+}
+
+void QuicEndpoint::OnCryptoFrame(const QuicCryptoFrame& frame) {}
+
+void QuicEndpoint::OnCanWrite() {
+ if (notifier_ != nullptr) {
+ notifier_->OnCanWrite();
+ return;
+ }
+ WriteStreamData();
+}
+bool QuicEndpoint::WillingAndAbleToWrite() const {
+ if (notifier_ != nullptr) {
+ return notifier_->WillingToWrite();
+ }
+ return bytes_to_transfer_ != 0;
+}
+bool QuicEndpoint::HasPendingHandshake() const {
+ return false;
+}
+bool QuicEndpoint::ShouldKeepConnectionAlive() const {
+ return true;
+}
+
+bool QuicEndpoint::AllowSelfAddressChange() const {
+ return false;
+}
+
+bool QuicEndpoint::OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) {
+ if (notifier_ != nullptr) {
+ return notifier_->OnFrameAcked(frame, ack_delay_time);
+ }
+ return false;
+}
+
+void QuicEndpoint::OnFrameLost(const QuicFrame& frame) {
+ DCHECK(notifier_);
+ notifier_->OnFrameLost(frame);
+}
+
+void QuicEndpoint::RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) {
+ DCHECK(notifier_);
+ notifier_->RetransmitFrames(frames, type);
+}
+
+bool QuicEndpoint::IsFrameOutstanding(const QuicFrame& frame) const {
+ DCHECK(notifier_);
+ return notifier_->IsFrameOutstanding(frame);
+}
+
+bool QuicEndpoint::HasUnackedCryptoData() const {
+ return false;
+}
+
+QuicEndpoint::Writer::Writer(QuicEndpoint* endpoint)
+ : endpoint_(endpoint), is_blocked_(false) {}
+
+QuicEndpoint::Writer::~Writer() {}
+
+WriteResult QuicEndpoint::Writer::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) {
+ DCHECK(!IsWriteBlocked());
+ DCHECK(options == nullptr);
+ DCHECK(buf_len <= kMaxOutgoingPacketSize);
+
+ // Instead of losing a packet, become write-blocked when the egress queue is
+ // full.
+ if (endpoint_->nic_tx_queue_.packets_queued() > kTxQueueSize) {
+ is_blocked_ = true;
+ endpoint_->write_blocked_count_++;
+ return WriteResult(WRITE_STATUS_BLOCKED, 0);
+ }
+
+ auto packet = QuicMakeUnique<Packet>();
+ packet->source = endpoint_->name();
+ packet->destination = endpoint_->peer_name_;
+ packet->tx_timestamp = endpoint_->clock_->Now();
+
+ packet->contents = std::string(buffer, buf_len);
+ packet->size = buf_len;
+
+ endpoint_->nic_tx_queue_.AcceptPacket(std::move(packet));
+
+ return WriteResult(WRITE_STATUS_OK, buf_len);
+}
+
+bool QuicEndpoint::Writer::IsWriteBlocked() const {
+ return is_blocked_;
+}
+
+void QuicEndpoint::Writer::SetWritable() {
+ is_blocked_ = false;
+}
+
+QuicByteCount QuicEndpoint::Writer::GetMaxPacketSize(
+ const QuicSocketAddress& /*peer_address*/) const {
+ return kMaxOutgoingPacketSize;
+}
+
+bool QuicEndpoint::Writer::SupportsReleaseTime() const {
+ return false;
+}
+
+bool QuicEndpoint::Writer::IsBatchMode() const {
+ return false;
+}
+
+char* QuicEndpoint::Writer::GetNextWriteLocation(
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ return nullptr;
+}
+
+WriteResult QuicEndpoint::Writer::Flush() {
+ return WriteResult(WRITE_STATUS_OK, 0);
+}
+
+WriteStreamDataResult QuicEndpoint::DataProducer::WriteStreamData(
+ QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ writer->WriteRepeatedByte(kStreamDataContents, data_length);
+ return WRITE_SUCCESS;
+}
+
+bool QuicEndpoint::DataProducer::WriteCryptoData(EncryptionLevel leve,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) {
+ QUIC_BUG << "QuicEndpoint::DataProducer::WriteCryptoData is unimplemented";
+ return false;
+}
+
+void QuicEndpoint::WriteStreamData() {
+ // Instantiate a flusher which would normally be here due to QuicSession.
+ QuicConnection::ScopedPacketFlusher flusher(
+ &connection_, QuicConnection::SEND_ACK_IF_QUEUED);
+
+ while (bytes_to_transfer_ > 0) {
+ // Transfer data in chunks of size at most |kWriteChunkSize|.
+ const size_t transmission_size =
+ std::min(kWriteChunkSize, bytes_to_transfer_);
+
+ QuicConsumedData consumed_data = connection_.SendStreamData(
+ kDataStream, transmission_size, bytes_transferred_, NO_FIN);
+
+ DCHECK(consumed_data.bytes_consumed <= transmission_size);
+ bytes_transferred_ += consumed_data.bytes_consumed;
+ bytes_to_transfer_ -= consumed_data.bytes_consumed;
+ if (consumed_data.bytes_consumed != transmission_size) {
+ return;
+ }
+ }
+}
+
+QuicEndpointMultiplexer::QuicEndpointMultiplexer(
+ std::string name,
+ std::initializer_list<QuicEndpoint*> endpoints)
+ : Endpoint((*endpoints.begin())->simulator(), name) {
+ for (QuicEndpoint* endpoint : endpoints) {
+ mapping_.insert(std::make_pair(endpoint->name(), endpoint));
+ }
+}
+
+QuicEndpointMultiplexer::~QuicEndpointMultiplexer() {}
+
+void QuicEndpointMultiplexer::AcceptPacket(std::unique_ptr<Packet> packet) {
+ auto key_value_pair_it = mapping_.find(packet->destination);
+ if (key_value_pair_it == mapping_.end()) {
+ return;
+ }
+
+ key_value_pair_it->second->GetRxPort()->AcceptPacket(std::move(packet));
+}
+UnconstrainedPortInterface* QuicEndpointMultiplexer::GetRxPort() {
+ return this;
+}
+void QuicEndpointMultiplexer::SetTxPort(ConstrainedPortInterface* port) {
+ for (auto& key_value_pair : mapping_) {
+ key_value_pair.second->SetTxPort(port);
+ }
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h
new file mode 100644
index 00000000000..955ac8fcb64
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h
@@ -0,0 +1,234 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h"
+#include "net/third_party/quiche/src/quic/core/quic_trace_visitor.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/simple_session_notifier.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+
+namespace quic {
+namespace simulator {
+
+// Size of the TX queue used by the kernel/NIC. 1000 is the Linux
+// kernel default.
+const QuicByteCount kTxQueueSize = 1000;
+
+// Generate a random local network host-port tuple based on the name of the
+// endpoint.
+QuicSocketAddress GetAddressFromName(std::string name);
+
+// A QUIC connection endpoint. Wraps around QuicConnection. In order to
+// initiate a transfer, the caller has to call AddBytesToTransfer(). The data
+// transferred is always the same and is always transferred on a single stream.
+// The endpoint receives all packets addressed to it, and verifies that the data
+// received is what it's supposed to be.
+class QuicEndpoint : public Endpoint,
+ public UnconstrainedPortInterface,
+ public Queue::ListenerInterface,
+ public QuicConnectionVisitorInterface,
+ public SessionNotifierInterface {
+ public:
+ QuicEndpoint(Simulator* simulator,
+ std::string name,
+ std::string peer_name,
+ Perspective perspective,
+ QuicConnectionId connection_id);
+ ~QuicEndpoint() override;
+
+ inline QuicConnection* connection() { return &connection_; }
+ QuicByteCount bytes_to_transfer() const;
+ QuicByteCount bytes_transferred() const;
+ QuicByteCount bytes_received() const;
+ inline size_t write_blocked_count() { return write_blocked_count_; }
+ inline bool wrong_data_received() const { return wrong_data_received_; }
+
+ // Send |bytes| bytes. Initiates the transfer if one is not already in
+ // progress.
+ void AddBytesToTransfer(QuicByteCount bytes);
+
+ // Drop the next packet upon receipt.
+ void DropNextIncomingPacket();
+
+ // UnconstrainedPortInterface method. Called whenever the endpoint receives a
+ // packet.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+
+ // Enables logging of the connection trace at the end of the unit test.
+ void RecordTrace();
+
+ // Begin Endpoint implementation.
+ UnconstrainedPortInterface* GetRxPort() override;
+ void SetTxPort(ConstrainedPortInterface* port) override;
+ // End Endpoint implementation.
+
+ // Actor method.
+ void Act() override {}
+
+ // Queue::ListenerInterface method.
+ void OnPacketDequeued() override;
+
+ // Begin QuicConnectionVisitorInterface implementation.
+ void OnStreamFrame(const QuicStreamFrame& frame) override;
+ void OnCryptoFrame(const QuicCryptoFrame& frame) override;
+ void OnCanWrite() override;
+ bool WillingAndAbleToWrite() const override;
+ bool HasPendingHandshake() const override;
+ bool ShouldKeepConnectionAlive() const override;
+
+ void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {}
+ void OnBlockedFrame(const QuicBlockedFrame& frame) override {}
+ void OnRstStream(const QuicRstStreamFrame& frame) override {}
+ void OnGoAway(const QuicGoAwayFrame& frame) override {}
+ void OnMessageReceived(QuicStringPiece message) override {}
+ void OnConnectionClosed(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source) override {}
+ void OnWriteBlocked() override {}
+ void OnSuccessfulVersionNegotiation(
+ const ParsedQuicVersion& version) override {}
+ void OnConnectivityProbeReceived(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address) override {}
+ void OnCongestionWindowChange(QuicTime now) override {}
+ void OnConnectionMigration(AddressChangeType type) override {}
+ void OnPathDegrading() override {}
+ void OnAckNeedsRetransmittableFrame() override {}
+ void SendPing() override {}
+ bool AllowSelfAddressChange() const override;
+ void OnForwardProgressConfirmed() override {}
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
+ return true;
+ }
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
+ return true;
+ }
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+ return true;
+ }
+
+ // End QuicConnectionVisitorInterface implementation.
+
+ // Begin SessionNotifierInterface methods:
+ bool OnFrameAcked(const QuicFrame& frame,
+ QuicTime::Delta ack_delay_time) override;
+ void OnStreamFrameRetransmitted(const QuicStreamFrame& frame) override {}
+ void OnFrameLost(const QuicFrame& frame) override;
+ void RetransmitFrames(const QuicFrames& frames,
+ TransmissionType type) override;
+ bool IsFrameOutstanding(const QuicFrame& frame) const override;
+ bool HasUnackedCryptoData() const override;
+ // End SessionNotifierInterface implementation.
+
+ private:
+ // A Writer object that writes into the |nic_tx_queue_|.
+ class Writer : public QuicPacketWriter {
+ public:
+ explicit Writer(QuicEndpoint* endpoint);
+ ~Writer() override;
+
+ WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ PerPacketOptions* options) override;
+ bool IsWriteBlocked() const override;
+ void SetWritable() override;
+ QuicByteCount GetMaxPacketSize(
+ const QuicSocketAddress& peer_address) const override;
+ bool SupportsReleaseTime() const override;
+ bool IsBatchMode() const override;
+ char* GetNextWriteLocation(const QuicIpAddress& self_address,
+ const QuicSocketAddress& peer_address) override;
+ WriteResult Flush() override;
+
+ private:
+ QuicEndpoint* endpoint_;
+
+ bool is_blocked_;
+ };
+
+ // The producer outputs the repetition of the same byte. That sequence is
+ // verified by the receiver.
+ class DataProducer : public QuicStreamFrameDataProducer {
+ public:
+ WriteStreamDataResult WriteStreamData(QuicStreamId id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+ bool WriteCryptoData(EncryptionLevel level,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ QuicDataWriter* writer) override;
+ };
+
+ // Write stream data until |bytes_to_transfer_| is zero or the connection is
+ // write-blocked.
+ void WriteStreamData();
+
+ std::string peer_name_;
+
+ Writer writer_;
+ DataProducer producer_;
+ // The queue for the outgoing packets. In reality, this might be either on
+ // the network card, or in the kernel, but for concreteness we assume it's on
+ // the network card.
+ Queue nic_tx_queue_;
+ QuicConnection connection_;
+
+ QuicByteCount bytes_to_transfer_;
+ QuicByteCount bytes_transferred_;
+
+ // Counts the number of times the writer became write-blocked.
+ size_t write_blocked_count_;
+
+ // Set to true if the endpoint receives stream data different from what it
+ // expects.
+ bool wrong_data_received_;
+
+ // If true, drop the next packet when receiving it.
+ bool drop_next_packet_;
+
+ // Record of received offsets in the data stream.
+ QuicIntervalSet<QuicStreamOffset> offsets_received_;
+
+ std::unique_ptr<test::SimpleSessionNotifier> notifier_;
+ std::unique_ptr<QuicTraceVisitor> trace_visitor_;
+};
+
+// Multiplexes multiple connections at the same host on the network.
+class QuicEndpointMultiplexer : public Endpoint,
+ public UnconstrainedPortInterface {
+ public:
+ QuicEndpointMultiplexer(std::string name,
+ std::initializer_list<QuicEndpoint*> endpoints);
+ ~QuicEndpointMultiplexer() override;
+
+ // Receives a packet and passes it to the specified endpoint if that endpoint
+ // is one of the endpoints being multiplexed, otherwise ignores the packet.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+ UnconstrainedPortInterface* GetRxPort() override;
+
+ // Sets the egress port for all the endpoints being multiplexed.
+ void SetTxPort(ConstrainedPortInterface* port) override;
+
+ void Act() override {}
+
+ private:
+ QuicUnorderedMap<std::string, QuicEndpoint*> mapping_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_QUIC_ENDPOINT_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_test.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_test.cc
new file mode 100644
index 00000000000..5382eb56b1e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_test.cc
@@ -0,0 +1,209 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+namespace quic {
+namespace simulator {
+
+const QuicBandwidth kDefaultBandwidth =
+ QuicBandwidth::FromKBitsPerSecond(10 * 1000);
+const QuicTime::Delta kDefaultPropagationDelay =
+ QuicTime::Delta::FromMilliseconds(20);
+const QuicByteCount kDefaultBdp = kDefaultBandwidth * kDefaultPropagationDelay;
+
+// A simple test harness where all hosts are connected to a switch with
+// identical links.
+class QuicEndpointTest : public QuicTest {
+ public:
+ QuicEndpointTest()
+ : simulator_(), switch_(&simulator_, "Switch", 8, kDefaultBdp * 2) {}
+
+ protected:
+ Simulator simulator_;
+ Switch switch_;
+
+ std::unique_ptr<SymmetricLink> Link(Endpoint* a, Endpoint* b) {
+ return QuicMakeUnique<SymmetricLink>(a, b, kDefaultBandwidth,
+ kDefaultPropagationDelay);
+ }
+
+ std::unique_ptr<SymmetricLink> CustomLink(Endpoint* a,
+ Endpoint* b,
+ uint64_t extra_rtt_ms) {
+ return QuicMakeUnique<SymmetricLink>(
+ a, b, kDefaultBandwidth,
+ kDefaultPropagationDelay +
+ QuicTime::Delta::FromMilliseconds(extra_rtt_ms));
+ }
+};
+
+// Test transmission from one host to another.
+TEST_F(QuicEndpointTest, OneWayTransmission) {
+ QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B",
+ Perspective::IS_CLIENT, test::TestConnectionId(42));
+ QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A",
+ Perspective::IS_SERVER, test::TestConnectionId(42));
+ auto link_a = Link(&endpoint_a, switch_.port(1));
+ auto link_b = Link(&endpoint_b, switch_.port(2));
+
+ // First transmit a small, packet-size chunk of data.
+ endpoint_a.AddBytesToTransfer(600);
+ QuicTime end_time =
+ simulator_.GetClock()->Now() + QuicTime::Delta::FromMilliseconds(1000);
+ simulator_.RunUntil(
+ [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; });
+
+ EXPECT_EQ(600u, endpoint_a.bytes_transferred());
+ ASSERT_EQ(600u, endpoint_b.bytes_received());
+ EXPECT_FALSE(endpoint_a.wrong_data_received());
+ EXPECT_FALSE(endpoint_b.wrong_data_received());
+
+ // After a small chunk succeeds, try to transfer 2 MiB.
+ endpoint_a.AddBytesToTransfer(2 * 1024 * 1024);
+ end_time = simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(5);
+ simulator_.RunUntil(
+ [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; });
+
+ const QuicByteCount total_bytes_transferred = 600 + 2 * 1024 * 1024;
+ EXPECT_EQ(total_bytes_transferred, endpoint_a.bytes_transferred());
+ EXPECT_EQ(total_bytes_transferred, endpoint_b.bytes_received());
+ EXPECT_EQ(0u, endpoint_a.write_blocked_count());
+ EXPECT_FALSE(endpoint_a.wrong_data_received());
+ EXPECT_FALSE(endpoint_b.wrong_data_received());
+}
+
+// Test the situation in which the writer becomes write-blocked.
+TEST_F(QuicEndpointTest, WriteBlocked) {
+ QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B",
+ Perspective::IS_CLIENT, test::TestConnectionId(42));
+ QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A",
+ Perspective::IS_SERVER, test::TestConnectionId(42));
+ auto link_a = Link(&endpoint_a, switch_.port(1));
+ auto link_b = Link(&endpoint_b, switch_.port(2));
+
+ // Will be owned by the sent packet manager.
+ auto* sender = new NiceMock<test::MockSendAlgorithm>();
+ EXPECT_CALL(*sender, CanSend(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*sender, PacingRate(_))
+ .WillRepeatedly(Return(10 * kDefaultBandwidth));
+ EXPECT_CALL(*sender, BandwidthEstimate())
+ .WillRepeatedly(Return(10 * kDefaultBandwidth));
+ EXPECT_CALL(*sender, GetCongestionWindow())
+ .WillRepeatedly(
+ Return(kMaxOutgoingPacketSize * kDefaultMaxCongestionWindowPackets));
+ test::QuicConnectionPeer::SetSendAlgorithm(endpoint_a.connection(), sender);
+
+ // First transmit a small, packet-size chunk of data.
+ QuicByteCount bytes_to_transfer = 3 * 1024 * 1024;
+ endpoint_a.AddBytesToTransfer(bytes_to_transfer);
+ QuicTime end_time =
+ simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(30);
+ simulator_.RunUntil([this, &endpoint_b, bytes_to_transfer, end_time]() {
+ return endpoint_b.bytes_received() == bytes_to_transfer ||
+ simulator_.GetClock()->Now() >= end_time;
+ });
+
+ EXPECT_EQ(bytes_to_transfer, endpoint_a.bytes_transferred());
+ EXPECT_EQ(bytes_to_transfer, endpoint_b.bytes_received());
+ EXPECT_GT(endpoint_a.write_blocked_count(), 0u);
+ EXPECT_FALSE(endpoint_a.wrong_data_received());
+ EXPECT_FALSE(endpoint_b.wrong_data_received());
+}
+
+// Test transmission of 1 MiB of data between two hosts simultaneously in both
+// directions.
+TEST_F(QuicEndpointTest, TwoWayTransmission) {
+ QuicEndpoint endpoint_a(&simulator_, "Endpoint A", "Endpoint B",
+ Perspective::IS_CLIENT, test::TestConnectionId(42));
+ QuicEndpoint endpoint_b(&simulator_, "Endpoint B", "Endpoint A",
+ Perspective::IS_SERVER, test::TestConnectionId(42));
+ auto link_a = Link(&endpoint_a, switch_.port(1));
+ auto link_b = Link(&endpoint_b, switch_.port(2));
+
+ endpoint_a.RecordTrace();
+ endpoint_b.RecordTrace();
+
+ endpoint_a.AddBytesToTransfer(1024 * 1024);
+ endpoint_b.AddBytesToTransfer(1024 * 1024);
+ QuicTime end_time =
+ simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(5);
+ simulator_.RunUntil(
+ [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; });
+
+ EXPECT_EQ(1024u * 1024u, endpoint_a.bytes_transferred());
+ EXPECT_EQ(1024u * 1024u, endpoint_b.bytes_transferred());
+ EXPECT_EQ(1024u * 1024u, endpoint_a.bytes_received());
+ EXPECT_EQ(1024u * 1024u, endpoint_b.bytes_received());
+ EXPECT_FALSE(endpoint_a.wrong_data_received());
+ EXPECT_FALSE(endpoint_b.wrong_data_received());
+}
+
+// Simulate three hosts trying to send data to a fourth one simultaneously.
+TEST_F(QuicEndpointTest, Competition) {
+ // TODO(63765788): Turn back on this flag when the issue if fixed.
+ SetQuicReloadableFlag(quic_bbr_one_mss_conservation, false);
+ auto endpoint_a = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint A", "Endpoint D (A)", Perspective::IS_CLIENT,
+ test::TestConnectionId(42));
+ auto endpoint_b = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint B", "Endpoint D (B)", Perspective::IS_CLIENT,
+ test::TestConnectionId(43));
+ auto endpoint_c = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint C", "Endpoint D (C)", Perspective::IS_CLIENT,
+ test::TestConnectionId(44));
+ auto endpoint_d_a = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint D (A)", "Endpoint A", Perspective::IS_SERVER,
+ test::TestConnectionId(42));
+ auto endpoint_d_b = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint D (B)", "Endpoint B", Perspective::IS_SERVER,
+ test::TestConnectionId(43));
+ auto endpoint_d_c = QuicMakeUnique<QuicEndpoint>(
+ &simulator_, "Endpoint D (C)", "Endpoint C", Perspective::IS_SERVER,
+ test::TestConnectionId(44));
+ QuicEndpointMultiplexer endpoint_d(
+ "Endpoint D",
+ {endpoint_d_a.get(), endpoint_d_b.get(), endpoint_d_c.get()});
+
+ // Create links with slightly different RTTs in order to avoid pathological
+ // side-effects of packets entering the queue at the exactly same time.
+ auto link_a = CustomLink(endpoint_a.get(), switch_.port(1), 0);
+ auto link_b = CustomLink(endpoint_b.get(), switch_.port(2), 1);
+ auto link_c = CustomLink(endpoint_c.get(), switch_.port(3), 2);
+ auto link_d = Link(&endpoint_d, switch_.port(4));
+
+ endpoint_a->AddBytesToTransfer(2 * 1024 * 1024);
+ endpoint_b->AddBytesToTransfer(2 * 1024 * 1024);
+ endpoint_c->AddBytesToTransfer(2 * 1024 * 1024);
+ QuicTime end_time =
+ simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(10);
+ simulator_.RunUntil(
+ [this, end_time]() { return simulator_.GetClock()->Now() >= end_time; });
+
+ for (QuicEndpoint* endpoint :
+ {endpoint_a.get(), endpoint_b.get(), endpoint_c.get()}) {
+ EXPECT_EQ(2u * 1024u * 1024u, endpoint->bytes_transferred());
+ EXPECT_GE(endpoint->connection()->GetStats().packets_lost, 0u);
+ }
+ for (QuicEndpoint* endpoint :
+ {endpoint_d_a.get(), endpoint_d_b.get(), endpoint_d_c.get()}) {
+ EXPECT_EQ(2u * 1024u * 1024u, endpoint->bytes_received());
+ EXPECT_FALSE(endpoint->wrong_data_received());
+ }
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.cc
new file mode 100644
index 00000000000..fdb59dbc7bb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+namespace simulator {
+
+Simulator::Simulator()
+ : random_generator_(nullptr),
+ alarm_factory_(this, "Default Alarm Manager"),
+ run_for_should_stop_(false),
+ enable_random_delays_(false) {
+ run_for_alarm_.reset(
+ alarm_factory_.CreateAlarm(new RunForDelegate(&run_for_should_stop_)));
+}
+
+Simulator::~Simulator() {
+ // Ensure that Actor under run_for_alarm_ is removed before Simulator data
+ // structures are destructed.
+ run_for_alarm_.reset();
+}
+
+Simulator::Clock::Clock() : now_(kStartTime) {}
+
+QuicTime Simulator::Clock::ApproximateNow() const {
+ return now_;
+}
+
+QuicTime Simulator::Clock::Now() const {
+ return now_;
+}
+
+QuicWallTime Simulator::Clock::WallNow() const {
+ return QuicWallTime::FromUNIXMicroseconds(
+ (now_ - QuicTime::Zero()).ToMicroseconds());
+}
+
+void Simulator::AddActor(Actor* actor) {
+ auto emplace_times_result =
+ scheduled_times_.insert(std::make_pair(actor, QuicTime::Infinite()));
+ auto emplace_names_result = actor_names_.insert(actor->name());
+
+ // Ensure that the object was actually placed into the map.
+ DCHECK(emplace_times_result.second);
+ DCHECK(emplace_names_result.second);
+}
+
+void Simulator::RemoveActor(Actor* actor) {
+ auto scheduled_time_it = scheduled_times_.find(actor);
+ auto actor_names_it = actor_names_.find(actor->name());
+ DCHECK(scheduled_time_it != scheduled_times_.end());
+ DCHECK(actor_names_it != actor_names_.end());
+
+ QuicTime scheduled_time = scheduled_time_it->second;
+ if (scheduled_time != QuicTime::Infinite()) {
+ Unschedule(actor);
+ }
+
+ scheduled_times_.erase(scheduled_time_it);
+ actor_names_.erase(actor_names_it);
+}
+
+void Simulator::Schedule(Actor* actor, QuicTime new_time) {
+ auto scheduled_time_it = scheduled_times_.find(actor);
+ DCHECK(scheduled_time_it != scheduled_times_.end());
+ QuicTime scheduled_time = scheduled_time_it->second;
+
+ if (scheduled_time <= new_time) {
+ return;
+ }
+
+ if (scheduled_time != QuicTime::Infinite()) {
+ Unschedule(actor);
+ }
+
+ scheduled_time_it->second = new_time;
+ schedule_.insert(std::make_pair(new_time, actor));
+}
+
+void Simulator::Unschedule(Actor* actor) {
+ auto scheduled_time_it = scheduled_times_.find(actor);
+ DCHECK(scheduled_time_it != scheduled_times_.end());
+ QuicTime scheduled_time = scheduled_time_it->second;
+
+ DCHECK(scheduled_time != QuicTime::Infinite());
+ auto range = schedule_.equal_range(scheduled_time);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second == actor) {
+ schedule_.erase(it);
+ scheduled_time_it->second = QuicTime::Infinite();
+ return;
+ }
+ }
+ DCHECK(false);
+}
+
+const QuicClock* Simulator::GetClock() const {
+ return &clock_;
+}
+
+QuicRandom* Simulator::GetRandomGenerator() {
+ if (random_generator_ == nullptr) {
+ random_generator_ = QuicRandom::GetInstance();
+ }
+
+ return random_generator_;
+}
+
+QuicBufferAllocator* Simulator::GetStreamSendBufferAllocator() {
+ return &buffer_allocator_;
+}
+
+QuicAlarmFactory* Simulator::GetAlarmFactory() {
+ return &alarm_factory_;
+}
+
+Simulator::RunForDelegate::RunForDelegate(bool* run_for_should_stop)
+ : run_for_should_stop_(run_for_should_stop) {}
+
+void Simulator::RunForDelegate::OnAlarm() {
+ *run_for_should_stop_ = true;
+}
+
+void Simulator::RunFor(QuicTime::Delta time_span) {
+ DCHECK(!run_for_alarm_->IsSet());
+
+ // RunFor() ensures that the simulation stops at the exact time specified by
+ // scheduling an alarm at that point and using that alarm to abort the
+ // simulation. An alarm is necessary because otherwise it is possible that
+ // nothing is scheduled at |end_time|, so the simulation will either go
+ // further than requested or stop before reaching |end_time|.
+ const QuicTime end_time = clock_.Now() + time_span;
+ run_for_alarm_->Set(end_time);
+ run_for_should_stop_ = false;
+ bool simulation_result = RunUntil([this]() { return run_for_should_stop_; });
+
+ DCHECK(simulation_result);
+ DCHECK(clock_.Now() == end_time);
+}
+
+void Simulator::HandleNextScheduledActor() {
+ const auto current_event_it = schedule_.begin();
+ QuicTime event_time = current_event_it->first;
+ Actor* actor = current_event_it->second;
+ QUIC_DVLOG(3) << "At t = " << event_time.ToDebuggingValue() << ", calling "
+ << actor->name();
+
+ Unschedule(actor);
+
+ if (clock_.Now() > event_time) {
+ QUIC_BUG << "Error: event registered by [" << actor->name()
+ << "] requires travelling back in time. Current time: "
+ << clock_.Now().ToDebuggingValue()
+ << ", scheduled time: " << event_time.ToDebuggingValue();
+ }
+ clock_.now_ = event_time;
+
+ actor->Act();
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.h
new file mode 100644
index 00000000000..e21dd501fe5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SIMULATOR_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SIMULATOR_H_
+
+#include <map>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/actor.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h"
+
+namespace quic {
+namespace simulator {
+
+// Simulator is responsible for scheduling actors in the simulation and
+// providing basic utility interfaces (clock, alarms, RNG and others).
+class Simulator : public QuicConnectionHelperInterface {
+ public:
+ Simulator();
+ Simulator(const Simulator&) = delete;
+ Simulator& operator=(const Simulator&) = delete;
+ ~Simulator() override;
+
+ // Schedule the specified actor. This method will ensure that |actor| is
+ // called at |new_time| at latest. If Schedule() is called multiple times
+ // before the Actor is called, Act() is called exactly once, at the earliest
+ // time requested, and the Actor has to reschedule itself manually for the
+ // subsequent times if they are still necessary.
+ void Schedule(Actor* actor, QuicTime new_time);
+
+ // Remove the specified actor from the schedule.
+ void Unschedule(Actor* actor);
+
+ // Begin QuicConnectionHelperInterface implementation.
+ const QuicClock* GetClock() const override;
+ QuicRandom* GetRandomGenerator() override;
+ QuicBufferAllocator* GetStreamSendBufferAllocator() override;
+ // End QuicConnectionHelperInterface implementation.
+
+ QuicAlarmFactory* GetAlarmFactory();
+
+ inline void set_random_generator(QuicRandom* random) {
+ random_generator_ = random;
+ }
+
+ inline bool enable_random_delays() const { return enable_random_delays_; }
+
+ // Run the simulation until either no actors are scheduled or
+ // |termination_predicate| returns true. Returns true if terminated due to
+ // predicate, and false otherwise.
+ template <class TerminationPredicate>
+ bool RunUntil(TerminationPredicate termination_predicate);
+
+ // Same as RunUntil, except this function also accepts a |deadline|, and will
+ // return false if the deadline is exceeded.
+ template <class TerminationPredicate>
+ bool RunUntilOrTimeout(TerminationPredicate termination_predicate,
+ QuicTime::Delta deadline);
+
+ // Runs the simulation for exactly the specified |time_span|.
+ void RunFor(QuicTime::Delta time_span);
+
+ private:
+ friend class Actor;
+
+ class Clock : public QuicClock {
+ public:
+ // Do not start at zero as certain code can treat zero as an invalid
+ // timestamp.
+ const QuicTime kStartTime =
+ QuicTime::Zero() + QuicTime::Delta::FromMicroseconds(1);
+
+ Clock();
+
+ QuicTime ApproximateNow() const override;
+ QuicTime Now() const override;
+ QuicWallTime WallNow() const override;
+
+ QuicTime now_;
+ };
+
+ // The delegate used for RunFor().
+ class RunForDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit RunForDelegate(bool* run_for_should_stop);
+ void OnAlarm() override;
+
+ private:
+ // Pointer to |run_for_should_stop_| in the parent simulator.
+ bool* run_for_should_stop_;
+ };
+
+ // Register an actor with the simulator. Invoked by Actor constructor.
+ void AddActor(Actor* actor);
+
+ // Unregister an actor with the simulator. Invoked by Actor destructor.
+ void RemoveActor(Actor* actor);
+
+ // Finds the next scheduled actor, advances time to the schedule time and
+ // notifies the actor.
+ void HandleNextScheduledActor();
+
+ Clock clock_;
+ QuicRandom* random_generator_;
+ SimpleBufferAllocator buffer_allocator_;
+ AlarmFactory alarm_factory_;
+
+ // Alarm for RunFor() method.
+ std::unique_ptr<QuicAlarm> run_for_alarm_;
+ // Flag used to stop simulations ran via RunFor().
+ bool run_for_should_stop_;
+
+ // Indicates whether the simulator should add random delays on the links in
+ // order to avoid synchronization issues.
+ bool enable_random_delays_;
+
+ // Schedule of when the actors will be executed via an Act() call. The
+ // schedule is subject to the following invariants:
+ // - An actor cannot be scheduled for a later time than it's currently in the
+ // schedule.
+ // - An actor is removed from schedule either immediately before Act() is
+ // called or by explicitly calling Unschedule().
+ // - Each Actor appears in the map at most once.
+ std::multimap<QuicTime, Actor*> schedule_;
+ // For each actor, maintain the time it is scheduled at. The value for
+ // unscheduled actors is QuicTime::Infinite().
+ QuicUnorderedMap<Actor*, QuicTime> scheduled_times_;
+ QuicUnorderedSet<std::string> actor_names_;
+};
+
+template <class TerminationPredicate>
+bool Simulator::RunUntil(TerminationPredicate termination_predicate) {
+ bool predicate_value = false;
+ while (true) {
+ predicate_value = termination_predicate();
+ if (predicate_value || schedule_.empty()) {
+ break;
+ }
+ HandleNextScheduledActor();
+ }
+ return predicate_value;
+}
+
+template <class TerminationPredicate>
+bool Simulator::RunUntilOrTimeout(TerminationPredicate termination_predicate,
+ QuicTime::Delta timeout) {
+ QuicTime end_time = clock_.Now() + timeout;
+ bool return_value = RunUntil([end_time, &termination_predicate, this]() {
+ return termination_predicate() || clock_.Now() >= end_time;
+ });
+
+ if (clock_.Now() >= end_time) {
+ return false;
+ }
+ return return_value;
+}
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SIMULATOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc
new file mode 100644
index 00000000000..33ad564324e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc
@@ -0,0 +1,830 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/alarm_factory.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace simulator {
+
+// A simple counter that increments its value by 1 every specified period.
+class Counter : public Actor {
+ public:
+ Counter(Simulator* simulator, std::string name, QuicTime::Delta period)
+ : Actor(simulator, name), value_(-1), period_(period) {
+ Schedule(clock_->Now());
+ }
+ ~Counter() override {}
+
+ inline int get_value() const { return value_; }
+
+ void Act() override {
+ ++value_;
+ QUIC_DVLOG(1) << name_ << " has value " << value_ << " at time "
+ << clock_->Now().ToDebuggingValue();
+ Schedule(clock_->Now() + period_);
+ }
+
+ private:
+ int value_;
+ QuicTime::Delta period_;
+};
+
+class SimulatorTest : public QuicTest {};
+
+// Test that the basic event handling works, and that Actors can be created and
+// destroyed mid-simulation.
+TEST_F(SimulatorTest, Counters) {
+ Simulator simulator;
+ for (int i = 0; i < 2; ++i) {
+ Counter fast_counter(&simulator, "fast_counter",
+ QuicTime::Delta::FromSeconds(3));
+ Counter slow_counter(&simulator, "slow_counter",
+ QuicTime::Delta::FromSeconds(10));
+
+ simulator.RunUntil(
+ [&slow_counter]() { return slow_counter.get_value() >= 10; });
+
+ EXPECT_EQ(10, slow_counter.get_value());
+ EXPECT_EQ(10 * 10 / 3, fast_counter.get_value());
+ }
+}
+
+// A port which counts the number of packets received on it, both total and
+// per-destination.
+class CounterPort : public UnconstrainedPortInterface {
+ public:
+ CounterPort() { Reset(); }
+ ~CounterPort() override {}
+
+ inline QuicByteCount bytes() const { return bytes_; }
+ inline QuicPacketCount packets() const { return packets_; }
+
+ void AcceptPacket(std::unique_ptr<Packet> packet) override {
+ bytes_ += packet->size;
+ packets_ += 1;
+
+ per_destination_packet_counter_[packet->destination] += 1;
+ }
+
+ void Reset() {
+ bytes_ = 0;
+ packets_ = 0;
+ per_destination_packet_counter_.clear();
+ }
+
+ QuicPacketCount CountPacketsForDestination(std::string destination) const {
+ auto result_it = per_destination_packet_counter_.find(destination);
+ if (result_it == per_destination_packet_counter_.cend()) {
+ return 0;
+ }
+ return result_it->second;
+ }
+
+ private:
+ QuicByteCount bytes_;
+ QuicPacketCount packets_;
+
+ QuicUnorderedMap<std::string, QuicPacketCount>
+ per_destination_packet_counter_;
+};
+
+// Sends the packet to the specified destination at the uplink rate. Provides a
+// CounterPort as an Rx interface.
+class LinkSaturator : public Endpoint {
+ public:
+ LinkSaturator(Simulator* simulator,
+ std::string name,
+ QuicByteCount packet_size,
+ std::string destination)
+ : Endpoint(simulator, name),
+ packet_size_(packet_size),
+ destination_(std::move(destination)),
+ bytes_transmitted_(0),
+ packets_transmitted_(0) {
+ Schedule(clock_->Now());
+ }
+
+ void Act() override {
+ if (tx_port_->TimeUntilAvailable().IsZero()) {
+ auto packet = QuicMakeUnique<Packet>();
+ packet->source = name_;
+ packet->destination = destination_;
+ packet->tx_timestamp = clock_->Now();
+ packet->size = packet_size_;
+
+ tx_port_->AcceptPacket(std::move(packet));
+
+ bytes_transmitted_ += packet_size_;
+ packets_transmitted_ += 1;
+ }
+
+ Schedule(clock_->Now() + tx_port_->TimeUntilAvailable());
+ }
+
+ UnconstrainedPortInterface* GetRxPort() override {
+ return static_cast<UnconstrainedPortInterface*>(&rx_port_);
+ }
+
+ void SetTxPort(ConstrainedPortInterface* port) override { tx_port_ = port; }
+
+ CounterPort* counter() { return &rx_port_; }
+
+ inline QuicByteCount bytes_transmitted() const { return bytes_transmitted_; }
+ inline QuicPacketCount packets_transmitted() const {
+ return packets_transmitted_;
+ }
+
+ void Pause() { Unschedule(); }
+ void Resume() { Schedule(clock_->Now()); }
+
+ private:
+ QuicByteCount packet_size_;
+ std::string destination_;
+
+ ConstrainedPortInterface* tx_port_;
+ CounterPort rx_port_;
+
+ QuicByteCount bytes_transmitted_;
+ QuicPacketCount packets_transmitted_;
+};
+
+// Saturate a symmetric link and verify that the number of packets sent and
+// received is correct.
+TEST_F(SimulatorTest, DirectLinkSaturation) {
+ Simulator simulator;
+ LinkSaturator saturator_a(&simulator, "Saturator A", 1000, "Saturator B");
+ LinkSaturator saturator_b(&simulator, "Saturator B", 100, "Saturator A");
+ SymmetricLink link(&saturator_a, &saturator_b,
+ QuicBandwidth::FromKBytesPerSecond(1000),
+ QuicTime::Delta::FromMilliseconds(100) +
+ QuicTime::Delta::FromMicroseconds(1));
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ const QuicTime after_first_50_ms =
+ start_time + QuicTime::Delta::FromMilliseconds(50);
+ simulator.RunUntil([&simulator, after_first_50_ms]() {
+ return simulator.GetClock()->Now() >= after_first_50_ms;
+ });
+ EXPECT_LE(1000u * 50u, saturator_a.bytes_transmitted());
+ EXPECT_GE(1000u * 51u, saturator_a.bytes_transmitted());
+ EXPECT_LE(1000u * 50u, saturator_b.bytes_transmitted());
+ EXPECT_GE(1000u * 51u, saturator_b.bytes_transmitted());
+ EXPECT_LE(50u, saturator_a.packets_transmitted());
+ EXPECT_GE(51u, saturator_a.packets_transmitted());
+ EXPECT_LE(500u, saturator_b.packets_transmitted());
+ EXPECT_GE(501u, saturator_b.packets_transmitted());
+ EXPECT_EQ(0u, saturator_a.counter()->bytes());
+ EXPECT_EQ(0u, saturator_b.counter()->bytes());
+
+ simulator.RunUntil([&saturator_a, &saturator_b]() {
+ if (saturator_a.counter()->packets() > 1000 ||
+ saturator_b.counter()->packets() > 100) {
+ ADD_FAILURE() << "The simulation did not arrive at the expected "
+ "termination contidition. Saturator A counter: "
+ << saturator_a.counter()->packets()
+ << ", saturator B counter: "
+ << saturator_b.counter()->packets();
+ return true;
+ }
+
+ return saturator_a.counter()->packets() == 1000 &&
+ saturator_b.counter()->packets() == 100;
+ });
+ EXPECT_EQ(201u, saturator_a.packets_transmitted());
+ EXPECT_EQ(2001u, saturator_b.packets_transmitted());
+ EXPECT_EQ(201u * 1000, saturator_a.bytes_transmitted());
+ EXPECT_EQ(2001u * 100, saturator_b.bytes_transmitted());
+
+ EXPECT_EQ(1000u,
+ saturator_a.counter()->CountPacketsForDestination("Saturator A"));
+ EXPECT_EQ(100u,
+ saturator_b.counter()->CountPacketsForDestination("Saturator B"));
+ EXPECT_EQ(0u,
+ saturator_a.counter()->CountPacketsForDestination("Saturator B"));
+ EXPECT_EQ(0u,
+ saturator_b.counter()->CountPacketsForDestination("Saturator A"));
+
+ const QuicTime end_time = simulator.GetClock()->Now();
+ const QuicBandwidth observed_bandwidth = QuicBandwidth::FromBytesAndTimeDelta(
+ saturator_a.bytes_transmitted(), end_time - start_time);
+ test::ExpectApproxEq(link.bandwidth(), observed_bandwidth, 0.01f);
+}
+
+// Accepts packets and stores them internally.
+class PacketAcceptor : public ConstrainedPortInterface {
+ public:
+ void AcceptPacket(std::unique_ptr<Packet> packet) override {
+ packets_.emplace_back(std::move(packet));
+ }
+
+ QuicTime::Delta TimeUntilAvailable() override {
+ return QuicTime::Delta::Zero();
+ }
+
+ std::vector<std::unique_ptr<Packet>>* packets() { return &packets_; }
+
+ private:
+ std::vector<std::unique_ptr<Packet>> packets_;
+};
+
+// Ensure the queue behaves correctly with accepting packets.
+TEST_F(SimulatorTest, Queue) {
+ Simulator simulator;
+ Queue queue(&simulator, "Queue", 1000);
+ PacketAcceptor acceptor;
+ queue.set_tx_port(&acceptor);
+
+ EXPECT_EQ(0u, queue.bytes_queued());
+ EXPECT_EQ(0u, queue.packets_queued());
+ EXPECT_EQ(0u, acceptor.packets()->size());
+
+ auto first_packet = QuicMakeUnique<Packet>();
+ first_packet->size = 600;
+ queue.AcceptPacket(std::move(first_packet));
+ EXPECT_EQ(600u, queue.bytes_queued());
+ EXPECT_EQ(1u, queue.packets_queued());
+ EXPECT_EQ(0u, acceptor.packets()->size());
+
+ // The second packet does not fit and is dropped.
+ auto second_packet = QuicMakeUnique<Packet>();
+ second_packet->size = 500;
+ queue.AcceptPacket(std::move(second_packet));
+ EXPECT_EQ(600u, queue.bytes_queued());
+ EXPECT_EQ(1u, queue.packets_queued());
+ EXPECT_EQ(0u, acceptor.packets()->size());
+
+ auto third_packet = QuicMakeUnique<Packet>();
+ third_packet->size = 400;
+ queue.AcceptPacket(std::move(third_packet));
+ EXPECT_EQ(1000u, queue.bytes_queued());
+ EXPECT_EQ(2u, queue.packets_queued());
+ EXPECT_EQ(0u, acceptor.packets()->size());
+
+ // Run until there is nothing scheduled, so that the queue can deplete.
+ simulator.RunUntil([]() { return false; });
+ EXPECT_EQ(0u, queue.bytes_queued());
+ EXPECT_EQ(0u, queue.packets_queued());
+ ASSERT_EQ(2u, acceptor.packets()->size());
+ EXPECT_EQ(600u, acceptor.packets()->at(0)->size);
+ EXPECT_EQ(400u, acceptor.packets()->at(1)->size);
+}
+
+// Simulate a situation where the bottleneck link is 10 times slower than the
+// uplink, and they are separated by a queue.
+TEST_F(SimulatorTest, QueueBottleneck) {
+ const QuicBandwidth local_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(1000);
+ const QuicBandwidth bottleneck_bandwidth = 0.1f * local_bandwidth;
+ const QuicTime::Delta local_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(1);
+ const QuicTime::Delta bottleneck_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(20);
+ const QuicByteCount bdp =
+ bottleneck_bandwidth *
+ (local_propagation_delay + bottleneck_propagation_delay);
+
+ Simulator simulator;
+ LinkSaturator saturator(&simulator, "Saturator", 1000, "Counter");
+ ASSERT_GE(bdp, 1000u);
+ Queue queue(&simulator, "Queue", bdp);
+ CounterPort counter;
+
+ OneWayLink local_link(&simulator, "Local link", &queue, local_bandwidth,
+ local_propagation_delay);
+ OneWayLink bottleneck_link(&simulator, "Bottleneck link", &counter,
+ bottleneck_bandwidth,
+ bottleneck_propagation_delay);
+ saturator.SetTxPort(&local_link);
+ queue.set_tx_port(&bottleneck_link);
+
+ static const QuicPacketCount packets_received = 1000;
+ simulator.RunUntil(
+ [&counter]() { return counter.packets() == packets_received; });
+ const double loss_ratio = 1 - static_cast<double>(packets_received) /
+ saturator.packets_transmitted();
+ EXPECT_NEAR(loss_ratio, 0.9, 0.001);
+}
+
+// Verify that the queue of exactly one packet allows the transmission to
+// actually go through.
+TEST_F(SimulatorTest, OnePacketQueue) {
+ const QuicBandwidth local_bandwidth =
+ QuicBandwidth::FromKBytesPerSecond(1000);
+ const QuicBandwidth bottleneck_bandwidth = 0.1f * local_bandwidth;
+ const QuicTime::Delta local_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(1);
+ const QuicTime::Delta bottleneck_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(20);
+
+ Simulator simulator;
+ LinkSaturator saturator(&simulator, "Saturator", 1000, "Counter");
+ Queue queue(&simulator, "Queue", 1000);
+ CounterPort counter;
+
+ OneWayLink local_link(&simulator, "Local link", &queue, local_bandwidth,
+ local_propagation_delay);
+ OneWayLink bottleneck_link(&simulator, "Bottleneck link", &counter,
+ bottleneck_bandwidth,
+ bottleneck_propagation_delay);
+ saturator.SetTxPort(&local_link);
+ queue.set_tx_port(&bottleneck_link);
+
+ static const QuicPacketCount packets_received = 10;
+ // The deadline here is to prevent this tests from looping infinitely in case
+ // the packets never reach the receiver.
+ const QuicTime deadline =
+ simulator.GetClock()->Now() + QuicTime::Delta::FromSeconds(10);
+ simulator.RunUntil([&simulator, &counter, deadline]() {
+ return counter.packets() == packets_received ||
+ simulator.GetClock()->Now() > deadline;
+ });
+ ASSERT_EQ(packets_received, counter.packets());
+}
+
+// Simulate a network where three endpoints are connected to a switch and they
+// are sending traffic in circle (1 -> 2, 2 -> 3, 3 -> 1).
+TEST_F(SimulatorTest, SwitchedNetwork) {
+ const QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(10000);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(50);
+
+ Simulator simulator;
+ LinkSaturator saturator1(&simulator, "Saturator 1", 1000, "Saturator 2");
+ LinkSaturator saturator2(&simulator, "Saturator 2", 1000, "Saturator 3");
+ LinkSaturator saturator3(&simulator, "Saturator 3", 1000, "Saturator 1");
+ Switch network_switch(&simulator, "Switch", 8,
+ bandwidth * base_propagation_delay * 10);
+
+ // For determinicity, make it so that the first packet will arrive from
+ // Saturator 1, then from Saturator 2, and then from Saturator 3.
+ SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
+ base_propagation_delay);
+ SymmetricLink link2(&saturator2, network_switch.port(2), bandwidth,
+ base_propagation_delay * 2);
+ SymmetricLink link3(&saturator3, network_switch.port(3), bandwidth,
+ base_propagation_delay * 3);
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ static const QuicPacketCount bytes_received = 64 * 1000;
+ simulator.RunUntil([&saturator1]() {
+ return saturator1.counter()->bytes() >= bytes_received;
+ });
+ const QuicTime end_time = simulator.GetClock()->Now();
+
+ const QuicBandwidth observed_bandwidth = QuicBandwidth::FromBytesAndTimeDelta(
+ bytes_received, end_time - start_time);
+ const double bandwidth_ratio =
+ static_cast<double>(observed_bandwidth.ToBitsPerSecond()) /
+ bandwidth.ToBitsPerSecond();
+ EXPECT_NEAR(1, bandwidth_ratio, 0.1);
+
+ const double normalized_received_packets_for_saturator_2 =
+ static_cast<double>(saturator2.counter()->packets()) /
+ saturator1.counter()->packets();
+ const double normalized_received_packets_for_saturator_3 =
+ static_cast<double>(saturator3.counter()->packets()) /
+ saturator1.counter()->packets();
+ EXPECT_NEAR(1, normalized_received_packets_for_saturator_2, 0.1);
+ EXPECT_NEAR(1, normalized_received_packets_for_saturator_3, 0.1);
+
+ // Since Saturator 1 has its packet arrive first into the switch, switch will
+ // always know how to route traffic to it.
+ EXPECT_EQ(0u,
+ saturator2.counter()->CountPacketsForDestination("Saturator 1"));
+ EXPECT_EQ(0u,
+ saturator3.counter()->CountPacketsForDestination("Saturator 1"));
+
+ // Packets from the other saturators will be broadcast at least once.
+ EXPECT_EQ(1u,
+ saturator1.counter()->CountPacketsForDestination("Saturator 2"));
+ EXPECT_EQ(1u,
+ saturator3.counter()->CountPacketsForDestination("Saturator 2"));
+ EXPECT_EQ(1u,
+ saturator1.counter()->CountPacketsForDestination("Saturator 3"));
+ EXPECT_EQ(1u,
+ saturator2.counter()->CountPacketsForDestination("Saturator 3"));
+}
+
+// Toggle an alarm on and off at the specified interval. Assumes that alarm is
+// initially set and unsets it almost immediately after the object is
+// instantiated.
+class AlarmToggler : public Actor {
+ public:
+ AlarmToggler(Simulator* simulator,
+ std::string name,
+ QuicAlarm* alarm,
+ QuicTime::Delta interval)
+ : Actor(simulator, name),
+ alarm_(alarm),
+ interval_(interval),
+ deadline_(alarm->deadline()),
+ times_set_(0),
+ times_cancelled_(0) {
+ EXPECT_TRUE(alarm->IsSet());
+ EXPECT_GE(alarm->deadline(), clock_->Now());
+ Schedule(clock_->Now());
+ }
+
+ void Act() override {
+ if (deadline_ <= clock_->Now()) {
+ return;
+ }
+
+ if (alarm_->IsSet()) {
+ alarm_->Cancel();
+ times_cancelled_++;
+ } else {
+ alarm_->Set(deadline_);
+ times_set_++;
+ }
+
+ Schedule(clock_->Now() + interval_);
+ }
+
+ inline int times_set() { return times_set_; }
+ inline int times_cancelled() { return times_cancelled_; }
+
+ private:
+ QuicAlarm* alarm_;
+ QuicTime::Delta interval_;
+ QuicTime deadline_;
+
+ // Counts the number of times the alarm was set.
+ int times_set_;
+ // Counts the number of times the alarm was cancelled.
+ int times_cancelled_;
+};
+
+// Counts the number of times an alarm has fired.
+class CounterDelegate : public QuicAlarm::Delegate {
+ public:
+ explicit CounterDelegate(size_t* counter) : counter_(counter) {}
+
+ void OnAlarm() override { *counter_ += 1; }
+
+ private:
+ size_t* counter_;
+};
+
+// Verifies that the alarms work correctly, even when they are repeatedly
+// toggled.
+TEST_F(SimulatorTest, Alarms) {
+ Simulator simulator;
+ QuicAlarmFactory* alarm_factory = simulator.GetAlarmFactory();
+
+ size_t fast_alarm_counter = 0;
+ size_t slow_alarm_counter = 0;
+ std::unique_ptr<QuicAlarm> alarm_fast(
+ alarm_factory->CreateAlarm(new CounterDelegate(&fast_alarm_counter)));
+ std::unique_ptr<QuicAlarm> alarm_slow(
+ alarm_factory->CreateAlarm(new CounterDelegate(&slow_alarm_counter)));
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ alarm_fast->Set(start_time + QuicTime::Delta::FromMilliseconds(100));
+ alarm_slow->Set(start_time + QuicTime::Delta::FromMilliseconds(750));
+ AlarmToggler toggler(&simulator, "Toggler", alarm_slow.get(),
+ QuicTime::Delta::FromMilliseconds(100));
+
+ const QuicTime end_time =
+ start_time + QuicTime::Delta::FromMilliseconds(1000);
+ EXPECT_FALSE(simulator.RunUntil([&simulator, end_time]() {
+ return simulator.GetClock()->Now() >= end_time;
+ }));
+ EXPECT_EQ(1u, slow_alarm_counter);
+ EXPECT_EQ(1u, fast_alarm_counter);
+
+ EXPECT_EQ(4, toggler.times_set());
+ EXPECT_EQ(4, toggler.times_cancelled());
+}
+
+// Verifies that a cancelled alarm is never fired.
+TEST_F(SimulatorTest, AlarmCancelling) {
+ Simulator simulator;
+ QuicAlarmFactory* alarm_factory = simulator.GetAlarmFactory();
+
+ size_t alarm_counter = 0;
+ std::unique_ptr<QuicAlarm> alarm(
+ alarm_factory->CreateAlarm(new CounterDelegate(&alarm_counter)));
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ const QuicTime alarm_at = start_time + QuicTime::Delta::FromMilliseconds(300);
+ const QuicTime end_time = start_time + QuicTime::Delta::FromMilliseconds(400);
+
+ alarm->Set(alarm_at);
+ alarm->Cancel();
+ EXPECT_FALSE(alarm->IsSet());
+
+ EXPECT_FALSE(simulator.RunUntil([&simulator, end_time]() {
+ return simulator.GetClock()->Now() >= end_time;
+ }));
+
+ EXPECT_FALSE(alarm->IsSet());
+ EXPECT_EQ(0u, alarm_counter);
+}
+
+// Verifies that alarms can be scheduled into the past.
+TEST_F(SimulatorTest, AlarmInPast) {
+ Simulator simulator;
+ QuicAlarmFactory* alarm_factory = simulator.GetAlarmFactory();
+
+ size_t alarm_counter = 0;
+ std::unique_ptr<QuicAlarm> alarm(
+ alarm_factory->CreateAlarm(new CounterDelegate(&alarm_counter)));
+
+ const QuicTime start_time = simulator.GetClock()->Now();
+ simulator.RunFor(QuicTime::Delta::FromMilliseconds(400));
+
+ alarm->Set(start_time);
+ simulator.RunFor(QuicTime::Delta::FromMilliseconds(1));
+ EXPECT_FALSE(alarm->IsSet());
+ EXPECT_EQ(1u, alarm_counter);
+}
+
+// Tests Simulator::RunUntilOrTimeout() interface.
+TEST_F(SimulatorTest, RunUntilOrTimeout) {
+ Simulator simulator;
+ bool simulation_result;
+
+ // Count the number of seconds since the beginning of the simulation.
+ Counter counter(&simulator, "counter", QuicTime::Delta::FromSeconds(1));
+
+ // Ensure that the counter reaches the value of 10 given a 20 second deadline.
+ simulation_result = simulator.RunUntilOrTimeout(
+ [&counter]() { return counter.get_value() == 10; },
+ QuicTime::Delta::FromSeconds(20));
+ ASSERT_TRUE(simulation_result);
+
+ // Ensure that the counter will not reach the value of 100 given that the
+ // starting value is 10 and the deadline is 20 seconds.
+ simulation_result = simulator.RunUntilOrTimeout(
+ [&counter]() { return counter.get_value() == 100; },
+ QuicTime::Delta::FromSeconds(20));
+ ASSERT_FALSE(simulation_result);
+}
+
+// Tests Simulator::RunFor() interface.
+TEST_F(SimulatorTest, RunFor) {
+ Simulator simulator;
+
+ Counter counter(&simulator, "counter", QuicTime::Delta::FromSeconds(3));
+
+ simulator.RunFor(QuicTime::Delta::FromSeconds(100));
+
+ EXPECT_EQ(33, counter.get_value());
+}
+
+class MockPacketFilter : public PacketFilter {
+ public:
+ MockPacketFilter(Simulator* simulator, std::string name, Endpoint* endpoint)
+ : PacketFilter(simulator, name, endpoint) {}
+ MOCK_METHOD1(FilterPacket, bool(const Packet&));
+};
+
+// Set up two trivial packet filters, one allowing any packets, and one dropping
+// all of them.
+TEST_F(SimulatorTest, PacketFilter) {
+ const QuicBandwidth bandwidth =
+ QuicBandwidth::FromBytesPerSecond(1024 * 1024);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(5);
+
+ Simulator simulator;
+ LinkSaturator saturator_a(&simulator, "Saturator A", 1000, "Saturator B");
+ LinkSaturator saturator_b(&simulator, "Saturator B", 1000, "Saturator A");
+
+ // Attach packets to the switch to create a delay between the point at which
+ // the packet is generated and the point at which it is filtered. Note that
+ // if the saturators were connected directly, the link would be always
+ // available for the endpoint which has all of its packets dropped, resulting
+ // in saturator looping infinitely.
+ Switch network_switch(&simulator, "Switch", 8,
+ bandwidth * base_propagation_delay * 10);
+ StrictMock<MockPacketFilter> a_to_b_filter(&simulator, "A -> B filter",
+ network_switch.port(1));
+ StrictMock<MockPacketFilter> b_to_a_filter(&simulator, "B -> A filter",
+ network_switch.port(2));
+ SymmetricLink link_a(&a_to_b_filter, &saturator_b, bandwidth,
+ base_propagation_delay);
+ SymmetricLink link_b(&b_to_a_filter, &saturator_a, bandwidth,
+ base_propagation_delay);
+
+ // Allow packets from A to B, but not from B to A.
+ EXPECT_CALL(a_to_b_filter, FilterPacket(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(b_to_a_filter, FilterPacket(_)).WillRepeatedly(Return(false));
+
+ // Run the simulation for a while, and expect that only B will receive any
+ // packets.
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ EXPECT_GE(saturator_b.counter()->packets(), 1u);
+ EXPECT_EQ(saturator_a.counter()->packets(), 0u);
+}
+
+// Set up a traffic policer in one direction that throttles at 25% of link
+// bandwidth, and put two link saturators at each endpoint.
+TEST_F(SimulatorTest, TrafficPolicer) {
+ const QuicBandwidth bandwidth =
+ QuicBandwidth::FromBytesPerSecond(1024 * 1024);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(5);
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
+
+ Simulator simulator;
+ LinkSaturator saturator1(&simulator, "Saturator 1", 1000, "Saturator 2");
+ LinkSaturator saturator2(&simulator, "Saturator 2", 1000, "Saturator 1");
+ Switch network_switch(&simulator, "Switch", 8,
+ bandwidth * base_propagation_delay * 10);
+
+ static const QuicByteCount initial_burst = 1000 * 10;
+ static const QuicByteCount max_bucket_size = 1000 * 100;
+ static const QuicBandwidth target_bandwidth = bandwidth * 0.25;
+ TrafficPolicer policer(&simulator, "Policer", initial_burst, max_bucket_size,
+ target_bandwidth, network_switch.port(2));
+
+ SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
+ base_propagation_delay);
+ SymmetricLink link2(&saturator2, &policer, bandwidth, base_propagation_delay);
+
+ // Ensure the initial burst passes without being dropped at all.
+ bool simulator_result = simulator.RunUntilOrTimeout(
+ [&saturator1]() {
+ return saturator1.bytes_transmitted() == initial_burst;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ saturator1.Pause();
+ simulator_result = simulator.RunUntilOrTimeout(
+ [&saturator2]() {
+ return saturator2.counter()->bytes() == initial_burst;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+ saturator1.Resume();
+
+ // Run for some time so that the initial burst is not visible.
+ const QuicTime::Delta simulation_time = QuicTime::Delta::FromSeconds(10);
+ simulator.RunFor(simulation_time);
+
+ // Ensure we've transmitted the amount of data we expected.
+ for (auto* saturator : {&saturator1, &saturator2}) {
+ test::ExpectApproxEq(bandwidth * simulation_time,
+ saturator->bytes_transmitted(), 0.01f);
+ }
+
+ // Check that only one direction is throttled.
+ test::ExpectApproxEq(saturator1.bytes_transmitted() / 4,
+ saturator2.counter()->bytes(), 0.1f);
+ test::ExpectApproxEq(saturator2.bytes_transmitted(),
+ saturator1.counter()->bytes(), 0.1f);
+}
+
+// Ensure that a larger burst is allowed when the policed saturator exits
+// quiescence.
+TEST_F(SimulatorTest, TrafficPolicerBurst) {
+ const QuicBandwidth bandwidth =
+ QuicBandwidth::FromBytesPerSecond(1024 * 1024);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMilliseconds(5);
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(10);
+
+ Simulator simulator;
+ LinkSaturator saturator1(&simulator, "Saturator 1", 1000, "Saturator 2");
+ LinkSaturator saturator2(&simulator, "Saturator 2", 1000, "Saturator 1");
+ Switch network_switch(&simulator, "Switch", 8,
+ bandwidth * base_propagation_delay * 10);
+
+ const QuicByteCount initial_burst = 1000 * 10;
+ const QuicByteCount max_bucket_size = 1000 * 100;
+ const QuicBandwidth target_bandwidth = bandwidth * 0.25;
+ TrafficPolicer policer(&simulator, "Policer", initial_burst, max_bucket_size,
+ target_bandwidth, network_switch.port(2));
+
+ SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
+ base_propagation_delay);
+ SymmetricLink link2(&saturator2, &policer, bandwidth, base_propagation_delay);
+
+ // Ensure at least one packet is sent on each side.
+ bool simulator_result = simulator.RunUntilOrTimeout(
+ [&saturator1, &saturator2]() {
+ return saturator1.packets_transmitted() > 0 &&
+ saturator2.packets_transmitted() > 0;
+ },
+ timeout);
+ ASSERT_TRUE(simulator_result);
+
+ // Wait until the bucket fills up.
+ saturator1.Pause();
+ saturator2.Pause();
+ simulator.RunFor(1.5f * target_bandwidth.TransferTime(max_bucket_size));
+
+ // Send a burst.
+ saturator1.Resume();
+ simulator.RunFor(bandwidth.TransferTime(max_bucket_size));
+ saturator1.Pause();
+ simulator.RunFor(2 * base_propagation_delay);
+
+ // Expect the burst to pass without losses.
+ test::ExpectApproxEq(saturator1.bytes_transmitted(),
+ saturator2.counter()->bytes(), 0.1f);
+
+ // Expect subsequent traffic to be policed.
+ saturator1.Resume();
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ test::ExpectApproxEq(saturator1.bytes_transmitted() / 4,
+ saturator2.counter()->bytes(), 0.1f);
+}
+
+// Test that the packet aggregation support in queues work.
+TEST_F(SimulatorTest, PacketAggregation) {
+ // Model network where the delays are dominated by transfer delay.
+ const QuicBandwidth bandwidth = QuicBandwidth::FromBytesPerSecond(1000);
+ const QuicTime::Delta base_propagation_delay =
+ QuicTime::Delta::FromMicroseconds(1);
+ const QuicByteCount aggregation_threshold = 1000;
+ const QuicTime::Delta aggregation_timeout = QuicTime::Delta::FromSeconds(30);
+
+ Simulator simulator;
+ LinkSaturator saturator1(&simulator, "Saturator 1", 10, "Saturator 2");
+ LinkSaturator saturator2(&simulator, "Saturator 2", 10, "Saturator 1");
+ Switch network_switch(&simulator, "Switch", 8, 10 * aggregation_threshold);
+
+ // Make links with asymmetric propagation delay so that Saturator 2 only
+ // receives packets addressed to it.
+ SymmetricLink link1(&saturator1, network_switch.port(1), bandwidth,
+ base_propagation_delay);
+ SymmetricLink link2(&saturator2, network_switch.port(2), bandwidth,
+ 2 * base_propagation_delay);
+
+ // Enable aggregation in 1 -> 2 direction.
+ Queue* queue = network_switch.port_queue(2);
+ queue->EnableAggregation(aggregation_threshold, aggregation_timeout);
+
+ // Enable aggregation in 2 -> 1 direction in a way that all packets are larger
+ // than the threshold, so that aggregation is effectively a no-op.
+ network_switch.port_queue(1)->EnableAggregation(5, aggregation_timeout);
+
+ // Fill up the aggregation buffer up to 90% (900 bytes).
+ simulator.RunFor(0.9 * bandwidth.TransferTime(aggregation_threshold));
+ EXPECT_EQ(0u, saturator2.counter()->bytes());
+
+ // Stop sending, ensure that given a timespan much shorter than timeout, the
+ // packets remain in the queue.
+ saturator1.Pause();
+ saturator2.Pause();
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ EXPECT_EQ(0u, saturator2.counter()->bytes());
+ EXPECT_EQ(900u, queue->bytes_queued());
+
+ // Ensure that all packets have reached the saturator not affected by
+ // aggregation. Here, 10 extra bytes account for a misrouted packet in the
+ // beginning.
+ EXPECT_EQ(910u, saturator1.counter()->bytes());
+
+ // Send 500 more bytes. Since the aggregation threshold is 1000 bytes, and
+ // queue already has 900 bytes, 1000 bytes will be send and 400 will be in the
+ // queue.
+ saturator1.Resume();
+ simulator.RunFor(0.5 * bandwidth.TransferTime(aggregation_threshold));
+ saturator1.Pause();
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ EXPECT_EQ(1000u, saturator2.counter()->bytes());
+ EXPECT_EQ(400u, queue->bytes_queued());
+
+ // Actually time out, and cause all of the data to be received.
+ simulator.RunFor(aggregation_timeout);
+ EXPECT_EQ(1400u, saturator2.counter()->bytes());
+ EXPECT_EQ(0u, queue->bytes_queued());
+
+ // Run saturator for a longer time, to ensure that the logic to cancel and
+ // reset alarms works correctly.
+ saturator1.Resume();
+ simulator.RunFor(5.5 * bandwidth.TransferTime(aggregation_threshold));
+ saturator1.Pause();
+ simulator.RunFor(QuicTime::Delta::FromSeconds(10));
+ EXPECT_EQ(6400u, saturator2.counter()->bytes());
+ EXPECT_EQ(500u, queue->bytes_queued());
+
+ // Time out again.
+ simulator.RunFor(aggregation_timeout);
+ EXPECT_EQ(6900u, saturator2.counter()->bytes());
+ EXPECT_EQ(0u, queue->bytes_queued());
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/switch.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/switch.cc
new file mode 100644
index 00000000000..809c7f8d785
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/switch.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cinttypes>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+
+namespace quic {
+namespace simulator {
+
+Switch::Switch(Simulator* simulator,
+ std::string name,
+ SwitchPortNumber port_count,
+ QuicByteCount queue_capacity) {
+ for (size_t port_number = 1; port_number <= port_count; port_number++) {
+ ports_.emplace_back(simulator,
+ QuicStrCat(name, " (port ", port_number, ")"), this,
+ port_number, queue_capacity);
+ }
+}
+
+Switch::~Switch() {}
+
+Switch::Port::Port(Simulator* simulator,
+ std::string name,
+ Switch* parent,
+ SwitchPortNumber port_number,
+ QuicByteCount queue_capacity)
+ : Endpoint(simulator, name),
+ parent_(parent),
+ port_number_(port_number),
+ connected_(false),
+ queue_(simulator,
+ QuicStringPrintf("%s (queue)", name.c_str()),
+ queue_capacity) {}
+
+void Switch::Port::AcceptPacket(std::unique_ptr<Packet> packet) {
+ parent_->DispatchPacket(port_number_, std::move(packet));
+}
+
+void Switch::Port::EnqueuePacket(std::unique_ptr<Packet> packet) {
+ queue_.AcceptPacket(std::move(packet));
+}
+
+UnconstrainedPortInterface* Switch::Port::GetRxPort() {
+ return this;
+}
+
+void Switch::Port::SetTxPort(ConstrainedPortInterface* port) {
+ queue_.set_tx_port(port);
+ connected_ = true;
+}
+
+void Switch::Port::Act() {}
+
+void Switch::DispatchPacket(SwitchPortNumber port_number,
+ std::unique_ptr<Packet> packet) {
+ Port* source_port = &ports_[port_number - 1];
+ const auto source_mapping_it = switching_table_.find(packet->source);
+ if (source_mapping_it == switching_table_.end()) {
+ switching_table_.insert(std::make_pair(packet->source, source_port));
+ }
+
+ const auto destination_mapping_it =
+ switching_table_.find(packet->destination);
+ if (destination_mapping_it != switching_table_.end()) {
+ destination_mapping_it->second->EnqueuePacket(std::move(packet));
+ return;
+ }
+
+ // If no mapping is available yet, broadcast the packet to all ports
+ // different from the source.
+ for (Port& egress_port : ports_) {
+ if (!egress_port.connected()) {
+ continue;
+ }
+ egress_port.EnqueuePacket(QuicMakeUnique<Packet>(*packet));
+ }
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/switch.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/switch.h
new file mode 100644
index 00000000000..6d3efc37423
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/switch.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SWITCH_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SWITCH_H_
+
+#include <deque>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/queue.h"
+
+namespace quic {
+namespace simulator {
+
+typedef size_t SwitchPortNumber;
+
+// Simulates a network switch with simple persistent learning scheme and queues
+// on every output port.
+class Switch {
+ public:
+ Switch(Simulator* simulator,
+ std::string name,
+ SwitchPortNumber port_count,
+ QuicByteCount queue_capacity);
+ Switch(const Switch&) = delete;
+ Switch& operator=(const Switch&) = delete;
+ ~Switch();
+
+ // Returns Endpoint associated with the port under number |port_number|. Just
+ // like on most real switches, port numbering starts with 1.
+ inline Endpoint* port(SwitchPortNumber port_number) {
+ DCHECK_NE(port_number, 0u);
+ return &ports_[port_number - 1];
+ }
+
+ inline Queue* port_queue(SwitchPortNumber port_number) {
+ return ports_[port_number - 1].queue();
+ }
+
+ private:
+ class Port : public Endpoint, public UnconstrainedPortInterface {
+ public:
+ Port(Simulator* simulator,
+ std::string name,
+ Switch* parent,
+ SwitchPortNumber port_number,
+ QuicByteCount queue_capacity);
+ Port(Port&&) = delete;
+ Port(const Port&) = delete;
+ Port& operator=(const Port&) = delete;
+ ~Port() override {}
+
+ // Accepts packet to be routed into the switch.
+ void AcceptPacket(std::unique_ptr<Packet> packet) override;
+ // Enqueue packet to be routed out of the switch.
+ void EnqueuePacket(std::unique_ptr<Packet> packet);
+
+ UnconstrainedPortInterface* GetRxPort() override;
+ void SetTxPort(ConstrainedPortInterface* port) override;
+
+ void Act() override;
+
+ inline bool connected() const { return connected_; }
+ inline Queue* queue() { return &queue_; }
+
+ private:
+ Switch* parent_;
+ SwitchPortNumber port_number_;
+ bool connected_;
+
+ Queue queue_;
+ };
+
+ // Sends the packet to the appropriate port, or to all ports if the
+ // appropriate port is not known.
+ void DispatchPacket(SwitchPortNumber port_number,
+ std::unique_ptr<Packet> packet);
+
+ // This can not be a QuicDeque since pointers into this are
+ // assumed to be stable.
+ std::deque<Port> ports_;
+ QuicUnorderedMap<std::string, Port*> switching_table_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_SWITCH_H_
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.cc b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.cc
new file mode 100644
index 00000000000..fa0bcfb6e59
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h"
+
+#include <algorithm>
+
+namespace quic {
+namespace simulator {
+
+TrafficPolicer::TrafficPolicer(Simulator* simulator,
+ std::string name,
+ QuicByteCount initial_bucket_size,
+ QuicByteCount max_bucket_size,
+ QuicBandwidth target_bandwidth,
+ Endpoint* input)
+ : PacketFilter(simulator, name, input),
+ initial_bucket_size_(initial_bucket_size),
+ max_bucket_size_(max_bucket_size),
+ target_bandwidth_(target_bandwidth),
+ last_refill_time_(clock_->Now()) {}
+
+TrafficPolicer::~TrafficPolicer() {}
+
+void TrafficPolicer::Refill() {
+ QuicTime::Delta time_passed = clock_->Now() - last_refill_time_;
+ QuicByteCount refill_size = time_passed * target_bandwidth_;
+
+ for (auto& bucket : token_buckets_) {
+ bucket.second = std::min(bucket.second + refill_size, max_bucket_size_);
+ }
+
+ last_refill_time_ = clock_->Now();
+}
+
+bool TrafficPolicer::FilterPacket(const Packet& packet) {
+ // Refill existing buckets.
+ Refill();
+
+ // Create a new bucket if one does not exist.
+ if (token_buckets_.count(packet.destination) == 0) {
+ token_buckets_.insert(
+ std::make_pair(packet.destination, initial_bucket_size_));
+ }
+
+ auto bucket = token_buckets_.find(packet.destination);
+ DCHECK(bucket != token_buckets_.end());
+
+ // Silently drop the packet on the floor if out of tokens
+ if (bucket->second < packet.size) {
+ return false;
+ }
+
+ bucket->second -= packet.size;
+ return true;
+}
+
+} // namespace simulator
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h
new file mode 100644
index 00000000000..c2a3e9928a6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/test_tools/simulator/traffic_policer.h
@@ -0,0 +1,54 @@
+// 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 QUICHE_QUIC_TEST_TOOLS_SIMULATOR_TRAFFIC_POLICER_H_
+#define QUICHE_QUIC_TEST_TOOLS_SIMULATOR_TRAFFIC_POLICER_H_
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h"
+#include "net/third_party/quiche/src/quic/test_tools/simulator/port.h"
+
+namespace quic {
+namespace simulator {
+
+// Traffic policer uses a token bucket to limit the bandwidth of the traffic
+// passing through. It wraps around an input port and exposes an output port.
+// Only the traffic from input to the output is policed, so in case when
+// bidirectional policing is desired, two policers have to be used. The flows
+// are hashed by the destination only.
+class TrafficPolicer : public PacketFilter {
+ public:
+ TrafficPolicer(Simulator* simulator,
+ std::string name,
+ QuicByteCount initial_bucket_size,
+ QuicByteCount max_bucket_size,
+ QuicBandwidth target_bandwidth,
+ Endpoint* input);
+ TrafficPolicer(const TrafficPolicer&) = delete;
+ TrafficPolicer& operator=(const TrafficPolicer&) = delete;
+ ~TrafficPolicer() override;
+
+ protected:
+ bool FilterPacket(const Packet& packet) override;
+
+ private:
+ // Refill the token buckets with all the tokens that have been granted since
+ // |last_refill_time_|.
+ void Refill();
+
+ QuicByteCount initial_bucket_size_;
+ QuicByteCount max_bucket_size_;
+ QuicBandwidth target_bandwidth_;
+
+ // The time at which the token buckets were last refilled.
+ QuicTime last_refill_time_;
+
+ // Maps each destination to the number of tokens it has left.
+ QuicUnorderedMap<std::string, QuicByteCount> token_buckets_;
+};
+
+} // namespace simulator
+} // namespace quic
+
+#endif // QUICHE_QUIC_TEST_TOOLS_SIMULATOR_TRAFFIC_POLICER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.cc
new file mode 100644
index 00000000000..7900683913d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+
+namespace quic {
+
+QuicBackendResponse::ServerPushInfo::ServerPushInfo(
+ QuicUrl request_url,
+ spdy::SpdyHeaderBlock headers,
+ spdy::SpdyPriority priority,
+ std::string body)
+ : request_url(request_url),
+ headers(std::move(headers)),
+ priority(priority),
+ body(body) {}
+
+QuicBackendResponse::ServerPushInfo::ServerPushInfo(const ServerPushInfo& other)
+ : request_url(other.request_url),
+ headers(other.headers.Clone()),
+ priority(other.priority),
+ body(other.body) {}
+
+QuicBackendResponse::QuicBackendResponse() : response_type_(REGULAR_RESPONSE) {}
+
+QuicBackendResponse::~QuicBackendResponse() = default;
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h
new file mode 100644
index 00000000000..4ef0fdc1bad
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_backend_response.h
@@ -0,0 +1,84 @@
+// 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_TOOLS_QUIC_BACKEND_RESPONSE_H_
+#define QUICHE_QUIC_TOOLS_QUIC_BACKEND_RESPONSE_H_
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+namespace quic {
+
+// Container for HTTP response header/body pairs
+// fetched by the QuicSimpleServerBackend
+class QuicBackendResponse {
+ public:
+ // A ServerPushInfo contains path of the push request and everything needed in
+ // comprising a response for the push request.
+ struct ServerPushInfo {
+ ServerPushInfo(QuicUrl request_url,
+ spdy::SpdyHeaderBlock headers,
+ spdy::SpdyPriority priority,
+ std::string body);
+ ServerPushInfo(const ServerPushInfo& other);
+
+ QuicUrl request_url;
+ spdy::SpdyHeaderBlock headers;
+ spdy::SpdyPriority priority;
+ std::string body;
+ };
+
+ enum SpecialResponseType {
+ REGULAR_RESPONSE, // Send the headers and body like a server should.
+ CLOSE_CONNECTION, // Close the connection (sending the close packet).
+ IGNORE_REQUEST, // Do nothing, expect the client to time out.
+ BACKEND_ERR_RESPONSE, // There was an error fetching the response from
+ // the backend, for example as a TCP connection
+ // error.
+ INCOMPLETE_RESPONSE, // The server will act as if there is a non-empty
+ // trailer but it will not be sent, as a result, FIN
+ // will not be sent too.
+ STOP_SENDING, // Acts like INCOMPLETE_RESPONSE in that the entire
+ // response is not sent. After sending what is sent,
+ // the server will send a STOP_SENDING.
+ };
+ QuicBackendResponse();
+
+ QuicBackendResponse(const QuicBackendResponse& other) = delete;
+ QuicBackendResponse& operator=(const QuicBackendResponse& other) = delete;
+
+ ~QuicBackendResponse();
+
+ SpecialResponseType response_type() const { return response_type_; }
+ const spdy::SpdyHeaderBlock& headers() const { return headers_; }
+ const spdy::SpdyHeaderBlock& trailers() const { return trailers_; }
+ const QuicStringPiece body() const { return QuicStringPiece(body_); }
+
+ void set_response_type(SpecialResponseType response_type) {
+ response_type_ = response_type;
+ }
+
+ void set_headers(spdy::SpdyHeaderBlock headers) {
+ headers_ = std::move(headers);
+ }
+ void set_trailers(spdy::SpdyHeaderBlock trailers) {
+ trailers_ = std::move(trailers);
+ }
+ void set_body(QuicStringPiece body) {
+ body_.assign(body.data(), body.size());
+ }
+ uint16_t stop_sending_code() const { return stop_sending_code_; }
+ void set_stop_sending_code(uint16_t code) { stop_sending_code_ = code; }
+
+ private:
+ SpecialResponseType response_type_;
+ spdy::SpdyHeaderBlock headers_;
+ spdy::SpdyHeaderBlock trailers_;
+ std::string body_;
+ uint16_t stop_sending_code_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_BACKEND_RESPONSE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc
new file mode 100644
index 00000000000..b736c71aef4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_client_session.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace quic {
+
+QuicClient::QuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicClient(
+ server_address,
+ server_id,
+ supported_versions,
+ QuicConfig(),
+ epoll_server,
+ QuicWrapUnique(new QuicClientEpollNetworkHelper(epoll_server, this)),
+ std::move(proof_verifier)) {}
+
+QuicClient::QuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicClient(server_address,
+ server_id,
+ supported_versions,
+ QuicConfig(),
+ epoll_server,
+ std::move(network_helper),
+ std::move(proof_verifier)) {}
+
+QuicClient::QuicClient(
+ QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicSpdyClientBase(
+ server_id,
+ supported_versions,
+ config,
+ new QuicEpollConnectionHelper(epoll_server, QuicAllocator::SIMPLE),
+ new QuicEpollAlarmFactory(epoll_server),
+ std::move(network_helper),
+ std::move(proof_verifier)) {
+ set_server_address(server_address);
+}
+
+QuicClient::~QuicClient() = default;
+
+std::unique_ptr<QuicSession> QuicClient::CreateQuicClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) {
+ return QuicMakeUnique<QuicSimpleClientSession>(
+ *config(), supported_versions, connection, server_id(), crypto_config(),
+ push_promise_index(), drop_response_body_);
+}
+
+QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() {
+ return static_cast<QuicClientEpollNetworkHelper*>(network_helper());
+}
+
+const QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() const {
+ return static_cast<const QuicClientEpollNetworkHelper*>(network_helper());
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client.h b/chromium/net/third_party/quiche/src/quic/tools/quic_client.h
new file mode 100644
index 00000000000..b84c597969a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A toy client, which connects to a specified port and sends QUIC
+// request to that endpoint.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_CLIENT_H_
+#define QUICHE_QUIC_TOOLS_QUIC_CLIENT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h"
+#include "net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h"
+
+namespace quic {
+
+class QuicServerId;
+
+namespace test {
+class QuicClientPeer;
+} // namespace test
+
+class QuicClient : public QuicSpdyClientBase {
+ public:
+ // This will create its own QuicClientEpollNetworkHelper.
+ QuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ // This will take ownership of a passed in network primitive.
+ QuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicClient(QuicSocketAddress server_address,
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicEpollServer* epoll_server,
+ std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicClient(const QuicClient&) = delete;
+ QuicClient& operator=(const QuicClient&) = delete;
+
+ ~QuicClient() override;
+
+ std::unique_ptr<QuicSession> CreateQuicClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) override;
+
+ // Exposed for the quic client test.
+ int GetLatestFD() const { return epoll_network_helper()->GetLatestFD(); }
+
+ QuicClientEpollNetworkHelper* epoll_network_helper();
+ const QuicClientEpollNetworkHelper* epoll_network_helper() const;
+
+ void set_drop_response_body(bool drop_response_body) {
+ drop_response_body_ = drop_response_body;
+ }
+
+ private:
+ friend class test::QuicClientPeer;
+ bool drop_response_body_ = false;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_CLIENT_H_
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
new file mode 100644
index 00000000000..db0503c5f9b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.cc
@@ -0,0 +1,353 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_client_base.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+QuicClientBase::NetworkHelper::~NetworkHelper() = default;
+
+QuicClientBase::QuicClientBase(
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<NetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : server_id_(server_id),
+ initialized_(false),
+ local_port_(0),
+ config_(config),
+ crypto_config_(std::move(proof_verifier),
+ TlsClientHandshaker::CreateSslCtx()),
+ helper_(helper),
+ alarm_factory_(alarm_factory),
+ supported_versions_(supported_versions),
+ initial_max_packet_length_(0),
+ num_stateless_rejects_received_(0),
+ num_sent_client_hellos_(0),
+ connection_error_(QUIC_NO_ERROR),
+ connected_or_attempting_connect_(false),
+ network_helper_(std::move(network_helper)) {}
+
+QuicClientBase::~QuicClientBase() = default;
+
+bool QuicClientBase::Initialize() {
+ num_sent_client_hellos_ = 0;
+ num_stateless_rejects_received_ = 0;
+ connection_error_ = QUIC_NO_ERROR;
+ connected_or_attempting_connect_ = false;
+
+ // If an initial flow control window has not explicitly been set, then use the
+ // same values that Chrome uses.
+ const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB
+ const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB
+ if (config()->GetInitialStreamFlowControlWindowToSend() ==
+ kMinimumFlowControlSendWindow) {
+ config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
+ }
+ if (config()->GetInitialSessionFlowControlWindowToSend() ==
+ kMinimumFlowControlSendWindow) {
+ config()->SetInitialSessionFlowControlWindowToSend(
+ kSessionMaxRecvWindowSize);
+ }
+
+ if (!network_helper_->CreateUDPSocketAndBind(server_address_,
+ bind_to_address_, local_port_)) {
+ return false;
+ }
+
+ initialized_ = true;
+ return true;
+}
+
+bool QuicClientBase::Connect() {
+ // Attempt multiple connects until the maximum number of client hellos have
+ // been sent.
+ while (!connected() &&
+ GetNumSentClientHellos() <= QuicCryptoClientStream::kMaxClientHellos) {
+ StartConnect();
+ while (EncryptionBeingEstablished()) {
+ WaitForEvents();
+ }
+ if (GetQuicReloadableFlag(enable_quic_stateless_reject_support) &&
+ connected()) {
+ // Resend any previously queued data.
+ ResendSavedData();
+ }
+ ParsedQuicVersion version = UnsupportedQuicVersion();
+ if (session() != nullptr &&
+ session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT &&
+ !CanReconnectWithDifferentVersion(&version)) {
+ // We've successfully created a session but we're not connected, and there
+ // is no stateless reject to recover from and cannot try to reconnect with
+ // different version. Give up trying.
+ break;
+ }
+ }
+ if (!connected() &&
+ GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos &&
+ session() != nullptr &&
+ session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
+ // The overall connection failed due too many stateless rejects.
+ set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS);
+ }
+ return session()->connection()->connected();
+}
+
+void QuicClientBase::StartConnect() {
+ DCHECK(initialized_);
+ DCHECK(!connected());
+ QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
+ ParsedQuicVersion mutual_version = UnsupportedQuicVersion();
+ const bool can_reconnect_with_different_version =
+ CanReconnectWithDifferentVersion(&mutual_version);
+ if (connected_or_attempting_connect()) {
+ // If the last error was not a stateless reject, then the queued up data
+ // does not need to be resent.
+ // Keep queued up data if client can try to connect with a different
+ // version.
+ if (session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT &&
+ !can_reconnect_with_different_version) {
+ ClearDataToResend();
+ }
+ // Before we destroy the last session and create a new one, gather its stats
+ // and update the stats for the overall connection.
+ UpdateStats();
+ }
+
+ session_ = CreateQuicClientSession(
+ supported_versions(),
+ new QuicConnection(GetNextConnectionId(), server_address(), helper(),
+ alarm_factory(), writer,
+ /* owns_writer= */ false, Perspective::IS_CLIENT,
+ can_reconnect_with_different_version
+ ? ParsedQuicVersionVector{mutual_version}
+ : supported_versions()));
+ if (initial_max_packet_length_ != 0) {
+ session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
+ }
+ // Reset |writer()| after |session()| so that the old writer outlives the old
+ // session.
+ set_writer(writer);
+ InitializeSession();
+ set_connected_or_attempting_connect(true);
+}
+
+void QuicClientBase::InitializeSession() {
+ session()->Initialize();
+}
+
+void QuicClientBase::Disconnect() {
+ DCHECK(initialized_);
+
+ initialized_ = false;
+ if (connected()) {
+ session()->connection()->CloseConnection(
+ QUIC_PEER_GOING_AWAY, "Client disconnecting",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ ClearDataToResend();
+
+ network_helper_->CleanUpAllUDPSockets();
+}
+
+ProofVerifier* QuicClientBase::proof_verifier() const {
+ return crypto_config_.proof_verifier();
+}
+
+bool QuicClientBase::EncryptionBeingEstablished() {
+ return !session_->IsEncryptionEstablished() &&
+ session_->connection()->connected();
+}
+
+bool QuicClientBase::WaitForEvents() {
+ DCHECK(connected());
+
+ network_helper_->RunEventLoop();
+
+ DCHECK(session() != nullptr);
+ ParsedQuicVersion version = UnsupportedQuicVersion();
+ if (!connected() &&
+ (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT ||
+ CanReconnectWithDifferentVersion(&version))) {
+ if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
+ DCHECK(GetQuicReloadableFlag(enable_quic_stateless_reject_support));
+ QUIC_DLOG(INFO) << "Detected stateless reject while waiting for events. "
+ << "Attempting to reconnect.";
+ } else {
+ QUIC_DLOG(INFO) << "Can reconnect with version: " << version
+ << ", attempting to reconnect.";
+ }
+ Connect();
+ }
+
+ return HasActiveRequests();
+}
+
+bool QuicClientBase::MigrateSocket(const QuicIpAddress& new_host) {
+ return MigrateSocketWithSpecifiedPort(new_host, local_port_);
+}
+
+bool QuicClientBase::MigrateSocketWithSpecifiedPort(
+ const QuicIpAddress& new_host,
+ int port) {
+ if (!connected()) {
+ return false;
+ }
+
+ network_helper_->CleanUpAllUDPSockets();
+
+ set_bind_to_address(new_host);
+ if (!network_helper_->CreateUDPSocketAndBind(server_address_,
+ bind_to_address_, port)) {
+ return false;
+ }
+
+ session()->connection()->SetSelfAddress(
+ network_helper_->GetLatestClientAddress());
+
+ QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
+ set_writer(writer);
+ session()->connection()->SetQuicPacketWriter(writer, false);
+
+ return true;
+}
+
+bool QuicClientBase::ChangeEphemeralPort() {
+ auto current_host = network_helper_->GetLatestClientAddress().host();
+ return MigrateSocketWithSpecifiedPort(current_host, 0 /*any ephemeral port*/);
+}
+
+QuicSession* QuicClientBase::session() {
+ return session_.get();
+}
+
+QuicClientBase::NetworkHelper* QuicClientBase::network_helper() {
+ return network_helper_.get();
+}
+
+const QuicClientBase::NetworkHelper* QuicClientBase::network_helper() const {
+ return network_helper_.get();
+}
+
+void QuicClientBase::WaitForStreamToClose(QuicStreamId id) {
+ DCHECK(connected());
+
+ while (connected() && !session_->IsClosedStream(id)) {
+ WaitForEvents();
+ }
+}
+
+bool QuicClientBase::WaitForCryptoHandshakeConfirmed() {
+ DCHECK(connected());
+
+ while (connected() && !session_->IsCryptoHandshakeConfirmed()) {
+ WaitForEvents();
+ }
+
+ // If the handshake fails due to a timeout, the connection will be closed.
+ QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed.";
+ return connected();
+}
+
+bool QuicClientBase::connected() const {
+ return session_.get() && session_->connection() &&
+ session_->connection()->connected();
+}
+
+bool QuicClientBase::goaway_received() const {
+ return session_ != nullptr && session_->goaway_received();
+}
+
+int QuicClientBase::GetNumSentClientHellos() {
+ // If we are not actively attempting to connect, the session object
+ // corresponds to the previous connection and should not be used.
+ const int current_session_hellos = !connected_or_attempting_connect_
+ ? 0
+ : GetNumSentClientHellosFromSession();
+ return num_sent_client_hellos_ + current_session_hellos;
+}
+
+void QuicClientBase::UpdateStats() {
+ num_sent_client_hellos_ += GetNumSentClientHellosFromSession();
+ if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
+ ++num_stateless_rejects_received_;
+ }
+}
+
+int QuicClientBase::GetNumReceivedServerConfigUpdates() {
+ // If we are not actively attempting to connect, the session object
+ // corresponds to the previous connection and should not be used.
+ // We do not need to take stateless rejects into account, since we
+ // don't expect any scup messages to be sent during a
+ // statelessly-rejected connection.
+ return !connected_or_attempting_connect_
+ ? 0
+ : GetNumReceivedServerConfigUpdatesFromSession();
+}
+
+QuicErrorCode QuicClientBase::connection_error() const {
+ // Return the high-level error if there was one. Otherwise, return the
+ // connection error from the last session.
+ if (connection_error_ != QUIC_NO_ERROR) {
+ return connection_error_;
+ }
+ if (session_ == nullptr) {
+ return QUIC_NO_ERROR;
+ }
+ return session_->error();
+}
+
+QuicConnectionId QuicClientBase::GetNextConnectionId() {
+ QuicConnectionId server_designated_id = GetNextServerDesignatedConnectionId();
+ return !server_designated_id.IsEmpty() ? server_designated_id
+ : GenerateNewConnectionId();
+}
+
+QuicConnectionId QuicClientBase::GetNextServerDesignatedConnectionId() {
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id_);
+ // If the cached state indicates that we should use a server-designated
+ // connection ID, then return that connection ID.
+ CHECK(cached != nullptr) << "QuicClientCryptoConfig::LookupOrCreate returned "
+ << "unexpected nullptr.";
+ return cached->has_server_designated_connection_id()
+ ? cached->GetNextServerDesignatedConnectionId()
+ : EmptyQuicConnectionId();
+}
+
+QuicConnectionId QuicClientBase::GenerateNewConnectionId() {
+ return QuicUtils::CreateRandomConnectionId();
+}
+
+bool QuicClientBase::CanReconnectWithDifferentVersion(
+ ParsedQuicVersion* version) const {
+ if (session_ == nullptr || session_->connection() == nullptr ||
+ session_->error() != QUIC_INVALID_VERSION ||
+ session_->connection()->server_supported_versions().empty()) {
+ return false;
+ }
+ for (const auto& client_version : supported_versions_) {
+ if (QuicContainsValue(session_->connection()->server_supported_versions(),
+ client_version)) {
+ *version = client_version;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..6ad326eb4dc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_base.h
@@ -0,0 +1,364 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A base class for the toy client, which connects to a specified port and sends
+// QUIC request to that endpoint.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_
+#define QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_macros.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class ProofVerifier;
+class QuicServerId;
+
+// QuicClientBase handles establishing a connection to the passed in
+// server id, including ensuring that it supports the passed in versions
+// and config.
+// Subclasses derived from this class are responsible for creating the
+// actual QuicSession instance, as well as defining functions that
+// create and run the underlying network transport.
+class QuicClientBase {
+ public:
+ // An interface to various network events that the QuicClient will need to
+ // interact with.
+ class NetworkHelper {
+ public:
+ virtual ~NetworkHelper();
+
+ // Runs one iteration of the event loop.
+ virtual void RunEventLoop() = 0;
+
+ // Used during initialization: creates the UDP socket FD, sets socket
+ // options, and binds the socket to our address.
+ virtual bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
+ QuicIpAddress bind_to_address,
+ int bind_to_port) = 0;
+
+ // Unregister and close all open UDP sockets.
+ virtual void CleanUpAllUDPSockets() = 0;
+
+ // If the client has at least one UDP socket, return address of the latest
+ // created one. Otherwise, return an empty socket address.
+ virtual QuicSocketAddress GetLatestClientAddress() const = 0;
+
+ // Creates a packet writer to be used for the next connection.
+ virtual QuicPacketWriter* CreateQuicPacketWriter() = 0;
+ };
+
+ QuicClientBase(const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<NetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicClientBase(const QuicClientBase&) = delete;
+ QuicClientBase& operator=(const QuicClientBase&) = delete;
+
+ virtual ~QuicClientBase();
+
+ // Initializes the client to create a connection. Should be called exactly
+ // once before calling StartConnect or Connect. Returns true if the
+ // initialization succeeds, false otherwise.
+ virtual bool Initialize();
+
+ // "Connect" to the QUIC server, including performing synchronous crypto
+ // handshake.
+ bool Connect();
+
+ // Start the crypto handshake. This can be done in place of the synchronous
+ // Connect(), but callers are responsible for making sure the crypto handshake
+ // completes.
+ void StartConnect();
+
+ // Calls session()->Initialize(). Subclasses may override this if any extra
+ // initialization needs to be done. Subclasses should expect that session()
+ // is non-null and valid.
+ virtual void InitializeSession();
+
+ // Disconnects from the QUIC server.
+ void Disconnect();
+
+ // Returns true if the crypto handshake has yet to establish encryption.
+ // Returns false if encryption is active (even if the server hasn't confirmed
+ // the handshake) or if the connection has been closed.
+ bool EncryptionBeingEstablished();
+
+ // Wait for events until the stream with the given ID is closed.
+ void WaitForStreamToClose(QuicStreamId id);
+
+ // Wait for events until the handshake is confirmed.
+ // Returns true if the crypto handshake succeeds, false otherwise.
+ bool WaitForCryptoHandshakeConfirmed() QUIC_MUST_USE_RESULT;
+
+ // Wait up to 50ms, and handle any events which occur.
+ // Returns true if there are any outstanding requests.
+ bool WaitForEvents();
+
+ // Migrate to a new socket (new_host) during an active connection.
+ bool MigrateSocket(const QuicIpAddress& new_host);
+
+ // Migrate to a new socket (new_host, port) during an active connection.
+ bool MigrateSocketWithSpecifiedPort(const QuicIpAddress& new_host, int port);
+
+ // Open a new socket to change to a new ephemeral port.
+ bool ChangeEphemeralPort();
+
+ QuicSession* session();
+
+ bool connected() const;
+ bool goaway_received() const;
+
+ const QuicServerId& server_id() const { return server_id_; }
+
+ // This should only be set before the initial Connect()
+ void set_server_id(const QuicServerId& server_id) { server_id_ = server_id; }
+
+ void SetUserAgentID(const std::string& user_agent_id) {
+ crypto_config_.set_user_agent_id(user_agent_id);
+ }
+
+ // SetChannelIDSource sets a ChannelIDSource that will be called, when the
+ // server supports channel IDs, to obtain a channel ID for signing a message
+ // proving possession of the channel ID.
+ void SetChannelIDSource(std::unique_ptr<ChannelIDSource> source) {
+ crypto_config_.SetChannelIDSource(std::move(source));
+ }
+
+ const ParsedQuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+
+ void SetSupportedVersions(const ParsedQuicVersionVector& versions) {
+ supported_versions_ = versions;
+ }
+
+ QuicConfig* config() { return &config_; }
+
+ QuicCryptoClientConfig* crypto_config() { return &crypto_config_; }
+
+ // Change the initial maximum packet size of the connection. Has to be called
+ // before Connect()/StartConnect() in order to have any effect.
+ void set_initial_max_packet_length(QuicByteCount initial_max_packet_length) {
+ initial_max_packet_length_ = initial_max_packet_length;
+ }
+
+ int num_stateless_rejects_received() const {
+ return num_stateless_rejects_received_;
+ }
+
+ // The number of client hellos sent, taking stateless rejects into
+ // account. In the case of a stateless reject, the initial
+ // connection object may be torn down and a new one created. The
+ // user cannot rely upon the latest connection object to get the
+ // total number of client hellos sent, and should use this function
+ // instead.
+ int GetNumSentClientHellos();
+
+ // Gather the stats for the last session and update the stats for the overall
+ // connection.
+ void UpdateStats();
+
+ // The number of server config updates received. We assume no
+ // updates can be sent during a previously, statelessly rejected
+ // connection, so only the latest session is taken into account.
+ int GetNumReceivedServerConfigUpdates();
+
+ // Returns any errors that occurred at the connection-level (as
+ // opposed to the session-level). When a stateless reject occurs,
+ // the error of the last session may not reflect the overall state
+ // of the connection.
+ QuicErrorCode connection_error() const;
+ void set_connection_error(QuicErrorCode connection_error) {
+ connection_error_ = connection_error;
+ }
+
+ bool connected_or_attempting_connect() const {
+ return connected_or_attempting_connect_;
+ }
+ void set_connected_or_attempting_connect(
+ bool connected_or_attempting_connect) {
+ connected_or_attempting_connect_ = connected_or_attempting_connect;
+ }
+
+ QuicPacketWriter* writer() { return writer_.get(); }
+ void set_writer(QuicPacketWriter* writer) {
+ if (writer_.get() != writer) {
+ writer_.reset(writer);
+ }
+ }
+
+ void reset_writer() { writer_.reset(); }
+
+ ProofVerifier* proof_verifier() const;
+
+ void set_bind_to_address(QuicIpAddress address) {
+ bind_to_address_ = address;
+ }
+
+ QuicIpAddress bind_to_address() const { return bind_to_address_; }
+
+ void set_local_port(int local_port) { local_port_ = local_port; }
+
+ int local_port() const { return local_port_; }
+
+ const QuicSocketAddress& server_address() const { return server_address_; }
+
+ void set_server_address(const QuicSocketAddress& server_address) {
+ server_address_ = server_address;
+ }
+
+ QuicConnectionHelperInterface* helper() { return helper_.get(); }
+
+ NetworkHelper* network_helper();
+ const NetworkHelper* network_helper() const;
+
+ bool initialized() const { return initialized_; }
+
+ void SetPreSharedKey(QuicStringPiece key) {
+ crypto_config_.set_pre_shared_key(key);
+ }
+
+ protected:
+ // TODO(rch): Move GetNumSentClientHellosFromSession and
+ // GetNumReceivedServerConfigUpdatesFromSession into a new/better
+ // QuicSpdyClientSession class. The current inherits dependencies from
+ // Spdy. When that happens this class and all its subclasses should
+ // work with QuicSpdyClientSession instead of QuicSession.
+ // That will obviate the need for the pure virtual functions below.
+
+ // Extract the number of sent client hellos from the session.
+ virtual int GetNumSentClientHellosFromSession() = 0;
+
+ // The number of server config updates received. We assume no
+ // updates can be sent during a previously, statelessly rejected
+ // connection, so only the latest session is taken into account.
+ virtual int GetNumReceivedServerConfigUpdatesFromSession() = 0;
+
+ // If this client supports buffering data, resend it.
+ virtual void ResendSavedData() = 0;
+
+ // If this client supports buffering data, clear it.
+ virtual void ClearDataToResend() = 0;
+
+ // Takes ownership of |connection|. If you override this function,
+ // you probably want to call ResetSession() in your destructor.
+ // TODO(rch): Change the connection parameter to take in a
+ // std::unique_ptr<QuicConnection> instead.
+ virtual std::unique_ptr<QuicSession> CreateQuicClientSession(
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) = 0;
+
+ // Generates the next ConnectionId for |server_id_|. By default, if the
+ // cached server config contains a server-designated ID, that ID will be
+ // returned. Otherwise, the next random ID will be returned.
+ QuicConnectionId GetNextConnectionId();
+
+ // Returns the next server-designated ConnectionId from the cached config for
+ // |server_id_|, if it exists. Otherwise, returns 0.
+ QuicConnectionId GetNextServerDesignatedConnectionId();
+
+ // Generates a new, random connection ID (as opposed to a server-designated
+ // connection ID).
+ virtual QuicConnectionId GenerateNewConnectionId();
+
+ QuicAlarmFactory* alarm_factory() { return alarm_factory_.get(); }
+
+ // Subclasses may need to explicitly clear the session on destruction
+ // if they create it with objects that will be destroyed before this is.
+ // You probably want to call this if you override CreateQuicSpdyClientSession.
+ void ResetSession() { session_.reset(); }
+
+ // Returns true if the corresponding of this client has active requests.
+ virtual bool HasActiveRequests() = 0;
+
+ private:
+ // Returns true and set |version| if client can reconnect with a different
+ // version.
+ bool CanReconnectWithDifferentVersion(ParsedQuicVersion* version) const;
+
+ // |server_id_| is a tuple (hostname, port, is_https) of the server.
+ QuicServerId server_id_;
+
+ // Tracks if the client is initialized to connect.
+ bool initialized_;
+
+ // Address of the server.
+ QuicSocketAddress server_address_;
+
+ // If initialized, the address to bind to.
+ QuicIpAddress bind_to_address_;
+
+ // Local port to bind to. Initialize to 0.
+ int local_port_;
+
+ // config_ and crypto_config_ contain configuration and cached state about
+ // servers.
+ QuicConfig config_;
+ QuicCryptoClientConfig crypto_config_;
+
+ // Helper to be used by created connections. Must outlive |session_|.
+ std::unique_ptr<QuicConnectionHelperInterface> helper_;
+
+ // Alarm factory to be used by created connections. Must outlive |session_|.
+ std::unique_ptr<QuicAlarmFactory> alarm_factory_;
+
+ // Writer used to actually send packets to the wire. Must outlive |session_|.
+ std::unique_ptr<QuicPacketWriter> writer_;
+
+ // Session which manages streams.
+ std::unique_ptr<QuicSession> session_;
+
+ // This vector contains QUIC versions which we currently support.
+ // This should be ordered such that the highest supported version is the first
+ // element, with subsequent elements in descending order (versions can be
+ // skipped as necessary). We will always pick supported_versions_[0] as the
+ // initial version to use.
+ ParsedQuicVersionVector supported_versions_;
+
+ // The initial value of maximum packet size of the connection. If set to
+ // zero, the default is used.
+ QuicByteCount initial_max_packet_length_;
+
+ // The number of stateless rejects received during the current/latest
+ // connection.
+ // TODO(jokulik): Consider some consistent naming scheme (or other) for member
+ // variables that are kept per-request, per-connection, and over the client's
+ // lifetime.
+ int num_stateless_rejects_received_;
+
+ // The number of hellos sent during the current/latest connection.
+ int num_sent_client_hellos_;
+
+ // Used to store any errors that occurred with the overall connection (as
+ // opposed to that associated with the last session object).
+ QuicErrorCode connection_error_;
+
+ // True when the client is attempting to connect or re-connect the session (in
+ // the case of a stateless reject). Set to false between a call to
+ // Disconnect() and the subsequent call to StartConnect(). When
+ // connected_or_attempting_connect_ is false, the session object corresponds
+ // to the previous client-level connection.
+ bool connected_or_attempting_connect_;
+
+ // The network helper used to create sockets and manage the event loop.
+ // Not owned by this class.
+ std::unique_ptr<NetworkHelper> network_helper_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_bin.cc
new file mode 100644
index 00000000000..ee8262f751f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_bin.cc
@@ -0,0 +1,403 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A binary wrapper for QuicClient.
+// Connects to a host using QUIC, sends a request to the provided URL, and
+// displays the response.
+//
+// Some usage examples:
+//
+// Standard request/response:
+// quic_client www.google.com
+// quic_client www.google.com --quiet
+// quic_client www.google.com --port=443
+//
+// Use a specific version:
+// quic_client www.google.com --quic_version=23
+//
+// Send a POST instead of a GET:
+// quic_client www.google.com --body="this is a POST body"
+//
+// Append additional headers to the request:
+// quic_client www.google.com --headers="Header-A: 1234; Header-B: 5678"
+//
+// Connect to a host different to the URL being requested:
+// quic_client mail.google.com --host=www.google.com
+//
+// Connect to a specific IP:
+// IP=`dig www.google.com +short | head -1`
+// quic_client www.google.com --host=${IP}
+//
+// Send repeated requests and change ephemeral port between requests
+// quic_client www.google.com --num_requests=10
+//
+// Try to connect to a host which does not speak QUIC:
+// quic_client www.example.com
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+namespace {
+
+using quic::QuicSocketAddress;
+using quic::QuicStringPiece;
+using quic::QuicTextUtils;
+using quic::QuicUrl;
+
+class FakeProofVerifier : public quic::ProofVerifier {
+ public:
+ ~FakeProofVerifier() override {}
+ quic::QuicAsyncStatus VerifyProof(
+ const std::string& /*hostname*/,
+ const uint16_t /*port*/,
+ const std::string& /*server_config*/,
+ quic::QuicTransportVersion /*quic_version*/,
+ quic::QuicStringPiece /*chlo_hash*/,
+ const std::vector<std::string>& /*certs*/,
+ const std::string& /*cert_sct*/,
+ const std::string& /*signature*/,
+ const quic::ProofVerifyContext* /*context*/,
+ std::string* /*error_details*/,
+ std::unique_ptr<quic::ProofVerifyDetails>* /*details*/,
+ std::unique_ptr<quic::ProofVerifierCallback> /*callback*/) override {
+ return quic::QUIC_SUCCESS;
+ }
+ quic::QuicAsyncStatus VerifyCertChain(
+ const std::string& /*hostname*/,
+ const std::vector<std::string>& /*certs*/,
+ const quic::ProofVerifyContext* /*context*/,
+ std::string* /*error_details*/,
+ std::unique_ptr<quic::ProofVerifyDetails>* /*details*/,
+ std::unique_ptr<quic::ProofVerifierCallback> /*callback*/) override {
+ return quic::QUIC_SUCCESS;
+ }
+ std::unique_ptr<quic::ProofVerifyContext> CreateDefaultContext() override {
+ return nullptr;
+ }
+};
+
+QuicSocketAddress LookupAddress(std::string host, std::string port) {
+ addrinfo hint;
+ memset(&hint, 0, sizeof(hint));
+ hint.ai_protocol = IPPROTO_UDP;
+
+ addrinfo* info_list = nullptr;
+ int result = getaddrinfo(host.c_str(), port.c_str(), &hint, &info_list);
+ if (result != 0) {
+ QUIC_LOG(ERROR) << "Failed to look up " << host << ": "
+ << gai_strerror(result);
+ return QuicSocketAddress();
+ }
+
+ CHECK(info_list != nullptr);
+ std::unique_ptr<addrinfo, void (*)(addrinfo*)> info_list_owned(info_list,
+ freeaddrinfo);
+ return QuicSocketAddress(*info_list->ai_addr);
+}
+
+} // namespace
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ host,
+ "",
+ "The IP or hostname to connect to. If not provided, the host "
+ "will be derived from the provided URL.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, 0, "The port to connect to.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(std::string,
+ body,
+ "",
+ "If set, send a POST with this body.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ body_hex,
+ "",
+ "If set, contents are converted from hex to ascii, before "
+ "sending as body of a POST. e.g. --body_hex=\"68656c6c6f\"");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ headers,
+ "",
+ "A semicolon separated list of key:value pairs to "
+ "add to request headers.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(bool,
+ quiet,
+ false,
+ "Set to true for a quieter output experience.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ quic_version,
+ "",
+ "QUIC version to speak, e.g. 21. If not set, then all available "
+ "versions are offered in the handshake. Also supports wire versions "
+ "such as Q043 or T099.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ int32_t,
+ quic_ietf_draft,
+ 0,
+ "QUIC IETF draft number to use over the wire, e.g. 18. "
+ "By default this sets quic_version to T099. "
+ "This also enables required internal QUIC flags.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ bool,
+ version_mismatch_ok,
+ false,
+ "If true, a version mismatch in the handshake is not considered a "
+ "failure. Useful for probing a server to determine if it speaks "
+ "any version of QUIC.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ bool,
+ redirect_is_success,
+ true,
+ "If true, an HTTP response code of 3xx is considered to be a "
+ "successful response, otherwise a failure.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
+ initial_mtu,
+ 0,
+ "Initial MTU of the connection.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ int32_t,
+ num_requests,
+ 1,
+ "How many sequential requests to make on a single connection.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(bool,
+ disable_certificate_verification,
+ false,
+ "If true, don't verify the server certificate.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ bool,
+ drop_response_body,
+ false,
+ "If true, drop response body immediately after it is received.");
+
+int main(int argc, char* argv[]) {
+ QuicSystemEventLoop event_loop("quic_client");
+ const char* usage = "Usage: quic_client [options] <url>";
+
+ // All non-flag arguments should be interpreted as URLs to fetch.
+ std::vector<std::string> urls =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+ if (urls.size() != 1) {
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ exit(0);
+ }
+
+ QuicUrl url(urls[0], "https");
+ std::string host = GetQuicFlag(FLAGS_host);
+ if (host.empty()) {
+ host = url.host();
+ }
+ int port = GetQuicFlag(FLAGS_port);
+ if (port == 0) {
+ port = url.port();
+ }
+
+ // Determine IP address to connect to from supplied hostname.
+ QuicSocketAddress addr = LookupAddress(host, quic::QuicStrCat(port));
+ if (!addr.IsInitialized()) {
+ return 1;
+ }
+ std::cerr << "Resolved " << url.ToString() << " to " << addr.ToString()
+ << std::endl;
+
+ // Build the client, and try to connect.
+ quic::QuicEpollServer epoll_server;
+ quic::QuicServerId server_id(url.host(), port, false);
+ quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions();
+
+ std::string quic_version_string = GetQuicFlag(FLAGS_quic_version);
+ const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft);
+ if (quic_ietf_draft > 0) {
+ quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft);
+ if (quic_version_string.length() == 0) {
+ quic_version_string = "T099";
+ }
+ }
+ if (quic_version_string.length() > 0) {
+ if (quic_version_string[0] == 'T') {
+ // ParseQuicVersionString checks quic_supports_tls_handshake.
+ SetQuicFlag(&FLAGS_quic_supports_tls_handshake, true);
+ }
+ quic::ParsedQuicVersion parsed_quic_version =
+ quic::ParseQuicVersionString(quic_version_string);
+ if (parsed_quic_version.transport_version ==
+ quic::QUIC_VERSION_UNSUPPORTED) {
+ return 1;
+ }
+ versions.clear();
+ versions.push_back(parsed_quic_version);
+ quic::QuicEnableVersion(parsed_quic_version);
+ }
+
+ const int32_t num_requests(GetQuicFlag(FLAGS_num_requests));
+ std::unique_ptr<quic::ProofVerifier> proof_verifier;
+ if (GetQuicFlag(FLAGS_disable_certificate_verification)) {
+ proof_verifier = quic::QuicMakeUnique<FakeProofVerifier>();
+ } else {
+ proof_verifier = quic::CreateDefaultProofVerifier();
+ }
+ quic::QuicClient client(addr, server_id, versions, &epoll_server,
+ std::move(proof_verifier));
+ int32_t initial_mtu = GetQuicFlag(FLAGS_initial_mtu);
+ client.set_initial_max_packet_length(
+ initial_mtu != 0 ? initial_mtu : quic::kDefaultMaxPacketSize);
+ client.set_drop_response_body(GetQuicFlag(FLAGS_drop_response_body));
+ if (!client.Initialize()) {
+ std::cerr << "Failed to initialize client." << std::endl;
+ return 1;
+ }
+ if (!client.Connect()) {
+ quic::QuicErrorCode error = client.session()->error();
+ if (error == quic::QUIC_INVALID_VERSION) {
+ std::cerr << "Server talks QUIC, but none of the versions supported by "
+ << "this client: " << ParsedQuicVersionVectorToString(versions)
+ << std::endl;
+ // 0: No error.
+ // 20: Failed to connect due to QUIC_INVALID_VERSION.
+ return GetQuicFlag(FLAGS_version_mismatch_ok) ? 0 : 20;
+ }
+ std::cerr << "Failed to connect to " << addr.ToString()
+ << ". Error: " << quic::QuicErrorCodeToString(error) << std::endl;
+ return 1;
+ }
+ std::cerr << "Connected to " << addr.ToString() << std::endl;
+
+ // Construct the string body from flags, if provided.
+ std::string body = GetQuicFlag(FLAGS_body);
+ if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+ DCHECK(GetQuicFlag(FLAGS_body).empty())
+ << "Only set one of --body and --body_hex.";
+ body = QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex));
+ }
+
+ // Construct a GET or POST request for supplied URL.
+ spdy::SpdyHeaderBlock header_block;
+ header_block[":method"] = body.empty() ? "GET" : "POST";
+ header_block[":scheme"] = url.scheme();
+ header_block[":authority"] = url.HostPort();
+ header_block[":path"] = url.PathParamsQuery();
+
+ // Append any additional headers supplied on the command line.
+ for (QuicStringPiece sp :
+ QuicTextUtils::Split(GetQuicFlag(FLAGS_headers), ';')) {
+ QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&sp);
+ if (sp.empty()) {
+ continue;
+ }
+ std::vector<QuicStringPiece> kv = QuicTextUtils::Split(sp, ':');
+ QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[0]);
+ QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[1]);
+ header_block[kv[0]] = kv[1];
+ }
+
+ // Make sure to store the response, for later output.
+ client.set_store_response(true);
+
+ for (int i = 0; i < num_requests; ++i) {
+ // Send the request.
+ client.SendRequestAndWaitForResponse(header_block, body, /*fin=*/true);
+
+ // Print request and response details.
+ if (!GetQuicFlag(FLAGS_quiet)) {
+ std::cout << "Request:" << std::endl;
+ std::cout << "headers:" << header_block.DebugString();
+ if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+ // Print the user provided hex, rather than binary body.
+ std::cout << "body:\n"
+ << QuicTextUtils::HexDump(
+ QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex)))
+ << std::endl;
+ } else {
+ std::cout << "body: " << body << std::endl;
+ }
+ std::cout << std::endl;
+
+ if (!client.preliminary_response_headers().empty()) {
+ std::cout << "Preliminary response headers: "
+ << client.preliminary_response_headers() << std::endl;
+ std::cout << std::endl;
+ }
+
+ std::cout << "Response:" << std::endl;
+ std::cout << "headers: " << client.latest_response_headers() << std::endl;
+ std::string response_body = client.latest_response_body();
+ if (!GetQuicFlag(FLAGS_body_hex).empty()) {
+ // Assume response is binary data.
+ std::cout << "body:\n"
+ << QuicTextUtils::HexDump(response_body) << std::endl;
+ } else {
+ std::cout << "body: " << response_body << std::endl;
+ }
+ std::cout << "trailers: " << client.latest_response_trailers()
+ << std::endl;
+ }
+
+ if (!client.connected()) {
+ std::cerr << "Request caused connection failure. Error: "
+ << quic::QuicErrorCodeToString(client.session()->error())
+ << std::endl;
+ return 1;
+ }
+
+ size_t response_code = client.latest_response_code();
+ if (response_code >= 200 && response_code < 300) {
+ std::cerr << "Request succeeded (" << response_code << ")." << std::endl;
+ } else if (response_code >= 300 && response_code < 400) {
+ if (GetQuicFlag(FLAGS_redirect_is_success)) {
+ std::cerr << "Request succeeded (redirect " << response_code << ")."
+ << std::endl;
+ } else {
+ std::cerr << "Request failed (redirect " << response_code << ")."
+ << std::endl;
+ return 1;
+ }
+ } else {
+ std::cerr << "Request failed (" << response_code << ")." << std::endl;
+ return 1;
+ }
+
+ // Change the ephemeral port if there are more requests to do.
+ if (i + 1 < num_requests) {
+ if (!client.ChangeEphemeralPort()) {
+ std::cerr << "Failed to change ephemeral port." << std::endl;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.cc
new file mode 100644
index 00000000000..e545ee2c62c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.cc
@@ -0,0 +1,206 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace quic {
+
+namespace {
+const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
+} // namespace
+
+QuicClientEpollNetworkHelper::QuicClientEpollNetworkHelper(
+ QuicEpollServer* epoll_server,
+ QuicClientBase* client)
+ : epoll_server_(epoll_server),
+ packets_dropped_(0),
+ overflow_supported_(false),
+ packet_reader_(new QuicPacketReader()),
+ client_(client),
+ max_reads_per_epoll_loop_(std::numeric_limits<int>::max()) {}
+
+QuicClientEpollNetworkHelper::~QuicClientEpollNetworkHelper() {
+ if (client_->connected()) {
+ client_->session()->connection()->CloseConnection(
+ QUIC_PEER_GOING_AWAY, "Client being torn down",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ }
+
+ CleanUpAllUDPSockets();
+}
+
+std::string QuicClientEpollNetworkHelper::Name() const {
+ return "QuicClientEpollNetworkHelper";
+}
+
+bool QuicClientEpollNetworkHelper::CreateUDPSocketAndBind(
+ QuicSocketAddress server_address,
+ QuicIpAddress bind_to_address,
+ int bind_to_port) {
+ epoll_server_->set_timeout_in_us(50 * 1000);
+
+ int fd = CreateUDPSocket(server_address, &overflow_supported_);
+ if (fd < 0) {
+ return false;
+ }
+
+ QuicSocketAddress client_address;
+ if (bind_to_address.IsInitialized()) {
+ client_address = QuicSocketAddress(bind_to_address, client_->local_port());
+ } else if (server_address.host().address_family() == IpAddressFamily::IP_V4) {
+ client_address = QuicSocketAddress(QuicIpAddress::Any4(), bind_to_port);
+ } else {
+ client_address = QuicSocketAddress(QuicIpAddress::Any6(), bind_to_port);
+ }
+
+ sockaddr_storage addr = client_address.generic_address();
+ int rc = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (rc < 0) {
+ QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno);
+ return false;
+ }
+
+ if (client_address.FromSocket(fd) != 0) {
+ QUIC_LOG(ERROR) << "Unable to get self address. Error: "
+ << strerror(errno);
+ }
+
+ fd_address_map_[fd] = client_address;
+
+ epoll_server_->RegisterFD(fd, this, kEpollFlags);
+ return true;
+}
+
+void QuicClientEpollNetworkHelper::CleanUpUDPSocket(int fd) {
+ CleanUpUDPSocketImpl(fd);
+ fd_address_map_.erase(fd);
+}
+
+void QuicClientEpollNetworkHelper::CleanUpAllUDPSockets() {
+ for (std::pair<int, QuicSocketAddress> fd_address : fd_address_map_) {
+ CleanUpUDPSocketImpl(fd_address.first);
+ }
+ fd_address_map_.clear();
+}
+
+void QuicClientEpollNetworkHelper::CleanUpUDPSocketImpl(int fd) {
+ if (fd > -1) {
+ epoll_server_->UnregisterFD(fd);
+ int rc = close(fd);
+ DCHECK_EQ(0, rc);
+ }
+}
+
+void QuicClientEpollNetworkHelper::RunEventLoop() {
+ QuicRunSystemEventLoopIteration();
+ epoll_server_->WaitForEventsAndExecuteCallbacks();
+}
+
+void QuicClientEpollNetworkHelper::OnRegistration(QuicEpollServer* eps,
+ int fd,
+ int event_mask) {}
+void QuicClientEpollNetworkHelper::OnModification(int fd, int event_mask) {}
+void QuicClientEpollNetworkHelper::OnUnregistration(int fd, bool replaced) {}
+void QuicClientEpollNetworkHelper::OnShutdown(QuicEpollServer* eps, int fd) {}
+
+void QuicClientEpollNetworkHelper::OnEvent(int fd, QuicEpollEvent* event) {
+ DCHECK_EQ(fd, GetLatestFD());
+
+ if (event->in_events & EPOLLIN) {
+ DVLOG(1) << "Read packets on EPOLLIN";
+ int times_to_read = max_reads_per_epoll_loop_;
+ bool more_to_read = true;
+ QuicPacketCount packets_dropped = 0;
+ while (client_->connected() && more_to_read && times_to_read > 0) {
+ more_to_read = packet_reader_->ReadAndDispatchPackets(
+ GetLatestFD(), GetLatestClientAddress().port(),
+ *client_->helper()->GetClock(), this,
+ overflow_supported_ ? &packets_dropped : nullptr);
+ --times_to_read;
+ }
+ if (packets_dropped_ < packets_dropped) {
+ QUIC_LOG(ERROR)
+ << packets_dropped - packets_dropped_
+ << " more packets are dropped in the socket receive buffer.";
+ packets_dropped_ = packets_dropped;
+ }
+ if (client_->connected() && more_to_read) {
+ event->out_ready_mask |= EPOLLIN;
+ }
+ }
+ if (client_->connected() && (event->in_events & EPOLLOUT)) {
+ client_->writer()->SetWritable();
+ client_->session()->connection()->OnCanWrite();
+ }
+ if (event->in_events & EPOLLERR) {
+ QUIC_DLOG(INFO) << "Epollerr";
+ }
+}
+
+QuicPacketWriter* QuicClientEpollNetworkHelper::CreateQuicPacketWriter() {
+ return new QuicDefaultPacketWriter(GetLatestFD());
+}
+
+void QuicClientEpollNetworkHelper::SetClientPort(int port) {
+ fd_address_map_.back().second =
+ QuicSocketAddress(GetLatestClientAddress().host(), port);
+}
+
+QuicSocketAddress QuicClientEpollNetworkHelper::GetLatestClientAddress() const {
+ if (fd_address_map_.empty()) {
+ return QuicSocketAddress();
+ }
+
+ return fd_address_map_.back().second;
+}
+
+int QuicClientEpollNetworkHelper::GetLatestFD() const {
+ if (fd_address_map_.empty()) {
+ return -1;
+ }
+
+ return fd_address_map_.back().first;
+}
+
+void QuicClientEpollNetworkHelper::ProcessPacket(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) {
+ client_->session()->ProcessUdpPacket(self_address, peer_address, packet);
+}
+
+int QuicClientEpollNetworkHelper::CreateUDPSocket(
+ QuicSocketAddress server_address,
+ bool* overflow_supported) {
+ return QuicSocketUtils::CreateUDPSocket(
+ server_address,
+ /*receive_buffer_size =*/kDefaultSocketReceiveBuffer,
+ /*send_buffer_size =*/kDefaultSocketReceiveBuffer, overflow_supported);
+}
+} // namespace quic
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
new file mode 100644
index 00000000000..89d12946c7b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_epoll_network_helper.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// An implementation of the QuicClientBase::NetworkHelper
+// that is based off the epoll server.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_process_packet_interface.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client_base.h"
+
+namespace quic {
+
+namespace test {
+class QuicClientPeer;
+} // namespace test
+
+// An implementation of the QuicClientBase::NetworkHelper based off
+// the epoll server.
+class QuicClientEpollNetworkHelper : public QuicClientBase::NetworkHelper,
+ public QuicEpollCallbackInterface,
+ public ProcessPacketInterface {
+ public:
+ // Create a quic client, which will have events managed by an externally owned
+ // EpollServer.
+ QuicClientEpollNetworkHelper(QuicEpollServer* epoll_server,
+ QuicClientBase* client);
+ QuicClientEpollNetworkHelper(const QuicClientEpollNetworkHelper&) = delete;
+ QuicClientEpollNetworkHelper& operator=(const QuicClientEpollNetworkHelper&) =
+ delete;
+
+ ~QuicClientEpollNetworkHelper() override;
+
+ // Return a name describing the class for use in debug/error reporting.
+ std::string Name() const override;
+
+ // From EpollCallbackInterface
+ void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override;
+ void OnModification(int fd, int event_mask) override;
+ void OnEvent(int fd, QuicEpollEvent* event) override;
+ // |fd_| can be unregistered without the client being disconnected. This
+ // happens in b3m QuicProber where we unregister |fd_| to feed in events to
+ // the client from the SelectServer.
+ void OnUnregistration(int fd, bool replaced) override;
+ void OnShutdown(QuicEpollServer* eps, int fd) override;
+
+ // From ProcessPacketInterface. This will be called for each received
+ // packet.
+ void ProcessPacket(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ const QuicReceivedPacket& packet) override;
+
+ // From NetworkHelper.
+ void RunEventLoop() override;
+ bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
+ QuicIpAddress bind_to_address,
+ int bind_to_port) override;
+ void CleanUpAllUDPSockets() override;
+ QuicSocketAddress GetLatestClientAddress() const override;
+ QuicPacketWriter* CreateQuicPacketWriter() override;
+
+ // Accessors provided for convenience, not part of any interface.
+
+ QuicEpollServer* epoll_server() { return epoll_server_; }
+
+ const QuicLinkedHashMap<int, QuicSocketAddress>& fd_address_map() const {
+ return fd_address_map_;
+ }
+
+ // If the client has at least one UDP socket, return the latest created one.
+ // Otherwise, return -1.
+ int GetLatestFD() const;
+
+ // Create socket for connection to |server_address| with default socket
+ // options.
+ // Return fd index.
+ virtual int CreateUDPSocket(QuicSocketAddress server_address,
+ bool* overflow_supported);
+
+ QuicClientBase* client() { return client_; }
+
+ void set_max_reads_per_epoll_loop(int num_reads) {
+ max_reads_per_epoll_loop_ = num_reads;
+ }
+ // If |fd| is an open UDP socket, unregister and close it. Otherwise, do
+ // nothing.
+ void CleanUpUDPSocket(int fd);
+
+ private:
+ friend class test::QuicClientPeer;
+
+ // Used for testing.
+ void SetClientPort(int port);
+
+ // Actually clean up |fd|.
+ void CleanUpUDPSocketImpl(int fd);
+
+ // Listens for events on the client socket.
+ QuicEpollServer* epoll_server_;
+
+ // 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_;
+
+ // If overflow_supported_ is true, this will be the number of packets dropped
+ // during the lifetime of the server.
+ QuicPacketCount packets_dropped_;
+
+ // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
+ // because the socket would otherwise overflow.
+ bool overflow_supported_;
+
+ // Point to a QuicPacketReader object on the heap. The reader allocates more
+ // space than allowed on the stack.
+ std::unique_ptr<QuicPacketReader> packet_reader_;
+
+ QuicClientBase* client_;
+
+ int max_reads_per_epoll_loop_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_CLIENT_EPOLL_NETWORK_HELPER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc
new file mode 100644
index 00000000000..12a687c1671
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_client_test.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_client.h"
+
+#include <dirent.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_client_peer.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const char* kPathToFds = "/proc/self/fd";
+
+std::string ReadLink(const std::string& path) {
+ std::string result(PATH_MAX, '\0');
+ ssize_t result_size = readlink(path.c_str(), &result[0], result.size());
+ CHECK(result_size > 0 && static_cast<size_t>(result_size) < result.size());
+ result.resize(result_size);
+ return result;
+}
+
+// Counts the number of open sockets for the current process.
+size_t NumOpenSocketFDs() {
+ size_t socket_count = 0;
+ dirent* file;
+ std::unique_ptr<DIR, int (*)(DIR*)> fd_directory(opendir(kPathToFds),
+ closedir);
+ while ((file = readdir(fd_directory.get())) != nullptr) {
+ QuicStringPiece name(file->d_name);
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ std::string fd_path = ReadLink(QuicStrCat(kPathToFds, "/", name));
+ if (QuicTextUtils::StartsWith(fd_path, "socket:")) {
+ socket_count++;
+ }
+ }
+ return socket_count;
+}
+
+// Creates a new QuicClient and Initializes it. Caller is responsible for
+// deletion.
+std::unique_ptr<QuicClient> CreateAndInitializeQuicClient(QuicEpollServer* eps,
+ uint16_t port) {
+ QuicSocketAddress server_address(QuicSocketAddress(TestLoopback(), port));
+ QuicServerId server_id("hostname", server_address.port(), false);
+ ParsedQuicVersionVector versions = AllSupportedVersions();
+ auto client =
+ QuicMakeUnique<QuicClient>(server_address, server_id, versions, eps,
+ crypto_test_utils::ProofVerifierForTesting());
+ EXPECT_TRUE(client->Initialize());
+ return client;
+}
+
+class QuicClientTest : public QuicTest {};
+
+TEST_F(QuicClientTest, DoNotLeakSocketFDs) {
+ // Make sure that the QuicClient doesn't leak socket FDs. Doing so could cause
+ // port exhaustion in long running processes which repeatedly create clients.
+
+ // Record initial number of FDs, after creating EpollServer and creating and
+ // destroying a single client (the latter is needed since initializing
+ // platform dependencies like certificate verifier may open a persistent
+ // socket).
+ QuicEpollServer eps;
+ CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie());
+ size_t number_of_open_fds = NumOpenSocketFDs();
+
+ // Create a number of clients, initialize them, and verify this has resulted
+ // in additional FDs being opened.
+ const int kNumClients = 50;
+ for (int i = 0; i < kNumClients; ++i) {
+ std::unique_ptr<QuicClient> client(
+ CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie()));
+
+ // Initializing the client will create a new FD.
+ EXPECT_LT(number_of_open_fds, NumOpenSocketFDs());
+ }
+
+ // The FDs created by the QuicClients should now be closed.
+ EXPECT_EQ(number_of_open_fds, NumOpenSocketFDs());
+}
+
+TEST_F(QuicClientTest, CreateAndCleanUpUDPSockets) {
+ QuicEpollServer eps;
+ size_t number_of_open_fds = NumOpenSocketFDs();
+
+ std::unique_ptr<QuicClient> client(
+ CreateAndInitializeQuicClient(&eps, QuicPickUnusedPortOrDie()));
+ EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs());
+ // Create more UDP sockets.
+ EXPECT_TRUE(QuicClientPeer::CreateUDPSocketAndBind(client.get()));
+ EXPECT_EQ(number_of_open_fds + 2, NumOpenSocketFDs());
+ EXPECT_TRUE(QuicClientPeer::CreateUDPSocketAndBind(client.get()));
+ EXPECT_EQ(number_of_open_fds + 3, NumOpenSocketFDs());
+
+ // Clean up UDP sockets.
+ QuicClientPeer::CleanUpUDPSocket(client.get(), client->GetLatestFD());
+ EXPECT_EQ(number_of_open_fds + 2, NumOpenSocketFDs());
+ QuicClientPeer::CleanUpUDPSocket(client.get(), client->GetLatestFD());
+ EXPECT_EQ(number_of_open_fds + 1, NumOpenSocketFDs());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..76a52c3bf87
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.cc
@@ -0,0 +1,419 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_file_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using spdy::kV3LowestPriority;
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+QuicMemoryCacheBackend::ResourceFile::ResourceFile(const std::string& file_name)
+ : file_name_(file_name) {}
+
+QuicMemoryCacheBackend::ResourceFile::~ResourceFile() = default;
+
+void QuicMemoryCacheBackend::ResourceFile::Read() {
+ ReadFileContents(file_name_, &file_contents_);
+
+ // First read the headers.
+ size_t start = 0;
+ while (start < file_contents_.length()) {
+ size_t pos = file_contents_.find("\n", start);
+ if (pos == std::string::npos) {
+ QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file_name_;
+ return;
+ }
+ size_t len = pos - start;
+ // Support both dos and unix line endings for convenience.
+ if (file_contents_[pos - 1] == '\r') {
+ len -= 1;
+ }
+ QuicStringPiece line(file_contents_.data() + start, len);
+ start = pos + 1;
+ // Headers end with an empty line.
+ if (line.empty()) {
+ break;
+ }
+ // Extract the status from the HTTP first line.
+ if (line.substr(0, 4) == "HTTP") {
+ pos = line.find(" ");
+ if (pos == std::string::npos) {
+ QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: "
+ << file_name_;
+ return;
+ }
+ spdy_headers_[":status"] = line.substr(pos + 1, 3);
+ continue;
+ }
+ // Headers are "key: value".
+ pos = line.find(": ");
+ if (pos == std::string::npos) {
+ QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file_name_;
+ return;
+ }
+ spdy_headers_.AppendValueOrAddHeader(
+ QuicTextUtils::ToLower(line.substr(0, pos)), line.substr(pos + 2));
+ }
+
+ // The connection header is prohibited in HTTP/2.
+ spdy_headers_.erase("connection");
+
+ // Override the URL with the X-Original-Url header, if present.
+ auto it = spdy_headers_.find("x-original-url");
+ if (it != spdy_headers_.end()) {
+ x_original_url_ = it->second;
+ HandleXOriginalUrl();
+ }
+
+ // X-Push-URL header is a relatively quick way to support sever push
+ // in the toy server. A production server should use link=preload
+ // stuff as described in https://w3c.github.io/preload/.
+ it = spdy_headers_.find("x-push-url");
+ if (it != spdy_headers_.end()) {
+ QuicStringPiece push_urls = it->second;
+ size_t start = 0;
+ while (start < push_urls.length()) {
+ size_t pos = push_urls.find('\0', start);
+ if (pos == std::string::npos) {
+ push_urls_.push_back(QuicStringPiece(push_urls.data() + start,
+ push_urls.length() - start));
+ break;
+ }
+ push_urls_.push_back(QuicStringPiece(push_urls.data() + start, pos));
+ start += pos + 1;
+ }
+ }
+
+ body_ = QuicStringPiece(file_contents_.data() + start,
+ file_contents_.size() - start);
+}
+
+void QuicMemoryCacheBackend::ResourceFile::SetHostPathFromBase(
+ QuicStringPiece base) {
+ size_t path_start = base.find_first_of('/');
+ DCHECK_LT(0UL, path_start);
+ host_ = base.substr(0, path_start);
+ size_t query_start = base.find_first_of(',');
+ if (query_start > 0) {
+ path_ = base.substr(path_start, query_start - 1);
+ } else {
+ path_ = base.substr(path_start);
+ }
+}
+
+QuicStringPiece QuicMemoryCacheBackend::ResourceFile::RemoveScheme(
+ QuicStringPiece url) {
+ if (QuicTextUtils::StartsWith(url, "https://")) {
+ url.remove_prefix(8);
+ } else if (QuicTextUtils::StartsWith(url, "http://")) {
+ url.remove_prefix(7);
+ }
+ return url;
+}
+
+void QuicMemoryCacheBackend::ResourceFile::HandleXOriginalUrl() {
+ QuicStringPiece url(x_original_url_);
+ // Remove the protocol so we can add it below.
+ url = RemoveScheme(url);
+ SetHostPathFromBase(url);
+}
+
+const QuicBackendResponse* QuicMemoryCacheBackend::GetResponse(
+ QuicStringPiece host,
+ QuicStringPiece path) const {
+ QuicWriterMutexLock lock(&response_mutex_);
+
+ auto it = responses_.find(GetKey(host, path));
+ if (it == responses_.end()) {
+ DVLOG(1) << "Get response for resource failed: host " << host << " path "
+ << path;
+ if (default_response_) {
+ return default_response_.get();
+ }
+ return nullptr;
+ }
+ return it->second.get();
+}
+
+typedef QuicBackendResponse::ServerPushInfo ServerPushInfo;
+typedef QuicBackendResponse::SpecialResponseType SpecialResponseType;
+
+void QuicMemoryCacheBackend::AddSimpleResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body) {
+ SpdyHeaderBlock response_headers;
+ response_headers[":status"] = QuicTextUtils::Uint64ToString(response_code);
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(body.length());
+ AddResponse(host, path, std::move(response_headers), body);
+}
+
+void QuicMemoryCacheBackend::AddSimpleResponseWithServerPushResources(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body,
+ std::list<ServerPushInfo> push_resources) {
+ AddSimpleResponse(host, path, response_code, body);
+ MaybeAddServerPushResources(host, path, push_resources);
+}
+
+void QuicMemoryCacheBackend::AddDefaultResponse(QuicBackendResponse* response) {
+ QuicWriterMutexLock lock(&response_mutex_);
+ default_response_.reset(response);
+}
+
+void QuicMemoryCacheBackend::AddResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body) {
+ AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE,
+ std::move(response_headers), response_body, SpdyHeaderBlock(),
+ 0);
+}
+
+void QuicMemoryCacheBackend::AddResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ SpdyHeaderBlock response_trailers) {
+ AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE,
+ std::move(response_headers), response_body,
+ std::move(response_trailers), 0);
+}
+
+void QuicMemoryCacheBackend::AddSpecialResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ SpecialResponseType response_type) {
+ AddResponseImpl(host, path, response_type, SpdyHeaderBlock(), "",
+ SpdyHeaderBlock(), 0);
+}
+
+void QuicMemoryCacheBackend::AddSpecialResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ SpecialResponseType response_type) {
+ AddResponseImpl(host, path, response_type, std::move(response_headers),
+ response_body, SpdyHeaderBlock(), 0);
+}
+
+void QuicMemoryCacheBackend::AddStopSendingResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ uint16_t stop_sending_code) {
+ AddResponseImpl(host, path, SpecialResponseType::STOP_SENDING,
+ std::move(response_headers), response_body, SpdyHeaderBlock(),
+ stop_sending_code);
+}
+
+QuicMemoryCacheBackend::QuicMemoryCacheBackend() : cache_initialized_(false) {}
+
+bool QuicMemoryCacheBackend::InitializeBackend(
+ const std::string& cache_directory) {
+ if (cache_directory.empty()) {
+ QUIC_BUG << "cache_directory must not be empty.";
+ return false;
+ }
+ QUIC_LOG(INFO)
+ << "Attempting to initialize QuicMemoryCacheBackend from directory: "
+ << cache_directory;
+ std::vector<std::string> files = ReadFileContents(cache_directory);
+ std::list<std::unique_ptr<ResourceFile>> resource_files;
+ for (const auto& filename : files) {
+ std::unique_ptr<ResourceFile> resource_file(new ResourceFile(filename));
+
+ // Tease apart filename into host and path.
+ QuicStringPiece base(resource_file->file_name());
+ base.remove_prefix(cache_directory.length());
+ if (base[0] == '/') {
+ base.remove_prefix(1);
+ }
+
+ resource_file->SetHostPathFromBase(base);
+ resource_file->Read();
+
+ AddResponse(resource_file->host(), resource_file->path(),
+ resource_file->spdy_headers().Clone(), resource_file->body());
+
+ resource_files.push_back(std::move(resource_file));
+ }
+
+ for (const auto& resource_file : resource_files) {
+ std::list<ServerPushInfo> push_resources;
+ for (const auto& push_url : resource_file->push_urls()) {
+ QuicUrl url(push_url);
+ const QuicBackendResponse* response = GetResponse(url.host(), url.path());
+ if (!response) {
+ QUIC_BUG << "Push URL '" << push_url << "' not found.";
+ return false;
+ }
+ push_resources.push_back(ServerPushInfo(url, response->headers().Clone(),
+ kV3LowestPriority,
+ (std::string(response->body()))));
+ }
+ MaybeAddServerPushResources(resource_file->host(), resource_file->path(),
+ push_resources);
+ }
+ cache_initialized_ = true;
+ return true;
+}
+
+bool QuicMemoryCacheBackend::IsBackendInitialized() const {
+ return cache_initialized_;
+}
+
+void QuicMemoryCacheBackend::FetchResponseFromBackend(
+ const SpdyHeaderBlock& request_headers,
+ const std::string& request_body,
+ QuicSimpleServerBackend::RequestHandler* quic_stream) {
+ const QuicBackendResponse* quic_response = nullptr;
+ // Find response in cache. If not found, send error response.
+ auto authority = request_headers.find(":authority");
+ auto path = request_headers.find(":path");
+ if (authority != request_headers.end() && path != request_headers.end()) {
+ quic_response = GetResponse(authority->second, path->second);
+ }
+
+ std::string request_url =
+ std::string(authority->second) + std::string(path->second);
+ std::list<ServerPushInfo> resources = GetServerPushResources(request_url);
+ QUIC_DVLOG(1)
+ << "Fetching QUIC response from backend in-memory cache for url "
+ << request_url;
+ quic_stream->OnResponseBackendComplete(quic_response, resources);
+}
+
+// The memory cache does not have a per-stream handler
+void QuicMemoryCacheBackend::CloseBackendResponseStream(
+ QuicSimpleServerBackend::RequestHandler* quic_stream) {}
+
+std::list<ServerPushInfo> QuicMemoryCacheBackend::GetServerPushResources(
+ std::string request_url) {
+ QuicWriterMutexLock lock(&response_mutex_);
+
+ std::list<ServerPushInfo> resources;
+ auto resource_range = server_push_resources_.equal_range(request_url);
+ for (auto it = resource_range.first; it != resource_range.second; ++it) {
+ resources.push_back(it->second);
+ }
+ QUIC_DVLOG(1) << "Found " << resources.size() << " push resources for "
+ << request_url;
+ return resources;
+}
+
+QuicMemoryCacheBackend::~QuicMemoryCacheBackend() {
+ {
+ QuicWriterMutexLock lock(&response_mutex_);
+ responses_.clear();
+ }
+}
+
+void QuicMemoryCacheBackend::AddResponseImpl(QuicStringPiece host,
+ QuicStringPiece path,
+ SpecialResponseType response_type,
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ SpdyHeaderBlock response_trailers,
+ uint16_t stop_sending_code) {
+ QuicWriterMutexLock lock(&response_mutex_);
+
+ DCHECK(!host.empty()) << "Host must be populated, e.g. \"www.google.com\"";
+ std::string key = GetKey(host, path);
+ if (QuicContainsKey(responses_, key)) {
+ QUIC_BUG << "Response for '" << key << "' already exists!";
+ return;
+ }
+ auto new_response = QuicMakeUnique<QuicBackendResponse>();
+ new_response->set_response_type(response_type);
+ new_response->set_headers(std::move(response_headers));
+ new_response->set_body(response_body);
+ new_response->set_trailers(std::move(response_trailers));
+ new_response->set_stop_sending_code(stop_sending_code);
+ QUIC_DVLOG(1) << "Add response with key " << key;
+ responses_[key] = std::move(new_response);
+}
+
+std::string QuicMemoryCacheBackend::GetKey(QuicStringPiece host,
+ QuicStringPiece path) const {
+ std::string host_string = std::string(host);
+ size_t port = host_string.find(':');
+ if (port != std::string::npos)
+ host_string = std::string(host_string.c_str(), port);
+ return host_string + std::string(path);
+}
+
+void QuicMemoryCacheBackend::MaybeAddServerPushResources(
+ QuicStringPiece request_host,
+ QuicStringPiece request_path,
+ std::list<ServerPushInfo> push_resources) {
+ std::string request_url = GetKey(request_host, request_path);
+
+ for (const auto& push_resource : push_resources) {
+ if (PushResourceExistsInCache(request_url, push_resource)) {
+ continue;
+ }
+
+ QUIC_DVLOG(1) << "Add request-resource association: request url "
+ << request_url << " push url "
+ << push_resource.request_url.ToString()
+ << " response headers "
+ << push_resource.headers.DebugString();
+ {
+ QuicWriterMutexLock lock(&response_mutex_);
+ server_push_resources_.insert(std::make_pair(request_url, push_resource));
+ }
+ std::string host = push_resource.request_url.host();
+ if (host.empty()) {
+ host = std::string(request_host);
+ }
+ std::string path = push_resource.request_url.path();
+ bool found_existing_response = false;
+ {
+ QuicWriterMutexLock lock(&response_mutex_);
+ found_existing_response = QuicContainsKey(responses_, GetKey(host, path));
+ }
+ if (!found_existing_response) {
+ // Add a server push response to responses map, if it is not in the map.
+ QuicStringPiece body = push_resource.body;
+ QUIC_DVLOG(1) << "Add response for push resource: host " << host
+ << " path " << path;
+ AddResponse(host, path, push_resource.headers.Clone(), body);
+ }
+ }
+}
+
+bool QuicMemoryCacheBackend::PushResourceExistsInCache(
+ std::string original_request_url,
+ ServerPushInfo resource) {
+ QuicWriterMutexLock lock(&response_mutex_);
+ auto resource_range =
+ server_push_resources_.equal_range(original_request_url);
+ for (auto it = resource_range.first; it != resource_range.second; ++it) {
+ ServerPushInfo push_resource = it->second;
+ if (push_resource.request_url.ToString() ==
+ resource.request_url.ToString()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h
new file mode 100644
index 00000000000..ff76c9ad41b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_MEMORY_CACHE_BACKEND_H_
+#define QUICHE_QUIC_TOOLS_QUIC_MEMORY_CACHE_BACKEND_H_
+
+#include <list>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+// In-memory cache for HTTP responses.
+// Reads from disk cache generated by:
+// `wget -p --save_headers <url>`
+class QuicMemoryCacheBackend : public QuicSimpleServerBackend {
+ public:
+ // Class to manage loading a resource file into memory. There are
+ // two uses: called by InitializeBackend to load resources
+ // from files, and recursively called when said resources specify
+ // server push associations.
+ class ResourceFile {
+ public:
+ explicit ResourceFile(const std::string& file_name);
+ ResourceFile(const ResourceFile&) = delete;
+ ResourceFile& operator=(const ResourceFile&) = delete;
+ virtual ~ResourceFile();
+
+ void Read();
+
+ // |base| is |file_name_| with |cache_directory| prefix stripped.
+ void SetHostPathFromBase(QuicStringPiece base);
+
+ const std::string& file_name() { return file_name_; }
+
+ QuicStringPiece host() { return host_; }
+
+ QuicStringPiece path() { return path_; }
+
+ const spdy::SpdyHeaderBlock& spdy_headers() { return spdy_headers_; }
+
+ QuicStringPiece body() { return body_; }
+
+ const std::vector<QuicStringPiece>& push_urls() { return push_urls_; }
+
+ protected:
+ void HandleXOriginalUrl();
+ QuicStringPiece RemoveScheme(QuicStringPiece url);
+
+ std::string file_name_;
+ std::string file_contents_;
+ QuicStringPiece body_;
+ spdy::SpdyHeaderBlock spdy_headers_;
+ QuicStringPiece x_original_url_;
+ std::vector<QuicStringPiece> push_urls_;
+
+ private:
+ QuicStringPiece host_;
+ QuicStringPiece path_;
+ };
+
+ QuicMemoryCacheBackend();
+ QuicMemoryCacheBackend(const QuicMemoryCacheBackend&) = delete;
+ QuicMemoryCacheBackend& operator=(const QuicMemoryCacheBackend&) = delete;
+ ~QuicMemoryCacheBackend() override;
+
+ // Retrieve a response from this cache for a given host and path..
+ // If no appropriate response exists, nullptr is returned.
+ const QuicBackendResponse* GetResponse(QuicStringPiece host,
+ QuicStringPiece path) const;
+
+ // Adds a simple response to the cache. The response headers will
+ // only contain the "content-length" header with the length of |body|.
+ void AddSimpleResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body);
+
+ // Add a simple response to the cache as AddSimpleResponse() does, and add
+ // some server push resources(resource path, corresponding response status and
+ // path) associated with it.
+ // Push resource implicitly come from the same host.
+ void AddSimpleResponseWithServerPushResources(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ int response_code,
+ QuicStringPiece body,
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources);
+
+ // Add a response to the cache.
+ void AddResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body);
+
+ // Add a response, with trailers, to the cache.
+ void AddResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ spdy::SpdyHeaderBlock response_trailers);
+
+ // Simulate a special behavior at a particular path.
+ void AddSpecialResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ QuicBackendResponse::SpecialResponseType response_type);
+
+ void AddSpecialResponse(
+ QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ QuicBackendResponse::SpecialResponseType response_type);
+
+ void AddStopSendingResponse(QuicStringPiece host,
+ QuicStringPiece path,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ uint16_t stop_sending_code);
+
+ // Sets a default response in case of cache misses. Takes ownership of
+ // 'response'.
+ void AddDefaultResponse(QuicBackendResponse* response);
+
+ // |cache_cirectory| can be generated using `wget -p --save-headers <url>`.
+ void InitializeFromDirectory(const std::string& cache_directory);
+
+ // Find all the server push resources associated with |request_url|.
+ std::list<QuicBackendResponse::ServerPushInfo> GetServerPushResources(
+ std::string request_url);
+
+ // Implements the functions for interface QuicSimpleServerBackend
+ // |cache_cirectory| can be generated using `wget -p --save-headers <url>`.
+ bool InitializeBackend(const std::string& cache_directory) override;
+ bool IsBackendInitialized() const override;
+ void FetchResponseFromBackend(
+ const spdy::SpdyHeaderBlock& request_headers,
+ const std::string& request_body,
+ QuicSimpleServerBackend::RequestHandler* quic_server_stream) override;
+ void CloseBackendResponseStream(
+ QuicSimpleServerBackend::RequestHandler* quic_server_stream) override;
+
+ private:
+ void AddResponseImpl(QuicStringPiece host,
+ QuicStringPiece path,
+ QuicBackendResponse::SpecialResponseType response_type,
+ spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece response_body,
+ spdy::SpdyHeaderBlock response_trailers,
+ uint16_t stop_sending_code);
+
+ std::string GetKey(QuicStringPiece host, QuicStringPiece path) const;
+
+ // Add some server push urls with given responses for specified
+ // request if these push resources are not associated with this request yet.
+ void MaybeAddServerPushResources(
+ QuicStringPiece request_host,
+ QuicStringPiece request_path,
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources);
+
+ // Check if push resource(push_host/push_path) associated with given request
+ // url already exists in server push map.
+ bool PushResourceExistsInCache(std::string original_request_url,
+ QuicBackendResponse::ServerPushInfo resource);
+
+ // Cached responses.
+ QuicUnorderedMap<std::string, std::unique_ptr<QuicBackendResponse>> responses_
+ GUARDED_BY(response_mutex_);
+
+ // The default response for cache misses, if set.
+ std::unique_ptr<QuicBackendResponse> default_response_
+ GUARDED_BY(response_mutex_);
+
+ // A map from request URL to associated server push responses (if any).
+ std::multimap<std::string, QuicBackendResponse::ServerPushInfo>
+ server_push_resources_ GUARDED_BY(response_mutex_);
+
+ // Protects against concurrent access from test threads setting responses, and
+ // server threads accessing those responses.
+ mutable QuicMutex response_mutex_;
+ bool cache_initialized_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_MEMORY_CACHE_BACKEND_H_
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
new file mode 100644
index 00000000000..80b1b293206
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_memory_cache_backend_test.cc
@@ -0,0 +1,235 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+
+namespace quic {
+namespace test {
+
+namespace {
+typedef QuicBackendResponse Response;
+typedef QuicBackendResponse::ServerPushInfo ServerPushInfo;
+} // namespace
+
+class QuicMemoryCacheBackendTest : public QuicTest {
+ protected:
+ void CreateRequest(std::string host,
+ std::string path,
+ spdy::SpdyHeaderBlock* headers) {
+ (*headers)[":method"] = "GET";
+ (*headers)[":path"] = path;
+ (*headers)[":authority"] = host;
+ (*headers)[":scheme"] = "https";
+ }
+
+ std::string CacheDirectory() { return QuicGetTestMemoryCachePath(); }
+
+ QuicMemoryCacheBackend cache_;
+};
+
+TEST_F(QuicMemoryCacheBackendTest, GetResponseNoMatch) {
+ const Response* response =
+ cache_.GetResponse("mail.google.com", "/index.html");
+ ASSERT_FALSE(response);
+}
+
+TEST_F(QuicMemoryCacheBackendTest, AddSimpleResponseGetResponse) {
+ std::string response_body("hello response");
+ cache_.AddSimpleResponse("www.google.com", "/", 200, response_body);
+
+ spdy::SpdyHeaderBlock request_headers;
+ CreateRequest("www.google.com", "/", &request_headers);
+ const Response* response = cache_.GetResponse("www.google.com", "/");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+ EXPECT_EQ(response_body.size(), response->body().length());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, AddResponse) {
+ const std::string kRequestHost = "www.foo.com";
+ const std::string kRequestPath = "/";
+ const std::string kResponseBody("hello response");
+
+ spdy::SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = "200";
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(kResponseBody.size());
+
+ spdy::SpdyHeaderBlock response_trailers;
+ response_trailers["key-1"] = "value-1";
+ response_trailers["key-2"] = "value-2";
+ response_trailers["key-3"] = "value-3";
+
+ cache_.AddResponse(kRequestHost, "/", response_headers.Clone(), kResponseBody,
+ response_trailers.Clone());
+
+ const Response* response = cache_.GetResponse(kRequestHost, kRequestPath);
+ EXPECT_EQ(response->headers(), response_headers);
+ EXPECT_EQ(response->body(), kResponseBody);
+ EXPECT_EQ(response->trailers(), response_trailers);
+}
+
+TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDir) {
+ cache_.InitializeBackend(CacheDirectory());
+ const Response* response =
+ cache_.GetResponse("test.example.com", "/index.html");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+ // Connection headers are not valid in HTTP/2.
+ EXPECT_FALSE(QuicContainsKey(response->headers(), "connection"));
+ EXPECT_LT(0U, response->body().length());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDirWithServerPushResource) {
+ cache_.InitializeBackend(CacheDirectory() + "_with_push");
+ std::list<ServerPushInfo> resources =
+ cache_.GetServerPushResources("test.example.com/");
+ ASSERT_EQ(1UL, resources.size());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDirWithServerPushResources) {
+ cache_.InitializeBackend(CacheDirectory() + "_with_push");
+ std::list<ServerPushInfo> resources =
+ cache_.GetServerPushResources("test.example.com/index2.html");
+ ASSERT_EQ(2UL, resources.size());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrl) {
+ cache_.InitializeBackend(CacheDirectory());
+ const Response* response =
+ cache_.GetResponse("test.example.com", "/site_map.html");
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+ // Connection headers are not valid in HTTP/2.
+ EXPECT_FALSE(QuicContainsKey(response->headers(), "connection"));
+ EXPECT_LT(0U, response->body().length());
+}
+
+TEST_F(QuicMemoryCacheBackendTest, DefaultResponse) {
+ // Verify GetResponse returns nullptr when no default is set.
+ const Response* response = cache_.GetResponse("www.google.com", "/");
+ ASSERT_FALSE(response);
+
+ // Add a default response.
+ spdy::SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = "200";
+ response_headers["content-length"] = "0";
+ Response* default_response = new Response;
+ default_response->set_headers(std::move(response_headers));
+ cache_.AddDefaultResponse(default_response);
+
+ // 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"));
+ 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"));
+ 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"));
+ EXPECT_EQ("200", response->headers().find(":status")->second);
+}
+
+TEST_F(QuicMemoryCacheBackendTest, AddSimpleResponseWithServerPushResources) {
+ std::string request_host = "www.foo.com";
+ std::string response_body("hello response");
+ const size_t kNumResources = 5;
+ int NumResources = 5;
+ std::list<ServerPushInfo> push_resources;
+ std::string scheme = "http";
+ for (int i = 0; i < NumResources; ++i) {
+ std::string path = "/server_push_src" + QuicTextUtils::Uint64ToString(i);
+ std::string url = scheme + "://" + request_host + path;
+ QuicUrl resource_url(url);
+ std::string body =
+ QuicStrCat("This is server push response body for ", path);
+ spdy::SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = "200";
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(body.size());
+ push_resources.push_back(
+ ServerPushInfo(resource_url, response_headers.Clone(), i, body));
+ }
+
+ cache_.AddSimpleResponseWithServerPushResources(
+ request_host, "/", 200, response_body, push_resources);
+
+ std::string request_url = request_host + "/";
+ std::list<ServerPushInfo> resources =
+ cache_.GetServerPushResources(request_url);
+ ASSERT_EQ(kNumResources, resources.size());
+ for (const auto& push_resource : push_resources) {
+ ServerPushInfo resource = resources.front();
+ EXPECT_EQ(resource.request_url.ToString(),
+ push_resource.request_url.ToString());
+ EXPECT_EQ(resource.priority, push_resource.priority);
+ resources.pop_front();
+ }
+}
+
+TEST_F(QuicMemoryCacheBackendTest, GetServerPushResourcesAndPushResponses) {
+ std::string request_host = "www.foo.com";
+ std::string response_body("hello response");
+ const size_t kNumResources = 4;
+ int NumResources = 4;
+ std::string scheme = "http";
+ std::string push_response_status[kNumResources] = {"200", "200", "301",
+ "404"};
+ std::list<ServerPushInfo> push_resources;
+ for (int i = 0; i < NumResources; ++i) {
+ std::string path = "/server_push_src" + QuicTextUtils::Uint64ToString(i);
+ std::string url = scheme + "://" + request_host + path;
+ QuicUrl resource_url(url);
+ std::string body = "This is server push response body for " + path;
+ spdy::SpdyHeaderBlock response_headers;
+ response_headers[":version"] = "HTTP/1.1";
+ response_headers[":status"] = push_response_status[i];
+ response_headers["content-length"] =
+ QuicTextUtils::Uint64ToString(body.size());
+ push_resources.push_back(
+ ServerPushInfo(resource_url, response_headers.Clone(), i, body));
+ }
+ cache_.AddSimpleResponseWithServerPushResources(
+ request_host, "/", 200, response_body, push_resources);
+ std::string request_url = request_host + "/";
+ std::list<ServerPushInfo> resources =
+ cache_.GetServerPushResources(request_url);
+ ASSERT_EQ(kNumResources, resources.size());
+ int i = 0;
+ for (const auto& push_resource : push_resources) {
+ QuicUrl url = resources.front().request_url;
+ std::string host = url.host();
+ std::string path = url.path();
+ const Response* response = cache_.GetResponse(host, path);
+ ASSERT_TRUE(response);
+ ASSERT_TRUE(QuicContainsKey(response->headers(), ":status"));
+ EXPECT_EQ(push_response_status[i++],
+ response->headers().find(":status")->second);
+ EXPECT_EQ(push_resource.body, response->body());
+ resources.pop_front();
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc
new file mode 100644
index 00000000000..c8cfc11ec8f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_packet_printer_bin.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// clang-format off
+
+// Dumps out the decryptable contents of a QUIC packet in a human-readable way.
+// If the packet is null encrypted, this will dump full packet contents.
+// Otherwise it will dump the header, and fail with an error that the
+// packet is undecryptable.
+//
+// Usage: quic_packet_printer server|client <hex dump of packet>
+//
+// Example input:
+// quic_packet_printer server 0c6b810308320f24c004a939a38a2e3fd6ca589917f200400201b80b0100501c0700060003023d0000001c00556e656e637279707465642073747265616d2064617461207365656e
+//
+// Example output:
+// OnPacket
+// OnUnauthenticatedPublicHeader
+// OnUnauthenticatedHeader: { connection_id: 13845207862000976235, connection_id_length:8, packet_number_length:1, multipath_flag: 0, reset_flag: 0, version_flag: 0, path_id: , packet_number: 4 }
+// OnDecryptedPacket
+// OnPacketHeader
+// OnAckFrame: largest_observed: 1 ack_delay_time: 3000 missing_packets: [ ] is_truncated: 0 received_packets: [ 1 at 466016 ]
+// OnStopWaitingFrame
+// OnConnectionCloseFrame: error_code { 61 } error_details { Unencrypted stream data seen }
+
+// clang-format on
+
+#include <iostream>
+
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(std::string,
+ quic_version,
+ "",
+ "If set, specify the QUIC version to use.");
+
+namespace quic {
+
+class QuicPacketPrinter : public QuicFramerVisitorInterface {
+ public:
+ explicit QuicPacketPrinter(QuicFramer* framer) : framer_(framer) {}
+
+ void OnError(QuicFramer* framer) override {
+ std::cerr << "OnError: " << QuicErrorCodeToString(framer->error())
+ << " detail: " << framer->detailed_error() << "\n";
+ }
+ bool OnProtocolVersionMismatch(ParsedQuicVersion received_version,
+ PacketHeaderFormat form) override {
+ framer_->set_version(received_version);
+ std::cerr << "OnProtocolVersionMismatch: "
+ << ParsedQuicVersionToString(received_version) << "\n";
+ return true;
+ }
+ void OnPacket() override { std::cerr << "OnPacket\n"; }
+ void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {
+ std::cerr << "OnPublicResetPacket\n";
+ }
+ void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) override {
+ std::cerr << "OnVersionNegotiationPacket\n";
+ }
+ bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override {
+ std::cerr << "OnUnauthenticatedPublicHeader\n";
+ return true;
+ }
+ bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override {
+ std::cerr << "OnUnauthenticatedHeader: " << header;
+ return true;
+ }
+ void OnDecryptedPacket(EncryptionLevel level) override {
+ // This only currently supports "decrypting" null encrypted packets.
+ DCHECK_EQ(ENCRYPTION_INITIAL, level);
+ std::cerr << "OnDecryptedPacket\n";
+ }
+ bool OnPacketHeader(const QuicPacketHeader& header) override {
+ std::cerr << "OnPacketHeader\n";
+ return true;
+ }
+ void OnCoalescedPacket(const QuicEncryptedPacket& packet) override {
+ std::cerr << "OnCoalescedPacket\n";
+ }
+ bool OnStreamFrame(const QuicStreamFrame& frame) override {
+ std::cerr << "OnStreamFrame: " << frame;
+ std::cerr << " data: { "
+ << QuicTextUtils::HexEncode(frame.data_buffer, frame.data_length)
+ << " }\n";
+ return true;
+ }
+ bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+ std::cerr << "OnCryptoFrame: " << frame;
+ std::cerr << " data: { "
+ << QuicTextUtils::HexEncode(frame.data_buffer, frame.data_length)
+ << " }\n";
+ return true;
+ }
+ bool OnAckFrameStart(QuicPacketNumber largest_acked,
+ QuicTime::Delta /*ack_delay_time*/) override {
+ std::cerr << "OnAckFrameStart, largest_acked: " << largest_acked;
+ return true;
+ }
+ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
+ std::cerr << "OnAckRange: [" << start << ", " << end << ")";
+ return true;
+ }
+ bool OnAckTimestamp(QuicPacketNumber packet_number,
+ QuicTime timestamp) override {
+ std::cerr << "OnAckTimestamp: [" << packet_number << ", "
+ << timestamp.ToDebuggingValue() << ")";
+ return true;
+ }
+ bool OnAckFrameEnd(QuicPacketNumber start) override {
+ std::cerr << "OnAckFrameEnd, start: " << start;
+ return true;
+ }
+ bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
+ std::cerr << "OnStopWaitingFrame: " << frame;
+ return true;
+ }
+ bool OnPaddingFrame(const QuicPaddingFrame& frame) override {
+ std::cerr << "OnPaddingFrame: " << frame;
+ return true;
+ }
+ bool OnPingFrame(const QuicPingFrame& frame) override {
+ std::cerr << "OnPingFrame\n";
+ return true;
+ }
+ bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
+ std::cerr << "OnRstStreamFrame: " << frame;
+ return true;
+ }
+ bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override {
+ // The frame printout will indicate whether it's a Google QUIC
+ // CONNECTION_CLOSE, IETF QUIC CONNECTION_CLOSE/Transport, or IETF QUIC
+ // CONNECTION_CLOSE/Application frame.
+ std::cerr << "OnConnectionCloseFrame: " << frame;
+ return true;
+ }
+ bool OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override {
+ std::cerr << "OnNewConnectionIdFrame: " << frame;
+ return true;
+ }
+ bool OnRetireConnectionIdFrame(
+ const QuicRetireConnectionIdFrame& frame) override {
+ std::cerr << "OnRetireConnectionIdFrame: " << frame;
+ return true;
+ }
+ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override {
+ std::cerr << "OnNewTokenFrame: " << frame;
+ return true;
+ }
+ bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override {
+ std::cerr << "OnStopSendingFrame: " << frame;
+ return true;
+ }
+ bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override {
+ std::cerr << "OnPathChallengeFrame: " << frame;
+ return true;
+ }
+ bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override {
+ std::cerr << "OnPathResponseFrame: " << frame;
+ return true;
+ }
+ bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override {
+ std::cerr << "OnGoAwayFrame: " << frame;
+ return true;
+ }
+ bool OnMaxStreamIdFrame(const QuicMaxStreamIdFrame& frame) override {
+ std::cerr << "OnMaxStreamIdFrame: " << frame;
+ return true;
+ }
+ bool OnStreamIdBlockedFrame(const QuicStreamIdBlockedFrame& frame) override {
+ std::cerr << "OnStreamIdBlockedFrame: " << frame;
+ return true;
+ }
+ bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override {
+ std::cerr << "OnWindowUpdateFrame: " << frame;
+ return true;
+ }
+ bool OnBlockedFrame(const QuicBlockedFrame& frame) override {
+ std::cerr << "OnBlockedFrame: " << frame;
+ return true;
+ }
+ bool OnMessageFrame(const QuicMessageFrame& frame) override {
+ std::cerr << "OnMessageFrame: " << frame;
+ return true;
+ }
+ void OnPacketComplete() override { std::cerr << "OnPacketComplete\n"; }
+ bool IsValidStatelessResetToken(QuicUint128 token) const override {
+ std::cerr << "IsValidStatelessResetToken\n";
+ return false;
+ }
+ void OnAuthenticatedIetfStatelessResetPacket(
+ const QuicIetfStatelessResetPacket& packet) override {
+ std::cerr << "OnAuthenticatedIetfStatelessResetPacket\n";
+ }
+
+ private:
+ QuicFramer* framer_; // Unowned.
+};
+
+} // namespace quic
+
+int main(int argc, char* argv[]) {
+ const char* usage = "Usage: quic_packet_printer client|server <hex>";
+ std::vector<std::string> args =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+
+ if (args.size() < 2) {
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ return 1;
+ }
+
+ std::string perspective_string = args[0];
+ quic::Perspective perspective;
+ if (perspective_string == "client") {
+ perspective = quic::Perspective::IS_CLIENT;
+ } else if (perspective_string == "server") {
+ perspective = quic::Perspective::IS_SERVER;
+ } else {
+ std::cerr << "Invalid perspective" << std::endl;
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ return 1;
+ }
+ std::string hex = quic::QuicTextUtils::HexDecode(args[1]);
+ quic::ParsedQuicVersionVector versions = quic::AllSupportedVersions();
+ // Fake a time since we're not actually generating acks.
+ quic::QuicTime start(quic::QuicTime::Zero());
+ quic::QuicFramer framer(versions, start, perspective,
+ quic::kQuicDefaultConnectionIdLength);
+ if (!GetQuicFlag(FLAGS_quic_version).empty()) {
+ for (quic::ParsedQuicVersion version : versions) {
+ if (quic::QuicVersionToString(version.transport_version) ==
+ GetQuicFlag(FLAGS_quic_version)) {
+ framer.set_version(version);
+ }
+ }
+ }
+ quic::QuicPacketPrinter visitor(&framer);
+ framer.set_visitor(&visitor);
+ quic::QuicEncryptedPacket encrypted(hex.c_str(), hex.length());
+ return framer.ProcessPacket(encrypted);
+}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc
new file mode 100644
index 00000000000..de66774bab3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc
@@ -0,0 +1,43 @@
+// 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.
+
+// Decodes the packet HandshakeFailureReason from the chromium histogram
+// Net.QuicClientHelloRejectReasons
+
+#include <iostream>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using quic::CryptoUtils;
+using quic::HandshakeFailureReason;
+using quic::MAX_FAILURE_REASON;
+
+int main(int argc, char* argv[]) {
+ const char* usage = "Usage: quic_reject_reason_decoder <packed_reason>";
+ std::vector<std::string> args =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+
+ if (args.size() != 1) {
+ std::cerr << usage << std::endl;
+ return 1;
+ }
+
+ uint32_t packed_error = 0;
+ if (!quic::QuicTextUtils::StringToUint32(args[0], &packed_error)) {
+ std::cerr << "Unable to parse: " << args[0] << "\n";
+ return 2;
+ }
+
+ for (int i = 1; i < MAX_FAILURE_REASON; ++i) {
+ if ((packed_error & (1 << (i - 1))) == 0) {
+ continue;
+ }
+ HandshakeFailureReason reason = static_cast<HandshakeFailureReason>(i);
+ std::cout << CryptoUtils::HandshakeFailureReasonToString(reason) << "\n";
+ }
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc
new file mode 100644
index 00000000000..d67bc3078d0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server.cc
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+#include <errno.h>
+#include <features.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include <cstdint>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/quic/platform/impl/quic_epoll_clock.h"
+#include "net/quic/platform/impl/quic_socket_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace quic {
+
+namespace {
+
+const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
+const char kSourceAddressTokenSecret[] = "secret";
+
+} // namespace
+
+const size_t kNumSessionsToCreatePerSocketEvent = 16;
+
+QuicServer::QuicServer(std::unique_ptr<ProofSource> proof_source,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicServer(std::move(proof_source),
+ QuicConfig(),
+ QuicCryptoServerConfig::ConfigOptions(),
+ AllSupportedVersions(),
+ quic_simple_server_backend,
+ kQuicDefaultConnectionIdLength) {}
+
+QuicServer::QuicServer(
+ std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const QuicCryptoServerConfig::ConfigOptions& crypto_config_options,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length)
+ : port_(0),
+ fd_(-1),
+ packets_dropped_(0),
+ overflow_supported_(false),
+ silent_close_(false),
+ config_(config),
+ crypto_config_(kSourceAddressTokenSecret,
+ QuicRandom::GetInstance(),
+ std::move(proof_source),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ crypto_config_options_(crypto_config_options),
+ version_manager_(supported_versions),
+ packet_reader_(new QuicPacketReader()),
+ quic_simple_server_backend_(quic_simple_server_backend),
+ expected_connection_id_length_(expected_connection_id_length) {
+ Initialize();
+}
+
+void QuicServer::Initialize() {
+ // If an initial flow control window has not explicitly been set, then use a
+ // sensible value for a server: 1 MB for session, 64 KB for each stream.
+ const uint32_t kInitialSessionFlowControlWindow = 1 * 1024 * 1024; // 1 MB
+ const uint32_t kInitialStreamFlowControlWindow = 64 * 1024; // 64 KB
+ if (config_.GetInitialStreamFlowControlWindowToSend() ==
+ kMinimumFlowControlSendWindow) {
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindow);
+ }
+ if (config_.GetInitialSessionFlowControlWindowToSend() ==
+ kMinimumFlowControlSendWindow) {
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindow);
+ }
+
+ epoll_server_.set_timeout_in_us(50 * 1000);
+
+ QuicEpollClock clock(&epoll_server_);
+
+ std::unique_ptr<CryptoHandshakeMessage> scfg(crypto_config_.AddDefaultConfig(
+ QuicRandom::GetInstance(), &clock, crypto_config_options_));
+}
+
+QuicServer::~QuicServer() = default;
+
+bool QuicServer::CreateUDPSocketAndListen(const QuicSocketAddress& address) {
+ fd_ = QuicSocketUtils::CreateUDPSocket(
+ address,
+ /*receive_buffer_size =*/kDefaultSocketReceiveBuffer,
+ /*send_buffer_size =*/kDefaultSocketReceiveBuffer, &overflow_supported_);
+ if (fd_ < 0) {
+ QUIC_LOG(ERROR) << "CreateSocket() failed: " << strerror(errno);
+ return false;
+ }
+
+ sockaddr_storage addr = address.generic_address();
+ int rc = bind(fd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+ if (rc < 0) {
+ QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno);
+ return false;
+ }
+ QUIC_LOG(INFO) << "Listening on " << address.ToString();
+ port_ = address.port();
+ if (port_ == 0) {
+ QuicSocketAddress address;
+ if (address.FromSocket(fd_) != 0) {
+ QUIC_LOG(ERROR) << "Unable to get self address. Error: "
+ << strerror(errno);
+ }
+ port_ = address.port();
+ }
+
+ epoll_server_.RegisterFD(fd_, this, kEpollFlags);
+ dispatcher_.reset(CreateQuicDispatcher());
+ dispatcher_->InitializeWithWriter(CreateWriter(fd_));
+
+ return true;
+}
+
+QuicPacketWriter* QuicServer::CreateWriter(int fd) {
+ return new QuicDefaultPacketWriter(fd);
+}
+
+QuicDispatcher* QuicServer::CreateQuicDispatcher() {
+ QuicEpollAlarmFactory alarm_factory(&epoll_server_);
+ return new QuicSimpleDispatcher(
+ &config_, &crypto_config_, &version_manager_,
+ std::unique_ptr<QuicEpollConnectionHelper>(new QuicEpollConnectionHelper(
+ &epoll_server_, QuicAllocator::BUFFER_POOL)),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())),
+ std::unique_ptr<QuicEpollAlarmFactory>(
+ new QuicEpollAlarmFactory(&epoll_server_)),
+ quic_simple_server_backend_, expected_connection_id_length_);
+}
+
+void QuicServer::WaitForEvents() {
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+}
+
+void QuicServer::Shutdown() {
+ if (!silent_close_) {
+ // Before we shut down the epoll server, give all active sessions a chance
+ // to notify clients that they're closing.
+ dispatcher_->Shutdown();
+ }
+
+ epoll_server_.Shutdown();
+
+ close(fd_);
+ fd_ = -1;
+}
+
+void QuicServer::OnEvent(int fd, QuicEpollEvent* event) {
+ DCHECK_EQ(fd, fd_);
+ event->out_ready_mask = 0;
+
+ if (event->in_events & EPOLLIN) {
+ QUIC_DVLOG(1) << "EPOLLIN";
+
+ dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);
+
+ bool more_to_read = true;
+ while (more_to_read) {
+ more_to_read = packet_reader_->ReadAndDispatchPackets(
+ fd_, port_, QuicEpollClock(&epoll_server_), dispatcher_.get(),
+ overflow_supported_ ? &packets_dropped_ : nullptr);
+ }
+
+ if (dispatcher_->HasChlosBuffered()) {
+ // Register EPOLLIN event to consume buffered CHLO(s).
+ event->out_ready_mask |= EPOLLIN;
+ }
+ }
+ if (event->in_events & EPOLLOUT) {
+ dispatcher_->OnCanWrite();
+ if (dispatcher_->HasPendingWrites()) {
+ event->out_ready_mask |= EPOLLOUT;
+ }
+ }
+ if (event->in_events & EPOLLERR) {
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server.h b/chromium/net/third_party/quiche/src/quic/tools/quic_server.h
new file mode 100644
index 00000000000..4d7e08199a9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A toy server, which listens on a specified address for QUIC traffic and
+// handles incoming responses.
+//
+// Note that this server is intended to verify correctness of the client and is
+// in no way expected to be performant.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SERVER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SERVER_H_
+
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packet_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+
+namespace quic {
+
+namespace test {
+class QuicServerPeer;
+} // namespace test
+
+class QuicDispatcher;
+class QuicPacketReader;
+
+class QuicServer : public QuicEpollCallbackInterface {
+ public:
+ QuicServer(std::unique_ptr<ProofSource> proof_source,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicServer(std::unique_ptr<ProofSource> proof_source,
+ const QuicConfig& config,
+ const QuicCryptoServerConfig::ConfigOptions& server_config_options,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length);
+ QuicServer(const QuicServer&) = delete;
+ QuicServer& operator=(const QuicServer&) = delete;
+
+ ~QuicServer() override;
+
+ std::string Name() const override { return "QuicServer"; }
+
+ // Start listening on the specified address.
+ bool CreateUDPSocketAndListen(const QuicSocketAddress& address);
+
+ // Wait up to 50ms, and handle any events which occur.
+ void WaitForEvents();
+
+ // Server deletion is imminent. Start cleaning up the epoll server.
+ virtual void Shutdown();
+
+ // From EpollCallbackInterface
+ void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override {}
+ void OnModification(int fd, int event_mask) override {}
+ void OnEvent(int fd, QuicEpollEvent* event) override;
+ void OnUnregistration(int fd, bool replaced) override {}
+
+ void OnShutdown(QuicEpollServer* eps, int fd) override {}
+
+ void SetChloMultiplier(size_t multiplier) {
+ crypto_config_.set_chlo_multiplier(multiplier);
+ }
+
+ void SetPreSharedKey(QuicStringPiece key) {
+ crypto_config_.set_pre_shared_key(key);
+ }
+
+ bool overflow_supported() { return overflow_supported_; }
+
+ QuicPacketCount packets_dropped() { return packets_dropped_; }
+
+ int port() { return port_; }
+
+ protected:
+ virtual QuicPacketWriter* CreateWriter(int fd);
+
+ virtual QuicDispatcher* CreateQuicDispatcher();
+
+ const QuicConfig& config() const { return config_; }
+ const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; }
+ QuicEpollServer* epoll_server() { return &epoll_server_; }
+
+ QuicDispatcher* dispatcher() { return dispatcher_.get(); }
+
+ QuicVersionManager* version_manager() { return &version_manager_; }
+
+ QuicSimpleServerBackend* server_backend() {
+ return quic_simple_server_backend_;
+ }
+
+ void set_silent_close(bool value) { silent_close_ = value; }
+
+ uint8_t expected_connection_id_length() {
+ return expected_connection_id_length_;
+ }
+
+ private:
+ friend class quic::test::QuicServerPeer;
+
+ // Initialize the internal state of the server.
+ void Initialize();
+
+ // Accepts data from the framer and demuxes clients to sessions.
+ std::unique_ptr<QuicDispatcher> dispatcher_;
+ // Frames incoming packets and hands them to the dispatcher.
+ QuicEpollServer epoll_server_;
+
+ // The port the server is listening on.
+ int port_;
+
+ // Listening connection. Also used for outbound client communication.
+ int fd_;
+
+ // If overflow_supported_ is true this will be the number of packets dropped
+ // during the lifetime of the server. This may overflow if enough packets
+ // are dropped.
+ QuicPacketCount packets_dropped_;
+
+ // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
+ // because the socket would otherwise overflow.
+ bool overflow_supported_;
+
+ // If true, do not call Shutdown on the dispatcher. Connections will close
+ // without sending a final connection close.
+ bool silent_close_;
+
+ // config_ contains non-crypto parameters that are negotiated in the crypto
+ // handshake.
+ QuicConfig config_;
+ // crypto_config_ contains crypto parameters for the handshake.
+ QuicCryptoServerConfig crypto_config_;
+ // crypto_config_options_ contains crypto parameters for the handshake.
+ QuicCryptoServerConfig::ConfigOptions crypto_config_options_;
+
+ // Used to generate current supported versions.
+ QuicVersionManager version_manager_;
+
+ // Point to a QuicPacketReader object on the heap. The reader allocates more
+ // space than allowed on the stack.
+ std::unique_ptr<QuicPacketReader> packet_reader_;
+
+ QuicSimpleServerBackend* quic_simple_server_backend_; // unowned.
+
+ // Connection ID length expected to be read on incoming IETF short headers.
+ uint8_t expected_connection_id_length_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SERVER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc
new file mode 100644
index 00000000000..128c873dfda
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server_bin.cc
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A binary wrapper for QuicServer. It listens forever on --port
+// (default 6121) until it's killed or ctrl-cd to death.
+
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t,
+ port,
+ 6121,
+ "The port the quic server will listen on.");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ std::string,
+ quic_response_cache_dir,
+ "",
+ "Specifies the directory used during QuicHttpResponseCache "
+ "construction to seed the cache. Cache directory can be "
+ "generated using `wget -p --save-headers <url>`");
+
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+ int32_t,
+ quic_ietf_draft,
+ 0,
+ "QUIC IETF draft number to use over the wire, e.g. 18. "
+ "This also enables required internal QUIC flags.");
+
+int main(int argc, char* argv[]) {
+ const char* usage = "Usage: quic_server [options]";
+ std::vector<std::string> non_option_args =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+ if (!non_option_args.empty()) {
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ exit(0);
+ }
+
+ const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft);
+ if (quic_ietf_draft > 0) {
+ quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft);
+ quic::QuicEnableVersion(
+ quic::ParsedQuicVersion(quic::PROTOCOL_TLS1_3, quic::QUIC_VERSION_99));
+ }
+
+ quic::QuicMemoryCacheBackend memory_cache_backend;
+ if (!GetQuicFlag(FLAGS_quic_response_cache_dir).empty()) {
+ memory_cache_backend.InitializeBackend(
+ GetQuicFlag(FLAGS_quic_response_cache_dir));
+ }
+
+ quic::QuicServer server(quic::CreateDefaultProofSource(),
+ &memory_cache_backend);
+
+ if (!server.CreateUDPSocketAndListen(quic::QuicSocketAddress(
+ quic::QuicIpAddress::Any6(), GetQuicFlag(FLAGS_port)))) {
+ return 1;
+ }
+
+ while (true) {
+ server.WaitForEvents();
+ }
+}
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc
new file mode 100644
index 00000000000..bb007cbb49a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_server_test.cc
@@ -0,0 +1,215 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_server.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
+#include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+
+namespace quic {
+namespace test {
+
+using ::testing::_;
+
+namespace {
+
+class MockQuicSimpleDispatcher : public QuicSimpleDispatcher {
+ public:
+ MockQuicSimpleDispatcher(
+ const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleDispatcher(config,
+ crypto_config,
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ quic_simple_server_backend,
+ kQuicDefaultConnectionIdLength) {}
+ ~MockQuicSimpleDispatcher() override = default;
+
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_CONST_METHOD0(HasPendingWrites, bool());
+ MOCK_CONST_METHOD0(HasChlosBuffered, bool());
+ MOCK_METHOD1(ProcessBufferedChlos, void(size_t));
+};
+
+class TestQuicServer : public QuicServer {
+ public:
+ TestQuicServer()
+ : QuicServer(crypto_test_utils::ProofSourceForTesting(),
+ &quic_simple_server_backend_) {}
+
+ ~TestQuicServer() override = default;
+
+ MockQuicSimpleDispatcher* mock_dispatcher() { return mock_dispatcher_; }
+
+ protected:
+ QuicDispatcher* CreateQuicDispatcher() override {
+ mock_dispatcher_ = new MockQuicSimpleDispatcher(
+ &config(), &crypto_config(), version_manager(),
+ std::unique_ptr<QuicEpollConnectionHelper>(
+ new QuicEpollConnectionHelper(epoll_server(),
+ QuicAllocator::BUFFER_POOL)),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(QuicRandom::GetInstance())),
+ std::unique_ptr<QuicEpollAlarmFactory>(
+ new QuicEpollAlarmFactory(epoll_server())),
+ &quic_simple_server_backend_);
+ return mock_dispatcher_;
+ }
+
+ MockQuicSimpleDispatcher* mock_dispatcher_ = nullptr;
+ QuicMemoryCacheBackend quic_simple_server_backend_;
+};
+
+class QuicServerEpollInTest : public QuicTest {
+ public:
+ QuicServerEpollInTest()
+ : port_(QuicPickUnusedPortOrDie()),
+ server_address_(TestLoopback(), port_) {}
+
+ void StartListening() {
+ server_.CreateUDPSocketAndListen(server_address_);
+ ASSERT_TRUE(QuicServerPeer::SetSmallSocket(&server_));
+
+ if (!server_.overflow_supported()) {
+ QUIC_LOG(WARNING) << "Overflow not supported. Not testing.";
+ return;
+ }
+ }
+
+ protected:
+ int port_;
+ QuicSocketAddress server_address_;
+ TestQuicServer server_;
+};
+
+// Tests that if dispatcher has CHLOs waiting for connection creation, EPOLLIN
+// event should try to create connections for them. And set epoll mask with
+// EPOLLIN if there are still CHLOs remaining at the end of epoll event.
+TEST_F(QuicServerEpollInTest, ProcessBufferedCHLOsOnEpollin) {
+ // Given an EPOLLIN event, try to create session for buffered CHLOs. In first
+ // event, dispatcher can't create session for all of CHLOs. So listener should
+ // register another EPOLLIN event by itself. Even without new packet arrival,
+ // the rest CHLOs should be process in next epoll event.
+ StartListening();
+ bool more_chlos = true;
+ MockQuicSimpleDispatcher* dispatcher_ = server_.mock_dispatcher();
+ DCHECK(dispatcher_ != nullptr);
+ EXPECT_CALL(*dispatcher_, OnCanWrite()).Times(testing::AnyNumber());
+ EXPECT_CALL(*dispatcher_, ProcessBufferedChlos(_)).Times(2);
+ EXPECT_CALL(*dispatcher_, HasPendingWrites()).Times(testing::AnyNumber());
+ // Expect there are still CHLOs buffered after 1st event. But not any more
+ // after 2nd event.
+ EXPECT_CALL(*dispatcher_, HasChlosBuffered())
+ .WillOnce(testing::Return(true))
+ .WillOnce(
+ DoAll(testing::Assign(&more_chlos, false), testing::Return(false)));
+
+ // Send a packet to trigger epoll event.
+ int fd = socket(
+ AddressFamilyUnderTest() == IpAddressFamily::IP_V4 ? AF_INET : AF_INET6,
+ SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+ ASSERT_LT(0, fd);
+
+ char buf[1024];
+ memset(buf, 0, QUIC_ARRAYSIZE(buf));
+ sockaddr_storage storage = server_address_.generic_address();
+ int rc = sendto(fd, buf, QUIC_ARRAYSIZE(buf), 0,
+ reinterpret_cast<sockaddr*>(&storage), sizeof(storage));
+ if (rc < 0) {
+ QUIC_DLOG(INFO) << errno << " " << strerror(errno);
+ }
+
+ while (more_chlos) {
+ server_.WaitForEvents();
+ }
+}
+
+class QuicServerDispatchPacketTest : public QuicTest {
+ public:
+ QuicServerDispatchPacketTest()
+ : crypto_config_("blah",
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ version_manager_(AllSupportedVersions()),
+ dispatcher_(
+ &config_,
+ &crypto_config_,
+ &version_manager_,
+ std::unique_ptr<QuicEpollConnectionHelper>(
+ new QuicEpollConnectionHelper(&eps_,
+ QuicAllocator::BUFFER_POOL)),
+ std::unique_ptr<QuicCryptoServerStream::Helper>(
+ new QuicSimpleCryptoServerStreamHelper(
+ QuicRandom::GetInstance())),
+ std::unique_ptr<QuicEpollAlarmFactory>(
+ new QuicEpollAlarmFactory(&eps_)),
+ &quic_simple_server_backend_) {
+ dispatcher_.InitializeWithWriter(new QuicDefaultPacketWriter(1234));
+ }
+
+ void DispatchPacket(const QuicReceivedPacket& packet) {
+ QuicSocketAddress client_addr, server_addr;
+ dispatcher_.ProcessPacket(server_addr, client_addr, packet);
+ }
+
+ protected:
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ QuicVersionManager version_manager_;
+ QuicEpollServer eps_;
+ QuicMemoryCacheBackend quic_simple_server_backend_;
+ MockQuicDispatcher dispatcher_;
+};
+
+TEST_F(QuicServerDispatchPacketTest, DispatchPacket) {
+ // clang-format off
+ unsigned char valid_packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00
+ };
+ // clang-format on
+ QuicReceivedPacket encrypted_valid_packet(
+ reinterpret_cast<char*>(valid_packet), QUIC_ARRAYSIZE(valid_packet),
+ QuicTime::Zero(), false);
+
+ EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _)).Times(1);
+ DispatchPacket(encrypted_valid_packet);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..7cc875b5a63
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.cc
@@ -0,0 +1,18 @@
+// 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 "net/third_party/quiche/src/quic/tools/quic_simple_client_session.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+std::unique_ptr<QuicSpdyClientStream>
+QuicSimpleClientSession::CreateClientStream() {
+ return QuicMakeUnique<QuicSimpleClientStream>(
+ GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL,
+ drop_response_body_);
+}
+
+} // 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
new file mode 100644
index 00000000000..1a17f3d74f3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_session.h
@@ -0,0 +1,38 @@
+// 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_TOOLS_QUIC_SIMPLE_CLIENT_SESSION_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_SESSION_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h"
+
+namespace quic {
+
+class QuicSimpleClientSession : public QuicSpdyClientSession {
+ public:
+ QuicSimpleClientSession(const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config,
+ QuicClientPushPromiseIndex* push_promise_index,
+ bool drop_response_body)
+ : QuicSpdyClientSession(config,
+ supported_versions,
+ connection,
+ server_id,
+ crypto_config,
+ push_promise_index),
+ drop_response_body_(drop_response_body) {}
+
+ std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override;
+
+ private:
+ const bool drop_response_body_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_SESSION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.cc
new file mode 100644
index 00000000000..a627007972c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.cc
@@ -0,0 +1,33 @@
+// 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 "net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h"
+
+namespace quic {
+
+void QuicSimpleClientStream::OnBodyAvailable() {
+ if (!drop_response_body_) {
+ QuicSpdyClientStream::OnBodyAvailable();
+ return;
+ }
+
+ while (HasBytesToRead()) {
+ struct iovec iov;
+ if (GetReadableRegions(&iov, 1) == 0) {
+ break;
+ }
+ MarkConsumed(iov.iov_len);
+ }
+ if (sequencer()->IsClosed()) {
+ OnFinRead();
+ } else {
+ sequencer()->SetUnblocked();
+ }
+}
+
+void QuicSimpleClientStream::OnStopSending(uint16_t code) {
+ last_stop_sending_code_ = code;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h
new file mode 100644
index 00000000000..f1eb653bea4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_client_stream.h
@@ -0,0 +1,34 @@
+// 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_TOOLS_QUIC_SIMPLE_CLIENT_STREAM_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_STREAM_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+
+namespace quic {
+
+class QuicSimpleClientStream : public QuicSpdyClientStream {
+ public:
+ QuicSimpleClientStream(QuicStreamId id,
+ QuicSpdyClientSession* session,
+ StreamType type,
+ bool drop_response_body)
+ : QuicSpdyClientStream(id, session, type),
+ drop_response_body_(drop_response_body),
+ last_stop_sending_code_(0) {}
+ void OnBodyAvailable() override;
+ void OnStopSending(uint16_t code) override;
+
+ uint16_t last_stop_sending_code() { return last_stop_sending_code_; }
+ private:
+ const bool drop_response_body_;
+ // Application code value that was in the most recently received
+ // STOP_SENDING frame for this stream.
+ uint16_t last_stop_sending_code_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_STREAM_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc
new file mode 100644
index 00000000000..2bb9656cb2f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+
+namespace quic {
+
+QuicSimpleCryptoServerStreamHelper::QuicSimpleCryptoServerStreamHelper(
+ QuicRandom* random)
+ : random_(random) {}
+
+QuicSimpleCryptoServerStreamHelper::~QuicSimpleCryptoServerStreamHelper() =
+ default;
+
+QuicConnectionId
+QuicSimpleCryptoServerStreamHelper::GenerateConnectionIdForReject(
+ QuicTransportVersion /*version*/,
+ QuicConnectionId /*connection_id*/) const {
+ return QuicUtils::CreateRandomConnectionId(random_);
+}
+
+bool QuicSimpleCryptoServerStreamHelper::CanAcceptClientHello(
+ const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const {
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h
new file mode 100644
index 00000000000..f8a58f6f4d0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+
+namespace quic {
+
+// Simple helper for server crypto streams which generates a new random
+// connection ID for stateless rejects.
+class QuicSimpleCryptoServerStreamHelper
+ : public QuicCryptoServerStream::Helper {
+ public:
+ explicit QuicSimpleCryptoServerStreamHelper(QuicRandom* random);
+
+ ~QuicSimpleCryptoServerStreamHelper() override;
+
+ QuicConnectionId GenerateConnectionIdForReject(
+ QuicTransportVersion /*version*/,
+ QuicConnectionId /*connection_id*/) const override;
+
+ bool CanAcceptClientHello(const CryptoHandshakeMessage& message,
+ const QuicSocketAddress& client_address,
+ const QuicSocketAddress& peer_address,
+ const QuicSocketAddress& self_address,
+ std::string* error_details) const override;
+
+ private:
+ QuicRandom* random_; // Unowned.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CRYPTO_SERVER_STREAM_HELPER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc
new file mode 100644
index 00000000000..4ede5889a9f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc
@@ -0,0 +1,25 @@
+// 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 "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_random.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+class QuicSimpleCryptoServerStreamHelperTest : public QuicTest {};
+
+TEST_F(QuicSimpleCryptoServerStreamHelperTest, GenerateConnectionIdForReject) {
+ test::MockRandom random;
+ QuicSimpleCryptoServerStreamHelper helper(&random);
+
+ EXPECT_EQ(QuicUtils::CreateRandomConnectionId(&random),
+ helper.GenerateConnectionIdForReject(QUIC_VERSION_99,
+ test::TestConnectionId()));
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc
new file mode 100644
index 00000000000..4706b606843
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h"
+
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+namespace quic {
+
+QuicSimpleDispatcher::QuicSimpleDispatcher(
+ const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length)
+ : QuicDispatcher(config,
+ crypto_config,
+ version_manager,
+ std::move(helper),
+ std::move(session_helper),
+ std::move(alarm_factory),
+ expected_connection_id_length),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+QuicSimpleDispatcher::~QuicSimpleDispatcher() = default;
+
+int QuicSimpleDispatcher::GetRstErrorCount(
+ QuicRstStreamErrorCode error_code) const {
+ auto it = rst_error_map_.find(error_code);
+ if (it == rst_error_map_.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+void QuicSimpleDispatcher::OnRstStreamReceived(
+ const QuicRstStreamFrame& frame) {
+ auto it = rst_error_map_.find(frame.error_code);
+ if (it == rst_error_map_.end()) {
+ rst_error_map_.insert(std::make_pair(frame.error_code, 1));
+ } else {
+ it->second++;
+ }
+}
+
+QuicServerSessionBase* QuicSimpleDispatcher::CreateQuicSession(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address,
+ QuicStringPiece /*alpn*/,
+ const ParsedQuicVersion& version) {
+ // The QuicServerSessionBase takes ownership of |connection| below.
+ QuicConnection* connection = new QuicConnection(
+ connection_id, client_address, helper(), alarm_factory(), writer(),
+ /* owns_writer= */ false, Perspective::IS_SERVER,
+ ParsedQuicVersionVector{version});
+
+ QuicServerSessionBase* session = new QuicSimpleServerSession(
+ config(), GetSupportedVersions(), connection, this, session_helper(),
+ crypto_config(), compressed_certs_cache(), quic_simple_server_backend_);
+ session->Initialize();
+ return session;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h
new file mode 100644
index 00000000000..46d976c7b5b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_DISPATCHER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_DISPATCHER_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+#include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+
+namespace quic {
+
+class QuicSimpleDispatcher : public QuicDispatcher {
+ public:
+ QuicSimpleDispatcher(
+ const QuicConfig* config,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicVersionManager* version_manager,
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
+ std::unique_ptr<QuicCryptoServerStream::Helper> session_helper,
+ std::unique_ptr<QuicAlarmFactory> alarm_factory,
+ QuicSimpleServerBackend* quic_simple_server_backend,
+ uint8_t expected_connection_id_length);
+
+ ~QuicSimpleDispatcher() override;
+
+ int GetRstErrorCount(QuicRstStreamErrorCode rst_error_code) const;
+
+ void OnRstStreamReceived(const QuicRstStreamFrame& frame) override;
+
+ protected:
+ QuicServerSessionBase* CreateQuicSession(
+ QuicConnectionId connection_id,
+ const QuicSocketAddress& client_address,
+ QuicStringPiece alpn,
+ const ParsedQuicVersion& version) override;
+
+ QuicSimpleServerBackend* server_backend() {
+ return quic_simple_server_backend_;
+ }
+
+ private:
+ QuicSimpleServerBackend* quic_simple_server_backend_; // Unowned.
+
+ // The map of the reset error code with its counter.
+ std::map<QuicRstStreamErrorCode, int> rst_error_map_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_DISPATCHER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h
new file mode 100644
index 00000000000..74afb60c2d2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_
+
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+
+namespace spdy {
+class SpdyHeaderBlock;
+} // namespace spdy
+
+namespace quic {
+
+// This interface implements the functionality to fetch a response
+// from the backend (such as cache, http-proxy etc) to serve
+// requests received by a Quic Server
+class QuicSimpleServerBackend {
+ public:
+ // This interface implements the methods
+ // called by the QuicSimpleServerBackend implementation
+ // to process the request in the backend
+ class RequestHandler {
+ public:
+ virtual ~RequestHandler() {}
+
+ virtual QuicConnectionId connection_id() const = 0;
+ virtual QuicStreamId stream_id() const = 0;
+ virtual std::string peer_host() const = 0;
+ // Called when the response is ready at the backend and can be send back to
+ // the QUIC client.
+ virtual void OnResponseBackendComplete(
+ const QuicBackendResponse* response,
+ std::list<QuicBackendResponse::ServerPushInfo> resources) = 0;
+ };
+
+ virtual ~QuicSimpleServerBackend() = default;
+ // This method initializes the backend instance to fetch responses
+ // from a backend server, in-memory cache etc.
+ virtual bool InitializeBackend(const std::string& backend_url) = 0;
+ // Returns true if the backend has been successfully initialized
+ // and could be used to fetch HTTP requests
+ virtual bool IsBackendInitialized() const = 0;
+ // Triggers a HTTP request to be sent to the backend server or cache
+ // If response is immediately available, the function synchronously calls
+ // the |request_handler| with the HTTP response.
+ // If the response has to be fetched over the network, the function
+ // asynchronously calls |request_handler| with the HTTP response.
+ virtual void FetchResponseFromBackend(
+ const spdy::SpdyHeaderBlock& request_headers,
+ const std::string& request_body,
+ RequestHandler* request_handler) = 0;
+ // Clears the state of the backend instance
+ virtual void CloseBackendResponseStream(RequestHandler* request_handler) = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_BACKEND_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc
new file mode 100644
index 00000000000..a722d373dd7
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.cc
@@ -0,0 +1,228 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+namespace quic {
+
+QuicSimpleServerSession::QuicSimpleServerSession(
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicServerSessionBase(config,
+ supported_versions,
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache),
+ highest_promised_stream_id_(
+ QuicUtils::GetInvalidStreamId(connection->transport_version())),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+QuicSimpleServerSession::~QuicSimpleServerSession() {
+ delete connection();
+}
+
+QuicCryptoServerStreamBase*
+QuicSimpleServerSession::CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) {
+ return new QuicCryptoServerStream(
+ crypto_config, compressed_certs_cache,
+ GetQuicReloadableFlag(enable_quic_stateless_reject_support), this,
+ stream_helper());
+}
+
+void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) {
+ if (!IsIncomingStream(frame.stream_id)) {
+ QUIC_LOG(WARNING) << "Client shouldn't send data on server push stream";
+ connection()->CloseConnection(
+ QUIC_INVALID_STREAM_ID, "Client sent data on server push stream",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ QuicSpdySession::OnStreamFrame(frame);
+}
+
+void QuicSimpleServerSession::PromisePushResources(
+ const std::string& request_url,
+ const std::list<QuicBackendResponse::ServerPushInfo>& resources,
+ QuicStreamId original_stream_id,
+ const spdy::SpdyHeaderBlock& original_request_headers) {
+ if (!server_push_enabled()) {
+ return;
+ }
+
+ for (QuicBackendResponse::ServerPushInfo resource : resources) {
+ spdy::SpdyHeaderBlock headers = SynthesizePushRequestHeaders(
+ request_url, resource, original_request_headers);
+ highest_promised_stream_id_ +=
+ QuicUtils::StreamIdDelta(connection()->transport_version());
+ SendPushPromise(original_stream_id, highest_promised_stream_id_,
+ headers.Clone());
+ promised_streams_.push_back(PromisedStreamInfo(
+ std::move(headers), highest_promised_stream_id_, resource.priority));
+ }
+
+ // Procese promised push request as many as possible.
+ HandlePromisedPushRequests();
+}
+
+QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) {
+ if (!ShouldCreateIncomingStream(id)) {
+ return nullptr;
+ }
+
+ QuicSpdyStream* stream = new QuicSimpleServerStream(
+ id, this, BIDIRECTIONAL, quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+}
+
+QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(
+ PendingStream pending) {
+ QuicSpdyStream* stream = new QuicSimpleServerStream(
+ std::move(pending), this, BIDIRECTIONAL, quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+}
+
+QuicSimpleServerStream*
+QuicSimpleServerSession::CreateOutgoingBidirectionalStream() {
+ DCHECK(false);
+ return nullptr;
+}
+
+QuicSimpleServerStream*
+QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() {
+ if (!ShouldCreateOutgoingUnidirectionalStream()) {
+ return nullptr;
+ }
+
+ QuicSimpleServerStream* stream = new QuicSimpleServerStream(
+ GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL,
+ quic_simple_server_backend_);
+ ActivateStream(QuicWrapUnique(stream));
+ return stream;
+}
+
+void QuicSimpleServerSession::HandleFrameOnNonexistentOutgoingStream(
+ QuicStreamId stream_id) {
+ // If this stream is a promised but not created stream (stream_id within the
+ // range of next_outgoing_stream_id_ and highes_promised_stream_id_),
+ // connection shouldn't be closed.
+ // Otherwise behave in the same way as base class.
+ if (highest_promised_stream_id_ ==
+ QuicUtils::GetInvalidStreamId(connection()->transport_version()) ||
+ stream_id > highest_promised_stream_id_) {
+ QuicSession::HandleFrameOnNonexistentOutgoingStream(stream_id);
+ }
+}
+
+void QuicSimpleServerSession::HandleRstOnValidNonexistentStream(
+ const QuicRstStreamFrame& frame) {
+ QuicSession::HandleRstOnValidNonexistentStream(frame);
+ if (!IsClosedStream(frame.stream_id)) {
+ // If a nonexistent stream is not a closed stream and still valid, it must
+ // be a locally preserved stream. Resetting this kind of stream means
+ // cancelling the promised server push.
+ // Since PromisedStreamInfo are queued in sequence, the corresponding
+ // index for it in promised_streams_ can be calculated.
+ QuicStreamId next_stream_id = next_outgoing_unidirectional_stream_id();
+ if (connection()->transport_version() == QUIC_VERSION_99) {
+ DCHECK(!QuicUtils::IsBidirectionalStreamId(frame.stream_id));
+ }
+ DCHECK_GE(frame.stream_id, next_stream_id);
+ size_t index = (frame.stream_id - next_stream_id) /
+ QuicUtils::StreamIdDelta(connection()->transport_version());
+ DCHECK_LE(index, promised_streams_.size());
+ promised_streams_[index].is_cancelled = true;
+ control_frame_manager().WriteOrBufferRstStream(frame.stream_id,
+ QUIC_RST_ACKNOWLEDGEMENT, 0);
+ connection()->OnStreamReset(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT);
+ }
+}
+
+spdy::SpdyHeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders(
+ std::string request_url,
+ QuicBackendResponse::ServerPushInfo resource,
+ const spdy::SpdyHeaderBlock& original_request_headers) {
+ QuicUrl push_request_url = resource.request_url;
+
+ spdy::SpdyHeaderBlock spdy_headers = original_request_headers.Clone();
+ // :authority could be different from original request.
+ spdy_headers[":authority"] = push_request_url.host();
+ spdy_headers[":path"] = push_request_url.path();
+ // Push request always use GET.
+ spdy_headers[":method"] = "GET";
+ spdy_headers["referer"] = request_url;
+ spdy_headers[":scheme"] = push_request_url.scheme();
+ // It is not possible to push a response to a request that includes a request
+ // body.
+ spdy_headers["content-length"] = "0";
+ // Remove "host" field as push request is a directly generated HTTP2 request
+ // which should use ":authority" instead of "host".
+ spdy_headers.erase("host");
+ return spdy_headers;
+}
+
+void QuicSimpleServerSession::SendPushPromise(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ spdy::SpdyHeaderBlock headers) {
+ QUIC_DLOG(INFO) << "stream " << original_stream_id
+ << " send PUSH_PROMISE for promised stream "
+ << promised_stream_id;
+ WritePushPromise(original_stream_id, promised_stream_id, std::move(headers));
+}
+
+void QuicSimpleServerSession::HandlePromisedPushRequests() {
+ while (!promised_streams_.empty() &&
+ ShouldCreateOutgoingUnidirectionalStream()) {
+ PromisedStreamInfo& promised_info = promised_streams_.front();
+ DCHECK_EQ(next_outgoing_unidirectional_stream_id(),
+ promised_info.stream_id);
+
+ if (promised_info.is_cancelled) {
+ // This stream has been reset by client. Skip this stream id.
+ promised_streams_.pop_front();
+ GetNextOutgoingUnidirectionalStreamId();
+ return;
+ }
+
+ QuicSimpleServerStream* promised_stream =
+ static_cast<QuicSimpleServerStream*>(
+ CreateOutgoingUnidirectionalStream());
+ DCHECK_NE(promised_stream, nullptr);
+ DCHECK_EQ(promised_info.stream_id, promised_stream->id());
+ QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id();
+
+ promised_stream->SetPriority(promised_info.priority);
+
+ spdy::SpdyHeaderBlock request_headers(
+ std::move(promised_info.request_headers));
+
+ promised_streams_.pop_front();
+ promised_stream->PushResponse(std::move(request_headers));
+ }
+}
+
+void QuicSimpleServerSession::OnCanCreateNewOutgoingStream() {
+ HandlePromisedPushRequests();
+}
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h
new file mode 100644
index 00000000000..c3705342359
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A toy server specific QuicSession subclass.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_SESSION_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_SESSION_H_
+
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_server_session_base.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+namespace quic {
+
+namespace test {
+class QuicSimpleServerSessionPeer;
+} // namespace test
+
+class QuicSimpleServerSession : public QuicServerSessionBase {
+ public:
+ // A PromisedStreamInfo is an element of the queue to store promised
+ // stream which hasn't been created yet. It keeps a mapping between promised
+ // stream id with its priority and the headers sent out in PUSH_PROMISE.
+ struct PromisedStreamInfo {
+ public:
+ PromisedStreamInfo(spdy::SpdyHeaderBlock request_headers,
+ QuicStreamId stream_id,
+ spdy::SpdyPriority priority)
+ : request_headers(std::move(request_headers)),
+ stream_id(stream_id),
+ priority(priority),
+ is_cancelled(false) {}
+ spdy::SpdyHeaderBlock request_headers;
+ QuicStreamId stream_id;
+ spdy::SpdyPriority priority;
+ bool is_cancelled;
+ };
+
+ // Takes ownership of |connection|.
+ QuicSimpleServerSession(const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicSimpleServerSession(const QuicSimpleServerSession&) = delete;
+ QuicSimpleServerSession& operator=(const QuicSimpleServerSession&) = delete;
+
+ ~QuicSimpleServerSession() override;
+
+ // Override base class to detact client sending data on server push stream.
+ void OnStreamFrame(const QuicStreamFrame& frame) override;
+
+ // Send out PUSH_PROMISE for all |resources| promised stream id in each frame
+ // will increase by 2 for each item in |resources|.
+ // And enqueue HEADERS block in those PUSH_PROMISED for sending push response
+ // later.
+ virtual void PromisePushResources(
+ const std::string& request_url,
+ const std::list<QuicBackendResponse::ServerPushInfo>& resources,
+ QuicStreamId original_stream_id,
+ const spdy::SpdyHeaderBlock& original_request_headers);
+
+ void OnCanCreateNewOutgoingStream() override;
+
+ protected:
+ // QuicSession methods:
+ QuicSpdyStream* CreateIncomingStream(QuicStreamId id) override;
+ QuicSpdyStream* CreateIncomingStream(PendingStream pending) override;
+ QuicSimpleServerStream* CreateOutgoingBidirectionalStream() override;
+ QuicSimpleServerStream* CreateOutgoingUnidirectionalStream() override;
+ // Override to return true for locally preserved server push stream.
+ void HandleFrameOnNonexistentOutgoingStream(QuicStreamId stream_id) override;
+ // Override to handle reseting locally preserved streams.
+ void HandleRstOnValidNonexistentStream(
+ const QuicRstStreamFrame& frame) override;
+
+ // QuicServerSessionBaseMethod:
+ QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache) override;
+
+ QuicSimpleServerBackend* server_backend() {
+ return quic_simple_server_backend_;
+ }
+
+ private:
+ friend class test::QuicSimpleServerSessionPeer;
+
+ // Create a server push headers block by copying request's headers block.
+ // But replace or add these pseudo-headers as they are specific to each
+ // request:
+ // :authority, :path, :method, :scheme, referer.
+ // Copying the rest headers ensures they are the same as the original
+ // request, especially cookies.
+ spdy::SpdyHeaderBlock SynthesizePushRequestHeaders(
+ std::string request_url,
+ QuicBackendResponse::ServerPushInfo resource,
+ const spdy::SpdyHeaderBlock& original_request_headers);
+
+ // Send PUSH_PROMISE frame on headers stream.
+ void SendPushPromise(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ spdy::SpdyHeaderBlock headers);
+
+ // Fetch response from cache for request headers enqueued into
+ // promised_headers_and_streams_ and send them on dedicated stream until
+ // reaches max_open_stream_ limit.
+ // Called when return value of GetNumOpenOutgoingStreams() changes:
+ // CloseStreamInner();
+ // StreamDraining();
+ // Note that updateFlowControlOnFinalReceivedByteOffset() won't change the
+ // return value becasue all push streams are impossible to become locally
+ // closed. Since a locally preserved stream becomes remotely closed after
+ // HandlePromisedPushRequests() starts to process it, and if it is reset
+ // locally afterwards, it will be immediately become closed and never get into
+ // locally_closed_stream_highest_offset_. So all the streams in this map
+ // are not outgoing streams.
+ void HandlePromisedPushRequests();
+
+ // Keep track of the highest stream id which has been sent in PUSH_PROMISE.
+ QuicStreamId highest_promised_stream_id_;
+
+ // Promised streams which hasn't been created yet because of max_open_stream_
+ // limit. New element is added to the end of the queue.
+ // Since outgoing stream is created in sequence, stream_id of each element in
+ // the queue also increases by 2 from previous one's. The front element's
+ // stream_id is always next_outgoing_stream_id_, and the last one is always
+ // highest_promised_stream_id_.
+ QuicDeque<PromisedStreamInfo> promised_streams_;
+
+ QuicSimpleServerBackend* quic_simple_server_backend_; // Not owned.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_SESSION_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc
new file mode 100644
index 00000000000..7203f128caa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_session_test.cc
@@ -0,0 +1,846 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/mock_quic_session_visitor.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+using testing::_;
+using testing::AtLeast;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+typedef QuicSimpleServerSession::PromisedStreamInfo PromisedStreamInfo;
+} // namespace
+
+class QuicSimpleServerSessionPeer {
+ public:
+ static void SetCryptoStream(QuicSimpleServerSession* s,
+ QuicCryptoServerStream* crypto_stream) {
+ s->crypto_stream_.reset(crypto_stream);
+ s->RegisterStaticStream(
+ QuicUtils::GetCryptoStreamId(s->connection()->transport_version()),
+ crypto_stream);
+ }
+
+ static QuicSpdyStream* CreateIncomingStream(QuicSimpleServerSession* s,
+ QuicStreamId id) {
+ return s->CreateIncomingStream(id);
+ }
+
+ static QuicSimpleServerStream* CreateOutgoingUnidirectionalStream(
+ QuicSimpleServerSession* s) {
+ return s->CreateOutgoingUnidirectionalStream();
+ }
+};
+
+namespace {
+
+const size_t kMaxStreamsForTest = 10;
+
+class MockQuicCryptoServerStream : public QuicCryptoServerStream {
+ public:
+ explicit MockQuicCryptoServerStream(
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicServerSessionBase* session,
+ QuicCryptoServerStream::Helper* helper)
+ : QuicCryptoServerStream(
+ crypto_config,
+ compressed_certs_cache,
+ GetQuicReloadableFlag(
+ enable_quic_stateless_reject_support), // NOLINT
+ session,
+ helper) {}
+ MockQuicCryptoServerStream(const MockQuicCryptoServerStream&) = delete;
+ MockQuicCryptoServerStream& operator=(const MockQuicCryptoServerStream&) =
+ delete;
+ ~MockQuicCryptoServerStream() override {}
+
+ MOCK_METHOD1(SendServerConfigUpdate,
+ void(const CachedNetworkParameters* cached_network_parameters));
+
+ void set_encryption_established(bool has_established) {
+ encryption_established_override_ = has_established;
+ }
+
+ bool encryption_established() const override {
+ return QuicCryptoServerStream::encryption_established() ||
+ encryption_established_override_;
+ }
+
+ private:
+ bool encryption_established_override_ = false;
+};
+
+class MockQuicConnectionWithSendStreamData : public MockQuicConnection {
+ public:
+ MockQuicConnectionWithSendStreamData(
+ MockQuicConnectionHelper* helper,
+ MockAlarmFactory* alarm_factory,
+ Perspective perspective,
+ const ParsedQuicVersionVector& supported_versions)
+ : MockQuicConnection(helper,
+ alarm_factory,
+ perspective,
+ supported_versions) {
+ auto consume_all_data = [](QuicStreamId id, size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ return QuicConsumedData(write_length, state != NO_FIN);
+ };
+ ON_CALL(*this, SendStreamData(_, _, _, _))
+ .WillByDefault(Invoke(consume_all_data));
+ }
+
+ MOCK_METHOD4(SendStreamData,
+ QuicConsumedData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+};
+
+class MockQuicSimpleServerSession : public QuicSimpleServerSession {
+ public:
+ MockQuicSimpleServerSession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSession::Visitor* visitor,
+ QuicCryptoServerStream::Helper* helper,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerSession(config,
+ CurrentSupportedVersions(),
+ connection,
+ visitor,
+ helper,
+ crypto_config,
+ compressed_certs_cache,
+ quic_simple_server_backend) {}
+ // Methods taking non-copyable types like SpdyHeaderBlock by value cannot be
+ // mocked directly.
+ size_t WritePushPromise(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ spdy::SpdyHeaderBlock headers) override {
+ return WritePushPromiseMock(original_stream_id, promised_stream_id,
+ headers);
+ }
+ MOCK_METHOD3(WritePushPromiseMock,
+ size_t(QuicStreamId original_stream_id,
+ QuicStreamId promised_stream_id,
+ const spdy::SpdyHeaderBlock& headers));
+
+ MOCK_METHOD1(SendBlocked, void(QuicStreamId));
+};
+
+class QuicSimpleServerSessionTest
+ : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ bool ClearControlFrame(const QuicFrame& frame) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+
+ // The function ensures that A) the max stream id frames get properly deleted
+ // (since the test uses a 'did we leak memory' check ... if we just lose the
+ // frame, the test fails) and B) returns true (instead of the default, false)
+ // which ensures that the rest of the system thinks that the frame actually
+ // was transmitted.
+ bool ClearMaxStreamIdControlFrame(const QuicFrame& frame) {
+ if (frame.type == MAX_STREAM_ID_FRAME) {
+ DeleteFrame(&const_cast<QuicFrame&>(frame));
+ return true;
+ }
+ return false;
+ }
+
+ protected:
+ QuicSimpleServerSessionTest()
+ : crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx()),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {
+ config_.SetMaxIncomingDynamicStreamsToSend(kMaxStreamsForTest);
+ QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(&config_,
+ kMaxStreamsForTest);
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+
+ ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam());
+ connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>(
+ &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions);
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ session_ = QuicMakeUnique<MockQuicSimpleServerSession>(
+ config_, connection_, &owner_, &stream_helper_, &crypto_config_,
+ &compressed_certs_cache_, &memory_cache_backend_);
+ MockClock clock;
+ handshake_message_ = crypto_config_.AddDefaultConfig(
+ QuicRandom::GetInstance(), &clock,
+ QuicCryptoServerConfig::ConfigOptions());
+ session_->Initialize();
+ QuicSessionPeer::GetMutableCryptoStream(session_.get())
+ ->OnSuccessfulVersionNegotiation(supported_versions.front());
+ visitor_ = QuicConnectionPeer::GetVisitor(connection_);
+
+ if (IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(
+ this,
+ &QuicSimpleServerSessionTest::ClearMaxStreamIdControlFrame));
+ }
+ session_->OnConfigNegotiated();
+ }
+
+ QuicStreamId GetNthClientInitiatedBidirectionalId(int n) {
+ return GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ QuicStreamId GetNthServerInitiatedUnidirectionalId(int n) {
+ return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), n);
+ }
+
+ bool IsVersion99() const {
+ return connection_->transport_version() == QUIC_VERSION_99;
+ }
+
+ void InjectStopSending(QuicStreamId stream_id,
+ QuicRstStreamErrorCode rst_stream_code) {
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ if (!IsVersion99()) {
+ // Only needed for version 99/IETF QUIC.
+ return;
+ }
+ EXPECT_CALL(owner_, OnStopSendingReceived(_)).Times(1);
+ QuicStopSendingFrame stop_sending(
+ kInvalidControlFrameId, stream_id,
+ static_cast<QuicApplicationErrorCode>(rst_stream_code));
+ // Expect the RESET_STREAM that is generated in response to receiving a
+ // STOP_SENDING.
+ EXPECT_CALL(*connection_, OnStreamReset(stream_id, rst_stream_code));
+ session_->OnStopSendingFrame(stop_sending);
+ }
+
+ StrictMock<MockQuicSessionVisitor> owner_;
+ StrictMock<MockQuicCryptoServerStreamHelper> stream_helper_;
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnectionWithSendStreamData>* connection_;
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicMemoryCacheBackend memory_cache_backend_;
+ std::unique_ptr<MockQuicSimpleServerSession> session_;
+ std::unique_ptr<CryptoHandshakeMessage> handshake_message_;
+ QuicConnectionVisitorInterface* visitor_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSimpleServerSessionTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSimpleServerSessionTest, CloseStreamDueToReset) {
+ // Open a stream, then reset it.
+ // Send two bytes of payload to open it.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ session_->OnStreamFrame(data1);
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+
+ // Receive a reset (and send a RST in response).
+ QuicRstStreamFrame rst1(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ if (!IsVersion99()) {
+ // For version 99, this is covered in InjectStopSending()
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst1);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ InjectStopSending(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+
+ // Send the same two bytes of payload in a new packet.
+ visitor_->OnStreamFrame(data1);
+
+ // The stream should not be re-opened.
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicSimpleServerSessionTest, NeverOpenStreamDueToReset) {
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst1(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ if (!IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ // For version 99, this is covered in InjectStopSending()
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst1);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ InjectStopSending(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+
+ // Send two bytes of payload.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ visitor_->OnStreamFrame(data1);
+
+ // The stream should never be opened, now that the reset is received.
+ EXPECT_EQ(0u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicSimpleServerSessionTest, AcceptClosedStream) {
+ // Send (empty) compressed headers followed by two bytes of data.
+ QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("\1\0\0\0\0\0\0\0HT"));
+ QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(1), false, 0,
+ QuicStringPiece("\2\0\0\0\0\0\0\0HT"));
+ visitor_->OnStreamFrame(frame1);
+ visitor_->OnStreamFrame(frame2);
+ EXPECT_EQ(2u, session_->GetNumOpenIncomingStreams());
+
+ // Send a reset (and expect the peer to send a RST in response).
+ QuicRstStreamFrame rst(kInvalidControlFrameId,
+ GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ if (!IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ // For version 99, this is covered in InjectStopSending()
+ EXPECT_CALL(*connection_,
+ OnStreamReset(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ visitor_->OnRstStream(rst);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ InjectStopSending(GetNthClientInitiatedBidirectionalId(0),
+ QUIC_ERROR_PROCESSING_STREAM);
+
+ // If we were tracking, we'd probably want to reject this because it's data
+ // past the reset point of stream 3. As it's a closed stream we just drop the
+ // data on the floor, but accept the packet because it has data for stream 5.
+ QuicStreamFrame frame3(GetNthClientInitiatedBidirectionalId(0), false, 2,
+ QuicStringPiece("TP"));
+ QuicStreamFrame frame4(GetNthClientInitiatedBidirectionalId(1), false, 2,
+ QuicStringPiece("TP"));
+ visitor_->OnStreamFrame(frame3);
+ visitor_->OnStreamFrame(frame4);
+ // The stream should never be opened, now that the reset is received.
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateIncomingStreamDisconnected) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Tests that incoming stream creation fails when connection is not connected.
+ size_t initial_num_open_stream = session_->GetNumOpenIncomingStreams();
+ QuicConnectionPeer::TearDownLocalConnectionState(connection_);
+ EXPECT_QUIC_BUG(QuicSimpleServerSessionPeer::CreateIncomingStream(
+ session_.get(), GetNthClientInitiatedBidirectionalId(0)),
+ "ShouldCreateIncomingStream called when disconnected");
+ EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenIncomingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateEvenIncomingDynamicStream) {
+ // Tests that incoming stream creation fails when given stream id is even.
+ size_t initial_num_open_stream = session_->GetNumOpenIncomingStreams();
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Client created even numbered stream", _));
+ QuicSimpleServerSessionPeer::CreateIncomingStream(
+ session_.get(), GetNthServerInitiatedUnidirectionalId(0));
+ EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenIncomingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateIncomingStream) {
+ QuicSpdyStream* stream = QuicSimpleServerSessionPeer::CreateIncomingStream(
+ session_.get(), GetNthClientInitiatedBidirectionalId(0));
+ EXPECT_NE(nullptr, stream);
+ EXPECT_EQ(GetNthClientInitiatedBidirectionalId(0), stream->id());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamDisconnected) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Tests that outgoing stream creation fails when connection is not connected.
+ size_t initial_num_open_stream = session_->GetNumOpenOutgoingStreams();
+ QuicConnectionPeer::TearDownLocalConnectionState(connection_);
+ EXPECT_QUIC_BUG(
+ QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
+ session_.get()),
+ "ShouldCreateOutgoingUnidirectionalStream called when disconnected");
+
+ EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenOutgoingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUnencrypted) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Tests that outgoing stream creation fails when encryption has not yet been
+ // established.
+ size_t initial_num_open_stream = session_->GetNumOpenOutgoingStreams();
+ EXPECT_QUIC_BUG(
+ QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
+ session_.get()),
+ "Encryption not established so no outgoing stream created.");
+ EXPECT_EQ(initial_num_open_stream, session_->GetNumOpenOutgoingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, CreateOutgoingDynamicStreamUptoLimit) {
+ // Tests that outgoing stream creation should not be affected by existing
+ // incoming stream and vice-versa. But when reaching the limit of max outgoing
+ // stream allowed, creation should fail.
+
+ // Receive some data to initiate a incoming stream which should not effect
+ // creating outgoing streams.
+ QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
+ QuicStringPiece("HT"));
+ session_->OnStreamFrame(data1);
+ EXPECT_EQ(1u, session_->GetNumOpenIncomingStreams());
+ EXPECT_EQ(0u, session_->GetNumOpenOutgoingStreams());
+
+ session_->UnregisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true);
+ // Assume encryption already established.
+ QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), nullptr);
+ MockQuicCryptoServerStream* crypto_stream =
+ new MockQuicCryptoServerStream(&crypto_config_, &compressed_certs_cache_,
+ session_.get(), &stream_helper_);
+ crypto_stream->set_encryption_established(true);
+ QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream);
+ session_->RegisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true, QuicStream::kDefaultPriority);
+
+ // Create push streams till reaching the upper limit of allowed open streams.
+ for (size_t i = 0; i < kMaxStreamsForTest; ++i) {
+ QuicSpdyStream* created_stream =
+ QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
+ session_.get());
+ EXPECT_EQ(GetNthServerInitiatedUnidirectionalId(i), created_stream->id());
+ EXPECT_EQ(i + 1, session_->GetNumOpenOutgoingStreams());
+ }
+
+ // Continuing creating push stream would fail.
+ EXPECT_EQ(nullptr,
+ QuicSimpleServerSessionPeer::CreateOutgoingUnidirectionalStream(
+ session_.get()));
+ EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
+
+ // Create peer initiated stream should have no problem.
+ QuicStreamFrame data2(GetNthClientInitiatedBidirectionalId(1), false, 0,
+ QuicStringPiece("HT"));
+ session_->OnStreamFrame(data2);
+ EXPECT_EQ(2u, session_->GetNumOpenIncomingStreams());
+}
+
+TEST_P(QuicSimpleServerSessionTest, OnStreamFrameWithEvenStreamId) {
+ QuicStreamFrame frame(GetNthServerInitiatedUnidirectionalId(0), false, 0,
+ QuicStringPiece());
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Client sent data on server push stream", _));
+ session_->OnStreamFrame(frame);
+}
+
+TEST_P(QuicSimpleServerSessionTest, GetEvenIncomingError) {
+ // Tests that calling GetOrCreateDynamicStream() on an outgoing stream not
+ // promised yet should result close connection.
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Data for nonexistent stream", _));
+ EXPECT_EQ(nullptr,
+ QuicSessionPeer::GetOrCreateDynamicStream(
+ session_.get(), GetNthServerInitiatedUnidirectionalId(1)));
+}
+
+// In order to test the case where server push stream creation goes beyond
+// limit, server push streams need to be hanging there instead of
+// immediately closing after sending back response.
+// To achieve this goal, this class resets flow control windows so that large
+// responses will not be sent fully in order to prevent push streams from being
+// closed immediately.
+// Also adjust connection-level flow control window to ensure a large response
+// can cause stream-level flow control blocked but not connection-level.
+class QuicSimpleServerSessionServerPushTest
+ : public QuicSimpleServerSessionTest {
+ protected:
+ const size_t kStreamFlowControlWindowSize = 32 * 1024; // 32KB.
+
+ QuicSimpleServerSessionServerPushTest() {
+ // Reset stream level flow control window to be 32KB.
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ &config_, kStreamFlowControlWindowSize);
+ // Reset connection level flow control window to be 1.5 MB which is large
+ // enough that it won't block any stream to write before stream level flow
+ // control blocks it.
+ QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
+ &config_, kInitialSessionFlowControlWindowForTest);
+
+ ParsedQuicVersionVector supported_versions = SupportedVersions(GetParam());
+ connection_ = new StrictMock<MockQuicConnectionWithSendStreamData>(
+ &helper_, &alarm_factory_, Perspective::IS_SERVER, supported_versions);
+ session_ = QuicMakeUnique<MockQuicSimpleServerSession>(
+ config_, connection_, &owner_, &stream_helper_, &crypto_config_,
+ &compressed_certs_cache_, &memory_cache_backend_);
+ session_->Initialize();
+ QuicSessionPeer::GetMutableCryptoStream(session_.get())
+ ->OnSuccessfulVersionNegotiation(supported_versions.front());
+ // Needed to make new session flow control window and server push work.
+
+ if (IsVersion99()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(this, &QuicSimpleServerSessionServerPushTest::
+ ClearMaxStreamIdControlFrame));
+ }
+ session_->OnConfigNegotiated();
+
+ visitor_ = QuicConnectionPeer::GetVisitor(connection_);
+
+ session_->UnregisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true);
+ QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), nullptr);
+ // Assume encryption already established.
+ MockQuicCryptoServerStream* crypto_stream = new MockQuicCryptoServerStream(
+ &crypto_config_, &compressed_certs_cache_, session_.get(),
+ &stream_helper_);
+
+ crypto_stream->set_encryption_established(true);
+ QuicSimpleServerSessionPeer::SetCryptoStream(session_.get(), crypto_stream);
+ session_->RegisterStreamPriority(
+ QuicUtils::GetHeadersStreamId(connection_->transport_version()),
+ /*is_static=*/true, QuicStream::kDefaultPriority);
+ }
+
+ // Given |num_resources|, create this number of fake push resources and push
+ // them by sending PUSH_PROMISE for all and sending push responses for as much
+ // as possible(limited by kMaxStreamsForTest).
+ // If |num_resources| > kMaxStreamsForTest, the left over will be queued.
+ // Returns the length of the DATA frame header, or 0 if the version does not
+ // use DATA frames.
+ QuicByteCount PromisePushResources(size_t num_resources) {
+ // testing::InSequence seq;
+ // To prevent push streams from being closed the response need to be larger
+ // than stream flow control window so stream won't send the full body.
+ size_t body_size = 2 * kStreamFlowControlWindowSize; // 64KB.
+
+ std::string request_url = "mail.google.com/";
+ spdy::SpdyHeaderBlock request_headers;
+ std::string resource_host = "www.google.com";
+ std::string partial_push_resource_path = "/server_push_src";
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources;
+ std::string scheme = "http";
+ QuicByteCount data_frame_header_length = 0;
+ for (unsigned int i = 1; i <= num_resources; ++i) {
+ QuicStreamId stream_id = GetNthServerInitiatedUnidirectionalId(i - 1);
+ std::string path =
+ partial_push_resource_path + QuicTextUtils::Uint64ToString(i);
+ std::string url = scheme + "://" + resource_host + path;
+ QuicUrl resource_url = QuicUrl(url);
+ std::string body(body_size, 'a');
+ std::string data;
+ data_frame_header_length = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ HttpEncoder encoder;
+ std::unique_ptr<char[]> buffer;
+ data_frame_header_length =
+ encoder.SerializeDataFrameHeader(body.length(), &buffer);
+ std::string header(buffer.get(), data_frame_header_length);
+ data = header + body;
+ } else {
+ data = body;
+ }
+
+ memory_cache_backend_.AddSimpleResponse(resource_host, path, 200, data);
+ push_resources.push_back(QuicBackendResponse::ServerPushInfo(
+ resource_url, spdy::SpdyHeaderBlock(), QuicStream::kDefaultPriority,
+ body));
+ // PUSH_PROMISED are sent for all the resources.
+ EXPECT_CALL(*session_,
+ WritePushPromiseMock(GetNthClientInitiatedBidirectionalId(0),
+ stream_id, _));
+ if (i <= kMaxStreamsForTest) {
+ // |kMaxStreamsForTest| promised responses should be sent.
+ // Since flow control window is smaller than response body, not the
+ // whole body will be sent.
+ QuicStreamOffset offset = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ EXPECT_CALL(*connection_,
+ SendStreamData(stream_id, data_frame_header_length,
+ offset, NO_FIN));
+ offset += data_frame_header_length;
+ }
+ EXPECT_CALL(*connection_, SendStreamData(stream_id, _, offset, NO_FIN))
+ .WillOnce(Return(QuicConsumedData(
+ kStreamFlowControlWindowSize - offset, false)));
+ EXPECT_CALL(*session_, SendBlocked(stream_id));
+ }
+ }
+ session_->PromisePushResources(request_url, push_resources,
+ GetNthClientInitiatedBidirectionalId(0),
+ request_headers);
+ return data_frame_header_length;
+ }
+
+ void ConsumeHeadersStreamData() {
+ QuicStreamId headers_stream_id =
+ QuicUtils::GetHeadersStreamId(connection_->transport_version());
+ EXPECT_CALL(*connection_, SendStreamData(headers_stream_id, _, _, _))
+ .Times(AtLeast(1));
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSimpleServerSessionServerPushTest,
+ ::testing::ValuesIn(AllSupportedVersions()));
+
+// Tests that given more than kMaxStreamsForTest resources, all their
+// PUSH_PROMISE's will be sent out and only kMaxStreamsForTest streams will be
+// opened and send push response.
+TEST_P(QuicSimpleServerSessionServerPushTest, TestPromisePushResources) {
+ ConsumeHeadersStreamData();
+ size_t num_resources = kMaxStreamsForTest + 5;
+ PromisePushResources(num_resources);
+ EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
+}
+
+// Tests that after promised stream queued up, when an opened stream is marked
+// draining, a queued promised stream will become open and send push response.
+TEST_P(QuicSimpleServerSessionServerPushTest,
+ HandlePromisedPushRequestsAfterStreamDraining) {
+ ConsumeHeadersStreamData();
+ size_t num_resources = kMaxStreamsForTest + 1;
+ QuicByteCount data_frame_header_length = PromisePushResources(num_resources);
+ QuicStreamId next_out_going_stream_id =
+ GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest);
+
+ // After an open stream is marked draining, a new stream is expected to be
+ // created and a response sent on the stream.
+ QuicStreamOffset offset = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ EXPECT_CALL(*connection_,
+ SendStreamData(next_out_going_stream_id,
+ data_frame_header_length, offset, NO_FIN));
+ offset += data_frame_header_length;
+ }
+ EXPECT_CALL(*connection_,
+ SendStreamData(next_out_going_stream_id, _, offset, NO_FIN))
+ .WillOnce(Return(
+ QuicConsumedData(kStreamFlowControlWindowSize - offset, false)));
+ EXPECT_CALL(*session_, SendBlocked(next_out_going_stream_id));
+
+ if (IsVersion99()) {
+ // The PromisePushedResources call, above, will have used all available
+ // stream ids. For version 99, stream ids are not made available until
+ // a MAX_STREAM_ID frame is received. This emulates the reception of one.
+ // For pre-v-99, the node monitors its own stream usage and makes streams
+ // available as it closes/etc them.
+ session_->OnMaxStreamIdFrame(
+ QuicMaxStreamIdFrame(0, GetNthServerInitiatedUnidirectionalId(10)));
+ }
+ session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(0));
+ // Number of open outgoing streams should still be the same, because a new
+ // stream is opened. And the queue should be empty.
+ EXPECT_EQ(kMaxStreamsForTest, session_->GetNumOpenOutgoingStreams());
+}
+
+// Tests that after all resources are promised, a RST frame from client can
+// prevent a promised resource to be send out.
+TEST_P(QuicSimpleServerSessionServerPushTest,
+ ResetPromisedStreamToCancelServerPush) {
+ ConsumeHeadersStreamData();
+
+ // Having two extra resources to be send later. One of them will be reset, so
+ // when opened stream become close, only one will become open.
+ size_t num_resources = kMaxStreamsForTest + 2;
+ if (IsVersion99()) {
+ // V99 will send out a stream-id-blocked frame when the we desired to exceed
+ // the limit. This will clear the frames so that they do not block the later
+ // rst-stream frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(
+ this, &QuicSimpleServerSessionServerPushTest::ClearControlFrame));
+ }
+ QuicByteCount data_frame_header_length = PromisePushResources(num_resources);
+
+ // Reset the last stream in the queue. It should be marked cancelled.
+ QuicStreamId stream_got_reset =
+ GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest + 1);
+ QuicRstStreamFrame rst(kInvalidControlFrameId, stream_got_reset,
+ QUIC_STREAM_CANCELLED, 0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(
+ this, &QuicSimpleServerSessionServerPushTest::ClearControlFrame));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream_got_reset, QUIC_RST_ACKNOWLEDGEMENT));
+ visitor_->OnRstStream(rst);
+
+ // When the first 2 streams becomes draining, the two queued up stream could
+ // be created. But since one of them was marked cancelled due to RST frame,
+ // only one queued resource will be sent out.
+ QuicStreamId stream_not_reset =
+ GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest);
+ InSequence s;
+ QuicStreamOffset offset = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ EXPECT_CALL(*connection_,
+ SendStreamData(stream_not_reset, data_frame_header_length,
+ offset, NO_FIN));
+ offset += data_frame_header_length;
+ }
+ EXPECT_CALL(*connection_, SendStreamData(stream_not_reset, _, offset, NO_FIN))
+ .WillOnce(Return(
+ QuicConsumedData(kStreamFlowControlWindowSize - offset, false)));
+ EXPECT_CALL(*session_, SendBlocked(stream_not_reset));
+
+ if (IsVersion99()) {
+ // The PromisePushedResources call, above, will have used all available
+ // stream ids. For version 99, stream ids are not made available until
+ // a MAX_STREAM_ID frame is received. This emulates the reception of one.
+ // For pre-v-99, the node monitors its own stream usage and makes streams
+ // available as it closes/etc them.
+ session_->OnMaxStreamIdFrame(
+ QuicMaxStreamIdFrame(0, GetNthServerInitiatedUnidirectionalId(11)));
+ }
+ session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(0));
+ session_->StreamDraining(GetNthServerInitiatedUnidirectionalId(1));
+}
+
+// Tests that closing a open outgoing stream can trigger a promised resource in
+// the queue to be send out.
+TEST_P(QuicSimpleServerSessionServerPushTest,
+ CloseStreamToHandleMorePromisedStream) {
+ ConsumeHeadersStreamData();
+ size_t num_resources = kMaxStreamsForTest + 1;
+ if (IsVersion99()) {
+ // V99 will send out a stream-id-blocked frame when the we desired to exceed
+ // the limit. This will clear the frames so that they do not block the later
+ // rst-stream frame.
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillOnce(Invoke(
+ this, &QuicSimpleServerSessionServerPushTest::ClearControlFrame));
+ }
+ QuicByteCount data_frame_header_length = PromisePushResources(num_resources);
+ QuicStreamId stream_to_open =
+ GetNthServerInitiatedUnidirectionalId(kMaxStreamsForTest);
+
+ // Resetting 1st open stream will close the stream and give space for extra
+ // stream to be opened.
+ QuicStreamId stream_got_reset = GetNthServerInitiatedUnidirectionalId(0);
+ EXPECT_CALL(owner_, OnRstStreamReceived(_)).Times(1);
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ if (!IsVersion99()) {
+ // For version 99, this is covered in InjectStopSending()
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream_got_reset, QUIC_RST_ACKNOWLEDGEMENT));
+ }
+ QuicStreamOffset offset = 0;
+ if (VersionHasDataFrameHeader(connection_->transport_version())) {
+ EXPECT_CALL(*connection_,
+ SendStreamData(stream_to_open, data_frame_header_length, offset,
+ NO_FIN));
+ offset += data_frame_header_length;
+ }
+ EXPECT_CALL(*connection_, SendStreamData(stream_to_open, _, offset, NO_FIN))
+ .WillOnce(Return(
+ QuicConsumedData(kStreamFlowControlWindowSize - offset, false)));
+
+ EXPECT_CALL(*session_, SendBlocked(stream_to_open));
+ QuicRstStreamFrame rst(kInvalidControlFrameId, stream_got_reset,
+ QUIC_STREAM_CANCELLED, 0);
+ if (IsVersion99()) {
+ // The PromisePushedResources call, above, will have used all available
+ // stream ids. For version 99, stream ids are not made available until
+ // a MAX_STREAM_ID frame is received. This emulates the reception of one.
+ // For pre-v-99, the node monitors its own stream usage and makes streams
+ // available as it closes/etc them.
+ session_->OnMaxStreamIdFrame(
+ QuicMaxStreamIdFrame(0, GetNthServerInitiatedUnidirectionalId(10)));
+ }
+ visitor_->OnRstStream(rst);
+ // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a
+ // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes
+ // a one-way close.
+ InjectStopSending(stream_got_reset, QUIC_STREAM_CANCELLED);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..4e25552d03c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.cc
@@ -0,0 +1,341 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+#include <list>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+QuicSimpleServerStream::QuicSimpleServerStream(
+ QuicStreamId id,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSpdyServerStreamBase(id, session, type),
+ content_length_(-1),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+QuicSimpleServerStream::QuicSimpleServerStream(
+ PendingStream pending,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSpdyServerStreamBase(std::move(pending), session, type),
+ content_length_(-1),
+ quic_simple_server_backend_(quic_simple_server_backend) {}
+
+QuicSimpleServerStream::~QuicSimpleServerStream() {
+ quic_simple_server_backend_->CloseBackendResponseStream(this);
+}
+
+void QuicSimpleServerStream::OnInitialHeadersComplete(
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
+ if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
+ &request_headers_)) {
+ QUIC_DVLOG(1) << "Invalid headers";
+ SendErrorResponse();
+ }
+ ConsumeHeaderList();
+}
+
+void QuicSimpleServerStream::OnTrailingHeadersComplete(
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) {
+ QUIC_BUG << "Server does not support receiving Trailers.";
+ SendErrorResponse();
+}
+
+void QuicSimpleServerStream::OnBodyAvailable() {
+ while (HasBytesToRead()) {
+ struct iovec iov;
+ if (GetReadableRegions(&iov, 1) == 0) {
+ // No more data to read.
+ break;
+ }
+ QUIC_DVLOG(1) << "Stream " << id() << " processed " << iov.iov_len
+ << " bytes.";
+ body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);
+
+ if (content_length_ >= 0 &&
+ body_.size() > static_cast<uint64_t>(content_length_)) {
+ QUIC_DVLOG(1) << "Body size (" << body_.size() << ") > content length ("
+ << content_length_ << ").";
+ SendErrorResponse();
+ return;
+ }
+ MarkConsumed(iov.iov_len);
+ }
+ if (!sequencer()->IsClosed()) {
+ sequencer()->SetUnblocked();
+ return;
+ }
+
+ // If the sequencer is closed, then all the body, including the fin, has been
+ // consumed.
+ OnFinRead();
+
+ if (write_side_closed() || fin_buffered()) {
+ return;
+ }
+
+ SendResponse();
+}
+
+void QuicSimpleServerStream::PushResponse(
+ SpdyHeaderBlock push_request_headers) {
+ if (QuicUtils::IsClientInitiatedStreamId(
+ session()->connection()->transport_version(), id())) {
+ QUIC_BUG << "Client initiated stream shouldn't be used as promised stream.";
+ return;
+ }
+ // Change the stream state to emulate a client request.
+ request_headers_ = std::move(push_request_headers);
+ content_length_ = 0;
+ QUIC_DVLOG(1) << "Stream " << id()
+ << " ready to receive server push response.";
+ DCHECK(reading_stopped());
+
+ // Directly send response based on the emulated request_headers_.
+ SendResponse();
+}
+
+void QuicSimpleServerStream::SendResponse() {
+ if (request_headers_.empty()) {
+ QUIC_DVLOG(1) << "Request headers empty.";
+ SendErrorResponse();
+ return;
+ }
+
+ if (content_length_ > 0 &&
+ static_cast<uint64_t>(content_length_) != body_.size()) {
+ QUIC_DVLOG(1) << "Content length (" << content_length_ << ") != body size ("
+ << body_.size() << ").";
+ SendErrorResponse();
+ return;
+ }
+
+ if (!QuicContainsKey(request_headers_, ":authority") ||
+ !QuicContainsKey(request_headers_, ":path")) {
+ QUIC_DVLOG(1) << "Request headers do not contain :authority or :path.";
+ SendErrorResponse();
+ return;
+ }
+
+ // Fetch the response from the backend interface and wait for callback once
+ // response is ready
+ quic_simple_server_backend_->FetchResponseFromBackend(request_headers_, body_,
+ this);
+}
+
+QuicConnectionId QuicSimpleServerStream::connection_id() const {
+ return spdy_session()->connection_id();
+}
+
+QuicStreamId QuicSimpleServerStream::stream_id() const {
+ return id();
+}
+
+std::string QuicSimpleServerStream::peer_host() const {
+ return spdy_session()->peer_address().host().ToString();
+}
+
+void QuicSimpleServerStream::OnResponseBackendComplete(
+ const QuicBackendResponse* response,
+ std::list<QuicBackendResponse::ServerPushInfo> resources) {
+ if (response == nullptr) {
+ QUIC_DVLOG(1) << "Response not found in cache.";
+ SendNotFoundResponse();
+ return;
+ }
+
+ if (response->response_type() == QuicBackendResponse::CLOSE_CONNECTION) {
+ QUIC_DVLOG(1) << "Special response: closing connection.";
+ CloseConnectionWithDetails(QUIC_NO_ERROR, "Toy server forcing close");
+ return;
+ }
+
+ if (response->response_type() == QuicBackendResponse::IGNORE_REQUEST) {
+ QUIC_DVLOG(1) << "Special response: ignoring request.";
+ return;
+ }
+
+ if (response->response_type() == QuicBackendResponse::BACKEND_ERR_RESPONSE) {
+ QUIC_DVLOG(1) << "Quic Proxy: Backend connection error.";
+ /*502 Bad Gateway
+ The server was acting as a gateway or proxy and received an
+ invalid response from the upstream server.*/
+ SendErrorResponse(502);
+ return;
+ }
+
+ // Examing response status, if it was not pure integer as typical h2
+ // response status, send error response. Notice that
+ // QuicHttpResponseCache push urls are strictly authority + path only,
+ // scheme is not included (see |QuicHttpResponseCache::GetKey()|).
+ std::string request_url = request_headers_[":authority"].as_string() +
+ request_headers_[":path"].as_string();
+ int response_code;
+ const SpdyHeaderBlock& response_headers = response->headers();
+ if (!ParseHeaderStatusCode(response_headers, &response_code)) {
+ auto status = response_headers.find(":status");
+ if (status == response_headers.end()) {
+ QUIC_LOG(WARNING)
+ << ":status not present in response from cache for request "
+ << request_url;
+ } else {
+ QUIC_LOG(WARNING) << "Illegal (non-integer) response :status from cache: "
+ << status->second << " for request " << request_url;
+ }
+ SendErrorResponse();
+ return;
+ }
+
+ if (QuicUtils::IsServerInitiatedStreamId(
+ session()->connection()->transport_version(), id())) {
+ // A server initiated stream is only used for a server push response,
+ // and only 200 and 30X response codes are supported for server push.
+ // This behavior mirrors the HTTP/2 implementation.
+ bool is_redirection = response_code / 100 == 3;
+ if (response_code != 200 && !is_redirection) {
+ QUIC_LOG(WARNING) << "Response to server push request " << request_url
+ << " result in response code " << response_code;
+ Reset(QUIC_STREAM_CANCELLED);
+ return;
+ }
+ }
+
+ if (!resources.empty()) {
+ QUIC_DVLOG(1) << "Stream " << id() << " found " << resources.size()
+ << " push resources.";
+ QuicSimpleServerSession* session =
+ static_cast<QuicSimpleServerSession*>(spdy_session());
+ session->PromisePushResources(request_url, resources, id(),
+ request_headers_);
+ }
+
+ if (response->response_type() == QuicBackendResponse::INCOMPLETE_RESPONSE) {
+ QUIC_DVLOG(1)
+ << "Stream " << id()
+ << " sending an incomplete response, i.e. no trailer, no fin.";
+ SendIncompleteResponse(response->headers().Clone(), response->body());
+ return;
+ }
+
+ if (response->response_type() == QuicBackendResponse::STOP_SENDING) {
+ QUIC_DVLOG(1)
+ << "Stream " << id()
+ << " sending an incomplete response, i.e. no trailer, no fin.";
+ SendIncompleteResponse(response->headers().Clone(), response->body());
+ SendStopSending(response->stop_sending_code());
+ return;
+ }
+
+ QUIC_DVLOG(1) << "Stream " << id() << " sending response.";
+ SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(),
+ response->trailers().Clone());
+}
+
+void QuicSimpleServerStream::SendNotFoundResponse() {
+ QUIC_DVLOG(1) << "Stream " << id() << " sending not found response.";
+ SpdyHeaderBlock headers;
+ headers[":status"] = "404";
+ headers["content-length"] =
+ QuicTextUtils::Uint64ToString(strlen(kNotFoundResponseBody));
+ SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
+}
+
+void QuicSimpleServerStream::SendErrorResponse() {
+ SendErrorResponse(0);
+}
+
+void QuicSimpleServerStream::SendErrorResponse(int resp_code) {
+ QUIC_DVLOG(1) << "Stream " << id() << " sending error response.";
+ SpdyHeaderBlock headers;
+ if (resp_code <= 0) {
+ headers[":status"] = "500";
+ } else {
+ headers[":status"] = QuicTextUtils::Uint64ToString(resp_code);
+ }
+ headers["content-length"] =
+ QuicTextUtils::Uint64ToString(strlen(kErrorResponseBody));
+ SendHeadersAndBody(std::move(headers), kErrorResponseBody);
+}
+
+void QuicSimpleServerStream::SendIncompleteResponse(
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece body) {
+ QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = false) : "
+ << response_headers.DebugString();
+ WriteHeaders(std::move(response_headers), /*fin=*/false, nullptr);
+
+ QUIC_DLOG(INFO) << "Stream " << id()
+ << " writing body (fin = false) with size: " << body.size();
+ if (!body.empty()) {
+ WriteOrBufferBody(body, /*fin=*/false);
+ }
+}
+
+void QuicSimpleServerStream::SendHeadersAndBody(
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece body) {
+ SendHeadersAndBodyAndTrailers(std::move(response_headers), body,
+ SpdyHeaderBlock());
+}
+
+void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers(
+ SpdyHeaderBlock response_headers,
+ QuicStringPiece body,
+ SpdyHeaderBlock response_trailers) {
+ // Send the headers, with a FIN if there's nothing else to send.
+ bool send_fin = (body.empty() && response_trailers.empty());
+ QUIC_DLOG(INFO) << "Stream " << id() << " writing headers (fin = " << send_fin
+ << ") : " << response_headers.DebugString();
+ WriteHeaders(std::move(response_headers), send_fin, nullptr);
+ if (send_fin) {
+ // Nothing else to send.
+ return;
+ }
+
+ // Send the body, with a FIN if there's no trailers to send.
+ send_fin = response_trailers.empty();
+ QUIC_DLOG(INFO) << "Stream " << id() << " writing body (fin = " << send_fin
+ << ") with size: " << body.size();
+ if (!body.empty() || send_fin) {
+ WriteOrBufferBody(body, send_fin);
+ }
+ if (send_fin) {
+ // Nothing else to send.
+ return;
+ }
+
+ // Send the trailers. A FIN is always sent with trailers.
+ QUIC_DLOG(INFO) << "Stream " << id() << " writing trailers (fin = true): "
+ << response_trailers.DebugString();
+ WriteTrailers(std::move(response_trailers), nullptr);
+}
+
+const char* const QuicSimpleServerStream::kErrorResponseBody = "bad";
+const char* const QuicSimpleServerStream::kNotFoundResponseBody =
+ "file not found";
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h
new file mode 100644
index 00000000000..e3eaa12c967
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_STREAM_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_STREAM_H_
+
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_server_stream_base.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
+
+namespace quic {
+
+// All this does right now is aggregate data, and on fin, send an HTTP
+// response.
+class QuicSimpleServerStream : public QuicSpdyServerStreamBase,
+ public QuicSimpleServerBackend::RequestHandler {
+ public:
+ QuicSimpleServerStream(QuicStreamId id,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicSimpleServerStream(PendingStream pending,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend);
+ QuicSimpleServerStream(const QuicSimpleServerStream&) = delete;
+ QuicSimpleServerStream& operator=(const QuicSimpleServerStream&) = delete;
+ ~QuicSimpleServerStream() override;
+
+ // QuicSpdyStream
+ void OnInitialHeadersComplete(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) override;
+ void OnTrailingHeadersComplete(bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list) override;
+
+ // QuicStream implementation called by the sequencer when there is
+ // data (or a FIN) to be read.
+ void OnBodyAvailable() override;
+
+ // Make this stream start from as if it just finished parsing an incoming
+ // request whose headers are equivalent to |push_request_headers|.
+ // Doing so will trigger this toy stream to fetch response and send it back.
+ virtual void PushResponse(spdy::SpdyHeaderBlock push_request_headers);
+
+ // The response body of error responses.
+ static const char* const kErrorResponseBody;
+ static const char* const kNotFoundResponseBody;
+
+ // Implements QuicSimpleServerBackend::RequestHandler callbacks
+ QuicConnectionId connection_id() const override;
+ QuicStreamId stream_id() const override;
+ std::string peer_host() const override;
+ void OnResponseBackendComplete(
+ const QuicBackendResponse* response,
+ std::list<QuicBackendResponse::ServerPushInfo> resources) override;
+
+ protected:
+ // Sends a basic 200 response using SendHeaders for the headers and WriteData
+ // for the body.
+ virtual void SendResponse();
+
+ // Sends a basic 500 response using SendHeaders for the headers and WriteData
+ // for the body.
+ virtual void SendErrorResponse();
+ void SendErrorResponse(int resp_code);
+
+ // Sends a basic 404 response using SendHeaders for the headers and WriteData
+ // for the body.
+ void SendNotFoundResponse();
+
+ // Sends the response header and body, but not the fin.
+ void SendIncompleteResponse(spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece body);
+
+ void SendHeadersAndBody(spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece body);
+ void SendHeadersAndBodyAndTrailers(spdy::SpdyHeaderBlock response_headers,
+ QuicStringPiece body,
+ spdy::SpdyHeaderBlock response_trailers);
+
+ spdy::SpdyHeaderBlock* request_headers() { return &request_headers_; }
+
+ const std::string& body() { return body_; }
+
+ // The parsed headers received from the client.
+ spdy::SpdyHeaderBlock request_headers_;
+ int64_t content_length_;
+ std::string body_;
+
+ private:
+ QuicSimpleServerBackend* quic_simple_server_backend_; // Not owned.
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SIMPLE_SERVER_STREAM_H_
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
new file mode 100644
index 00000000000..7572a75da84
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_simple_server_stream_test.cc
@@ -0,0 +1,679 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+#include <list>
+#include <memory>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/tools/quic_backend_response.h"
+#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+using testing::ValuesIn;
+
+namespace quic {
+namespace test {
+
+const size_t kFakeFrameLen = 60;
+const size_t kErrorLength = strlen(QuicSimpleServerStream::kErrorResponseBody);
+const size_t kDataFrameHeaderLength = 2;
+
+class TestStream : public QuicSimpleServerStream {
+ public:
+ TestStream(QuicStreamId stream_id,
+ QuicSpdySession* session,
+ StreamType type,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerStream(stream_id,
+ session,
+ type,
+ quic_simple_server_backend) {}
+
+ ~TestStream() override = default;
+
+ MOCK_METHOD1(WriteHeadersMock, void(bool fin));
+
+ size_t WriteHeaders(spdy::SpdyHeaderBlock header_block,
+ bool fin,
+ QuicReferenceCountedPointer<QuicAckListenerInterface>
+ ack_listener) override {
+ WriteHeadersMock(fin);
+ return 0;
+ }
+
+ // Expose protected QuicSimpleServerStream methods.
+ void DoSendResponse() { SendResponse(); }
+ void DoSendErrorResponse() { SendErrorResponse(); }
+
+ spdy::SpdyHeaderBlock* mutable_headers() { return &request_headers_; }
+ void set_body(std::string body) { body_ = std::move(body); }
+ const std::string& body() const { return body_; }
+ int content_length() const { return content_length_; }
+
+ QuicStringPiece GetHeader(QuicStringPiece key) const {
+ auto it = request_headers_.find(key);
+ DCHECK(it != request_headers_.end());
+ return it->second;
+ }
+};
+
+namespace {
+
+class MockQuicSimpleServerSession : public QuicSimpleServerSession {
+ public:
+ const size_t kMaxStreamsForTest = 100;
+
+ MockQuicSimpleServerSession(
+ QuicConnection* connection,
+ MockQuicSessionVisitor* owner,
+ MockQuicCryptoServerStreamHelper* helper,
+ QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ QuicSimpleServerBackend* quic_simple_server_backend)
+ : QuicSimpleServerSession(DefaultQuicConfig(),
+ CurrentSupportedVersions(),
+ connection,
+ owner,
+ helper,
+ crypto_config,
+ compressed_certs_cache,
+ quic_simple_server_backend) {
+ QuicSessionPeer::SetMaxOpenIncomingStreams(this, kMaxStreamsForTest);
+ QuicSessionPeer::SetMaxOpenOutgoingStreams(this, kMaxStreamsForTest);
+ ON_CALL(*this, WritevData(_, _, _, _, _))
+ .WillByDefault(Invoke(MockQuicSession::ConsumeData));
+ }
+
+ MockQuicSimpleServerSession(const MockQuicSimpleServerSession&) = delete;
+ MockQuicSimpleServerSession& operator=(const MockQuicSimpleServerSession&) =
+ delete;
+ ~MockQuicSimpleServerSession() override = default;
+
+ MOCK_METHOD3(OnConnectionClosed,
+ void(QuicErrorCode error,
+ const std::string& error_details,
+ ConnectionCloseSource source));
+ MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(QuicStreamId id));
+ MOCK_METHOD5(WritevData,
+ QuicConsumedData(QuicStream* stream,
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state));
+ MOCK_METHOD4(OnStreamHeaderList,
+ void(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len,
+ const QuicHeaderList& header_list));
+ MOCK_METHOD2(OnStreamHeadersPriority,
+ void(QuicStreamId stream_id, spdy::SpdyPriority priority));
+ MOCK_METHOD3(SendRstStream,
+ void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+ MOCK_METHOD1(OnHeadersHeadOfLineBlocking, void(QuicTime::Delta delta));
+ // Matchers cannot be used on non-copyable types like SpdyHeaderBlock.
+ void PromisePushResources(
+ const std::string& request_url,
+ const std::list<QuicBackendResponse::ServerPushInfo>& resources,
+ QuicStreamId original_stream_id,
+ const spdy::SpdyHeaderBlock& original_request_headers) override {
+ original_request_headers_ = original_request_headers.Clone();
+ PromisePushResourcesMock(request_url, resources, original_stream_id,
+ original_request_headers);
+ }
+ MOCK_METHOD4(PromisePushResourcesMock,
+ void(const std::string&,
+ const std::list<QuicBackendResponse::ServerPushInfo>&,
+ QuicStreamId,
+ const spdy::SpdyHeaderBlock&));
+
+ using QuicSession::ActivateStream;
+
+ MOCK_METHOD1(OnStopSendingReceived, void(const QuicStopSendingFrame& frame));
+
+ spdy::SpdyHeaderBlock original_request_headers_;
+};
+
+class QuicSimpleServerStreamTest : public QuicTestWithParam<ParsedQuicVersion> {
+ public:
+ QuicSimpleServerStreamTest()
+ : connection_(
+ new StrictMock<MockQuicConnection>(&helper_,
+ &alarm_factory_,
+ Perspective::IS_SERVER,
+ SupportedVersions(GetParam()))),
+ crypto_config_(new QuicCryptoServerConfig(
+ QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default(),
+ TlsServerHandshaker::CreateSslCtx())),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
+ session_(connection_,
+ &session_owner_,
+ &session_helper_,
+ crypto_config_.get(),
+ &compressed_certs_cache_,
+ &memory_cache_backend_),
+ quic_response_(new QuicBackendResponse),
+ body_("hello world") {
+ connection_->set_visitor(&session_);
+ header_list_.OnHeaderBlockStart();
+ header_list_.OnHeader(":authority", "www.google.com");
+ header_list_.OnHeader(":path", "/");
+ header_list_.OnHeader(":method", "POST");
+ header_list_.OnHeader(":version", "HTTP/1.1");
+ header_list_.OnHeader("content-length", "11");
+
+ header_list_.OnHeaderBlockEnd(128, 128);
+
+ // New streams rely on having the peer's flow control receive window
+ // negotiated in the config.
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ stream_ = new StrictMock<TestStream>(
+ GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0),
+ &session_, BIDIRECTIONAL, &memory_cache_backend_);
+ // Register stream_ in dynamic_stream_map_ and pass ownership to session_.
+ session_.ActivateStream(QuicWrapUnique(stream_));
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ const std::string& StreamBody() { return stream_->body(); }
+
+ std::string StreamHeadersValue(const std::string& key) {
+ return (*stream_->mutable_headers())[key].as_string();
+ }
+
+ bool IsVersion99() const {
+ return connection_->transport_version() == QUIC_VERSION_99;
+ }
+
+ bool HasFrameHeader() const {
+ return VersionHasDataFrameHeader(connection_->transport_version());
+ }
+
+ spdy::SpdyHeaderBlock response_headers_;
+ MockQuicConnectionHelper helper_;
+ MockAlarmFactory alarm_factory_;
+ StrictMock<MockQuicConnection>* connection_;
+ StrictMock<MockQuicSessionVisitor> session_owner_;
+ StrictMock<MockQuicCryptoServerStreamHelper> session_helper_;
+ std::unique_ptr<QuicCryptoServerConfig> crypto_config_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ QuicMemoryCacheBackend memory_cache_backend_;
+ StrictMock<MockQuicSimpleServerSession> session_;
+ StrictMock<TestStream>* stream_; // Owned by session_.
+ std::unique_ptr<QuicBackendResponse> quic_response_;
+ std::string body_;
+ QuicHeaderList header_list_;
+ HttpEncoder encoder_;
+};
+
+INSTANTIATE_TEST_SUITE_P(Tests,
+ QuicSimpleServerStreamTest,
+ ValuesIn(AllSupportedVersions()));
+
+TEST_P(QuicSimpleServerStreamTest, TestFraming) {
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body_ : body_;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+ EXPECT_EQ("11", StreamHeadersValue("content-length"));
+ EXPECT_EQ("/", StreamHeadersValue(":path"));
+ EXPECT_EQ("POST", StreamHeadersValue(":method"));
+ EXPECT_EQ(body_, StreamBody());
+}
+
+TEST_P(QuicSimpleServerStreamTest, TestFramingOnePacket) {
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+
+ stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + body_ : body_;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, data));
+ EXPECT_EQ("11", StreamHeadersValue("content-length"));
+ EXPECT_EQ("/", StreamHeadersValue(":path"));
+ EXPECT_EQ("POST", StreamHeadersValue(":method"));
+ EXPECT_EQ(body_, StreamBody());
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendQuicRstStreamNoErrorInStopReading) {
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+
+ EXPECT_FALSE(stream_->fin_received());
+ EXPECT_FALSE(stream_->rst_received());
+
+ stream_->set_fin_sent(true);
+ stream_->CloseWriteSide();
+
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1);
+ stream_->StopReading();
+}
+
+TEST_P(QuicSimpleServerStreamTest, TestFramingExtraData) {
+ InSequence seq;
+ std::string large_body = "hello world!!!!!!";
+
+ // We'll automatically write out an error (headers + body)
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, kDataFrameHeaderLength, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
+
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body_.length(), &buffer);
+ std::string header = std::string(buffer.get(), header_length);
+ std::string data = HasFrameHeader() ? header + 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 =
+ encoder_.SerializeDataFrameHeader(large_body.length(), &buffer);
+ header = std::string(buffer.get(), header_length);
+ std::string data2 = HasFrameHeader() ? header + large_body : large_body;
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), /*fin=*/true, data.size(), data2));
+ EXPECT_EQ("11", StreamHeadersValue("content-length"));
+ EXPECT_EQ("/", StreamHeadersValue(":path"));
+ EXPECT_EQ("POST", StreamHeadersValue(":method"));
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus) {
+ // Send an illegal response with response status not supported by HTTP/2.
+ spdy::SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = "/bar";
+ (*request_headers)[":authority"] = "www.google.com";
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ // HTTP/2 only supports integer responsecode, so "200 OK" is illegal.
+ response_headers_[":status"] = "200 OK";
+ response_headers_["content-length"] = "5";
+ std::string body = "Yummm";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+
+ memory_cache_backend_.AddResponse("www.google.com", "/bar",
+ std::move(response_headers_), body);
+
+ stream_->set_fin_received(true);
+
+ InSequence s;
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
+
+ stream_->DoSendResponse();
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus2) {
+ // Send an illegal response with response status not supported by HTTP/2.
+ spdy::SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = "/bar";
+ (*request_headers)[":authority"] = "www.google.com";
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ // HTTP/2 only supports 3-digit-integer, so "+200" is illegal.
+ response_headers_[":status"] = "+200";
+ response_headers_["content-length"] = "5";
+ std::string body = "Yummm";
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+
+ memory_cache_backend_.AddResponse("www.google.com", "/bar",
+ std::move(response_headers_), body);
+
+ stream_->set_fin_received(true);
+
+ InSequence s;
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
+
+ stream_->DoSendResponse();
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendPushResponseWith404Response) {
+ // Create a new promised stream with even id().
+ auto promised_stream = new StrictMock<TestStream>(
+ GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), 0),
+ &session_, WRITE_UNIDIRECTIONAL, &memory_cache_backend_);
+ session_.ActivateStream(QuicWrapUnique(promised_stream));
+
+ // Send a push response with response status 404, which will be regarded as
+ // invalid server push response.
+ spdy::SpdyHeaderBlock* request_headers = promised_stream->mutable_headers();
+ (*request_headers)[":path"] = "/bar";
+ (*request_headers)[":authority"] = "www.google.com";
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ response_headers_[":status"] = "404";
+ response_headers_["content-length"] = "8";
+ std::string body = "NotFound";
+
+ memory_cache_backend_.AddResponse("www.google.com", "/bar",
+ std::move(response_headers_), body);
+
+ InSequence s;
+ EXPECT_CALL(session_,
+ SendRstStream(promised_stream->id(), QUIC_STREAM_CANCELLED, 0));
+
+ promised_stream->DoSendResponse();
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithValidHeaders) {
+ // Add a request and response with valid headers.
+ spdy::SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = "/bar";
+ (*request_headers)[":authority"] = "www.google.com";
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ response_headers_[":status"] = "200";
+ response_headers_["content-length"] = "5";
+ std::string body = "Yummm";
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+
+ memory_cache_backend_.AddResponse("www.google.com", "/bar",
+ std::move(response_headers_), body);
+ stream_->set_fin_received(true);
+
+ InSequence s;
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, body.length(), _, FIN));
+
+ stream_->DoSendResponse();
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithPushResources) {
+ // Tests that if a response has push resources to be send, SendResponse() will
+ // call PromisePushResources() to handle these resources.
+
+ // Add a request and response with valid headers into cache.
+ std::string host = "www.google.com";
+ std::string request_path = "/foo";
+ std::string body = "Yummm";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(body.length(), &buffer);
+ QuicBackendResponse::ServerPushInfo push_info(
+ QuicUrl(host, "/bar"), spdy::SpdyHeaderBlock(),
+ QuicStream::kDefaultPriority, "Push body");
+ std::list<QuicBackendResponse::ServerPushInfo> push_resources;
+ push_resources.push_back(push_info);
+ memory_cache_backend_.AddSimpleResponseWithServerPushResources(
+ host, request_path, 200, body, push_resources);
+
+ spdy::SpdyHeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = request_path;
+ (*request_headers)[":authority"] = host;
+ (*request_headers)[":version"] = "HTTP/1.1";
+ (*request_headers)[":method"] = "GET";
+
+ stream_->set_fin_received(true);
+ InSequence s;
+ EXPECT_CALL(session_, PromisePushResourcesMock(
+ host + request_path, _,
+ GetNthClientInitiatedBidirectionalStreamId(
+ connection_->transport_version(), 0),
+ _));
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, header_length, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, body.length(), _, FIN));
+ stream_->DoSendResponse();
+ EXPECT_EQ(*request_headers, session_.original_request_headers_);
+}
+
+TEST_P(QuicSimpleServerStreamTest, PushResponseOnClientInitiatedStream) {
+ // EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
+ if (GetParam() != AllSupportedVersions()[0]) {
+ return;
+ }
+
+ // Calling PushResponse() on a client initialted stream is never supposed to
+ // happen.
+ EXPECT_QUIC_BUG(stream_->PushResponse(spdy::SpdyHeaderBlock()),
+ "Client initiated stream"
+ " shouldn't be used as promised stream.");
+}
+
+TEST_P(QuicSimpleServerStreamTest, PushResponseOnServerInitiatedStream) {
+ // Tests that PushResponse() should take the given headers as request headers
+ // and fetch response from cache, and send it out.
+
+ // Create a stream with even stream id and test against this stream.
+ const QuicStreamId kServerInitiatedStreamId =
+ GetNthServerInitiatedUnidirectionalStreamId(
+ connection_->transport_version(), 0);
+ // Create a server initiated stream and pass it to session_.
+ auto server_initiated_stream =
+ new StrictMock<TestStream>(kServerInitiatedStreamId, &session_,
+ WRITE_UNIDIRECTIONAL, &memory_cache_backend_);
+ session_.ActivateStream(QuicWrapUnique(server_initiated_stream));
+
+ const std::string kHost = "www.foo.com";
+ const std::string kPath = "/bar";
+ spdy::SpdyHeaderBlock headers;
+ headers[":path"] = kPath;
+ headers[":authority"] = kHost;
+ headers[":version"] = "HTTP/1.1";
+ headers[":method"] = "GET";
+
+ response_headers_[":version"] = "HTTP/1.1";
+ response_headers_[":status"] = "200";
+ response_headers_["content-length"] = "5";
+ const std::string kBody = "Hello";
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ encoder_.SerializeDataFrameHeader(kBody.length(), &buffer);
+ memory_cache_backend_.AddResponse(kHost, kPath, std::move(response_headers_),
+ kBody);
+
+ // Call PushResponse() should trigger stream to fetch response from cache
+ // and send it back.
+ InSequence s;
+ EXPECT_CALL(*server_initiated_stream, WriteHeadersMock(false));
+
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, kServerInitiatedStreamId, header_length,
+ _, NO_FIN));
+ }
+ EXPECT_CALL(session_,
+ WritevData(_, kServerInitiatedStreamId, kBody.size(), _, FIN));
+ server_initiated_stream->PushResponse(std::move(headers));
+ EXPECT_EQ(kPath, server_initiated_stream->GetHeader(":path"));
+ EXPECT_EQ("GET", server_initiated_stream->GetHeader(":method"));
+}
+
+TEST_P(QuicSimpleServerStreamTest, TestSendErrorResponse) {
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ stream_->set_fin_received(true);
+
+ InSequence s;
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (HasFrameHeader()) {
+ EXPECT_CALL(session_, WritevData(_, _, kDataFrameHeaderLength, _, NO_FIN));
+ }
+ EXPECT_CALL(session_, WritevData(_, _, kErrorLength, _, FIN));
+
+ stream_->DoSendErrorResponse();
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, InvalidMultipleContentLength) {
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ spdy::SpdyHeaderBlock request_headers;
+ // \000 is a way to write the null byte when followed by a literal digit.
+ header_list_.OnHeader("content-length", QuicStringPiece("11\00012", 5));
+
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnStreamHeaderList(true, kFakeFrameLen, header_list_);
+
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->reading_stopped());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, InvalidLeadingNullContentLength) {
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+
+ spdy::SpdyHeaderBlock request_headers;
+ // \000 is a way to write the null byte when followed by a literal digit.
+ header_list_.OnHeader("content-length", QuicStringPiece("\00012", 3));
+
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ EXPECT_CALL(session_, WritevData(_, _, _, _, _))
+ .WillRepeatedly(Invoke(MockQuicSession::ConsumeData));
+ stream_->OnStreamHeaderList(true, kFakeFrameLen, header_list_);
+
+ EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->reading_stopped());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest, ValidMultipleContentLength) {
+ spdy::SpdyHeaderBlock request_headers;
+ // \000 is a way to write the null byte when followed by a literal digit.
+ header_list_.OnHeader("content-length", QuicStringPiece("11\00011", 5));
+
+ stream_->OnStreamHeaderList(false, kFakeFrameLen, header_list_);
+
+ EXPECT_EQ(11, stream_->content_length());
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_FALSE(stream_->reading_stopped());
+ EXPECT_FALSE(stream_->write_side_closed());
+}
+
+TEST_P(QuicSimpleServerStreamTest,
+ DoNotSendQuicRstStreamNoErrorWithRstReceived) {
+ InSequence s;
+ EXPECT_FALSE(stream_->reading_stopped());
+
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
+ EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _)).Times(1);
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ if (IsVersion99()) {
+ // For V99 receiving a RST_STREAM causes a 1-way close; the test requires
+ // a full close. A CloseWriteSide closes the other half of the stream.
+ // Everything should then work properly.
+ stream_->CloseWriteSide();
+ }
+ EXPECT_TRUE(stream_->reading_stopped());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+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
+ };
+ QuicStringPiece data(arr, QUIC_ARRAYSIZE(arr));
+ QuicStreamFrame frame(stream_->id(), true, 0, data);
+ // Verify that we don't crash when we get a invalid headers in stream frame.
+ stream_->OnStreamFrame(frame);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
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
new file mode 100644
index 00000000000..3d7ef028e8a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.cc
@@ -0,0 +1,273 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h"
+
+#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
+#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using spdy::SpdyHeaderBlock;
+
+namespace quic {
+
+void QuicSpdyClientBase::ClientQuicDataToResend::Resend() {
+ client_->SendRequest(*headers_, body_, fin_);
+ headers_ = nullptr;
+}
+
+QuicSpdyClientBase::QuicDataToResend::QuicDataToResend(
+ std::unique_ptr<SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin)
+ : headers_(std::move(headers)), body_(body), fin_(fin) {}
+
+QuicSpdyClientBase::QuicDataToResend::~QuicDataToResend() = default;
+
+QuicSpdyClientBase::QuicSpdyClientBase(
+ const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<NetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier)
+ : QuicClientBase(server_id,
+ supported_versions,
+ config,
+ helper,
+ alarm_factory,
+ std::move(network_helper),
+ std::move(proof_verifier)),
+ store_response_(false),
+ latest_response_code_(-1) {}
+
+QuicSpdyClientBase::~QuicSpdyClientBase() {
+ // We own the push promise index. We need to explicitly kill
+ // the session before the push promise index goes out of scope.
+ ResetSession();
+}
+
+QuicSpdyClientSession* QuicSpdyClientBase::client_session() {
+ return static_cast<QuicSpdyClientSession*>(QuicClientBase::session());
+}
+
+void QuicSpdyClientBase::InitializeSession() {
+ client_session()->Initialize();
+ client_session()->CryptoConnect();
+}
+
+void QuicSpdyClientBase::OnClose(QuicSpdyStream* stream) {
+ DCHECK(stream != nullptr);
+ QuicSpdyClientStream* client_stream =
+ static_cast<QuicSpdyClientStream*>(stream);
+
+ const SpdyHeaderBlock& response_headers = client_stream->response_headers();
+ if (response_listener_ != nullptr) {
+ response_listener_->OnCompleteResponse(stream->id(), response_headers,
+ client_stream->data());
+ }
+
+ // Store response headers and body.
+ if (store_response_) {
+ auto status = response_headers.find(":status");
+ if (status == response_headers.end() ||
+ !QuicTextUtils::StringToInt(status->second, &latest_response_code_)) {
+ QUIC_LOG(ERROR) << "Invalid response headers";
+ }
+ latest_response_headers_ = response_headers.DebugString();
+ preliminary_response_headers_ =
+ client_stream->preliminary_headers().DebugString();
+ latest_response_header_block_ = response_headers.Clone();
+ latest_response_body_ = client_stream->data();
+ latest_response_trailers_ =
+ client_stream->received_trailers().DebugString();
+ }
+}
+
+std::unique_ptr<QuicSession> QuicSpdyClientBase::CreateQuicClientSession(
+ const quic::ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) {
+ return QuicMakeUnique<QuicSpdyClientSession>(
+ *config(), supported_versions, connection, server_id(), crypto_config(),
+ &push_promise_index_);
+}
+
+void QuicSpdyClientBase::SendRequest(const SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ QuicClientPushPromiseIndex::TryHandle* handle;
+ QuicAsyncStatus rv = push_promise_index()->Try(headers, this, &handle);
+ if (rv == QUIC_SUCCESS)
+ return;
+
+ if (rv == QUIC_PENDING) {
+ // May need to retry request if asynchronous rendezvous fails.
+ AddPromiseDataToResend(headers, body, fin);
+ return;
+ }
+
+ QuicSpdyClientStream* stream = CreateClientStream();
+ if (stream == nullptr) {
+ QUIC_BUG << "stream creation failed!";
+ return;
+ }
+ stream->SendRequest(headers.Clone(), body, fin);
+ // Record this in case we need to resend.
+ MaybeAddDataToResend(headers, body, fin);
+}
+
+void QuicSpdyClientBase::SendRequestAndWaitForResponse(
+ const SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ SendRequest(headers, body, fin);
+ while (WaitForEvents()) {
+ }
+}
+
+void QuicSpdyClientBase::SendRequestsAndWaitForResponse(
+ const std::vector<std::string>& url_list) {
+ for (size_t i = 0; i < url_list.size(); ++i) {
+ SpdyHeaderBlock headers;
+ if (!SpdyUtils::PopulateHeaderBlockFromUrl(url_list[i], &headers)) {
+ QUIC_BUG << "Unable to create request";
+ continue;
+ }
+ SendRequest(headers, "", true);
+ }
+ while (WaitForEvents()) {
+ }
+}
+
+QuicSpdyClientStream* QuicSpdyClientBase::CreateClientStream() {
+ if (!connected()) {
+ return nullptr;
+ }
+
+ auto* stream = static_cast<QuicSpdyClientStream*>(
+ client_session()->CreateOutgoingBidirectionalStream());
+ if (stream) {
+ stream->SetPriority(QuicStream::kDefaultPriority);
+ stream->set_visitor(this);
+ }
+ return stream;
+}
+
+int QuicSpdyClientBase::GetNumSentClientHellosFromSession() {
+ return client_session()->GetNumSentClientHellos();
+}
+
+int QuicSpdyClientBase::GetNumReceivedServerConfigUpdatesFromSession() {
+ return client_session()->GetNumReceivedServerConfigUpdates();
+}
+
+void QuicSpdyClientBase::MaybeAddDataToResend(const SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ if (!GetQuicReloadableFlag(enable_quic_stateless_reject_support)) {
+ return;
+ }
+
+ if (client_session()->IsCryptoHandshakeConfirmed()) {
+ // The handshake is confirmed. No need to continue saving requests to
+ // resend.
+ data_to_resend_on_connect_.clear();
+ return;
+ }
+
+ // The handshake is not confirmed. Push the data onto the queue of data to
+ // resend if statelessly rejected.
+ std::unique_ptr<SpdyHeaderBlock> new_headers(
+ new SpdyHeaderBlock(headers.Clone()));
+ std::unique_ptr<QuicDataToResend> data_to_resend(
+ new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
+ MaybeAddQuicDataToResend(std::move(data_to_resend));
+}
+
+void QuicSpdyClientBase::MaybeAddQuicDataToResend(
+ std::unique_ptr<QuicDataToResend> data_to_resend) {
+ data_to_resend_on_connect_.push_back(std::move(data_to_resend));
+}
+
+void QuicSpdyClientBase::ClearDataToResend() {
+ data_to_resend_on_connect_.clear();
+}
+
+void QuicSpdyClientBase::ResendSavedData() {
+ // Calling Resend will re-enqueue the data, so swap out
+ // data_to_resend_on_connect_ before iterating.
+ std::vector<std::unique_ptr<QuicDataToResend>> old_data;
+ old_data.swap(data_to_resend_on_connect_);
+ for (const auto& data : old_data) {
+ data->Resend();
+ }
+}
+
+void QuicSpdyClientBase::AddPromiseDataToResend(const SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin) {
+ std::unique_ptr<SpdyHeaderBlock> new_headers(
+ new SpdyHeaderBlock(headers.Clone()));
+ push_promise_data_to_resend_.reset(
+ new ClientQuicDataToResend(std::move(new_headers), body, fin, this));
+}
+
+bool QuicSpdyClientBase::CheckVary(const SpdyHeaderBlock& client_request,
+ const SpdyHeaderBlock& promise_request,
+ const SpdyHeaderBlock& promise_response) {
+ return true;
+}
+
+void QuicSpdyClientBase::OnRendezvousResult(QuicSpdyStream* stream) {
+ std::unique_ptr<ClientQuicDataToResend> data_to_resend =
+ std::move(push_promise_data_to_resend_);
+ if (stream) {
+ stream->set_visitor(this);
+ stream->OnBodyAvailable();
+ } else if (data_to_resend) {
+ data_to_resend->Resend();
+ }
+}
+
+size_t QuicSpdyClientBase::latest_response_code() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_code_;
+}
+
+const std::string& QuicSpdyClientBase::latest_response_headers() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_headers_;
+}
+
+const std::string& QuicSpdyClientBase::preliminary_response_headers() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return preliminary_response_headers_;
+}
+
+const SpdyHeaderBlock& QuicSpdyClientBase::latest_response_header_block()
+ const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_header_block_;
+}
+
+const std::string& QuicSpdyClientBase::latest_response_body() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_body_;
+}
+
+const std::string& QuicSpdyClientBase::latest_response_trailers() const {
+ QUIC_BUG_IF(!store_response_) << "Response not stored!";
+ return latest_response_trailers_;
+}
+
+bool QuicSpdyClientBase::HasActiveRequests() {
+ return client_session()->HasActiveRequestStreams();
+}
+
+} // namespace quic
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
new file mode 100644
index 00000000000..03a981e1b1c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_spdy_client_base.h
@@ -0,0 +1,216 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A base class for the toy client, which connects to a specified port and sends
+// QUIC request to that endpoint.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_SPDY_CLIENT_BASE_H_
+#define QUICHE_QUIC_TOOLS_QUIC_SPDY_CLIENT_BASE_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_client_push_promise_index.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
+#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/tools/quic_client_base.h"
+
+namespace quic {
+
+class ProofVerifier;
+class QuicServerId;
+
+class QuicSpdyClientBase : public QuicClientBase,
+ public QuicClientPushPromiseIndex::Delegate,
+ public QuicSpdyStream::Visitor {
+ public:
+ // A ResponseListener is notified when a complete response is received.
+ class ResponseListener {
+ public:
+ ResponseListener() {}
+ virtual ~ResponseListener() {}
+ virtual void OnCompleteResponse(
+ QuicStreamId id,
+ const spdy::SpdyHeaderBlock& response_headers,
+ const std::string& response_body) = 0;
+ };
+
+ // The client uses these objects to keep track of any data to resend upon
+ // receipt of a stateless reject. Recall that the client API allows callers
+ // to optimistically send data to the server prior to handshake-confirmation.
+ // If the client subsequently receives a stateless reject, it must tear down
+ // its existing session, create a new session, and resend all previously sent
+ // data. It uses these objects to keep track of all the sent data, and to
+ // resend the data upon a subsequent connection.
+ class QuicDataToResend {
+ public:
+ // |headers| may be null, since it's possible to send data without headers.
+ QuicDataToResend(std::unique_ptr<spdy::SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin);
+ QuicDataToResend(const QuicDataToResend&) = delete;
+ QuicDataToResend& operator=(const QuicDataToResend&) = delete;
+
+ virtual ~QuicDataToResend();
+
+ // Must be overridden by specific classes with the actual method for
+ // re-sending data.
+ virtual void Resend() = 0;
+
+ protected:
+ std::unique_ptr<spdy::SpdyHeaderBlock> headers_;
+ QuicStringPiece body_;
+ bool fin_;
+ };
+
+ QuicSpdyClientBase(const QuicServerId& server_id,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicConfig& config,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
+ std::unique_ptr<NetworkHelper> network_helper,
+ std::unique_ptr<ProofVerifier> proof_verifier);
+ QuicSpdyClientBase(const QuicSpdyClientBase&) = delete;
+ QuicSpdyClientBase& operator=(const QuicSpdyClientBase&) = delete;
+
+ ~QuicSpdyClientBase() override;
+
+ // QuicSpdyStream::Visitor
+ void OnClose(QuicSpdyStream* stream) override;
+
+ // A spdy session has to call CryptoConnect on top of the regular
+ // initialization.
+ void InitializeSession() override;
+
+ // Sends an HTTP request and does not wait for response before returning.
+ void SendRequest(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+
+ // Sends an HTTP request and waits for response before returning.
+ void SendRequestAndWaitForResponse(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+
+ // Sends a request simple GET for each URL in |url_list|, and then waits for
+ // each to complete.
+ void SendRequestsAndWaitForResponse(const std::vector<std::string>& url_list);
+
+ // Returns a newly created QuicSpdyClientStream.
+ QuicSpdyClientStream* CreateClientStream();
+
+ // Returns a the session used for this client downcasted to a
+ // QuicSpdyClientSession.
+ QuicSpdyClientSession* client_session();
+
+ QuicClientPushPromiseIndex* push_promise_index() {
+ return &push_promise_index_;
+ }
+
+ bool CheckVary(const spdy::SpdyHeaderBlock& client_request,
+ const spdy::SpdyHeaderBlock& promise_request,
+ const spdy::SpdyHeaderBlock& promise_response) override;
+ void OnRendezvousResult(QuicSpdyStream*) override;
+
+ // If the crypto handshake has not yet been confirmed, adds the data to the
+ // queue of data to resend if the client receives a stateless reject.
+ // Otherwise, deletes the data.
+ void MaybeAddQuicDataToResend(
+ std::unique_ptr<QuicDataToResend> data_to_resend);
+
+ void set_store_response(bool val) { store_response_ = val; }
+
+ size_t latest_response_code() const;
+ const std::string& latest_response_headers() const;
+ const std::string& preliminary_response_headers() const;
+ const spdy::SpdyHeaderBlock& latest_response_header_block() const;
+ const std::string& latest_response_body() const;
+ const std::string& latest_response_trailers() const;
+
+ void set_response_listener(std::unique_ptr<ResponseListener> listener) {
+ response_listener_ = std::move(listener);
+ }
+
+ protected:
+ int GetNumSentClientHellosFromSession() override;
+ int GetNumReceivedServerConfigUpdatesFromSession() override;
+
+ // Takes ownership of |connection|.
+ std::unique_ptr<QuicSession> CreateQuicClientSession(
+ const quic::ParsedQuicVersionVector& supported_versions,
+ QuicConnection* connection) override;
+
+ // If the crypto handshake has not yet been confirmed, adds the data to the
+ // queue of data to resend if the client receives a stateless reject.
+ // Otherwise, deletes the data.
+ void MaybeAddDataToResend(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+
+ void ClearDataToResend() override;
+
+ void ResendSavedData() override;
+
+ void AddPromiseDataToResend(const spdy::SpdyHeaderBlock& headers,
+ QuicStringPiece body,
+ bool fin);
+ bool HasActiveRequests() override;
+
+ private:
+ // Specific QuicClient class for storing data to resend.
+ class ClientQuicDataToResend : public QuicDataToResend {
+ public:
+ ClientQuicDataToResend(std::unique_ptr<spdy::SpdyHeaderBlock> headers,
+ QuicStringPiece body,
+ bool fin,
+ QuicSpdyClientBase* client)
+ : QuicDataToResend(std::move(headers), body, fin), client_(client) {
+ DCHECK(headers_);
+ DCHECK(client);
+ }
+
+ ClientQuicDataToResend(const ClientQuicDataToResend&) = delete;
+ ClientQuicDataToResend& operator=(const ClientQuicDataToResend&) = delete;
+ ~ClientQuicDataToResend() override {}
+
+ void Resend() override;
+
+ private:
+ QuicSpdyClientBase* client_;
+ };
+
+ // Index of pending promised streams. Must outlive |session_|.
+ QuicClientPushPromiseIndex push_promise_index_;
+
+ // If true, store the latest response code, headers, and body.
+ bool store_response_;
+ // HTTP response code from most recent response.
+ int latest_response_code_;
+ // HTTP/2 headers from most recent response.
+ std::string latest_response_headers_;
+ // preliminary 100 Continue HTTP/2 headers from most recent response, if any.
+ std::string preliminary_response_headers_;
+ // HTTP/2 headers from most recent response.
+ spdy::SpdyHeaderBlock latest_response_header_block_;
+ // Body of most recent response.
+ std::string latest_response_body_;
+ // HTTP/2 trailers from most recent response.
+ std::string latest_response_trailers_;
+
+ // Listens for full responses.
+ std::unique_ptr<ResponseListener> response_listener_;
+
+ // Keeps track of any data that must be resent upon a subsequent successful
+ // connection, in case the client receives a stateless reject.
+ std::vector<std::unique_ptr<QuicDataToResend>> data_to_resend_on_connect_;
+
+ std::unique_ptr<ClientQuicDataToResend> push_promise_data_to_resend_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_SPDY_CLIENT_BASE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc
new file mode 100644
index 00000000000..63bbc5d549c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.cc
@@ -0,0 +1,111 @@
+// 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 "net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+
+namespace quic {
+
+QuicTcpLikeTraceConverter::QuicTcpLikeTraceConverter()
+ : largest_observed_control_frame_id_(kInvalidControlFrameId),
+ connection_offset_(0) {}
+
+QuicTcpLikeTraceConverter::StreamOffsetSegment::StreamOffsetSegment()
+ : connection_offset(0) {}
+
+QuicTcpLikeTraceConverter::StreamOffsetSegment::StreamOffsetSegment(
+ QuicStreamOffset stream_offset,
+ uint64_t connection_offset,
+ QuicByteCount data_length)
+ : stream_data(stream_offset, stream_offset + data_length),
+ connection_offset(connection_offset) {}
+
+QuicTcpLikeTraceConverter::StreamInfo::StreamInfo() : fin(false) {}
+
+QuicIntervalSet<uint64_t> QuicTcpLikeTraceConverter::OnStreamFrameSent(
+ QuicStreamId stream_id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin) {
+ QuicIntervalSet<uint64_t> connection_offsets;
+ if (fin) {
+ // Stream fin consumes a connection offset.
+ ++data_length;
+ }
+ StreamInfo* stream_info =
+ &streams_info_.emplace(stream_id, StreamInfo()).first->second;
+ // Get connection offsets of retransmission data in this frame.
+ for (const auto& segment : stream_info->segments) {
+ QuicInterval<QuicStreamOffset> retransmission(offset, offset + data_length);
+ retransmission.IntersectWith(segment.stream_data);
+ if (retransmission.Empty()) {
+ continue;
+ }
+ const uint64_t connection_offset = segment.connection_offset +
+ retransmission.min() -
+ segment.stream_data.min();
+ connection_offsets.Add(connection_offset,
+ connection_offset + retransmission.Length());
+ }
+
+ if (stream_info->fin) {
+ return connection_offsets;
+ }
+
+ // Get connection offsets of new data in this frame.
+ QuicStreamOffset least_unsent_offset =
+ stream_info->segments.empty()
+ ? 0
+ : stream_info->segments.back().stream_data.max();
+ if (least_unsent_offset >= offset + data_length) {
+ return connection_offsets;
+ }
+ // Ignore out-of-order stream data so that as connection offset increases,
+ // stream offset increases.
+ QuicStreamOffset new_data_offset = std::max(least_unsent_offset, offset);
+ QuicByteCount new_data_length = offset + data_length - new_data_offset;
+ connection_offsets.Add(connection_offset_,
+ connection_offset_ + new_data_length);
+ if (!stream_info->segments.empty() &&
+ new_data_offset == least_unsent_offset &&
+ connection_offset_ ==
+ stream_info->segments.back().connection_offset +
+ stream_info->segments.back().stream_data.Length()) {
+ // Extend the last segment if both stream and connection offsets are
+ // contiguous.
+ stream_info->segments.back().stream_data.SetMax(new_data_offset +
+ new_data_length);
+ } else {
+ stream_info->segments.emplace_back(new_data_offset, connection_offset_,
+ new_data_length);
+ }
+ stream_info->fin = fin;
+ connection_offset_ += new_data_length;
+
+ return connection_offsets;
+}
+
+QuicInterval<uint64_t> QuicTcpLikeTraceConverter::OnControlFrameSent(
+ QuicControlFrameId control_frame_id,
+ QuicByteCount control_frame_length) {
+ if (control_frame_id > largest_observed_control_frame_id_) {
+ // New control frame.
+ QuicInterval<uint64_t> connection_offset = QuicInterval<uint64_t>(
+ connection_offset_, connection_offset_ + control_frame_length);
+ connection_offset_ += control_frame_length;
+ control_frames_info_[control_frame_id] = QuicInterval<uint64_t>(
+ connection_offset_, connection_offset_ + control_frame_length);
+ largest_observed_control_frame_id_ = control_frame_id;
+ return connection_offset;
+ }
+ const auto iter = control_frames_info_.find(control_frame_id);
+ if (iter == control_frames_info_.end()) {
+ // Ignore out of order control frames.
+ return {};
+ }
+ return iter->second;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h
new file mode 100644
index 00000000000..432980531be
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h
@@ -0,0 +1,73 @@
+// 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_TOOLS_QUIC_TCP_LIKE_TRACE_CONVERTER_H_
+#define QUICHE_QUIC_TOOLS_QUIC_TCP_LIKE_TRACE_CONVERTER_H_
+
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval.h"
+#include "net/third_party/quiche/src/quic/core/quic_interval_set.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
+
+namespace quic {
+
+// This converter converts sent QUIC frames to connection byte offset (just like
+// TCP byte sequence number).
+class QuicTcpLikeTraceConverter {
+ public:
+ // StreamOffsetSegment stores a stream offset range which has contiguous
+ // connection offset.
+ struct StreamOffsetSegment {
+ StreamOffsetSegment();
+ StreamOffsetSegment(QuicStreamOffset stream_offset,
+ uint64_t connection_offset,
+ QuicByteCount data_length);
+
+ QuicInterval<QuicStreamOffset> stream_data;
+ uint64_t connection_offset;
+ };
+
+ QuicTcpLikeTraceConverter();
+ QuicTcpLikeTraceConverter(const QuicTcpLikeTraceConverter& other) = delete;
+ QuicTcpLikeTraceConverter(QuicTcpLikeTraceConverter&& other) = delete;
+
+ ~QuicTcpLikeTraceConverter() {}
+
+ // Called when a stream frame is sent. Returns the corresponding connection
+ // offsets.
+ QuicIntervalSet<uint64_t> OnStreamFrameSent(QuicStreamId stream_id,
+ QuicStreamOffset offset,
+ QuicByteCount data_length,
+ bool fin);
+
+ // Called when a control frame is sent. Returns the corresponding connection
+ // offsets.
+ QuicInterval<uint64_t> OnControlFrameSent(QuicControlFrameId control_frame_id,
+ QuicByteCount control_frame_length);
+
+ private:
+ struct StreamInfo {
+ StreamInfo();
+
+ // Stores contiguous connection offset pieces.
+ std::vector<StreamOffsetSegment> segments;
+ // Indicates whether fin has been sent.
+ bool fin;
+ };
+
+ QuicUnorderedMap<QuicStreamId, StreamInfo> streams_info_;
+ QuicUnorderedMap<QuicControlFrameId, QuicInterval<uint64_t>>
+ control_frames_info_;
+
+ QuicControlFrameId largest_observed_control_frame_id_;
+
+ uint64_t connection_offset_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_TCP_LIKE_TRACE_CONVERTER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc
new file mode 100644
index 00000000000..44f7351036b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter_test.cc
@@ -0,0 +1,103 @@
+// 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 "net/third_party/quiche/src/quic/tools/quic_tcp_like_trace_converter.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+TEST(QuicTcpLikeTraceConverterTest, BasicTest) {
+ QuicTcpLikeTraceConverter converter;
+
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(0, 100),
+ converter.OnStreamFrameSent(1, 0, 100, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(100, 200),
+ converter.OnStreamFrameSent(3, 0, 100, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(200, 300),
+ converter.OnStreamFrameSent(3, 100, 100, false));
+ EXPECT_EQ(QuicInterval<uint64_t>(300, 450),
+ converter.OnControlFrameSent(2, 150));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(450, 550),
+ converter.OnStreamFrameSent(1, 100, 100, false));
+ EXPECT_EQ(QuicInterval<uint64_t>(550, 650),
+ converter.OnControlFrameSent(3, 100));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(650, 850),
+ converter.OnStreamFrameSent(3, 200, 200, false));
+ EXPECT_EQ(QuicInterval<uint64_t>(850, 1050),
+ converter.OnControlFrameSent(4, 200));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(1050, 1100),
+ converter.OnStreamFrameSent(1, 200, 50, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(1100, 1150),
+ converter.OnStreamFrameSent(1, 250, 50, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(1150, 1350),
+ converter.OnStreamFrameSent(3, 400, 200, false));
+
+ // Stream 1 retransmits [50, 300) and sends new data [300, 350) in the same
+ // frame.
+ QuicIntervalSet<uint64_t> expected;
+ expected.Add(50, 100);
+ expected.Add(450, 550);
+ expected.Add(1050, 1150);
+ expected.Add(1350, 1401);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(1, 50, 300, true));
+
+ expected.Clear();
+ // Stream 3 retransmits [150, 500).
+ expected.Add(250, 300);
+ expected.Add(650, 850);
+ expected.Add(1150, 1250);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(3, 150, 350, false));
+
+ // Stream 3 retransmits [300, 600) and sends new data [600, 800) in the same
+ // frame.
+ expected.Clear();
+ expected.Add(750, 850);
+ expected.Add(1150, 1350);
+ expected.Add(1401, 1602);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(3, 300, 500, true));
+
+ // Stream 3 retransmits fin only frame.
+ expected.Clear();
+ expected.Add(1601, 1602);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(3, 800, 0, true));
+
+ QuicInterval<uint64_t> expected2;
+ // Ignore out of order control frames.
+ EXPECT_EQ(expected2, converter.OnControlFrameSent(1, 100));
+
+ // Ignore passed in length for retransmitted frame.
+ expected2 = {450, 600};
+ EXPECT_EQ(expected2, converter.OnControlFrameSent(2, 200));
+
+ expected2 = {1602, 1702};
+ EXPECT_EQ(expected2, converter.OnControlFrameSent(10, 100));
+}
+
+TEST(QuicTcpLikeTraceConverterTest, FuzzerTest) {
+ QuicTcpLikeTraceConverter converter;
+ // Stream does not start from offset 0.
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(0, 100),
+ converter.OnStreamFrameSent(1, 100, 100, false));
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(100, 300),
+ converter.OnStreamFrameSent(3, 200, 200, false));
+ // Stream does not send data contiguously.
+ EXPECT_EQ(QuicIntervalSet<uint64_t>(300, 400),
+ converter.OnStreamFrameSent(1, 300, 100, false));
+
+ // Stream fills existing holes.
+ QuicIntervalSet<uint64_t> expected;
+ expected.Add(0, 100);
+ expected.Add(300, 501);
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(1, 0, 500, true));
+
+ // Stream sends frame after fin.
+ EXPECT_EQ(expected, converter.OnStreamFrameSent(1, 50, 600, false));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_url.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_url.cc
new file mode 100644
index 00000000000..4094ffebb7e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_url.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+static constexpr size_t kMaxHostNameLength = 256;
+
+QuicUrl::QuicUrl(QuicStringPiece url) : url_(static_cast<std::string>(url)) {}
+
+QuicUrl::QuicUrl(QuicStringPiece url, QuicStringPiece default_scheme)
+ : QuicUrl(url) {
+ if (url_.has_scheme()) {
+ return;
+ }
+
+ url_ = GURL(QuicStrCat(default_scheme, "://", url));
+}
+
+std::string QuicUrl::ToString() const {
+ if (IsValid()) {
+ return url_.spec();
+ }
+ return "";
+}
+
+bool QuicUrl::IsValid() const {
+ if (!url_.is_valid() || !url_.has_scheme()) {
+ return false;
+ }
+
+ if (url_.has_host() && url_.host().length() > kMaxHostNameLength) {
+ return false;
+ }
+
+ return true;
+}
+
+std::string QuicUrl::HostPort() const {
+ if (!IsValid() || !url_.has_host()) {
+ return "";
+ }
+
+ std::string host = url_.host();
+ int port = url_.IntPort();
+ if (port == url::PORT_UNSPECIFIED) {
+ return host;
+ }
+ return QuicStrCat(host, ":", port);
+}
+
+std::string QuicUrl::PathParamsQuery() const {
+ if (!IsValid() || !url_.has_path()) {
+ return "/";
+ }
+
+ return url_.PathForRequest();
+}
+
+std::string QuicUrl::scheme() const {
+ if (!IsValid()) {
+ return "";
+ }
+
+ return url_.scheme();
+}
+
+std::string QuicUrl::host() const {
+ if (!IsValid()) {
+ return "";
+ }
+
+ return url_.HostNoBrackets();
+}
+
+std::string QuicUrl::path() const {
+ if (!IsValid()) {
+ return "";
+ }
+
+ return url_.path();
+}
+
+uint16_t QuicUrl::port() const {
+ if (!IsValid()) {
+ return 0;
+ }
+
+ int port = url_.EffectiveIntPort();
+ if (port == url::PORT_UNSPECIFIED) {
+ return 0;
+ }
+ return port;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_url.h b/chromium/net/third_party/quiche/src/quic/tools/quic_url.h
new file mode 100644
index 00000000000..da1b69db14d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_url.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_TOOLS_QUIC_URL_H_
+#define QUICHE_QUIC_TOOLS_QUIC_URL_H_
+
+#include <string>
+
+#include "url/gurl.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// A utility class that wraps GURL.
+class QuicUrl {
+ public:
+ // Constructs an empty QuicUrl.
+ QuicUrl() = default;
+
+ // Constructs a QuicUrl from the url string |url|.
+ //
+ // NOTE: If |url| doesn't have a scheme, it will have an empty scheme
+ // field. If that's not what you want, use the QuicUrlImpl(url,
+ // default_scheme) form below.
+ explicit QuicUrl(QuicStringPiece url);
+
+ // Constructs a QuicUrlImpl from |url|, assuming that the scheme for the URL
+ // is |default_scheme| if there is no scheme specified in |url|.
+ QuicUrl(QuicStringPiece url, QuicStringPiece default_scheme);
+
+ // Returns false if the URL is not valid.
+ bool IsValid() const;
+
+ // Returns full text of the QuicUrl if it is valid. Return empty string
+ // otherwise.
+ std::string ToString() const;
+
+ // Returns host:port.
+ // If the host is empty, it will return an empty string.
+ // If the host is an IPv6 address, it will be bracketed.
+ // If port is not present or is equal to default_port of scheme (e.g., port
+ // 80 for HTTP), it won't be returned.
+ std::string HostPort() const;
+
+ // Returns a string assembles path, parameters and query.
+ std::string PathParamsQuery() const;
+
+ std::string scheme() const;
+ std::string host() const;
+ std::string path() const;
+ uint16_t port() const;
+
+ private:
+ GURL url_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_TOOLS_QUIC_URL_H_
diff --git a/chromium/net/third_party/quiche/src/quic/tools/quic_url_test.cc b/chromium/net/third_party/quiche/src/quic/tools/quic_url_test.cc
new file mode 100644
index 00000000000..608fdb107fc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/tools/quic_url_test.cc
@@ -0,0 +1,157 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/tools/quic_url.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+class QuicUrlTest : public QuicTest {};
+
+TEST_F(QuicUrlTest, Basic) {
+ // No scheme specified.
+ std::string url_str = "www.example.com";
+ QuicUrl url(url_str);
+ EXPECT_FALSE(url.IsValid());
+
+ // scheme is HTTP.
+ url_str = "http://www.example.com";
+ url = QuicUrl(url_str);
+ EXPECT_TRUE(url.IsValid());
+ EXPECT_EQ("http://www.example.com/", url.ToString());
+ EXPECT_EQ("http", url.scheme());
+ EXPECT_EQ("www.example.com", url.HostPort());
+ EXPECT_EQ("/", url.PathParamsQuery());
+ EXPECT_EQ(80u, url.port());
+
+ // scheme is HTTPS.
+ url_str = "https://www.example.com:12345/path/to/resource?a=1&campaign=2";
+ url = QuicUrl(url_str);
+ EXPECT_TRUE(url.IsValid());
+ EXPECT_EQ("https://www.example.com:12345/path/to/resource?a=1&campaign=2",
+ url.ToString());
+ EXPECT_EQ("https", url.scheme());
+ EXPECT_EQ("www.example.com:12345", url.HostPort());
+ EXPECT_EQ("/path/to/resource?a=1&campaign=2", url.PathParamsQuery());
+ EXPECT_EQ(12345u, url.port());
+
+ // scheme is FTP.
+ url_str = "ftp://www.example.com";
+ url = QuicUrl(url_str);
+ EXPECT_TRUE(url.IsValid());
+ EXPECT_EQ("ftp://www.example.com/", url.ToString());
+ EXPECT_EQ("ftp", url.scheme());
+ EXPECT_EQ("www.example.com", url.HostPort());
+ EXPECT_EQ("/", url.PathParamsQuery());
+ EXPECT_EQ(21u, url.port());
+}
+
+TEST_F(QuicUrlTest, DefaultScheme) {
+ // Default scheme to HTTP.
+ std::string url_str = "www.example.com";
+ QuicUrl url(url_str, "http");
+ EXPECT_EQ("http://www.example.com/", url.ToString());
+ EXPECT_EQ("http", url.scheme());
+
+ // URL already has a scheme specified.
+ url_str = "http://www.example.com";
+ url = QuicUrl(url_str, "https");
+ EXPECT_EQ("http://www.example.com/", url.ToString());
+ EXPECT_EQ("http", url.scheme());
+
+ // Default scheme to FTP.
+ url_str = "www.example.com";
+ url = QuicUrl(url_str, "ftp");
+ EXPECT_EQ("ftp://www.example.com/", url.ToString());
+ EXPECT_EQ("ftp", url.scheme());
+}
+
+TEST_F(QuicUrlTest, IsValid) {
+ std::string url_str =
+ "ftp://www.example.com:12345/path/to/resource?a=1&campaign=2";
+ EXPECT_TRUE(QuicUrl(url_str).IsValid());
+
+ // Invalid characters in host name.
+ url_str = "https://www%.example.com:12345/path/to/resource?a=1&campaign=2";
+ EXPECT_FALSE(QuicUrl(url_str).IsValid());
+
+ // Invalid characters in scheme.
+ url_str = "%http://www.example.com:12345/path/to/resource?a=1&campaign=2";
+ EXPECT_FALSE(QuicUrl(url_str).IsValid());
+
+ // Host name too long.
+ std::string host(1024, 'a');
+ url_str = "https://" + host;
+ EXPECT_FALSE(QuicUrl(url_str).IsValid());
+
+ // Invalid port number.
+ url_str = "https://www..example.com:123456/path/to/resource?a=1&campaign=2";
+ EXPECT_FALSE(QuicUrl(url_str).IsValid());
+}
+
+TEST_F(QuicUrlTest, HostPort) {
+ std::string url_str = "http://www.example.com/";
+ QuicUrl url(url_str);
+ EXPECT_EQ("www.example.com", url.HostPort());
+ EXPECT_EQ("www.example.com", url.host());
+ EXPECT_EQ(80u, url.port());
+
+ url_str = "http://www.example.com:80/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("www.example.com", url.HostPort());
+ EXPECT_EQ("www.example.com", url.host());
+ EXPECT_EQ(80u, url.port());
+
+ url_str = "http://www.example.com:81/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("www.example.com:81", url.HostPort());
+ EXPECT_EQ("www.example.com", url.host());
+ EXPECT_EQ(81u, url.port());
+
+ url_str = "https://192.168.1.1:443/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("192.168.1.1", url.HostPort());
+ EXPECT_EQ("192.168.1.1", url.host());
+ EXPECT_EQ(443u, url.port());
+
+ url_str = "http://[2001::1]:80/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("[2001::1]", url.HostPort());
+ EXPECT_EQ("2001::1", url.host());
+ EXPECT_EQ(80u, url.port());
+
+ url_str = "http://[2001::1]:81/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("[2001::1]:81", url.HostPort());
+ EXPECT_EQ("2001::1", url.host());
+ EXPECT_EQ(81u, url.port());
+}
+
+TEST_F(QuicUrlTest, PathParamsQuery) {
+ std::string url_str =
+ "https://www.example.com:12345/path/to/resource?a=1&campaign=2";
+ QuicUrl url(url_str);
+ EXPECT_EQ("/path/to/resource?a=1&campaign=2", url.PathParamsQuery());
+ EXPECT_EQ("/path/to/resource", url.path());
+
+ url_str = "https://www.example.com/?";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("/?", url.PathParamsQuery());
+ EXPECT_EQ("/", url.path());
+
+ url_str = "https://www.example.com/";
+ url = QuicUrl(url_str);
+ EXPECT_EQ("/", url.PathParamsQuery());
+ EXPECT_EQ("/", url.path());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_constants.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_constants.cc
index 9c1498a9d2e..77cae3ae9bc 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_constants.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_constants.cc
@@ -8,10 +8,10 @@
#include <memory>
#include <vector>
-#include "base/logging.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_arraysize.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
namespace spdy {
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 309af39a06b..ad05b1fdb62 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
@@ -4,10 +4,10 @@
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
using ::http2::DecodeBuffer;
using ::http2::HpackEntryType;
@@ -26,13 +26,13 @@ HpackDecoderAdapter::HpackDecoderAdapter()
HpackDecoderAdapter::~HpackDecoderAdapter() = default;
void HpackDecoderAdapter::ApplyHeaderTableSizeSetting(size_t size_setting) {
- DVLOG(2) << "HpackDecoderAdapter::ApplyHeaderTableSizeSetting";
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::ApplyHeaderTableSizeSetting";
hpack_decoder_.ApplyHeaderTableSizeSetting(size_setting);
}
void HpackDecoderAdapter::HandleControlFrameHeadersStart(
SpdyHeadersHandlerInterface* handler) {
- DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersStart";
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersStart";
DCHECK(!header_block_started_);
listener_adapter_.set_handler(handler);
}
@@ -40,8 +40,8 @@ void HpackDecoderAdapter::HandleControlFrameHeadersStart(
bool HpackDecoderAdapter::HandleControlFrameHeadersData(
const char* headers_data,
size_t headers_data_length) {
- DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersData: len="
- << headers_data_length;
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersData: len="
+ << headers_data_length;
if (!header_block_started_) {
// Initialize the decoding process here rather than in
// HandleControlFrameHeadersStart because that method is not always called.
@@ -58,8 +58,9 @@ bool HpackDecoderAdapter::HandleControlFrameHeadersData(
if (headers_data_length > 0) {
DCHECK_NE(headers_data, nullptr);
if (headers_data_length > max_decode_buffer_size_bytes_) {
- DVLOG(1) << "max_decode_buffer_size_bytes_ < headers_data_length: "
- << max_decode_buffer_size_bytes_ << " < " << headers_data_length;
+ SPDY_DVLOG(1) << "max_decode_buffer_size_bytes_ < headers_data_length: "
+ << max_decode_buffer_size_bytes_ << " < "
+ << headers_data_length;
return false;
}
listener_adapter_.AddToTotalHpackBytes(headers_data_length);
@@ -73,12 +74,12 @@ bool HpackDecoderAdapter::HandleControlFrameHeadersData(
bool HpackDecoderAdapter::HandleControlFrameHeadersComplete(
size_t* compressed_len) {
- DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersComplete";
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersComplete";
if (compressed_len != nullptr) {
*compressed_len = listener_adapter_.total_hpack_bytes();
}
if (!hpack_decoder_.EndDecodingBlock()) {
- DVLOG(3) << "EndDecodingBlock returned false";
+ SPDY_DVLOG(3) << "EndDecodingBlock returned false";
return false;
}
header_block_started_ = false;
@@ -91,7 +92,7 @@ const SpdyHeaderBlock& HpackDecoderAdapter::decoded_block() const {
void HpackDecoderAdapter::SetHeaderTableDebugVisitor(
std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) {
- DVLOG(2) << "HpackDecoderAdapter::SetHeaderTableDebugVisitor";
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::SetHeaderTableDebugVisitor";
if (visitor != nullptr) {
listener_adapter_.SetHeaderTableDebugVisitor(std::move(visitor));
hpack_decoder_.set_tables_debug_listener(&listener_adapter_);
@@ -103,7 +104,7 @@ void HpackDecoderAdapter::SetHeaderTableDebugVisitor(
void HpackDecoderAdapter::set_max_decode_buffer_size_bytes(
size_t max_decode_buffer_size_bytes) {
- DVLOG(2) << "HpackDecoderAdapter::set_max_decode_buffer_size_bytes";
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::set_max_decode_buffer_size_bytes";
max_decode_buffer_size_bytes_ = max_decode_buffer_size_bytes;
hpack_decoder_.set_max_string_size_bytes(max_decode_buffer_size_bytes);
}
@@ -126,7 +127,7 @@ void HpackDecoderAdapter::ListenerAdapter::SetHeaderTableDebugVisitor(
}
void HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart() {
- DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart";
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart";
total_hpack_bytes_ = 0;
total_uncompressed_bytes_ = 0;
decoded_block_.clear();
@@ -138,21 +139,21 @@ void HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart() {
void HpackDecoderAdapter::ListenerAdapter::OnHeader(HpackEntryType entry_type,
const HpackString& name,
const HpackString& value) {
- DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeader:\n name: " << name
- << "\n value: " << value;
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeader:\n name: "
+ << name << "\n value: " << value;
total_uncompressed_bytes_ += name.size() + value.size();
if (handler_ == nullptr) {
- DVLOG(3) << "Adding to decoded_block";
+ SPDY_DVLOG(3) << "Adding to decoded_block";
decoded_block_.AppendValueOrAddHeader(name.ToStringPiece(),
value.ToStringPiece());
} else {
- DVLOG(3) << "Passing to handler";
+ SPDY_DVLOG(3) << "Passing to handler";
handler_->OnHeader(name.ToStringPiece(), value.ToStringPiece());
}
}
void HpackDecoderAdapter::ListenerAdapter::OnHeaderListEnd() {
- DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListEnd";
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListEnd";
// We don't clear the SpdyHeaderBlock here to allow access to it until the
// next HPACK block is decoded.
if (handler_ != nullptr) {
@@ -163,21 +164,21 @@ void HpackDecoderAdapter::ListenerAdapter::OnHeaderListEnd() {
void HpackDecoderAdapter::ListenerAdapter::OnHeaderErrorDetected(
SpdyStringPiece error_message) {
- VLOG(1) << error_message;
+ SPDY_VLOG(1) << error_message;
}
int64_t HpackDecoderAdapter::ListenerAdapter::OnEntryInserted(
const http2::HpackStringPair& sp,
size_t insert_count) {
- DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnEntryInserted: " << sp
- << ", insert_count=" << insert_count;
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnEntryInserted: "
+ << sp << ", insert_count=" << insert_count;
if (visitor_ == nullptr) {
return 0;
}
HpackEntry entry(sp.name.ToStringPiece(), sp.value.ToStringPiece(),
/*is_static*/ false, insert_count);
int64_t time_added = visitor_->OnNewEntry(entry);
- DVLOG(2)
+ SPDY_DVLOG(2)
<< "HpackDecoderAdapter::ListenerAdapter::OnEntryInserted: time_added="
<< time_added;
return time_added;
@@ -187,9 +188,9 @@ void HpackDecoderAdapter::ListenerAdapter::OnUseEntry(
const http2::HpackStringPair& sp,
size_t insert_count,
int64_t time_added) {
- DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnUseEntry: " << sp
- << ", insert_count=" << insert_count
- << ", time_added=" << time_added;
+ SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnUseEntry: " << sp
+ << ", insert_count=" << insert_count
+ << ", time_added=" << time_added;
if (visitor_ != nullptr) {
HpackEntry entry(sp.name.ToStringPiece(), sp.value.ToStringPiece(),
/*is_static*/ false, insert_count);
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 b497fe7a47c..d54cf1e4755 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
@@ -13,7 +13,6 @@
#include <cstdint>
#include <memory>
-#include "base/macros.h"
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder.h"
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_listener.h"
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.h"
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 9c941263cac..0d952d1ad8c 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
@@ -12,7 +12,6 @@
#include <utility>
#include <vector>
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h"
@@ -24,6 +23,7 @@
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h"
#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_arraysize.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
@@ -135,7 +135,7 @@ class HpackDecoderAdapterTest
}
bool HandleControlFrameHeadersData(SpdyStringPiece str) {
- VLOG(3) << "HandleControlFrameHeadersData:\n" << SpdyHexDump(str);
+ SPDY_VLOG(3) << "HandleControlFrameHeadersData:\n" << SpdyHexDump(str);
bytes_passed_in_ += str.size();
return decoder_.HandleControlFrameHeadersData(str.data(), str.size());
}
@@ -260,12 +260,14 @@ class HpackDecoderAdapterTest
};
INSTANTIATE_TEST_SUITE_P(
- NoHandler, HpackDecoderAdapterTest,
+ NoHandler,
+ HpackDecoderAdapterTest,
::testing::Combine(::testing::Values(START_WITHOUT_HANDLER, NO_START),
::testing::Bool()));
INSTANTIATE_TEST_SUITE_P(
- WithHandler, HpackDecoderAdapterTest,
+ WithHandler,
+ HpackDecoderAdapterTest,
::testing::Combine(::testing::Values(START_WITH_HANDLER),
::testing::Bool()));
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 a9981b95a73..d76ead083c1 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
@@ -7,12 +7,12 @@
#include <algorithm>
#include <limits>
-#include "base/logging.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h"
namespace spdy {
@@ -155,22 +155,22 @@ void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter,
}
void HpackEncoder::EmitIndex(const HpackEntry* entry) {
- DVLOG(2) << "Emitting index " << header_table_.IndexOf(entry);
+ SPDY_DVLOG(2) << "Emitting index " << header_table_.IndexOf(entry);
output_stream_.AppendPrefix(kIndexedOpcode);
output_stream_.AppendUint32(header_table_.IndexOf(entry));
}
void HpackEncoder::EmitIndexedLiteral(const Representation& representation) {
- DVLOG(2) << "Emitting indexed literal: (" << representation.first << ", "
- << representation.second << ")";
+ SPDY_DVLOG(2) << "Emitting indexed literal: (" << representation.first << ", "
+ << representation.second << ")";
output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode);
EmitLiteral(representation);
header_table_.TryAddEntry(representation.first, representation.second);
}
void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation) {
- DVLOG(2) << "Emitting nonindexed literal: (" << representation.first << ", "
- << representation.second << ")";
+ SPDY_DVLOG(2) << "Emitting nonindexed literal: (" << representation.first
+ << ", " << representation.second << ")";
output_stream_.AppendPrefix(kLiteralNoIndexOpcode);
output_stream_.AppendUint32(0);
EmitString(representation.first);
@@ -192,12 +192,13 @@ void HpackEncoder::EmitString(SpdyStringPiece str) {
size_t encoded_size =
enable_compression_ ? huffman_table_.EncodedSize(str) : str.size();
if (encoded_size < str.size()) {
- DVLOG(2) << "Emitted Huffman-encoded string of length " << encoded_size;
+ SPDY_DVLOG(2) << "Emitted Huffman-encoded string of length "
+ << encoded_size;
output_stream_.AppendPrefix(kStringLiteralHuffmanEncoded);
output_stream_.AppendUint32(encoded_size);
huffman_table_.EncodeString(str, &output_stream_);
} else {
- DVLOG(2) << "Emitted literal string of length " << str.size();
+ SPDY_DVLOG(2) << "Emitted literal string of length " << str.size();
output_stream_.AppendPrefix(kStringLiteralIdentityEncoded);
output_stream_.AppendUint32(str.size());
output_stream_.AppendBytes(str);
@@ -209,9 +210,9 @@ void HpackEncoder::MaybeEmitTableSize() {
return;
}
const size_t current_size = CurrentHeaderTableSizeSetting();
- DVLOG(1) << "MaybeEmitTableSize current_size=" << current_size;
- DVLOG(1) << "MaybeEmitTableSize min_table_size_setting_received_="
- << min_table_size_setting_received_;
+ SPDY_DVLOG(1) << "MaybeEmitTableSize current_size=" << current_size;
+ SPDY_DVLOG(1) << "MaybeEmitTableSize min_table_size_setting_received_="
+ << min_table_size_setting_received_;
if (min_table_size_setting_received_ < current_size) {
output_stream_.AppendPrefix(kHeaderTableSizeUpdateOpcode);
output_stream_.AppendUint32(min_table_size_setting_received_);
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 486d53693e2..c0cf968ab5d 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
@@ -13,7 +13,6 @@
#include <utility>
#include <vector>
-#include "base/macros.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
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 7d7d37d1c0d..a4f763cd5d7 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
@@ -207,7 +207,8 @@ class HpackEncoderTest : public ::testing::TestWithParam<bool> {
bool use_incremental_;
};
-INSTANTIATE_TEST_SUITE_P(HpackEncoderTests, HpackEncoderTest,
+INSTANTIATE_TEST_SUITE_P(HpackEncoderTests,
+ HpackEncoderTest,
::testing::Bool());
TEST_P(HpackEncoderTest, SingleDynamicIndex) {
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 90d53e65d73..46e608615aa 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
@@ -4,8 +4,8 @@
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
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 28dee47807a..61103fddd8e 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
@@ -8,7 +8,6 @@
#include <cstddef>
#include <cstdint>
-#include "base/macros.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h"
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 dbe565f1c27..17f91cdf491 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
@@ -6,11 +6,11 @@
#include <algorithm>
-#include "base/logging.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_containers.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
namespace spdy {
@@ -202,9 +202,9 @@ const HpackEntry* HpackHeaderTable::TryAddEntry(SpdyStringPiece name,
if (!index_result.second) {
// An entry with the same name and value already exists in the dynamic
// index. We should replace it with the newly added entry.
- DVLOG(1) << "Found existing entry: "
- << (*index_result.first)->GetDebugString()
- << " replacing with: " << new_entry->GetDebugString();
+ SPDY_DVLOG(1) << "Found existing entry: "
+ << (*index_result.first)->GetDebugString()
+ << " replacing with: " << new_entry->GetDebugString();
DCHECK_GT(new_entry->InsertionIndex(),
(*index_result.first)->InsertionIndex());
dynamic_index_.erase(index_result.first);
@@ -216,9 +216,9 @@ const HpackEntry* HpackHeaderTable::TryAddEntry(SpdyStringPiece name,
if (!name_result.second) {
// An entry with the same name already exists in the dynamic index. We
// should replace it with the newly added entry.
- DVLOG(1) << "Found existing entry: "
- << name_result.first->second->GetDebugString()
- << " replacing with: " << new_entry->GetDebugString();
+ SPDY_DVLOG(1) << "Found existing entry: "
+ << name_result.first->second->GetDebugString()
+ << " replacing with: " << new_entry->GetDebugString();
DCHECK_GT(new_entry->InsertionIndex(),
name_result.first->second->InsertionIndex());
dynamic_name_index_.erase(name_result.first);
@@ -233,35 +233,35 @@ const HpackEntry* HpackHeaderTable::TryAddEntry(SpdyStringPiece name,
// Call |debug_visitor_->OnNewEntry()| to get the current time.
HpackEntry& entry = dynamic_entries_.front();
entry.set_time_added(debug_visitor_->OnNewEntry(entry));
- DVLOG(2) << "HpackHeaderTable::OnNewEntry: name=" << entry.name()
- << ", value=" << entry.value()
- << ", insert_index=" << entry.InsertionIndex()
- << ", time_added=" << entry.time_added();
+ SPDY_DVLOG(2) << "HpackHeaderTable::OnNewEntry: name=" << entry.name()
+ << ", value=" << entry.value()
+ << ", insert_index=" << entry.InsertionIndex()
+ << ", time_added=" << entry.time_added();
}
return &dynamic_entries_.front();
}
void HpackHeaderTable::DebugLogTableState() const {
- DVLOG(2) << "Dynamic table:";
+ SPDY_DVLOG(2) << "Dynamic table:";
for (auto it = dynamic_entries_.begin(); it != dynamic_entries_.end(); ++it) {
- DVLOG(2) << " " << it->GetDebugString();
+ SPDY_DVLOG(2) << " " << it->GetDebugString();
}
- DVLOG(2) << "Full Static Index:";
+ SPDY_DVLOG(2) << "Full Static Index:";
for (const auto* entry : static_index_) {
- DVLOG(2) << " " << entry->GetDebugString();
+ SPDY_DVLOG(2) << " " << entry->GetDebugString();
}
- DVLOG(2) << "Full Static Name Index:";
+ SPDY_DVLOG(2) << "Full Static Name Index:";
for (const auto it : static_name_index_) {
- DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString();
+ SPDY_DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString();
}
- DVLOG(2) << "Full Dynamic Index:";
+ SPDY_DVLOG(2) << "Full Dynamic Index:";
for (const auto* entry : dynamic_index_) {
- DVLOG(2) << " " << entry->GetDebugString();
+ SPDY_DVLOG(2) << " " << entry->GetDebugString();
}
- DVLOG(2) << "Full Dynamic Name Index:";
+ SPDY_DVLOG(2) << "Full Dynamic Name Index:";
for (const auto it : dynamic_name_index_) {
- DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString();
+ SPDY_DVLOG(2) << " " << it.first << ": " << it.second->GetDebugString();
}
}
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 e85edb7896b..7ff49f3b2b7 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
@@ -10,7 +10,6 @@
#include <deque>
#include <memory>
-#include "base/macros.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_containers.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc
index b032aba435c..6649cc2d4f6 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc
@@ -9,7 +9,6 @@
#include <set>
#include <vector>
-#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.cc
index c917767aa07..3c637a43164 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table.cc
@@ -9,9 +9,9 @@
#include <limits>
#include <memory>
-#include "base/logging.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
namespace spdy {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc
index f30926c35e6..2febbd82286 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc
@@ -6,7 +6,6 @@
#include <utility>
-#include "base/macros.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h"
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 30b566294a5..fc01c3f3bc4 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
@@ -6,8 +6,8 @@
#include <utility>
-#include "base/logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
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 450803ff471..01631468283 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
@@ -8,7 +8,6 @@
#include <cstdint>
#include <map>
-#include "base/macros.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
diff --git a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc
index bd4d6ee2a1b..17477a7974d 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc
@@ -78,8 +78,10 @@ class HpackRoundTripTest : public ::testing::TestWithParam<InputSizeParam> {
HpackDecoderAdapter decoder_;
};
-INSTANTIATE_TEST_SUITE_P(Tests, HpackRoundTripTest,
- ::testing::Values(ALL_INPUT, ONE_BYTE,
+INSTANTIATE_TEST_SUITE_P(Tests,
+ HpackRoundTripTest,
+ ::testing::Values(ALL_INPUT,
+ ONE_BYTE,
ZERO_THEN_ONE_BYTE));
TEST_P(HpackRoundTripTest, ResponseFixtures) {
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 14e282ec5ff..eea1d9eae61 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
@@ -4,10 +4,10 @@
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h"
-#include "base/logging.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h"
namespace spdy {
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 c4e728d2334..e5e3e9ad411 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
@@ -4,10 +4,10 @@
#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
-// Logging policy: If an error in the input is detected, VLOG(n) is used so that
-// the option exists to debug the situation. Otherwise, this code mostly uses
-// DVLOG so that the logging does not slow down production code when things are
-// working OK.
+// Logging policy: If an error in the input is detected, SPDY_VLOG(n) is used so
+// that the option exists to debug the situation. Otherwise, this code mostly
+// uses DVLOG so that the logging does not slow down production code when things
+// are working OK.
#include <stddef.h>
@@ -15,7 +15,6 @@
#include <cstring>
#include <utility>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/decoder/http2_frame_decoder.h"
@@ -33,6 +32,7 @@
#include "net/third_party/quiche/src/spdy/platform/api/spdy_endianness_util.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_flags.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
@@ -173,7 +173,7 @@ const char* Http2DecoderAdapter::SpdyFramerErrorToString(
}
Http2DecoderAdapter::Http2DecoderAdapter() {
- DVLOG(1) << "Http2DecoderAdapter ctor";
+ SPDY_DVLOG(1) << "Http2DecoderAdapter ctor";
ResetInternal();
}
@@ -261,7 +261,7 @@ size_t Http2DecoderAdapter::EstimateMemoryUsage() const {
// This function is largely based on Http2DecoderAdapter::ValidateFrameHeader
// and some parts of Http2DecoderAdapter::ProcessCommonHeader.
bool Http2DecoderAdapter::OnFrameHeader(const Http2FrameHeader& header) {
- DVLOG(1) << "OnFrameHeader: " << header;
+ SPDY_DVLOG(1) << "OnFrameHeader: " << header;
decoded_frame_header_ = true;
if (!latched_probable_http_response_) {
latched_probable_http_response_ = header.IsProbableHttpResponse();
@@ -273,9 +273,10 @@ bool Http2DecoderAdapter::OnFrameHeader(const Http2FrameHeader& header) {
// Report an unexpected frame error and close the connection if we
// expect a known frame type (probably CONTINUATION) and receive an
// unknown frame.
- VLOG(1) << "The framer was expecting to receive a " << expected_frame_type_
- << " frame, but instead received an unknown frame of type "
- << header.type;
+ SPDY_VLOG(1) << "The framer was expecting to receive a "
+ << expected_frame_type_
+ << " frame, but instead received an unknown frame of type "
+ << header.type;
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME);
return false;
}
@@ -291,34 +292,34 @@ bool Http2DecoderAdapter::OnFrameHeader(const Http2FrameHeader& header) {
visitor()->OnUnknownFrame(header.stream_id, raw_frame_type);
if (!valid_stream) {
// Report an invalid frame error if the stream_id is not valid.
- VLOG(1) << "Unknown control frame type " << header.type
- << " received on invalid stream " << header.stream_id;
+ SPDY_VLOG(1) << "Unknown control frame type " << header.type
+ << " received on invalid stream " << header.stream_id;
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_CONTROL_FRAME);
return false;
} else {
- DVLOG(1) << "Ignoring unknown frame type " << header.type;
+ SPDY_DVLOG(1) << "Ignoring unknown frame type " << header.type;
return true;
}
}
SpdyFrameType frame_type = ToSpdyFrameType(header.type);
if (!IsValidHTTP2FrameStreamId(header.stream_id, frame_type)) {
- VLOG(1) << "The framer received an invalid streamID of " << header.stream_id
- << " for a frame of type " << header.type;
+ SPDY_VLOG(1) << "The framer received an invalid streamID of "
+ << header.stream_id << " for a frame of type " << header.type;
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_STREAM_ID);
return false;
}
if (has_expected_frame_type_ && header.type != expected_frame_type_) {
- VLOG(1) << "Expected frame type " << expected_frame_type_ << ", not "
- << header.type;
+ SPDY_VLOG(1) << "Expected frame type " << expected_frame_type_ << ", not "
+ << header.type;
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME);
return false;
}
if (!has_expected_frame_type_ &&
header.type == Http2FrameType::CONTINUATION) {
- VLOG(1) << "Got CONTINUATION frame when not expected.";
+ SPDY_VLOG(1) << "Got CONTINUATION frame when not expected.";
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME);
return false;
}
@@ -336,7 +337,7 @@ bool Http2DecoderAdapter::OnFrameHeader(const Http2FrameHeader& header) {
}
void Http2DecoderAdapter::OnDataStart(const Http2FrameHeader& header) {
- DVLOG(1) << "OnDataStart: " << header;
+ SPDY_DVLOG(1) << "OnDataStart: " << header;
if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) {
frame_header_ = header;
@@ -347,14 +348,14 @@ void Http2DecoderAdapter::OnDataStart(const Http2FrameHeader& header) {
}
void Http2DecoderAdapter::OnDataPayload(const char* data, size_t len) {
- DVLOG(1) << "OnDataPayload: len=" << len;
+ SPDY_DVLOG(1) << "OnDataPayload: len=" << len;
DCHECK(has_frame_header_);
DCHECK_EQ(frame_header_.type, Http2FrameType::DATA);
visitor()->OnStreamFrameData(frame_header().stream_id, data, len);
}
void Http2DecoderAdapter::OnDataEnd() {
- DVLOG(1) << "OnDataEnd";
+ SPDY_DVLOG(1) << "OnDataEnd";
DCHECK(has_frame_header_);
DCHECK_EQ(frame_header_.type, Http2FrameType::DATA);
if (frame_header().IsEndStream()) {
@@ -364,7 +365,7 @@ void Http2DecoderAdapter::OnDataEnd() {
}
void Http2DecoderAdapter::OnHeadersStart(const Http2FrameHeader& header) {
- DVLOG(1) << "OnHeadersStart: " << header;
+ SPDY_DVLOG(1) << "OnHeadersStart: " << header;
if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) {
frame_header_ = header;
has_frame_header_ = true;
@@ -387,7 +388,7 @@ void Http2DecoderAdapter::OnHeadersStart(const Http2FrameHeader& header) {
void Http2DecoderAdapter::OnHeadersPriority(
const Http2PriorityFields& priority) {
- DVLOG(1) << "OnHeadersPriority: " << priority;
+ SPDY_DVLOG(1) << "OnHeadersPriority: " << priority;
DCHECK(has_frame_header_);
DCHECK_EQ(frame_type(), Http2FrameType::HEADERS) << frame_header_;
DCHECK(frame_header_.HasPriority());
@@ -402,7 +403,7 @@ void Http2DecoderAdapter::OnHeadersPriority(
}
void Http2DecoderAdapter::OnHpackFragment(const char* data, size_t len) {
- DVLOG(1) << "OnHpackFragment: len=" << len;
+ SPDY_DVLOG(1) << "OnHpackFragment: len=" << len;
on_hpack_fragment_called_ = true;
if (!GetHpackDecoder()->HandleControlFrameHeadersData(data, len)) {
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_DECOMPRESS_FAILURE);
@@ -411,14 +412,14 @@ void Http2DecoderAdapter::OnHpackFragment(const char* data, size_t len) {
}
void Http2DecoderAdapter::OnHeadersEnd() {
- DVLOG(1) << "OnHeadersEnd";
+ SPDY_DVLOG(1) << "OnHeadersEnd";
CommonHpackFragmentEnd();
opt_pad_length_.reset();
}
void Http2DecoderAdapter::OnPriorityFrame(const Http2FrameHeader& header,
const Http2PriorityFields& priority) {
- DVLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
+ SPDY_DVLOG(1) << "OnPriorityFrame: " << header << "; priority: " << priority;
if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) {
visitor()->OnPriority(header.stream_id, priority.stream_dependency,
priority.weight, priority.is_exclusive);
@@ -426,7 +427,7 @@ void Http2DecoderAdapter::OnPriorityFrame(const Http2FrameHeader& header,
}
void Http2DecoderAdapter::OnContinuationStart(const Http2FrameHeader& header) {
- DVLOG(1) << "OnContinuationStart: " << header;
+ SPDY_DVLOG(1) << "OnContinuationStart: " << header;
if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) {
DCHECK(has_hpack_first_frame_header_);
if (header.stream_id != hpack_first_frame_header_.stream_id) {
@@ -441,12 +442,12 @@ void Http2DecoderAdapter::OnContinuationStart(const Http2FrameHeader& header) {
}
void Http2DecoderAdapter::OnContinuationEnd() {
- DVLOG(1) << "OnContinuationEnd";
+ SPDY_DVLOG(1) << "OnContinuationEnd";
CommonHpackFragmentEnd();
}
void Http2DecoderAdapter::OnPadLength(size_t trailing_length) {
- DVLOG(1) << "OnPadLength: " << trailing_length;
+ SPDY_DVLOG(1) << "OnPadLength: " << trailing_length;
opt_pad_length_ = trailing_length;
DCHECK_LT(trailing_length, 256u);
if (frame_header_.type == Http2FrameType::DATA) {
@@ -456,7 +457,7 @@ void Http2DecoderAdapter::OnPadLength(size_t trailing_length) {
void Http2DecoderAdapter::OnPadding(const char* padding,
size_t skipped_length) {
- DVLOG(1) << "OnPadding: " << skipped_length;
+ SPDY_DVLOG(1) << "OnPadding: " << skipped_length;
if (frame_header_.type == Http2FrameType::DATA) {
visitor()->OnStreamPadding(stream_id(), skipped_length);
} else {
@@ -466,7 +467,7 @@ void Http2DecoderAdapter::OnPadding(const char* padding,
void Http2DecoderAdapter::OnRstStream(const Http2FrameHeader& header,
Http2ErrorCode http2_error_code) {
- DVLOG(1) << "OnRstStream: " << header << "; code=" << http2_error_code;
+ SPDY_DVLOG(1) << "OnRstStream: " << header << "; code=" << http2_error_code;
if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) {
SpdyErrorCode error_code =
ParseErrorCode(static_cast<uint32_t>(http2_error_code));
@@ -475,7 +476,7 @@ void Http2DecoderAdapter::OnRstStream(const Http2FrameHeader& header,
}
void Http2DecoderAdapter::OnSettingsStart(const Http2FrameHeader& header) {
- DVLOG(1) << "OnSettingsStart: " << header;
+ SPDY_DVLOG(1) << "OnSettingsStart: " << header;
if (IsOkToStartFrame(header) && HasRequiredStreamIdZero(header)) {
frame_header_ = header;
has_frame_header_ = true;
@@ -484,7 +485,7 @@ void Http2DecoderAdapter::OnSettingsStart(const Http2FrameHeader& header) {
}
void Http2DecoderAdapter::OnSetting(const Http2SettingFields& setting_fields) {
- DVLOG(1) << "OnSetting: " << setting_fields;
+ SPDY_DVLOG(1) << "OnSetting: " << setting_fields;
const auto parameter = static_cast<SpdySettingsId>(setting_fields.parameter);
visitor()->OnSetting(parameter, setting_fields.value);
if (extension_ != nullptr) {
@@ -493,12 +494,12 @@ void Http2DecoderAdapter::OnSetting(const Http2SettingFields& setting_fields) {
}
void Http2DecoderAdapter::OnSettingsEnd() {
- DVLOG(1) << "OnSettingsEnd";
+ SPDY_DVLOG(1) << "OnSettingsEnd";
visitor()->OnSettingsEnd();
}
void Http2DecoderAdapter::OnSettingsAck(const Http2FrameHeader& header) {
- DVLOG(1) << "OnSettingsAck: " << header;
+ SPDY_DVLOG(1) << "OnSettingsAck: " << header;
if (IsOkToStartFrame(header) && HasRequiredStreamIdZero(header)) {
visitor()->OnSettingsAck();
}
@@ -508,8 +509,8 @@ void Http2DecoderAdapter::OnPushPromiseStart(
const Http2FrameHeader& header,
const Http2PushPromiseFields& promise,
size_t total_padding_length) {
- DVLOG(1) << "OnPushPromiseStart: " << header << "; promise: " << promise
- << "; total_padding_length: " << total_padding_length;
+ SPDY_DVLOG(1) << "OnPushPromiseStart: " << header << "; promise: " << promise
+ << "; total_padding_length: " << total_padding_length;
if (IsOkToStartFrame(header) && HasRequiredStreamId(header)) {
if (promise.promised_stream_id == 0) {
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_CONTROL_FRAME);
@@ -525,14 +526,14 @@ void Http2DecoderAdapter::OnPushPromiseStart(
}
void Http2DecoderAdapter::OnPushPromiseEnd() {
- DVLOG(1) << "OnPushPromiseEnd";
+ SPDY_DVLOG(1) << "OnPushPromiseEnd";
CommonHpackFragmentEnd();
opt_pad_length_.reset();
}
void Http2DecoderAdapter::OnPing(const Http2FrameHeader& header,
const Http2PingFields& ping) {
- DVLOG(1) << "OnPing: " << header << "; ping: " << ping;
+ SPDY_DVLOG(1) << "OnPing: " << header << "; ping: " << ping;
if (IsOkToStartFrame(header) && HasRequiredStreamIdZero(header)) {
visitor()->OnPing(ToSpdyPingId(ping), false);
}
@@ -540,7 +541,7 @@ void Http2DecoderAdapter::OnPing(const Http2FrameHeader& header,
void Http2DecoderAdapter::OnPingAck(const Http2FrameHeader& header,
const Http2PingFields& ping) {
- DVLOG(1) << "OnPingAck: " << header << "; ping: " << ping;
+ SPDY_DVLOG(1) << "OnPingAck: " << header << "; ping: " << ping;
if (IsOkToStartFrame(header) && HasRequiredStreamIdZero(header)) {
visitor()->OnPing(ToSpdyPingId(ping), true);
}
@@ -548,7 +549,7 @@ void Http2DecoderAdapter::OnPingAck(const Http2FrameHeader& header,
void Http2DecoderAdapter::OnGoAwayStart(const Http2FrameHeader& header,
const Http2GoAwayFields& goaway) {
- DVLOG(1) << "OnGoAwayStart: " << header << "; goaway: " << goaway;
+ SPDY_DVLOG(1) << "OnGoAwayStart: " << header << "; goaway: " << goaway;
if (IsOkToStartFrame(header) && HasRequiredStreamIdZero(header)) {
frame_header_ = header;
has_frame_header_ = true;
@@ -559,18 +560,18 @@ void Http2DecoderAdapter::OnGoAwayStart(const Http2FrameHeader& header,
}
void Http2DecoderAdapter::OnGoAwayOpaqueData(const char* data, size_t len) {
- DVLOG(1) << "OnGoAwayOpaqueData: len=" << len;
+ SPDY_DVLOG(1) << "OnGoAwayOpaqueData: len=" << len;
visitor()->OnGoAwayFrameData(data, len);
}
void Http2DecoderAdapter::OnGoAwayEnd() {
- DVLOG(1) << "OnGoAwayEnd";
+ SPDY_DVLOG(1) << "OnGoAwayEnd";
visitor()->OnGoAwayFrameData(nullptr, 0);
}
void Http2DecoderAdapter::OnWindowUpdate(const Http2FrameHeader& header,
uint32_t increment) {
- DVLOG(1) << "OnWindowUpdate: " << header << "; increment=" << increment;
+ SPDY_DVLOG(1) << "OnWindowUpdate: " << header << "; increment=" << increment;
if (IsOkToStartFrame(header)) {
visitor()->OnWindowUpdate(header.stream_id, increment);
}
@@ -583,9 +584,9 @@ void Http2DecoderAdapter::OnWindowUpdate(const Http2FrameHeader& header,
void Http2DecoderAdapter::OnAltSvcStart(const Http2FrameHeader& header,
size_t origin_length,
size_t value_length) {
- DVLOG(1) << "OnAltSvcStart: " << header
- << "; origin_length: " << origin_length
- << "; value_length: " << value_length;
+ SPDY_DVLOG(1) << "OnAltSvcStart: " << header
+ << "; origin_length: " << origin_length
+ << "; value_length: " << value_length;
if (!IsOkToStartFrame(header)) {
return;
}
@@ -596,20 +597,20 @@ void Http2DecoderAdapter::OnAltSvcStart(const Http2FrameHeader& header,
}
void Http2DecoderAdapter::OnAltSvcOriginData(const char* data, size_t len) {
- DVLOG(1) << "OnAltSvcOriginData: len=" << len;
+ SPDY_DVLOG(1) << "OnAltSvcOriginData: len=" << len;
alt_svc_origin_.append(data, len);
}
// Called when decoding the Alt-Svc-Field-Value of an ALTSVC;
// the field is uninterpreted.
void Http2DecoderAdapter::OnAltSvcValueData(const char* data, size_t len) {
- DVLOG(1) << "OnAltSvcValueData: len=" << len;
+ SPDY_DVLOG(1) << "OnAltSvcValueData: len=" << len;
alt_svc_value_.append(data, len);
}
void Http2DecoderAdapter::OnAltSvcEnd() {
- DVLOG(1) << "OnAltSvcEnd: origin.size(): " << alt_svc_origin_.size()
- << "; value.size(): " << alt_svc_value_.size();
+ SPDY_DVLOG(1) << "OnAltSvcEnd: origin.size(): " << alt_svc_origin_.size()
+ << "; value.size(): " << alt_svc_value_.size();
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
if (!SpdyAltSvcWireFormat::ParseHeaderFieldValue(alt_svc_value_,
&altsvc_vector)) {
@@ -628,7 +629,7 @@ void Http2DecoderAdapter::OnAltSvcEnd() {
// Except for BLOCKED frames, all other unknown frames are either dropped or
// passed to a registered extension.
void Http2DecoderAdapter::OnUnknownStart(const Http2FrameHeader& header) {
- DVLOG(1) << "OnUnknownStart: " << header;
+ SPDY_DVLOG(1) << "OnUnknownStart: " << header;
if (IsOkToStartFrame(header)) {
if (extension_ != nullptr) {
const uint8_t type = static_cast<uint8_t>(header.type);
@@ -643,19 +644,19 @@ void Http2DecoderAdapter::OnUnknownPayload(const char* data, size_t len) {
if (handling_extension_payload_) {
extension_->OnFramePayload(data, len);
} else {
- DVLOG(1) << "OnUnknownPayload: len=" << len;
+ SPDY_DVLOG(1) << "OnUnknownPayload: len=" << len;
}
}
void Http2DecoderAdapter::OnUnknownEnd() {
- DVLOG(1) << "OnUnknownEnd";
+ SPDY_DVLOG(1) << "OnUnknownEnd";
handling_extension_payload_ = false;
}
void Http2DecoderAdapter::OnPaddingTooLong(const Http2FrameHeader& header,
size_t missing_length) {
- DVLOG(1) << "OnPaddingTooLong: " << header
- << "; missing_length: " << missing_length;
+ SPDY_DVLOG(1) << "OnPaddingTooLong: " << header
+ << "; missing_length: " << missing_length;
if (header.type == Http2FrameType::DATA) {
if (header.payload_length == 0) {
DCHECK_EQ(1u, missing_length);
@@ -668,7 +669,7 @@ void Http2DecoderAdapter::OnPaddingTooLong(const Http2FrameHeader& header,
}
void Http2DecoderAdapter::OnFrameSizeError(const Http2FrameHeader& header) {
- DVLOG(1) << "OnFrameSizeError: " << header;
+ SPDY_DVLOG(1) << "OnFrameSizeError: " << header;
size_t recv_limit = recv_frame_size_limit_;
if (header.payload_length > recv_limit) {
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_OVERSIZED_PAYLOAD);
@@ -698,8 +699,8 @@ size_t Http2DecoderAdapter::ProcessInputFrame(const char* data, size_t len) {
if (spdy_state_ != SpdyState::SPDY_ERROR) {
DetermineSpdyState(status);
} else {
- VLOG(1) << "ProcessInputFrame spdy_framer_error_="
- << SpdyFramerErrorToString(spdy_framer_error_);
+ SPDY_VLOG(1) << "ProcessInputFrame spdy_framer_error_="
+ << SpdyFramerErrorToString(spdy_framer_error_);
if (spdy_framer_error_ == SpdyFramerError::SPDY_INVALID_PADDING &&
has_frame_header_ && frame_type() != Http2FrameType::DATA) {
// spdy_framer_test checks that all of the available frame payload
@@ -707,8 +708,8 @@ size_t Http2DecoderAdapter::ProcessInputFrame(const char* data, size_t len) {
size_t total = remaining_total_payload();
if (total <= frame_header().payload_length) {
size_t avail = db.MinLengthRemaining(total);
- VLOG(1) << "Skipping past " << avail << " bytes, of " << total
- << " total remaining in the frame's payload.";
+ SPDY_VLOG(1) << "Skipping past " << avail << " bytes, of " << total
+ << " total remaining in the frame's payload.";
db.AdvanceCursor(avail);
} else {
SPDY_BUG << "Total remaining (" << total
@@ -729,11 +730,11 @@ void Http2DecoderAdapter::DetermineSpdyState(DecodeStatus status) {
DCHECK(!HasError()) << spdy_framer_error_;
switch (status) {
case DecodeStatus::kDecodeDone:
- DVLOG(1) << "ProcessInputFrame -> DecodeStatus::kDecodeDone";
+ SPDY_DVLOG(1) << "ProcessInputFrame -> DecodeStatus::kDecodeDone";
ResetBetweenFrames();
break;
case DecodeStatus::kDecodeInProgress:
- DVLOG(1) << "ProcessInputFrame -> DecodeStatus::kDecodeInProgress";
+ SPDY_DVLOG(1) << "ProcessInputFrame -> DecodeStatus::kDecodeInProgress";
if (decoded_frame_header_) {
if (IsDiscardingPayload()) {
set_spdy_state(SpdyState::SPDY_IGNORE_REMAINING_PAYLOAD);
@@ -753,7 +754,7 @@ void Http2DecoderAdapter::DetermineSpdyState(DecodeStatus status) {
}
break;
case DecodeStatus::kDecodeError:
- VLOG(1) << "ProcessInputFrame -> DecodeStatus::kDecodeError";
+ SPDY_VLOG(1) << "ProcessInputFrame -> DecodeStatus::kDecodeError";
if (IsDiscardingPayload()) {
if (remaining_total_payload() == 0) {
// Push the Http2FrameDecoder out of state kDiscardPayload now
@@ -808,7 +809,7 @@ void Http2DecoderAdapter::ResetInternal() {
}
void Http2DecoderAdapter::set_spdy_state(SpdyState v) {
- DVLOG(2) << "set_spdy_state(" << StateToString(v) << ")";
+ SPDY_DVLOG(2) << "set_spdy_state(" << StateToString(v) << ")";
spdy_state_ = v;
}
@@ -816,8 +817,8 @@ void Http2DecoderAdapter::SetSpdyErrorAndNotify(SpdyFramerError error) {
if (HasError()) {
DCHECK_EQ(spdy_state_, SpdyState::SPDY_ERROR);
} else {
- VLOG(2) << "SetSpdyErrorAndNotify(" << SpdyFramerErrorToString(error)
- << ")";
+ SPDY_VLOG(2) << "SetSpdyErrorAndNotify(" << SpdyFramerErrorToString(error)
+ << ")";
DCHECK_NE(error, SpdyFramerError::SPDY_NO_ERROR);
spdy_framer_error_ = error;
set_spdy_state(SpdyState::SPDY_ERROR);
@@ -860,33 +861,33 @@ size_t Http2DecoderAdapter::remaining_total_payload() const {
bool Http2DecoderAdapter::IsReadingPaddingLength() {
bool result = frame_header_.IsPadded() && !opt_pad_length_;
- DVLOG(2) << "Http2DecoderAdapter::IsReadingPaddingLength: " << result;
+ SPDY_DVLOG(2) << "Http2DecoderAdapter::IsReadingPaddingLength: " << result;
return result;
}
bool Http2DecoderAdapter::IsSkippingPadding() {
bool result = frame_header_.IsPadded() && opt_pad_length_ &&
frame_decoder_->remaining_payload() == 0 &&
frame_decoder_->remaining_padding() > 0;
- DVLOG(2) << "Http2DecoderAdapter::IsSkippingPadding: " << result;
+ SPDY_DVLOG(2) << "Http2DecoderAdapter::IsSkippingPadding: " << result;
return result;
}
bool Http2DecoderAdapter::IsDiscardingPayload() {
bool result = decoded_frame_header_ && frame_decoder_->IsDiscardingPayload();
- DVLOG(2) << "Http2DecoderAdapter::IsDiscardingPayload: " << result;
+ SPDY_DVLOG(2) << "Http2DecoderAdapter::IsDiscardingPayload: " << result;
return result;
}
// Called from OnXyz or OnXyzStart methods to decide whether it is OK to
// handle the callback.
bool Http2DecoderAdapter::IsOkToStartFrame(const Http2FrameHeader& header) {
- DVLOG(3) << "IsOkToStartFrame";
+ SPDY_DVLOG(3) << "IsOkToStartFrame";
if (HasError()) {
- VLOG(2) << "HasError()";
+ SPDY_VLOG(2) << "HasError()";
return false;
}
DCHECK(!has_frame_header_);
if (has_expected_frame_type_ && header.type != expected_frame_type_) {
- VLOG(1) << "Expected frame type " << expected_frame_type_ << ", not "
- << header.type;
+ SPDY_VLOG(1) << "Expected frame type " << expected_frame_type_ << ", not "
+ << header.type;
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_UNEXPECTED_FRAME);
return false;
}
@@ -895,15 +896,15 @@ bool Http2DecoderAdapter::IsOkToStartFrame(const Http2FrameHeader& header) {
}
bool Http2DecoderAdapter::HasRequiredStreamId(uint32_t stream_id) {
- DVLOG(3) << "HasRequiredStreamId: " << stream_id;
+ SPDY_DVLOG(3) << "HasRequiredStreamId: " << stream_id;
if (HasError()) {
- VLOG(2) << "HasError()";
+ SPDY_VLOG(2) << "HasError()";
return false;
}
if (stream_id != 0) {
return true;
}
- VLOG(1) << "Stream Id is required, but zero provided";
+ SPDY_VLOG(1) << "Stream Id is required, but zero provided";
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_STREAM_ID);
return false;
}
@@ -913,15 +914,15 @@ bool Http2DecoderAdapter::HasRequiredStreamId(const Http2FrameHeader& header) {
}
bool Http2DecoderAdapter::HasRequiredStreamIdZero(uint32_t stream_id) {
- DVLOG(3) << "HasRequiredStreamIdZero: " << stream_id;
+ SPDY_DVLOG(3) << "HasRequiredStreamIdZero: " << stream_id;
if (HasError()) {
- VLOG(2) << "HasError()";
+ SPDY_VLOG(2) << "HasError()";
return false;
}
if (stream_id == 0) {
return true;
}
- VLOG(1) << "Stream Id was not zero, as required: " << stream_id;
+ SPDY_VLOG(1) << "Stream Id was not zero, as required: " << stream_id;
SetSpdyErrorAndNotify(SpdyFramerError::SPDY_INVALID_STREAM_ID);
return false;
}
@@ -948,7 +949,7 @@ HpackDecoderAdapter* Http2DecoderAdapter::GetHpackDecoder() {
}
void Http2DecoderAdapter::CommonStartHpackBlock() {
- DVLOG(1) << "CommonStartHpackBlock";
+ SPDY_DVLOG(1) << "CommonStartHpackBlock";
DCHECK(!has_hpack_first_frame_header_);
if (!frame_header_.IsEndHeaders()) {
hpack_first_frame_header_ = frame_header_;
@@ -977,9 +978,9 @@ void Http2DecoderAdapter::MaybeAnnounceEmptyFirstHpackFragment() {
}
void Http2DecoderAdapter::CommonHpackFragmentEnd() {
- DVLOG(1) << "CommonHpackFragmentEnd: stream_id=" << stream_id();
+ SPDY_DVLOG(1) << "CommonHpackFragmentEnd: stream_id=" << stream_id();
if (HasError()) {
- VLOG(1) << "HasError(), returning";
+ SPDY_VLOG(1) << "HasError(), returning";
return;
}
DCHECK(has_frame_header_);
diff --git a/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h b/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h
index d5c3c44f3af..b02d4634715 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/priority_write_scheduler.h
@@ -13,11 +13,11 @@
#include <utility>
#include <vector>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_containers.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
#include "net/third_party/quiche/src/spdy/core/write_scheduler.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_macros.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
@@ -92,7 +92,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> {
StreamIdType stream_id) const override {
auto it = stream_infos_.find(stream_id);
if (it == stream_infos_.end()) {
- DVLOG(1) << "Stream " << stream_id << " not registered";
+ SPDY_DVLOG(1) << "Stream " << stream_id << " not registered";
return StreamPrecedenceType(kV3LowestPriority);
}
return StreamPrecedenceType(it->second.priority);
@@ -113,7 +113,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> {
auto it = stream_infos_.find(stream_id);
if (it == stream_infos_.end()) {
// TODO(mpw): add to stream_infos_ on demand--see b/15676312.
- DVLOG(1) << "Stream " << stream_id << " not registered";
+ SPDY_DVLOG(1) << "Stream " << stream_id << " not registered";
return;
}
StreamInfo& stream_info = it->second;
@@ -266,7 +266,7 @@ class PriorityWriteScheduler : public WriteScheduler<StreamIdType> {
bool IsStreamReady(StreamIdType stream_id) const {
auto it = stream_infos_.find(stream_id);
if (it == stream_infos_.end()) {
- DLOG(INFO) << "Stream " << stream_id << " not registered";
+ SPDY_DLOG(INFO) << "Stream " << stream_id << " not registered";
return false;
}
return it->second.ready;
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 15234a6e445..d230ae09fdd 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
@@ -8,7 +8,7 @@
#include <cctype>
#include <limits>
-#include "base/logging.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
namespace spdy {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc
index c2595f0b4ec..c30edf298a5 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc
@@ -4,9 +4,9 @@
#include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h"
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
namespace spdy {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc
index 76188e9ce8b..ac93de41f11 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc
@@ -11,7 +11,6 @@
#include <limits>
#include <memory>
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
@@ -20,6 +19,7 @@
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_flags.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h"
@@ -245,7 +245,7 @@ std::unique_ptr<SpdyTestDeframer> SpdyTestDeframer::CreateConverter(
}
void SpdyTestDeframerImpl::AtDataEnd() {
- DVLOG(1) << "AtDataEnd";
+ SPDY_DVLOG(1) << "AtDataEnd";
CHECK_EQ(data_len_, padding_len_ + data_->size());
auto ptr = SpdyMakeUnique<SpdyDataIR>(stream_id_, std::move(*data_));
CHECK_EQ(0u, data_->size());
@@ -266,7 +266,7 @@ void SpdyTestDeframerImpl::AtDataEnd() {
}
void SpdyTestDeframerImpl::AtGoAwayEnd() {
- DVLOG(1) << "AtDataEnd";
+ SPDY_DVLOG(1) << "AtDataEnd";
CHECK_EQ(frame_type_, GOAWAY);
if (HTTP2_DIE_IF_NULL(goaway_description_)->empty()) {
listener_->OnGoAway(std::move(goaway_ir_));
@@ -282,7 +282,7 @@ void SpdyTestDeframerImpl::AtGoAwayEnd() {
}
void SpdyTestDeframerImpl::AtHeadersEnd() {
- DVLOG(1) << "AtDataEnd";
+ SPDY_DVLOG(1) << "AtDataEnd";
CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK(end_) << " frame_type_=" << Http2FrameTypeToString(frame_type_);
@@ -310,7 +310,7 @@ void SpdyTestDeframerImpl::AtHeadersEnd() {
}
void SpdyTestDeframerImpl::AtPushPromiseEnd() {
- DVLOG(1) << "AtDataEnd";
+ SPDY_DVLOG(1) << "AtDataEnd";
CHECK(frame_type_ == PUSH_PROMISE || frame_type_ == CONTINUATION)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK(end_) << " frame_type_=" << Http2FrameTypeToString(frame_type_);
@@ -372,7 +372,7 @@ bool SpdyTestDeframerImpl::AtFrameEnd() {
} else if (push_promise_ir_) {
AtPushPromiseEnd();
} else {
- LOG(FATAL) << "Where is the SpdyFrameIR for the headers!";
+ SPDY_LOG(FATAL) << "Where is the SpdyFrameIR for the headers!";
}
} else {
incomplete_logical_header = true;
@@ -413,7 +413,7 @@ void SpdyTestDeframerImpl::OnAltSvc(
SpdyStreamId stream_id,
SpdyStringPiece origin,
const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) {
- DVLOG(1) << "OnAltSvc stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnAltSvc stream_id: " << stream_id;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_GT(stream_id, 0u);
@@ -430,7 +430,7 @@ void SpdyTestDeframerImpl::OnAltSvc(
// PUSH_PROMISE or CONTINUATION). The last such frame has the END flag set.
// SpdyFramer ensures that the behavior is correct before calling the visitor.
void SpdyTestDeframerImpl::OnContinuation(SpdyStreamId stream_id, bool end) {
- DVLOG(1) << "OnContinuation stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnContinuation stream_id: " << stream_id;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_GT(stream_id, 0u);
@@ -447,7 +447,7 @@ void SpdyTestDeframerImpl::OnContinuation(SpdyStreamId stream_id, bool end) {
void SpdyTestDeframerImpl::OnDataFrameHeader(SpdyStreamId stream_id,
size_t length,
bool fin) {
- DVLOG(1) << "OnDataFrameHeader stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnDataFrameHeader stream_id: " << stream_id;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_GT(stream_id, 0u);
@@ -463,9 +463,9 @@ void SpdyTestDeframerImpl::OnDataFrameHeader(SpdyStreamId stream_id,
// The SpdyFramer will not process any more data at this point.
void SpdyTestDeframerImpl::OnError(
http2::Http2DecoderAdapter::SpdyFramerError error) {
- DVLOG(1) << "SpdyFramer detected an error in the stream: "
- << http2::Http2DecoderAdapter::SpdyFramerErrorToString(error)
- << " frame_type_: " << Http2FrameTypeToString(frame_type_);
+ SPDY_DVLOG(1) << "SpdyFramer detected an error in the stream: "
+ << http2::Http2DecoderAdapter::SpdyFramerErrorToString(error)
+ << " frame_type_: " << Http2FrameTypeToString(frame_type_);
listener_->OnError(error, this);
}
@@ -476,8 +476,8 @@ void SpdyTestDeframerImpl::OnError(
// to indicate the end of the GOAWAY frame.
void SpdyTestDeframerImpl::OnGoAway(SpdyStreamId last_good_stream_id,
SpdyErrorCode error_code) {
- DVLOG(1) << "OnGoAway last_good_stream_id: " << last_good_stream_id
- << " error code: " << error_code;
+ SPDY_DVLOG(1) << "OnGoAway last_good_stream_id: " << last_good_stream_id
+ << " error code: " << error_code;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
frame_type_ = GOAWAY;
@@ -489,7 +489,7 @@ void SpdyTestDeframerImpl::OnGoAway(SpdyStreamId last_good_stream_id,
// If len==0 then we've reached the end of the GOAWAY frame.
bool SpdyTestDeframerImpl::OnGoAwayFrameData(const char* goaway_data,
size_t len) {
- DVLOG(1) << "OnGoAwayFrameData";
+ SPDY_DVLOG(1) << "OnGoAwayFrameData";
CHECK_EQ(frame_type_, GOAWAY)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK(goaway_description_ != nullptr);
@@ -503,7 +503,7 @@ SpdyHeadersHandlerInterface* SpdyTestDeframerImpl::OnHeaderFrameStart(
}
void SpdyTestDeframerImpl::OnHeaderFrameEnd(SpdyStreamId stream_id) {
- DVLOG(1) << "OnHeaderFrameEnd stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnHeaderFrameEnd stream_id: " << stream_id;
}
// Received the fixed portion of a HEADERS frame. Called before the variable
@@ -520,7 +520,7 @@ void SpdyTestDeframerImpl::OnHeaders(SpdyStreamId stream_id,
bool exclusive,
bool fin,
bool end) {
- DVLOG(1) << "OnHeaders stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnHeaders stream_id: " << stream_id;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_GT(stream_id, 0u);
@@ -548,8 +548,8 @@ void SpdyTestDeframerImpl::OnHeaders(SpdyStreamId stream_id,
// or frame id, as the SpdyPingId naming might imply.
// Responding to a PING is supposed to be at the highest priority.
void SpdyTestDeframerImpl::OnPing(uint64_t unique_id, bool is_ack) {
- DVLOG(1) << "OnPing unique_id: " << unique_id
- << " is_ack: " << (is_ack ? "true" : "false");
+ SPDY_DVLOG(1) << "OnPing unique_id: " << unique_id
+ << " is_ack: " << (is_ack ? "true" : "false");
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
auto ptr = SpdyMakeUnique<SpdyPingIR>(unique_id);
@@ -565,7 +565,7 @@ void SpdyTestDeframerImpl::OnPriority(SpdyStreamId stream_id,
SpdyStreamId parent_stream_id,
int weight,
bool exclusive) {
- DVLOG(1) << "OnPriority stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnPriority stream_id: " << stream_id;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_GT(stream_id, 0u);
@@ -577,7 +577,7 @@ void SpdyTestDeframerImpl::OnPriority(SpdyStreamId stream_id,
void SpdyTestDeframerImpl::OnPushPromise(SpdyStreamId stream_id,
SpdyStreamId promised_stream_id,
bool end) {
- DVLOG(1) << "OnPushPromise stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnPushPromise stream_id: " << stream_id;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_GT(stream_id, 0u);
@@ -596,8 +596,8 @@ void SpdyTestDeframerImpl::OnPushPromise(SpdyStreamId stream_id,
// frames for this stream, which we can ignore.
void SpdyTestDeframerImpl::OnRstStream(SpdyStreamId stream_id,
SpdyErrorCode error_code) {
- DVLOG(1) << "OnRstStream stream_id: " << stream_id
- << " error code: " << error_code;
+ SPDY_DVLOG(1) << "OnRstStream stream_id: " << stream_id
+ << " error code: " << error_code;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_GT(stream_id, 0u);
@@ -609,7 +609,7 @@ void SpdyTestDeframerImpl::OnRstStream(SpdyStreamId stream_id,
// Called for an individual setting. There is no negotiation; the sender is
// stating the value that the sender is using.
void SpdyTestDeframerImpl::OnSetting(SpdySettingsId id, uint32_t value) {
- DVLOG(1) << "OnSetting id: " << id << std::hex << " value: " << value;
+ SPDY_DVLOG(1) << "OnSetting id: " << id << std::hex << " value: " << value;
CHECK_EQ(frame_type_, SETTINGS)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK(settings_ != nullptr);
@@ -624,7 +624,7 @@ void SpdyTestDeframerImpl::OnSetting(SpdySettingsId id, uint32_t value) {
// (required) ACK of a SETTINGS frame. There is no stream_id because
// the settings apply to the entire connection, not to an individual stream.
void SpdyTestDeframerImpl::OnSettings() {
- DVLOG(1) << "OnSettings";
+ SPDY_DVLOG(1) << "OnSettings";
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_EQ(nullptr, settings_ir_.get());
@@ -637,7 +637,7 @@ void SpdyTestDeframerImpl::OnSettings() {
}
void SpdyTestDeframerImpl::OnSettingsAck() {
- DVLOG(1) << "OnSettingsAck";
+ SPDY_DVLOG(1) << "OnSettingsAck";
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
auto ptr = SpdyMakeUnique<SpdySettingsIR>();
@@ -646,7 +646,7 @@ void SpdyTestDeframerImpl::OnSettingsAck() {
}
void SpdyTestDeframerImpl::OnSettingsEnd() {
- DVLOG(1) << "OnSettingsEnd";
+ SPDY_DVLOG(1) << "OnSettingsEnd";
CHECK_EQ(frame_type_, SETTINGS)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK(!ack_);
@@ -661,7 +661,7 @@ void SpdyTestDeframerImpl::OnSettingsEnd() {
// frame with the END_STREAM flag set. Doesn't apply to PUSH_PROMISE frames
// because they don't have END_STREAM flags.
void SpdyTestDeframerImpl::OnStreamEnd(SpdyStreamId stream_id) {
- DVLOG(1) << "OnStreamEnd stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnStreamEnd stream_id: " << stream_id;
CHECK_EQ(stream_id_, stream_id);
CHECK(frame_type_ == DATA || frame_type_ == HEADERS ||
frame_type_ == CONTINUATION)
@@ -677,8 +677,8 @@ void SpdyTestDeframerImpl::OnStreamEnd(SpdyStreamId stream_id) {
void SpdyTestDeframerImpl::OnStreamFrameData(SpdyStreamId stream_id,
const char* data,
size_t len) {
- DVLOG(1) << "OnStreamFrameData stream_id: " << stream_id
- << " len: " << len;
+ SPDY_DVLOG(1) << "OnStreamFrameData stream_id: " << stream_id
+ << " len: " << len;
CHECK_EQ(stream_id_, stream_id);
CHECK_EQ(frame_type_, DATA);
data_->append(data, len);
@@ -688,8 +688,8 @@ void SpdyTestDeframerImpl::OnStreamFrameData(SpdyStreamId stream_id,
// payload. value will be in the range 0 to 255.
void SpdyTestDeframerImpl::OnStreamPadLength(SpdyStreamId stream_id,
size_t value) {
- DVLOG(1) << "OnStreamPadding stream_id: " << stream_id
- << " value: " << value;
+ SPDY_DVLOG(1) << "OnStreamPadding stream_id: " << stream_id
+ << " value: " << value;
CHECK(frame_type_ == DATA || frame_type_ == HEADERS ||
frame_type_ == PUSH_PROMISE)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
@@ -703,7 +703,8 @@ void SpdyTestDeframerImpl::OnStreamPadLength(SpdyStreamId stream_id,
// Called when padding is skipped over at the end of the DATA frame. len will
// be in the range 1 to 255.
void SpdyTestDeframerImpl::OnStreamPadding(SpdyStreamId stream_id, size_t len) {
- DVLOG(1) << "OnStreamPadding stream_id: " << stream_id << " len: " << len;
+ SPDY_DVLOG(1) << "OnStreamPadding stream_id: " << stream_id
+ << " len: " << len;
CHECK(frame_type_ == DATA || frame_type_ == HEADERS ||
frame_type_ == PUSH_PROMISE)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
@@ -720,8 +721,8 @@ void SpdyTestDeframerImpl::OnStreamPadding(SpdyStreamId stream_id, size_t len) {
// closed.
void SpdyTestDeframerImpl::OnWindowUpdate(SpdyStreamId stream_id,
int delta_window_size) {
- DVLOG(1) << "OnWindowUpdate stream_id: " << stream_id
- << " delta_window_size: " << delta_window_size;
+ SPDY_DVLOG(1) << "OnWindowUpdate stream_id: " << stream_id
+ << " delta_window_size: " << delta_window_size;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
CHECK_NE(0, delta_window_size);
@@ -736,7 +737,7 @@ void SpdyTestDeframerImpl::OnWindowUpdate(SpdyStreamId stream_id,
// frame types are unsupported.
bool SpdyTestDeframerImpl::OnUnknownFrame(SpdyStreamId stream_id,
uint8_t frame_type) {
- DVLOG(1) << "OnAltSvc stream_id: " << stream_id;
+ SPDY_DVLOG(1) << "OnAltSvc stream_id: " << stream_id;
CHECK_EQ(frame_type_, UNSET)
<< " frame_type_=" << Http2FrameTypeToString(frame_type_);
frame_type_ = UNKNOWN;
@@ -789,15 +790,15 @@ class LoggingSpdyDeframerDelegate : public SpdyDeframerVisitorInterface {
~LoggingSpdyDeframerDelegate() override = default;
void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnAltSvc";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnAltSvc";
wrapped_->OnAltSvc(std::move(frame));
}
void OnData(std::unique_ptr<SpdyDataIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnData";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnData";
wrapped_->OnData(std::move(frame));
}
void OnGoAway(std::unique_ptr<SpdyGoAwayIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnGoAway";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnGoAway";
wrapped_->OnGoAway(std::move(frame));
}
@@ -806,21 +807,21 @@ class LoggingSpdyDeframerDelegate : public SpdyDeframerVisitorInterface {
// and value strings) are provided in a vector.
void OnHeaders(std::unique_ptr<SpdyHeadersIR> frame,
std::unique_ptr<StringPairVector> headers) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnHeaders";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnHeaders";
wrapped_->OnHeaders(std::move(frame), std::move(headers));
}
void OnPing(std::unique_ptr<SpdyPingIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPing";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPing";
wrapped_->OnPing(std::move(frame));
}
void OnPingAck(std::unique_ptr<SpdyPingIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPingAck";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPingAck";
wrapped_->OnPingAck(std::move(frame));
}
void OnPriority(std::unique_ptr<SpdyPriorityIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPriority";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPriority";
wrapped_->OnPriority(std::move(frame));
}
@@ -829,12 +830,12 @@ class LoggingSpdyDeframerDelegate : public SpdyDeframerVisitorInterface {
// and value strings) are provided in a vector.
void OnPushPromise(std::unique_ptr<SpdyPushPromiseIR> frame,
std::unique_ptr<StringPairVector> headers) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPushPromise";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPushPromise";
wrapped_->OnPushPromise(std::move(frame), std::move(headers));
}
void OnRstStream(std::unique_ptr<SpdyRstStreamIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnRstStream";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnRstStream";
wrapped_->OnRstStream(std::move(frame));
}
@@ -843,26 +844,26 @@ class LoggingSpdyDeframerDelegate : public SpdyDeframerVisitorInterface {
// the actual settings (parameter and value) are provided in a vector.
void OnSettings(std::unique_ptr<SpdySettingsIR> frame,
std::unique_ptr<SettingVector> settings) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettings";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettings";
wrapped_->OnSettings(std::move(frame), std::move(settings));
}
// A settings frame with an ACK has no content, but for uniformity passing
// a frame with the ACK flag set.
void OnSettingsAck(std::unique_ptr<SpdySettingsIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettingsAck";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettingsAck";
wrapped_->OnSettingsAck(std::move(frame));
}
void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> frame) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnWindowUpdate";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnWindowUpdate";
wrapped_->OnWindowUpdate(std::move(frame));
}
// The SpdyFramer will not process any more data at this point.
void OnError(http2::Http2DecoderAdapter::SpdyFramerError error,
SpdyTestDeframer* deframer) override {
- DVLOG(1) << "LoggingSpdyDeframerDelegate::OnError";
+ SPDY_DVLOG(1) << "LoggingSpdyDeframerDelegate::OnError";
wrapped_->OnError(error, deframer);
}
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h
index 50d9987b811..e3d01b2de99 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h
@@ -74,13 +74,12 @@
#include <utility>
#include <vector>
-#include "base/logging.h"
-#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/spdy/core/http2_frame_decoder_adapter.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h"
#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
namespace spdy {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor_test.cc
index ede1a20b733..e4f7218f740 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_deframer_visitor_test.cc
@@ -9,7 +9,6 @@
#include <algorithm>
#include <limits>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
@@ -20,6 +19,7 @@
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h"
#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h"
namespace spdy {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc
index a056b70f68c..f4b71d323ad 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.cc
@@ -9,10 +9,10 @@
#include <limits>
#include <new>
-#include "base/logging.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
#include "net/third_party/quiche/src/spdy/core/zero_copy_output_buffer.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
namespace spdy {
@@ -167,8 +167,8 @@ bool SpdyFrameBuilder::CanWrite(size_t length) const {
if (output_ == nullptr) {
if (offset_ + length_ + length > capacity_) {
- DLOG(FATAL) << "Requested: " << length << " capacity: " << capacity_
- << " used: " << offset_ + length_;
+ SPDY_DLOG(FATAL) << "Requested: " << length << " capacity: " << capacity_
+ << " used: " << offset_ + length_;
return false;
}
} else {
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h
index c569c8c9377..e1dc32b1e82 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder.h
@@ -15,10 +15,13 @@
#include "net/third_party/quiche/src/spdy/platform/api/spdy_endianness_util.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h"
-#include "net/third_party/quiche/src/spdy/platform/api/spdy_test_utils_prod.h"
namespace spdy {
+namespace test {
+class SpdyFrameBuilderPeer;
+} // namespace test
+
// This class provides facilities for basic binary value packing
// into Spdy frames.
//
@@ -101,9 +104,7 @@ class SPDY_EXPORT_PRIVATE SpdyFrameBuilder {
bool WriteBytes(const void* data, uint32_t data_len);
private:
- SPDY_FRIEND_TEST(SpdyFrameBuilderTest, GetWritableBuffer);
- SPDY_FRIEND_TEST(SpdyFrameBuilderTest, GetWritableOutput);
- SPDY_FRIEND_TEST(SpdyFrameBuilderTest, GetWritableOutputNegative);
+ friend class test::SpdyFrameBuilderPeer;
// Populates this frame with a HTTP2 frame prefix with type and length
// information.
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder_test.cc
index 97d10f12e93..11d3c7b1925 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder_test.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_frame_builder_test.cc
@@ -10,9 +10,25 @@
#include "net/third_party/quiche/src/spdy/core/array_output_buffer.h"
#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
namespace spdy {
+namespace test {
+
+class SPDY_EXPORT_PRIVATE SpdyFrameBuilderPeer {
+ public:
+ static char* GetWritableBuffer(SpdyFrameBuilder* builder, size_t length) {
+ return builder->GetWritableBuffer(length);
+ }
+
+ static char* GetWritableOutput(SpdyFrameBuilder* builder,
+ size_t desired_length,
+ size_t* actual_length) {
+ return builder->GetWritableOutput(desired_length, actual_length);
+ }
+};
+
namespace {
const int64_t kSize = 64 * 1024;
@@ -25,7 +41,8 @@ char output_buffer[kSize] = "";
TEST(SpdyFrameBuilderTest, GetWritableBuffer) {
const size_t kBuilderSize = 10;
SpdyFrameBuilder builder(kBuilderSize);
- char* writable_buffer = builder.GetWritableBuffer(kBuilderSize);
+ char* writable_buffer =
+ SpdyFrameBuilderPeer::GetWritableBuffer(&builder, kBuilderSize);
memset(writable_buffer, ~1, kBuilderSize);
EXPECT_TRUE(builder.Seek(kBuilderSize));
SpdySerializedFrame frame(builder.take());
@@ -42,7 +59,8 @@ TEST(SpdyFrameBuilderTest, GetWritableOutput) {
const size_t kBuilderSize = 10;
SpdyFrameBuilder builder(kBuilderSize, &output);
size_t actual_size = 0;
- char* writable_buffer = builder.GetWritableOutput(kBuilderSize, &actual_size);
+ char* writable_buffer = SpdyFrameBuilderPeer::GetWritableOutput(
+ &builder, kBuilderSize, &actual_size);
memset(writable_buffer, ~1, kBuilderSize);
EXPECT_TRUE(builder.Seek(kBuilderSize));
SpdySerializedFrame frame(output.Begin(), kBuilderSize, false);
@@ -59,10 +77,11 @@ TEST(SpdyFrameBuilderTest, GetWritableOutputNegative) {
const size_t kBuilderSize = 10;
SpdyFrameBuilder builder(kBuilderSize, &output);
size_t actual_size = 0;
- char* writable_buffer = builder.GetWritableOutput(kBuilderSize, &actual_size);
- builder.GetWritableOutput(kBuilderSize, &actual_size);
+ char* writable_buffer = SpdyFrameBuilderPeer::GetWritableOutput(
+ &builder, kBuilderSize, &actual_size);
EXPECT_EQ(0u, actual_size);
EXPECT_EQ(nullptr, writable_buffer);
}
+} // namespace test
} // 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 a9252a1af34..4e3ad1bb570 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
@@ -10,7 +10,6 @@
#include <list>
#include <new>
-#include "base/logging.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h"
#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
#include "net/third_party/quiche/src/spdy/core/spdy_bitmasks.h"
@@ -18,6 +17,7 @@
#include "net/third_party/quiche/src/spdy/core/spdy_frame_reader.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
@@ -129,7 +129,7 @@ bool SerializeHeadersGivenEncoding(const SpdyHeadersIR& headers,
}
if (!ret) {
- DLOG(WARNING) << "Failed to build HEADERS. Not enough space in output";
+ SPDY_DLOG(WARNING) << "Failed to build HEADERS. Not enough space in output";
}
return ret;
}
@@ -159,8 +159,9 @@ bool SerializePushPromiseGivenEncoding(const SpdyPushPromiseIR& push_promise,
ok = builder.WriteBytes(padding.data(), padding.length());
}
- DLOG_IF(ERROR, !ok) << "Failed to write PUSH_PROMISE encoding, not enough "
- << "space in output";
+ SPDY_DLOG_IF(ERROR, !ok)
+ << "Failed to write PUSH_PROMISE encoding, not enough "
+ << "space in output";
return ok;
}
@@ -176,8 +177,8 @@ bool WritePayloadWithContinuation(SpdyFrameBuilder* builder,
} else if (type == SpdyFrameType::PUSH_PROMISE) {
end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE;
} else {
- DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type "
- << FrameTypeToString(type);
+ SPDY_DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type "
+ << FrameTypeToString(type);
}
// Write all the padding payload and as much of the data payload as possible
@@ -421,7 +422,7 @@ std::unique_ptr<SpdyFrameSequence> SpdyFramer::CreateIterator(
static_cast<const SpdyPushPromiseIR*>(frame_ir.release())));
}
case SpdyFrameType::DATA: {
- DVLOG(1) << "Serialize a stream end DATA frame for VTL";
+ SPDY_DVLOG(1) << "Serialize a stream end DATA frame for VTL";
HTTP2_FALLTHROUGH;
}
default: {
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 3841172baa0..0caa160adc8 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
@@ -12,8 +12,6 @@
#include <tuple>
#include <vector>
-#include "base/logging.h"
-#include "base/macros.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/spdy/core/array_output_buffer.h"
@@ -26,6 +24,7 @@
#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_arraysize.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_flags.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
@@ -65,8 +64,9 @@ MATCHER_P(IsFrameUnionOf, frame_list, "") {
size_t size_verified = 0;
for (const auto& frame : *frame_list) {
if (arg.size() < size_verified + frame.size()) {
- LOG(FATAL) << "Incremental header serialization should not lead to a "
- << "higher total frame length than non-incremental method.";
+ SPDY_LOG(FATAL)
+ << "Incremental header serialization should not lead to a "
+ << "higher total frame length than non-incremental method.";
return false;
}
if (memcmp(arg.data() + size_verified, frame.data(), frame.size())) {
@@ -269,16 +269,16 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
header_buffer_valid_(false) {}
void OnError(Http2DecoderAdapter::SpdyFramerError error) override {
- VLOG(1) << "SpdyFramer Error: "
- << Http2DecoderAdapter::SpdyFramerErrorToString(error);
+ SPDY_VLOG(1) << "SpdyFramer Error: "
+ << Http2DecoderAdapter::SpdyFramerErrorToString(error);
++error_count_;
}
void OnDataFrameHeader(SpdyStreamId stream_id,
size_t length,
bool fin) override {
- VLOG(1) << "OnDataFrameHeader(" << stream_id << ", " << length << ", "
- << fin << ")";
+ SPDY_VLOG(1) << "OnDataFrameHeader(" << stream_id << ", " << length << ", "
+ << fin << ")";
++data_frame_count_;
header_stream_id_ = stream_id;
}
@@ -286,29 +286,30 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
void OnStreamFrameData(SpdyStreamId stream_id,
const char* data,
size_t len) override {
- VLOG(1) << "OnStreamFrameData(" << stream_id << ", data, " << len << ", "
- << ") data:\n"
- << SpdyHexDump(SpdyStringPiece(data, len));
+ SPDY_VLOG(1) << "OnStreamFrameData(" << stream_id << ", data, " << len
+ << ", "
+ << ") data:\n"
+ << SpdyHexDump(SpdyStringPiece(data, len));
EXPECT_EQ(header_stream_id_, stream_id);
data_bytes_ += len;
}
void OnStreamEnd(SpdyStreamId stream_id) override {
- VLOG(1) << "OnStreamEnd(" << stream_id << ")";
+ SPDY_VLOG(1) << "OnStreamEnd(" << stream_id << ")";
EXPECT_EQ(header_stream_id_, stream_id);
++end_of_stream_count_;
}
void OnStreamPadLength(SpdyStreamId stream_id, size_t value) override {
- VLOG(1) << "OnStreamPadding(" << stream_id << ", " << value << ")\n";
+ SPDY_VLOG(1) << "OnStreamPadding(" << stream_id << ", " << value << ")\n";
EXPECT_EQ(header_stream_id_, stream_id);
// Count the padding length field byte against total data bytes.
data_bytes_ += 1;
}
void OnStreamPadding(SpdyStreamId stream_id, size_t len) override {
- VLOG(1) << "OnStreamPadding(" << stream_id << ", " << len << ")\n";
+ SPDY_VLOG(1) << "OnStreamPadding(" << stream_id << ", " << len << ")\n";
EXPECT_EQ(header_stream_id_, stream_id);
data_bytes_ += len;
}
@@ -329,33 +330,34 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
}
void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override {
- VLOG(1) << "OnRstStream(" << stream_id << ", " << error_code << ")";
+ SPDY_VLOG(1) << "OnRstStream(" << stream_id << ", " << error_code << ")";
++fin_frame_count_;
}
void OnSetting(SpdySettingsId id, uint32_t value) override {
- VLOG(1) << "OnSetting(" << id << ", " << std::hex << value << ")";
+ SPDY_VLOG(1) << "OnSetting(" << id << ", " << std::hex << value << ")";
++setting_count_;
}
void OnSettingsAck() override {
- VLOG(1) << "OnSettingsAck";
+ SPDY_VLOG(1) << "OnSettingsAck";
++settings_ack_received_;
}
void OnSettingsEnd() override {
- VLOG(1) << "OnSettingsEnd";
+ SPDY_VLOG(1) << "OnSettingsEnd";
++settings_ack_sent_;
}
void OnPing(SpdyPingId unique_id, bool is_ack) override {
- LOG(DFATAL) << "OnPing(" << unique_id << ", " << (is_ack ? 1 : 0) << ")";
+ SPDY_LOG(DFATAL) << "OnPing(" << unique_id << ", " << (is_ack ? 1 : 0)
+ << ")";
}
void OnGoAway(SpdyStreamId last_accepted_stream_id,
SpdyErrorCode error_code) override {
- VLOG(1) << "OnGoAway(" << last_accepted_stream_id << ", " << error_code
- << ")";
+ SPDY_VLOG(1) << "OnGoAway(" << last_accepted_stream_id << ", " << error_code
+ << ")";
++goaway_count_;
}
@@ -366,9 +368,9 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
bool exclusive,
bool fin,
bool end) override {
- VLOG(1) << "OnHeaders(" << stream_id << ", " << has_priority << ", "
- << weight << ", " << parent_stream_id << ", " << exclusive << ", "
- << fin << ", " << end << ")";
+ SPDY_VLOG(1) << "OnHeaders(" << stream_id << ", " << has_priority << ", "
+ << weight << ", " << parent_stream_id << ", " << exclusive
+ << ", " << fin << ", " << end << ")";
++headers_frame_count_;
InitHeaderStreaming(SpdyFrameType::HEADERS, stream_id);
if (fin) {
@@ -380,8 +382,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
}
void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override {
- VLOG(1) << "OnWindowUpdate(" << stream_id << ", " << delta_window_size
- << ")";
+ SPDY_VLOG(1) << "OnWindowUpdate(" << stream_id << ", " << delta_window_size
+ << ")";
last_window_update_stream_ = stream_id;
last_window_update_delta_ = delta_window_size;
}
@@ -389,8 +391,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
void OnPushPromise(SpdyStreamId stream_id,
SpdyStreamId promised_stream_id,
bool end) override {
- VLOG(1) << "OnPushPromise(" << stream_id << ", " << promised_stream_id
- << ", " << end << ")";
+ SPDY_VLOG(1) << "OnPushPromise(" << stream_id << ", " << promised_stream_id
+ << ", " << end << ")";
++push_promise_frame_count_;
InitHeaderStreaming(SpdyFrameType::PUSH_PROMISE, stream_id);
last_push_promise_stream_ = stream_id;
@@ -398,7 +400,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
}
void OnContinuation(SpdyStreamId stream_id, bool end) override {
- VLOG(1) << "OnContinuation(" << stream_id << ", " << end << ")";
+ SPDY_VLOG(1) << "OnContinuation(" << stream_id << ", " << end << ")";
++continuation_count_;
}
@@ -406,8 +408,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
SpdyStringPiece origin,
const SpdyAltSvcWireFormat::AlternativeServiceVector&
altsvc_vector) override {
- VLOG(1) << "OnAltSvc(" << stream_id << ", \"" << origin
- << "\", altsvc_vector)";
+ SPDY_VLOG(1) << "OnAltSvc(" << stream_id << ", \"" << origin
+ << "\", altsvc_vector)";
test_altsvc_ir_ = SpdyMakeUnique<SpdyAltSvcIR>(stream_id);
if (origin.length() > 0) {
test_altsvc_ir_->set_origin(SpdyString(origin));
@@ -422,13 +424,13 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
SpdyStreamId parent_stream_id,
int weight,
bool exclusive) override {
- VLOG(1) << "OnPriority(" << stream_id << ", " << parent_stream_id << ", "
- << weight << ", " << (exclusive ? 1 : 0) << ")";
+ SPDY_VLOG(1) << "OnPriority(" << stream_id << ", " << parent_stream_id
+ << ", " << weight << ", " << (exclusive ? 1 : 0) << ")";
++priority_count_;
}
bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override {
- VLOG(1) << "OnUnknownFrame(" << stream_id << ", " << frame_type << ")";
+ SPDY_VLOG(1) << "OnUnknownFrame(" << stream_id << ", " << frame_type << ")";
return on_unknown_frame_result_;
}
@@ -436,8 +438,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
SpdyFrameType type,
size_t payload_len,
size_t frame_len) override {
- VLOG(1) << "OnSendCompressedFrame(" << stream_id << ", " << type << ", "
- << payload_len << ", " << frame_len << ")";
+ SPDY_VLOG(1) << "OnSendCompressedFrame(" << stream_id << ", " << type
+ << ", " << payload_len << ", " << frame_len << ")";
last_payload_len_ = payload_len;
last_frame_len_ = frame_len;
}
@@ -445,8 +447,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
void OnReceiveCompressedFrame(SpdyStreamId stream_id,
SpdyFrameType type,
size_t frame_len) override {
- VLOG(1) << "OnReceiveCompressedFrame(" << stream_id << ", " << type << ", "
- << frame_len << ")";
+ SPDY_VLOG(1) << "OnReceiveCompressedFrame(" << stream_id << ", " << type
+ << ", " << frame_len << ")";
last_frame_len_ = frame_len;
}
@@ -472,8 +474,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface,
void InitHeaderStreaming(SpdyFrameType header_control_type,
SpdyStreamId stream_id) {
if (!IsDefinedFrameType(SerializeFrameType(header_control_type))) {
- DLOG(FATAL) << "Attempted to init header streaming with "
- << "invalid control frame type: " << header_control_type;
+ SPDY_DLOG(FATAL) << "Attempted to init header streaming with "
+ << "invalid control frame type: " << header_control_type;
}
memset(header_buffer_.get(), 0, header_buffer_size_);
header_buffer_length_ = 0;
@@ -615,7 +617,8 @@ class SpdyFramerTest : public ::testing::TestWithParam<Output> {
Http2DecoderAdapter deframer_;
};
-INSTANTIATE_TEST_SUITE_P(SpdyFramerTests, SpdyFramerTest,
+INSTANTIATE_TEST_SUITE_P(SpdyFramerTests,
+ SpdyFramerTest,
::testing::Values(USE, NOT_USE));
// Test that we can encode and decode a SpdyHeaderBlock in serialized form.
@@ -4641,8 +4644,8 @@ TEST_P(SpdyFramerTest, ProcessAllInput) {
const size_t frame1_size = frame1.size();
const size_t frame2_size = frame2.size();
- VLOG(1) << "frame1_size = " << frame1_size;
- VLOG(1) << "frame2_size = " << frame2_size;
+ SPDY_VLOG(1) << "frame1_size = " << frame1_size;
+ SPDY_VLOG(1) << "frame2_size = " << frame2_size;
SpdyString input_buffer;
input_buffer.append(frame1.data(), frame1_size);
@@ -4651,7 +4654,7 @@ TEST_P(SpdyFramerTest, ProcessAllInput) {
const char* buf = input_buffer.data();
const size_t buf_size = input_buffer.size();
- VLOG(1) << "buf_size = " << buf_size;
+ SPDY_VLOG(1) << "buf_size = " << buf_size;
size_t processed = deframer_.ProcessInput(buf, buf_size);
EXPECT_EQ(buf_size, processed);
@@ -4688,8 +4691,8 @@ TEST_P(SpdyFramerTest, ProcessAtMostOneFrame) {
const size_t frame1_size = frame1.size();
const size_t frame2_size = frame2.size();
- VLOG(1) << "frame1_size = " << frame1_size;
- VLOG(1) << "frame2_size = " << frame2_size;
+ SPDY_VLOG(1) << "frame1_size = " << frame1_size;
+ SPDY_VLOG(1) << "frame2_size = " << frame2_size;
SpdyString input_buffer;
input_buffer.append(frame1.data(), frame1_size);
@@ -4698,10 +4701,10 @@ TEST_P(SpdyFramerTest, ProcessAtMostOneFrame) {
const char* buf = input_buffer.data();
const size_t buf_size = input_buffer.size();
- VLOG(1) << "buf_size = " << buf_size;
+ SPDY_VLOG(1) << "buf_size = " << buf_size;
for (size_t first_size = 0; first_size <= buf_size; ++first_size) {
- VLOG(1) << "first_size = " << first_size;
+ SPDY_VLOG(1) << "first_size = " << first_size;
auto visitor =
SpdyMakeUnique<TestSpdyVisitor>(SpdyFramer::DISABLE_COMPRESSION);
deframer_.set_visitor(visitor.get());
@@ -4720,7 +4723,7 @@ TEST_P(SpdyFramerTest, ProcessAtMostOneFrame) {
const char* rest = buf + processed_first;
const size_t remaining = buf_size - processed_first;
- VLOG(1) << "remaining = " << remaining;
+ SPDY_VLOG(1) << "remaining = " << remaining;
size_t processed_second = deframer_.ProcessInput(rest, remaining);
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 dcf84b95b7f..c99bbf32e3c 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
@@ -9,9 +9,8 @@
#include <algorithm>
#include <utility>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_unsafe_arena.h"
@@ -204,14 +203,14 @@ SpdyHeaderBlock::ValueProxy& SpdyHeaderBlock::ValueProxy::operator=(
const SpdyStringPiece value) {
*spdy_header_block_value_size_ += value.size();
if (lookup_result_ == block_->end()) {
- DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")";
+ SPDY_DVLOG(1) << "Inserting: (" << key_ << ", " << value << ")";
lookup_result_ =
block_
->emplace(std::make_pair(
key_, HeaderValue(storage_, key_, storage_->Write(value))))
.first;
} else {
- DVLOG(1) << "Updating key: " << key_ << " with value: " << value;
+ SPDY_DVLOG(1) << "Updating key: " << key_ << " with value: " << value;
*spdy_header_block_value_size_ -= lookup_result_->second.SizeEstimate();
lookup_result_->second =
HeaderValue(storage_, key_, storage_->Write(value));
@@ -279,7 +278,7 @@ SpdyString SpdyHeaderBlock::DebugString() const {
void SpdyHeaderBlock::erase(SpdyStringPiece key) {
auto iter = block_.find(key);
if (iter != block_.end()) {
- DVLOG(1) << "Erasing header with name: " << key;
+ SPDY_DVLOG(1) << "Erasing header with name: " << key;
key_size_ -= key.size();
value_size_ -= iter->second.SizeEstimate();
block_.erase(iter);
@@ -299,11 +298,12 @@ void SpdyHeaderBlock::insert(const SpdyHeaderBlock::value_type& value) {
auto iter = block_.find(value.first);
if (iter == block_.end()) {
- DVLOG(1) << "Inserting: (" << value.first << ", " << value.second << ")";
+ SPDY_DVLOG(1) << "Inserting: (" << value.first << ", " << value.second
+ << ")";
AppendHeader(value.first, value.second);
} else {
- DVLOG(1) << "Updating key: " << iter->first
- << " with value: " << value.second;
+ SPDY_DVLOG(1) << "Updating key: " << iter->first
+ << " with value: " << value.second;
value_size_ -= iter->second.SizeEstimate();
auto* storage = GetStorage();
iter->second =
@@ -313,16 +313,16 @@ void SpdyHeaderBlock::insert(const SpdyHeaderBlock::value_type& value) {
SpdyHeaderBlock::ValueProxy SpdyHeaderBlock::operator[](
const SpdyStringPiece key) {
- DVLOG(2) << "Operator[] saw key: " << key;
+ SPDY_DVLOG(2) << "Operator[] saw key: " << key;
SpdyStringPiece out_key;
auto iter = block_.find(key);
if (iter == block_.end()) {
// We write the key first, to assure that the ValueProxy has a
// reference to a valid SpdyStringPiece in its operator=.
out_key = WriteKey(key);
- DVLOG(2) << "Key written as: " << std::hex
- << static_cast<const void*>(key.data()) << ", " << std::dec
- << key.size();
+ SPDY_DVLOG(2) << "Key written as: " << std::hex
+ << static_cast<const void*>(key.data()) << ", " << std::dec
+ << key.size();
} else {
out_key = iter->first;
}
@@ -335,12 +335,13 @@ void SpdyHeaderBlock::AppendValueOrAddHeader(const SpdyStringPiece key,
auto iter = block_.find(key);
if (iter == block_.end()) {
- DVLOG(1) << "Inserting: (" << key << ", " << value << ")";
+ SPDY_DVLOG(1) << "Inserting: (" << key << ", " << value << ")";
AppendHeader(key, value);
return;
}
- DVLOG(1) << "Updating key: " << iter->first << "; appending value: " << value;
+ SPDY_DVLOG(1) << "Updating key: " << iter->first
+ << "; appending value: " << value;
value_size_ += SeparatorForKey(key).size();
iter->second.Append(GetStorage()->Write(value));
}
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 f453246486d..ac172af0942 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
@@ -12,7 +12,6 @@
#include <utility>
#include <vector>
-#include "base/macros.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_containers.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_macros.h"
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader.cc
index 9f1fa7fda7c..484cefe731d 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader.cc
@@ -6,7 +6,7 @@
#include <new>
-#include "base/logging.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
namespace spdy {
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 f51dc6e6dfc..e454e081389 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
@@ -417,7 +417,7 @@ SpdyFrameType SpdyContinuationIR::frame_type() const {
size_t SpdyContinuationIR::size() const {
// We don't need to get the size of CONTINUATION frame directly. It is
// calculated in HEADERS or PUSH_PROMISE frame.
- DLOG(WARNING) << "Shouldn't not call size() for CONTINUATION frame.";
+ SPDY_DLOG(WARNING) << "Shouldn't not call size() for CONTINUATION frame.";
return 0;
}
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 d300d10bed2..0454d73ff86 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
@@ -18,13 +18,12 @@
#include <new>
#include <utility>
-#include "base/logging.h"
-#include "base/macros.h"
#include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h"
#include "net/third_party/quiche/src/spdy/core/spdy_bitmasks.h"
#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_ptr_util.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h"
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.cc
index 14d338502eb..5714aa9b377 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.cc
@@ -17,7 +17,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameWithHeaderBlockIREquals(
const SpdyFrameWithHeaderBlockIR& expected,
const SpdyFrameWithHeaderBlockIR& actual) {
- VLOG(1) << "VerifySpdyFrameWithHeaderBlockIREquals";
+ SPDY_VLOG(1) << "VerifySpdyFrameWithHeaderBlockIREquals";
VERIFY_TRUE(actual.header_block() == expected.header_block());
return ::testing::AssertionSuccess();
}
@@ -40,7 +40,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(const SpdyDataIR& expected,
const SpdyDataIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdyDataIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdyDataIR";
VERIFY_EQ(expected.stream_id(), actual.stream_id());
VERIFY_EQ(expected.fin(), actual.fin());
VERIFY_EQ(expected.data_len(), actual.data_len());
@@ -56,7 +56,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(const SpdyGoAwayIR& expected,
const SpdyGoAwayIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdyGoAwayIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdyGoAwayIR";
VERIFY_EQ(expected.last_good_stream_id(), actual.last_good_stream_id());
VERIFY_EQ(expected.error_code(), actual.error_code());
VERIFY_EQ(expected.description(), actual.description());
@@ -66,7 +66,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(
const SpdyHeadersIR& expected,
const SpdyHeadersIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdyHeadersIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdyHeadersIR";
VERIFY_EQ(expected.stream_id(), actual.stream_id());
VERIFY_EQ(expected.fin(), actual.fin());
VERIFY_SUCCESS(VerifySpdyFrameWithHeaderBlockIREquals(expected, actual));
@@ -80,7 +80,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(const SpdyPingIR& expected,
const SpdyPingIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdyPingIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdyPingIR";
VERIFY_EQ(expected.id(), actual.id());
VERIFY_EQ(expected.is_ack(), actual.is_ack());
return ::testing::AssertionSuccess();
@@ -89,7 +89,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(
const SpdyPriorityIR& expected,
const SpdyPriorityIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdyPriorityIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdyPriorityIR";
VERIFY_EQ(expected.stream_id(), actual.stream_id());
VERIFY_SUCCESS(VerifySpdyFrameWithPriorityIREquals(expected, actual));
return ::testing::AssertionSuccess();
@@ -98,7 +98,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(
const SpdyPushPromiseIR& expected,
const SpdyPushPromiseIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdyPushPromiseIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdyPushPromiseIR";
VERIFY_EQ(expected.stream_id(), actual.stream_id());
VERIFY_SUCCESS(VerifySpdyFrameWithPaddingIREquals(expected, actual));
VERIFY_EQ(expected.promised_stream_id(), actual.promised_stream_id());
@@ -109,7 +109,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(
const SpdyRstStreamIR& expected,
const SpdyRstStreamIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdyRstStreamIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdyRstStreamIR";
VERIFY_EQ(expected.stream_id(), actual.stream_id());
VERIFY_EQ(expected.error_code(), actual.error_code());
return ::testing::AssertionSuccess();
@@ -118,7 +118,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(
const SpdySettingsIR& expected,
const SpdySettingsIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdySettingsIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdySettingsIR";
// Note, ignoring non-HTTP/2 fields such as clear_settings.
VERIFY_EQ(expected.is_ack(), actual.is_ack());
@@ -145,7 +145,7 @@ namespace test {
::testing::AssertionResult VerifySpdyFrameIREquals(
const SpdyWindowUpdateIR& expected,
const SpdyWindowUpdateIR& actual) {
- VLOG(1) << "VerifySpdyFrameIREquals SpdyWindowUpdateIR";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals SpdyWindowUpdateIR";
VERIFY_EQ(expected.stream_id(), actual.stream_id());
VERIFY_EQ(expected.delta(), actual.delta());
return ::testing::AssertionSuccess();
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h
index bbec5979337..ce2c9d538e7 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h
@@ -19,11 +19,11 @@
#include <typeinfo>
-#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
namespace spdy {
namespace test {
@@ -38,7 +38,7 @@ namespace test {
template <class T>
::testing::AssertionResult VerifySpdyFrameWithPaddingIREquals(const T& expected,
const T& actual) {
- VLOG(1) << "VerifySpdyFrameWithPaddingIREquals";
+ SPDY_VLOG(1) << "VerifySpdyFrameWithPaddingIREquals";
VERIFY_EQ(expected.padded(), actual.padded());
if (expected.padded()) {
VERIFY_EQ(expected.padding_payload_len(), actual.padding_payload_len());
@@ -52,7 +52,7 @@ template <class T>
::testing::AssertionResult VerifySpdyFrameWithPriorityIREquals(
const T& expected,
const T& actual) {
- VLOG(1) << "VerifySpdyFrameWithPriorityIREquals";
+ SPDY_VLOG(1) << "VerifySpdyFrameWithPriorityIREquals";
VERIFY_EQ(expected.parent_stream_id(), actual.parent_stream_id());
VERIFY_EQ(expected.weight(), actual.weight());
VERIFY_EQ(expected.exclusive(), actual.exclusive());
@@ -123,12 +123,12 @@ template <class E>
::testing::AssertionResult VerifySpdyFrameIREquals(const E* expected,
const SpdyFrameIR* actual) {
if (expected == nullptr || actual == nullptr) {
- VLOG(1) << "VerifySpdyFrameIREquals one null";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals one null";
VERIFY_EQ(expected, nullptr);
VERIFY_EQ(actual, nullptr);
return ::testing::AssertionSuccess();
}
- VLOG(1) << "VerifySpdyFrameIREquals not null";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals not null";
VERIFY_EQ(actual->frame_type(), expected->frame_type());
const E* actual2 = static_cast<const E*>(actual);
return VerifySpdyFrameIREquals(*expected, *actual2);
@@ -139,7 +139,7 @@ template <class E>
template <class E>
::testing::AssertionResult VerifySpdyFrameIREquals(const E& expected,
const SpdyFrameIR* actual) {
- VLOG(1) << "VerifySpdyFrameIREquals";
+ SPDY_VLOG(1) << "VerifySpdyFrameIREquals";
return VerifySpdyFrameIREquals(&expected, actual);
}
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena.cc
new file mode 100644
index 00000000000..08cdc62db0c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena.cc
@@ -0,0 +1,103 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/spdy/core/spdy_simple_arena.h"
+
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
+
+namespace spdy {
+
+SpdySimpleArena::SpdySimpleArena(size_t block_size) : block_size_(block_size) {}
+
+SpdySimpleArena::~SpdySimpleArena() = default;
+
+SpdySimpleArena::SpdySimpleArena(SpdySimpleArena&& other) = default;
+SpdySimpleArena& SpdySimpleArena::operator=(SpdySimpleArena&& other) = default;
+
+char* SpdySimpleArena::Alloc(size_t size) {
+ Reserve(size);
+ Block& b = blocks_.back();
+ DCHECK_GE(b.size, b.used + size);
+ char* out = b.data.get() + b.used;
+ b.used += size;
+ return out;
+}
+
+char* SpdySimpleArena::Realloc(char* original, size_t oldsize, size_t newsize) {
+ DCHECK(!blocks_.empty());
+ Block& last = blocks_.back();
+ if (last.data.get() <= original && original < last.data.get() + last.size) {
+ // (original, oldsize) is in the last Block.
+ DCHECK_GE(last.data.get() + last.used, original + oldsize);
+ if (original + oldsize == last.data.get() + last.used) {
+ // (original, oldsize) was the most recent allocation,
+ if (original + newsize < last.data.get() + last.size) {
+ // (original, newsize) fits in the same Block.
+ last.used += newsize - oldsize;
+ return original;
+ }
+ }
+ }
+ char* out = Alloc(newsize);
+ memcpy(out, original, oldsize);
+ return out;
+}
+
+char* SpdySimpleArena::Memdup(const char* data, size_t size) {
+ char* out = Alloc(size);
+ memcpy(out, data, size);
+ return out;
+}
+
+void SpdySimpleArena::Free(char* data, size_t size) {
+ if (blocks_.empty()) {
+ return;
+ }
+ Block& b = blocks_.back();
+ if (size <= b.used && data + size == b.data.get() + b.used) {
+ // The memory region passed by the caller was the most recent allocation
+ // from the final block in this arena.
+ b.used -= size;
+ }
+}
+
+void SpdySimpleArena::Reset() {
+ blocks_.clear();
+ status_.bytes_allocated_ = 0;
+}
+
+void SpdySimpleArena::Reserve(size_t additional_space) {
+ if (blocks_.empty()) {
+ AllocBlock(std::max(additional_space, block_size_));
+ } else {
+ const Block& last = blocks_.back();
+ if (last.size < last.used + additional_space) {
+ AllocBlock(std::max(additional_space, block_size_));
+ }
+ }
+}
+
+void SpdySimpleArena::AllocBlock(size_t size) {
+ blocks_.push_back(Block(size));
+ status_.bytes_allocated_ += size;
+}
+
+SpdySimpleArena::Block::Block(size_t s) : data(new char[s]), size(s), used(0) {}
+
+SpdySimpleArena::Block::~Block() = default;
+
+SpdySimpleArena::Block::Block(SpdySimpleArena::Block&& other)
+ : size(other.size), used(other.used) {
+ data = std::move(other.data);
+}
+
+SpdySimpleArena::Block& SpdySimpleArena::Block::operator=(
+ SpdySimpleArena::Block&& other) {
+ size = other.size;
+ used = other.used;
+ data = std::move(other.data);
+ return *this;
+}
+
+} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena.h b/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena.h
new file mode 100644
index 00000000000..4cd48ce002d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena.h
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_SPDY_CORE_SPDY_SIMPLE_ARENA_H_
+#define QUICHE_SPDY_CORE_SPDY_SIMPLE_ARENA_H_
+
+#include <memory>
+#include <vector>
+
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_export.h"
+
+namespace spdy {
+
+// Allocates large blocks of memory, and doles them out in smaller chunks.
+// Not thread-safe.
+class SPDY_EXPORT_PRIVATE SpdySimpleArena {
+ public:
+ class Status {
+ private:
+ friend class SpdySimpleArena;
+ size_t bytes_allocated_;
+
+ public:
+ Status() : bytes_allocated_(0) {}
+ size_t bytes_allocated() const { return bytes_allocated_; }
+ };
+
+ // Blocks allocated by this arena will be at least |block_size| bytes.
+ explicit SpdySimpleArena(size_t block_size);
+ ~SpdySimpleArena();
+
+ // Copy and assign are not allowed.
+ SpdySimpleArena() = delete;
+ SpdySimpleArena(const SpdySimpleArena&) = delete;
+ SpdySimpleArena& operator=(const SpdySimpleArena&) = delete;
+
+ // Move is allowed.
+ SpdySimpleArena(SpdySimpleArena&& other);
+ SpdySimpleArena& operator=(SpdySimpleArena&& other);
+
+ char* Alloc(size_t size);
+ char* Realloc(char* original, size_t oldsize, size_t newsize);
+ char* Memdup(const char* data, size_t size);
+
+ // If |data| and |size| describe the most recent allocation made from this
+ // arena, the memory is reclaimed. Otherwise, this method is a no-op.
+ void Free(char* data, size_t size);
+
+ void Reset();
+
+ Status status() const { return status_; }
+
+ private:
+ struct Block {
+ std::unique_ptr<char[]> data;
+ size_t size = 0;
+ size_t used = 0;
+
+ explicit Block(size_t s);
+ ~Block();
+
+ Block(Block&& other);
+ Block& operator=(Block&& other);
+ };
+
+ void Reserve(size_t additional_space);
+ void AllocBlock(size_t size);
+
+ size_t block_size_;
+ std::vector<Block> blocks_;
+ Status status_;
+};
+
+} // namespace spdy
+
+#endif // QUICHE_SPDY_CORE_SPDY_SIMPLE_ARENA_H_
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena_test.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena_test.cc
new file mode 100644
index 00000000000..fc4b13033b9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_simple_arena_test.cc
@@ -0,0 +1,136 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/spdy/core/spdy_simple_arena.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_string.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_string_piece.h"
+
+namespace spdy {
+namespace {
+
+size_t kDefaultBlockSize = 2048;
+const char kTestString[] = "This is a decently long test string.";
+
+TEST(SpdySimpleArenaTest, Memdup) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ char* c = arena.Memdup(kTestString, length);
+ EXPECT_NE(nullptr, c);
+ EXPECT_NE(c, kTestString);
+ EXPECT_EQ(SpdyStringPiece(c, length), kTestString);
+}
+
+TEST(SpdySimpleArenaTest, MemdupLargeString) {
+ SpdySimpleArena arena(10 /* block size */);
+ const size_t length = strlen(kTestString);
+ char* c = arena.Memdup(kTestString, length);
+ EXPECT_NE(nullptr, c);
+ EXPECT_NE(c, kTestString);
+ EXPECT_EQ(SpdyStringPiece(c, length), kTestString);
+}
+
+TEST(SpdySimpleArenaTest, MultipleBlocks) {
+ SpdySimpleArena arena(40 /* block size */);
+ std::vector<SpdyString> strings = {
+ "One decently long string.", "Another string.",
+ "A third string that will surely go in a different block."};
+ std::vector<SpdyStringPiece> copies;
+ for (const SpdyString& s : strings) {
+ SpdyStringPiece sp(arena.Memdup(s.data(), s.size()), s.size());
+ copies.push_back(sp);
+ }
+ EXPECT_EQ(strings.size(), copies.size());
+ for (size_t i = 0; i < strings.size(); ++i) {
+ EXPECT_EQ(copies[i], strings[i]);
+ }
+}
+
+TEST(SpdySimpleArenaTest, UseAfterReset) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ char* c = arena.Memdup(kTestString, length);
+ arena.Reset();
+ c = arena.Memdup(kTestString, length);
+ EXPECT_NE(nullptr, c);
+ EXPECT_NE(c, kTestString);
+ EXPECT_EQ(SpdyStringPiece(c, length), kTestString);
+}
+
+TEST(SpdySimpleArenaTest, Free) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ // Freeing memory not owned by the arena should be a no-op, and freeing
+ // before any allocations from the arena should be a no-op.
+ arena.Free(const_cast<char*>(kTestString), length);
+ char* c1 = arena.Memdup("Foo", 3);
+ char* c2 = arena.Memdup(kTestString, length);
+ arena.Free(const_cast<char*>(kTestString), length);
+ char* c3 = arena.Memdup("Bar", 3);
+ char* c4 = arena.Memdup(kTestString, length);
+ EXPECT_NE(c1, c2);
+ EXPECT_NE(c1, c3);
+ EXPECT_NE(c1, c4);
+ EXPECT_NE(c2, c3);
+ EXPECT_NE(c2, c4);
+ EXPECT_NE(c3, c4);
+ // Freeing c4 should succeed, since it was the most recent allocation.
+ arena.Free(c4, length);
+ // Freeing c2 should be a no-op.
+ arena.Free(c2, length);
+ // c5 should reuse memory that was previously used by c4.
+ char* c5 = arena.Memdup("Baz", 3);
+ EXPECT_EQ(c4, c5);
+}
+
+TEST(SpdySimpleArenaTest, Alloc) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ char* c1 = arena.Alloc(length);
+ char* c2 = arena.Alloc(2 * length);
+ char* c3 = arena.Alloc(3 * length);
+ char* c4 = arena.Memdup(kTestString, length);
+ EXPECT_EQ(c1 + length, c2);
+ EXPECT_EQ(c2 + 2 * length, c3);
+ EXPECT_EQ(c3 + 3 * length, c4);
+ EXPECT_EQ(SpdyStringPiece(c4, length), kTestString);
+}
+
+TEST(SpdySimpleArenaTest, Realloc) {
+ SpdySimpleArena arena(kDefaultBlockSize);
+ const size_t length = strlen(kTestString);
+ // Simple realloc that fits in the block.
+ char* c1 = arena.Memdup(kTestString, length);
+ char* c2 = arena.Realloc(c1, length, 2 * length);
+ EXPECT_TRUE(c1);
+ EXPECT_EQ(c1, c2);
+ EXPECT_EQ(SpdyStringPiece(c1, length), kTestString);
+ // Multiple reallocs.
+ char* c3 = arena.Memdup(kTestString, length);
+ EXPECT_EQ(c2 + 2 * length, c3);
+ EXPECT_EQ(SpdyStringPiece(c3, length), kTestString);
+ char* c4 = arena.Realloc(c3, length, 2 * length);
+ EXPECT_EQ(c3, c4);
+ EXPECT_EQ(SpdyStringPiece(c4, length), kTestString);
+ char* c5 = arena.Realloc(c4, 2 * length, 3 * length);
+ EXPECT_EQ(c4, c5);
+ EXPECT_EQ(SpdyStringPiece(c5, length), kTestString);
+ char* c6 = arena.Memdup(kTestString, length);
+ EXPECT_EQ(c5 + 3 * length, c6);
+ EXPECT_EQ(SpdyStringPiece(c6, length), kTestString);
+ // Realloc that does not fit in the remainder of the first block.
+ char* c7 = arena.Realloc(c6, length, kDefaultBlockSize);
+ EXPECT_EQ(SpdyStringPiece(c7, length), kTestString);
+ arena.Free(c7, kDefaultBlockSize);
+ char* c8 = arena.Memdup(kTestString, length);
+ EXPECT_NE(c6, c7);
+ EXPECT_EQ(c7, c8);
+ EXPECT_EQ(SpdyStringPiece(c8, length), kTestString);
+}
+
+} // namespace
+} // namespace spdy
diff --git a/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc b/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc
index dd6c417abeb..622a5a696a1 100644
--- a/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc
+++ b/chromium/net/third_party/quiche/src/spdy/core/spdy_test_utils.cc
@@ -11,9 +11,9 @@
#include <utility>
#include <vector>
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_endianness_util.h"
+#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
namespace spdy {
namespace test {
@@ -27,7 +27,7 @@ SpdyString HexDumpWithMarks(const unsigned char* data,
const int kSizeLimit = 1024;
if (length > kSizeLimit || mark_length > kSizeLimit) {
- LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
+ SPDY_LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
length = std::min(length, kSizeLimit);
mark_length = std::min(mark_length, kSizeLimit);
}
diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_logging.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_logging.h
new file mode 100644
index 00000000000..657c5b1a584
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_logging.h
@@ -0,0 +1,23 @@
+// Copyright 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.
+
+#ifndef QUICHE_SPDY_PLATFORM_API_SPDY_LOGGING_H_
+#define QUICHE_SPDY_PLATFORM_API_SPDY_LOGGING_H_
+
+#include "net/spdy/platform/impl/spdy_logging_impl.h"
+
+#define SPDY_LOG(severity) SPDY_LOG_IMPL(severity)
+
+#define SPDY_VLOG(verbose_level) SPDY_VLOG_IMPL(verbose_level)
+
+#define SPDY_DLOG(severity) SPDY_DLOG_IMPL(severity)
+
+#define SPDY_DLOG_IF(severity, condition) SPDY_DLOG_IF_IMPL(severity, condition)
+
+#define SPDY_DVLOG(verbose_level) SPDY_DVLOG_IMPL(verbose_level)
+
+#define SPDY_DVLOG_IF(verbose_level, condition) \
+ SPDY_DVLOG_IF_IMPL(verbose_level, condition)
+
+#endif // QUICHE_SPDY_PLATFORM_API_SPDY_LOGGING_H_
diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_macros.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_macros.h
index a75c9576fc8..58ea4d3efbd 100644
--- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_macros.h
+++ b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_macros.h
@@ -10,6 +10,4 @@
#define SPDY_MUST_USE_RESULT SPDY_MUST_USE_RESULT_IMPL
#define SPDY_UNUSED SPDY_UNUSED_IMPL
-#define SPDY_DVLOG_IF SPDY_DVLOG_IF_IMPL
-
#endif // QUICHE_SPDY_PLATFORM_API_SPDY_MACROS_H_
diff --git a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_test_utils_prod.h b/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_test_utils_prod.h
deleted file mode 100644
index 14667701905..00000000000
--- a/chromium/net/third_party/quiche/src/spdy/platform/api/spdy_test_utils_prod.h
+++ /dev/null
@@ -1,12 +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.
-
-#ifndef QUICHE_SPDY_PLATFORM_API_SPDY_TEST_UTILS_PROD_H_
-#define QUICHE_SPDY_PLATFORM_API_SPDY_TEST_UTILS_PROD_H_
-
-#include "net/spdy/platform/impl/spdy_test_utils_prod_impl.h"
-
-#define SPDY_FRIEND_TEST SPDY_FRIEND_TEST_IMPL
-
-#endif // QUICHE_SPDY_PLATFORM_API_SPDY_TEST_UTILS_PROD_H_